MOBILE-3675 course: Migrate grades tab

main
Noel De Martin 2021-02-09 17:04:04 +01:00
parent 9ba630c02a
commit 2ef54f4cac
12 changed files with 130 additions and 31 deletions

View File

@ -1,5 +1,3 @@
<ion-app> <ion-app>
<!-- @todo move /login/init UI here -->
<ion-router-outlet></ion-router-outlet> <ion-router-outlet></ion-router-outlet>
</ion-app> </ion-app>

View File

@ -520,12 +520,12 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
async selectByIndex(index: number, e?: Event): Promise<void> { async selectByIndex(index: number, e?: Event): Promise<void> {
e?.preventDefault();
e?.stopPropagation();
if (index < 0 || index >= this.tabs.length) { if (index < 0 || index >= this.tabs.length) {
if (this.selected) { if (this.selected) {
// Invalid index do not change tab. // Invalid index do not change tab.
e?.preventDefault();
e?.stopPropagation();
return; return;
} }
@ -536,9 +536,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
const tabToSelect = this.tabs[index]; const tabToSelect = this.tabs[index];
if (!tabToSelect || !tabToSelect.enabled || tabToSelect.id == this.selected) { if (!tabToSelect || !tabToSelect.enabled || tabToSelect.id == this.selected) {
// Already selected or not enabled. // Already selected or not enabled.
e?.preventDefault();
e?.stopPropagation();
return; return;
} }

View File

@ -18,7 +18,7 @@ import { IonRouterOutlet } from '@ionic/angular';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
enum CoreSplitViewMode { export enum CoreSplitViewMode {
MenuOnly = 'menu-only', // Hides content. MenuOnly = 'menu-only', // Hides content.
ContentOnly = 'content-only', // Hides menu. ContentOnly = 'content-only', // Hides menu.
MenuAndContent = 'menu-and-content', // Shows both menu and content. MenuAndContent = 'menu-and-content', // Shows both menu and content.
@ -34,6 +34,7 @@ export class CoreSplitViewComponent implements AfterViewInit, OnDestroy {
@ViewChild(IonRouterOutlet) outlet!: IonRouterOutlet; @ViewChild(IonRouterOutlet) outlet!: IonRouterOutlet;
@HostBinding('class') classes = ''; @HostBinding('class') classes = '';
@Input() placeholderText = 'core.emptysplit'; @Input() placeholderText = 'core.emptysplit';
@Input() mode?: CoreSplitViewMode;
isNested = false; isNested = false;
private outletRouteSubject: BehaviorSubject<ActivatedRouteSnapshot | null> = new BehaviorSubject(null); private outletRouteSubject: BehaviorSubject<ActivatedRouteSnapshot | null> = new BehaviorSubject(null);
@ -100,6 +101,10 @@ export class CoreSplitViewComponent implements AfterViewInit, OnDestroy {
* @return Split view mode. * @return Split view mode.
*/ */
private getCurrentMode(): CoreSplitViewMode { private getCurrentMode(): CoreSplitViewMode {
if (this.mode) {
return this.mode;
}
if (this.isNested) { if (this.isNested) {
return CoreSplitViewMode.MenuOnly; return CoreSplitViewMode.MenuOnly;
} }

View File

@ -43,8 +43,8 @@ import { CoreTabBase, CoreTabsBaseComponent } from '@classes/tabs';
* *
* Tab contents will only be shown if that tab is selected. * Tab contents will only be shown if that tab is selected.
* *
* @todo: Test behaviour when tabs are added late.
* @todo: Test RTL and tab history. * @todo: Test RTL and tab history.
* @todo: This should behave like the split-view in relation to routing (maybe we could reuse some code from CoreItemsListManager).
*/ */
@Component({ @Component({
selector: 'core-tabs-outlet', selector: 'core-tabs-outlet',

View File

@ -115,7 +115,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
// Load the course handlers. // Load the course handlers.
const handlers = await CoreCourseOptionsDelegate.instance.getHandlersToDisplay(this.course!, false, false); const handlers = await CoreCourseOptionsDelegate.instance.getHandlersToDisplay(this.course!, false, false);
this.tabs.concat(handlers.map(handler => handler.data)); this.tabs = [...this.tabs, ...handlers.map(handler => handler.data)];
let tabToLoad: number | undefined; let tabToLoad: number | undefined;

View File

@ -0,0 +1,38 @@
// (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 { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CoreGradesCoursePage } from './pages/course/course.page';
import { CoreGradesCoursePageModule } from './pages/course/course.module';
const routes: Routes = [
{
path: '',
component: CoreGradesCoursePage,
data: {
useSplitView: false,
outsideGradesTab: true,
},
},
];
@NgModule({
imports: [
RouterModule.forChild(routes),
CoreGradesCoursePageModule,
],
})
export class CoreGradesCourseLazyModule {}

View File

@ -18,13 +18,14 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CoreSharedModule } from '@/core/shared.module'; import { conditionalRoutes } from '@/app/app-routing.module';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreGradesCoursePage } from './pages/course/course'; import { CoreGradesCoursePage } from './pages/course/course.page';
import { CoreGradesCoursePageModule } from './pages/course/course.module';
import { CoreGradesCoursesPage } from './pages/courses/courses'; import { CoreGradesCoursesPage } from './pages/courses/courses';
import { CoreGradesGradePage } from './pages/grade/grade'; import { CoreGradesGradePage } from './pages/grade/grade';
import { conditionalRoutes } from '@/app/app-routing.module';
const mobileRoutes: Routes = [ const mobileRoutes: Routes = [
{ {
@ -76,10 +77,10 @@ const routes: Routes = [
IonicModule, IonicModule,
TranslateModule.forChild(), TranslateModule.forChild(),
CoreSharedModule, CoreSharedModule,
CoreGradesCoursePageModule,
], ],
declarations: [ declarations: [
CoreGradesCoursesPage, CoreGradesCoursesPage,
CoreGradesCoursePage,
CoreGradesGradePage, CoreGradesGradePage,
], ],
}) })

View File

@ -15,6 +15,7 @@
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { APP_INITIALIZER, NgModule } from '@angular/core';
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreCourseIndexRoutingModule } from '@features/course/pages/index/index-routing.module';
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate'; import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module'; import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
@ -33,10 +34,18 @@ const routes: Routes = [
}, },
]; ];
const courseIndexRoutes: Routes = [
{
path: 'grades',
loadChildren: () => import('@features/grades/grades-course-lazy.module').then(m => m.CoreGradesCourseLazyModule),
},
];
@NgModule({ @NgModule({
imports: [ imports: [
CoreMainMenuTabRoutingModule.forChild(routes), CoreMainMenuTabRoutingModule.forChild(routes),
CoreMainMenuRoutingModule.forChild({ children: routes }), CoreMainMenuRoutingModule.forChild({ children: routes }),
CoreCourseIndexRoutingModule.forChild({ children: courseIndexRoutes }),
], ],
providers: [ providers: [
{ {

View File

@ -7,7 +7,7 @@
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content> <ion-content>
<core-split-view> <core-split-view [mode]="splitViewMode">
<ion-refresher slot="fixed" [disabled]="!grades.loaded" (ionRefresh)="refreshGrades($event.target)"> <ion-refresher slot="fixed" [disabled]="!grades.loaded" (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

@ -0,0 +1,35 @@
// (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 { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreGradesCoursePage } from './course.page';
@NgModule({
imports: [
CommonModule,
IonicModule,
TranslateModule.forChild(),
CoreSharedModule,
],
declarations: [
CoreGradesCoursePage,
],
})
export class CoreGradesCoursePageModule {}

View File

@ -27,9 +27,10 @@ import {
} from '@features/grades/services/grades-helper'; } from '@features/grades/services/grades-helper';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent, CoreSplitViewMode } from '@components/split-view/split-view';
import { CoreObject } from '@singletons/object'; import { CoreObject } from '@singletons/object';
import { CorePageItemsListManager } from '@classes/page-items-list-manager'; import { CorePageItemsListManager } from '@classes/page-items-list-manager';
import { CoreNavigator } from '@services/navigator';
/** /**
* Page that displays a course grades. * Page that displays a course grades.
@ -42,14 +43,18 @@ import { CorePageItemsListManager } from '@classes/page-items-list-manager';
export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
grades: CoreGradesCourseManager; grades: CoreGradesCourseManager;
splitViewMode?: CoreSplitViewMode;
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
constructor(route: ActivatedRoute) { constructor(route: ActivatedRoute) {
const courseId = parseInt(route.snapshot.params.courseId); const courseId = parseInt(route.snapshot.params.courseId ?? route.snapshot.queryParams.courseId);
const userId = parseInt(route.snapshot.queryParams.userId ?? CoreSites.instance.getCurrentSiteUserId()); const userId = parseInt(route.snapshot.queryParams.userId ?? CoreSites.instance.getCurrentSiteUserId());
const useSplitView = route.snapshot.data.useSplitView ?? true;
const outsideGradesTab = route.snapshot.data.outsideGradesTab ?? false;
this.grades = new CoreGradesCourseManager(CoreGradesCoursePage, courseId, userId); this.splitViewMode = useSplitView ? undefined : CoreSplitViewMode.MenuOnly;
this.grades = new CoreGradesCourseManager(CoreGradesCoursePage, courseId, userId, outsideGradesTab);
} }
/** /**
@ -118,11 +123,14 @@ class CoreGradesCourseManager extends CorePageItemsListManager<CoreGradesFormatt
columns?: CoreGradesFormattedTableColumn[]; columns?: CoreGradesFormattedTableColumn[];
rows?: CoreGradesFormattedTableRow[]; rows?: CoreGradesFormattedTableRow[];
constructor(pageComponent: unknown, courseId: number, userId: number) { private outsideGradesTab: boolean;
constructor(pageComponent: unknown, courseId: number, userId: number, outsideGradesTab: boolean) {
super(pageComponent); super(pageComponent);
this.courseId = courseId; this.courseId = courseId;
this.userId = userId; this.userId = userId;
this.outsideGradesTab = outsideGradesTab;
} }
/** /**
@ -137,6 +145,19 @@ class CoreGradesCourseManager extends CorePageItemsListManager<CoreGradesFormatt
this.setItems(table.rows.filter(this.isFilledRow)); this.setItems(table.rows.filter(this.isFilledRow));
} }
/**
* @inheritdoc
*/
async select(row: CoreGradesFormattedTableRowFilled): Promise<void> {
if (this.outsideGradesTab) {
await CoreNavigator.instance.navigateToSitePath(`/grades/${this.courseId}/${row.id}`);
return;
}
return super.select(row);
}
/** /**
* @inheritdoc * @inheritdoc
*/ */

View File

@ -83,19 +83,14 @@ export class CoreGradesCourseOptionHandlerService implements CoreCourseOptionsHa
} }
/** /**
* Returns the data needed to render the handler. * @inheritdoc
*
* @return Data or promise resolved with the data.
*/ */
getDisplayData(): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> { getDisplayData(): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> {
throw new Error('CoreGradesCourseOptionHandler.getDisplayData is not implemented'); return {
title: 'core.grades.grades',
// @todo class: 'core-grades-course-handler',
// return { page: 'grades',
// title: 'core.grades.grades', };
// class: 'core-grades-course-handler',
// component: CoreGradesCourseComponent,
// };
} }
/** /**