MOBILE-3934 grades: Add swipe navigation

main
Noel De Martin 2022-01-12 11:53:11 +01:00
parent 275991d9ef
commit 11c2468d58
5 changed files with 79 additions and 29 deletions

View File

@ -0,0 +1,41 @@
// (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 { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source';
import { CoreGrades } from '../services/grades';
import { CoreGradesGradeOverviewWithCourseData, CoreGradesHelper } from '../services/grades-helper';
/**
* Provides a collection of courses.
*/
export class CoreGradesCoursesSource extends CoreRoutedItemsManagerSource<CoreGradesGradeOverviewWithCourseData> {
/**
* @inheritdoc
*/
protected async loadPageItems(): Promise<{ items: CoreGradesGradeOverviewWithCourseData[] }> {
const grades = await CoreGrades.getCoursesGrades();
const courses = await CoreGradesHelper.getGradesCourseData(grades);
return { items: courses };
}
/**
* @inheritdoc
*/
getItemPath(course: CoreGradesGradeOverviewWithCourseData): string {
return course.courseid.toString();
}
}

View File

@ -22,6 +22,7 @@ const routes: Routes = [
{ {
path: '', path: '',
component: CoreGradesCoursePage, component: CoreGradesCoursePage,
data: { swipeEnabled: false },
}, },
]; ];

View File

@ -4,11 +4,11 @@
<ion-back-button [text]="'core.back' | translate"></ion-back-button> <ion-back-button [text]="'core.back' | translate"></ion-back-button>
</ion-buttons> </ion-buttons>
<ion-title> <ion-title>
<h1>{{ 'core.grades.grades' | translate }}</h1> <h1>{{ title }}</h1>
</ion-title> </ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content> <ion-content [core-swipe-navigation]="courses">
<ion-refresher slot="fixed" [disabled]="!columns || !rows" (ionRefresh)="refreshGrades($event.target)"> <ion-refresher slot="fixed" [disabled]="!columns || !rows" (ionRefresh)="refreshGrades($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher> </ion-refresher>

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { AfterViewInit, Component, ElementRef } from '@angular/core'; import { AfterViewInit, Component, ElementRef, OnDestroy } from '@angular/core';
import { IonRefresher } from '@ionic/angular'; import { IonRefresher } from '@ionic/angular';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
@ -28,6 +28,9 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CoreGradesCoursesSource } from '@features/grades/classes/grades-courses-source';
/** /**
* Page that displays a course grades. * Page that displays a course grades.
@ -37,12 +40,14 @@ import { Translate } from '@singletons';
templateUrl: 'course.html', templateUrl: 'course.html',
styleUrls: ['course.scss'], styleUrls: ['course.scss'],
}) })
export class CoreGradesCoursePage implements AfterViewInit { export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
courseId!: number; courseId!: number;
userId!: number; userId!: number;
expandLabel!: string; expandLabel!: string;
collapseLabel!: string; collapseLabel!: string;
title?: string;
courses?: CoreSwipeNavigationItemsManager;
columns?: CoreGradesFormattedTableColumn[]; columns?: CoreGradesFormattedTableColumn[];
rows?: CoreGradesFormattedTableRow[]; rows?: CoreGradesFormattedTableRow[];
totalColumnsSpan?: number; totalColumnsSpan?: number;
@ -54,6 +59,12 @@ export class CoreGradesCoursePage implements AfterViewInit {
this.userId = CoreNavigator.getRouteNumberParam('userId', { route }) ?? CoreSites.getCurrentSiteUserId(); this.userId = CoreNavigator.getRouteNumberParam('userId', { route }) ?? CoreSites.getCurrentSiteUserId();
this.expandLabel = Translate.instant('core.expand'); this.expandLabel = Translate.instant('core.expand');
this.collapseLabel = Translate.instant('core.collapse'); this.collapseLabel = Translate.instant('core.collapse');
if (route.snapshot.data.swipeEnabled ?? true) {
const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(CoreGradesCoursesSource, []);
this.courses = new CoreSwipeNavigationItemsManager(source);
}
} catch (error) { } catch (error) {
CoreDomUtils.showErrorModal(error); CoreDomUtils.showErrorModal(error);
@ -73,10 +84,18 @@ export class CoreGradesCoursePage implements AfterViewInit {
async ngAfterViewInit(): Promise<void> { async ngAfterViewInit(): Promise<void> {
this.withinSplitView = !!this.element.nativeElement.parentElement?.closest('core-split-view'); this.withinSplitView = !!this.element.nativeElement.parentElement?.closest('core-split-view');
await this.courses?.start();
await this.fetchInitialGrades(); await this.fetchInitialGrades();
await CoreGrades.logCourseGradesView(this.courseId, this.userId); await CoreGrades.logCourseGradesView(this.courseId, this.userId);
} }
/**
* @inheritdoc
*/
ngOnDestroy(): void {
this.courses?.destroy();
}
/** /**
* Get aria label for row. * Get aria label for row.
* *
@ -151,6 +170,7 @@ export class CoreGradesCoursePage implements AfterViewInit {
const table = await CoreGrades.getCourseGradesTable(this.courseId, this.userId); const table = await CoreGrades.getCourseGradesTable(this.courseId, this.userId);
const formattedTable = await CoreGradesHelper.formatGradesTable(table); const formattedTable = await CoreGradesHelper.formatGradesTable(table);
this.title = formattedTable.rows[0]?.gradeitem ?? Translate.instant('core.grades.grades');
this.columns = formattedTable.columns; this.columns = formattedTable.columns;
this.rows = formattedTable.rows; this.rows = formattedTable.rows;
this.totalColumnsSpan = formattedTable.columns.reduce((total, column) => total + column.colspan, 0); this.totalColumnsSpan = formattedTable.columns.reduce((total, column) => total + column.colspan, 0);

View File

@ -13,11 +13,12 @@
// limitations under the License. // limitations under the License.
import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
import { CorePageItemsListManager } from '@classes/page-items-list-manager'; import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreGradesCoursesSource } from '@features/grades/classes/grades-courses-source';
import { CoreGrades } from '@features/grades/services/grades'; import { CoreGrades } from '@features/grades/services/grades';
import { CoreGradesGradeOverviewWithCourseData, CoreGradesHelper } from '@features/grades/services/grades-helper';
import { IonRefresher } from '@ionic/angular'; import { IonRefresher } from '@ionic/angular';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
@ -31,10 +32,16 @@ import { CoreUtils } from '@services/utils/utils';
}) })
export class CoreGradesCoursesPage implements OnDestroy, AfterViewInit { export class CoreGradesCoursesPage implements OnDestroy, AfterViewInit {
courses: CoreGradesCoursesManager = new CoreGradesCoursesManager(CoreGradesCoursesPage); courses: CoreGradesCoursesManager;
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
constructor() {
const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(CoreGradesCoursesSource, []);
this.courses = new CoreGradesCoursesManager(source, CoreGradesCoursesPage);
}
/** /**
* @inheritdoc * @inheritdoc
*/ */
@ -58,7 +65,7 @@ export class CoreGradesCoursesPage implements OnDestroy, AfterViewInit {
*/ */
async refreshCourses(refresher: IonRefresher): Promise<void> { async refreshCourses(refresher: IonRefresher): Promise<void> {
await CoreUtils.ignoreErrors(CoreGrades.invalidateCoursesGradesData()); await CoreUtils.ignoreErrors(CoreGrades.invalidateCoursesGradesData());
await CoreUtils.ignoreErrors(this.fetchCourses()); await CoreUtils.ignoreErrors(this.courses.reload());
refresher?.complete(); refresher?.complete();
} }
@ -68,37 +75,18 @@ export class CoreGradesCoursesPage implements OnDestroy, AfterViewInit {
*/ */
private async fetchInitialCourses(): Promise<void> { private async fetchInitialCourses(): Promise<void> {
try { try {
await this.fetchCourses(); await this.courses.load();
} catch (error) { } catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'Error loading courses'); CoreDomUtils.showErrorModalDefault(error, 'Error loading courses');
this.courses.setItems([]);
} }
} }
/**
* Update the list of courses.
*/
private async fetchCourses(): Promise<void> {
const grades = await CoreGrades.getCoursesGrades();
const courses = await CoreGradesHelper.getGradesCourseData(grades);
this.courses.setItems(courses);
}
} }
/** /**
* Helper class to manage courses. * Helper class to manage courses.
*/ */
class CoreGradesCoursesManager extends CorePageItemsListManager<CoreGradesGradeOverviewWithCourseData> { class CoreGradesCoursesManager extends CoreListItemsManager {
/**
* @inheritdoc
*/
protected getItemPath(courseGrade: CoreGradesGradeOverviewWithCourseData): string {
return courseGrade.courseid.toString();
}
/** /**
* @inheritdoc * @inheritdoc