MOBILE-3915 course: Improve Course index page

main
Pau Ferrer Ocaña 2022-01-25 17:33:20 +01:00
parent 6b46f48b3c
commit 0af808571c
16 changed files with 264 additions and 95 deletions

View File

@ -1536,19 +1536,23 @@
"core.course.confirmpartialdownloadsize": "local_moodlemobileapp",
"core.course.couldnotloadsectioncontent": "local_moodlemobileapp",
"core.course.couldnotloadsections": "local_moodlemobileapp",
"core.course.courseindex": "courseformat",
"core.course.coursesummary": "moodle",
"core.course.done": "completion",
"core.course.downloadcourse": "tool_mobile",
"core.course.downloadcoursesprogressdescription": "local_moodlemobileapp",
"core.course.downloadsectionprogressdescription": "local_moodlemobileapp",
"core.course.errordownloadingcourse": "local_moodlemobileapp",
"core.course.errordownloadingsection": "local_moodlemobileapp",
"core.course.errorgetmodule": "local_moodlemobileapp",
"core.course.failed": "completion",
"core.course.gotonextactivity": "local_moodlemobileapp",
"core.course.gotonextactivitynotfound": "local_moodlemobileapp",
"core.course.gotopreviousactivity": "local_moodlemobileapp",
"core.course.gotopreviousactivitynotfound": "local_moodlemobileapp",
"core.course.hiddenfromstudents": "moodle",
"core.course.hiddenoncoursepage": "moodle",
"core.course.highlighted": "moodle",
"core.course.insufficientavailablequota": "local_moodlemobileapp",
"core.course.insufficientavailablespace": "local_moodlemobileapp",
"core.course.manualcompletionnotsynced": "local_moodlemobileapp",
@ -1557,7 +1561,8 @@
"core.course.overriddennotice": "grades",
"core.course.refreshcourse": "local_moodlemobileapp",
"core.course.section": "moodle",
"core.course.sections": "moodle",
"core.course.thisweek": "format_weeks/currentsection",
"core.course.todo": "completion",
"core.course.useactivityonbrowser": "local_moodlemobileapp",
"core.course.warningmanualcompletionmodified": "local_moodlemobileapp",
"core.course.warningofflinemanualcompletiondeleted": "local_moodlemobileapp",

View File

@ -13,49 +13,66 @@
<ion-content>
<ion-list id="core-course-section-selector" role="listbox" aria-labelledby="core-course-section-selector-label">
<ng-container *ngFor="let section of sections">
<ion-item-divider *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" class="ion-text-wrap"
(click)="selectSection(section)" [attr.aria-current]="selected?.id == section.id ? 'page' : 'false'"
[class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail="false"
[attr.aria-hidden]="section.uservisible === false" button sticky="true">
<ion-icon name="fas-folder" slot="start" aria-hidden="true"></ion-icon>
<ion-item *ngIf="allSectionId == section.id" class="ion-text-wrap divider" (click)="selectSection($event, section)" button
[class.item-current]="selectedId === section.id" detail="false">
<ion-label>
<p class="item-heading">
<core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id">
</core-format-text>
</p>
<core-progress-bar *ngIf="section.progress >= 0" [progress]="section.progress"
a11yText="core.course.aria:sectionprogress">
</core-progress-bar>
<ion-badge color="info" *ngIf="section.visible === 0 && section.uservisible !== false" class="ion-text-wrap">
{{ 'core.course.hiddenfromstudents' | translate }}
</ion-badge>
<ion-badge color="info" *ngIf="section.visible === 0 && section.uservisible === false" class="ion-text-wrap">
{{ 'core.notavailable' | translate }}
</ion-badge>
<ion-badge color="info" *ngIf="section.availabilityinfo" class="ion-text-wrap">
<core-format-text [text]=" section.availabilityinfo" contextLevel="course" [contextInstanceId]="course?.id">
</core-format-text>
</ion-badge>
</ion-label>
</ion-item-divider>
<ng-container *ngFor="let module of section.modules">
<ion-item *ngIf="module.visibleoncoursepage !== 0" class="ion-text-wrap">
<!-- TODO Add Aria, styles when disabled, etc. -->
<ion-icon name="" *ngIf="module.completionStatus === undefined" slot="start"></ion-icon>
<ion-icon name="far-circle" *ngIf="module.completionStatus === 0" slot="start"></ion-icon>
<ion-icon name="fas-circle" *ngIf="module.completionStatus === 1" color="success" slot="start"></ion-icon>
<ion-icon name="fas-circle" *ngIf="module.completionStatus === 2" color="success" slot="start"></ion-icon>
<ion-icon name="fas-circle" *ngIf="module.completionStatus === 3" color="danger" slot="start"></ion-icon>
</ion-item>
<ng-container *ngIf="allSectionId != section.id && !section.hiddenbynumsections &&
section.id != stealthModulesSectionId && section.uservisible !== false">
<ion-item class="ion-text-wrap divider section" (click)="selectSection($event, section)"
[button]="section.visible !== 0 && section.uservisible !== false" [class.item-current]="selectedId === section.id"
[class.item-dimmed]="section.visible === 0" detail="false" sticky="true">
<ion-icon [name]="section.expanded ? 'fas-chevron-down' : 'fas-chevron-right'" flip-rtl slot="start"
class="expandable-status-icon" (click)="toggleExpand($event, section)"
[attr.aria-label]="(section.expanded ? 'core.collapse' : 'core.expand') | translate"
[attr.aria-expanded]="section.expanded" [attr.aria-controls]="'core-course-index-section-' + section.id">
</ion-icon>
<ion-label>
<p class="item-heading">
<core-format-text [text]="module.name" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="module.courseid">
<core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id">
</core-format-text>
</p>
</ion-label>
<ion-badge *ngIf="section.highlighted && highlighted">{{highlighted}}</ion-badge>
<ion-icon name="fas-lock" *ngIf="section.availabilityinfo" slot="end" class="restricted"
[attr.aria-label]="'core.restricted' | translate"></ion-icon>
</ion-item>
<div [hidden]="!section.expanded" [id]="'core-course-index-section-' + section.id">
<ng-container *ngIf="section.expanded">
<ng-container *ngFor="let module of section.modules">
<ion-item [class.item-dimmed]="module.visible === 0"
*ngIf="module.visibleoncoursepage !== 0 && !module.noviewlink"
(click)="selectModule($event, section, module)" button>
<ion-icon class="completioninfo completion_none" name="" *ngIf="module.completionStatus === undefined"
slot="start" aria-hidden="true"></ion-icon>
<ion-icon class="completioninfo completion_incomplete" name="far-circle"
*ngIf="module.completionStatus === 0" slot="start" [attr.aria-label]="'core.course.todo' | translate">
</ion-icon>
<ion-icon class="completioninfo completion_complete" name="fas-circle"
*ngIf="module.completionStatus === 1 || module.completionStatus === 2" color="success" slot="start"
[attr.aria-label]="'core.course.done' | translate">
</ion-icon>
<ion-icon class="completioninfo completion_fail" name="fas-circle" *ngIf="module.completionStatus === 3"
color="danger" slot="start" [attr.aria-label]="'core.course.failed' | translate">
</ion-icon>
<ion-label>
<p class="item-heading">
<core-format-text [text]="module.name" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="module.courseid">
</core-format-text>
</p>
</ion-label>
<ion-icon name="fas-lock" *ngIf="module.uservisible === false" slot="end" class="restricted"
[attr.aria-label]="'core.restricted' | translate"></ion-icon>
</ion-item>
</ng-container>
</ng-container>
</div>
</ng-container>
</ng-container>
</ion-list>

View File

@ -2,6 +2,7 @@
core-progress-bar {
--bar-margin: 8px 0 4px 0;
--line-height: 20px;
--background: var(--contrast-background);
}
@if ($core-hide-progress-on-section-selector) {
@ -10,6 +11,25 @@ core-progress-bar {
}
}
ion-badge {
text-align: start;
ion-icon.completioninfo {
font-size: 10px;
width: 18px;
}
ion-item.section::part(native) {
--padding-start: 0;
}
ion-icon.expandable-status-icon {
margin: 0;
@include padding(12px, 32px, 12px, 16px);
}
ion-item.item-current ion-icon.expandable-status-icon {
@include padding(null, null, null, 11px);
}
ion-icon.restricted {
font-size: 14px;
}

View File

@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, OnInit } from '@angular/core';
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { CoreCourseModuleData, CoreCourseSection, CoreCourseSectionWithStatus } from '@features/course/services/course-helper';
import { CoreCourseModuleData, CoreCourseSectionWithStatus } from '@features/course/services/course-helper';
import {
CoreCourseModuleCompletionStatus,
CoreCourseModuleCompletionTracking,
@ -23,6 +23,9 @@ import {
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { CoreUtils } from '@services/utils/utils';
import { ModalController } from '@singletons';
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
import { IonContent } from '@ionic/angular';
import { CoreDomUtils } from '@services/utils/dom';
/**
* Component to display course index modal.
@ -34,19 +37,30 @@ import { ModalController } from '@singletons';
})
export class CoreCourseCourseIndexComponent implements OnInit {
@Input() sections?: SectionWithProgress[];
@Input() selected?: CoreCourseSection;
@ViewChild(IonContent) content?: IonContent;
@Input() sections?: CourseIndexSection[];
@Input() selectedId?: number;
@Input() course?: CoreCourseAnyCourseData;
stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
allSectionId = CoreCourseProvider.ALL_SECTIONS_ID;
highlighted?: string;
constructor(
protected elementRef: ElementRef,
) {
}
/**
* @inheritdoc
*/
ngOnInit(): void {
async ngOnInit(): Promise<void> {
if (!this.course || !this.sections || !this.course.enablecompletion || !('courseformatoptions' in this.course) ||
!this.course.courseformatoptions) {
this.closeModal();
return;
}
@ -55,32 +69,48 @@ export class CoreCourseCourseIndexComponent implements OnInit {
if (!formatOptions || formatOptions.completionusertracked === false) {
return;
}
const currentSection = await CoreCourseFormatDelegate.getCurrentSection(this.course, this.sections);
currentSection.highlighted = true;
if (this.selectedId === undefined) {
currentSection.expanded = true;
this.selectedId = currentSection.id;
} else {
const selectedSection = this.sections.find((section) => section.id == this.selectedId);
if (selectedSection) {
selectedSection.expanded = true;
}
}
this.sections.forEach((section) => {
let complete = 0;
let total = 0;
section.modules.forEach((module) => {
console.error(module);
if (!module.uservisible || module.completiondata === undefined ||
module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE) {
module.completionStatus = undefined;
return;
}
module.completionStatus = module.completiondata.state;
total++;
if (module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) {
complete++;
}
module.completionStatus = module.completiondata === undefined ||
module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE
? undefined
: module.completiondata.state;
});
if (total > 0) {
section.progress = complete / total * 100;
}
});
this.highlighted = CoreCourseFormatDelegate.getSectionHightlightedName(this.course);
setTimeout(() => {
CoreDomUtils.scrollToElementBySelector(
this.elementRef.nativeElement,
this.content,
'.item.item-current',
);
}, 200);
}
/**
* Toggle expand status.
*
* @param event Event object.
* @param section Section to expand / collapse.
*/
toggleExpand(event: Event, section: CourseIndexSection): void {
section.expanded = !section.expanded;
event.stopPropagation();
event.preventDefault();
}
/**
@ -93,19 +123,40 @@ export class CoreCourseCourseIndexComponent implements OnInit {
/**
* Select a section.
*
* @param event Event.
* @param section Selected section object.
*/
selectSection(section: SectionWithProgress): void {
selectSection(event: Event, section: CoreCourseSectionWithStatus): void {
if (section.uservisible !== false) {
ModalController.dismiss(section);
ModalController.dismiss({ event, section });
}
}
/**
* Select a section and open a module
*
* @param event Event.
* @param section Selected section object.
* @param module Selected module object.
*/
selectModule(event: Event,section: CoreCourseSectionWithStatus, module: CoreCourseModuleData): void {
if (module.uservisible !== false) {
ModalController.dismiss({ event, section, module });
}
}
}
type SectionWithProgress = Omit<CoreCourseSectionWithStatus, 'modules'> & {
progress?: number;
type CourseIndexSection = Omit<CoreCourseSectionWithStatus, 'modules'> & {
highlighted?: boolean;
expanded?: boolean;
modules: (CoreCourseModuleData & {
completionStatus?: CoreCourseModuleCompletionStatus;
})[];
};
export type CoreCourseIndexSectionWithModule = {
event: Event;
section: CourseIndexSection;
module?: CoreCourseModuleData;
};

View File

@ -66,7 +66,8 @@
<!-- Template to render a section. -->
<ng-template #sectionTemplate let-section="section">
<section *ngIf="!section.hiddenbynumsections && section.id != allSectionsId && section.id != stealthModulesSectionId">
<ion-item-divider class="ion-text-wrap" color="light" [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
<ion-item-divider class="course-section ion-text-wrap" color="light"
[class.item-dimmed]="section.visible === 0 || section.uservisible === false">
<ion-icon name="fas-folder" aria-label="hidden" slot="start"></ion-icon>
<ion-label>
<h2 *ngIf="section.name">

View File

@ -9,3 +9,9 @@
text-transform: none;
}
}
.course-section {
ion-badge {
text-align: start;
}
}

View File

@ -44,9 +44,11 @@ import { CoreCourseFormatDelegate } from '@features/course/services/format-deleg
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { IonContent, IonRefresher } from '@ionic/angular';
import { CoreUtils } from '@services/utils/utils';
import { CoreCourseCourseIndexComponent } from '../course-index/course-index';
import { CoreCourseCourseIndexComponent, CoreCourseIndexSectionWithModule } from '../course-index/course-index';
import { CoreBlockHelper } from '@features/block/services/block-helper';
import { CoreNavigator } from '@services/navigator';
import { database } from 'faker';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
/**
* Component to display course contents using a certain format. If the format isn't found, use default one.
@ -182,7 +184,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
// Course has changed, try to get the components.
this.getComponents();
this.displayCourseIndex = CoreCourseFormatDelegate.displaySectionSelector(this.course);
this.displayCourseIndex = CoreCourseFormatDelegate.displayCourseIndex(this.course);
this.displayBlocks = CoreCourseFormatDelegate.displayBlocks(this.course);
this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.course.id);
@ -319,17 +321,28 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* Display the course index modal.
*/
async openCourseIndex(): Promise<void> {
const data = await CoreDomUtils.openModal<CoreCourseSection>({
const data = await CoreDomUtils.openModal<CoreCourseIndexSectionWithModule>({
component: CoreCourseCourseIndexComponent,
componentProps: {
course: this.course,
sections: this.sections,
selected: this.selectedSection,
selectedId: this.selectedSection?.id,
},
});
if (data) {
this.sectionChanged(data);
this.sectionChanged(data.section);
if (data.module) {
if (!data.module.handlerData) {
data.module.handlerData =
await CoreCourseModuleDelegate.getModuleDataFor(data.module.modname, data.module, this.course.id);
}
if (data.module.uservisible !== false && data.module.handlerData?.action) {
data.module.handlerData.action(data.event, data.module, data.module.course);
}
this.moduleId = data.module.id;
}
}
}

View File

@ -71,7 +71,7 @@ export class CoreCourseFormatSingleActivityHandlerService implements CoreCourseF
/**
* @inheritdoc
*/
displaySectionSelector(): boolean {
displayCourseIndex(): boolean {
return false;
}

View File

@ -16,7 +16,7 @@ import { Injectable } from '@angular/core';
import { CoreTimeUtils } from '@services/utils/time';
import { CoreCourseFormatHandler } from '@features/course/services/format-delegate';
import { makeSingleton } from '@singletons';
import { makeSingleton, Translate } from '@singletons';
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { CoreCourseWSSection } from '@features/course/services/course';
import { CoreConstants } from '@/core/constants';
@ -32,20 +32,14 @@ export class CoreCourseFormatWeeksHandlerService implements CoreCourseFormatHand
format = 'weeks';
/**
* Whether or not the handler is enabled on a site level.
*
* @return True or promise resolved with true if enabled.
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
/**
* Given a list of sections, get the "current" section that should be displayed first.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Current section (or promise resolved with current section).
* @inheritdoc
*/
async getCurrentSection(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection> {
const now = CoreTimeUtils.timestamp();
@ -71,6 +65,13 @@ export class CoreCourseFormatWeeksHandlerService implements CoreCourseFormatHand
return sections[0];
}
/**
* @inheritdoc
*/
getSectionHightlightedName(): string {
return Translate.instant('core.course.thisweek');
}
/**
* Return the start and end date of a section.
*
@ -83,7 +84,7 @@ export class CoreCourseFormatWeeksHandlerService implements CoreCourseFormatHand
startDate = startDate + 7200;
const dates = {
start: startDate + (CoreConstants.SECONDS_WEEK * (section.section! - 1)),
start: startDate + (CoreConstants.SECONDS_WEEK * ((section.section || 0) - 1)),
end: 0,
};
dates.end = dates.start + CoreConstants.SECONDS_WEEK;

View File

@ -27,21 +27,24 @@
"confirmpartialdownloadsize": "You are about to download <strong>at least</strong> {{size}}.{{availableSpace}} Are you sure you want to continue?",
"confirmlimiteddownload": "You are not currently connected to Wi-Fi. ",
"courseindex": "Course index",
"gotonextactivity": "Continue to next activity",
"gotonextactivitynotfound": "Next activity not found. It's possible that it has been hidden or deleted.",
"gotopreviousactivity": "Continue to previous activity",
"gotopreviousactivitynotfound": "Previous activity not found. It's possible that it has been hidden or deleted.",
"couldnotloadsectioncontent": "Could not load the section content. Please try again later.",
"couldnotloadsections": "Could not load the sections. Please try again later.",
"coursesummary": "Course summary",
"done": "Done",
"downloadcourse": "Download course",
"downloadcoursesprogressdescription": "Downloading courses: downloaded {{count}} out of {{total}}.",
"downloadsectionprogressdescription": "Downloading section: downloaded {{count}} out of {{total}}.",
"errordownloadingcourse": "Error downloading course.",
"errordownloadingsection": "Error downloading section.",
"errorgetmodule": "Error getting activity data.",
"failed": "Failed",
"gotonextactivity": "Continue to next activity",
"gotonextactivitynotfound": "Next activity not found. It's possible that it has been hidden or deleted.",
"gotopreviousactivity": "Continue to previous activity",
"gotopreviousactivitynotfound": "Previous activity not found. It's possible that it has been hidden or deleted.",
"hiddenfromstudents": "Hidden from students",
"hiddenoncoursepage": "Available but not shown on course page",
"highlighted": "Highlighted",
"insufficientavailablespace": "You are trying to download {{size}}. This will leave your device with insufficient space to operate normally. Please clear some storage space first.",
"insufficientavailablequota": "Your device could not allocate space to save this download. It may be reserving space for app and system updates. Please clear some storage space first.",
"manualcompletionnotsynced": "Manual completion not synchronised.",
@ -50,6 +53,8 @@
"overriddennotice": "Your final grade from this activity was manually adjusted.",
"refreshcourse": "Refresh course",
"section": "Section",
"thisweek": "This week",
"todo": "To do",
"useactivityonbrowser": "You can still use it using your device's web browser.",
"warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.",
"warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}"

View File

@ -66,13 +66,22 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler {
*/
displayEnableDownload?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the default course index should be displayed. Defaults to true.
*
* @deprecated on 4.0. Please use displayCourseIndex instead.
* @param course The course to check.
* @return Whether the default course index should be displayed.
*/
displaySectionSelector?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the default section selector should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the default section selector should be displayed.
*/
displaySectionSelector?(course: CoreCourseAnyCourseData): boolean;
displayCourseIndex?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
@ -93,6 +102,13 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler {
*/
getCurrentSection?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection>;
/**
* Returns the name for the highlighted section.
*
* @return The name for the highlighted section based on the given course format.
*/
getSectionHightlightedName?(): string;
/**
* Open the page to display a course. If not defined, the page CoreCourseSectionPage will be opened.
* Implement it only if you want to create your own page to display the course. In general it's better to use the method
@ -209,12 +225,19 @@ export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseForm
}
/**
* Whether the default section selector should be displayed. Defaults to true.
* Whether the default course index should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the section selector should be displayed.
* @return Whether the course index should be displayed.
*/
displaySectionSelector(course: CoreCourseAnyCourseData): boolean {
displayCourseIndex(course: CoreCourseAnyCourseData): boolean {
const display = this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayCourseIndex', [course]);
if (display !== undefined) {
return display;
}
// Use displaySectionSelector while is not completely deprecated.
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displaySectionSelector', [course]);
}
@ -278,9 +301,9 @@ export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseForm
* @param sections List of sections.
* @return Promise resolved with current section.
*/
async getCurrentSection(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection> {
async getCurrentSection<T = CoreCourseSection>(course: CoreCourseAnyCourseData, sections: T[]): Promise<T> {
try {
const section = await this.executeFunctionOnEnabled<CoreCourseSection>(
const section = await this.executeFunctionOnEnabled<T>(
course.format || '',
'getCurrentSection',
[course, sections],
@ -293,6 +316,19 @@ export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseForm
}
}
/**
* Returns the name for the highlighted section.
*
* @param course The course to get the text.
* @return The name for the highlighted section based on the given course format.
*/
getSectionHightlightedName(course: CoreCourseAnyCourseData): string | undefined {
return this.executeFunctionOnEnabled<string>(
course.format || '',
'getSectionHightlightedName',
);
}
/**
* Get the component to use to display a single section. This component will only be used if the user is viewing
* a single section. If all the sections are displayed at once then it won't be used.

View File

@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses';
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
import { CoreUtils } from '@services/utils/utils';
import { Translate } from '@singletons';
import { CoreCourseSection } from '../course-helper';
import { CoreCourseFormatHandler } from '../format-delegate';
@ -65,7 +66,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
/**
* @inheritdoc
*/
displaySectionSelector(): boolean {
displayCourseIndex(): boolean {
return true;
}
@ -106,6 +107,13 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
return sections[0];
}
/**
* @inheritdoc
*/
getSectionHightlightedName(): string {
return Translate.instant('core.course.highlighted');
}
/**
* @inheritdoc
*/

View File

@ -38,8 +38,9 @@ export class CoreSitePluginsCourseFormatHandler extends CoreSitePluginsBaseHandl
/**
* @inheritdoc
*/
displaySectionSelector(): boolean {
return this.handlerSchema.displaysectionselector ?? true;
displayCourseIndex(): boolean {
// Use displaysectionselector while is not completely deprecated.
return this.handlerSchema.displaycourseindex ?? this.handlerSchema.displaysectionselector ?? true;
}
/**

View File

@ -884,7 +884,11 @@ export type CoreSitePluginsCourseModuleHandlerData = CoreSitePluginsHandlerCommo
export type CoreSitePluginsCourseFormatHandlerData = CoreSitePluginsHandlerCommonData & {
canviewallsections?: boolean;
displayenabledownload?: boolean;
/**
* @deprecated on 4.0, use displaycourseindex instead.
*/
displaysectionselector?: boolean;
displaycourseindex?: boolean;
};
/**

View File

@ -1118,7 +1118,7 @@ export class CoreDomUtilsProvider {
content.scrollToPoint(position[0], position[1], duration || 0);
return true;
} catch (error) {
} catch {
return false;
}
}

View File

@ -16,6 +16,7 @@ information provided here is intended especially for developers.
The user handler function isEnabledForCourse is now called isEnabledForContext and receives a context + contextId instead of a courseId.
Some user handler's functions have also changed to accept context + contextId instead of a courseId: isEnabledForUser, getDisplayData, action.
- CoreCourseHelperProvider.openCourse parameters changed, now it admits CoreNavigationOptions + siteId on the same object that includes Params passed to page.
- displaySectionSelector has been deprecated on CoreCourseFormatHandler, use displayCourseIndex instead.
=== 3.9.5 ===