diff --git a/src/core/features/course/components/components.module.ts b/src/core/features/course/components/components.module.ts index 516a43111..739dc216e 100644 --- a/src/core/features/course/components/components.module.ts +++ b/src/core/features/course/components/components.module.ts @@ -20,7 +20,7 @@ import { CoreCourseFormatComponent } from './format/format'; import { CoreCourseModuleComponent } from './module/module'; import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion'; 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 { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsupported-module'; import { CoreCourseModuleCompletionLegacyComponent } from './module-completion-legacy/module-completion-legacy'; @@ -37,7 +37,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module- CoreCourseModuleDescriptionComponent, CoreCourseModuleInfoComponent, CoreCourseModuleManualCompletionComponent, - CoreCourseSectionSelectorComponent, + CoreCourseCourseIndexComponent, CoreCourseTagAreaComponent, CoreCourseUnsupportedModuleComponent, CoreCourseModuleNavigationComponent, @@ -54,7 +54,7 @@ import { CoreCourseModuleNavigationComponent } from './module-navigation/module- CoreCourseModuleDescriptionComponent, CoreCourseModuleInfoComponent, CoreCourseModuleManualCompletionComponent, - CoreCourseSectionSelectorComponent, + CoreCourseCourseIndexComponent, CoreCourseTagAreaComponent, CoreCourseUnsupportedModuleComponent, CoreCourseModuleNavigationComponent, diff --git a/src/core/features/course/components/section-selector/section-selector.html b/src/core/features/course/components/course-index/course-index.html similarity index 61% rename from src/core/features/course/components/section-selector/section-selector.html rename to src/core/features/course/components/course-index/course-index.html index 82b2bf8b0..242cf97c4 100644 --- a/src/core/features/course/components/section-selector/section-selector.html +++ b/src/core/features/course/components/course-index/course-index.html @@ -1,7 +1,7 @@ -

{{ 'core.course.sections' | translate }}

+

{{ 'core.course.courseindex' | translate }}

@@ -13,10 +13,10 @@ - + [attr.aria-hidden]="section.uservisible === false" button sticky="true"> @@ -39,7 +39,24 @@ - + + + + + + + + + + +

+ + +

+
+
+
diff --git a/src/core/features/course/components/section-selector/section-selector.scss b/src/core/features/course/components/course-index/course-index.scss similarity index 100% rename from src/core/features/course/components/section-selector/section-selector.scss rename to src/core/features/course/components/course-index/course-index.scss diff --git a/src/core/features/course/components/section-selector/section-selector.ts b/src/core/features/course/components/course-index/course-index.ts similarity index 77% rename from src/core/features/course/components/section-selector/section-selector.ts rename to src/core/features/course/components/course-index/course-index.ts index e71790478..3b51cad30 100644 --- a/src/core/features/course/components/section-selector/section-selector.ts +++ b/src/core/features/course/components/course-index/course-index.ts @@ -14,7 +14,7 @@ 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 { CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionTracking, @@ -25,14 +25,14 @@ import { CoreUtils } from '@services/utils/utils'; import { ModalController } from '@singletons'; /** - * Component to display course section selector in a modal. + * Component to display course index modal. */ @Component({ - selector: 'core-course-section-selector', - templateUrl: 'section-selector.html', - styleUrls: ['section-selector.scss'], + selector: 'core-course-course-index', + templateUrl: 'course-index.html', + styleUrls: ['course-index.scss'], }) -export class CoreCourseSectionSelectorComponent implements OnInit { +export class CoreCourseCourseIndexComponent implements OnInit { @Input() sections?: SectionWithProgress[]; @Input() selected?: CoreCourseSection; @@ -41,7 +41,7 @@ export class CoreCourseSectionSelectorComponent implements OnInit { stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; /** - * Component being initialized. + * @inheritdoc */ ngOnInit(): void { @@ -52,7 +52,7 @@ export class CoreCourseSectionSelectorComponent implements OnInit { const formatOptions = CoreUtils.objectToKeyValueMap(this.course.courseformatoptions, 'name', 'value'); - if (!formatOptions || formatOptions.coursedisplay != 1 || formatOptions.completionusertracked === false) { + if (!formatOptions || formatOptions.completionusertracked === false) { return; } @@ -60,11 +60,16 @@ export class CoreCourseSectionSelectorComponent implements OnInit { 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) { @@ -98,6 +103,9 @@ export class CoreCourseSectionSelectorComponent implements OnInit { } -type SectionWithProgress = CoreCourseSection & { +type SectionWithProgress = Omit & { progress?: number; + modules: (CoreCourseModuleData & { + completionStatus?: CoreCourseModuleCompletionStatus; + })[]; }; diff --git a/src/core/features/course/components/format/core-course-format.html b/src/core/features/course/components/format/core-course-format.html index 81b7ad122..9ecb91ece 100644 --- a/src/core/features/course/components/format/core-course-format.html +++ b/src/core/features/course/components/format/core-course-format.html @@ -1,8 +1,8 @@ - + @@ -32,46 +32,6 @@
- - - -

- - -

- - {{ 'core.course.hiddenfromstudents' | translate }} - - - {{ 'core.notavailable' | translate }} - - - - - -
-
- - - - -
- - - - - {{ 'core.course.sections' | translate }} - - -
@@ -98,7 +58,7 @@ + *ngIf="displayCourseIndex && sections?.length"> @@ -115,17 +75,25 @@ + + + + + + {{'core.course.courseindex' | translate }} + + +
- - + + -

+

diff --git a/src/core/features/course/components/format/format.ts b/src/core/features/course/components/format/format.ts index c4121074f..e7efb6bc4 100644 --- a/src/core/features/course/components/format/format.ts +++ b/src/core/features/course/components/format/format.ts @@ -26,7 +26,6 @@ import { Type, ElementRef, } from '@angular/core'; -import { ModalOptions } from '@ionic/core'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; 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 { IonContent, IonRefresher } from '@ionic/angular'; 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 { CoreNavigator } from '@services/navigator'; @@ -80,7 +79,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { // All the possible component classes. courseFormatComponent?: Type; courseSummaryComponent?: Type; - sectionSelectorComponent?: Type; singleSectionComponent?: Type; allSectionsComponent?: Type; @@ -88,7 +86,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { showSectionId = 0; data: Record = {}; // Data to pass to the components. - displaySectionSelector = false; + displayCourseIndex = false; displayBlocks = false; hasBlocks = false; selectedSection?: CoreCourseSection; @@ -97,17 +95,11 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID; stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; loaded = false; - hasSeveralSections?: boolean; imageThumb?: string; progress?: number; - sectionSelectorModalOptions: ModalOptions = { - component: CoreCourseSectionSelectorComponent, - componentProps: {}, - }; protected selectTabObserver?: CoreEventObserver; protected lastCourseFormat?: string; - protected sectionSelectorExpanded = false; constructor( protected content: IonContent, @@ -154,14 +146,12 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { */ async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise { this.setInputData(); - this.sectionSelectorModalOptions.componentProps!.course = this.course; - this.sectionSelectorModalOptions.componentProps!.sections = this.sections; if (changes.course && this.course) { // Course has changed, try to get the components. this.getComponents(); - this.displaySectionSelector = CoreCourseFormatDelegate.displaySectionSelector(this.course); + this.displayCourseIndex = CoreCourseFormatDelegate.displaySectionSelector(this.course); this.displayBlocks = CoreCourseFormatDelegate.displayBlocks(this.course); this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.course.id); @@ -174,7 +164,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { } if (changes.sections && this.sections) { - this.sectionSelectorModalOptions.componentProps!.sections = this.sections; this.treatSections(this.sections); } } @@ -205,7 +194,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { await Promise.all([ this.loadCourseFormatComponent(), this.loadCourseSummaryComponent(), - this.loadSectionSelectorComponent(), this.loadSingleSectionComponent(), this.loadAllSectionsComponent(), ]); @@ -229,15 +217,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { this.courseSummaryComponent = await CoreCourseFormatDelegate.getCourseSummaryComponent(this.course); } - /** - * Load section selector component. - * - * @return Promise resolved when done. - */ - protected async loadSectionSelectorComponent(): Promise { - this.sectionSelectorComponent = await CoreCourseFormatDelegate.getSectionSelectorComponent(this.course); - } - /** * Load single section component. * @@ -264,7 +243,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { */ protected async treatSections(sections: CoreCourseSection[]): Promise { 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) { // 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. - if (!this.hasSeveralSections) { + if (!hasSeveralSections) { // Always load "All sections" to display the section title. If it isn't there just load the section. this.loaded = true; 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 { - if (this.sectionSelectorExpanded) { - return; - } + async openCourseIndex(): Promise { + const data = await CoreDomUtils.openModal({ + component: CoreCourseCourseIndexComponent, + componentProps: { + course: this.course, + sections: this.sections, + selected: this.selectedSection, + }, + }); - this.sectionSelectorExpanded = true; - - const data = await CoreDomUtils.openModal(this.sectionSelectorModalOptions); - - this.sectionSelectorExpanded = false; if (data) { this.sectionChanged(data); } @@ -334,7 +313,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { sectionChanged(newSection: CoreCourseSection): void { const previousValue = this.selectedSection; this.selectedSection = newSection; - this.sectionSelectorModalOptions.componentProps!.selected = this.selectedSection; this.data.section = this.selectedSection; if (newSection.id != this.allSectionsId) { diff --git a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts index 1b9e0b453..6bf6d2414 100644 --- a/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts +++ b/src/core/features/course/components/module-completion-legacy/module-completion-legacy.ts @@ -52,30 +52,28 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC let langKey: string | undefined; let image: string | undefined; - if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL && - this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { - image = 'completion-manual-n'; - langKey = 'core.completion-alt-manual-n'; - } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL && - this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) { - image = 'completion-manual-y'; - langKey = 'core.completion-alt-manual-y'; - } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && - this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { - image = 'completion-auto-n'; - langKey = 'core.completion-alt-auto-n'; - } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && - this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) { - image = 'completion-auto-y'; - langKey = 'core.completion-alt-auto-y'; - } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && - this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) { - image = 'completion-auto-pass'; - langKey = 'core.completion-alt-auto-pass'; - } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC && - this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL) { - image = 'completion-auto-fail'; - langKey = 'core.completion-alt-auto-fail'; + if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL) { + if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { + image = 'completion-manual-n'; + langKey = 'core.completion-alt-manual-n'; + } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) { + image = 'completion-manual-y'; + langKey = 'core.completion-alt-manual-y'; + } + } else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC) { + if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) { + image = 'completion-auto-n'; + langKey = 'core.completion-alt-auto-n'; + } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) { + image = 'completion-auto-y'; + langKey = 'core.completion-alt-auto-y'; + } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) { + image = 'completion-auto-pass'; + langKey = 'core.completion-alt-auto-pass'; + } else if (this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL) { + image = 'completion-auto-fail'; + langKey = 'core.completion-alt-auto-fail'; + } } if (image) { diff --git a/src/core/features/course/lang.json b/src/core/features/course/lang.json index f6f144b2d..d83be79fa 100644 --- a/src/core/features/course/lang.json +++ b/src/core/features/course/lang.json @@ -26,6 +26,7 @@ "confirmdownloadzerosize": "You are about to start downloading.{{availableSpace}} Are you sure you want to continue?", "confirmpartialdownloadsize": "You are about to download at least {{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", @@ -49,7 +50,6 @@ "overriddennotice": "Your final grade from this activity was manually adjusted.", "refreshcourse": "Refresh course", "section": "Section", - "sections": "Sections", "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}}" diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts index 24984dfc3..170cbbef5 100644 --- a/src/core/features/course/services/course.ts +++ b/src/core/features/course/services/course.ts @@ -62,7 +62,7 @@ declare module '@singletons/events' { } /** - * Completion status valid values. + * Course Module completion status enumeration. */ export enum CoreCourseModuleCompletionStatus { COMPLETION_INCOMPLETE = 0, diff --git a/src/core/features/course/services/format-delegate.ts b/src/core/features/course/services/format-delegate.ts index 295691a0e..dc2efc50b 100644 --- a/src/core/features/course/services/format-delegate.ts +++ b/src/core/features/course/services/format-delegate.ts @@ -125,15 +125,6 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler { */ getCourseSummaryComponent?(course: CoreCourseAnyCourseData): Promise | 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 | undefined>; - /** * 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. @@ -302,20 +293,6 @@ export class CoreCourseFormatDelegateService extends CoreDelegate | undefined> { - try { - return await this.executeFunctionOnEnabled>(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 * a single section. If all the sections are displayed at once then it won't be used.