MOBILE-3915 course: Implement new course index

main
Pau Ferrer Ocaña 2021-11-17 11:46:37 +01:00
parent 1dd5eba1de
commit 86365d260d
10 changed files with 95 additions and 149 deletions

View File

@ -20,7 +20,7 @@ import { CoreCourseFormatComponent } from './format/format';
import { CoreCourseModuleComponent } from './module/module'; import { CoreCourseModuleComponent } from './module/module';
import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion'; import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion';
import { CoreCourseModuleDescriptionComponent } from './module-description/module-description'; import { CoreCourseModuleDescriptionComponent } from './module-description/module-description';
import { CoreCourseSectionSelectorComponent } from './section-selector/section-selector'; import { CoreCourseCourseIndexComponent } from './course-index/course-index';
import { CoreCourseTagAreaComponent } from './tag-area/tag-area'; import { CoreCourseTagAreaComponent } from './tag-area/tag-area';
import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsupported-module'; import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsupported-module';
import { CoreCourseModuleCompletionLegacyComponent } from './module-completion-legacy/module-completion-legacy'; import { CoreCourseModuleCompletionLegacyComponent } from './module-completion-legacy/module-completion-legacy';
@ -37,7 +37,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module-
CoreCourseModuleDescriptionComponent, CoreCourseModuleDescriptionComponent,
CoreCourseModuleInfoComponent, CoreCourseModuleInfoComponent,
CoreCourseModuleManualCompletionComponent, CoreCourseModuleManualCompletionComponent,
CoreCourseSectionSelectorComponent, CoreCourseCourseIndexComponent,
CoreCourseTagAreaComponent, CoreCourseTagAreaComponent,
CoreCourseUnsupportedModuleComponent, CoreCourseUnsupportedModuleComponent,
CoreCourseModuleNavigationComponent, CoreCourseModuleNavigationComponent,
@ -54,7 +54,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module-
CoreCourseModuleDescriptionComponent, CoreCourseModuleDescriptionComponent,
CoreCourseModuleInfoComponent, CoreCourseModuleInfoComponent,
CoreCourseModuleManualCompletionComponent, CoreCourseModuleManualCompletionComponent,
CoreCourseSectionSelectorComponent, CoreCourseCourseIndexComponent,
CoreCourseTagAreaComponent, CoreCourseTagAreaComponent,
CoreCourseUnsupportedModuleComponent, CoreCourseUnsupportedModuleComponent,
CoreCourseModuleNavigationComponent, CoreCourseModuleNavigationComponent,

View File

@ -1,7 +1,7 @@
<ion-header> <ion-header>
<ion-toolbar> <ion-toolbar>
<ion-title> <ion-title>
<h2 id="core-course-section-selector-label">{{ 'core.course.sections' | translate }}</h2> <h2 id="core-course-section-selector-label">{{ 'core.course.courseindex' | translate }}</h2>
</ion-title> </ion-title>
<ion-buttons slot="end"> <ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate"> <ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
@ -13,10 +13,10 @@
<ion-content> <ion-content>
<ion-list id="core-course-section-selector" role="listbox" aria-labelledby="core-course-section-selector-label"> <ion-list id="core-course-section-selector" role="listbox" aria-labelledby="core-course-section-selector-label">
<ng-container *ngFor="let section of sections"> <ng-container *ngFor="let section of sections">
<ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" class="ion-text-wrap" <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'" (click)="selectSection(section)" [attr.aria-current]="selected?.id == section.id ? 'page' : 'false'"
[class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail="false" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail="false"
[attr.aria-hidden]="section.uservisible === false" button> [attr.aria-hidden]="section.uservisible === false" button sticky="true">
<ion-icon name="fas-folder" slot="start" aria-hidden="true"></ion-icon> <ion-icon name="fas-folder" slot="start" aria-hidden="true"></ion-icon>
<ion-label> <ion-label>
@ -39,7 +39,24 @@
</core-format-text> </core-format-text>
</ion-badge> </ion-badge>
</ion-label> </ion-label>
</ion-item> </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-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-item>
</ng-container>
</ng-container> </ng-container>
</ion-list> </ion-list>
</ion-content> </ion-content>

View File

@ -14,7 +14,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { CoreCourseSection } from '@features/course/services/course-helper'; import { CoreCourseModuleData, CoreCourseSection, CoreCourseSectionWithStatus } from '@features/course/services/course-helper';
import { import {
CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionStatus,
CoreCourseModuleCompletionTracking, CoreCourseModuleCompletionTracking,
@ -25,14 +25,14 @@ import { CoreUtils } from '@services/utils/utils';
import { ModalController } from '@singletons'; import { ModalController } from '@singletons';
/** /**
* Component to display course section selector in a modal. * Component to display course index modal.
*/ */
@Component({ @Component({
selector: 'core-course-section-selector', selector: 'core-course-course-index',
templateUrl: 'section-selector.html', templateUrl: 'course-index.html',
styleUrls: ['section-selector.scss'], styleUrls: ['course-index.scss'],
}) })
export class CoreCourseSectionSelectorComponent implements OnInit { export class CoreCourseCourseIndexComponent implements OnInit {
@Input() sections?: SectionWithProgress[]; @Input() sections?: SectionWithProgress[];
@Input() selected?: CoreCourseSection; @Input() selected?: CoreCourseSection;
@ -41,7 +41,7 @@ export class CoreCourseSectionSelectorComponent implements OnInit {
stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
/** /**
* Component being initialized. * @inheritdoc
*/ */
ngOnInit(): void { ngOnInit(): void {
@ -52,7 +52,7 @@ export class CoreCourseSectionSelectorComponent implements OnInit {
const formatOptions = CoreUtils.objectToKeyValueMap(this.course.courseformatoptions, 'name', 'value'); const formatOptions = CoreUtils.objectToKeyValueMap(this.course.courseformatoptions, 'name', 'value');
if (!formatOptions || formatOptions.coursedisplay != 1 || formatOptions.completionusertracked === false) { if (!formatOptions || formatOptions.completionusertracked === false) {
return; return;
} }
@ -60,11 +60,16 @@ export class CoreCourseSectionSelectorComponent implements OnInit {
let complete = 0; let complete = 0;
let total = 0; let total = 0;
section.modules.forEach((module) => { section.modules.forEach((module) => {
console.error(module);
if (!module.uservisible || module.completiondata === undefined || if (!module.uservisible || module.completiondata === undefined ||
module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE) { module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE) {
module.completionStatus = undefined;
return; return;
} }
module.completionStatus = module.completiondata.state;
total++; total++;
if (module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE || if (module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) { module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) {
@ -98,6 +103,9 @@ export class CoreCourseSectionSelectorComponent implements OnInit {
} }
type SectionWithProgress = CoreCourseSection & { type SectionWithProgress = Omit<CoreCourseSectionWithStatus, 'modules'> & {
progress?: number; progress?: number;
modules: (CoreCourseModuleData & {
completionStatus?: CoreCourseModuleCompletionStatus;
})[];
}; };

View File

@ -1,8 +1,8 @@
<!-- Buttons to add to the header. *ngIf is needed, otherwise the component is executed too soon and doesn't find the header. --> <!-- Buttons to add to the header. *ngIf is needed, otherwise the component is executed too soon and doesn't find the header. -->
<core-navbar-buttons slot="end" *ngIf="loaded"> <core-navbar-buttons slot="end" *ngIf="loaded">
<core-context-menu> <core-context-menu>
<core-context-menu-item [hidden]="!displaySectionSelector || !sections || !sections.length" [priority]="500" <core-context-menu-item [hidden]="!displayCourseIndex || !sections || !sections.length" [priority]="500"
[content]="'core.course.sections' | translate" (action)="showSectionSelector()" iconAction="menu"> [content]="'core.course.courseindex' | translate" (action)="openCourseIndex()" iconAction="menu">
</core-context-menu-item> </core-context-menu-item>
</core-context-menu> </core-context-menu>
</core-navbar-buttons> </core-navbar-buttons>
@ -32,46 +32,6 @@
<ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-info-circle" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
</ion-item> </ion-item>
<ion-item *ngIf="selectedSection && selectedSection.id != allSectionsId" class="ion-text-wrap">
<ion-icon name="fas-folder" aria-label="hidden" slot="start"></ion-icon>
<ion-label>
<p class="item-heading">
<core-format-text *ngIf="selectedSection" [text]="selectedSection.name" contextLevel="course"
[contextInstanceId]="course.id" [clean]="true" [singleLine]="true">
</core-format-text>
</p>
<ion-badge color="info" class="ion-text-wrap"
*ngIf="selectedSection.visible === 0 && selectedSection.uservisible !== false">
{{ 'core.course.hiddenfromstudents' | translate }}
</ion-badge>
<ion-badge color="info" class="ion-text-wrap"
*ngIf="selectedSection.visible === 0 && selectedSection.uservisible === false">
{{ 'core.notavailable' | translate }}
</ion-badge>
<ion-badge color="info" class="ion-text-wrap" *ngIf="selectedSection.availabilityinfo">
<core-format-text [text]="selectedSection.availabilityinfo" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text>
</ion-badge>
</ion-label>
</ion-item>
</core-dynamic-component>
<!-- Section selector. -->
<core-dynamic-component [component]="sectionSelectorComponent" [data]="data">
<div *ngIf="displaySectionSelector && sections && hasSeveralSections"
class="ion-text-wrap ion-justify-content-between ion-align-items-center core-button-selector-row">
<core-combobox [modalOptions]="sectionSelectorModalOptions" interface="modal" listboxId="core-course-section-button"
icon="fas-folder" [label]="'core.course.section' | translate"
[selection]="selectedSection ? selectedSection.name : 'core.course.sections' | translate"
(onChange)="sectionChanged($event)">
<span slot="text">
<core-format-text *ngIf="selectedSection" [text]="selectedSection.name" contextLevel="course"
[contextInstanceId]="course.id" [clean]="true" [singleLine]="true">
</core-format-text>
<ng-container *ngIf="!selectedSection">{{ 'core.course.sections' | translate }}</ng-container>
</span>
</core-combobox>
</div>
</core-dynamic-component> </core-dynamic-component>
<!-- Single section. --> <!-- Single section. -->
@ -98,7 +58,7 @@
</div> </div>
<ion-buttons class="ion-padding core-course-section-nav-buttons safe-area-padding-horizontal" <ion-buttons class="ion-padding core-course-section-nav-buttons safe-area-padding-horizontal"
*ngIf="displaySectionSelector && sections?.length"> *ngIf="displayCourseIndex && sections?.length">
<ion-button *ngIf="previousSection" (click)="sectionChanged(previousSection)" fill="outline" color="primary" <ion-button *ngIf="previousSection" (click)="sectionChanged(previousSection)" fill="outline" color="primary"
[attr.aria-label]="('core.previous' | translate) + ': ' + previousSection.name"> [attr.aria-label]="('core.previous' | translate) + ': ' + previousSection.name">
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
@ -115,17 +75,25 @@
<core-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id"> <core-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id">
</core-block-side-blocks-button> </core-block-side-blocks-button>
</core-loading> </core-loading>
</core-dynamic-component> </core-dynamic-component>
<!-- Course Index button. -->
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="displayCourseIndex">
<ion-fab-button (click)="openCourseIndex()" [attr.aria-label]="'core.course.courseindex' | translate">
<ion-icon name="fas-list-ul" aria-hidden="true"></ion-icon>
<span class="sr-only">{{'core.course.courseindex' | translate }}</span>
</ion-fab-button>
</ion-fab>
<!-- Template to render a section. --> <!-- Template to render a section. -->
<ng-template #sectionTemplate let-section="section"> <ng-template #sectionTemplate let-section="section">
<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. --> <ion-item-divider class="ion-text-wrap" color="light" [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
<ion-item-divider *ngIf="selectedSection?.id == allSectionsId && section.name" class="ion-text-wrap" color="light" <ion-icon name="fas-folder" aria-label="hidden" slot="start"></ion-icon>
[class.item-dimmed]="section.visible === 0 || section.uservisible === false">
<ion-label> <ion-label>
<h2> <h2 *ngIf="section.name">
<core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course.id"> <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text> </core-format-text>
</h2> </h2>

View File

@ -26,7 +26,6 @@ import {
Type, Type,
ElementRef, ElementRef,
} from '@angular/core'; } from '@angular/core';
import { ModalOptions } from '@ionic/core';
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';
@ -45,7 +44,7 @@ import { CoreCourseFormatDelegate } from '@features/course/services/format-deleg
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 { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector'; import { CoreCourseCourseIndexComponent } from '../course-index/course-index';
import { CoreBlockHelper } from '@features/block/services/block-helper'; import { CoreBlockHelper } from '@features/block/services/block-helper';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
@ -80,7 +79,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
// All the possible component classes. // All the possible component classes.
courseFormatComponent?: Type<unknown>; courseFormatComponent?: Type<unknown>;
courseSummaryComponent?: Type<unknown>; courseSummaryComponent?: Type<unknown>;
sectionSelectorComponent?: Type<unknown>;
singleSectionComponent?: Type<unknown>; singleSectionComponent?: Type<unknown>;
allSectionsComponent?: Type<unknown>; allSectionsComponent?: Type<unknown>;
@ -88,7 +86,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
showSectionId = 0; showSectionId = 0;
data: Record<string, unknown> = {}; // Data to pass to the components. data: Record<string, unknown> = {}; // Data to pass to the components.
displaySectionSelector = false; displayCourseIndex = false;
displayBlocks = false; displayBlocks = false;
hasBlocks = false; hasBlocks = false;
selectedSection?: CoreCourseSection; selectedSection?: CoreCourseSection;
@ -97,17 +95,11 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID; allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID;
stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
loaded = false; loaded = false;
hasSeveralSections?: boolean;
imageThumb?: string; imageThumb?: string;
progress?: number; progress?: number;
sectionSelectorModalOptions: ModalOptions = {
component: CoreCourseSectionSelectorComponent,
componentProps: {},
};
protected selectTabObserver?: CoreEventObserver; protected selectTabObserver?: CoreEventObserver;
protected lastCourseFormat?: string; protected lastCourseFormat?: string;
protected sectionSelectorExpanded = false;
constructor( constructor(
protected content: IonContent, protected content: IonContent,
@ -154,14 +146,12 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
*/ */
async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise<void> { async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise<void> {
this.setInputData(); this.setInputData();
this.sectionSelectorModalOptions.componentProps!.course = this.course;
this.sectionSelectorModalOptions.componentProps!.sections = this.sections;
if (changes.course && this.course) { if (changes.course && this.course) {
// Course has changed, try to get the components. // Course has changed, try to get the components.
this.getComponents(); this.getComponents();
this.displaySectionSelector = CoreCourseFormatDelegate.displaySectionSelector(this.course); this.displayCourseIndex = CoreCourseFormatDelegate.displaySectionSelector(this.course);
this.displayBlocks = CoreCourseFormatDelegate.displayBlocks(this.course); this.displayBlocks = CoreCourseFormatDelegate.displayBlocks(this.course);
this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.course.id); this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.course.id);
@ -174,7 +164,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
} }
if (changes.sections && this.sections) { if (changes.sections && this.sections) {
this.sectionSelectorModalOptions.componentProps!.sections = this.sections;
this.treatSections(this.sections); this.treatSections(this.sections);
} }
} }
@ -205,7 +194,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
await Promise.all([ await Promise.all([
this.loadCourseFormatComponent(), this.loadCourseFormatComponent(),
this.loadCourseSummaryComponent(), this.loadCourseSummaryComponent(),
this.loadSectionSelectorComponent(),
this.loadSingleSectionComponent(), this.loadSingleSectionComponent(),
this.loadAllSectionsComponent(), this.loadAllSectionsComponent(),
]); ]);
@ -229,15 +217,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.courseSummaryComponent = await CoreCourseFormatDelegate.getCourseSummaryComponent(this.course); this.courseSummaryComponent = await CoreCourseFormatDelegate.getCourseSummaryComponent(this.course);
} }
/**
* Load section selector component.
*
* @return Promise resolved when done.
*/
protected async loadSectionSelectorComponent(): Promise<void> {
this.sectionSelectorComponent = await CoreCourseFormatDelegate.getSectionSelectorComponent(this.course);
}
/** /**
* Load single section component. * Load single section component.
* *
@ -264,7 +243,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
*/ */
protected async treatSections(sections: CoreCourseSection[]): Promise<void> { protected async treatSections(sections: CoreCourseSection[]): Promise<void> {
const hasAllSections = sections[0].id == CoreCourseProvider.ALL_SECTIONS_ID; const hasAllSections = sections[0].id == CoreCourseProvider.ALL_SECTIONS_ID;
this.hasSeveralSections = sections.length > 2 || (sections.length == 2 && !hasAllSections); const hasSeveralSections = sections.length > 2 || (sections.length == 2 && !hasAllSections);
if (this.selectedSection) { if (this.selectedSection) {
// We have a selected section, but the list has changed. Search the section in the list. // We have a selected section, but the list has changed. Search the section in the list.
@ -281,7 +260,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
} }
// There is no selected section yet, calculate which one to load. // There is no selected section yet, calculate which one to load.
if (!this.hasSeveralSections) { if (!hasSeveralSections) {
// Always load "All sections" to display the section title. If it isn't there just load the section. // Always load "All sections" to display the section title. If it isn't there just load the section.
this.loaded = true; this.loaded = true;
this.sectionChanged(sections[0]); this.sectionChanged(sections[0]);
@ -309,18 +288,18 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
} }
/** /**
* Display the section selector modal. * Display the course index modal.
*/ */
async showSectionSelector(): Promise<void> { async openCourseIndex(): Promise<void> {
if (this.sectionSelectorExpanded) { const data = await CoreDomUtils.openModal<CoreCourseSection>({
return; component: CoreCourseCourseIndexComponent,
} componentProps: {
course: this.course,
sections: this.sections,
selected: this.selectedSection,
},
});
this.sectionSelectorExpanded = true;
const data = await CoreDomUtils.openModal<CoreCourseSection>(this.sectionSelectorModalOptions);
this.sectionSelectorExpanded = false;
if (data) { if (data) {
this.sectionChanged(data); this.sectionChanged(data);
} }
@ -334,7 +313,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
sectionChanged(newSection: CoreCourseSection): void { sectionChanged(newSection: CoreCourseSection): void {
const previousValue = this.selectedSection; const previousValue = this.selectedSection;
this.selectedSection = newSection; this.selectedSection = newSection;
this.sectionSelectorModalOptions.componentProps!.selected = this.selectedSection;
this.data.section = this.selectedSection; this.data.section = this.selectedSection;
if (newSection.id != this.allSectionsId) { if (newSection.id != this.allSectionsId) {

View File

@ -52,30 +52,28 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC
let langKey: string | undefined; let langKey: string | undefined;
let image: string | undefined; let image: string | undefined;
if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL && if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL) {
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) {
image = 'completion-manual-n'; image = 'completion-manual-n';
langKey = 'core.completion-alt-manual-n'; langKey = 'core.completion-alt-manual-n';
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL && } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) {
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) { image = 'completion-manual-y';
image = 'completion-manual-y'; langKey = 'core.completion-alt-manual-y';
langKey = 'core.completion-alt-manual-y'; }
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC) {
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) {
image = 'completion-auto-n'; image = 'completion-auto-n';
langKey = 'core.completion-alt-auto-n'; langKey = 'core.completion-alt-auto-n';
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) {
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) { image = 'completion-auto-y';
image = 'completion-auto-y'; langKey = 'core.completion-alt-auto-y';
langKey = 'core.completion-alt-auto-y'; } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) {
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && image = 'completion-auto-pass';
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) { langKey = 'core.completion-alt-auto-pass';
image = 'completion-auto-pass'; } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL) {
langKey = 'core.completion-alt-auto-pass'; image = 'completion-auto-fail';
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && langKey = 'core.completion-alt-auto-fail';
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL) { }
image = 'completion-auto-fail';
langKey = 'core.completion-alt-auto-fail';
} }
if (image) { if (image) {

View File

@ -26,6 +26,7 @@
"confirmdownloadzerosize": "You are about to start downloading.{{availableSpace}} Are you sure you want to continue?", "confirmdownloadzerosize": "You are about to start downloading.{{availableSpace}} Are you sure you want to continue?",
"confirmpartialdownloadsize": "You are about to download <strong>at least</strong> {{size}}.{{availableSpace}} Are you sure you want to continue?", "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. ", "confirmlimiteddownload": "You are not currently connected to Wi-Fi. ",
"courseindex": "Course index",
"gotonextactivity": "Continue to next activity", "gotonextactivity": "Continue to next activity",
"gotonextactivitynotfound": "Next activity not found. It's possible that it has been hidden or deleted.", "gotonextactivitynotfound": "Next activity not found. It's possible that it has been hidden or deleted.",
"gotopreviousactivity": "Continue to previous activity", "gotopreviousactivity": "Continue to previous activity",
@ -49,7 +50,6 @@
"overriddennotice": "Your final grade from this activity was manually adjusted.", "overriddennotice": "Your final grade from this activity was manually adjusted.",
"refreshcourse": "Refresh course", "refreshcourse": "Refresh course",
"section": "Section", "section": "Section",
"sections": "Sections",
"useactivityonbrowser": "You can still use it using your device's web browser.", "useactivityonbrowser": "You can still use it using your device's web browser.",
"warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.", "warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.",
"warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}" "warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}"

View File

@ -62,7 +62,7 @@ declare module '@singletons/events' {
} }
/** /**
* Completion status valid values. * Course Module completion status enumeration.
*/ */
export enum CoreCourseModuleCompletionStatus { export enum CoreCourseModuleCompletionStatus {
COMPLETION_INCOMPLETE = 0, COMPLETION_INCOMPLETE = 0,

View File

@ -125,15 +125,6 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler {
*/ */
getCourseSummaryComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>; getCourseSummaryComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
/**
* Return the Component to use to display the section selector inside the default course format.
* It's recommended to return the class of the component, but you can also return an instance of the component.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
getSectionSelectorComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
/** /**
* Return the Component to use to display a single section. This component will only be used if the user is viewing a * Return 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. * single section. If all the sections are displayed at once then it won't be used.
@ -302,20 +293,6 @@ export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseForm
} }
} }
/**
* Get the component to use to display the section selector inside the default course format.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
async getSectionSelectorComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
try {
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getSectionSelectorComponent', [course]);
} catch (error) {
this.logger.error('Error getting section selector component', error);
}
}
/** /**
* Get the component to use to display a single section. This component will only be used if the user is viewing * 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. * a single section. If all the sections are displayed at once then it won't be used.