MOBILE-4660 storagemanager: Prefetch subsections
This commit is contained in:
		
							parent
							
								
									a3bb081f60
								
							
						
					
					
						commit
						d3c3c56296
					
				
							
								
								
									
										15
									
								
								src/addons/mod/subsection/constants.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/addons/mod/subsection/constants.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
export const ADDON_MOD_SUBSECTION_COMPONENT = 'mmaModSubsection';
 | 
			
		||||
							
								
								
									
										98
									
								
								src/addons/mod/subsection/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/addons/mod/subsection/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,98 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler';
 | 
			
		||||
import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSSection } from '@features/course/services/course';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { ADDON_MOD_SUBSECTION_COMPONENT } from '../../constants';
 | 
			
		||||
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreFileSizeSum } from '@services/plugin-file-delegate';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to prefetch subsections.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModSubsectionPrefetchHandlerService extends CoreCourseResourcePrefetchHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModSubsection';
 | 
			
		||||
    modName = 'subsection';
 | 
			
		||||
    component = ADDON_MOD_SUBSECTION_COMPONENT;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    protected async performDownloadOrPrefetch(
 | 
			
		||||
        siteId: string,
 | 
			
		||||
        module: CoreCourseModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const section = await this.getSection(module, courseId, siteId);
 | 
			
		||||
        if (!section) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await CoreCourseHelper.prefetchSections([section], courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getDownloadSize(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreFileSizeSum> {
 | 
			
		||||
        const section = await this.getSection(module, courseId);
 | 
			
		||||
        if (!section) {
 | 
			
		||||
            return { size: 0, total: true };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return await CoreCourseModulePrefetchDelegate.getDownloadSize(section.modules, courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getDownloadedSize(module: CoreCourseAnyModuleData, courseId: number): Promise<number> {
 | 
			
		||||
        const section = await this.getSection(module, courseId);
 | 
			
		||||
        if (!section) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return CoreCourseHelper.getModulesDownloadedSize(section.modules, courseId);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the section of a module.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @returns Promise resolved with the section if found.
 | 
			
		||||
     */
 | 
			
		||||
    protected async getSection(
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreCourseWSSection | undefined> {
 | 
			
		||||
        siteId = siteId ?? CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const sections = await CoreCourse.getSections(courseId, false, true, undefined, siteId);
 | 
			
		||||
 | 
			
		||||
        return sections.find((section) =>
 | 
			
		||||
            section.component === 'mod_subsection' && section.itemid === module.instance);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModSubsectionPrefetchHandler = makeSingleton(AddonModSubsectionPrefetchHandlerService);
 | 
			
		||||
@ -15,6 +15,8 @@
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { AddonModSubsectionIndexLinkHandler } from './services/handlers/index-link';
 | 
			
		||||
import { AddonModSubsectionPrefetchHandler } from './services/handlers/prefetch';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    providers: [
 | 
			
		||||
@ -23,6 +25,7 @@ import { AddonModSubsectionIndexLinkHandler } from './services/handlers/index-li
 | 
			
		||||
            multi: true,
 | 
			
		||||
            useValue: () => {
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModSubsectionIndexLinkHandler.instance);
 | 
			
		||||
                CoreCourseModulePrefetchDelegate.registerHandler(AddonModSubsectionPrefetchHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@
 | 
			
		||||
                <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)"
 | 
			
		||||
                            [status]="section.downloadStatus" [enabled]="true" (action)="prefetchSection(section)"
 | 
			
		||||
                            [loading]="section.isDownloading || section.isCalculating" [canTrustDownload]="true"
 | 
			
		||||
                            [statusesTranslatable]="{notdownloaded: 'addon.storagemanager.downloaddatafrom' }"
 | 
			
		||||
                            [statusSubject]="section.name" />
 | 
			
		||||
 | 
			
		||||
@ -30,10 +30,10 @@ import { CoreLoadings } from '@services/loadings';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreArray } from '@singletons/array';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreEventObserver, CoreEvents, CoreEventSectionStatusChangedData } from '@singletons/events';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the amount of file storage used by each activity on the course, and allows
 | 
			
		||||
@ -120,11 +120,12 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
            .map(section => ({
 | 
			
		||||
                ...section,
 | 
			
		||||
                totalSize: 0,
 | 
			
		||||
                calculatingSize: true,
 | 
			
		||||
                calculatingSize: false,
 | 
			
		||||
                expanded: section.id === initialSectionId,
 | 
			
		||||
                modules: section.modules.map(module => ({
 | 
			
		||||
                    ...module,
 | 
			
		||||
                    calculatingSize: true,
 | 
			
		||||
                    totalSize: 0,
 | 
			
		||||
                    calculatingSize: false,
 | 
			
		||||
                })),
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
@ -162,8 +163,6 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Init course prefetch information.
 | 
			
		||||
     *
 | 
			
		||||
     * @returns Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async initCoursePrefetch(): Promise<void> {
 | 
			
		||||
        if (!this.downloadCourseEnabled || this.courseStatusObserver) {
 | 
			
		||||
@ -180,7 +179,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
        // Determine the course prefetch status.
 | 
			
		||||
        await this.determineCoursePrefetchIcon();
 | 
			
		||||
 | 
			
		||||
        if (this.prefetchCourseData.icon != CoreConstants.ICON_LOADING) {
 | 
			
		||||
        if (this.prefetchCourseData.icon !== CoreConstants.ICON_LOADING) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -203,8 +202,6 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Init module prefetch information.
 | 
			
		||||
     *
 | 
			
		||||
     * @returns Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async initModulePrefetch(): Promise<void> {
 | 
			
		||||
        if (!this.downloadEnabled || this.sectionStatusObserver) {
 | 
			
		||||
@ -219,46 +216,44 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Check if the affected section is being downloaded.
 | 
			
		||||
                // If so, we don't update section status because it'll already be updated when the download finishes.
 | 
			
		||||
                const downloadId = CoreCourseHelper.getSectionDownloadId({ id: data.sectionId });
 | 
			
		||||
                if (CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Get the affected section.
 | 
			
		||||
                const sectionFinder = CoreCourseHelper.findSectionWithSubsection(this.sections, data.sectionId);
 | 
			
		||||
                if (!sectionFinder?.section) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Recalculate the status.
 | 
			
		||||
                await CoreCourseHelper.calculateSectionStatus(sectionFinder.section, this.courseId, false);
 | 
			
		||||
                if (sectionFinder.subSection) {
 | 
			
		||||
                    await CoreCourseHelper.calculateSectionStatus(sectionFinder.subSection, this.courseId, false);
 | 
			
		||||
                const section = sectionFinder.section;
 | 
			
		||||
 | 
			
		||||
                // Check if the affected section is being downloaded.
 | 
			
		||||
                // If so, we don't update section status because it'll already be updated when the download finishes.
 | 
			
		||||
                const downloadId = CoreCourseHelper.getSectionDownloadId({ id: section.id });
 | 
			
		||||
                if (CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (sectionFinder.section.isDownloading && !CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
			
		||||
                // Recalculate the status.
 | 
			
		||||
                await this.updateSizes([section]);
 | 
			
		||||
 | 
			
		||||
                if (section.isDownloading && !CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
			
		||||
                    // All the modules are now downloading, set a download all promise.
 | 
			
		||||
                    this.prefecthSection(sectionFinder.section);
 | 
			
		||||
                    this.prefetchSection(section);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            CoreSites.getCurrentSiteId(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // The download status of a section might have been changed from within a module page.
 | 
			
		||||
        CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
 | 
			
		||||
 | 
			
		||||
        this.sections.forEach((section) => {
 | 
			
		||||
            this.calculateModulesStatusOnSection(section);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.moduleStatusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
 | 
			
		||||
            let moduleFound: AddonStorageManagerModule | undefined;
 | 
			
		||||
 | 
			
		||||
            this.sections.some((section) =>
 | 
			
		||||
                section.modules.some((module) => {
 | 
			
		||||
                    if (module.subSection) {
 | 
			
		||||
                    if (module.id === data.componentId &&
 | 
			
		||||
                        module.prefetchHandler &&
 | 
			
		||||
                        data.component === module.prefetchHandler?.component) {
 | 
			
		||||
                        moduleFound = module;
 | 
			
		||||
 | 
			
		||||
                        return true;
 | 
			
		||||
                    } else if (module.subSection) {
 | 
			
		||||
                        return module.subSection.modules.some((module) => {
 | 
			
		||||
                            if (module.id === data.componentId &&
 | 
			
		||||
                                module.prefetchHandler &&
 | 
			
		||||
@ -268,14 +263,6 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
                                return true;
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if (module.id === data.componentId &&
 | 
			
		||||
                            module.prefetchHandler &&
 | 
			
		||||
                            data.component === module.prefetchHandler?.component) {
 | 
			
		||||
                            moduleFound = module;
 | 
			
		||||
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return false;
 | 
			
		||||
@ -287,87 +274,81 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
            // Call determineModuleStatus to get the right status to display.
 | 
			
		||||
            const status = CoreCourseModulePrefetchDelegate.determineModuleStatus(moduleFound, data.status);
 | 
			
		||||
            if (moduleFound.subSection) {
 | 
			
		||||
                const data: CoreEventSectionStatusChangedData = {
 | 
			
		||||
                    sectionId: moduleFound.subSection.id,
 | 
			
		||||
                    courseId: this.courseId,
 | 
			
		||||
                };
 | 
			
		||||
                CoreEvents.trigger(CoreEvents.SECTION_STATUS_CHANGED, data, CoreSites.getCurrentSiteId());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update the status.
 | 
			
		||||
            this.updateModuleStatus(moduleFound, status);
 | 
			
		||||
        }, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        // The download status of a section might have been changed from within a module page.
 | 
			
		||||
        this.updateSizes(this.sections);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Init section, course and modules sizes.
 | 
			
		||||
     */
 | 
			
		||||
    protected async initSizes(): Promise<void> {
 | 
			
		||||
        const modules = this.getAllModulesList();
 | 
			
		||||
        await Promise.all(modules.map(async (module) => {
 | 
			
		||||
            await this.calculateModuleSize(module);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        await this.updateModulesSizes(modules);
 | 
			
		||||
        await this.updateSizes(this.sections);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update the sizes of some modules.
 | 
			
		||||
     * Update the sizes of some sections and modules.
 | 
			
		||||
     *
 | 
			
		||||
     * @param modules Modules.
 | 
			
		||||
     * @returns Promise resolved when done.
 | 
			
		||||
     * @param sections Modules.
 | 
			
		||||
     */
 | 
			
		||||
    protected async updateModulesSizes(modules: AddonStorageManagerModule[]): Promise<void> {
 | 
			
		||||
    protected async updateSizes(sections: AddonStorageManagerCourseSection[]): Promise<void> {
 | 
			
		||||
        sections = CoreArray.unique(sections, 'id');
 | 
			
		||||
 | 
			
		||||
        this.calculatingSize = true;
 | 
			
		||||
        let section: AddonStorageManagerCourseSection | undefined;
 | 
			
		||||
        let subSection: AddonStorageManagerCourseSection | undefined;
 | 
			
		||||
 | 
			
		||||
        await Promise.all(modules.map(async (module) => {
 | 
			
		||||
            if (module.calculatingSize) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
        sections.forEach((section) => {
 | 
			
		||||
            section.calculatingSize = true;
 | 
			
		||||
            section.modules.map((module) => {
 | 
			
		||||
                if (module.subSection) {
 | 
			
		||||
                    module.subSection.calculatingSize = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this.changeDetectorRef.markForCheck();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.changeDetectorRef.markForCheck();
 | 
			
		||||
 | 
			
		||||
        // Update only affected module sections.
 | 
			
		||||
        const modules = this.getAllModulesList(sections);
 | 
			
		||||
        await Promise.all(modules.map(async (module) => {
 | 
			
		||||
            await this.calculateModuleSize(module);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        const updateSectionSize = (section: AddonStorageManagerCourseSection): void => {
 | 
			
		||||
            section.totalSize = 0;
 | 
			
		||||
            section.calculatingSize = true;
 | 
			
		||||
 | 
			
		||||
            this.changeDetectorRef.markForCheck();
 | 
			
		||||
 | 
			
		||||
            section.modules.forEach((module) => {
 | 
			
		||||
                if (module.subSection) {
 | 
			
		||||
                    updateSectionSize(module.subSection);
 | 
			
		||||
                    module.totalSize = module.subSection.totalSize;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                section.totalSize += module.totalSize ?? 0;
 | 
			
		||||
                this.changeDetectorRef.markForCheck();
 | 
			
		||||
            });
 | 
			
		||||
            section.calculatingSize = false;
 | 
			
		||||
 | 
			
		||||
            this.changeDetectorRef.markForCheck();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Update section and total sizes.
 | 
			
		||||
        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;
 | 
			
		||||
            updateSectionSize(section);
 | 
			
		||||
            this.totalSize += section.totalSize;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.calculatingSize = false;
 | 
			
		||||
 | 
			
		||||
        // Mark course as not downloaded if course size is 0.
 | 
			
		||||
@ -376,6 +357,9 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.changeDetectorRef.markForCheck();
 | 
			
		||||
 | 
			
		||||
        await this.calculateSectionsStatus(sections);
 | 
			
		||||
        this.changeDetectorRef.markForCheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -402,7 +386,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const modules = this.getAllModulesList().filter((module) => module.totalSize && module.totalSize > 0);
 | 
			
		||||
        const modules = this.getAllModulesList(this.sections).filter((module) => module.totalSize && module.totalSize > 0);
 | 
			
		||||
 | 
			
		||||
        await this.deleteModules(modules);
 | 
			
		||||
    }
 | 
			
		||||
@ -489,16 +473,21 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
     * Deletes the specified modules, showing the loading overlay while it happens.
 | 
			
		||||
     *
 | 
			
		||||
     * @param modules Modules to delete
 | 
			
		||||
     * @returns Promise<void> Once deleting has finished
 | 
			
		||||
     */
 | 
			
		||||
    protected async deleteModules(modules: AddonStorageManagerModule[]): Promise<void> {
 | 
			
		||||
        const modal = await CoreLoadings.show('core.deleting', true);
 | 
			
		||||
 | 
			
		||||
        const sections: AddonStorageManagerCourseSection[]  = [];
 | 
			
		||||
        const promises = modules.map(async (module) => {
 | 
			
		||||
            // Remove the files.
 | 
			
		||||
            await CoreCourseHelper.removeModuleStoredData(module, this.courseId);
 | 
			
		||||
 | 
			
		||||
            module.totalSize = 0;
 | 
			
		||||
 | 
			
		||||
            const sectionFinder = CoreCourseHelper.findSectionWithSubsection(this.sections, module.section);
 | 
			
		||||
            if (sectionFinder?.section) {
 | 
			
		||||
                sections.push(sectionFinder?.section);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@ -508,8 +497,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
        } finally {
 | 
			
		||||
            modal.dismiss();
 | 
			
		||||
 | 
			
		||||
            await this.updateModulesSizes(modules);
 | 
			
		||||
            CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
 | 
			
		||||
            await this.updateSizes(sections);
 | 
			
		||||
 | 
			
		||||
            this.changeDetectorRef.markForCheck();
 | 
			
		||||
        }
 | 
			
		||||
@ -526,39 +514,26 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
        CoreCourse.setCourseStatus(this.courseId, DownloadStatus.DOWNLOADABLE_NOT_DOWNLOADED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the status of sections.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh If refresh or not.
 | 
			
		||||
     */
 | 
			
		||||
    protected calculateSectionsStatus(refresh?: boolean): void {
 | 
			
		||||
        if (!this.sections) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreUtils.ignoreErrors(CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, refresh));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Confirm and prefetch a section. If the section is "all sections", prefetch all the sections.
 | 
			
		||||
     *
 | 
			
		||||
     * @param section Section to download.
 | 
			
		||||
     */
 | 
			
		||||
    async prefecthSection(section: AddonStorageManagerCourseSection): Promise<void> {
 | 
			
		||||
    async prefetchSection(section: AddonStorageManagerCourseSection): Promise<void> {
 | 
			
		||||
        section.isCalculating = true;
 | 
			
		||||
        this.changeDetectorRef.markForCheck();
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreCourseHelper.confirmDownloadSizeSection(this.courseId, section, this.sections);
 | 
			
		||||
            await CoreCourseHelper.confirmDownloadSizeSection(this.courseId, [section]);
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await CoreCourseHelper.prefetchSection(section, this.courseId, this.sections);
 | 
			
		||||
                await CoreCourseHelper.prefetchSections([section], this.courseId);
 | 
			
		||||
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                if (!this.isDestroyed) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
 | 
			
		||||
                }
 | 
			
		||||
            } finally {
 | 
			
		||||
                await this.updateModulesSizes(section.modules);
 | 
			
		||||
                await this.updateSizes([section]);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // User cancelled or there was an error calculating the size.
 | 
			
		||||
@ -594,12 +569,9 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Get download size to ask for confirm if it's high.
 | 
			
		||||
 | 
			
		||||
            const size = await module.prefetchHandler.getDownloadSize(module, module.course, true);
 | 
			
		||||
 | 
			
		||||
            await CoreCourseHelper.prefetchModule(module.prefetchHandler, module, size, module.course, refresh);
 | 
			
		||||
 | 
			
		||||
            CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (!this.isDestroyed) {
 | 
			
		||||
                CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true);
 | 
			
		||||
@ -607,7 +579,10 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
        } finally {
 | 
			
		||||
            module.spinner = false;
 | 
			
		||||
 | 
			
		||||
            await this.updateModulesSizes([module]);
 | 
			
		||||
            const sectionFinder = CoreCourseHelper.findSectionWithSubsection(this.sections, module.section);
 | 
			
		||||
            if (sectionFinder?.section) {
 | 
			
		||||
                await this.updateSizes([sectionFinder?.section]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -636,37 +611,23 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
     */
 | 
			
		||||
    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) {
 | 
			
		||||
            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);
 | 
			
		||||
                const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(module, this.courseId);
 | 
			
		||||
 | 
			
		||||
                this.updateModuleStatus(module, status);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (module.subSection) {
 | 
			
		||||
                await this.calculateModulesStatusOnSection(module.subSection);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate and show module status.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module to update.
 | 
			
		||||
     * @returns Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async calculateModuleStatus(module: AddonStorageManagerModule): Promise<void> {
 | 
			
		||||
        if (!module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(module, this.courseId);
 | 
			
		||||
 | 
			
		||||
        this.updateModuleStatus(module, status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines the prefetch icon of the course.
 | 
			
		||||
     *
 | 
			
		||||
     * @returns Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async determineCoursePrefetchIcon(): Promise<void> {
 | 
			
		||||
        this.prefetchCourseData = await CoreCourseHelper.getCourseStatusIconAndTitle(this.courseId);
 | 
			
		||||
@ -739,8 +700,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const modules = this.getAllModulesList();
 | 
			
		||||
            await this.updateModulesSizes(modules);
 | 
			
		||||
            await this.updateSizes(this.sections);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (this.isDestroyed) {
 | 
			
		||||
                return;
 | 
			
		||||
@ -753,21 +713,20 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all modules list.
 | 
			
		||||
     *
 | 
			
		||||
     * @param sections Sections to get the modules from.
 | 
			
		||||
     * @returns All modules list.
 | 
			
		||||
     */
 | 
			
		||||
    protected getAllModulesList(): AddonStorageManagerModule[] {
 | 
			
		||||
    protected getAllModulesList(sections: AddonStorageManagerCourseSection[]): AddonStorageManagerModule[] {
 | 
			
		||||
        const modules: AddonStorageManagerModule[] = [];
 | 
			
		||||
        this.sections.forEach((section) => {
 | 
			
		||||
        sections.forEach((section) => {
 | 
			
		||||
            section.modules.forEach((module) => {
 | 
			
		||||
                modules.push(module);
 | 
			
		||||
 | 
			
		||||
                if (module.subSection) {
 | 
			
		||||
                    module.subSection.modules.forEach((module) => {
 | 
			
		||||
                        modules.push(module);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                modules.push(module);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -775,12 +734,17 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the size of a module.
 | 
			
		||||
     * Calculate the size of the modules.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module to calculate.
 | 
			
		||||
     */
 | 
			
		||||
    protected async calculateModuleSize(module: AddonStorageManagerModule): Promise<void> {
 | 
			
		||||
        if (module.calculatingSize) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        module.calculatingSize = true;
 | 
			
		||||
        this.changeDetectorRef.markForCheck();
 | 
			
		||||
 | 
			
		||||
        // 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.
 | 
			
		||||
@ -789,15 +753,10 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
        // 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));
 | 
			
		||||
        module.totalSize = await 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;
 | 
			
		||||
        this.changeDetectorRef.markForCheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -845,6 +804,39 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
 | 
			
		||||
        this.isDestroyed = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the status of a list of sections, setting attributes to determine the icons/data to be shown.
 | 
			
		||||
     *
 | 
			
		||||
     * @param sections Sections to calculate their status.
 | 
			
		||||
     */
 | 
			
		||||
    protected async calculateSectionsStatus(
 | 
			
		||||
        sections: AddonStorageManagerCourseSection[],
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        if (!sections) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Promise.all(sections.map(async (section) => {
 | 
			
		||||
            if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                section.isCalculating = true;
 | 
			
		||||
                await this.calculateModulesStatusOnSection(section);
 | 
			
		||||
                await CoreCourseHelper.calculateSectionStatus(section, this.courseId, false, false);
 | 
			
		||||
 | 
			
		||||
                await Promise.all(section.modules.map(async (module) => {
 | 
			
		||||
                    if (module.subSection) {
 | 
			
		||||
                        return CoreCourseHelper.calculateSectionStatus(module.subSection, this.courseId, false, false);
 | 
			
		||||
                    }
 | 
			
		||||
                }));
 | 
			
		||||
            } finally {
 | 
			
		||||
                section.isCalculating = false;
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AddonStorageManagerCourseSection = Omit<CoreCourseSectionWithStatus, 'modules'> & {
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreQueueRunner } from '@classes/queue-runner';
 | 
			
		||||
import { CoreCourse, CoreCourseProvider } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseHelper } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
import { CoreCourses, CoreEnrolledCourseData } from '@features/courses/services/courses';
 | 
			
		||||
import { CoreSettingsHelper, CoreSiteSpaceUsage } from '@features/settings/services/settings-helper';
 | 
			
		||||
import { CoreSiteHome } from '@features/sitehome/services/sitehome';
 | 
			
		||||
@ -241,14 +240,8 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
 | 
			
		||||
    private async calculateDownloadedCourseSize(courseId: number): Promise<number> {
 | 
			
		||||
        const sections = await CoreCourse.getSections(courseId);
 | 
			
		||||
        const modules = CoreCourseHelper.getSectionsModules(sections);
 | 
			
		||||
        const promisedModuleSizes = modules.map(async (module) => {
 | 
			
		||||
            const size = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, courseId);
 | 
			
		||||
 | 
			
		||||
            return isNaN(size) ? 0 : size;
 | 
			
		||||
        });
 | 
			
		||||
        const moduleSizes = await Promise.all(promisedModuleSizes);
 | 
			
		||||
 | 
			
		||||
        return moduleSizes.reduce((totalSize, moduleSize) => totalSize + moduleSize, 0);
 | 
			
		||||
        return CoreCourseHelper.getModulesDownloadedSize(modules, courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -142,11 +142,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const moduleSize = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(this.module, this.courseId);
 | 
			
		||||
 | 
			
		||||
                if (moduleSize) {
 | 
			
		||||
                    this.size = moduleSize;
 | 
			
		||||
                }
 | 
			
		||||
                this.size = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(this.module, this.courseId);
 | 
			
		||||
            }, 1000);
 | 
			
		||||
 | 
			
		||||
            this.fileStatusObserver = CoreEvents.on(
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ import {
 | 
			
		||||
    CoreCourseModuleCompletionStatus,
 | 
			
		||||
    CoreCourseGetContentsWSModule,
 | 
			
		||||
    sectionContentIsModule,
 | 
			
		||||
    CoreCourseAnyModuleData,
 | 
			
		||||
} from './course';
 | 
			
		||||
import { CoreConstants, DownloadStatus, ContextLevel } from '@/core/constants';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
@ -283,14 +284,14 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
        refresh?: boolean,
 | 
			
		||||
        checkUpdates: boolean = true,
 | 
			
		||||
    ): Promise<{statusData: CoreCourseModulesStatus; section: CoreCourseSectionWithStatus}> {
 | 
			
		||||
        if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
        if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
            throw new CoreError('Invalid section');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const sectionWithStatus = <CoreCourseSectionWithStatus> section;
 | 
			
		||||
 | 
			
		||||
        // Get the status of this section.
 | 
			
		||||
        const result = await CoreCourseModulePrefetchDelegate.getModulesStatus(
 | 
			
		||||
        const statusData = await CoreCourseModulePrefetchDelegate.getModulesStatus(
 | 
			
		||||
            section.contents,
 | 
			
		||||
            courseId,
 | 
			
		||||
            section.id,
 | 
			
		||||
@ -302,13 +303,13 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
        // Check if it's being downloaded.
 | 
			
		||||
        const downloadId = this.getSectionDownloadId(section);
 | 
			
		||||
        if (CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
			
		||||
            result.status = DownloadStatus.DOWNLOADING;
 | 
			
		||||
            statusData.status = DownloadStatus.DOWNLOADING;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sectionWithStatus.downloadStatus = result.status;
 | 
			
		||||
        sectionWithStatus.downloadStatus = statusData.status;
 | 
			
		||||
 | 
			
		||||
        // Set this section data.
 | 
			
		||||
        if (result.status !== DownloadStatus.DOWNLOADING) {
 | 
			
		||||
        if (statusData.status !== DownloadStatus.DOWNLOADING) {
 | 
			
		||||
            sectionWithStatus.isDownloading = false;
 | 
			
		||||
            sectionWithStatus.total = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
@ -320,62 +321,7 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return { statusData: result, section: sectionWithStatus };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the status of a list of sections, setting attributes to determine the icons/data to be shown.
 | 
			
		||||
     *
 | 
			
		||||
     * @param sections Sections to calculate their status.
 | 
			
		||||
     * @param courseId Course ID the sections belong to.
 | 
			
		||||
     * @param refresh True if it shouldn't use module status cache (slower).
 | 
			
		||||
     * @param checkUpdates Whether to use the WS to check updates. Defaults to true.
 | 
			
		||||
     * @returns Promise resolved when the states are calculated.
 | 
			
		||||
     */
 | 
			
		||||
    async calculateSectionsStatus(
 | 
			
		||||
        sections: CoreCourseSection[],
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        refresh?: boolean,
 | 
			
		||||
        checkUpdates: boolean = true,
 | 
			
		||||
    ): Promise<CoreCourseSectionWithStatus[]> {
 | 
			
		||||
        let allSectionsSection: CoreCourseSectionWithStatus | undefined;
 | 
			
		||||
        let allSectionsStatus = DownloadStatus.NOT_DOWNLOADABLE as DownloadStatus;
 | 
			
		||||
 | 
			
		||||
        const promises = sections.map(async (section: CoreCourseSectionWithStatus) => {
 | 
			
		||||
            section.isCalculating = true;
 | 
			
		||||
 | 
			
		||||
            if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
                // "All sections" section status is calculated using the status of the rest of sections.
 | 
			
		||||
                allSectionsSection = section;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                const result = await this.calculateSectionStatus(section, courseId, refresh, checkUpdates);
 | 
			
		||||
 | 
			
		||||
                // Calculate "All sections" status.
 | 
			
		||||
                allSectionsStatus = CoreFilepool.determinePackagesStatus(allSectionsStatus, result.statusData.status);
 | 
			
		||||
            } finally {
 | 
			
		||||
                section.isCalculating = false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
            if (allSectionsSection) {
 | 
			
		||||
                // Set "All sections" data.
 | 
			
		||||
                allSectionsSection.downloadStatus = allSectionsStatus;
 | 
			
		||||
                allSectionsSection.isDownloading = allSectionsStatus === DownloadStatus.DOWNLOADING;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return sections;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (allSectionsSection) {
 | 
			
		||||
                allSectionsSection.isCalculating = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return { statusData, section: sectionWithStatus };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -411,7 +357,7 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Confirm the download.
 | 
			
		||||
            await this.confirmDownloadSizeSection(course.id, undefined, options.sections, true);
 | 
			
		||||
            await this.confirmDownloadSizeSection(course.id, options.sections, true);
 | 
			
		||||
 | 
			
		||||
            // User confirmed, get the course handlers if needed.
 | 
			
		||||
            if (!options.courseHandlers) {
 | 
			
		||||
@ -508,48 +454,36 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
     * Calculate the size to download a section and show a confirm modal if needed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param courseId Course ID the section belongs to.
 | 
			
		||||
     * @param section Section. If not provided, all sections.
 | 
			
		||||
     * @param sections List of sections. Used when downloading all the sections.
 | 
			
		||||
     * @param sections List of sections to download
 | 
			
		||||
     * @param alwaysConfirm True to show a confirm even if the size isn't high, false otherwise.
 | 
			
		||||
     * @returns Promise resolved if the user confirms or there's no need to confirm.
 | 
			
		||||
     */
 | 
			
		||||
    async confirmDownloadSizeSection(
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        section?: CoreCourseWSSection,
 | 
			
		||||
        sections?: CoreCourseWSSection[],
 | 
			
		||||
        alwaysConfirm?: boolean,
 | 
			
		||||
        sections: CoreCourseWSSection[] = [],
 | 
			
		||||
        alwaysConfirm = false,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        let hasEmbeddedFiles = false;
 | 
			
		||||
        let sizeSum: CoreFileSizeSum = {
 | 
			
		||||
        const sizeSum: CoreFileSizeSum = {
 | 
			
		||||
            size: 0,
 | 
			
		||||
            total: true,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Calculate the size of the download.
 | 
			
		||||
        if (section && section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
            sizeSum = await CoreCourseModulePrefetchDelegate.getDownloadSize(section.contents, courseId);
 | 
			
		||||
        await Promise.all(sections.map(async (section) => {
 | 
			
		||||
            if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const sectionSize = await CoreCourseModulePrefetchDelegate.getDownloadSize(section.modules, courseId);
 | 
			
		||||
 | 
			
		||||
            sizeSum.total = sizeSum.total && sectionSize.total;
 | 
			
		||||
            sizeSum.size += sectionSize.size;
 | 
			
		||||
 | 
			
		||||
            // Check if the section has embedded files in the description.
 | 
			
		||||
            hasEmbeddedFiles = CoreFilepool.extractDownloadableFilesFromHtml(section.summary).length > 0;
 | 
			
		||||
        } else if (sections) {
 | 
			
		||||
            await Promise.all(sections.map(async (section) => {
 | 
			
		||||
                if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const sectionSize = await CoreCourseModulePrefetchDelegate.getDownloadSize(section.contents, courseId);
 | 
			
		||||
 | 
			
		||||
                sizeSum.total = sizeSum.total && sectionSize.total;
 | 
			
		||||
                sizeSum.size += sectionSize.size;
 | 
			
		||||
 | 
			
		||||
                // Check if the section has embedded files in the description.
 | 
			
		||||
                if (!hasEmbeddedFiles && CoreFilepool.extractDownloadableFilesFromHtml(section.summary).length > 0) {
 | 
			
		||||
                    hasEmbeddedFiles = true;
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new CoreError('Either section or list of sections needs to be supplied.');
 | 
			
		||||
        }
 | 
			
		||||
            if (!hasEmbeddedFiles && CoreFilepool.extractDownloadableFilesFromHtml(section.summary).length > 0) {
 | 
			
		||||
                hasEmbeddedFiles = true;
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (hasEmbeddedFiles) {
 | 
			
		||||
            sizeSum.total = false;
 | 
			
		||||
@ -559,6 +493,20 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
        await CoreDomUtils.confirmDownloadSize(sizeSum, undefined, undefined, undefined, undefined, alwaysConfirm);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sums the stored module sizes.
 | 
			
		||||
     *
 | 
			
		||||
     * @param modules List of modules.
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @returns Promise resolved with the sum of the stored sizes.
 | 
			
		||||
     */
 | 
			
		||||
    async getModulesDownloadedSize(modules: CoreCourseAnyModuleData[], courseId: number): Promise<number> {
 | 
			
		||||
        const moduleSizes = await Promise.all(modules.map(async (module) =>
 | 
			
		||||
            await CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, courseId)));
 | 
			
		||||
 | 
			
		||||
        return moduleSizes.reduce((totalSize, moduleSize) => totalSize + moduleSize, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a course is accessed using guest access and if it requires user input to enter.
 | 
			
		||||
     *
 | 
			
		||||
@ -1350,20 +1298,18 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
            await CoreUtils.ignoreErrors(CoreCourseModulePrefetchDelegate.invalidateCourseUpdates(courseId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const results = await Promise.all([
 | 
			
		||||
        const [size, status, packageData] = await Promise.all([
 | 
			
		||||
            CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, courseId),
 | 
			
		||||
            CoreCourseModulePrefetchDelegate.getModuleStatus(module, courseId),
 | 
			
		||||
            this.getModulePackageLastDownloaded(module, component),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        // Treat stored size.
 | 
			
		||||
        const size = results[0];
 | 
			
		||||
        const sizeReadable = CoreText.bytesToSize(results[0], 2);
 | 
			
		||||
        const sizeReadable = CoreText.bytesToSize(size, 2);
 | 
			
		||||
 | 
			
		||||
        // Treat module status.
 | 
			
		||||
        const status = results[1];
 | 
			
		||||
        let statusIcon: string | undefined;
 | 
			
		||||
        switch (results[1]) {
 | 
			
		||||
        switch (status) {
 | 
			
		||||
            case DownloadStatus.DOWNLOADABLE_NOT_DOWNLOADED:
 | 
			
		||||
                statusIcon = CoreConstants.ICON_NOT_DOWNLOADED;
 | 
			
		||||
                break;
 | 
			
		||||
@ -1380,8 +1326,6 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const packageData = results[2];
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            size,
 | 
			
		||||
            sizeReadable,
 | 
			
		||||
@ -1625,12 +1569,7 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
 | 
			
		||||
            const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
            // Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections".
 | 
			
		||||
            let allSectionsSection: CoreCourseWSSection = sections[0];
 | 
			
		||||
            if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
                allSectionsSection = this.createAllSectionsSection();
 | 
			
		||||
            }
 | 
			
		||||
            promises.push(this.prefetchSection(allSectionsSection, course.id, sections));
 | 
			
		||||
            promises.push(this.prefetchSections(sections, course.id, true));
 | 
			
		||||
 | 
			
		||||
            // Prefetch course options.
 | 
			
		||||
            courseHandlers.forEach((handler) => {
 | 
			
		||||
@ -1700,41 +1639,32 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch one section or all the sections.
 | 
			
		||||
     * If the section is "All sections" it will prefetch all the sections.
 | 
			
		||||
     * Prefetch some sections
 | 
			
		||||
     *
 | 
			
		||||
     * @param section Section.
 | 
			
		||||
     * @param sections List of sections. .
 | 
			
		||||
     * @param courseId Course ID the section belongs to.
 | 
			
		||||
     * @param sections List of sections. Used when downloading all the sections.
 | 
			
		||||
     * @returns Promise resolved when the prefetch is finished.
 | 
			
		||||
     * @param updateAllSections Update all sections status
 | 
			
		||||
     */
 | 
			
		||||
    async prefetchSection(
 | 
			
		||||
        section: CoreCourseSectionWithStatus,
 | 
			
		||||
    async prefetchSections(
 | 
			
		||||
        sections: (CoreCourseSectionWithStatus & CoreCourseSectionWithSubsections)[],
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        sections?: CoreCourseSectionWithStatus[],
 | 
			
		||||
        updateAllSections = false,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
            try {
 | 
			
		||||
                // Download only this section.
 | 
			
		||||
                await this.prefetchSingleSectionIfNeeded(section, courseId);
 | 
			
		||||
            } finally {
 | 
			
		||||
                // Calculate the status of the section that finished.
 | 
			
		||||
                await this.calculateSectionStatus(section, courseId, false, false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!sections) {
 | 
			
		||||
            throw new CoreError('List of sections is required when downloading all sections.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Download all the sections except "All sections".
 | 
			
		||||
        let allSectionsStatus = DownloadStatus.NOT_DOWNLOADABLE as DownloadStatus;
 | 
			
		||||
        let allSectionsSection: (CoreCourseSectionWithStatus) | undefined;
 | 
			
		||||
        if (updateAllSections) {
 | 
			
		||||
            // Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections".
 | 
			
		||||
            allSectionsSection = sections[0];
 | 
			
		||||
            if (sections[0].id !== CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
                allSectionsSection = this.createAllSectionsSection();
 | 
			
		||||
            }
 | 
			
		||||
            allSectionsSection.isDownloading = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        section.isDownloading = true;
 | 
			
		||||
        const promises = sections.map(async (section) => {
 | 
			
		||||
            if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
            // Download all the sections except "All sections".
 | 
			
		||||
            if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -1753,10 +1683,14 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
            await CoreUtils.allPromises(promises);
 | 
			
		||||
 | 
			
		||||
            // Set "All sections" data.
 | 
			
		||||
            section.downloadStatus = allSectionsStatus;
 | 
			
		||||
            section.isDownloading = allSectionsStatus === DownloadStatus.DOWNLOADING;
 | 
			
		||||
            if (allSectionsSection) {
 | 
			
		||||
                allSectionsSection.downloadStatus = allSectionsStatus;
 | 
			
		||||
                allSectionsSection.isDownloading = allSectionsStatus === DownloadStatus.DOWNLOADING;
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            section.isDownloading = false;
 | 
			
		||||
            if (allSectionsSection) {
 | 
			
		||||
                allSectionsSection.isDownloading = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1769,7 +1703,7 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
     * @returns Promise resolved when the section is prefetched.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchSingleSectionIfNeeded(section: CoreCourseSectionWithStatus, courseId: number): Promise<void> {
 | 
			
		||||
        if (section.id == CoreCourseProvider.ALL_SECTIONS_ID || section.hiddenbynumsections) {
 | 
			
		||||
        if (section.id === CoreCourseProvider.ALL_SECTIONS_ID || section.hiddenbynumsections) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1830,7 +1764,7 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
        result: CoreCourseModulesStatus,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
        if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -472,17 +472,21 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate<CoreCo
 | 
			
		||||
     * @returns Promise resolved with the total size (0 if unknown)
 | 
			
		||||
     */
 | 
			
		||||
    async getModuleStoredSize(module: CoreCourseAnyModuleData, courseId: number): Promise<number> {
 | 
			
		||||
        const site = CoreSites.getCurrentSite();
 | 
			
		||||
        const handler = this.getPrefetchHandlerFor(module.modname);
 | 
			
		||||
        try {
 | 
			
		||||
            const site = CoreSites.getCurrentSite();
 | 
			
		||||
            const handler = this.getPrefetchHandlerFor(module.modname);
 | 
			
		||||
 | 
			
		||||
        const [downloadedSize, cachedSize] = await Promise.all([
 | 
			
		||||
            this.getModuleDownloadedSize(module, courseId),
 | 
			
		||||
            handler && site ? site.getComponentCacheSize(handler.component, module.id) : 0,
 | 
			
		||||
        ]);
 | 
			
		||||
            const [downloadedSize, cachedSize] = await Promise.all([
 | 
			
		||||
                this.getModuleDownloadedSize(module, courseId),
 | 
			
		||||
                handler && site ? site.getComponentCacheSize(handler.component, module.id) : 0,
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
        const totalSize = cachedSize + downloadedSize;
 | 
			
		||||
            const totalSize = cachedSize + downloadedSize;
 | 
			
		||||
 | 
			
		||||
        return isNaN(totalSize) ? 0 : totalSize;
 | 
			
		||||
            return isNaN(totalSize) ? 0 : totalSize;
 | 
			
		||||
        } catch {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user