MOBILE-3659 course: Implement prefetch delegate
This commit is contained in:
		
							parent
							
								
									d14540218a
								
							
						
					
					
						commit
						031f238117
					
				| @ -201,6 +201,7 @@ const appConfig = { | |||||||
|         'no-duplicate-imports': 'error', |         'no-duplicate-imports': 'error', | ||||||
|         'no-empty': 'error', |         'no-empty': 'error', | ||||||
|         'no-eval': 'error', |         'no-eval': 'error', | ||||||
|  |         'no-fallthrough': 'off', | ||||||
|         'no-invalid-this': 'error', |         'no-invalid-this': 'error', | ||||||
|         'no-irregular-whitespace': 'error', |         'no-irregular-whitespace': 'error', | ||||||
|         'no-multiple-empty-lines': 'error', |         'no-multiple-empty-lines': 'error', | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ import { CoreSites } from '@services/sites'; | |||||||
| import { CoreCourse, CoreCourseSection } from '@features/course/services/course'; | import { CoreCourse, CoreCourseSection } from '@features/course/services/course'; | ||||||
| import { CoreCourseHelper } from '@features/course/services/course-helper'; | import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||||
| import { CoreSiteHome, FrontPageItemNames } from '@features/sitehome/services/sitehome'; | import { CoreSiteHome, FrontPageItemNames } from '@features/sitehome/services/sitehome'; | ||||||
| // @todo import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||||
| import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component'; | import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -63,7 +63,7 @@ export class AddonBlockSiteMainMenuComponent extends CoreBlockBaseComponent impl | |||||||
| 
 | 
 | ||||||
|         if (this.mainMenuBlock && this.mainMenuBlock.modules) { |         if (this.mainMenuBlock && this.mainMenuBlock.modules) { | ||||||
|             // Invalidate modules prefetch data.
 |             // Invalidate modules prefetch data.
 | ||||||
|             // @todo promises.push(this.prefetchDelegate.invalidateModules(this.mainMenuBlock.modules, this.siteHomeId));
 |             promises.push(CoreCourseModulePrefetchDelegate.instance.invalidateModules(this.mainMenuBlock.modules, this.siteHomeId)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         await Promise.all(promises); |         await Promise.all(promises); | ||||||
|  | |||||||
| @ -144,7 +144,7 @@ | |||||||
|         <ng-container *ngFor="let module of section.modules"> |         <ng-container *ngFor="let module of section.modules"> | ||||||
|             <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [courseId]="course?.id" |             <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [courseId]="course?.id" | ||||||
|                 [downloadEnabled]="downloadEnabled" [section]="section" (completionChanged)="onCompletionChange($event)" |                 [downloadEnabled]="downloadEnabled" [section]="section" (completionChanged)="onCompletionChange($event)" | ||||||
|                 (statusChanged)="onModuleStatusChange($event)"> |                 (statusChanged)="onModuleStatusChange()"> | ||||||
|             </core-course-module> |             </core-course-module> | ||||||
|         </ng-container> |         </ng-container> | ||||||
|     </section> |     </section> | ||||||
|  | |||||||
| @ -37,15 +37,13 @@ import { | |||||||
|     CoreCourseModuleData, |     CoreCourseModuleData, | ||||||
|     CoreCourseProvider, |     CoreCourseProvider, | ||||||
| } from '@features/course/services/course'; | } from '@features/course/services/course'; | ||||||
| // import { CoreCourseHelper } from '@features/course/services/course-helper';
 | import { CoreCourseHelper, CoreCourseSectionFormatted, CoreCourseSectionWithStatus } from '@features/course/services/course-helper'; | ||||||
| import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; | import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; | ||||||
| import { CoreEventObserver, CoreEvents, CoreEventSectionStatusChangedData, CoreEventSelectCourseTabData } from '@singletons/events'; | import { CoreEventObserver, CoreEvents, CoreEventSectionStatusChangedData, CoreEventSelectCourseTabData } from '@singletons/events'; | ||||||
| import { IonContent, IonRefresher } from '@ionic/angular'; | import { IonContent, IonRefresher } from '@ionic/angular'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils } from '@services/utils/utils'; | ||||||
| // import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
 | import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||||
| import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; | import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; | ||||||
| import { CoreCourseSectionFormatted } from '@features/course/services/course-helper'; |  | ||||||
| import { CoreCourseModuleStatusChangedData } from '../module/module'; |  | ||||||
| import { ModalController } from '@singletons'; | import { ModalController } from '@singletons'; | ||||||
| import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector'; | import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector'; | ||||||
| 
 | 
 | ||||||
| @ -62,13 +60,14 @@ import { CoreCourseSectionSelectorComponent } from '../section-selector/section- | |||||||
| @Component({ | @Component({ | ||||||
|     selector: 'core-course-format', |     selector: 'core-course-format', | ||||||
|     templateUrl: 'core-course-format.html', |     templateUrl: 'core-course-format.html', | ||||||
|  |     styleUrls: ['format.scss'], | ||||||
| }) | }) | ||||||
| export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||||
| 
 | 
 | ||||||
|     static readonly LOAD_MORE_ACTIVITIES = 20; // How many activities should load each time showMoreActivities is called.
 |     static readonly LOAD_MORE_ACTIVITIES = 20; // How many activities should load each time showMoreActivities is called.
 | ||||||
| 
 | 
 | ||||||
|     @Input() course?: CoreCourseAnyCourseData; // The course to render.
 |     @Input() course?: CoreCourseAnyCourseData; // The course to render.
 | ||||||
|     @Input() sections?: CoreCourseSectionFormatted[]; // List of course sections.
 |     @Input() sections?: CoreCourseSectionWithStatus[]; // List of course sections. The status will be calculated in this component.
 | ||||||
|     @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
 |     @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
 | ||||||
|     @Input() initialSectionId?: number; // The section to load first (by ID).
 |     @Input() initialSectionId?: number; // The section to load first (by ID).
 | ||||||
|     @Input() initialSectionNumber?: number; // The section to load first (by number).
 |     @Input() initialSectionNumber?: number; // The section to load first (by number).
 | ||||||
| @ -125,26 +124,26 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // @todo Check if the affected section is being downloaded.
 |                 // 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.
 |                 // If so, we don't update section status because it'll already be updated when the download finishes.
 | ||||||
|                 // const downloadId = CoreCourseHelper.instance.getSectionDownloadId({ id: data.sectionId });
 |                 const downloadId = CoreCourseHelper.instance.getSectionDownloadId({ id: data.sectionId }); | ||||||
|                 // if (prefetchDelegate.isBeingDownloaded(downloadId)) {
 |                 if (CoreCourseModulePrefetchDelegate.instance.isBeingDownloaded(downloadId)) { | ||||||
|                 //     return;
 |                     return; | ||||||
|                 // }
 |                 } | ||||||
| 
 | 
 | ||||||
|                 // Get the affected section.
 |                 // Get the affected section.
 | ||||||
|                 // const section = this.sections.find(section => section.id == data.sectionId);
 |                 const section = this.sections.find(section => section.id == data.sectionId); | ||||||
|                 // if (!section) {
 |                 if (!section) { | ||||||
|                 //     return;
 |                     return; | ||||||
|                 // }
 |                 } | ||||||
| 
 | 
 | ||||||
|                 // Recalculate the status.
 |                 // Recalculate the status.
 | ||||||
|                 // await CoreCourseHelper.instance.calculateSectionStatus(section, this.course.id, false);
 |                 await CoreCourseHelper.instance.calculateSectionStatus(section, this.course.id, false); | ||||||
| 
 | 
 | ||||||
|                 // if (section.isDownloading && !prefetchDelegate.isBeingDownloaded(downloadId)) {
 |                 if (section.isDownloading && !CoreCourseModulePrefetchDelegate.instance.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.prefetch(section);
 |                     this.prefetch(section); | ||||||
|                 // }
 |                 } | ||||||
|             }, |             }, | ||||||
|             CoreSites.instance.getCurrentSiteId(), |             CoreSites.instance.getCurrentSiteId(), | ||||||
|         ); |         ); | ||||||
| @ -392,7 +391,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|             this.canLoadMore = false; |             this.canLoadMore = false; | ||||||
|             this.showSectionId = 0; |             this.showSectionId = 0; | ||||||
|             this.showMoreActivities(); |             this.showMoreActivities(); | ||||||
|             // @todo CoreCourseHelper.instance.calculateSectionsStatus(this.sections, this.course.id, false, false);
 |             if (this.downloadEnabled) { | ||||||
|  |                 this.calculateSectionsStatus(false); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this.moduleId && typeof previousValue == 'undefined') { |         if (this.moduleId && typeof previousValue == 'undefined') { | ||||||
| @ -427,11 +428,12 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|      * |      * | ||||||
|      * @param refresh If refresh or not. |      * @param refresh If refresh or not. | ||||||
|      */ |      */ | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |  | ||||||
|     protected calculateSectionsStatus(refresh?: boolean): void { |     protected calculateSectionsStatus(refresh?: boolean): void { | ||||||
|         // @todo CoreCourseHelper.instance.calculateSectionsStatus(this.sections, this.course.id, refresh).catch(() => {
 |         if (!this.sections || !this.course) { | ||||||
|         //     // Ignore errors (shouldn't happen).
 |             return; | ||||||
|         // });
 |         } | ||||||
|  | 
 | ||||||
|  |         CoreUtils.instance.ignoreErrors(CoreCourseHelper.instance.calculateSectionsStatus(this.sections, this.course.id, refresh)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -440,38 +442,42 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|      * @param section Section to download. |      * @param section Section to download. | ||||||
|      * @param refresh Refresh clicked (not used). |      * @param refresh Refresh clicked (not used). | ||||||
|      */ |      */ | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |     async prefetch(section: CoreCourseSectionWithStatus): Promise<void> { | ||||||
|     prefetch(section: CoreCourseSectionFormatted): void { |         section.isCalculating = true; | ||||||
|         // section.isCalculating = true;
 | 
 | ||||||
|         // @todo CoreCourseHelper.instance.confirmDownloadSizeSection(this.course.id, section, this.sections).then(() => {
 |         try { | ||||||
|         //     this.prefetchSection(section, true);
 |             await CoreCourseHelper.instance.confirmDownloadSizeSection(this.course!.id, section, this.sections); | ||||||
|         // }, (error) => {
 | 
 | ||||||
|         //     // User cancelled or there was an error calculating the size.
 |             await this.prefetchSection(section, true); | ||||||
|         //     if (error) {
 |         } catch (error) { | ||||||
|         //         CoreDomUtils.instance.showErrorModal(error);
 |             // User cancelled or there was an error calculating the size.
 | ||||||
|         //     }
 |             if (error) { | ||||||
|         // }).finally(() => {
 |                 CoreDomUtils.instance.showErrorModal(error); | ||||||
|         //     section.isCalculating = false;
 |             } | ||||||
|         // });
 |         } finally { | ||||||
|  |             section.isCalculating = false; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Prefetch a section. @todo |      * Prefetch a section. | ||||||
|      * |      * | ||||||
|      * @param section The section to download. |      * @param section The section to download. | ||||||
|      * @param manual Whether the prefetch was started manually or it was automatically started because all modules |      * @param manual Whether the prefetch was started manually or it was automatically started because all modules | ||||||
|      *               are being downloaded. |      *               are being downloaded. | ||||||
|      */ |      */ | ||||||
|     // protected prefetchSection(section: Section, manual?: boolean): void {
 |     protected async prefetchSection(section: CoreCourseSectionWithStatus, manual?: boolean): Promise<void> { | ||||||
|     //     CoreCourseHelper.instance.prefetchSection(section, this.course.id, this.sections).catch((error) => {
 |         try { | ||||||
|     //         // Don't show error message if it's an automatic download.
 |             await CoreCourseHelper.instance.prefetchSection(section, this.course!.id, this.sections); | ||||||
|     //         if (!manual) {
 |         } catch (error) { | ||||||
|     //             return;
 |             // Don't show error message if it's an automatic download.
 | ||||||
|     //         }
 |             if (!manual) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|     //         CoreDomUtils.instance.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
 |             CoreDomUtils.instance.showErrorModalDefault(error, 'core.course.errordownloadingsection', true); | ||||||
|     //     });
 |         } | ||||||
|     // }
 |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Refresh the data. |      * Refresh the data. | ||||||
| @ -550,14 +556,16 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|             component.callComponentFunction('ionViewDidEnter'); |             component.callComponentFunction('ionViewDidEnter'); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         // @todo if (this.downloadEnabled) {
 |         if (!this.downloadEnabled || !this.course || !this.sections) { | ||||||
|         //     // The download status of a section might have been changed from within a module page.
 |             return; | ||||||
|         //     if (this.selectedSection && this.selectedSection.id !== CoreCourseProvider.ALL_SECTIONS_ID) {
 |         } | ||||||
|         //         CoreCourseHelper.instance.calculateSectionStatus(this.selectedSection, this.course.id, false, false);
 | 
 | ||||||
|         //     } else {
 |         // The download status of a section might have been changed from within a module page.
 | ||||||
|         //         CoreCourseHelper.instance.calculateSectionsStatus(this.sections, this.course.id, false, false);
 |         if (this.selectedSection && this.selectedSection.id !== CoreCourseProvider.ALL_SECTIONS_ID) { | ||||||
|         //     }
 |             CoreCourseHelper.instance.calculateSectionStatus(this.selectedSection, this.course.id, false, false); | ||||||
|         // }
 |         } else { | ||||||
|  |             CoreCourseHelper.instance.calculateSectionsStatus(this.sections, this.course.id, false, false); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -609,12 +617,13 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Recalculate the download status of each section, in response to a module being downloaded. |      * Recalculate the download status of each section, in response to a module being downloaded. | ||||||
|      * |  | ||||||
|      * @param eventData |  | ||||||
|      */ |      */ | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |     onModuleStatusChange(): void { | ||||||
|     onModuleStatusChange(eventData: CoreCourseModuleStatusChangedData): void { |         if (!this.downloadEnabled || !this.sections || !this.course) { | ||||||
|         // @todo CoreCourseHelper.instance.calculateSectionsStatus(this.sections, this.course.id, false, false);
 |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         CoreCourseHelper.instance.calculateSectionsStatus(this.sections, this.course.id, false, false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,13 +14,20 @@ | |||||||
| 
 | 
 | ||||||
| import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; | import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| // import { CoreSites } from '@services/sites';
 | import { CoreSites } from '@services/sites'; | ||||||
| // import { CoreDomUtils } from '@services/utils/dom';
 | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| // import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | import { CoreEventObserver, CoreEventPackageStatusChanged, CoreEvents } from '@singletons/events'; | ||||||
| import { CoreCourseModuleDataFormatted, CoreCourseSectionFormatted } from '@features/course/services/course-helper'; | import { | ||||||
|  |     CoreCourseHelper, | ||||||
|  |     CoreCourseModuleDataFormatted, | ||||||
|  |     CoreCourseSectionFormatted, | ||||||
|  | } from '@features/course/services/course-helper'; | ||||||
| import { CoreCourse, CoreCourseModuleCompletionData } from '@features/course/services/course'; | import { CoreCourse, CoreCourseModuleCompletionData } from '@features/course/services/course'; | ||||||
| import { CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate'; | import { CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate'; | ||||||
| // import { CoreCourseModulePrefetchDelegate, CoreCourseModulePrefetchHandler } from '../../providers/module-prefetch-delegate';
 | import { | ||||||
|  |     CoreCourseModulePrefetchDelegate, | ||||||
|  |     CoreCourseModulePrefetchHandler, | ||||||
|  | } from '@features/course/services/module-prefetch-delegate'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Component to display a module entry in a list of modules. |  * Component to display a module entry in a list of modules. | ||||||
| @ -32,6 +39,7 @@ import { CoreCourseModuleHandlerButton } from '@features/course/services/module- | |||||||
| @Component({ | @Component({ | ||||||
|     selector: 'core-course-module', |     selector: 'core-course-module', | ||||||
|     templateUrl: 'core-course-module.html', |     templateUrl: 'core-course-module.html', | ||||||
|  |     styleUrls: ['module.scss'], | ||||||
| }) | }) | ||||||
| export class CoreCourseModuleComponent implements OnInit, OnDestroy { | export class CoreCourseModuleComponent implements OnInit, OnDestroy { | ||||||
| 
 | 
 | ||||||
| @ -51,7 +59,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | |||||||
|         this.spinner = true; // Show spinner while calculating the status.
 |         this.spinner = true; // Show spinner while calculating the status.
 | ||||||
| 
 | 
 | ||||||
|         // Get current status to decide which icon should be shown.
 |         // Get current status to decide which icon should be shown.
 | ||||||
|         // @todo this.prefetchDelegate.getModuleStatus(this.module, this.courseId).then(this.showStatus.bind(this));
 |         this.calculateAndShowStatus(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     @Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when module completion changes.
 |     @Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when module completion changes.
 | ||||||
| @ -63,8 +71,8 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | |||||||
|     downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
 |     downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
 | ||||||
|     modNameTranslated = ''; |     modNameTranslated = ''; | ||||||
| 
 | 
 | ||||||
|     // protected prefetchHandler: CoreCourseModulePrefetchHandler;
 |     protected prefetchHandler?: CoreCourseModulePrefetchHandler; | ||||||
|     // protected statusObserver?: CoreEventObserver;
 |     protected statusObserver?: CoreEventObserver; | ||||||
|     protected statusCalculated = false; |     protected statusCalculated = false; | ||||||
|     protected isDestroyed = false; |     protected isDestroyed = false; | ||||||
| 
 | 
 | ||||||
| @ -86,26 +94,27 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | |||||||
|         this.module.handlerData.a11yTitle = this.module.handlerData.a11yTitle ?? this.module.handlerData.title; |         this.module.handlerData.a11yTitle = this.module.handlerData.a11yTitle ?? this.module.handlerData.title; | ||||||
| 
 | 
 | ||||||
|         if (this.module.handlerData.showDownloadButton) { |         if (this.module.handlerData.showDownloadButton) { | ||||||
|             // @todo Listen for changes on this module status, even if download isn't enabled.
 |             // Listen for changes on this module status, even if download isn't enabled.
 | ||||||
|             // this.prefetchHandler = this.prefetchDelegate.getPrefetchHandlerFor(this.module);
 |             this.prefetchHandler = CoreCourseModulePrefetchDelegate.instance.getPrefetchHandlerFor(this.module); | ||||||
|             // this.canCheckUpdates = this.prefetchDelegate.canCheckUpdates();
 |             this.canCheckUpdates = CoreCourseModulePrefetchDelegate.instance.canCheckUpdates(); | ||||||
| 
 | 
 | ||||||
|             // this.statusObserver = this.eventsProvider.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
 |             this.statusObserver = CoreEvents.on<CoreEventPackageStatusChanged>(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => { | ||||||
|             //     if (data.componentId === this.module.id && this.prefetchHandler &&
 |                 if (!this.module || data.componentId != this.module.id || !this.prefetchHandler || | ||||||
|             //             data.component === this.prefetchHandler.component) {
 |                         data.component != this.prefetchHandler.component) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
| 
 | 
 | ||||||
|             //         // Call determineModuleStatus to get the right status to display.
 |                 // Call determineModuleStatus to get the right status to display.
 | ||||||
|             //         const status = this.prefetchDelegate.determineModuleStatus(this.module, data.status);
 |                 const status = CoreCourseModulePrefetchDelegate.instance.determineModuleStatus(this.module, data.status); | ||||||
| 
 | 
 | ||||||
|             //         if (this.downloadEnabled) {
 |                 if (this.downloadEnabled) { | ||||||
|             //             // Download is enabled, show the status.
 |                     // Download is enabled, show the status.
 | ||||||
|             //             this.showStatus(status);
 |                     this.showStatus(status); | ||||||
|             //         } else if (this.module.handlerData.updateStatus) {
 |                 } else if (this.module.handlerData?.updateStatus) { | ||||||
|             //             // Download isn't enabled but the handler defines a updateStatus function, call it anyway.
 |                     // Download isn't enabled but the handler defines a updateStatus function, call it anyway.
 | ||||||
|             //             this.module.handlerData.updateStatus(status);
 |                     this.module.handlerData.updateStatus(status); | ||||||
|             //         }
 |                 } | ||||||
|             //     }
 |             }, CoreSites.instance.getCurrentSiteId()); | ||||||
|             // }, this.sitesProvider.getCurrentSiteId());
 |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -138,36 +147,39 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @todo Download the module. |      * Download the module. | ||||||
|      * |      * | ||||||
|      * @param refresh Whether it's refreshing. |      * @param refresh Whether it's refreshing. | ||||||
|  |      * @return Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     // download(refresh: boolean): void {
 |     async download(refresh: boolean): Promise<void> { | ||||||
|     //     if (!this.prefetchHandler) {
 |         if (!this.prefetchHandler || !this.module) { | ||||||
|     //         return;
 |             return; | ||||||
|     //     }
 |         } | ||||||
| 
 | 
 | ||||||
|     //     // Show spinner since this operation might take a while.
 |         // Show spinner since this operation might take a while.
 | ||||||
|     //     this.spinner = true;
 |         this.spinner = true; | ||||||
| 
 | 
 | ||||||
|     //     // Get download size to ask for confirm if it's high.
 |         try { | ||||||
|     //     this.prefetchHandler.getDownloadSize(this.module, this.courseId, true).then((size) => {
 |             // Get download size to ask for confirm if it's high.
 | ||||||
|     //         return this.courseHelper.prefetchModule(this.prefetchHandler, this.module, size, this.courseId, refresh);
 |             const size = await this.prefetchHandler.getDownloadSize(this.module, this.courseId!, true); | ||||||
|     //     }).then(() => {
 | 
 | ||||||
|     //         const eventData = {
 |             await CoreCourseHelper.instance.prefetchModule(this.prefetchHandler, this.module, size, this.courseId!, refresh); | ||||||
|     //             sectionId: this.section.id,
 | 
 | ||||||
|     //             moduleId: this.module.id,
 |             const eventData = { | ||||||
|     //             courseId: this.courseId
 |                 sectionId: this.section?.id, | ||||||
|     //         };
 |                 moduleId: this.module.id, | ||||||
|     //         this.statusChanged.emit(eventData);
 |                 courseId: this.courseId!, | ||||||
|     //     }).catch((error) => {
 |             }; | ||||||
|     //         // Error, hide spinner.
 |             this.statusChanged.emit(eventData); | ||||||
|     //         this.spinner = false;
 |         } catch (error) { | ||||||
|     //         if (!this.isDestroyed) {
 |             // Error, hide spinner.
 | ||||||
|     //             this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true);
 |             this.spinner = false; | ||||||
|     //         }
 |             if (!this.isDestroyed) { | ||||||
|     //     });
 |                 CoreDomUtils.instance.showErrorModalDefault(error, 'core.errordownloading', true); | ||||||
|     // }
 |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Show download buttons according to module status. |      * Show download buttons according to module status. | ||||||
| @ -185,6 +197,21 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | |||||||
|         this.module?.handlerData?.updateStatus?.(status); |         this.module?.handlerData?.updateStatus?.(status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Calculate and show module status. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async calculateAndShowStatus(): Promise<void> { | ||||||
|  |         if (!this.module || !this.courseId) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const status = await CoreCourseModulePrefetchDelegate.instance.getModuleStatus(this.module, this.courseId); | ||||||
|  | 
 | ||||||
|  |         this.showStatus(status); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Component destroyed. |      * Component destroyed. | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -12,16 +12,19 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { NgModule } from '@angular/core'; | import { APP_INITIALIZER, NgModule } from '@angular/core'; | ||||||
| import { Routes } from '@angular/router'; | import { Routes } from '@angular/router'; | ||||||
| 
 | 
 | ||||||
| import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; | import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; | ||||||
| import { CORE_SITE_SCHEMAS } from '@services/sites'; | import { CORE_SITE_SCHEMAS } from '@services/sites'; | ||||||
| import { CoreCourseComponentsModule } from './components/components.module'; | import { CoreCourseComponentsModule } from './components/components.module'; | ||||||
|  | import { CoreCourseDirectivesModule } from './directives/directives.module'; | ||||||
| import { CoreCourseFormatModule } from './format/formats.module'; | import { CoreCourseFormatModule } from './format/formats.module'; | ||||||
| import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/database/course'; | import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/database/course'; | ||||||
| import { SITE_SCHEMA as LOG_SITE_SCHEMA } from './services/database/log'; | import { SITE_SCHEMA as LOG_SITE_SCHEMA } from './services/database/log'; | ||||||
|  | import { SITE_SCHEMA as PREFETCH_SITE_SCHEMA } from './services/database/module-prefetch'; | ||||||
| import { CoreCourseIndexRoutingModule } from './pages/index/index-routing.module'; | import { CoreCourseIndexRoutingModule } from './pages/index/index-routing.module'; | ||||||
|  | import { CoreCourseModulePrefetchDelegate } from './services/module-prefetch-delegate'; | ||||||
| 
 | 
 | ||||||
| const routes: Routes = [ | const routes: Routes = [ | ||||||
|     { |     { | ||||||
| @ -43,14 +46,23 @@ const courseIndexRoutes: Routes = [ | |||||||
|         CoreMainMenuTabRoutingModule.forChild(routes), |         CoreMainMenuTabRoutingModule.forChild(routes), | ||||||
|         CoreCourseFormatModule, |         CoreCourseFormatModule, | ||||||
|         CoreCourseComponentsModule, |         CoreCourseComponentsModule, | ||||||
|  |         CoreCourseDirectivesModule, | ||||||
|     ], |     ], | ||||||
|     exports: [CoreCourseIndexRoutingModule], |     exports: [CoreCourseIndexRoutingModule], | ||||||
|     providers: [ |     providers: [ | ||||||
|         { |         { | ||||||
|             provide: CORE_SITE_SCHEMAS, |             provide: CORE_SITE_SCHEMAS, | ||||||
|             useValue: [SITE_SCHEMA, OFFLINE_SITE_SCHEMA, LOG_SITE_SCHEMA], |             useValue: [SITE_SCHEMA, OFFLINE_SITE_SCHEMA, LOG_SITE_SCHEMA, PREFETCH_SITE_SCHEMA], | ||||||
|             multi: true, |             multi: true, | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             provide: APP_INITIALIZER, | ||||||
|  |             multi: true, | ||||||
|  |             deps: [], | ||||||
|  |             useFactory: () => () => { | ||||||
|  |                 CoreCourseModulePrefetchDelegate.instance.initialize(); | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|     ], |     ], | ||||||
| }) | }) | ||||||
| export class CoreCourseModule {} | export class CoreCourseModule {} | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								src/core/features/course/directives/directives.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/core/features/course/directives/directives.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | // (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 { NgModule } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | import { CoreCourseDownloadModuleMainFileDirective } from './download-module-main-file'; | ||||||
|  | 
 | ||||||
|  | @NgModule({ | ||||||
|  |     declarations: [ | ||||||
|  |         CoreCourseDownloadModuleMainFileDirective, | ||||||
|  |     ], | ||||||
|  |     imports: [], | ||||||
|  |     exports: [ | ||||||
|  |         CoreCourseDownloadModuleMainFileDirective, | ||||||
|  |     ], | ||||||
|  | }) | ||||||
|  | export class CoreCourseDirectivesModule {} | ||||||
| @ -0,0 +1,85 @@ | |||||||
|  | // (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 { Directive, Input, OnInit, ElementRef } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
|  | import { CoreCourse, CoreCourseModuleContentFile, CoreCourseWSModule } from '@features/course/services/course'; | ||||||
|  | import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Directive to allow downloading and open the main file of a module. | ||||||
|  |  * When the item with this directive is clicked, the module will be downloaded (if needed) and opened. | ||||||
|  |  * This is meant for modules like mod_resource. | ||||||
|  |  * | ||||||
|  |  * This directive must receive either a module or a moduleId. If no files are provided, it will use module.contents. | ||||||
|  |  */ | ||||||
|  | @Directive({ | ||||||
|  |     selector: '[core-course-download-module-main-file]', | ||||||
|  | }) | ||||||
|  | export class CoreCourseDownloadModuleMainFileDirective implements OnInit { | ||||||
|  | 
 | ||||||
|  |     @Input() module?: CoreCourseWSModule; // The module.
 | ||||||
|  |     @Input() moduleId?: string | number; // The module ID. Required if module is not supplied.
 | ||||||
|  |     @Input() courseId?: string | number; // The course ID.
 | ||||||
|  |     @Input() component?: string; // Component to link the file to.
 | ||||||
|  |     @Input() componentId?: string | number; // Component ID to use in conjunction with the component. If not defined, use moduleId.
 | ||||||
|  |     @Input() files?: CoreCourseModuleContentFile[]; // List of files of the module. If not provided, use module.contents.
 | ||||||
|  | 
 | ||||||
|  |     protected element: HTMLElement; | ||||||
|  | 
 | ||||||
|  |     constructor(element: ElementRef) { | ||||||
|  |         this.element = element.nativeElement; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Component being initialized. | ||||||
|  |      */ | ||||||
|  |     ngOnInit(): void { | ||||||
|  |         this.element.addEventListener('click', async (ev: Event) => { | ||||||
|  |             if (!this.module && !this.moduleId) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ev.preventDefault(); | ||||||
|  |             ev.stopPropagation(); | ||||||
|  | 
 | ||||||
|  |             const modal = await CoreDomUtils.instance.showModalLoading(); | ||||||
|  |             const courseId = typeof this.courseId == 'string' ? parseInt(this.courseId, 10) : this.courseId; | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |                 if (!this.module) { | ||||||
|  |                     // Try to get the module from cache.
 | ||||||
|  |                     this.moduleId = typeof this.moduleId == 'string' ? parseInt(this.moduleId, 10) : this.moduleId; | ||||||
|  |                     this.module = await CoreCourse.instance.getModule(this.moduleId!, courseId); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 const componentId = this.componentId || module.id; | ||||||
|  | 
 | ||||||
|  |                 await CoreCourseHelper.instance.downloadModuleAndOpenFile( | ||||||
|  |                     this.module, | ||||||
|  |                     courseId ?? this.module.course!, | ||||||
|  |                     this.component, | ||||||
|  |                     componentId, | ||||||
|  |                     this.files, | ||||||
|  |                 ); | ||||||
|  |             } catch (error) { | ||||||
|  |                 CoreDomUtils.instance.showErrorModalDefault(error, 'core.errordownloading', true); | ||||||
|  |             } finally { | ||||||
|  |                 modal.dismiss(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -28,20 +28,19 @@ import { | |||||||
| } from '@features/course/services/course'; | } from '@features/course/services/course'; | ||||||
| import { CoreCourseHelper, CoreCourseSectionFormatted, CorePrefetchStatusInfo } from '@features/course/services/course-helper'; | import { CoreCourseHelper, CoreCourseSectionFormatted, CorePrefetchStatusInfo } from '@features/course/services/course-helper'; | ||||||
| import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; | import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; | ||||||
| // import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||||
| import { | import { | ||||||
|     CoreCourseOptionsDelegate, |     CoreCourseOptionsDelegate, | ||||||
|     CoreCourseOptionsMenuHandlerToDisplay, |     CoreCourseOptionsMenuHandlerToDisplay, | ||||||
| } from '@features/course/services/course-options-delegate'; | } from '@features/course/services/course-options-delegate'; | ||||||
| // import { CoreCourseSyncProvider } from '../../providers/sync';
 | // import { CoreCourseSyncProvider } from '../../providers/sync';
 | ||||||
| // import { CoreCourseFormatComponent } from '../../components/format/format';
 | import { CoreCourseFormatComponent } from '../../components/format/format'; | ||||||
| import { | import { | ||||||
|     CoreEvents, |     CoreEvents, | ||||||
|     CoreEventObserver, |     CoreEventObserver, | ||||||
|     CoreEventCourseStatusChanged, |     CoreEventCourseStatusChanged, | ||||||
|     CoreEventCompletionModuleViewedData, |     CoreEventCompletionModuleViewedData, | ||||||
| } from '@singletons/events'; | } from '@singletons/events'; | ||||||
| import { Translate } from '@singletons'; |  | ||||||
| import { CoreNavHelper } from '@services/nav-helper'; | import { CoreNavHelper } from '@services/nav-helper'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -54,7 +53,7 @@ import { CoreNavHelper } from '@services/nav-helper'; | |||||||
| export class CoreCourseContentsPage implements OnInit, OnDestroy { | export class CoreCourseContentsPage implements OnInit, OnDestroy { | ||||||
| 
 | 
 | ||||||
|     @ViewChild(IonContent) content?: IonContent; |     @ViewChild(IonContent) content?: IonContent; | ||||||
|     // @ViewChild(CoreCourseFormatComponent) formatComponent: CoreCourseFormatComponent;
 |     @ViewChild(CoreCourseFormatComponent) formatComponent?: CoreCourseFormatComponent; | ||||||
| 
 | 
 | ||||||
|     course!: CoreCourseAnyCourseData; |     course!: CoreCourseAnyCourseData; | ||||||
|     sections?: CoreCourseSectionFormatted[]; |     sections?: CoreCourseSectionFormatted[]; | ||||||
| @ -244,9 +243,9 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         if (refresh) { |         if (refresh) { | ||||||
|             // Invalidate the recently downloaded module list. To ensure info can be prefetched.
 |             // Invalidate the recently downloaded module list. To ensure info can be prefetched.
 | ||||||
|             // const modules = CoreCourse.instance.getSectionsModules(sections);
 |             const modules = CoreCourse.instance.getSectionsModules(sections); | ||||||
| 
 | 
 | ||||||
|             // @todo await this.prefetchDelegate.invalidateModules(modules, this.course.id);
 |             await CoreCourseModulePrefetchDelegate.instance.invalidateModules(modules, this.course.id); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let completionStatus: Record<string, CoreCourseCompletionActivityStatus> = {}; |         let completionStatus: Record<string, CoreCourseCompletionActivityStatus> = {}; | ||||||
| @ -279,14 +278,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         if (CoreCourseFormatDelegate.instance.canViewAllSections(this.course)) { |         if (CoreCourseFormatDelegate.instance.canViewAllSections(this.course)) { | ||||||
|             // Add a fake first section (all sections).
 |             // Add a fake first section (all sections).
 | ||||||
|             this.sections.unshift({ |             this.sections.unshift(CoreCourseHelper.instance.createAllSectionsSection()); | ||||||
|                 id: CoreCourseProvider.ALL_SECTIONS_ID, |  | ||||||
|                 name: Translate.instance.instant('core.course.allsections'), |  | ||||||
|                 hasContent: true, |  | ||||||
|                 summary: '', |  | ||||||
|                 summaryformat: 1, |  | ||||||
|                 modules: [], |  | ||||||
|             }); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get whether to show the refresher now that we have sections.
 |         // Get whether to show the refresher now that we have sections.
 | ||||||
| @ -345,8 +337,8 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | |||||||
|         } finally { |         } finally { | ||||||
|             // Do not call doRefresh on the format component if the refresher is defined in the format component
 |             // Do not call doRefresh on the format component if the refresher is defined in the format component
 | ||||||
|             // to prevent an inifinite loop.
 |             // to prevent an inifinite loop.
 | ||||||
|             if (this.displayRefresher) { |             if (this.displayRefresher && this.formatComponent) { | ||||||
|                 // @todo await CoreUtils.instance.ignoreErrors(this.formatComponent.doRefresh(refresher));
 |                 await CoreUtils.instance.ignoreErrors(this.formatComponent.doRefresh(refresher)); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             refresher?.detail.complete(); |             refresher?.detail.complete(); | ||||||
| @ -384,7 +376,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | |||||||
|         promises.push(CoreCourseFormatDelegate.instance.invalidateData(this.course, this.sections || [])); |         promises.push(CoreCourseFormatDelegate.instance.invalidateData(this.course, this.sections || [])); | ||||||
| 
 | 
 | ||||||
|         if (this.sections) { |         if (this.sections) { | ||||||
|             // @todo promises.push(this.prefetchDelegate.invalidateCourseUpdates(this.course.id));
 |             promises.push(CoreCourseModulePrefetchDelegate.instance.invalidateCourseUpdates(this.course.id)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         await Promise.all(promises); |         await Promise.all(promises); | ||||||
| @ -408,7 +400,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | |||||||
|         try { |         try { | ||||||
|             await this.loadData(true, sync); |             await this.loadData(true, sync); | ||||||
| 
 | 
 | ||||||
|             // @todo await this.formatComponent.doRefresh(undefined, undefined, true);
 |             await this.formatComponent?.doRefresh(undefined, undefined, true); | ||||||
|         } finally { |         } finally { | ||||||
|             this.dataLoaded = true; |             this.dataLoaded = true; | ||||||
| 
 | 
 | ||||||
| @ -431,15 +423,15 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | |||||||
|     /** |     /** | ||||||
|      * Prefetch the whole course. |      * Prefetch the whole course. | ||||||
|      */ |      */ | ||||||
|     prefetchCourse(): void { |     async prefetchCourse(): Promise<void> { | ||||||
|         try { |         try { | ||||||
|             // @todo await CoreCourseHelper.instance.confirmAndPrefetchCourse(
 |             await CoreCourseHelper.instance.confirmAndPrefetchCourse( | ||||||
|             //     this.prefetchCourseData,
 |                 this.prefetchCourseData, | ||||||
|             //     this.course,
 |                 this.course, | ||||||
|             //     this.sections,
 |                 this.sections, | ||||||
|             //     this.courseHandlers,
 |                 undefined, | ||||||
|             //     this.courseMenuHandlers,
 |                 this.courseMenuHandlers, | ||||||
|             // );
 |             ); | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             if (this.isDestroyed) { |             if (this.isDestroyed) { | ||||||
|                 return; |                 return; | ||||||
| @ -497,14 +489,14 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { | |||||||
|      * User entered the page. |      * User entered the page. | ||||||
|      */ |      */ | ||||||
|     ionViewDidEnter(): void { |     ionViewDidEnter(): void { | ||||||
|         // @todo this.formatComponent?.ionViewDidEnter();
 |         this.formatComponent?.ionViewDidEnter(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * User left the page. |      * User left the page. | ||||||
|      */ |      */ | ||||||
|     ionViewDidLeave(): void { |     ionViewDidLeave(): void { | ||||||
|         // @todo this.formatComponent?.ionViewDidLeave();
 |         this.formatComponent?.ionViewDidLeave(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -11,7 +11,6 @@ | |||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| // @todo test delegate
 |  | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||||
| import { CoreDelegate, CoreDelegateHandler, CoreDelegateToDisplay } from '@classes/delegate'; | import { CoreDelegate, CoreDelegateHandler, CoreDelegateToDisplay } from '@classes/delegate'; | ||||||
| @ -178,7 +177,7 @@ export interface CoreCourseOptionsHandlerToDisplay extends CoreDelegateToDisplay | |||||||
|      * @param course The course. |      * @param course The course. | ||||||
|      * @return Promise resolved when done. |      * @return Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     prefetch?(course: CoreEnrolledCourseDataWithExtraInfoAndOptions): Promise<void>; |     prefetch?(course: CoreCourseAnyCourseData): Promise<void>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -206,7 +205,7 @@ export interface CoreCourseOptionsMenuHandlerToDisplay { | |||||||
|      * @param course The course. |      * @param course The course. | ||||||
|      * @return Promise resolved when done. |      * @return Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     prefetch?(course: CoreEnrolledCourseDataWithExtraInfoAndOptions): Promise<void>; |     prefetch?(course: CoreCourseAnyCourseData): Promise<void>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
| @ -109,7 +109,6 @@ export class CoreCourseProvider { | |||||||
|      * |      * | ||||||
|      * @param courseId Course ID. |      * @param courseId Course ID. | ||||||
|      * @param completion Completion status of the module. |      * @param completion Completion status of the module. | ||||||
|      * @todo Add completion type. |  | ||||||
|      */ |      */ | ||||||
|     checkModuleCompletion(courseId: number, completion: CoreCourseModuleCompletionDataFormatted): void { |     checkModuleCompletion(courseId: number, completion: CoreCourseModuleCompletionDataFormatted): void { | ||||||
|         if (completion && completion.tracking === 2 && completion.state === 0) { |         if (completion && completion.tracking === 2 && completion.state === 0) { | ||||||
| @ -830,7 +829,7 @@ export class CoreCourseProvider { | |||||||
|      * @return Promise resolved when loaded. |      * @return Promise resolved when loaded. | ||||||
|      */ |      */ | ||||||
|     async loadModuleContents( |     async loadModuleContents( | ||||||
|         module: CoreCourseModuleData & CoreCourseModuleBasicInfo, |         module: CoreCourseModuleData, | ||||||
|         courseId?: number, |         courseId?: number, | ||||||
|         sectionId?: number, |         sectionId?: number, | ||||||
|         preferCache?: boolean, |         preferCache?: boolean, | ||||||
| @ -1412,14 +1411,13 @@ export type CoreCourseModuleContentFile = { | |||||||
|     filename: string; // Filename.
 |     filename: string; // Filename.
 | ||||||
|     filepath: string; // Filepath.
 |     filepath: string; // Filepath.
 | ||||||
|     filesize: number; // Filesize.
 |     filesize: number; // Filesize.
 | ||||||
|     fileurl?: string; // Downloadable file url.
 |     fileurl: string; // Downloadable file url.
 | ||||||
|     url?: string; // @deprecated. Use fileurl instead.
 |  | ||||||
|     content?: string; // Raw content, will be used when type is content.
 |     content?: string; // Raw content, will be used when type is content.
 | ||||||
|     timecreated: number; // Time created.
 |     timecreated: number; // Time created.
 | ||||||
|     timemodified: number; // Time modified.
 |     timemodified: number; // Time modified.
 | ||||||
|     sortorder: number; // Content sort order.
 |     sortorder: number; // Content sort order.
 | ||||||
|     mimetype?: string; // File mime type.
 |     mimetype?: string; // File mime type.
 | ||||||
|     isexternalfile?: boolean; // Whether is an external file.
 |     isexternalfile?: number; // Whether is an external file.
 | ||||||
|     repositorytype?: string; // The repository type for external files.
 |     repositorytype?: string; // The repository type for external files.
 | ||||||
|     userid: number; // User who added this content to moodle.
 |     userid: number; // User who added this content to moodle.
 | ||||||
|     author: string; // Content owner.
 |     author: string; // Content owner.
 | ||||||
|  | |||||||
| @ -0,0 +1,46 @@ | |||||||
|  | // (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 { CoreSiteSchema } from '@services/sites'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Database variables for CoreCourseModulePrefetchDelegate service. | ||||||
|  |  */ | ||||||
|  | export const CHECK_UPDATES_TIMES_TABLE = 'check_updates_times'; | ||||||
|  | export const SITE_SCHEMA: CoreSiteSchema = { | ||||||
|  |     name: 'CoreCourseModulePrefetchDelegate', | ||||||
|  |     version: 1, | ||||||
|  |     tables: [ | ||||||
|  |         { | ||||||
|  |             name: CHECK_UPDATES_TIMES_TABLE, | ||||||
|  |             columns: [ | ||||||
|  |                 { | ||||||
|  |                     name: 'courseId', | ||||||
|  |                     type: 'INTEGER', | ||||||
|  |                     primaryKey: true, | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     name: 'time', | ||||||
|  |                     type: 'INTEGER', | ||||||
|  |                     notNull: true, | ||||||
|  |                 }, | ||||||
|  |             ], | ||||||
|  |         }, | ||||||
|  |     ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export type CoreCourseCheckUpdatesDBRecord = { | ||||||
|  |     courseId: number; | ||||||
|  |     time: number; | ||||||
|  | }; | ||||||
							
								
								
									
										1594
									
								
								src/core/features/course/services/module-prefetch-delegate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1594
									
								
								src/core/features/course/services/module-prefetch-delegate.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -26,6 +26,7 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; | |||||||
| import { CoreCourseHelper } from '@features/course/services/course-helper'; | import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||||
| import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; | import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; | ||||||
| import { CoreCourseModuleDelegate, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | import { CoreCourseModuleDelegate, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | ||||||
|  | import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page that displays site home index. |  * Page that displays site home index. | ||||||
| @ -59,7 +60,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { | |||||||
|     constructor( |     constructor( | ||||||
|         protected route: ActivatedRoute, |         protected route: ActivatedRoute, | ||||||
|         protected navCtrl: NavController, |         protected navCtrl: NavController, | ||||||
|         // @todo private prefetchDelegate: CoreCourseModulePrefetchDelegate,
 |  | ||||||
|     ) {} |     ) {} | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -86,8 +86,8 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         const module = navParams['module']; |         const module = navParams['module']; | ||||||
|         if (module) { |         if (module) { | ||||||
|             // @todo const modParams = navParams.get('modParams');
 |             const modParams = navParams['modParams']; | ||||||
|             // CoreCourseHelper.instance.openModule(module, this.siteHomeId, undefined, modParams);
 |             CoreCourseHelper.instance.openModule(module, this.siteHomeId, undefined, modParams); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.loadContent().finally(() => { |         this.loadContent().finally(() => { | ||||||
| @ -174,7 +174,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         if (this.section && this.section.modules) { |         if (this.section && this.section.modules) { | ||||||
|             // Invalidate modules prefetch data.
 |             // Invalidate modules prefetch data.
 | ||||||
|             //  @todo promises.push(this.prefetchDelegate.invalidateModules(this.section.modules, this.siteHomeId));
 |             promises.push(CoreCourseModulePrefetchDelegate.instance.invalidateModules(this.section.modules, this.siteHomeId)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this.courseBlocksComponent) { |         if (this.courseBlocksComponent) { | ||||||
|  | |||||||
| @ -160,40 +160,39 @@ export class CoreFileHelperProvider { | |||||||
|                 onProgress({ calculating: true }); |                 onProgress({ calculating: true }); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             try { |             const shouldDownloadFirst = await CoreFilepool.instance.shouldDownloadFileBeforeOpen(fixedUrl, file.filesize || 0); | ||||||
|                 await CoreFilepool.instance.shouldDownloadBeforeOpen(fixedUrl, file.filesize || 0); |             if (shouldDownloadFirst) { | ||||||
|             } catch (error) { |                 // Download the file first.
 | ||||||
|                 // Start the download if in wifi, but return the URL right away so the file is opened.
 |                 if (state == CoreConstants.DOWNLOADING) { | ||||||
|                 if (isWifi) { |                     // It's already downloading, stop.
 | ||||||
|                     this.downloadFile(fileUrl, component, componentId, timemodified, onProgress, file, siteId); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (!this.isStateDownloaded(state) || isOnline) { |  | ||||||
|                     // Not downloaded or online, return the online URL.
 |  | ||||||
|                     return fixedUrl; |                     return fixedUrl; | ||||||
|                 } else { |  | ||||||
|                     // Outdated but offline, so we return the local URL.
 |  | ||||||
|                     return CoreFilepool.instance.getUrlByUrl( |  | ||||||
|                         siteId, |  | ||||||
|                         fileUrl, |  | ||||||
|                         component, |  | ||||||
|                         componentId, |  | ||||||
|                         timemodified, |  | ||||||
|                         false, |  | ||||||
|                         false, |  | ||||||
|                         file, |  | ||||||
|                     ); |  | ||||||
|                 } |                 } | ||||||
|  | 
 | ||||||
|  |                 // Download and then return the local URL.
 | ||||||
|  |                 return this.downloadFile(fileUrl, component, componentId, timemodified, onProgress, file, siteId); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Download the file first.
 |             // Start the download if in wifi, but return the URL right away so the file is opened.
 | ||||||
|             if (state == CoreConstants.DOWNLOADING) { |             if (isWifi) { | ||||||
|                 // It's already downloading, stop.
 |                 this.downloadFile(fileUrl, component, componentId, timemodified, onProgress, file, siteId); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!this.isStateDownloaded(state) || isOnline) { | ||||||
|  |                 // Not downloaded or online, return the online URL.
 | ||||||
|                 return fixedUrl; |                 return fixedUrl; | ||||||
|  |             } else { | ||||||
|  |                 // Outdated but offline, so we return the local URL.
 | ||||||
|  |                 return CoreFilepool.instance.getUrlByUrl( | ||||||
|  |                     siteId, | ||||||
|  |                     fileUrl, | ||||||
|  |                     component, | ||||||
|  |                     componentId, | ||||||
|  |                     timemodified, | ||||||
|  |                     false, | ||||||
|  |                     false, | ||||||
|  |                     file, | ||||||
|  |                 ); | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             // Download and then return the local URL.
 |  | ||||||
|             return this.downloadFile(fileUrl, component, componentId, timemodified, onProgress, file, siteId); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2763,13 +2763,7 @@ export class CoreFilepoolProvider { | |||||||
|      * @param url File online URL. |      * @param url File online URL. | ||||||
|      * @param size File size. |      * @param size File size. | ||||||
|      * @return Promise resolved if should download before open, rejected otherwise. |      * @return Promise resolved if should download before open, rejected otherwise. | ||||||
|      * @description |      * @ddeprecated since 3.9.5. Please use shouldDownloadFileBeforeOpen instead. | ||||||
|      * Convenience function to check if a file should be downloaded before opening it. |  | ||||||
|      * |  | ||||||
|      * The default behaviour in the app is to download first and then open the local file in the following cases: |  | ||||||
|      *     - The file is small (less than DOWNLOAD_THRESHOLD). |  | ||||||
|      *     - The file cannot be streamed. |  | ||||||
|      * If the file is big and can be streamed, the promise returned by this function will be rejected. |  | ||||||
|      */ |      */ | ||||||
|     async shouldDownloadBeforeOpen(url: string, size: number): Promise<void> { |     async shouldDownloadBeforeOpen(url: string, size: number): Promise<void> { | ||||||
|         if (size >= 0 && size <= CoreFilepoolProvider.DOWNLOAD_THRESHOLD) { |         if (size >= 0 && size <= CoreFilepoolProvider.DOWNLOAD_THRESHOLD) { | ||||||
| @ -2784,6 +2778,32 @@ export class CoreFilepoolProvider { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Convenience function to check if a file should be downloaded before opening it. | ||||||
|  |      * | ||||||
|  |      * @param url File online URL. | ||||||
|  |      * @param size File size. | ||||||
|  |      * @return Promise resolved with boolean: whether file should be downloaded before opening it. | ||||||
|  |      * @description | ||||||
|  |      * Convenience function to check if a file should be downloaded before opening it. | ||||||
|  |      * | ||||||
|  |      * The default behaviour in the app is to download first and then open the local file in the following cases: | ||||||
|  |      *     - The file is small (less than DOWNLOAD_THRESHOLD). | ||||||
|  |      *     - The file cannot be streamed. | ||||||
|  |      * If the file is big and can be streamed, the promise returned by this function will be rejected. | ||||||
|  |      */ | ||||||
|  |     async shouldDownloadFileBeforeOpen(url: string, size: number): Promise<boolean> { | ||||||
|  |         if (size >= 0 && size <= CoreFilepoolProvider.DOWNLOAD_THRESHOLD) { | ||||||
|  |             // The file is small, download it.
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const mimetype = await CoreUtils.instance.getMimeTypeFromUrl(url); | ||||||
|  | 
 | ||||||
|  |         // If the file is streaming (audio or video), return false.
 | ||||||
|  |         return mimetype.indexOf('video') == -1 && mimetype.indexOf('audio') == -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Store package status. |      * Store package status. | ||||||
|      * |      * | ||||||
|  | |||||||
| @ -114,7 +114,7 @@ export class CoreUtilsProvider { | |||||||
|      * @param result Object where to put the properties. If not defined, a new object will be created. |      * @param result Object where to put the properties. If not defined, a new object will be created. | ||||||
|      * @return The object. |      * @return The object. | ||||||
|      */ |      */ | ||||||
|     arrayToObject<T extends Record<string, unknown> | string>( |     arrayToObject<T>( | ||||||
|         array: T[], |         array: T[], | ||||||
|         propertyName?: string, |         propertyName?: string, | ||||||
|         result: Record<string, T> = {}, |         result: Record<string, T> = {}, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user