forked from CIT/Vmeda.Online
		
	MOBILE-4660 storagemanager: Manage subsections on storage manager
This commit is contained in:
		
							parent
							
								
									a169d9301a
								
							
						
					
					
						commit
						a2c6a5b578
					
				@ -46,101 +46,109 @@
 | 
				
			|||||||
            </ion-card-header>
 | 
					            </ion-card-header>
 | 
				
			||||||
        </ion-card>
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ion-accordion-group [multiple]="true" (ionChange)="accordionGroupChange($event.detail)" #accordionGroup>
 | 
					        <ion-accordion-group [multiple]="true" (ionChange)="accordionGroupChange($event.detail)" [value]="accordionMultipleValue">
 | 
				
			||||||
            <ng-container *ngFor="let section of sections">
 | 
					            <ng-container *ngFor="let section of sections">
 | 
				
			||||||
                <ion-card class="section" *ngIf="section.modules.length > 0">
 | 
					                <ng-container *ngTemplateOutlet="sectionCard; context: { section }" />
 | 
				
			||||||
                    <ion-accordion [value]="section.id" toggleIconSlot="start">
 | 
					 | 
				
			||||||
                        <ion-item [detail]="false" slot="header" class="card-header">
 | 
					 | 
				
			||||||
                            <ion-label>
 | 
					 | 
				
			||||||
                                <p class="item-heading ion-text-wrap">
 | 
					 | 
				
			||||||
                                    <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="section.course"
 | 
					 | 
				
			||||||
                                        [adaptImg]="false" />
 | 
					 | 
				
			||||||
                                </p>
 | 
					 | 
				
			||||||
                                <ion-badge [color]="section.downloadStatus === statusDownloaded ? 'success' : 'light'"
 | 
					 | 
				
			||||||
                                    *ngIf="!section.calculatingSize && section.totalSize > 0">
 | 
					 | 
				
			||||||
                                    <ion-icon name="fam-cloud-done" *ngIf="section.downloadStatus === statusDownloaded"
 | 
					 | 
				
			||||||
                                        [attr.aria-label]="'core.downloaded' | translate" />{{ section.totalSize | coreBytesToSize }}
 | 
					 | 
				
			||||||
                                </ion-badge>
 | 
					 | 
				
			||||||
                                <ion-badge color="light" *ngIf="section.calculatingSize">
 | 
					 | 
				
			||||||
                                    {{ 'core.calculating' | translate }}
 | 
					 | 
				
			||||||
                                </ion-badge>
 | 
					 | 
				
			||||||
                                <!-- Download progress. -->
 | 
					 | 
				
			||||||
                                <p *ngIf="downloadEnabled && section.isDownloading">
 | 
					 | 
				
			||||||
                                    <core-progress-bar [progress]="section.total === 0 ? -1 : (section.count / section.total) * 100" />
 | 
					 | 
				
			||||||
                                </p>
 | 
					 | 
				
			||||||
                            </ion-label>
 | 
					 | 
				
			||||||
                            <div class="storage-buttons" slot="end"
 | 
					 | 
				
			||||||
                                *ngIf="(!section.calculatingSize && section.totalSize > 0) || downloadEnabled">
 | 
					 | 
				
			||||||
                                <div *ngIf="downloadEnabled" slot="end" class="core-button-spinner">
 | 
					 | 
				
			||||||
                                    <core-download-refresh *ngIf="!section.isDownloading && section.downloadStatus !== statusDownloaded"
 | 
					 | 
				
			||||||
                                        [status]="section.downloadStatus" [enabled]="true" (action)="prefecthSection(section)"
 | 
					 | 
				
			||||||
                                        [loading]="section.isDownloading || section.isCalculating" [canTrustDownload]="true"
 | 
					 | 
				
			||||||
                                        [statusesTranslatable]="{notdownloaded: 'addon.storagemanager.downloaddatafrom' }"
 | 
					 | 
				
			||||||
                                        [statusSubject]="section.name" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                    <ion-badge class="core-course-download-section-progress"
 | 
					 | 
				
			||||||
                                        *ngIf="section.isDownloading && section.count < section.total" role="progressbar"
 | 
					 | 
				
			||||||
                                        [attr.aria-valuemax]="section.total" [attr.aria-valuenow]="section.count"
 | 
					 | 
				
			||||||
                                        [attr.aria-valuetext]="'core.course.downloadsectionprogressdescription' | translate:section">
 | 
					 | 
				
			||||||
                                        {{section.count}} / {{section.total}}
 | 
					 | 
				
			||||||
                                    </ion-badge>
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                                <ion-button (click)="deleteForSection($event, section)"
 | 
					 | 
				
			||||||
                                    *ngIf="!section.calculatingSize && section.totalSize > 0" color="danger" fill="clear">
 | 
					 | 
				
			||||||
                                    <ion-icon name="fas-trash" slot="icon-only"
 | 
					 | 
				
			||||||
                                        [attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: section.name }" />
 | 
					 | 
				
			||||||
                                </ion-button>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </ion-item>
 | 
					 | 
				
			||||||
                        <ion-card-content slot="content">
 | 
					 | 
				
			||||||
                            <ng-container *ngIf="section.expanded">
 | 
					 | 
				
			||||||
                                <ng-container *ngFor="let module of section.modules">
 | 
					 | 
				
			||||||
                                    <ion-item class="core-course-storage-activity"
 | 
					 | 
				
			||||||
                                        *ngIf="downloadEnabled || (!module.calculatingSize && module.totalSize > 0)">
 | 
					 | 
				
			||||||
                                        <core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon"
 | 
					 | 
				
			||||||
                                            [modname]="module.modname" [componentId]="module.instance"
 | 
					 | 
				
			||||||
                                            [fallbackTranslation]="module.modplural" [isBranded]="module.branded" />
 | 
					 | 
				
			||||||
                                        <ion-label class="ion-text-wrap">
 | 
					 | 
				
			||||||
                                            <p class="item-heading {{module.handlerData!.class}} addon-storagemanager-module-size">
 | 
					 | 
				
			||||||
                                                <core-format-text [text]="module.handlerData.title" [courseId]="module.course"
 | 
					 | 
				
			||||||
                                                    contextLevel="module" [contextInstanceId]="module.id" [adaptImg]="false" />
 | 
					 | 
				
			||||||
                                            </p>
 | 
					 | 
				
			||||||
                                            <ion-badge [color]="module.downloadStatus === statusDownloaded ? 'success' : 'light'"
 | 
					 | 
				
			||||||
                                                *ngIf="!module.calculatingSize && module.totalSize > 0">
 | 
					 | 
				
			||||||
                                                <ion-icon name="fam-cloud-done" *ngIf="module.downloadStatus === statusDownloaded"
 | 
					 | 
				
			||||||
                                                    [attr.aria-label]="'core.downloaded' | translate" />{{ module.totalSize |
 | 
					 | 
				
			||||||
                                                coreBytesToSize }}
 | 
					 | 
				
			||||||
                                            </ion-badge>
 | 
					 | 
				
			||||||
                                            <ion-badge color="light" *ngIf="module.calculatingSize ||
 | 
					 | 
				
			||||||
                                        (section.isDownloading && module.downloadStatus === statusDownloaded)">
 | 
					 | 
				
			||||||
                                                {{ 'core.calculating' | translate }}
 | 
					 | 
				
			||||||
                                            </ion-badge>
 | 
					 | 
				
			||||||
                                        </ion-label>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                        <div class="storage-buttons" slot="end">
 | 
					 | 
				
			||||||
                                            <core-download-refresh *ngIf="downloadEnabled && module.handlerData?.showDownloadButton &&
 | 
					 | 
				
			||||||
                                        module.downloadStatus !== statusDownloaded" [status]="module.downloadStatus" [enabled]="true"
 | 
					 | 
				
			||||||
                                                [canTrustDownload]="true" [loading]="module.spinner || module.handlerData.spinner"
 | 
					 | 
				
			||||||
                                                (action)="prefetchModule(module)"
 | 
					 | 
				
			||||||
                                                [statusesTranslatable]="{notdownloaded: 'addon.storagemanager.downloaddatafrom' }"
 | 
					 | 
				
			||||||
                                                [statusSubject]="module.name" />
 | 
					 | 
				
			||||||
                                            <ion-button fill="clear" (click)="deleteForModule($event, module, section)"
 | 
					 | 
				
			||||||
                                                *ngIf="!module.calculatingSize && module.totalSize > 0" color="danger">
 | 
					 | 
				
			||||||
                                                <ion-icon name="fas-trash" slot="icon-only" [attr.aria-label]="
 | 
					 | 
				
			||||||
                                                    'addon.storagemanager.deletedatafrom' | translate: { name: module.name }" />
 | 
					 | 
				
			||||||
                                            </ion-button>
 | 
					 | 
				
			||||||
                                            <p *ngIf="!downloadEnabled || !module.handlerData?.showDownloadButton" class="sr-only">
 | 
					 | 
				
			||||||
                                                {{ 'core.notdownloadable' | translate }}
 | 
					 | 
				
			||||||
                                            </p>
 | 
					 | 
				
			||||||
                                        </div>
 | 
					 | 
				
			||||||
                                    </ion-item>
 | 
					 | 
				
			||||||
                                </ng-container>
 | 
					 | 
				
			||||||
                            </ng-container>
 | 
					 | 
				
			||||||
                        </ion-card-content>
 | 
					 | 
				
			||||||
                    </ion-accordion>
 | 
					 | 
				
			||||||
                </ion-card>
 | 
					 | 
				
			||||||
            </ng-container>
 | 
					            </ng-container>
 | 
				
			||||||
        </ion-accordion-group>
 | 
					        </ion-accordion-group>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </core-loading>
 | 
					    </core-loading>
 | 
				
			||||||
</ion-content>
 | 
					</ion-content>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ng-template #sectionCard let-section="section">
 | 
				
			||||||
 | 
					    <ion-card class="section" *ngIf="section.modules.length > 0" [id]="'addons-course-storage-'+section.id">
 | 
				
			||||||
 | 
					        <ion-accordion [value]="section.id" toggleIconSlot="start">
 | 
				
			||||||
 | 
					            <ion-item [detail]="false" slot="header" class="card-header">
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <p class="item-heading ion-text-wrap">
 | 
				
			||||||
 | 
					                        <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="section.course"
 | 
				
			||||||
 | 
					                            [adaptImg]="false" />
 | 
				
			||||||
 | 
					                    </p>
 | 
				
			||||||
 | 
					                    <ion-badge [color]="section.downloadStatus === statusDownloaded ? 'success' : 'light'"
 | 
				
			||||||
 | 
					                        *ngIf="!section.calculatingSize && section.totalSize > 0">
 | 
				
			||||||
 | 
					                        <ion-icon name="fam-cloud-done" *ngIf="section.downloadStatus === statusDownloaded"
 | 
				
			||||||
 | 
					                            [attr.aria-label]="'core.downloaded' | translate" />{{ section.totalSize | coreBytesToSize }}
 | 
				
			||||||
 | 
					                    </ion-badge>
 | 
				
			||||||
 | 
					                    <ion-badge color="light" *ngIf="section.calculatingSize">
 | 
				
			||||||
 | 
					                        {{ 'core.calculating' | translate }}
 | 
				
			||||||
 | 
					                    </ion-badge>
 | 
				
			||||||
 | 
					                    <!-- Download progress. -->
 | 
				
			||||||
 | 
					                    <p *ngIf="downloadEnabled && section.isDownloading">
 | 
				
			||||||
 | 
					                        <core-progress-bar [progress]="section.total === 0 ? -1 : (section.count / section.total) * 100" />
 | 
				
			||||||
 | 
					                    </p>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					                <div class="storage-buttons" slot="end" *ngIf="(!section.calculatingSize && section.totalSize > 0) || downloadEnabled">
 | 
				
			||||||
 | 
					                    <div *ngIf="downloadEnabled" slot="end" class="core-button-spinner">
 | 
				
			||||||
 | 
					                        <core-download-refresh *ngIf="!section.isDownloading && section.downloadStatus !== statusDownloaded"
 | 
				
			||||||
 | 
					                            [status]="section.downloadStatus" [enabled]="true" (action)="prefecthSection(section)"
 | 
				
			||||||
 | 
					                            [loading]="section.isDownloading || section.isCalculating" [canTrustDownload]="true"
 | 
				
			||||||
 | 
					                            [statusesTranslatable]="{notdownloaded: 'addon.storagemanager.downloaddatafrom' }"
 | 
				
			||||||
 | 
					                            [statusSubject]="section.name" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <ion-badge class="core-course-download-section-progress"
 | 
				
			||||||
 | 
					                            *ngIf="section.isDownloading && section.count < section.total" role="progressbar"
 | 
				
			||||||
 | 
					                            [attr.aria-valuemax]="section.total" [attr.aria-valuenow]="section.count"
 | 
				
			||||||
 | 
					                            [attr.aria-valuetext]="'core.course.downloadsectionprogressdescription' | translate:section">
 | 
				
			||||||
 | 
					                            {{section.count}} / {{section.total}}
 | 
				
			||||||
 | 
					                        </ion-badge>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <ion-button (click)="deleteForSection($event, section)" *ngIf="!section.calculatingSize && section.totalSize > 0"
 | 
				
			||||||
 | 
					                        color="danger" fill="clear">
 | 
				
			||||||
 | 
					                        <ion-icon name="fas-trash" slot="icon-only"
 | 
				
			||||||
 | 
					                            [attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: section.name }" />
 | 
				
			||||||
 | 
					                    </ion-button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					            <ion-card-content slot="content">
 | 
				
			||||||
 | 
					                <ng-container *ngIf="section.expanded">
 | 
				
			||||||
 | 
					                    <ng-container *ngFor="let module of section.modules">
 | 
				
			||||||
 | 
					                        @if (module.subSection) {
 | 
				
			||||||
 | 
					                        <ng-container *ngTemplateOutlet="sectionCard; context: { section: module.subSection }" />
 | 
				
			||||||
 | 
					                        } @else {
 | 
				
			||||||
 | 
					                        <ion-item class="core-course-storage-activity"
 | 
				
			||||||
 | 
					                            *ngIf="downloadEnabled || (!module.calculatingSize && module.totalSize > 0)">
 | 
				
			||||||
 | 
					                            <core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon"
 | 
				
			||||||
 | 
					                                [modname]="module.modname" [componentId]="module.instance" [fallbackTranslation]="module.modplural"
 | 
				
			||||||
 | 
					                                [isBranded]="module.branded" />
 | 
				
			||||||
 | 
					                            <ion-label class="ion-text-wrap">
 | 
				
			||||||
 | 
					                                <p class="item-heading {{module.handlerData!.class}} addon-storagemanager-module-size">
 | 
				
			||||||
 | 
					                                    <core-format-text [text]="module.handlerData.title" [courseId]="module.course" contextLevel="module"
 | 
				
			||||||
 | 
					                                        [contextInstanceId]="module.id" [adaptImg]="false" />
 | 
				
			||||||
 | 
					                                </p>
 | 
				
			||||||
 | 
					                                <ion-badge [color]="module.downloadStatus === statusDownloaded ? 'success' : 'light'"
 | 
				
			||||||
 | 
					                                    *ngIf="!module.calculatingSize && module.totalSize > 0">
 | 
				
			||||||
 | 
					                                    <ion-icon name="fam-cloud-done" *ngIf="module.downloadStatus === statusDownloaded"
 | 
				
			||||||
 | 
					                                        [attr.aria-label]="'core.downloaded' | translate" />{{ module.totalSize |
 | 
				
			||||||
 | 
					                                    coreBytesToSize }}
 | 
				
			||||||
 | 
					                                </ion-badge>
 | 
				
			||||||
 | 
					                                <ion-badge color="light" *ngIf="module.calculatingSize ||
 | 
				
			||||||
 | 
					                            (section.isDownloading && module.downloadStatus === statusDownloaded)">
 | 
				
			||||||
 | 
					                                    {{ 'core.calculating' | translate }}
 | 
				
			||||||
 | 
					                                </ion-badge>
 | 
				
			||||||
 | 
					                            </ion-label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <div class="storage-buttons" slot="end">
 | 
				
			||||||
 | 
					                                <core-download-refresh *ngIf="downloadEnabled && module.handlerData?.showDownloadButton &&
 | 
				
			||||||
 | 
					                            module.downloadStatus !== statusDownloaded" [status]="module.downloadStatus" [enabled]="true"
 | 
				
			||||||
 | 
					                                    [canTrustDownload]="true" [loading]="module.spinner || module.handlerData.spinner"
 | 
				
			||||||
 | 
					                                    (action)="prefetchModule(module)"
 | 
				
			||||||
 | 
					                                    [statusesTranslatable]="{notdownloaded: 'addon.storagemanager.downloaddatafrom' }"
 | 
				
			||||||
 | 
					                                    [statusSubject]="module.name" />
 | 
				
			||||||
 | 
					                                <ion-button fill="clear" (click)="deleteForModule($event, module)"
 | 
				
			||||||
 | 
					                                    *ngIf="!module.calculatingSize && module.totalSize > 0" color="danger">
 | 
				
			||||||
 | 
					                                    <ion-icon name="fas-trash" slot="icon-only" [attr.aria-label]="
 | 
				
			||||||
 | 
					                                        'addon.storagemanager.deletedatafrom' | translate: { name: module.name }" />
 | 
				
			||||||
 | 
					                                </ion-button>
 | 
				
			||||||
 | 
					                                <p *ngIf="!downloadEnabled || !module.handlerData?.showDownloadButton" class="sr-only">
 | 
				
			||||||
 | 
					                                    {{ 'core.notdownloadable' | translate }}
 | 
				
			||||||
 | 
					                                </p>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </ion-item>
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    </ng-container>
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					            </ion-card-content>
 | 
				
			||||||
 | 
					        </ion-accordion>
 | 
				
			||||||
 | 
					    </ion-card>
 | 
				
			||||||
 | 
					</ng-template>
 | 
				
			||||||
 | 
				
			|||||||
@ -17,11 +17,6 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .accordion-expanded {
 | 
					 | 
				
			||||||
            ion-item.card-header {
 | 
					 | 
				
			||||||
                --border-width: 0 0 1px 0;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ion-card-content {
 | 
					        ion-card-content {
 | 
				
			||||||
            padding: 0;
 | 
					            padding: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@
 | 
				
			|||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { CoreConstants, DownloadStatus } from '@/core/constants';
 | 
					import { CoreConstants, DownloadStatus } from '@/core/constants';
 | 
				
			||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
 | 
					import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit } from '@angular/core';
 | 
				
			||||||
import { CoreCourse, CoreCourseProvider } from '@features/course/services/course';
 | 
					import { CoreCourse, CoreCourseProvider } from '@features/course/services/course';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    CoreCourseHelper,
 | 
					    CoreCourseHelper,
 | 
				
			||||||
@ -25,7 +25,7 @@ import {
 | 
				
			|||||||
    CoreCourseModulePrefetchDelegate,
 | 
					    CoreCourseModulePrefetchDelegate,
 | 
				
			||||||
    CoreCourseModulePrefetchHandler } from '@features/course/services/module-prefetch-delegate';
 | 
					    CoreCourseModulePrefetchHandler } from '@features/course/services/module-prefetch-delegate';
 | 
				
			||||||
import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses';
 | 
					import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses';
 | 
				
			||||||
import { AccordionGroupChangeEventDetail, IonAccordionGroup } from '@ionic/angular';
 | 
					import { AccordionGroupChangeEventDetail } from '@ionic/angular';
 | 
				
			||||||
import { CoreLoadings } from '@services/loadings';
 | 
					import { CoreLoadings } from '@services/loadings';
 | 
				
			||||||
import { CoreNavigator } from '@services/navigator';
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
import { CoreSites } from '@services/sites';
 | 
					import { CoreSites } from '@services/sites';
 | 
				
			||||||
@ -47,14 +47,13 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
					export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ViewChild('accordionGroup', { static: true }) accordionGroup!: IonAccordionGroup;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    courseId!: number;
 | 
					    courseId!: number;
 | 
				
			||||||
    title = '';
 | 
					    title = '';
 | 
				
			||||||
    loaded = false;
 | 
					    loaded = false;
 | 
				
			||||||
    sections: AddonStorageManagerCourseSection[] = [];
 | 
					    sections: AddonStorageManagerCourseSection[] = [];
 | 
				
			||||||
    totalSize = 0;
 | 
					    totalSize = 0;
 | 
				
			||||||
    calculatingSize = true;
 | 
					    calculatingSize = true;
 | 
				
			||||||
 | 
					    accordionMultipleValue: string[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    downloadEnabled = false;
 | 
					    downloadEnabled = false;
 | 
				
			||||||
    downloadCourseEnabled = false;
 | 
					    downloadCourseEnabled = false;
 | 
				
			||||||
@ -116,7 +115,8 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const sections = (await CoreCourse.getSections(this.courseId, false, true))
 | 
					        const sections = (await CoreCourse.getSections(this.courseId, false, true))
 | 
				
			||||||
            .filter((section) => !CoreCourseHelper.isSectionStealth(section));
 | 
					            .filter((section) => !CoreCourseHelper.isSectionStealth(section));
 | 
				
			||||||
        this.sections = (await CoreCourseHelper.addHandlerDataForModules(sections, this.courseId)).sections
 | 
					
 | 
				
			||||||
 | 
					        const sectionsToRender = (await CoreCourseHelper.addHandlerDataForModules(sections, this.courseId)).sections
 | 
				
			||||||
            .map(section => ({
 | 
					            .map(section => ({
 | 
				
			||||||
                ...section,
 | 
					                ...section,
 | 
				
			||||||
                totalSize: 0,
 | 
					                totalSize: 0,
 | 
				
			||||||
@ -128,15 +128,29 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
                })),
 | 
					                })),
 | 
				
			||||||
            }));
 | 
					            }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const subSections = sectionsToRender.filter((section) => section.component === 'mod_subsection');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.sections = sectionsToRender.filter((section) => section.component !== 'mod_subsection');
 | 
				
			||||||
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
 | 
					            section.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                if (module.modname === 'subsection') {
 | 
				
			||||||
 | 
					                    module.subSection = subSections.find((section) =>
 | 
				
			||||||
 | 
					                        section.component === 'mod_subsection' && section.itemid === module.instance);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.loaded = true;
 | 
					        this.loaded = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.accordionGroup.value = String(initialSectionId);
 | 
					        if (initialSectionId !== undefined) {
 | 
				
			||||||
 | 
					            this.accordionMultipleValue.push(initialSectionId.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        CoreDom.scrollToElement(
 | 
					            CoreDom.scrollToElement(
 | 
				
			||||||
            this.elementRef.nativeElement,
 | 
					                this.elementRef.nativeElement,
 | 
				
			||||||
            '.accordion-expanded',
 | 
					                `#addons-course-storage-${initialSectionId}`,
 | 
				
			||||||
            { addYAxis: -10 },
 | 
					                { addYAxis: -10 },
 | 
				
			||||||
        );
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await Promise.all([
 | 
					        await Promise.all([
 | 
				
			||||||
            this.initSizes(),
 | 
					            this.initSizes(),
 | 
				
			||||||
@ -158,7 +172,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Listen for changes in course status.
 | 
					        // Listen for changes in course status.
 | 
				
			||||||
        this.courseStatusObserver = CoreEvents.on(CoreEvents.COURSE_STATUS_CHANGED, (data) => {
 | 
					        this.courseStatusObserver = CoreEvents.on(CoreEvents.COURSE_STATUS_CHANGED, (data) => {
 | 
				
			||||||
            if (data.courseId == this.courseId || data.courseId == CoreCourseProvider.ALL_COURSES_CLEARED) {
 | 
					            if (data.courseId === this.courseId || data.courseId === CoreCourseProvider.ALL_COURSES_CLEARED) {
 | 
				
			||||||
                this.updateCourseStatus(data.status);
 | 
					                this.updateCourseStatus(data.status);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }, CoreSites.getCurrentSiteId());
 | 
					        }, CoreSites.getCurrentSiteId());
 | 
				
			||||||
@ -213,17 +227,20 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Get the affected section.
 | 
					                // Get the affected section.
 | 
				
			||||||
                const section = this.sections.find(section => section.id == data.sectionId);
 | 
					                const sectionFinder = CoreCourseHelper.findSectionWithSubsection(this.sections, data.sectionId);
 | 
				
			||||||
                if (!section) {
 | 
					                if (!sectionFinder?.section) {
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Recalculate the status.
 | 
					                // Recalculate the status.
 | 
				
			||||||
                await CoreCourseHelper.calculateSectionStatus(section, this.courseId, false);
 | 
					                await CoreCourseHelper.calculateSectionStatus(sectionFinder.section, this.courseId, false);
 | 
				
			||||||
 | 
					                if (sectionFinder.subSection) {
 | 
				
			||||||
 | 
					                    await CoreCourseHelper.calculateSectionStatus(sectionFinder.subSection, this.courseId, false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (section.isDownloading && !CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
					                if (sectionFinder.section.isDownloading && !CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
				
			||||||
                    // All the modules are now downloading, set a download all promise.
 | 
					                    // All the modules are now downloading, set a download all promise.
 | 
				
			||||||
                    this.prefecthSection(section);
 | 
					                    this.prefecthSection(sectionFinder.section);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            CoreSites.getCurrentSiteId(),
 | 
					            CoreSites.getCurrentSiteId(),
 | 
				
			||||||
@ -233,36 +250,46 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
        CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
 | 
					        CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.sections.forEach((section) => {
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
            section.modules.forEach((module) => {
 | 
					            this.calculateModulesStatusOnSection(section);
 | 
				
			||||||
                if (module.handlerData?.showDownloadButton) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    module.spinner = true;
 | 
					 | 
				
			||||||
                    // Listen for changes on this module status, even if download isn't enabled.
 | 
					 | 
				
			||||||
                    module.prefetchHandler = CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(module.modname);
 | 
					 | 
				
			||||||
                    this.calculateModuleStatus(module);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.moduleStatusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
 | 
					        this.moduleStatusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
 | 
				
			||||||
            let module: AddonStorageManagerModule | undefined;
 | 
					            let moduleFound: AddonStorageManagerModule | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.sections.some((section) => {
 | 
					            this.sections.some((section) =>
 | 
				
			||||||
                module = section.modules.find((module) =>
 | 
					                section.modules.some((module) => {
 | 
				
			||||||
                    module.id == data.componentId && module.prefetchHandler && data.component == module.prefetchHandler?.component);
 | 
					                    if (module.subSection) {
 | 
				
			||||||
 | 
					                        return module.subSection.modules.some((module) => {
 | 
				
			||||||
 | 
					                            if (module.id === data.componentId &&
 | 
				
			||||||
 | 
					                                module.prefetchHandler &&
 | 
				
			||||||
 | 
					                                data.component === module.prefetchHandler?.component) {
 | 
				
			||||||
 | 
					                                moduleFound = module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return !!module;
 | 
					                                return true;
 | 
				
			||||||
            });
 | 
					                            }
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        if (module.id === data.componentId &&
 | 
				
			||||||
 | 
					                            module.prefetchHandler &&
 | 
				
			||||||
 | 
					                            data.component === module.prefetchHandler?.component) {
 | 
				
			||||||
 | 
					                            moduleFound = module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!module) {
 | 
					                            return true;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!moduleFound) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Call determineModuleStatus to get the right status to display.
 | 
					            // Call determineModuleStatus to get the right status to display.
 | 
				
			||||||
            const status = CoreCourseModulePrefetchDelegate.determineModuleStatus(module, data.status);
 | 
					            const status = CoreCourseModulePrefetchDelegate.determineModuleStatus(moduleFound, data.status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Update the status.
 | 
					            // Update the status.
 | 
				
			||||||
            this.updateModuleStatus(module, status);
 | 
					            this.updateModuleStatus(moduleFound, status);
 | 
				
			||||||
        }, CoreSites.getCurrentSiteId());
 | 
					        }, CoreSites.getCurrentSiteId());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -270,50 +297,24 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
     * Init section, course and modules sizes.
 | 
					     * Init section, course and modules sizes.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected async initSizes(): Promise<void> {
 | 
					    protected async initSizes(): Promise<void> {
 | 
				
			||||||
        await Promise.all(this.sections.map(async (section) => {
 | 
					        const modules = this.getAllModulesList();
 | 
				
			||||||
            await Promise.all(section.modules.map(async (module) => {
 | 
					        await Promise.all(modules.map(async (module) => {
 | 
				
			||||||
                // Note: This function only gets the size for modules which are downloadable.
 | 
					            await this.calculateModuleSize(module);
 | 
				
			||||||
                // For other modules it always returns 0, even if they have downloaded some files.
 | 
					 | 
				
			||||||
                // However there is no 100% reliable way to actually track the files in this case.
 | 
					 | 
				
			||||||
                // You can maybe guess it based on the component and componentid.
 | 
					 | 
				
			||||||
                // But these aren't necessarily consistent, for example mod_frog vs mmaModFrog.
 | 
					 | 
				
			||||||
                // There is nothing enforcing correct values.
 | 
					 | 
				
			||||||
                // Most modules which have large files are downloadable, so I think this is sufficient.
 | 
					 | 
				
			||||||
                const size = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, this.courseId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // There are some cases where the return from this is not a valid number.
 | 
					 | 
				
			||||||
                if (!isNaN(size)) {
 | 
					 | 
				
			||||||
                    module.totalSize = Number(size);
 | 
					 | 
				
			||||||
                    section.totalSize += size;
 | 
					 | 
				
			||||||
                    this.totalSize += size;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                module.calculatingSize = false;
 | 
					 | 
				
			||||||
            }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            section.calculatingSize = false;
 | 
					 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.calculatingSize = false;
 | 
					        await this.updateModulesSizes(modules);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Mark course as not downloaded if course size is 0.
 | 
					 | 
				
			||||||
        if (this.totalSize == 0) {
 | 
					 | 
				
			||||||
            this.markCourseAsNotDownloaded();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Update the sizes of some modules.
 | 
					     * Update the sizes of some modules.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param modules Modules.
 | 
					     * @param modules Modules.
 | 
				
			||||||
     * @param section Section the modules belong to.
 | 
					 | 
				
			||||||
     * @returns Promise resolved when done.
 | 
					     * @returns Promise resolved when done.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected async updateModulesSizes(
 | 
					    protected async updateModulesSizes(modules: AddonStorageManagerModule[]): Promise<void> {
 | 
				
			||||||
        modules: AddonStorageManagerModule[],
 | 
					 | 
				
			||||||
        section?: AddonStorageManagerCourseSection,
 | 
					 | 
				
			||||||
    ): Promise<void> {
 | 
					 | 
				
			||||||
        this.calculatingSize = true;
 | 
					        this.calculatingSize = true;
 | 
				
			||||||
 | 
					        let section: AddonStorageManagerCourseSection | undefined;
 | 
				
			||||||
 | 
					        let subSection: AddonStorageManagerCourseSection | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await Promise.all(modules.map(async (module) => {
 | 
					        await Promise.all(modules.map(async (module) => {
 | 
				
			||||||
            if (module.calculatingSize) {
 | 
					            if (module.calculatingSize) {
 | 
				
			||||||
@ -321,38 +322,59 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            module.calculatingSize = true;
 | 
					            module.calculatingSize = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const sectionFinder = CoreCourseHelper.findSectionWithSubsection(this.sections, module.section);
 | 
				
			||||||
 | 
					            section = sectionFinder?.section;
 | 
				
			||||||
 | 
					            if (section) {
 | 
				
			||||||
 | 
					                section.calculatingSize = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                subSection = sectionFinder?.subSection;
 | 
				
			||||||
 | 
					                if (subSection) {
 | 
				
			||||||
 | 
					                    subSection.calculatingSize = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            this.changeDetectorRef.markForCheck();
 | 
					            this.changeDetectorRef.markForCheck();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!section) {
 | 
					            await this.calculateModuleSize(module);
 | 
				
			||||||
                section = this.sections.find((section) => section.modules.some((mod) => mod.id === module.id));
 | 
					 | 
				
			||||||
                if (section) {
 | 
					 | 
				
			||||||
                    section.calculatingSize = true;
 | 
					 | 
				
			||||||
                    this.changeDetectorRef.markForCheck();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                const size = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, this.courseId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const diff = (isNaN(size) ? 0 : size) - (module.totalSize ?? 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                module.totalSize = Number(size);
 | 
					 | 
				
			||||||
                this.totalSize += diff;
 | 
					 | 
				
			||||||
                if (section) {
 | 
					 | 
				
			||||||
                    section.totalSize += diff;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } catch {
 | 
					 | 
				
			||||||
                // Ignore errors, it shouldn't happen.
 | 
					 | 
				
			||||||
            } finally {
 | 
					 | 
				
			||||||
                module.calculatingSize = false;
 | 
					 | 
				
			||||||
                this.changeDetectorRef.markForCheck();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.calculatingSize = false;
 | 
					        // Update section and total sizes.
 | 
				
			||||||
        if (section) {
 | 
					        this.totalSize = 0;
 | 
				
			||||||
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
 | 
					            section.totalSize = 0;
 | 
				
			||||||
 | 
					            section.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                if (module.subSection) {
 | 
				
			||||||
 | 
					                    const subSection = module.subSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    subSection.totalSize = 0;
 | 
				
			||||||
 | 
					                    subSection.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                        if (module.totalSize && module.totalSize > 0) {
 | 
				
			||||||
 | 
					                            subSection.totalSize += module.totalSize;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    subSection.calculatingSize = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    section.totalSize += module.subSection.totalSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (module.totalSize && module.totalSize > 0) {
 | 
				
			||||||
 | 
					                    section.totalSize += module.totalSize;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            section.calculatingSize = false;
 | 
					            section.calculatingSize = false;
 | 
				
			||||||
 | 
					            this.totalSize += section.totalSize;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.calculatingSize = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Mark course as not downloaded if course size is 0.
 | 
				
			||||||
 | 
					        if (this.totalSize === 0) {
 | 
				
			||||||
 | 
					            this.markCourseAsNotDownloaded();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.changeDetectorRef.markForCheck();
 | 
					        this.changeDetectorRef.markForCheck();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -380,16 +402,9 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const modules: AddonStorageManagerModule[] = [];
 | 
					        const modules = this.getAllModulesList().filter((module) => module.totalSize && module.totalSize > 0);
 | 
				
			||||||
        this.sections.forEach((section) => {
 | 
					 | 
				
			||||||
            section.modules.forEach((module) => {
 | 
					 | 
				
			||||||
                if (module.totalSize && module.totalSize > 0) {
 | 
					 | 
				
			||||||
                    modules.push(module);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.deleteModules(modules);
 | 
					        await this.deleteModules(modules);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -419,12 +434,22 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const modules: AddonStorageManagerModule[] = [];
 | 
					        const modules: AddonStorageManagerModule[] = [];
 | 
				
			||||||
        section.modules.forEach((module) => {
 | 
					        section.modules.forEach((module) => {
 | 
				
			||||||
 | 
					            if (module.subSection) {
 | 
				
			||||||
 | 
					                module.subSection.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                    if (module.totalSize && module.totalSize > 0) {
 | 
				
			||||||
 | 
					                        modules.push(module);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (module.totalSize && module.totalSize > 0) {
 | 
					            if (module.totalSize && module.totalSize > 0) {
 | 
				
			||||||
                modules.push(module);
 | 
					                modules.push(module);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.deleteModules(modules, section);
 | 
					        await this.deleteModules(modules);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -432,12 +457,10 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param event Event object.
 | 
					     * @param event Event object.
 | 
				
			||||||
     * @param module Module details
 | 
					     * @param module Module details
 | 
				
			||||||
     * @param section Section the module belongs to.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async deleteForModule(
 | 
					    async deleteForModule(
 | 
				
			||||||
        event: Event,
 | 
					        event: Event,
 | 
				
			||||||
        module: AddonStorageManagerModule,
 | 
					        module: AddonStorageManagerModule,
 | 
				
			||||||
        section: AddonStorageManagerCourseSection,
 | 
					 | 
				
			||||||
    ): Promise<void> {
 | 
					    ): Promise<void> {
 | 
				
			||||||
        event.stopPropagation();
 | 
					        event.stopPropagation();
 | 
				
			||||||
        event.preventDefault();
 | 
					        event.preventDefault();
 | 
				
			||||||
@ -459,35 +482,23 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.deleteModules([module], section);
 | 
					        await this.deleteModules([module]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Deletes the specified modules, showing the loading overlay while it happens.
 | 
					     * Deletes the specified modules, showing the loading overlay while it happens.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param modules Modules to delete
 | 
					     * @param modules Modules to delete
 | 
				
			||||||
     * @param section Section the modules belong to.
 | 
					 | 
				
			||||||
     * @returns Promise<void> Once deleting has finished
 | 
					     * @returns Promise<void> Once deleting has finished
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected async deleteModules(modules: AddonStorageManagerModule[], section?: AddonStorageManagerCourseSection): Promise<void> {
 | 
					    protected async deleteModules(modules: AddonStorageManagerModule[]): Promise<void> {
 | 
				
			||||||
        const modal = await CoreLoadings.show('core.deleting', true);
 | 
					        const modal = await CoreLoadings.show('core.deleting', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const promises: Promise<void>[] = [];
 | 
					        const promises = modules.map(async (module) => {
 | 
				
			||||||
        modules.forEach((module) => {
 | 
					 | 
				
			||||||
            // Remove the files.
 | 
					            // Remove the files.
 | 
				
			||||||
            const promise = CoreCourseHelper.removeModuleStoredData(module, this.courseId).then(() => {
 | 
					            await CoreCourseHelper.removeModuleStoredData(module, this.courseId);
 | 
				
			||||||
                const moduleSize = module.totalSize || 0;
 | 
					 | 
				
			||||||
                // When the files and cache are removed, update the size.
 | 
					 | 
				
			||||||
                if (section) {
 | 
					 | 
				
			||||||
                    section.totalSize -= moduleSize;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                this.totalSize -= moduleSize;
 | 
					 | 
				
			||||||
                module.totalSize = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return;
 | 
					            module.totalSize = 0;
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            promises.push(promise);
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@ -497,13 +508,9 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
            modal.dismiss();
 | 
					            modal.dismiss();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await this.updateModulesSizes(modules, section);
 | 
					            await this.updateModulesSizes(modules);
 | 
				
			||||||
            CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
 | 
					            CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // For delete all, reset all section sizes so icons are updated.
 | 
					 | 
				
			||||||
            if (this.totalSize === 0) {
 | 
					 | 
				
			||||||
                this.sections.map(section => section.totalSize = 0);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            this.changeDetectorRef.markForCheck();
 | 
					            this.changeDetectorRef.markForCheck();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -551,8 +558,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
                    CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
 | 
					                    CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } finally {
 | 
					            } finally {
 | 
				
			||||||
                await this.updateModulesSizes(section.modules, section);
 | 
					                await this.updateModulesSizes(section.modules);
 | 
				
			||||||
                this.changeDetectorRef.markForCheck();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (error) {
 | 
					        } catch (error) {
 | 
				
			||||||
            // User cancelled or there was an error calculating the size.
 | 
					            // User cancelled or there was an error calculating the size.
 | 
				
			||||||
@ -602,7 +608,6 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
            module.spinner = false;
 | 
					            module.spinner = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await this.updateModulesSizes([module]);
 | 
					            await this.updateModulesSizes([module]);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -624,6 +629,24 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
        this.changeDetectorRef.markForCheck();
 | 
					        this.changeDetectorRef.markForCheck();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Calculate all modules status on a section.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param section Section to check.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async calculateModulesStatusOnSection(section: AddonStorageManagerCourseSection): Promise<void> {
 | 
				
			||||||
 | 
					        await Promise.all(section.modules.map(async (module) => {
 | 
				
			||||||
 | 
					            if (module.subSection) {
 | 
				
			||||||
 | 
					                await this.calculateModulesStatusOnSection(module.subSection);
 | 
				
			||||||
 | 
					            } else if (module.handlerData?.showDownloadButton) {
 | 
				
			||||||
 | 
					                module.spinner = true;
 | 
				
			||||||
 | 
					                // Listen for changes on this module status, even if download isn't enabled.
 | 
				
			||||||
 | 
					                module.prefetchHandler = CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(module.modname);
 | 
				
			||||||
 | 
					                await this.calculateModuleStatus(module);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Calculate and show module status.
 | 
					     * Calculate and show module status.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@ -664,6 +687,12 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
        this.changeDetectorRef.markForCheck();
 | 
					        this.changeDetectorRef.markForCheck();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the course object.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param courseId Course ID.
 | 
				
			||||||
 | 
					     * @returns Promise resolved with the course object if found.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    protected async getCourse(courseId: number): Promise<CoreCourseAnyCourseData | undefined> {
 | 
					    protected async getCourse(courseId: number): Promise<CoreCourseAnyCourseData | undefined> {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // Check if user is enrolled. If enrolled, no guest access.
 | 
					            // Check if user is enrolled. If enrolled, no guest access.
 | 
				
			||||||
@ -709,8 +738,9 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
                    isGuest: this.isGuest,
 | 
					                    isGuest: this.isGuest,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            await Promise.all(this.sections.map(section => this.updateModulesSizes(section.modules, section)));
 | 
					
 | 
				
			||||||
            this.changeDetectorRef.markForCheck();
 | 
					            const modules = this.getAllModulesList();
 | 
				
			||||||
 | 
					            await this.updateModulesSizes(modules);
 | 
				
			||||||
        } catch (error) {
 | 
					        } catch (error) {
 | 
				
			||||||
            if (this.isDestroyed) {
 | 
					            if (this.isDestroyed) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@ -720,19 +750,76 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all modules list.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @returns All modules list.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected getAllModulesList(): AddonStorageManagerModule[] {
 | 
				
			||||||
 | 
					        const modules: AddonStorageManagerModule[] = [];
 | 
				
			||||||
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
 | 
					            section.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                if (module.subSection) {
 | 
				
			||||||
 | 
					                    module.subSection.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                        modules.push(module);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                modules.push(module);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return modules;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Calculate the size of a module.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module Module to calculate.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async calculateModuleSize(module: AddonStorageManagerModule): Promise<void> {
 | 
				
			||||||
 | 
					        module.calculatingSize = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Note: This function only gets the size for modules which are downloadable.
 | 
				
			||||||
 | 
					        // For other modules it always returns 0, even if they have downloaded some files.
 | 
				
			||||||
 | 
					        // However there is no 100% reliable way to actually track the files in this case.
 | 
				
			||||||
 | 
					        // You can maybe guess it based on the component and componentid.
 | 
				
			||||||
 | 
					        // But these aren't necessarily consistent, for example mod_frog vs mmaModFrog.
 | 
				
			||||||
 | 
					        // There is nothing enforcing correct values.
 | 
				
			||||||
 | 
					        // Most modules which have large files are downloadable, so I think this is sufficient.
 | 
				
			||||||
 | 
					        const size = await CoreUtils.ignoreErrors(CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, this.courseId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (size !== undefined) {
 | 
				
			||||||
 | 
					            // There are some cases where the return from this is not a valid number.
 | 
				
			||||||
 | 
					            module.totalSize = !isNaN(size) ? Number(size) : 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.changeDetectorRef.markForCheck();
 | 
				
			||||||
 | 
					        module.calculatingSize = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Toggle expand status.
 | 
					     * Toggle expand status.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param event Event object.
 | 
					     * @param event Event object.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    accordionGroupChange(event: AccordionGroupChangeEventDetail): void {
 | 
					    accordionGroupChange(event: AccordionGroupChangeEventDetail): void {
 | 
				
			||||||
 | 
					        const sectionIds = event.value as string[] | [];
 | 
				
			||||||
        this.sections.forEach((section) => {
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
            section.expanded = false;
 | 
					            section.expanded = false;
 | 
				
			||||||
 | 
					            section.modules.forEach((section) => {
 | 
				
			||||||
 | 
					                if (section.subSection) {
 | 
				
			||||||
 | 
					                    section.subSection.expanded = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        event.value.forEach((sectionId) => {
 | 
					
 | 
				
			||||||
            const section = this.sections.find((section) => section.id === Number(sectionId));
 | 
					        sectionIds.forEach((sectionId) => {
 | 
				
			||||||
            if (section) {
 | 
					            const sectionToExpand = CoreCourseHelper.findSectionById(this.sections, Number(sectionId));
 | 
				
			||||||
                section.expanded = true;
 | 
					            if (sectionToExpand) {
 | 
				
			||||||
 | 
					                sectionToExpand.expanded = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -748,6 +835,10 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.sections.forEach((section) => {
 | 
					        this.sections.forEach((section) => {
 | 
				
			||||||
            section.modules.forEach((module) => {
 | 
					            section.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                module.subSection?.modules.forEach((module) => {
 | 
				
			||||||
 | 
					                    module.handlerData?.onDestroy?.();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                module.handlerData?.onDestroy?.();
 | 
					                module.handlerData?.onDestroy?.();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -769,4 +860,5 @@ type AddonStorageManagerModule = CoreCourseModuleData & {
 | 
				
			|||||||
    prefetchHandler?: CoreCourseModulePrefetchHandler;
 | 
					    prefetchHandler?: CoreCourseModulePrefetchHandler;
 | 
				
			||||||
    spinner?: boolean;
 | 
					    spinner?: boolean;
 | 
				
			||||||
    downloadStatus?: DownloadStatus;
 | 
					    downloadStatus?: DownloadStatus;
 | 
				
			||||||
 | 
					    subSection?: AddonStorageManagerCourseSection;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1600,7 +1600,7 @@ export class CoreCourseHelperProvider {
 | 
				
			|||||||
     * @param siteId Site ID. If not defined, current site.
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
     * @returns Promise resolved when the download finishes.
 | 
					     * @returns Promise resolved when the download finishes.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async prefetchCourse(
 | 
					    protected async prefetchCourse(
 | 
				
			||||||
        course: CoreCourseAnyCourseData,
 | 
					        course: CoreCourseAnyCourseData,
 | 
				
			||||||
        sections: CoreCourseWSSection[],
 | 
					        sections: CoreCourseWSSection[],
 | 
				
			||||||
        courseHandlers: CoreCourseOptionsHandlerToDisplay[],
 | 
					        courseHandlers: CoreCourseOptionsHandlerToDisplay[],
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user