MOBILE-3954 course: Remove prefetch module from course and blocks

main
Pau Ferrer Ocaña 2021-12-23 15:11:21 +01:00
parent 255e987412
commit 018ebd6bb2
23 changed files with 30 additions and 548 deletions

View File

@ -12,7 +12,6 @@
</ion-label> </ion-label>
</ion-item> </ion-item>
<core-course-module *ngFor="let module of mainMenuBlock.modules" [module]="module" [courseId]="siteHomeId" <core-course-module *ngFor="let module of mainMenuBlock.modules" [module]="module" [section]="mainMenuBlock"></core-course-module>
[downloadEnabled]="downloadEnabled" [section]="mainMenuBlock"></core-course-module>
</ng-container> </ng-container>
</core-loading> </core-loading>

View File

@ -27,7 +27,6 @@ import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks';
export class CoreBlockSideBlocksButtonComponent { export class CoreBlockSideBlocksButtonComponent {
@Input() courseId!: number; @Input() courseId!: number;
@Input() downloadEnabled = false;
/** /**
* Open side blocks. * Open side blocks.
@ -37,7 +36,6 @@ export class CoreBlockSideBlocksButtonComponent {
component: CoreBlockSideBlocksComponent, component: CoreBlockSideBlocksComponent,
componentProps: { componentProps: {
courseId: this.courseId, courseId: this.courseId,
downloadEnabled: this.downloadEnabled,
}, },
}); });
} }

View File

@ -17,8 +17,7 @@
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<ion-list *ngIf="blocks.length > 0"> <ion-list *ngIf="blocks.length > 0">
<ng-container *ngFor="let block of blocks"> <ng-container *ngFor="let block of blocks">
<core-block *ngIf="block.visible" [block]="block" contextLevel="course" [instanceId]="courseId" <core-block *ngIf="block.visible" [block]="block" contextLevel="course" [instanceId]="courseId"></core-block>
[extraData]="{'downloadEnabled': downloadEnabled}"></core-block>
</ng-container> </ng-container>
</ion-list> </ion-list>

View File

@ -33,7 +33,6 @@ import { CoreCoursesDashboard } from '@features/courses/services/dashboard';
export class CoreBlockSideBlocksComponent implements OnInit { export class CoreBlockSideBlocksComponent implements OnInit {
@Input() courseId?: number; @Input() courseId?: number;
@Input() downloadEnabled = false;
@ViewChildren(CoreBlockComponent) blocksComponents?: QueryList<CoreBlockComponent>; @ViewChildren(CoreBlockComponent) blocksComponents?: QueryList<CoreBlockComponent>;

View File

@ -11,10 +11,8 @@
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<!-- Section selector. --> <!-- Section selector. -->
<core-dynamic-component [component]="sectionSelectorComponent" [data]="data"> <core-dynamic-component [component]="sectionSelectorComponent" [data]="data">
<div *ngIf="displaySectionSelector && sections && hasSeveralSections" <div *ngIf="displaySectionSelector && sections && hasSeveralSections"
class="ion-text-wrap ion-justify-content-between ion-align-items-center core-button-selector-row" class="ion-text-wrap ion-justify-content-between ion-align-items-center core-button-selector-row">
[class.core-section-download]="downloadEnabled">
<core-combobox [modalOptions]="sectionSelectorModalOptions" interface="modal" listboxId="core-course-section-button" <core-combobox [modalOptions]="sectionSelectorModalOptions" interface="modal" listboxId="core-course-section-button"
icon="fas-folder" [label]="'core.course.section' | translate" icon="fas-folder" [label]="'core.course.section' | translate"
[selection]="selectedSection ? selectedSection.name : 'core.course.sections' | translate" [selection]="selectedSection ? selectedSection.name : 'core.course.sections' | translate"
@ -26,16 +24,14 @@
<ng-container *ngIf="!selectedSection">{{ 'core.course.sections' | translate }}</ng-container> <ng-container *ngIf="!selectedSection">{{ 'core.course.sections' | translate }}</ng-container>
</span> </span>
</core-combobox> </core-combobox>
<!-- Section download. -->
<ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: selectedSection}"></ng-container>
</div> </div>
</core-dynamic-component> </core-dynamic-component>
<!-- Course summary. By default we only display the course progress. --> <!-- Course summary. By default we only display the course progress. -->
<core-dynamic-component [component]="courseSummaryComponent" [data]="data"> <core-dynamic-component [component]="courseSummaryComponent" [data]="data">
<ion-list lines="none" class="core-format-progress-list" *ngIf="imageThumb || (selectedSection?.id == allSectionsId && progress !== undefined) || <ion-list *ngIf="imageThumb || (selectedSection?.id == allSectionsId && progress !== undefined) ||
(selectedSection && selectedSection.id != allSectionsId && (selectedSection && selectedSection.id != allSectionsId &&
(selectedSection.availabilityinfo || selectedSection.visible === 0))"> (selectedSection.availabilityinfo || selectedSection.visible === 0))" lines="none" class="core-format-progress-list">
<div *ngIf="imageThumb" class="core-course-thumb"> <div *ngIf="imageThumb" class="core-course-thumb">
<img [src]="imageThumb" core-external-content alt="" /> <img [src]="imageThumb" core-external-content alt="" />
</div> </div>
@ -103,8 +99,7 @@
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>
<core-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id" <core-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id">
[downloadEnabled]="downloadEnabled">
</core-block-side-blocks-button> </core-block-side-blocks-button>
</core-loading> </core-loading>
</core-dynamic-component> </core-dynamic-component>
@ -114,7 +109,7 @@
<section *ngIf="!section.hiddenbynumsections && section.id != allSectionsId && section.id != stealthModulesSectionId"> <section *ngIf="!section.hiddenbynumsections && section.id != allSectionsId && section.id != stealthModulesSectionId">
<!-- Title is only displayed when viewing all sections. --> <!-- Title is only displayed when viewing all sections. -->
<ion-item-divider *ngIf="selectedSection?.id == allSectionsId && section.name" class="ion-text-wrap" color="light" <ion-item-divider *ngIf="selectedSection?.id == allSectionsId && section.name" class="ion-text-wrap" color="light"
[class.core-section-download]="downloadEnabled" [class.item-dimmed]="section.visible === 0 || section.uservisible === false"> [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
<ion-label> <ion-label>
<h2> <h2>
<core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id"> <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id">
@ -133,8 +128,6 @@
</ion-badge> </ion-badge>
</p> </p>
</ion-label> </ion-label>
<!-- Section download. -->
<ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: section}"></ng-container>
</ion-item-divider> </ion-item-divider>
<ion-item class="ion-text-wrap" *ngIf="section.summary"> <ion-item class="ion-text-wrap" *ngIf="section.summary">
@ -145,28 +138,10 @@
</ion-item> </ion-item>
<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" [section]="section"
[downloadEnabled]="downloadEnabled" [section]="section" (completionChanged)="onCompletionChange($event)" (completionChanged)="onCompletionChange($event)" [showActivityDates]="course?.showactivitydates"
(statusChanged)="onModuleStatusChange()" [showActivityDates]="course?.showactivitydates"
[showCompletionConditions]="course?.showcompletionconditions"> [showCompletionConditions]="course?.showcompletionconditions">
</core-course-module> </core-course-module>
</ng-container> </ng-container>
</section> </section>
</ng-template> </ng-template>
<!-- Template to render a section download button/progress. -->
<ng-template #sectionDownloadTemplate let-section="section">
<div *ngIf="section && downloadEnabled" slot="end" class="core-button-spinner">
<!-- Download progress. -->
<ion-badge class="core-course-download-section-progress"
*ngIf="section.isDownloading && section.total > 0 && section.count < section.total" role="progressbar"
[attr.aria-valuemax]="section.total" [attr.aria-valuenow]="section.count"
[attr.aria-valuetext]="'core.course.downloadsectionprogressdescription' | translate:section">
{{section.count}} / {{section.total}}
</ion-badge>
<core-download-refresh [status]="section.downloadStatus" [enabled]="downloadEnabled" (action)="prefetch(section)"
[loading]="section.isDownloading || section.isCalculating" [canTrustDownload]="true" size="small">
</core-download-refresh>
</div>
</ng-template>

View File

@ -55,21 +55,6 @@
} }
} }
.core-section-download {
core-combobox {
max-width: calc(100% - 64px);
}
.core-button-spinner {
display: flex;
align-items: center;
@include margin-horizontal(10px);
ion-badge.core-course-download-courses-progress {
@include margin(null, 12px, null, null);
}
}
}
.core-course-section-nav-buttons { .core-course-section-nav-buttons {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View File

@ -27,7 +27,6 @@ import {
ElementRef, ElementRef,
} from '@angular/core'; } from '@angular/core';
import { ModalOptions } from '@ionic/core'; import { ModalOptions } from '@ionic/core';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
@ -37,17 +36,14 @@ import {
CoreCourseProvider, CoreCourseProvider,
} from '@features/course/services/course'; } from '@features/course/services/course';
import { import {
CoreCourseHelper,
CoreCourseModuleData, CoreCourseModuleData,
CoreCourseModuleCompletionData, CoreCourseModuleCompletionData,
CoreCourseSection, CoreCourseSection,
CoreCourseSectionWithStatus,
} from '@features/course/services/course-helper'; } 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 } from '@singletons/events'; import { CoreEventObserver, CoreEvents } 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 '@features/course/services/module-prefetch-delegate';
import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector'; import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector';
import { CoreBlockHelper } from '@features/block/services/block-helper'; import { CoreBlockHelper } from '@features/block/services/block-helper';
@ -71,8 +67,7 @@ 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?: CoreCourseSectionWithStatus[]; // List of course sections. The status will be calculated in this component. @Input() sections?: CoreCourseSection[]; // List of course sections.
@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).
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section. @Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
@ -108,7 +103,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
componentProps: {}, componentProps: {},
}; };
protected sectionStatusObserver?: CoreEventObserver;
protected selectTabObserver?: CoreEventObserver; protected selectTabObserver?: CoreEventObserver;
protected lastCourseFormat?: string; protected lastCourseFormat?: string;
protected sectionSelectorExpanded = false; protected sectionSelectorExpanded = false;
@ -125,38 +119,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* Component being initialized. * Component being initialized.
*/ */
ngOnInit(): void { ngOnInit(): void {
// Listen for section status changes.
this.sectionStatusObserver = CoreEvents.on(
CoreEvents.SECTION_STATUS_CHANGED,
async (data) => {
if (!this.downloadEnabled || !this.sections?.length || !data.sectionId || data.courseId != this.course?.id) {
return;
}
// Check if the affected section is being downloaded.
// If so, we don't update section status because it'll already be updated when the download finishes.
const downloadId = CoreCourseHelper.getSectionDownloadId({ id: data.sectionId });
if (CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
return;
}
// Get the affected section.
const section = this.sections.find(section => section.id == data.sectionId);
if (!section) {
return;
}
// Recalculate the status.
await CoreCourseHelper.calculateSectionStatus(section, this.course.id, false);
if (section.isDownloading && !CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
// All the modules are now downloading, set a download all promise.
this.prefetch(section);
}
},
CoreSites.getCurrentSiteId(),
);
// Listen for select course tab events to select the right section if needed. // Listen for select course tab events to select the right section if needed.
this.selectTabObserver = CoreEvents.on(CoreEvents.SELECT_COURSE_TAB, (data) => { this.selectTabObserver = CoreEvents.on(CoreEvents.SELECT_COURSE_TAB, (data) => {
if (data.name) { if (data.name) {
@ -205,10 +167,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.sectionSelectorModalOptions.componentProps!.sections = this.sections; this.sectionSelectorModalOptions.componentProps!.sections = this.sections;
this.treatSections(this.sections); this.treatSections(this.sections);
} }
if (this.downloadEnabled && (changes.downloadEnabled || changes.sections)) {
this.calculateSectionsStatus(false);
}
} }
/** /**
@ -219,7 +177,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.data.sections = this.sections; this.data.sections = this.sections;
this.data.initialSectionId = this.initialSectionId; this.data.initialSectionId = this.initialSectionId;
this.data.initialSectionNumber = this.initialSectionNumber; this.data.initialSectionNumber = this.initialSectionNumber;
this.data.downloadEnabled = this.downloadEnabled;
this.data.moduleId = this.moduleId; this.data.moduleId = this.moduleId;
this.data.completionChanged = this.completionChanged; this.data.completionChanged = this.completionChanged;
} }
@ -394,9 +351,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.canLoadMore = false; this.canLoadMore = false;
this.showSectionId = 0; this.showSectionId = 0;
this.showMoreActivities(); this.showMoreActivities();
if (this.downloadEnabled) {
this.calculateSectionsStatus(false);
}
} }
if (this.moduleId && previousValue === undefined) { if (this.moduleId && previousValue === undefined) {
@ -432,62 +386,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
return section1 && section2 ? section1.id === section2.id : section1 === section2; return section1 && section2 ? section1.id === section2.id : section1 === section2;
} }
/**
* Calculate the status of sections.
*
* @param refresh If refresh or not.
*/
protected calculateSectionsStatus(refresh?: boolean): void {
if (!this.sections || !this.course) {
return;
}
CoreUtils.ignoreErrors(CoreCourseHelper.calculateSectionsStatus(this.sections, this.course.id, refresh));
}
/**
* Confirm and prefetch a section. If the section is "all sections", prefetch all the sections.
*
* @param section Section to download.
* @param refresh Refresh clicked (not used).
*/
async prefetch(section: CoreCourseSectionWithStatus): Promise<void> {
section.isCalculating = true;
try {
await CoreCourseHelper.confirmDownloadSizeSection(this.course!.id, section, this.sections);
await this.prefetchSection(section, true);
} catch (error) {
// User cancelled or there was an error calculating the size.
if (error) {
CoreDomUtils.showErrorModal(error);
}
} finally {
section.isCalculating = false;
}
}
/**
* Prefetch a section.
*
* @param section The section to download.
* @param manual Whether the prefetch was started manually or it was automatically started because all modules
* are being downloaded.
*/
protected async prefetchSection(section: CoreCourseSectionWithStatus, manual?: boolean): Promise<void> {
try {
await CoreCourseHelper.prefetchSection(section, this.course!.id, this.sections);
} catch (error) {
// Don't show error message if it's an automatic download.
if (!manual) {
return;
}
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
}
}
/** /**
* Refresh the data. * Refresh the data.
* *
@ -580,7 +478,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.sectionStatusObserver && this.sectionStatusObserver.off();
this.selectTabObserver && this.selectTabObserver.off(); this.selectTabObserver && this.selectTabObserver.off();
} }
@ -591,17 +488,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.dynamicComponents?.forEach((component) => { this.dynamicComponents?.forEach((component) => {
component.callComponentFunction('ionViewDidEnter'); component.callComponentFunction('ionViewDidEnter');
}); });
if (!this.downloadEnabled || !this.course || !this.sections) {
return;
}
// The download status of a section might have been changed from within a module page.
if (this.selectedSection && this.selectedSection.id !== CoreCourseProvider.ALL_SECTIONS_ID) {
CoreCourseHelper.calculateSectionStatus(this.selectedSection, this.course.id, false, false);
} else {
CoreCourseHelper.calculateSectionsStatus(this.sections, this.course.id, false, false);
}
} }
/** /**
@ -653,17 +539,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.updateProgress(); this.updateProgress();
} }
/**
* Recalculate the download status of each section, in response to a module being downloaded.
*/
onModuleStatusChange(): void {
if (!this.downloadEnabled || !this.sections || !this.course) {
return;
}
CoreCourseHelper.calculateSectionsStatus(this.sections, this.course.id, false, false);
}
/** /**
* Update course progress. * Update course progress.
*/ */

View File

@ -15,7 +15,7 @@
<ion-label class="core-module-title"> <ion-label class="core-module-title">
<p class="item-heading"> <p class="item-heading">
<core-format-text [text]="module.handlerData.title" contextLevel="module" [contextInstanceId]="module.id" <core-format-text [text]="module.handlerData.title" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="courseId" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated"> [courseId]="module.course" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
</core-format-text> </core-format-text>
</p> </p>
<ion-badge *ngIf="module.handlerData.extraBadge" [color]="module.handlerData.extraBadgeColor" <ion-badge *ngIf="module.handlerData.extraBadge" [color]="module.handlerData.extraBadgeColor"
@ -31,7 +31,7 @@
<div class="core-module-availabilityinfo" *ngIf="module.availabilityinfo"> <div class="core-module-availabilityinfo" *ngIf="module.availabilityinfo">
<ion-badge class="ion-text-wrap">{{ 'core.restricted' | translate }}</ion-badge> <ion-badge class="ion-text-wrap">{{ 'core.restricted' | translate }}</ion-badge>
<core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id" <core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="courseId" class="ion-text-wrap"> [courseId]="module.course" class="ion-text-wrap">
</core-format-text> </core-format-text>
</div> </div>
<ion-badge *ngIf="module.completiondata?.offline" color="warning" class="ion-text-wrap"> <ion-badge *ngIf="module.completiondata?.offline" color="warning" class="ion-text-wrap">
@ -48,13 +48,9 @@
</core-course-module-completion-legacy> </core-course-module-completion-legacy>
<div class="core-module-buttons-more"> <div class="core-module-buttons-more">
<core-download-refresh [status]="downloadStatus" [enabled]="downloadEnabled" [canTrustDownload]="true"
[loading]="spinner || module.handlerData.spinner" (action)="download($event)" size="small">
</core-download-refresh>
<!-- Buttons defined by the module handler. --> <!-- Buttons defined by the module handler. -->
<ion-button fill="clear" *ngFor="let button of module.handlerData.buttons" color="dark" size="small" <ion-button fill="clear" *ngFor="let button of module.handlerData.buttons" color="dark" size="small"
[hidden]="button.hidden || spinner || module.handlerData.spinner" class="core-animate-show-hide" [hidden]="button.hidden || module.handlerData.spinner" class="core-animate-show-hide"
(click)="buttonClicked($event, button)" [attr.aria-label]="button.label | translate:{$a: module.handlerData.title}"> (click)="buttonClicked($event, button)" [attr.aria-label]="button.label | translate:{$a: module.handlerData.title}">
<ion-icon [name]="button.icon" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon [name]="button.icon" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
@ -81,7 +77,7 @@
</core-course-module-completion> </core-course-module-completion>
<core-format-text class="core-module-description" *ngIf="module.description" [maxHeight]="80" [text]="module.description" <core-format-text class="core-module-description" *ngIf="module.description" [maxHeight]="80" [text]="module.description"
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
</core-format-text> </core-format-text>
</ion-label> </ion-label>
</ion-item> </ion-item>

View File

@ -15,20 +15,13 @@
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 { CoreEventObserver, CoreEvents } from '@singletons/events';
import { import {
CoreCourseHelper,
CoreCourseModuleData, CoreCourseModuleData,
CoreCourseModuleCompletionData, CoreCourseModuleCompletionData,
CoreCourseSection, CoreCourseSection,
} from '@features/course/services/course-helper'; } from '@features/course/services/course-helper';
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate'; import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-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.
@ -45,47 +38,20 @@ import {
export class CoreCourseModuleComponent implements OnInit, OnDestroy { export class CoreCourseModuleComponent implements OnInit, OnDestroy {
@Input() module!: CoreCourseModuleData; // The module to render. @Input() module!: CoreCourseModuleData; // The module to render.
@Input() courseId?: number; // The course the module belongs to.
@Input() section?: CoreCourseSection; // The section the module belongs to. @Input() section?: CoreCourseSection; // The section the module belongs to.
@Input() showActivityDates = false; // Whether to show activity dates. @Input() showActivityDates = false; // Whether to show activity dates.
@Input() showCompletionConditions = false; // Whether to show activity completion conditions. @Input() showCompletionConditions = false; // Whether to show activity completion conditions.
// eslint-disable-next-line @angular-eslint/no-input-rename
@Input('downloadEnabled') set enabled(value: boolean) {
this.downloadEnabled = value;
if (!this.module.handlerData?.showDownloadButton || !this.downloadEnabled || this.statusCalculated) {
return;
}
// First time that the download is enabled. Initialize the data.
this.statusCalculated = true;
this.spinner = true; // Show spinner while calculating the status.
// Get current status to decide which icon should be shown.
this.calculateAndShowStatus();
}
@Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when module completion changes. @Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when module completion changes.
@Output() statusChanged = new EventEmitter<CoreCourseModuleStatusChangedData>(); // Notify when the download status changes.
downloadStatus?: string;
spinner?: boolean; // Whether to display a loading spinner.
downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
modNameTranslated = ''; modNameTranslated = '';
hasInfo = false; hasInfo = false;
showLegacyCompletion = false; // Whether to show module completion in the old format. showLegacyCompletion = false; // Whether to show module completion in the old format.
showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled. showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled.
protected prefetchHandler?: CoreCourseModulePrefetchHandler;
protected statusObserver?: CoreEventObserver;
protected statusCalculated = false;
protected isDestroyed = false;
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit(): void { ngOnInit(): void {
this.courseId = this.courseId || this.module.course;
this.modNameTranslated = CoreCourse.translateModuleName(this.module.modname) || ''; this.modNameTranslated = CoreCourse.translateModuleName(this.module.modname) || '';
this.showLegacyCompletion = !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.11'); this.showLegacyCompletion = !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.11');
this.checkShowManualCompletion(); this.checkShowManualCompletion();
@ -103,29 +69,6 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
(this.showCompletionConditions && this.module.completiondata.isautomatic)) (this.showCompletionConditions && this.module.completiondata.isautomatic))
) )
); );
if (this.module.handlerData.showDownloadButton) {
// Listen for changes on this module status, even if download isn't enabled.
this.prefetchHandler = CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(this.module.modname);
this.statusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
if (!this.module || data.componentId != this.module.id || !this.prefetchHandler ||
data.component != this.prefetchHandler.component) {
return;
}
// Call determineModuleStatus to get the right status to display.
const status = CoreCourseModulePrefetchDelegate.determineModuleStatus(this.module, data.status);
if (this.downloadEnabled) {
// Download is enabled, show the status.
this.showStatus(status);
} else if (this.module.handlerData?.updateStatus) {
// Download isn't enabled but the handler defines a updateStatus function, call it anyway.
this.module.handlerData.updateStatus(status);
}
}, CoreSites.getCurrentSiteId());
}
} }
/** /**
@ -143,7 +86,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
*/ */
moduleClicked(event: Event): void { moduleClicked(event: Event): void {
if (this.module.uservisible !== false && this.module.handlerData?.action) { if (this.module.uservisible !== false && this.module.handlerData?.action) {
this.module.handlerData.action(event, this.module, this.courseId!); this.module.handlerData.action(event, this.module, this.module.course);
} }
} }
@ -161,91 +104,14 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
button.action(event, this.module, this.courseId!); button.action(event, this.module, this.module.course);
}
/**
* Download the module.
*
* @param refresh Whether it's refreshing.
* @return Promise resolved when done.
*/
async download(refresh: boolean): Promise<void> {
if (!this.prefetchHandler || !this.module) {
return;
}
// Show spinner since this operation might take a while.
this.spinner = true;
try {
// Get download size to ask for confirm if it's high.
const size = await this.prefetchHandler.getDownloadSize(this.module, this.module.course, true);
await CoreCourseHelper.prefetchModule(this.prefetchHandler, this.module, size, this.module.course, refresh);
const eventData = {
sectionId: this.section?.id,
moduleId: this.module.id,
courseId: this.module.course,
};
this.statusChanged.emit(eventData);
} catch (error) {
// Error, hide spinner.
this.spinner = false;
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true);
}
}
}
/**
* Show download buttons according to module status.
*
* @param status Module status.
*/
protected showStatus(status: string): void {
if (!status) {
return;
}
this.spinner = false;
this.downloadStatus = 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.getModuleStatus(this.module, this.courseId);
this.showStatus(status);
} }
/** /**
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
// this.statusObserver?.off();
this.module.handlerData?.onDestroy?.(); this.module.handlerData?.onDestroy?.();
this.isDestroyed = true;
} }
} }
/**
* Data sent to the status changed output.
*/
export type CoreCourseModuleStatusChangedData = {
moduleId: number;
courseId: number;
sectionId?: number;
};

View File

@ -1,4 +1,4 @@
<core-dynamic-component [component]="componentClass" [data]="data"></core-dynamic-component> <core-dynamic-component [component]="componentClass" [data]="data"></core-dynamic-component>
<core-block-side-blocks-button *ngIf="course && hasBlocks" [courseId]="course.id" [downloadEnabled]="downloadEnabled"> <core-block-side-blocks-button *ngIf="course && hasBlocks" [courseId]="course.id">
</core-block-side-blocks-button> </core-block-side-blocks-button>

View File

@ -36,7 +36,6 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
@Input() course?: CoreCourseAnyCourseData; // The course to render. @Input() course?: CoreCourseAnyCourseData; // The course to render.
@Input() sections?: CoreCourseSectionWithStatus[]; // List of course sections. @Input() sections?: CoreCourseSectionWithStatus[]; // List of course sections.
@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).
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section. @Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.

View File

@ -68,13 +68,6 @@ export class CoreCourseFormatSingleActivityHandlerService implements CoreCourseF
return ''; return '';
} }
/**
* @inheritdoc
*/
displayEnableDownload(): boolean {
return false;
}
/** /**
* @inheritdoc * @inheritdoc
*/ */

View File

@ -1,12 +1,5 @@
<core-navbar-buttons slot="end"> <core-navbar-buttons slot="end">
<core-context-menu> <core-context-menu>
<core-context-menu-item [hidden]="!displayEnableDownload" [priority]="2000" iconAction="toggle" [(toggle)]="downloadEnabled"
[content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()">
</core-context-menu-item>
<core-context-menu-item [hidden]="!downloadCourseEnabled" [priority]="1900"
[content]="prefetchCourseData.statusTranslatable | translate" (action)="prefetchCourse()" [iconAction]="prefetchCourseData.icon"
[closeOnClick]="false">
</core-context-menu-item>
<core-context-menu-item [priority]="1800" [content]="'core.course.coursesummary' | translate" (action)="openCourseSummary()" <core-context-menu-item [priority]="1800" [content]="'core.course.coursesummary' | translate" (action)="openCourseSummary()"
iconAction="fas-graduation-cap"> iconAction="fas-graduation-cap">
</core-context-menu-item> </core-context-menu-item>
@ -22,8 +15,7 @@
<core-loading [hideUntil]="dataLoaded"> <core-loading [hideUntil]="dataLoaded">
<core-course-format [course]="course" [sections]="sections" [initialSectionId]="sectionId" [initialSectionNumber]="sectionNumber" <core-course-format [course]="course" [sections]="sections" [initialSectionId]="sectionId" [initialSectionNumber]="sectionNumber"
[downloadEnabled]="downloadEnabled" [moduleId]="moduleId" (completionChanged)="onCompletionChange($event)" [moduleId]="moduleId" (completionChanged)="onCompletionChange($event)" class="core-course-format-{{course.format}}">
class="core-course-format-{{course.format}}">
</core-course-format> </core-course-format>
</core-loading> </core-loading>
</ion-content> </ion-content>

View File

@ -15,20 +15,17 @@
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { IonContent, IonRefresher } from '@ionic/angular'; import { IonContent, IonRefresher } from '@ionic/angular';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreCourses, CoreCourseAnyCourseData, CoreCoursesProvider } from '@features/courses/services/courses'; import { CoreCourses, CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { import {
CoreCourse, CoreCourse,
CoreCourseCompletionActivityStatus, CoreCourseCompletionActivityStatus,
CoreCourseProvider,
} from '@features/course/services/course'; } from '@features/course/services/course';
import { import {
CoreCourseHelper, CoreCourseHelper,
CoreCourseModuleCompletionData, CoreCourseModuleCompletionData,
CoreCourseSection, CoreCourseSection,
CorePrefetchStatusInfo,
} from '@features/course/services/course-helper'; } 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';
@ -43,7 +40,6 @@ import {
CoreEventObserver, CoreEventObserver,
} from '@singletons/events'; } from '@singletons/events';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreConstants } from '@/core/constants';
/** /**
* Page that displays the contents of a course. * Page that displays the contents of a course.
@ -63,47 +59,19 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
sectionNumber?: number; sectionNumber?: number;
courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = []; courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = [];
dataLoaded = false; dataLoaded = false;
downloadEnabled = false;
downloadCourseEnabled = false; downloadCourseEnabled = false;
moduleId?: number; moduleId?: number;
displayEnableDownload = false; displayEnableDownload = false;
displayRefresher = false; displayRefresher = false;
prefetchCourseData: CorePrefetchStatusInfo = {
icon: CoreConstants.ICON_LOADING,
statusTranslatable: 'core.course.downloadcourse',
status: '',
loading: true,
};
protected formatOptions?: Record<string, unknown>; protected formatOptions?: Record<string, unknown>;
protected completionObserver?: CoreEventObserver; protected completionObserver?: CoreEventObserver;
protected courseStatusObserver?: CoreEventObserver;
protected siteUpdatedObserver?: CoreEventObserver;
protected downloadEnabledObserver?: CoreEventObserver;
protected syncObserver?: CoreEventObserver; protected syncObserver?: CoreEventObserver;
protected isDestroyed = false; protected isDestroyed = false;
protected modulesHaveCompletion = false; protected modulesHaveCompletion = false;
protected isGuest?: boolean; protected isGuest = false;
protected debouncedUpdateCachedCompletion?: () => void; // Update the cached completion after a certain time. protected debouncedUpdateCachedCompletion?: () => void; // Update the cached completion after a certain time.
constructor() {
// Refresh the enabled flags if site is updated.
this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled() &&
CoreCourseFormatDelegate.displayEnableDownload(this.course);
this.downloadEnabled = this.displayEnableDownload && this.downloadEnabled;
this.initListeners();
}, CoreSites.getCurrentSiteId());
this.downloadEnabledObserver = CoreEvents.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED, (data) => {
this.downloadEnabled = this.displayEnableDownload && data.enabled;
});
}
/** /**
* @inheritdoc * @inheritdoc
*/ */
@ -121,13 +89,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
this.sectionId = CoreNavigator.getRouteNumberParam('sectionId'); this.sectionId = CoreNavigator.getRouteNumberParam('sectionId');
this.sectionNumber = CoreNavigator.getRouteNumberParam('sectionNumber'); this.sectionNumber = CoreNavigator.getRouteNumberParam('sectionNumber');
this.moduleId = CoreNavigator.getRouteNumberParam('moduleId'); this.moduleId = CoreNavigator.getRouteNumberParam('moduleId');
this.isGuest = CoreNavigator.getRouteBooleanParam('isGuest'); this.isGuest = !!CoreNavigator.getRouteBooleanParam('isGuest');
this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled() &&
CoreCourseFormatDelegate.displayEnableDownload(this.course);
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
this.downloadEnabled = this.displayEnableDownload && CoreCourses.getCourseDownloadOptionsEnabled();
this.debouncedUpdateCachedCompletion = CoreUtils.debounce(() => { this.debouncedUpdateCachedCompletion = CoreUtils.debounce(() => {
if (this.modulesHaveCompletion) { if (this.modulesHaveCompletion) {
@ -149,8 +111,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
await this.loadData(false, true); await this.loadData(false, true);
this.dataLoaded = true; this.dataLoaded = true;
this.initPrefetch();
} }
/** /**
@ -159,15 +119,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
protected async initListeners(): Promise<void> { protected async initListeners(): Promise<void> {
if (this.downloadCourseEnabled && !this.courseStatusObserver) {
// Listen for changes in course status.
this.courseStatusObserver = CoreEvents.on(CoreEvents.COURSE_STATUS_CHANGED, (data) => {
if (data.courseId == this.course.id || data.courseId == CoreCourseProvider.ALL_COURSES_CLEARED) {
this.updateCourseStatus(data.status);
}
}, CoreSites.getCurrentSiteId());
}
// Check if the course format requires the view to be refreshed when completion changes. // Check if the course format requires the view to be refreshed when completion changes.
const shouldRefresh = await CoreCourseFormatDelegate.shouldRefreshWhenCompletionChanges(this.course); const shouldRefresh = await CoreCourseFormatDelegate.shouldRefreshWhenCompletionChanges(this.course);
if (!shouldRefresh) { if (!shouldRefresh) {
@ -200,41 +151,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
} }
} }
/**
* Init prefetch data if needed.
*
* @return Promise resolved when done.
*/
protected async initPrefetch(): Promise<void> {
if (!this.downloadCourseEnabled) {
// Cannot download the whole course, stop.
return;
}
// Determine the course prefetch status.
await this.determineCoursePrefetchIcon();
if (this.prefetchCourseData.icon != CoreConstants.ICON_LOADING) {
return;
}
// Course is being downloaded. Get the download promise.
const promise = CoreCourseHelper.getCourseDownloadPromise(this.course.id);
if (promise) {
// There is a download promise. Show an error if it fails.
promise.catch((error) => {
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
}
});
} else {
// No download, this probably means that the app was closed while downloading. Set previous status.
const status = await CoreCourse.setCoursePreviousStatus(this.course.id);
this.updateCourseStatus(status);
}
}
/** /**
* Fetch and load all the data required for the view. * Fetch and load all the data required for the view.
* *
@ -463,59 +379,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
} }
} }
/**
* Determines the prefetch icon of the course.
*
* @return Promise resolved when done.
*/
protected async determineCoursePrefetchIcon(): Promise<void> {
this.prefetchCourseData = await CoreCourseHelper.getCourseStatusIconAndTitle(this.course.id);
}
/**
* Prefetch the whole course.
*/
async prefetchCourse(): Promise<void> {
try {
await CoreCourseHelper.confirmAndPrefetchCourse(
this.prefetchCourseData,
this.course,
{
sections: this.sections,
menuHandlers: this.courseMenuHandlers,
isGuest: this.isGuest,
},
);
} catch (error) {
if (this.isDestroyed) {
return;
}
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
}
}
/**
* Toggle download enabled.
*/
toggleDownload(): void {
CoreCourses.setCourseDownloadOptionsEnabled(this.downloadEnabled);
}
/**
* Update the course status icon and title.
*
* @param status Status to show.
*/
protected updateCourseStatus(status: string): void {
const statusData = CoreCourseHelper.getCoursePrefetchStatusInfo(status);
this.prefetchCourseData.status = statusData.status;
this.prefetchCourseData.icon = statusData.icon;
this.prefetchCourseData.statusTranslatable = statusData.statusTranslatable;
this.prefetchCourseData.loading = statusData.loading;
}
/** /**
* Open the course summary * Open the course summary
*/ */
@ -542,10 +405,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
ngOnDestroy(): void { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
this.completionObserver?.off(); this.completionObserver?.off();
this.courseStatusObserver?.off();
this.syncObserver?.off(); this.syncObserver?.off();
this.siteUpdatedObserver?.off();
this.downloadEnabledObserver?.off();
} }
/** /**

View File

@ -19,8 +19,7 @@
<ion-list> <ion-list>
<ng-container *ngFor="let section of sections"> <ng-container *ngFor="let section of sections">
<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" [section]="section" [courseId]="courseId" <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section">
[downloadEnabled]="downloadEnabled">
</core-course-module> </core-course-module>
</ng-container> </ng-container>
</ng-container> </ng-container>

View File

@ -14,7 +14,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
@ -36,7 +35,6 @@ export class CoreCourseListModTypePage implements OnInit {
sections: CoreCourseSection[] = []; sections: CoreCourseSection[] = [];
title = ''; title = '';
loaded = false; loaded = false;
downloadEnabled = false;
courseId?: number; courseId?: number;
protected modName?: string; protected modName?: string;
@ -49,7 +47,6 @@ export class CoreCourseListModTypePage implements OnInit {
this.title = CoreNavigator.getRouteParam('title') || ''; this.title = CoreNavigator.getRouteParam('title') || '';
this.courseId = CoreNavigator.getRouteNumberParam('courseId'); this.courseId = CoreNavigator.getRouteNumberParam('courseId');
this.modName = CoreNavigator.getRouteParam('modName'); this.modName = CoreNavigator.getRouteParam('modName');
this.downloadEnabled = !CoreSites.getCurrentSite()?.isOfflineDisabled();
try { try {
await this.fetchData(); await this.fetchData();

View File

@ -58,8 +58,9 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler {
displayBlocks?(course: CoreCourseAnyCourseData): boolean; displayBlocks?(course: CoreCourseAnyCourseData): boolean;
/** /**
* Whether the option to enable section/module download should be displayed. Defaults to true. * Whether the option to enable section/module download should be displayed.
* *
* @deprecated on 4.0 Not used anymore because prefetch has been moved to storage manager.
* @param course The course to check. * @param course The course to check.
* @return Whether the option to enable section/module download should be displayed. * @return Whether the option to enable section/module download should be displayed.
*/ */
@ -204,16 +205,6 @@ export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseForm
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayBlocks', [course]); return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayBlocks', [course]);
} }
/**
* Whether the option to enable section/module download should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the option to enable section/module download should be displayed
*/
displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayEnableDownload', [course]);
}
/** /**
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format, * Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
* and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true. * and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.

View File

@ -78,17 +78,6 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
return true; return true;
} }
/**
* Whether the option to enable section/module download should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the option to enable section/module download should be displayed
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
return true;
}
/** /**
* Whether the default section selector should be displayed. Defaults to true. * Whether the default section selector should be displayed. Defaults to true.
* *

View File

@ -23,7 +23,7 @@
</ng-container> </ng-container>
</ion-list> </ion-list>
<core-block-side-blocks-button *ngIf="hasSideBlocks" [downloadEnabled]="downloadEnabled"></core-block-side-blocks-button> <core-block-side-blocks-button *ngIf="hasSideBlocks"></core-block-side-blocks-button>
<core-empty-box *ngIf="blocks.length == 0" icon="fas-cubes" [message]="'core.course.nocontentavailable' | translate"> <core-empty-box *ngIf="blocks.length == 0" icon="fas-cubes" [message]="'core.course.nocontentavailable' | translate">
</core-empty-box> </core-empty-box>

View File

@ -3,8 +3,6 @@
<ion-icon name="fas-search" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-search" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
<core-context-menu> <core-context-menu>
<core-context-menu-item [priority]="1000" *ngIf="displayEnableDownload" [content]="'core.settings.showdownloadoptions' | translate"
(action)="switchDownload()" iconAction="toggle" [(toggle)]="downloadEnabled"></core-context-menu-item>
<core-context-menu-item [priority]="500" [content]="'addon.storagemanager.managestorage' | translate" <core-context-menu-item [priority]="500" [content]="'addon.storagemanager.managestorage' | translate"
(action)="manageCoursesStorage()" iconAction="fas-archive"></core-context-menu-item> (action)="manageCoursesStorage()" iconAction="fas-archive"></core-context-menu-item>
<core-context-menu-item [priority]="400" [content]="'addon.storagemanager.managecoursestorage' | translate" <core-context-menu-item [priority]="400" [content]="'addon.storagemanager.managecoursestorage' | translate"
@ -26,8 +24,7 @@
</ion-label> </ion-label>
</ion-item> </ion-item>
<core-course-module *ngFor="let module of section.modules" [module]="module" [courseId]="siteHomeId" <core-course-module *ngFor="let module of section.modules" [module]="module" [section]="section"></core-course-module>
[downloadEnabled]="downloadEnabled" [section]="section"></core-course-module>
</ng-container> </ng-container>
<!-- Site home items: news, categories, courses, etc. --> <!-- Site home items: news, categories, courses, etc. -->
@ -54,7 +51,7 @@
</ng-container> </ng-container>
</ng-container> </ng-container>
</ion-list> </ion-list>
<core-block-side-blocks-button *ngIf="hasBlocks" [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled"> <core-block-side-blocks-button *ngIf="hasBlocks" [courseId]="siteHomeId">
</core-block-side-blocks-button> </core-block-side-blocks-button>
<core-empty-box *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate"> <core-empty-box *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate">
@ -73,8 +70,7 @@
</ng-template> </ng-template>
<ng-template #news> <ng-template #news>
<core-course-module class="core-sitehome-news" *ngIf="newsForumModule" [module]="newsForumModule" [courseId]="siteHomeId" <core-course-module class="core-sitehome-news" *ngIf="newsForumModule" [module]="newsForumModule">
[downloadEnabled]="downloadEnabled">
</core-course-module> </core-course-module>
</ng-template> </ng-template>

View File

@ -21,7 +21,7 @@ import { CoreCourse, CoreCourseWSSection } from '@features/course/services/cours
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreSiteHome } from '@features/sitehome/services/sitehome'; import { CoreSiteHome } from '@features/sitehome/services/sitehome';
import { CoreCourses, CoreCoursesProvider } from '@features//courses/services/courses'; import { CoreCourses } from '@features//courses/services/courses';
import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
@ -50,24 +50,15 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
siteHomeId = 1; siteHomeId = 1;
currentSite!: CoreSite; currentSite!: CoreSite;
searchEnabled = false; searchEnabled = false;
displayEnableDownload = false;
downloadEnabled = false;
newsForumModule?: CoreCourseModuleData; newsForumModule?: CoreCourseModuleData;
protected updateSiteObserver: CoreEventObserver; protected updateSiteObserver: CoreEventObserver;
protected downloadEnabledObserver: CoreEventObserver;
constructor() { constructor() {
// Refresh the enabled flags if site is updated. // Refresh the enabled flags if site is updated.
this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => { this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite(); this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled();
}, CoreSites.getCurrentSiteId()); }, CoreSites.getCurrentSiteId());
this.downloadEnabledObserver = CoreEvents.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED, (data) => {
this.downloadEnabled = data.enabled;
});
} }
/** /**
@ -85,9 +76,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
CoreCourseHelper.openModule(module, this.siteHomeId, undefined, modParams); CoreCourseHelper.openModule(module, this.siteHomeId, undefined, modParams);
} }
this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled();
this.downloadEnabled = CoreCourses.getCourseDownloadOptionsEnabled();
this.loadContent().finally(() => { this.loadContent().finally(() => {
this.dataLoaded = true; this.dataLoaded = true;
}); });
@ -186,13 +174,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
}); });
} }
/**
* Switch download enabled.
*/
switchDownload(): void {
CoreCourses.setCourseDownloadOptionsEnabled(this.downloadEnabled);
}
/** /**
* Open page to manage courses storage. * Open page to manage courses storage.
*/ */
@ -240,7 +221,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.updateSiteObserver.off(); this.updateSiteObserver.off();
this.downloadEnabledObserver.off();
} }
} }

View File

@ -35,13 +35,6 @@ export class CoreSitePluginsCourseFormatHandler extends CoreSitePluginsBaseHandl
return this.handlerSchema.canviewallsections ?? true; return this.handlerSchema.canviewallsections ?? true;
} }
/**
* @inheritdoc
*/
displayEnableDownload(): boolean {
return this.handlerSchema.displayenabledownload ?? true;
}
/** /**
* @inheritdoc * @inheritdoc
*/ */

View File

@ -9,6 +9,7 @@ information provided here is intended especially for developers.
- CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor now admits module name instead of full module object. - CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor now admits module name instead of full module object.
- CoreCourse.getModuleBasicInfoByInstance and CoreCourse.getModuleBasicInfo have been modified to accept an "options" parameter instead of only siteId. - CoreCourse.getModuleBasicInfoByInstance and CoreCourse.getModuleBasicInfo have been modified to accept an "options" parameter instead of only siteId.
- The function CoreFilepool.isFileDownloadingByUrl now returns Promise<boolean> instead of relying on resolve/reject. - The function CoreFilepool.isFileDownloadingByUrl now returns Promise<boolean> instead of relying on resolve/reject.
- downloadEnabled input has been removed from CoreBlockSideBlocksComponent, CoreCourseFormatComponent, CoreCourseFormatSingleActivityComponent and CoreCourseModuleComponent.
=== 3.9.5 === === 3.9.5 ===