From 67295e2334aea3c44859e42c6aebcb5afd1b4ea5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 18:15:19 +0100 Subject: [PATCH] Handle unknown screens better (#33793) * Remove legacy export * Extract view map to a switch case statement with typescript checking that all cases are handled This lets us drop the `default` case as we know we will never reach it. Additionally move all the settings conditions to the state-setters rather than the renderer so we are never stuck in an undefined state * Fix comment * Handle unknown screens better Redirect to welcome/home rather than getting stuck at spinner --- .../src/components/structures/MatrixChat.tsx | 233 +++++++++--------- 1 file changed, 118 insertions(+), 115 deletions(-) diff --git a/apps/web/src/components/structures/MatrixChat.tsx b/apps/web/src/components/structures/MatrixChat.tsx index bea3d2d453..71094c9163 100644 --- a/apps/web/src/components/structures/MatrixChat.tsx +++ b/apps/web/src/components/structures/MatrixChat.tsx @@ -145,9 +145,6 @@ import { type URLParams } from "../../vector/url_utils.ts"; import { type QrLoginCredentials } from "../views/auth/LoginWithQR.tsx"; import { configureFromCompletedOAuthLogin } from "../../Lifecycle"; -// legacy export -export { default as Views } from "../../Views"; - const AUTH_SCREENS = ["register", "mobile_register", "login", "forgot_password", "start_sso", "start_cas", "welcome"]; // Actions that are redirected through the onboarding process prior to being @@ -404,7 +401,7 @@ export default class MatrixChat extends React.PureComponent { * Called when: * * - We successfully completed an OIDC or token login, via {@link initSession}. - * - The {@link Login} or {@link Register} components notify us that we successfully completed a non-OIDC login or + * - The {@link Login} or {@link Registration} components notify us that we successfully completed a non-OIDC login or * registration. * * In both cases, {@link Action.OnLoggedIn} will already have been emitted, but the call to {@link onShowPostLoginScreen} will @@ -739,10 +736,12 @@ export default class MatrixChat extends React.PureComponent { this.viewLogin(); break; case "start_password_recovery": - this.setStateForNewView({ - view: Views.FORGOT_PASSWORD, - }); - this.notifyNewScreen("forgot_password"); + if (SettingsStore.getValue(UIFeature.PasswordReset)) { + this.setStateForNewView({ + view: Views.FORGOT_PASSWORD, + }); + this.notifyNewScreen("forgot_password"); + } break; case "start_chat": createRoom(MatrixClientPeg.safeGet(), { @@ -2028,6 +2027,9 @@ export default class MatrixChat extends React.PureComponent { }); } else if (ModuleApi.instance.navigation.locationRenderers.get(screen)) { this.setState({ page_type: screen }); + } else { + // Unknown screen requested + return this.showScreen(isLoggedOutOrGuest ? "welcome" : "home"); } } @@ -2212,125 +2214,126 @@ export default class MatrixChat extends React.PureComponent { return fragmentAfterLogin; } - public render(): React.ReactNode { + private getView(): JSX.Element { const fragmentAfterLogin = this.getFragmentAfterLogin(); - let view: JSX.Element; - if (this.state.view === Views.LOADING) { - view = ( -
- -
- ); - } else if (this.state.view === Views.CONFIRM_LOCK_THEFT) { - view = ( - { - this.setState({ view: Views.LOADING }); - this.startInitSession(); - }} - /> - ); - } else if (this.state.view === Views.COMPLETE_SECURITY) { - view = ; - } else if (this.state.view === Views.E2E_SETUP) { - view = ; - } else if (this.state.view === Views.PENDING_CLIENT_START) { - // we think we are logged in, but are still waiting for the /sync to complete - view = ( - - ); - } else if (this.state.view === Views.LOGGED_IN) { - // `ready` and `view==LOGGED_IN` may be set before `page_type` (because the - // latter is set via the dispatcher). If we don't yet have a `page_type`, - // keep showing the spinner for now. - if (this.state.ready && this.state.page_type) { - /* for now, we stuff the entirety of our props and state into the LoggedInView. - * we should go through and figure out what we actually need to pass down, as well - * as using something like redux to avoid having a billion bits of state kicking around. - */ - view = ( - + + + ); + case Views.CONFIRM_LOCK_THEFT: + return ( + { + this.setState({ view: Views.LOADING }); + this.startInitSession(); + }} /> ); - } else { + case Views.COMPLETE_SECURITY: + return ; + case Views.E2E_SETUP: + return ; + case Views.PENDING_CLIENT_START: // we think we are logged in, but are still waiting for the /sync to complete - view = ( + return ( ); - } - } else if (this.state.view === Views.WELCOME) { - view = ; - } else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) { - const email = ThreepidInviteStore.instance.pickBestInvite()?.toEmail; - view = ( - - ); - } else if (this.state.view === Views.FORGOT_PASSWORD && SettingsStore.getValue(UIFeature.PasswordReset)) { - view = ( - - ); - } else if (this.state.view === Views.LOGIN) { - const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset); - view = ( - - ); - } else if (this.state.view === Views.SOFT_LOGOUT) { - view = ( - - ); - } else if (this.state.view === Views.LOCK_STOLEN) { - view = ; - } else { - logger.error(`Unknown view ${this.state.view}`); - return null; + case Views.LOGGED_IN: + // `ready` and `view==LOGGED_IN` may be set before `page_type` (because the + // latter is set via the dispatcher). If we don't yet have a `page_type`, + // keep showing the spinner for now. + if (this.state.ready && this.state.page_type) { + /* for now, we stuff the entirety of our props and state into the LoggedInView. + * we should go through and figure out what we actually need to pass down, as well + * as using something like redux to avoid having a billion bits of state kicking around. + */ + return ( + + ); + } else { + // we think we are logged in, but are still waiting for the /sync to complete + return ( + + ); + } + case Views.WELCOME: + return ; + case Views.REGISTER: + return ( + + ); + case Views.FORGOT_PASSWORD: + return ( + + ); + case Views.LOGIN: + return ( + + ); + case Views.SOFT_LOGOUT: + return ( + + ); + case Views.LOCK_STOLEN: + return ; } + } + + public render(): React.ReactNode { + const view = this.getView(); return (