Merge pull request #3998 from NoelDeMartin/MOBILE-4485

MOBILE-4485: Improve app unsupported message
main
Dani Palou 2024-04-11 14:31:43 +02:00 committed by GitHub
commit ae9048acae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 126 additions and 12 deletions

View File

@ -2194,6 +2194,8 @@
"core.login.stillcantconnect": "local_moodlemobileapp", "core.login.stillcantconnect": "local_moodlemobileapp",
"core.login.supplyinfo": "moodle", "core.login.supplyinfo": "moodle",
"core.login.toggleremove": "local_moodlemobileapp", "core.login.toggleremove": "local_moodlemobileapp",
"core.login.unsupportedsite": "local_moodlemobileapp",
"core.login.unsupportedsitemessage": "local_moodlemobileapp",
"core.login.username": "moodle", "core.login.username": "moodle",
"core.login.usernamelowercase": "moodle", "core.login.usernamelowercase": "moodle",
"core.login.usernameoremail": "moodle", "core.login.usernameoremail": "moodle",

View File

@ -21,3 +21,6 @@ export const EMAIL_SIGNUP_FEATURE_NAME = 'CoreLoginEmailSignup';
export const FORGOTTEN_PASSWORD_FEATURE_NAME = 'NoDelegate_ForgottenPassword'; export const FORGOTTEN_PASSWORD_FEATURE_NAME = 'NoDelegate_ForgottenPassword';
export const IDENTITY_PROVIDERS_FEATURE_NAME = 'NoDelegate_IdentityProviders'; export const IDENTITY_PROVIDERS_FEATURE_NAME = 'NoDelegate_IdentityProviders';
export const IDENTITY_PROVIDER_FEATURE_NAME_PREFIX = 'NoDelegate_IdentityProvider_'; export const IDENTITY_PROVIDER_FEATURE_NAME_PREFIX = 'NoDelegate_IdentityProvider_';
// Event indicating that a user left the app because it wasn't supported by a site.
export const APP_UNSUPPORTED_CHURN = 'app_unsupported_churn';

View File

@ -119,6 +119,8 @@
"stillcantconnect": "Still can't connect?", "stillcantconnect": "Still can't connect?",
"supplyinfo": "More details", "supplyinfo": "More details",
"toggleremove": "Edit accounts list", "toggleremove": "Edit accounts list",
"unsupportedsite": "Site not accessible through the app",
"unsupportedsitemessage": "{{site}} can't be accessed through this app.\nYou can still access it using a web browser",
"username": "Username", "username": "Username",
"usernamelowercase": "Only lowercase letters allowed", "usernamelowercase": "Only lowercase letters allowed",
"usernameoremail": "Enter either username or email address", "usernameoremail": "Enter either username or email address",

View File

@ -35,6 +35,7 @@ import { CorePlatform } from '@services/platform';
import { CoreSitesFactory } from '@services/sites-factory'; import { CoreSitesFactory } from '@services/sites-factory';
import { EMAIL_SIGNUP_FEATURE_NAME, FORGOTTEN_PASSWORD_FEATURE_NAME } from '@features/login/constants'; import { EMAIL_SIGNUP_FEATURE_NAME, FORGOTTEN_PASSWORD_FEATURE_NAME } from '@features/login/constants';
import { CoreCustomURLSchemes } from '@services/urlschemes'; import { CoreCustomURLSchemes } from '@services/urlschemes';
import { CoreSiteError } from '@classes/errors/siteerror';
/** /**
* Page to enter the user credentials. * Page to enter the user credentials.
@ -292,7 +293,11 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
await CoreNavigator.navigateToSiteHome({ params: { urlToOpen: this.urlToOpen } }); await CoreNavigator.navigateToSiteHome({ params: { urlToOpen: this.urlToOpen } });
} catch (error) { } catch (error) {
if (error instanceof CoreSiteError && CoreLoginHelper.isAppUnsupportedError(error)) {
await CoreLoginHelper.showAppUnsupportedModal(siteUrl, this.site, error.debug);
} else {
CoreLoginHelper.treatUserTokenError(siteUrl, error, username, password); CoreLoginHelper.treatUserTokenError(siteUrl, error, username, password);
}
if (error.loggedout) { if (error.loggedout) {
CoreNavigator.navigate('/login/sites', { reset: true }); CoreNavigator.navigate('/login/sites', { reset: true });

View File

@ -48,6 +48,7 @@ import { CorePlatform } from '@services/platform';
import { CoreReferrer } from '@services/referrer'; import { CoreReferrer } from '@services/referrer';
import { CoreSitesFactory } from '@services/sites-factory'; import { CoreSitesFactory } from '@services/sites-factory';
import { ONBOARDING_DONE } from '@features/login/constants'; import { ONBOARDING_DONE } from '@features/login/constants';
import { CoreUnauthenticatedSite } from '@classes/sites/unauthenticated-site';
/** /**
* Site (url) chooser when adding a new site. * Site (url) chooser when adding a new site.
@ -301,7 +302,7 @@ export class CoreLoginSitePage implements OnInit {
url = url.trim(); url = url.trim();
if (url.match(/^(https?:\/\/)?campus\.example\.edu/)) { if (url.match(/^(https?:\/\/)?campus\.example\.edu/)) {
this.showLoginIssue(null, new CoreError(Translate.instant('core.login.errorexampleurl'))); this.showLoginIssue(url, new CoreError(Translate.instant('core.login.errorexampleurl')));
return; return;
} }
@ -403,17 +404,23 @@ export class CoreLoginSitePage implements OnInit {
* @param url The URL the user was trying to connect to. * @param url The URL the user was trying to connect to.
* @param error Error to display. * @param error Error to display.
*/ */
protected async showLoginIssue(url: string | null, error: CoreError): Promise<void> { protected async showLoginIssue(url: string, error: CoreError): Promise<void> {
let errorMessage = CoreDomUtils.getErrorMessage(error); let errorMessage = CoreDomUtils.getErrorMessage(error);
let siteExists = false;
let supportConfig: CoreUserSupportConfig | undefined = undefined;
let errorTitle: string | undefined;
let debug: CoreSiteErrorDebug | undefined; let debug: CoreSiteErrorDebug | undefined;
let errorTitle: string | undefined;
let site: CoreUnauthenticatedSite | undefined;
let supportConfig: CoreUserSupportConfig | undefined;
if (error instanceof CoreSiteError) { if (error instanceof CoreSiteError) {
supportConfig = error.supportConfig; supportConfig = error.supportConfig;
siteExists = supportConfig instanceof CoreUserGuestSupportConfig; site = supportConfig instanceof CoreUserGuestSupportConfig ? supportConfig.getSite() : undefined;
debug = error.debug; debug = error.debug;
if (CoreLoginHelper.isAppUnsupportedError(error)) {
await CoreLoginHelper.showAppUnsupportedModal(url, site, debug);
return;
}
} }
if (error instanceof CoreLoginError) { if (error instanceof CoreLoginError) {
@ -440,7 +447,7 @@ export class CoreLoginSitePage implements OnInit {
}), }),
} }
: ( : (
!siteExists !site
? { ? {
text: Translate.instant('core.needhelp'), text: Translate.instant('core.needhelp'),
cssClass: 'core-login-need-help', cssClass: 'core-login-need-help',

View File

@ -48,6 +48,7 @@ import {
TypeOfLogin, TypeOfLogin,
} from '@classes/sites/unauthenticated-site'; } from '@classes/sites/unauthenticated-site';
import { import {
APP_UNSUPPORTED_CHURN,
EMAIL_SIGNUP_FEATURE_NAME, EMAIL_SIGNUP_FEATURE_NAME,
FAQ_QRCODE_IMAGE_HTML, FAQ_QRCODE_IMAGE_HTML,
FAQ_QRCODE_INFO_DONE, FAQ_QRCODE_INFO_DONE,
@ -56,6 +57,7 @@ import {
IDENTITY_PROVIDER_FEATURE_NAME_PREFIX, IDENTITY_PROVIDER_FEATURE_NAME_PREFIX,
} from '../constants'; } from '../constants';
import { LazyRoutesModule } from '@/app/app-routing.module'; import { LazyRoutesModule } from '@/app/app-routing.module';
import { CoreSiteError, CoreSiteErrorDebug } from '@classes/errors/siteerror';
/** /**
* Helper provider that provides some common features regarding authentication. * Helper provider that provides some common features regarding authentication.
@ -65,6 +67,15 @@ export class CoreLoginHelperProvider {
protected static readonly PASSWORD_RESETS_CONFIG_KEY = 'password-resets'; protected static readonly PASSWORD_RESETS_CONFIG_KEY = 'password-resets';
private static readonly APP_UNSUPPORTED_ERRORS = [
'loginfailed',
'logintokenempty',
'logintokenerror',
'mobileservicesnotenabled',
'sitehasredirect',
'webservicesnotenabled',
];
protected logger: CoreLogger; protected logger: CoreLogger;
protected sessionExpiredCheckingSite: Record<string, boolean> = {}; protected sessionExpiredCheckingSite: Record<string, boolean> = {};
protected isOpenEditAlertShown = false; protected isOpenEditAlertShown = false;
@ -930,11 +941,61 @@ export class CoreLoginHelperProvider {
return String(CoreConstants.CONFIG.skipssoconfirmation) === 'true'; return String(CoreConstants.CONFIG.skipssoconfirmation) === 'true';
} }
/**
* Check whether the given error means that the app is not working in the site.
*
* @param error Site error.
* @returns Whether the given error means that the app is not working in the site.
*/
isAppUnsupportedError(error: CoreSiteError): boolean {
return CoreLoginHelperProvider.APP_UNSUPPORTED_ERRORS.includes(error.debug?.code ?? '');
}
/**
* Show modal indicating that the app is not supported in the site.
*
* @param siteUrl Site url.
* @param site Site instance.
* @param debug Error debug information.
*/
async showAppUnsupportedModal(siteUrl: string, site?: CoreUnauthenticatedSite, debug?: CoreSiteErrorDebug): Promise<void> {
const siteName = await site?.getSiteName() ?? siteUrl;
await CoreDomUtils.showAlertWithOptions({
header: Translate.instant('core.login.unsupportedsite'),
message: Translate.instant('core.login.unsupportedsitemessage', { site: siteName }),
buttons: [
{
text: Translate.instant('core.cancel'),
role: 'cancel',
},
{
text: Translate.instant('core.openinbrowser'),
handler: () => this.openInBrowserFallback(site?.getURL() ?? siteUrl, debug),
},
],
});
}
/**
* Open site in browser as fallback when it is not supported in the app.
*
* @param siteUrl Site url.
* @param debug Error debug information.
*/
async openInBrowserFallback(siteUrl: string, debug?: CoreSiteErrorDebug): Promise<void> {
CoreEvents.trigger(APP_UNSUPPORTED_CHURN, { siteUrl, debug });
await CoreUtils.openInBrowser(siteUrl, { showBrowserWarning: false });
}
/** /**
* Show a modal warning that the credentials introduced were not correct. * Show a modal warning that the credentials introduced were not correct.
*/ */
protected showInvalidLoginModal(error: CoreWSError): void { protected showInvalidLoginModal(error: CoreError): void {
CoreDomUtils.showErrorModal(error.message); const errorDetails = error instanceof CoreSiteError ? error.debug?.details : null;
CoreDomUtils.showErrorModal(errorDetails ?? error.message);
} }
/** /**
@ -1074,8 +1135,10 @@ export class CoreLoginHelperProvider {
* @param username Username. * @param username Username.
* @param password User password. * @param password User password.
*/ */
treatUserTokenError(siteUrl: string, error: CoreWSError, username?: string, password?: string): void { treatUserTokenError(siteUrl: string, error: CoreError, username?: string, password?: string): void {
switch (error.errorcode) { const errorCode = 'errorcode' in error ? error.errorcode : null;
switch (errorCode) {
case 'forcepasswordchangenotice': case 'forcepasswordchangenotice':
this.openChangePassword(siteUrl, CoreTextUtils.getErrorMessageFromError(error) ?? ''); this.openChangePassword(siteUrl, CoreTextUtils.getErrorMessageFromError(error) ?? '');
break; break;
@ -1617,3 +1680,16 @@ export type CoreLoginSiteFinderSettings = {
displayurl: boolean; displayurl: boolean;
defaultimageurl?: string; defaultimageurl?: string;
}; };
declare module '@singletons/events' {
/**
* Augment CoreEventsData interface with events specific to this service.
*
* @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
*/
export interface CoreEventsData {
[APP_UNSUPPORTED_CHURN]: { siteUrl: string; debug?: CoreSiteErrorDebug };
}
}

View File

@ -47,6 +47,16 @@ Feature: Test basic usage of login in app
And I press "Connect to your site" in the app And I press "Connect to your site" in the app
Then I should find "Can't connect to site" in the app Then I should find "Can't connect to site" in the app
Scenario: Attempt invalid login
When I launch the app
And I set the field "Your site" to "$WWWROOT" in the app
And I press "Connect to your site" in the app
And I set the following fields to these values in the app:
| Username | student1 |
| Password | wrongpassword |
And I press "Log in" near "Lost password?" in the app
Then I should find "Invalid login" in the app
Scenario: Add a non existing account from accounts switcher Scenario: Add a non existing account from accounts switcher
Given I entered the app as "student1" Given I entered the app as "student1"
And I press the user menu button in the app And I press the user menu button in the app

View File

@ -71,6 +71,15 @@ export class CoreUserGuestSupportConfig extends CoreUserSupportConfig {
return 'supportpage' in this.config; return 'supportpage' in this.config;
} }
/**
* Get site.
*
* @returns site.
*/
getSite(): CoreUnauthenticatedSite {
return this.site;
}
/** /**
* @inheritdoc * @inheritdoc
*/ */