MOBILE-3659 course: Implement prefetch delegate
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 {}
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
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…
Reference in New Issue