Merge pull request #2823 from NoelDeMartin/MOBILE-3320

MOBILE-3320: Navigation improvements
main
Dani Palou 2021-06-10 16:30:39 +02:00 committed by GitHub
commit e3c74b3c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 84 additions and 105 deletions

View File

@ -4,10 +4,7 @@
* You can create your own environment files such as "moodle.config.prod.json" and "moodle.config.dev.json" * You can create your own environment files such as "moodle.config.prod.json" and "moodle.config.dev.json"
* to override some values. The values will be merged, so you don't need to duplicate everything in this file. * to override some values. The values will be merged, so you don't need to duplicate everything in this file.
*/ */
{ {
// @todo This could be read from package.json. // You can find all the properties you can configure in src/types/config.d.ts
"versionname": "3.9.3-dev",
// Override default language here.
"default_lang": "es" "default_lang": "es"
} }

View File

@ -23,6 +23,10 @@
"build": "ionic build", "build": "ionic build",
"build:prod": "NODE_ENV=production ionic build --prod", "build:prod": "NODE_ENV=production ionic build --prod",
"build:test": "NODE_ENV=testing ionic build", "build:test": "NODE_ENV=testing ionic build",
"dev:android": "ionic cordova run android --livereload",
"dev:ios": "ionic cordova run ios --livereload",
"prod:android": "NODE_ENV=production ionic cordova run android --aot",
"prod:ios": "NODE_ENV=production ionic cordova run ios --aot",
"test": "NODE_ENV=testing gulp && jest --verbose", "test": "NODE_ENV=testing gulp && jest --verbose",
"test:ci": "NODE_ENV=testing gulp && jest -ci --runInBand --verbose", "test:ci": "NODE_ENV=testing gulp && jest -ci --runInBand --verbose",
"test:watch": "NODE_ENV=testing gulp watch & jest --watch", "test:watch": "NODE_ENV=testing gulp watch & jest --watch",

View File

@ -158,15 +158,13 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
* Setup code for the page. * Setup code for the page.
*/ */
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
// Disable the profile button if we're already coming from a profile.
const backViewPage = CoreNavigator.getPreviousPath();
this.showInfo = !backViewPage || !CoreTextUtils.matchesGlob(backViewPage, '**/user/profile');
this.route.queryParams.subscribe(async (params) => { this.route.queryParams.subscribe(async (params) => {
const oldConversationId = this.conversationId; const oldConversationId = this.conversationId;
const oldUserId = this.userId; const oldUserId = this.userId;
this.conversationId = CoreNavigator.getRouteNumberParam('conversationId', { params }) || undefined; this.conversationId = CoreNavigator.getRouteNumberParam('conversationId', { params }) || undefined;
this.userId = CoreNavigator.getRouteNumberParam('userId', { params }) || undefined; this.userId = CoreNavigator.getRouteNumberParam('userId', { params }) || undefined;
this.showInfo = !params.hideInfo;
if (oldConversationId != this.conversationId || oldUserId != this.userId) { if (oldConversationId != this.conversationId || oldUserId != this.userId) {
// Showing reload again can break animations. // Showing reload again can break animations.

View File

@ -77,6 +77,7 @@ export class AddonMessagesSendMessageUserHandlerService implements CoreUserProfi
const pageParams: Params = { const pageParams: Params = {
showKeyboard: true, showKeyboard: true,
userId: user.id, userId: user.id,
hideInfo: true,
}; };
CoreNavigator.navigateToSitePath('/messages/discussion', { params: pageParams }); CoreNavigator.navigateToSitePath('/messages/discussion', { params: pageParams });
}, },

View File

@ -361,6 +361,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
{ {
params: { params: {
preview, preview,
fromIndex: true,
}, },
}, },
); );

View File

@ -61,6 +61,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
title?: string; title?: string;
preview = false; preview = false;
fromIndex = false;
cmId!: number; cmId!: number;
courseId!: number; courseId!: number;
feedback?: AddonModFeedbackWSFeedback; feedback?: AddonModFeedbackWSFeedback;
@ -96,6 +97,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
this.currentPage = CoreNavigator.getRouteNumberParam('page'); this.currentPage = CoreNavigator.getRouteNumberParam('page');
this.title = CoreNavigator.getRouteParam('title'); this.title = CoreNavigator.getRouteParam('title');
this.preview = !!CoreNavigator.getRouteBooleanParam('preview'); this.preview = !!CoreNavigator.getRouteBooleanParam('preview');
this.fromIndex = !!CoreNavigator.getRouteBooleanParam('fromIndex');
await this.fetchData(); await this.fetchData();
@ -373,10 +375,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
* Function to link implemented features. * Function to link implemented features.
*/ */
showAnalysis(): void { showAnalysis(): void {
const indexPath = AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${this.courseId}/${this.cmId}`; if (this.fromIndex) {
const previousPath = CoreNavigator.getPreviousPath();
if (previousPath.match(new RegExp(indexPath + '$'))) {
// Previous page is the index page, go back. // Previous page is the index page, go back.
CoreEvents.trigger(AddonModFeedbackProvider.FORM_SUBMITTED, { CoreEvents.trigger(AddonModFeedbackProvider.FORM_SUBMITTED, {
feedbackId: this.feedback!.id, feedbackId: this.feedback!.id,
@ -389,7 +388,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
return; return;
} }
CoreNavigator.navigateToSitePath(indexPath, { CoreNavigator.navigateToSitePath(AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${this.courseId}/${this.cmId}`, {
params: { params: {
module: this.module, module: this.module,
tab: 'analysis', tab: 'analysis',

View File

@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
/* eslint-disable @typescript-eslint/naming-convention */
import { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper';
import { CoreMainMenuLocalizedCustomItem } from '@features/mainmenu/services/mainmenu';
import { CoreSitesDemoSiteData } from '@services/sites';
import envJson from '@/assets/env.json'; import envJson from '@/assets/env.json';
import { OpenFileAction } from '@services/utils/utils'; import { EnvironmentConfig } from '@/types/config';
/** /**
* Context levels enumeration. * Context levels enumeration.
@ -141,46 +136,7 @@ export class CoreConstants {
} }
export interface EnvironmentConfig { interface EnvironmentBuild {
app_id: string;
appname: string;
versioncode: number;
versionname: string;
cache_update_frequency_usually: number;
cache_update_frequency_often: number;
cache_update_frequency_sometimes: number;
cache_update_frequency_rarely: number;
default_lang: string;
languages: Record<string, string>;
wsservice: string;
wsextservice: string;
demo_sites: Record<string, CoreSitesDemoSiteData>;
zoomlevels: Record<CoreZoomLevel, number>;
customurlscheme: string;
siteurl: string;
sitename: string;
multisitesdisplay: string;
sitefindersettings: Record<string, unknown>;
onlyallowlistedsites: boolean;
skipssoconfirmation: boolean;
forcedefaultlanguage: boolean;
privacypolicy: string;
notificoncolor: string;
enableanalytics: boolean;
enableonboarding: boolean;
forceColorScheme: CoreColorScheme;
forceLoginLogo: boolean;
ioswebviewscheme: string;
appstores: Record<string, string>;
displayqroncredentialscreen?: boolean;
displayqronsitescreen?: boolean;
forceOpenLinksIn: 'app' | 'browser';
iOSDefaultOpenFileAction?: OpenFileAction;
customMainMenuItems?: CoreMainMenuLocalizedCustomItem[];
feedbackFormUrl?: string | false;
};
export interface EnvironmentBuild {
version: string; version: string;
isProduction: boolean; isProduction: boolean;
isTesting: boolean; isTesting: boolean;

View File

@ -261,7 +261,6 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
this.siteId = id; this.siteId = id;
// @todo test that this is working properly.
await CoreNavigator.navigateToSiteHome({ params: { urlToOpen: this.urlToOpen } }); await CoreNavigator.navigateToSiteHome({ params: { urlToOpen: this.urlToOpen } });
} catch (error) { } catch (error) {
CoreLoginHelper.treatUserTokenError(siteUrl, error, username, password); CoreLoginHelper.treatUserTokenError(siteUrl, error, username, password);

View File

@ -23,7 +23,7 @@ import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/site'; import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/site';
import { CoreEvents } from '@singletons/events'; import { CoreEvents } from '@singletons/events';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreNavigationOptions, CoreNavigator, CoreRedirectPayload } from '@services/navigator'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
import { CoreForms } from '@singletons/form'; import { CoreForms } from '@singletons/form';
/** /**
@ -208,13 +208,9 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
this.credForm.controls['password'].reset(); this.credForm.controls['password'].reset();
// Go to the site initial page. // Go to the site initial page.
// @todo test that this is working properly (could we use navigateToSitePath instead?). this.page
await CoreNavigator.navigateToSiteHome({ ? await CoreNavigator.navigateToSitePath(this.page, { params: this.pageOptions })
params: { : await CoreNavigator.navigateToSiteHome();
redirectPath: this.page,
redirectOptions: this.pageOptions,
} as CoreRedirectPayload,
});
} catch (error) { } catch (error) {
CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password); CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password);

View File

@ -59,7 +59,7 @@ export class CoreMainMenuHomePage implements OnInit {
*/ */
ngOnInit(): void { ngOnInit(): void {
this.route.queryParams.subscribe((params: Partial<CoreRedirectPayload> & { urlToOpen?: string }) => { this.route.queryParams.subscribe((params: Partial<CoreRedirectPayload> & { urlToOpen?: string }) => {
this.urlToOpen = params.urlToOpen; this.urlToOpen = params.urlToOpen ?? this.urlToOpen;
if (params.redirectPath) { if (params.redirectPath) {
this.pendingRedirect = { this.pendingRedirect = {

View File

@ -455,7 +455,6 @@ export class CoreSettingsHelperProvider {
*/ */
canIUsePrefersColorScheme(): boolean { canIUsePrefersColorScheme(): boolean {
// The following check will check browser support but system may differ from that. // The following check will check browser support but system may differ from that.
// @todo Detect SO support to watch media query.
return window.matchMedia('(prefers-color-scheme)').media !== 'not all'; return window.matchMedia('(prefers-color-scheme)').media !== 'not all';
} }

View File

@ -16,6 +16,7 @@ import { ApplicationRef } from '@angular/core';
import { CorePushNotifications, CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications'; import { CorePushNotifications, CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications';
import { CoreApp, CoreAppProvider } from '@services/app'; import { CoreApp, CoreAppProvider } from '@services/app';
import { CoreCronDelegate, CoreCronDelegateService } from '@services/cron'; import { CoreCronDelegate, CoreCronDelegateService } from '@services/cron';
import { CoreCustomURLSchemes, CoreCustomURLSchemesProvider } from '@services/urlschemes';
import { Application } from '@singletons'; import { Application } from '@singletons';
type AutomatedTestsWindow = Window & { type AutomatedTestsWindow = Window & {
@ -23,6 +24,7 @@ type AutomatedTestsWindow = Window & {
appProvider?: CoreAppProvider; appProvider?: CoreAppProvider;
cronProvider?: CoreCronDelegateService; cronProvider?: CoreCronDelegateService;
pushNotifications?: CorePushNotificationsProvider; pushNotifications?: CorePushNotificationsProvider;
urlSchemes?: CoreCustomURLSchemesProvider;
}; };
function initializeAutomatedTestsWindow(window: AutomatedTestsWindow) { function initializeAutomatedTestsWindow(window: AutomatedTestsWindow) {
@ -30,6 +32,7 @@ function initializeAutomatedTestsWindow(window: AutomatedTestsWindow) {
window.appProvider = CoreApp.instance; window.appProvider = CoreApp.instance;
window.cronProvider = CoreCronDelegate.instance; window.cronProvider = CoreCronDelegate.instance;
window.pushNotifications = CorePushNotifications.instance; window.pushNotifications = CorePushNotifications.instance;
window.urlSchemes = CoreCustomURLSchemes.instance;
} }
export default function(): void { export default function(): void {

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRoute, NavigationStart, Params, Router as RouterService } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { NavigationOptions } from '@ionic/angular/providers/nav-controller'; import { NavigationOptions } from '@ionic/angular/providers/nav-controller';
@ -28,7 +28,6 @@ import { CoreUrlUtils } from '@services/utils/url';
import { CoreTextUtils } from '@services/utils/text'; import { CoreTextUtils } from '@services/utils/text';
import { makeSingleton, NavController, Router } from '@singletons'; import { makeSingleton, NavController, Router } from '@singletons';
import { CoreScreen } from './screen'; import { CoreScreen } from './screen';
import { filter } from 'rxjs/operators';
import { CoreApp } from './app'; import { CoreApp } from './app';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
@ -75,17 +74,6 @@ export class CoreNavigatorService {
protected storedParams: Record<number, unknown> = {}; protected storedParams: Record<number, unknown> = {};
protected lastParamId = 0; protected lastParamId = 0;
protected currentPath?: string;
protected previousPath?: string;
// @todo Param router is an optional param to let the mocking work.
constructor(router?: RouterService) {
router?.events.pipe(filter(event => event instanceof NavigationStart)).subscribe((routerEvent: NavigationStart) => {
// Using NavigationStart instead of NavigationEnd so it can be check on ngOnInit.
this.previousPath = this.currentPath;
this.currentPath = routerEvent.url;
});
}
/** /**
* Check whether the active route is using the given path. * Check whether the active route is using the given path.
@ -211,8 +199,6 @@ export class CoreNavigatorService {
const siteId = options.siteId ?? CoreSites.getCurrentSiteId(); const siteId = options.siteId ?? CoreSites.getCurrentSiteId();
const navigationOptions: CoreNavigationOptions = CoreObject.without(options, ['siteId']); const navigationOptions: CoreNavigationOptions = CoreObject.without(options, ['siteId']);
// @todo: When this function was in ContentLinksHelper, this code was inside NgZone. Check if it's needed.
// If the path doesn't belong to a site, call standard navigation. // If the path doesn't belong to a site, call standard navigation.
if (siteId === CoreConstants.NO_SITE_ID) { if (siteId === CoreConstants.NO_SITE_ID) {
return this.navigate(path, { return this.navigate(path, {
@ -267,17 +253,6 @@ export class CoreNavigatorService {
return CoreUrlUtils.removeUrlParams(Router.url); return CoreUrlUtils.removeUrlParams(Router.url);
} }
/**
* Get the previous navigation route path.
*
* @return Previous path.
*/
getPreviousPath(): string {
// @todo: Remove this method and the used attributes.
// This is a quick workarround to avoid loops. Ie, in messages we can navigate to user profile and there to messages.
return CoreUrlUtils.removeUrlParams(this.previousPath || '');
}
/** /**
* Iterately get the params checking parent routes. * Iterately get the params checking parent routes.
* *
@ -431,10 +406,6 @@ export class CoreNavigatorService {
* @return Whether navigation suceeded. * @return Whether navigation suceeded.
*/ */
protected async navigateToMainMenuPath(path: string, options: CoreNavigationOptions = {}): Promise<boolean> { protected async navigateToMainMenuPath(path: string, options: CoreNavigationOptions = {}): Promise<boolean> {
// Due to DeepLinker, we need to remove the path from the URL before going to main menu.
// IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL.
// @todo this.location.replaceState('');
options = { options = {
preferCurrentTab: true, preferCurrentTab: true,
...options, ...options,
@ -464,7 +435,6 @@ export class CoreNavigatorService {
} }
// Open the path within the default main tab. // Open the path within the default main tab.
// @todo test that this is working as expected
return this.navigate(`/main/${DEFAULT_MAIN_MENU_TAB}`, { return this.navigate(`/main/${DEFAULT_MAIN_MENU_TAB}`, {
...options, ...options,
params: { params: {

View File

@ -752,7 +752,6 @@ export class CoreSitesProvider {
}; };
const siteId = this.getCurrentSiteId(); const siteId = this.getCurrentSiteId();
const downloadUrl = CoreApp.getAppStoreUrl(storesConfig); const downloadUrl = CoreApp.getAppStoreUrl(storesConfig);
if (downloadUrl != null) { if (downloadUrl != null) {
@ -773,10 +772,8 @@ export class CoreSitesProvider {
} }
if (siteId) { if (siteId) {
// Logout if it's the currentSite. // Logout the currentSite.
if (siteId == this.getCurrentSiteId()) { await this.logout();
await this.logout();
}
// Always expire the token. // Always expire the token.
await this.setSiteLoggedOut(siteId, true); await this.setSiteLoggedOut(siteId, true);

59
src/types/config.d.ts vendored 100644
View File

@ -0,0 +1,59 @@
// (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 { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper';
import { CoreMainMenuLocalizedCustomItem } from '@features/mainmenu/services/mainmenu';
import { CoreSitesDemoSiteData } from '@services/sites';
import { OpenFileAction } from '@services/utils/utils';
/* eslint-disable @typescript-eslint/naming-convention */
export interface EnvironmentConfig {
app_id: string;
appname: string;
versioncode: number;
versionname: string; // @todo This could be removed and use build variables instead.
cache_update_frequency_usually: number;
cache_update_frequency_often: number;
cache_update_frequency_sometimes: number;
cache_update_frequency_rarely: number;
default_lang: string;
languages: Record<string, string>;
wsservice: string;
wsextservice: string;
demo_sites: Record<string, CoreSitesDemoSiteData>;
zoomlevels: Record<CoreZoomLevel, number>;
customurlscheme: string;
siteurl: string;
sitename: string;
multisitesdisplay: string;
sitefindersettings: Record<string, unknown>;
onlyallowlistedsites: boolean;
skipssoconfirmation: boolean;
forcedefaultlanguage: boolean;
privacypolicy: string;
notificoncolor: string;
enableanalytics: boolean;
enableonboarding: boolean;
forceColorScheme: CoreColorScheme;
forceLoginLogo: boolean;
ioswebviewscheme: string;
appstores: Record<string, string>;
displayqroncredentialscreen?: boolean;
displayqronsitescreen?: boolean;
forceOpenLinksIn: 'app' | 'browser';
iOSDefaultOpenFileAction?: OpenFileAction;
customMainMenuItems?: CoreMainMenuLocalizedCustomItem[];
feedbackFormUrl?: string | false;
}