From adc026cd50beeec94c24237bbae955c859c9723d Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Thu, 4 Feb 2021 19:55:55 +0100 Subject: [PATCH] Extract list items management from grades --- .../features/grades/pages/course/course.html | 18 +- .../features/grades/pages/course/course.ts | 201 ++++++++++-------- .../grades/pages/courses/courses.html | 22 +- .../features/grades/pages/courses/courses.ts | 144 ++++++------- src/core/features/grades/pages/grade/grade.ts | 6 +- .../features/grades/services/grades-helper.ts | 22 +- 6 files changed, 219 insertions(+), 194 deletions(-) diff --git a/src/core/features/grades/pages/course/course.html b/src/core/features/grades/pages/course/course.html index a7ba31495..f732a8470 100644 --- a/src/core/features/grades/pages/course/course.html +++ b/src/core/features/grades/pages/course/course.html @@ -8,18 +8,18 @@ - + - - + + -
+
@@ -45,14 +45,14 @@ - +
{ - this.layoutSubscription = CoreScreen.instance.layoutObservable.subscribe(() => this.updateActiveGrade()); + async ngAfterViewInit(): Promise { + await this.fetchInitialGrades(); - await this.fetchGradesTable(); - - // Add log in Moodle. - await CoreUtils.instance.ignoreErrors(CoreGrades.instance.logCourseGradesView(this.courseId, this.userId)); - } - - /** - * @inheritdoc - */ - ionViewWillEnter(): void { - this.updateActiveGrade(); + this.grades.watchSplitViewOutlet(this.splitView); + this.grades.start(); } /** * @inheritdoc */ ngOnDestroy(): void { - this.layoutSubscription?.unsubscribe(); + this.grades.destroy(); } /** - * Fetch all the data required for the view. - */ - async fetchGradesTable(): Promise { - try { - const table = await CoreGrades.instance.getCourseGradesTable(this.courseId, this.userId); - - this.gradesTable = CoreGradesHelper.instance.formatGradesTable(table); - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading grades'); - - this.gradesTable = { rows: [], columns: [] }; - } finally { - this.gradesTableLoaded = true; - } - } - - /** - * Refresh data. + * Refresh grades. * * @param refresher Refresher. */ - async refreshGradesTable(refresher: IonRefresher): Promise { - await CoreUtils.instance.ignoreErrors(CoreGrades.instance.invalidateCourseGradesData(this.courseId, this.userId)); - await CoreUtils.instance.ignoreErrors(this.fetchGradesTable()); + async refreshGrades(refresher: IonRefresher): Promise { + const { courseId, userId } = this.grades; - refresher.complete(); + await CoreUtils.instance.ignoreErrors(CoreGrades.instance.invalidateCourseGradesData(courseId, userId)); + await CoreUtils.instance.ignoreErrors(this.fetchGrades()); + + refresher?.complete(); } /** - * Navigate to the grade of the selected item. - * - * @param gradeId Grade item ID where to navigate. + * Obtain the initial table of grades. */ - async gotoGrade(gradeId: number): Promise { - const path = this.activeGradeId ? `../${gradeId}` : gradeId.toString(); + private async fetchInitialGrades(): Promise { + try { + await this.fetchGrades(); + } catch (error) { + CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading course'); - await CoreNavigator.instance.navigate(path, { - params: CoreObject.withoutEmpty({ userId: this.userId }), - }); - - this.updateActiveGrade(gradeId); - } - - /** - * Update active grade. - * - * @param activeGradeId Active grade id. - */ - private updateActiveGrade(activeGradeId?: number): void { - if (CoreScreen.instance.isMobile || this.splitView?.isNested) { - delete this.activeGradeId; - - return; + this.grades.setTable({ columns: [], rows: [] }); } - - this.activeGradeId = activeGradeId ?? this.guessActiveGrade(); } /** - * Guess active grade looking at the current route. - * - * @return Active grade id. + * Update the table of grades. */ - private guessActiveGrade(): number | undefined { - const gradeId = parseInt(this.route.snapshot?.firstChild?.params.gradeId); + private async fetchGrades(): Promise { + const table = await CoreGrades.instance.getCourseGradesTable(this.grades.courseId!, this.grades.userId); + const formattedTable = await CoreGradesHelper.instance.formatGradesTable(table); - return isNaN(gradeId) ? undefined : gradeId; + this.grades.setTable(formattedTable); + } + +} + +/** + * Helper to manage the table of grades. + */ +class CoreGradesCourseManager extends CorePageItemsListManager { + + courseId: number; + userId: number; + columns?: CoreGradesFormattedTableColumn[]; + rows?: CoreGradesFormattedTableRow[]; + + constructor(pageComponent: unknown, courseId: number, userId: number) { + super(pageComponent); + + this.courseId = courseId; + this.userId = userId; + } + + /** + * Set grades table. + * + * @param table Grades table. + */ + setTable(table: CoreGradesFormattedTable): void { + this.columns = table.columns; + this.rows = table.rows; + + this.setItems(table.rows.filter(this.isFilledRow)); + } + + /** + * @inheritdoc + */ + protected getDefaultItem(): CoreGradesFormattedTableRowFilled | null { + return null; + } + + /** + * @inheritdoc + */ + protected getItemPath(row: CoreGradesFormattedTableRowFilled): string { + return row.id.toString(); + } + + /** + * @inheritdoc + */ + protected getItemQueryParams(): Params { + return CoreObject.withoutEmpty({ userId: this.userId }); + } + + /** + * @inheritdoc + */ + protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null { + return route.params.gradeId ?? null; + } + + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + await CoreGrades.instance.logCourseGradesView(this.courseId!, this.userId!); + } + + /** + * Check whether the given row is filled or not. + * + * @param row Grades table row. + * @return Whether the given row is filled or not. + */ + private isFilledRow(row: CoreGradesFormattedTableRow): row is CoreGradesFormattedTableRowFilled { + return 'id' in row; } } diff --git a/src/core/features/grades/pages/courses/courses.html b/src/core/features/grades/pages/courses/courses.html index 64b6a39a4..c18910932 100644 --- a/src/core/features/grades/pages/courses/courses.html +++ b/src/core/features/grades/pages/courses/courses.html @@ -8,34 +8,34 @@ - + - + - + - {{grade.grade}} + {{course.grade}} diff --git a/src/core/features/grades/pages/courses/courses.ts b/src/core/features/grades/pages/courses/courses.ts index e25404996..013614f43 100644 --- a/src/core/features/grades/pages/courses/courses.ts +++ b/src/core/features/grades/pages/courses/courses.ts @@ -12,17 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { IonRefresher } from '@ionic/angular'; -import { Subscription } from 'rxjs'; +import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; +import { ActivatedRouteSnapshot } from '@angular/router'; +import { CorePageItemsListManager } from '@classes/page-items-list-manager'; -import { CoreDomUtils } from '@services/utils/dom'; +import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreGrades } from '@features/grades/services/grades'; -import { CoreGradesHelper, CoreGradesGradeOverviewWithCourseData } from '@features/grades/services/grades-helper'; -import { CoreNavigator } from '@services/navigator'; -import { CoreScreen } from '@services/screen'; +import { CoreGradesGradeOverviewWithCourseData, CoreGradesHelper } from '@features/grades/services/grades-helper'; +import { IonRefresher } from '@ionic/angular'; +import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; -import { ActivatedRoute } from '@angular/router'; /** * Page that displays courses grades (main menu option). @@ -31,113 +30,92 @@ import { ActivatedRoute } from '@angular/router'; selector: 'page-core-grades-courses', templateUrl: 'courses.html', }) -export class CoreGradesCoursesPage implements OnInit, OnDestroy { +export class CoreGradesCoursesPage implements OnDestroy, AfterViewInit { - grades?: CoreGradesGradeOverviewWithCourseData[]; - gradesLoaded = false; - activeCourseId?: number; - layoutSubscription?: Subscription; + courses: CoreGradesCoursesManager = new CoreGradesCoursesManager(CoreGradesCoursesPage); - constructor(private route: ActivatedRoute) {} + @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; /** * @inheritdoc */ - async ngOnInit(): Promise { - this.layoutSubscription = CoreScreen.instance.layoutObservable.subscribe(() => this.updateActiveCourse()); - this.updateActiveCourse(); + async ngAfterViewInit(): Promise { + await this.fetchInitialCourses(); - await this.fetchGrades(); - - if (!CoreScreen.instance.isMobile && !this.activeCourseId && this.grades && this.grades.length > 0) { - this.openCourse(this.grades[0].courseid); - } - - // Add log in Moodle. - await CoreUtils.instance.ignoreErrors(CoreGrades.instance.logCoursesGradesView()); - } - - /** - * @inheritdoc - */ - ionViewWillEnter(): void { - this.updateActiveCourse(); + this.courses.watchSplitViewOutlet(this.splitView); + this.courses.start(); } /** * @inheritdoc */ ngOnDestroy(): void { - this.layoutSubscription?.unsubscribe(); + this.courses.destroy(); } /** - * Fetch all the data required for the view. - */ - async fetchGrades(): Promise { - try { - const grades = await CoreGrades.instance.getCoursesGrades(); - const gradesWithCourseData = await CoreGradesHelper.instance.getGradesCourseData(grades); - - this.grades = gradesWithCourseData; - } catch (error) { - CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading grades'); - - this.grades = []; - } finally { - this.gradesLoaded = true; - } - } - - /** - * Refresh data. + * Refresh courses. * * @param refresher Refresher. */ - async refreshGrades(refresher: IonRefresher): Promise { + async refreshCourses(refresher: IonRefresher): Promise { await CoreUtils.instance.ignoreErrors(CoreGrades.instance.invalidateCoursesGradesData()); - await CoreUtils.instance.ignoreErrors(this.fetchGrades()); + await CoreUtils.instance.ignoreErrors(this.fetchCourses()); - refresher.complete(); + refresher?.complete(); } /** - * Navigate to the grades of the selected course. - * - * @param courseId Course Id where to navigate. + * Obtain the initial list of courses. */ - async openCourse(courseId: number): Promise { - const path = this.activeCourseId ? `../${courseId}` : courseId.toString(); + private async fetchInitialCourses(): Promise { + try { + await this.fetchCourses(); + } catch (error) { + CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading courses'); - await CoreNavigator.instance.navigate(path); - - this.updateActiveCourse(courseId); - } - - /** - * Update active course. - * - * @param activeCourseId Active course id. - */ - private updateActiveCourse(activeCourseId?: number): void { - if (CoreScreen.instance.isMobile) { - delete this.activeCourseId; - - return; + this.courses.setItems([]); } - - this.activeCourseId = activeCourseId ?? this.guessActiveCourse(); } /** - * Guess active course looking at the current route. - * - * @return Active course id. + * Update the list of courses. */ - private guessActiveCourse(): number | undefined { - const courseId = parseInt(this.route.snapshot?.firstChild?.params.courseId); + private async fetchCourses(): Promise { + const grades = await CoreGrades.instance.getCoursesGrades(); + const courses = await CoreGradesHelper.instance.getGradesCourseData(grades); - return isNaN(courseId) ? undefined : courseId; + this.courses.setItems(courses); + } + +} + +/** + * Helper class to manage courses. + */ +class CoreGradesCoursesManager extends CorePageItemsListManager { + + /** + * @inheritdoc + */ + protected getItemPath(courseGrade: CoreGradesGradeOverviewWithCourseData): string { + return courseGrade.courseid.toString(); + } + + /** + * @inheritdoc + */ + protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null { + const courseId = parseInt(route?.params.courseId); + + return isNaN(courseId) ? null : courseId.toString(); + } + + /** + * @inheritdoc + */ + protected async logActivity(): Promise { + await CoreGrades.instance.logCoursesGradesView(); } } diff --git a/src/core/features/grades/pages/grade/grade.ts b/src/core/features/grades/pages/grade/grade.ts index 3d41e0b7f..efec61258 100644 --- a/src/core/features/grades/pages/grade/grade.ts +++ b/src/core/features/grades/pages/grade/grade.ts @@ -38,9 +38,9 @@ export class CoreGradesGradePage implements OnInit { gradeLoaded = false; constructor(route: ActivatedRoute) { - this.courseId = route.snapshot.params.courseId ?? route.snapshot.parent?.params.courseId; - this.gradeId = route.snapshot.params.gradeId; - this.userId = route.snapshot.queryParams.userId ?? CoreSites.instance.getCurrentSiteUserId(); + this.courseId = parseInt(route.snapshot.params.courseId ?? route.snapshot.parent?.params.courseId); + this.gradeId = parseInt(route.snapshot.params.gradeId); + this.userId = parseInt(route.snapshot.queryParams.userId ?? CoreSites.instance.getCurrentSiteUserId()); } /** diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts index 5fa221650..8a27e791a 100644 --- a/src/core/features/grades/services/grades-helper.ts +++ b/src/core/features/grades/services/grades-helper.ts @@ -144,9 +144,9 @@ export class CoreGradesHelperProvider { */ formatGradesTable(table: CoreGradesTable): CoreGradesFormattedTable { const maxDepth = table.maxdepth; - const formatted: CoreGradesFormattedTable = { - columns: [], - rows: [], + const formatted = { + columns: [] as any[], + rows: [] as any[], }; // Columns, in order. @@ -673,9 +673,21 @@ export class CoreGradesHelper extends makeSingleton(CoreGradesHelperProvider) {} export type CoreGradesFormattedRow = any; export type CoreGradesFormattedRowForTable = any; export type CoreGradesFormattedItem = any; +export type CoreGradesFormattedTableColumn = any; +export type CoreGradesFormattedTableRow = CoreGradesFormattedTableRowFilled | CoreGradesFormattedTableRowEmpty; export type CoreGradesFormattedTable = { - columns: any[]; - rows: any[]; + columns: CoreGradesFormattedTableColumn[]; + rows: CoreGradesFormattedTableRow[]; +}; +export type CoreGradesFormattedTableRowFilled = { + // @todo complete types. + id: number; + itemtype: 'category' | 'leader'; + grade: unknown; + percentage: unknown; +}; +type CoreGradesFormattedTableRowEmpty ={ + // }; /**