From 01e9e200147f62e1aa1c96767a764330a6950efb Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 18 Jan 2021 16:16:18 +0100 Subject: [PATCH] MOBILE-3659 course: Implement section selector --- .../course/components/components.module.ts | 3 + .../components/format/core-course-format.html | 4 +- .../course/components/format/format.ts | 35 ++++--- .../section-selector/section-selector.html | 39 ++++++++ .../section-selector/section-selector.scss | 17 ++++ .../section-selector/section-selector.ts | 98 +++++++++++++++++++ src/theme/app.scss | 3 + 7 files changed, 181 insertions(+), 18 deletions(-) create mode 100644 src/core/features/course/components/section-selector/section-selector.html create mode 100644 src/core/features/course/components/section-selector/section-selector.scss create mode 100644 src/core/features/course/components/section-selector/section-selector.ts diff --git a/src/core/features/course/components/components.module.ts b/src/core/features/course/components/components.module.ts index 24637f512..02d524304 100644 --- a/src/core/features/course/components/components.module.ts +++ b/src/core/features/course/components/components.module.ts @@ -23,6 +23,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 { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsupported-module'; @NgModule({ @@ -31,6 +32,7 @@ import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsup CoreCourseModuleComponent, CoreCourseModuleCompletionComponent, CoreCourseModuleDescriptionComponent, + CoreCourseSectionSelectorComponent, CoreCourseUnsupportedModuleComponent, ], imports: [ @@ -45,6 +47,7 @@ import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsup CoreCourseModuleComponent, CoreCourseModuleCompletionComponent, CoreCourseModuleDescriptionComponent, + CoreCourseSectionSelectorComponent, CoreCourseUnsupportedModuleComponent, ], }) 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 a4016d942..8f7fcbe87 100644 --- a/src/core/features/course/components/format/core-course-format.html +++ b/src/core/features/course/components/format/core-course-format.html @@ -2,7 +2,7 @@ + [content]="'core.course.sections' | translate" (action)="showSectionSelector()" iconAction="menu"> @@ -19,7 +19,7 @@
- diff --git a/src/core/features/course/components/format/format.ts b/src/core/features/course/components/format/format.ts index 3bbad7a01..90b06f01f 100644 --- a/src/core/features/course/components/format/format.ts +++ b/src/core/features/course/components/format/format.ts @@ -46,6 +46,8 @@ import { CoreUtils } from '@services/utils/utils'; import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; import { CoreCourseSectionFormatted } from '@features/course/services/course-helper'; import { CoreCourseModuleStatusChangedData } from '../module/module'; +import { ModalController } from '@singletons'; +import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector'; /** * Component to display course contents using a certain format. If the format isn't found, use default one. @@ -330,29 +332,30 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { /** * Display the section selector modal. - * - * @param event Event. */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - showSectionSelector(event?: MouseEvent): void { + async showSectionSelector(): Promise { if (this.sectionSelectorExpanded) { return; } - // @todo this.sectionSelectorExpanded = true; - // const modal = this.modalCtrl.create('CoreCourseSectionSelectorPage', - // {course: this.course, sections: this.sections, selected: this.selectedSection}); - // modal.onDidDismiss((newSection) => { - // if (newSection) { - // this.sectionChanged(newSection); - // } + this.sectionSelectorExpanded = true; - // this.sectionSelectorExpanded = false; - // }); + const modal = await ModalController.instance.create({ + component: CoreCourseSectionSelectorComponent, + componentProps: { + course: this.course, + sections: this.sections, + selected: this.selectedSection, + }, + }); + await modal.present(); - // modal.present({ - // ev: event - // }); + const result = await modal.onWillDismiss(); + + this.sectionSelectorExpanded = false; + if (result?.data) { + this.sectionChanged(result?.data); + } } /** diff --git a/src/core/features/course/components/section-selector/section-selector.html b/src/core/features/course/components/section-selector/section-selector.html new file mode 100644 index 000000000..92b0ba8fa --- /dev/null +++ b/src/core/features/course/components/section-selector/section-selector.html @@ -0,0 +1,39 @@ + + + {{ 'core.course.sections' | translate }} + + + + + + + + + + + + + + +

+

+ + + + {{ 'core.course.hiddenfromstudents' | translate }} + + + {{ 'core.notavailable' | translate }} + + + + + +
+
+
+
+
\ No newline at end of file diff --git a/src/core/features/course/components/section-selector/section-selector.scss b/src/core/features/course/components/section-selector/section-selector.scss new file mode 100644 index 000000000..429076451 --- /dev/null +++ b/src/core/features/course/components/section-selector/section-selector.scss @@ -0,0 +1,17 @@ +:host { + core-progress-bar { + .core-progress-text { + line-height: 24px; + position: absolute; + top: -8px; + right: 10px; + } + progress { + margin: 8px 0 4px 0; + } + } + + ion-badge { + text-align: start; + } +} \ No newline at end of file diff --git a/src/core/features/course/components/section-selector/section-selector.ts b/src/core/features/course/components/section-selector/section-selector.ts new file mode 100644 index 000000000..0edc88144 --- /dev/null +++ b/src/core/features/course/components/section-selector/section-selector.ts @@ -0,0 +1,98 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, Input, OnInit } from '@angular/core'; + +import { CoreCourseSectionFormatted } from '@features/course/services/course-helper'; +import { CoreCourseProvider } from '@features/course/services/course'; +import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; +import { CoreUtils } from '@services/utils/utils'; +import { ModalController } from '@singletons'; + +/** + * Component to display course section selector in a modal. + */ +@Component({ + selector: 'core-course-section-selector', + templateUrl: 'section-selector.html', +}) +export class CoreCourseSectionSelectorComponent implements OnInit { + + @Input() sections?: SectionWithProgress[]; + @Input() selected?: CoreCourseSectionFormatted; + @Input() course?: CoreCourseAnyCourseData; + + stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; + + /** + * Component being initialized. + */ + ngOnInit(): void { + + if (!this.course || !this.sections || !this.course.enablecompletion || !('courseformatoptions' in this.course) || + !this.course.courseformatoptions) { + return; + } + + const formatOptions = CoreUtils.instance.objectToKeyValueMap(this.course.courseformatoptions, 'name', 'value'); + + if (!formatOptions || formatOptions.coursedisplay != 1 || formatOptions.completionusertracked === false) { + return; + } + + this.sections.forEach((section) => { + let complete = 0; + let total = 0; + section.modules.forEach((module) => { + if (!module.uservisible || module.completiondata === undefined || module.completiondata.tracking === undefined || + module.completiondata.tracking <= CoreCourseProvider.COMPLETION_TRACKING_NONE) { + return; + } + + total++; + if (module.completiondata.state == CoreCourseProvider.COMPLETION_COMPLETE || + module.completiondata.state == CoreCourseProvider.COMPLETION_COMPLETE_PASS) { + complete++; + } + }); + + if (total > 0) { + section.progress = complete / total * 100; + } + }); + } + + /** + * Close the modal. + */ + closeModal(): void { + ModalController.instance.dismiss(); + } + + /** + * Select a section. + * + * @param section Selected section object. + */ + selectSection(section: SectionWithProgress): void { + if (section.uservisible !== false) { + ModalController.instance.dismiss(section); + } + } + +} + +type SectionWithProgress = CoreCourseSectionFormatted & { + progress?: number; +}; diff --git a/src/theme/app.scss b/src/theme/app.scss index 3bb2d68c2..1e88bf9ee 100644 --- a/src/theme/app.scss +++ b/src/theme/app.scss @@ -169,6 +169,9 @@ ion-toolbar { .item.core-danger-item { --border-color: var(--ion-color-danger); } +.item-dimmed { + opacity: 0.71; +} // Extra text colors. .text-gray {