MOBILE-3833 course: Improve performance after download or delete
parent
1a07331396
commit
37af8e3c69
|
@ -25,8 +25,8 @@
|
||||||
<p class="item-heading ion-text-wrap">{{ 'addon.storagemanager.totaldownloads' | translate }}</p>
|
<p class="item-heading ion-text-wrap">{{ 'addon.storagemanager.totaldownloads' | translate }}</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-badge color="light" slot="end">
|
<ion-badge color="light" slot="end">
|
||||||
<ng-container *ngIf="sizeLoaded">{{ totalSize | coreBytesToSize }}</ng-container>
|
<ng-container *ngIf="!calculatingSize">{{ totalSize | coreBytesToSize }}</ng-container>
|
||||||
<ng-container *ngIf="!sizeLoaded">{{ 'core.calculating' | translate }}</ng-container>
|
<ng-container *ngIf="calculatingSize">{{ 'core.calculating' | translate }}</ng-container>
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-button *ngIf="downloadCourseEnabled" (click)="prefetchCourse()" expand="block" fill="outline" class="ion-no-margin"
|
<ion-button *ngIf="downloadCourseEnabled" (click)="prefetchCourse()" expand="block" fill="outline" class="ion-no-margin"
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<ion-spinner *ngIf="prefetchCourseData.loading" slot="start"></ion-spinner>
|
<ion-spinner *ngIf="prefetchCourseData.loading" slot="start"></ion-spinner>
|
||||||
{{ prefetchCourseData.statusTranslatable | translate }}
|
{{ prefetchCourseData.statusTranslatable | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
<ion-button *ngIf="sizeLoaded && totalSize > 0" (click)="deleteForCourse()" expand="block" color="danger"
|
<ion-button [disabled]="calculatingSize || totalSize <= 0" (click)="deleteForCourse()" expand="block" color="danger"
|
||||||
class="ion-no-margin ion-margin-top">
|
class="ion-no-margin ion-margin-top">
|
||||||
<ion-icon name="fas-trash" slot="start" [attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate:
|
<ion-icon name="fas-trash" slot="start" [attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate:
|
||||||
{ name: title }">
|
{ name: title }">
|
||||||
|
@ -55,12 +55,12 @@
|
||||||
</core-format-text>
|
</core-format-text>
|
||||||
</p>
|
</p>
|
||||||
<ion-badge [color]="section.downloadStatus == statusDownloaded ? 'success' : 'light'"
|
<ion-badge [color]="section.downloadStatus == statusDownloaded ? 'success' : 'light'"
|
||||||
*ngIf="section.sizeLoaded && section.totalSize > 0">
|
*ngIf="!section.calculatingSize && section.totalSize > 0">
|
||||||
<ion-icon name="fam-cloud-done" *ngIf="section.downloadStatus == statusDownloaded"
|
<ion-icon name="fam-cloud-done" *ngIf="section.downloadStatus == statusDownloaded"
|
||||||
[attr.aria-label]="'core.downloaded' | translate">
|
[attr.aria-label]="'core.downloaded' | translate">
|
||||||
</ion-icon>{{ section.totalSize | coreBytesToSize }}
|
</ion-icon>{{ section.totalSize | coreBytesToSize }}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
<ion-badge color="light" *ngIf="!section.sizeLoaded">
|
<ion-badge color="light" *ngIf="section.calculatingSize">
|
||||||
{{ 'core.calculating' | translate }}
|
{{ 'core.calculating' | translate }}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
<!-- Download progress. -->
|
<!-- Download progress. -->
|
||||||
|
@ -69,7 +69,8 @@
|
||||||
</core-progress-bar>
|
</core-progress-bar>
|
||||||
</p>
|
</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<div class="storage-buttons" slot="end" *ngIf="(section.sizeLoaded && section.totalSize > 0) || downloadEnabled">
|
<div class="storage-buttons" slot="end"
|
||||||
|
*ngIf="(!section.calculatingSize && section.totalSize > 0) || downloadEnabled">
|
||||||
<div *ngIf="downloadEnabled" slot="end" class="core-button-spinner">
|
<div *ngIf="downloadEnabled" slot="end" class="core-button-spinner">
|
||||||
<core-download-refresh *ngIf="!section.isDownloading && section.downloadStatus != statusDownloaded"
|
<core-download-refresh *ngIf="!section.isDownloading && section.downloadStatus != statusDownloaded"
|
||||||
[status]="section.downloadStatus" [enabled]="true" (action)="prefecthSection(section)"
|
[status]="section.downloadStatus" [enabled]="true" (action)="prefecthSection(section)"
|
||||||
|
@ -83,7 +84,7 @@
|
||||||
{{section.count}} / {{section.total}}
|
{{section.count}} / {{section.total}}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
</div>
|
</div>
|
||||||
<ion-button (click)="deleteForSection(section)" *ngIf="section.sizeLoaded && section.totalSize > 0"
|
<ion-button (click)="deleteForSection(section)" *ngIf="!section.calculatingSize && section.totalSize > 0"
|
||||||
color="danger" fill="clear">
|
color="danger" fill="clear">
|
||||||
<ion-icon name="fas-trash" slot="icon-only"
|
<ion-icon name="fas-trash" slot="icon-only"
|
||||||
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: section.name }">
|
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: section.name }">
|
||||||
|
@ -95,7 +96,7 @@
|
||||||
<ion-card-content>
|
<ion-card-content>
|
||||||
<ng-container *ngFor="let module of section.modules">
|
<ng-container *ngFor="let module of section.modules">
|
||||||
<ion-item class="ion-no-padding core-course-storage-activity"
|
<ion-item class="ion-no-padding core-course-storage-activity"
|
||||||
*ngIf="downloadEnabled || (module.sizeLoaded && module.totalSize > 0)">
|
*ngIf="downloadEnabled || (!module.calculatingSize && module.totalSize > 0)">
|
||||||
<core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon"
|
<core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon"
|
||||||
[modname]="module.modname" [componentId]="module.instance">
|
[modname]="module.modname" [componentId]="module.instance">
|
||||||
</core-mod-icon>
|
</core-mod-icon>
|
||||||
|
@ -106,12 +107,12 @@
|
||||||
</core-format-text>
|
</core-format-text>
|
||||||
</h3>
|
</h3>
|
||||||
<ion-badge [color]="module.downloadStatus == statusDownloaded ? 'success' : 'light'"
|
<ion-badge [color]="module.downloadStatus == statusDownloaded ? 'success' : 'light'"
|
||||||
*ngIf="module.sizeLoaded && module.totalSize > 0">
|
*ngIf="!module.calculatingSize && module.totalSize > 0">
|
||||||
<ion-icon name="fam-cloud-done" *ngIf="module.downloadStatus == statusDownloaded"
|
<ion-icon name="fam-cloud-done" *ngIf="module.downloadStatus == statusDownloaded"
|
||||||
[attr.aria-label]="'core.downloaded' | translate">
|
[attr.aria-label]="'core.downloaded' | translate">
|
||||||
</ion-icon>{{ module.totalSize | coreBytesToSize }}
|
</ion-icon>{{ module.totalSize | coreBytesToSize }}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
<ion-badge color="light" *ngIf="!module.sizeLoaded">
|
<ion-badge color="light" *ngIf="module.calculatingSize">
|
||||||
{{ 'core.calculating' | translate }}
|
{{ 'core.calculating' | translate }}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
|
@ -123,7 +124,7 @@
|
||||||
(action)="prefetchModule(module, section)">
|
(action)="prefetchModule(module, section)">
|
||||||
</core-download-refresh>
|
</core-download-refresh>
|
||||||
<ion-button fill="clear" (click)="deleteForModule(module, section)"
|
<ion-button fill="clear" (click)="deleteForModule(module, section)"
|
||||||
*ngIf="module.sizeLoaded && module.totalSize > 0" color="danger">
|
*ngIf="!module.calculatingSize && module.totalSize > 0" color="danger">
|
||||||
<ion-icon name="fas-trash" slot="icon-only"
|
<ion-icon name="fas-trash" slot="icon-only"
|
||||||
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: module.name }">
|
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: module.name }">
|
||||||
</ion-icon>
|
</ion-icon>
|
||||||
|
|
|
@ -48,7 +48,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
loaded = false;
|
loaded = false;
|
||||||
sections: AddonStorageManagerCourseSection[] = [];
|
sections: AddonStorageManagerCourseSection[] = [];
|
||||||
totalSize = 0;
|
totalSize = 0;
|
||||||
sizeLoaded = false;
|
calculatingSize = true;
|
||||||
|
|
||||||
downloadEnabled = false;
|
downloadEnabled = false;
|
||||||
downloadCourseEnabled = false;
|
downloadCourseEnabled = false;
|
||||||
|
@ -106,12 +106,20 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
const sections = await CoreCourse.getSections(this.courseId, false, true);
|
const sections = await CoreCourse.getSections(this.courseId, false, true);
|
||||||
this.sections = (await CoreCourseHelper.addHandlerDataForModules(sections, this.courseId)).sections
|
this.sections = (await CoreCourseHelper.addHandlerDataForModules(sections, this.courseId)).sections
|
||||||
.map((section) => ({ ...section, totalSize: 0 }));
|
.map(section => ({
|
||||||
|
...section,
|
||||||
|
totalSize: 0,
|
||||||
|
calculatingSize: true,
|
||||||
|
modules: section.modules.map(module => ({
|
||||||
|
...module,
|
||||||
|
calculatingSize: true,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.loadSizes(),
|
this.initSizes(),
|
||||||
this.initCoursePrefetch(),
|
this.initCoursePrefetch(),
|
||||||
this.initModulePrefetch(),
|
this.initModulePrefetch(),
|
||||||
]);
|
]);
|
||||||
|
@ -240,18 +248,9 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Init section, course and modules sizes.
|
* Init section, course and modules sizes.
|
||||||
*/
|
*/
|
||||||
protected async loadSizes(): Promise<void> {
|
protected async initSizes(): Promise<void> {
|
||||||
this.totalSize = 0;
|
|
||||||
this.sizeLoaded = false;
|
|
||||||
|
|
||||||
await Promise.all(this.sections.map(async (section) => {
|
await Promise.all(this.sections.map(async (section) => {
|
||||||
section.totalSize = 0;
|
|
||||||
section.sizeLoaded = false;
|
|
||||||
|
|
||||||
await Promise.all(section.modules.map(async (module) => {
|
await Promise.all(section.modules.map(async (module) => {
|
||||||
module.totalSize = 0;
|
|
||||||
module.sizeLoaded = false;
|
|
||||||
|
|
||||||
// Note: This function only gets the size for modules which are downloadable.
|
// Note: This function only gets the size for modules which are downloadable.
|
||||||
// For other modules it always returns 0, even if they have downloaded some files.
|
// For other modules it always returns 0, even if they have downloaded some files.
|
||||||
// However there is no 100% reliable way to actually track the files in this case.
|
// However there is no 100% reliable way to actually track the files in this case.
|
||||||
|
@ -268,13 +267,13 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
this.totalSize += size;
|
this.totalSize += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.sizeLoaded = true;
|
module.calculatingSize = false;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
section.sizeLoaded = true;
|
section.calculatingSize = false;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.sizeLoaded = true;
|
this.calculatingSize = false;
|
||||||
|
|
||||||
// Mark course as not downloaded if course size is 0.
|
// Mark course as not downloaded if course size is 0.
|
||||||
if (this.totalSize == 0) {
|
if (this.totalSize == 0) {
|
||||||
|
@ -282,6 +281,56 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the sizes of some modules.
|
||||||
|
*
|
||||||
|
* @param modules Modules.
|
||||||
|
* @param section Section the modules belong to.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async updateModulesSizes(
|
||||||
|
modules: AddonStorageManagerModule[],
|
||||||
|
section?: AddonStorageManagerCourseSection,
|
||||||
|
): Promise<void> {
|
||||||
|
this.calculatingSize = true;
|
||||||
|
|
||||||
|
await Promise.all(modules.map(async (module) => {
|
||||||
|
if (module.calculatingSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.calculatingSize = true;
|
||||||
|
|
||||||
|
if (!section) {
|
||||||
|
section = this.sections.find((section) => section.modules.some((mod) => mod.id === module.id));
|
||||||
|
if (section) {
|
||||||
|
section.calculatingSize = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const size = await CoreCourseModulePrefetchDelegate.getModuleStoredSize(module, this.courseId);
|
||||||
|
|
||||||
|
const diff = (isNaN(size) ? 0 : size) - (module.totalSize ?? 0);
|
||||||
|
|
||||||
|
module.totalSize = Number(size);
|
||||||
|
this.totalSize += diff;
|
||||||
|
if (section) {
|
||||||
|
section.totalSize += diff;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore errors, it shouldn't happen.
|
||||||
|
} finally {
|
||||||
|
module.calculatingSize = false;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.calculatingSize = false;
|
||||||
|
if (section) {
|
||||||
|
section.calculatingSize = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user has requested a delete for the whole course data.
|
* The user has requested a delete for the whole course data.
|
||||||
*
|
*
|
||||||
|
@ -406,7 +455,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
} finally {
|
} finally {
|
||||||
modal.dismiss();
|
modal.dismiss();
|
||||||
|
|
||||||
await this.loadSizes();
|
await this.updateModulesSizes(modules, section);
|
||||||
CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
|
CoreCourseHelper.calculateSectionsStatus(this.sections, this.courseId, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,7 +504,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
|
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await this.loadSizes();
|
await this.updateModulesSizes(section.modules, section);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// User cancelled or there was an error calculating the size.
|
// User cancelled or there was an error calculating the size.
|
||||||
|
@ -501,7 +550,7 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
} finally {
|
} finally {
|
||||||
module.spinner = false;
|
module.spinner = false;
|
||||||
|
|
||||||
await this.loadSizes();
|
await this.updateModulesSizes([module]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,13 +662,13 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
type AddonStorageManagerCourseSection = Omit<CoreCourseSectionWithStatus, 'modules'> & {
|
type AddonStorageManagerCourseSection = Omit<CoreCourseSectionWithStatus, 'modules'> & {
|
||||||
totalSize: number;
|
totalSize: number;
|
||||||
sizeLoaded?: boolean;
|
calculatingSize: boolean;
|
||||||
modules: AddonStorageManagerModule[];
|
modules: AddonStorageManagerModule[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type AddonStorageManagerModule = CoreCourseModuleData & {
|
type AddonStorageManagerModule = CoreCourseModuleData & {
|
||||||
totalSize?: number;
|
totalSize?: number;
|
||||||
sizeLoaded?: boolean;
|
calculatingSize: boolean;
|
||||||
prefetchHandler?: CoreCourseModulePrefetchHandler;
|
prefetchHandler?: CoreCourseModulePrefetchHandler;
|
||||||
spinner?: boolean;
|
spinner?: boolean;
|
||||||
downloadStatus?: string;
|
downloadStatus?: string;
|
||||||
|
|
Loading…
Reference in New Issue