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
This commit is contained in:
@@ -145,9 +145,6 @@ import { type URLParams } from "../../vector/url_utils.ts";
|
|||||||
import { type QrLoginCredentials } from "../views/auth/LoginWithQR.tsx";
|
import { type QrLoginCredentials } from "../views/auth/LoginWithQR.tsx";
|
||||||
import { configureFromCompletedOAuthLogin } from "../../Lifecycle";
|
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"];
|
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
|
// Actions that are redirected through the onboarding process prior to being
|
||||||
@@ -404,7 +401,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
* Called when:
|
* Called when:
|
||||||
*
|
*
|
||||||
* - We successfully completed an OIDC or token login, via {@link initSession}.
|
* - 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.
|
* registration.
|
||||||
*
|
*
|
||||||
* In both cases, {@link Action.OnLoggedIn} will already have been emitted, but the call to {@link onShowPostLoginScreen} will
|
* 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<IProps, IState> {
|
|||||||
this.viewLogin();
|
this.viewLogin();
|
||||||
break;
|
break;
|
||||||
case "start_password_recovery":
|
case "start_password_recovery":
|
||||||
this.setStateForNewView({
|
if (SettingsStore.getValue(UIFeature.PasswordReset)) {
|
||||||
view: Views.FORGOT_PASSWORD,
|
this.setStateForNewView({
|
||||||
});
|
view: Views.FORGOT_PASSWORD,
|
||||||
this.notifyNewScreen("forgot_password");
|
});
|
||||||
|
this.notifyNewScreen("forgot_password");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "start_chat":
|
case "start_chat":
|
||||||
createRoom(MatrixClientPeg.safeGet(), {
|
createRoom(MatrixClientPeg.safeGet(), {
|
||||||
@@ -2028,6 +2027,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
});
|
});
|
||||||
} else if (ModuleApi.instance.navigation.locationRenderers.get(screen)) {
|
} else if (ModuleApi.instance.navigation.locationRenderers.get(screen)) {
|
||||||
this.setState({ page_type: 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<IProps, IState> {
|
|||||||
return fragmentAfterLogin;
|
return fragmentAfterLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
private getView(): JSX.Element {
|
||||||
const fragmentAfterLogin = this.getFragmentAfterLogin();
|
const fragmentAfterLogin = this.getFragmentAfterLogin();
|
||||||
let view: JSX.Element;
|
|
||||||
|
|
||||||
if (this.state.view === Views.LOADING) {
|
switch (this.state.view) {
|
||||||
view = (
|
case Views.LOADING:
|
||||||
<div className="mx_MatrixChat_splash">
|
return (
|
||||||
<Spinner />
|
<div className="mx_MatrixChat_splash">
|
||||||
</div>
|
<Spinner />
|
||||||
);
|
</div>
|
||||||
} else if (this.state.view === Views.CONFIRM_LOCK_THEFT) {
|
);
|
||||||
view = (
|
case Views.CONFIRM_LOCK_THEFT:
|
||||||
<ConfirmSessionLockTheftView
|
return (
|
||||||
onConfirm={() => {
|
<ConfirmSessionLockTheftView
|
||||||
this.setState({ view: Views.LOADING });
|
onConfirm={() => {
|
||||||
this.startInitSession();
|
this.setState({ view: Views.LOADING });
|
||||||
}}
|
this.startInitSession();
|
||||||
/>
|
}}
|
||||||
);
|
|
||||||
} else if (this.state.view === Views.COMPLETE_SECURITY) {
|
|
||||||
view = <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />;
|
|
||||||
} else if (this.state.view === Views.E2E_SETUP) {
|
|
||||||
view = <E2eSetup onCancelled={this.onCompleteSecurityE2eSetupFinished} />;
|
|
||||||
} 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 = (
|
|
||||||
<LoginSplashView
|
|
||||||
matrixClient={MatrixClientPeg.safeGet()}
|
|
||||||
onLogoutClick={this.onLogoutClick}
|
|
||||||
syncError={this.state.syncError}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} 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 = (
|
|
||||||
<LoggedInView
|
|
||||||
{...this.props}
|
|
||||||
{...this.state}
|
|
||||||
ref={this.loggedInView}
|
|
||||||
matrixClient={MatrixClientPeg.safeGet()}
|
|
||||||
onRegistered={this.onRegistered}
|
|
||||||
currentRoomId={this.state.currentRoomId}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
case Views.COMPLETE_SECURITY:
|
||||||
|
return <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />;
|
||||||
|
case Views.E2E_SETUP:
|
||||||
|
return <E2eSetup onCancelled={this.onCompleteSecurityE2eSetupFinished} />;
|
||||||
|
case Views.PENDING_CLIENT_START:
|
||||||
// we think we are logged in, but are still waiting for the /sync to complete
|
// we think we are logged in, but are still waiting for the /sync to complete
|
||||||
view = (
|
return (
|
||||||
<LoginSplashView
|
<LoginSplashView
|
||||||
matrixClient={MatrixClientPeg.safeGet()}
|
matrixClient={MatrixClientPeg.safeGet()}
|
||||||
onLogoutClick={this.onLogoutClick}
|
onLogoutClick={this.onLogoutClick}
|
||||||
syncError={this.state.syncError}
|
syncError={this.state.syncError}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
case Views.LOGGED_IN:
|
||||||
} else if (this.state.view === Views.WELCOME) {
|
// `ready` and `view==LOGGED_IN` may be set before `page_type` (because the
|
||||||
view = <Welcome {...this.getServerProperties()} />;
|
// latter is set via the dispatcher). If we don't yet have a `page_type`,
|
||||||
} else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) {
|
// keep showing the spinner for now.
|
||||||
const email = ThreepidInviteStore.instance.pickBestInvite()?.toEmail;
|
if (this.state.ready && this.state.page_type) {
|
||||||
view = (
|
/* for now, we stuff the entirety of our props and state into the LoggedInView.
|
||||||
<Registration
|
* we should go through and figure out what we actually need to pass down, as well
|
||||||
clientSecret={this.state.register_client_secret}
|
* as using something like redux to avoid having a billion bits of state kicking around.
|
||||||
sessionId={this.state.register_session_id}
|
*/
|
||||||
idSid={this.state.register_id_sid}
|
return (
|
||||||
email={email}
|
<LoggedInView
|
||||||
brand={this.props.config.brand}
|
{...this.props}
|
||||||
onLoggedIn={this.onRegisterFlowComplete}
|
{...this.state}
|
||||||
onLoginClick={this.onLoginClick}
|
ref={this.loggedInView}
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
matrixClient={MatrixClientPeg.safeGet()}
|
||||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
onRegistered={this.onRegistered}
|
||||||
fragmentAfterLogin={fragmentAfterLogin}
|
currentRoomId={this.state.currentRoomId}
|
||||||
mobileRegister={this.state.isMobileRegistration}
|
/>
|
||||||
{...this.getServerProperties()}
|
);
|
||||||
/>
|
} else {
|
||||||
);
|
// we think we are logged in, but are still waiting for the /sync to complete
|
||||||
} else if (this.state.view === Views.FORGOT_PASSWORD && SettingsStore.getValue(UIFeature.PasswordReset)) {
|
return (
|
||||||
view = (
|
<LoginSplashView
|
||||||
<ForgotPassword
|
matrixClient={MatrixClientPeg.safeGet()}
|
||||||
onComplete={this.onLoginClick}
|
onLogoutClick={this.onLogoutClick}
|
||||||
onLoginClick={this.onLoginClick}
|
syncError={this.state.syncError}
|
||||||
{...this.getServerProperties()}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
}
|
||||||
} else if (this.state.view === Views.LOGIN) {
|
case Views.WELCOME:
|
||||||
const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset);
|
return <Welcome {...this.getServerProperties()} />;
|
||||||
view = (
|
case Views.REGISTER:
|
||||||
<Login
|
return (
|
||||||
isSyncing={this.state.pendingInitialSync}
|
<Registration
|
||||||
onLoggedIn={this.onUserCompletedLoginFlow}
|
clientSecret={this.state.register_client_secret}
|
||||||
onRegisterClick={this.onRegisterClick}
|
sessionId={this.state.register_session_id}
|
||||||
fallbackHsUrl={this.getFallbackHsUrl()}
|
idSid={this.state.register_id_sid}
|
||||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
email={ThreepidInviteStore.instance.pickBestInvite()?.toEmail}
|
||||||
onForgotPasswordClick={showPasswordReset ? this.onForgotPasswordClick : undefined}
|
brand={this.props.config.brand}
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
onLoggedIn={this.onRegisterFlowComplete}
|
||||||
fragmentAfterLogin={fragmentAfterLogin}
|
onLoginClick={this.onLoginClick}
|
||||||
defaultUsername={this.props.urlParams?.defaults?.defaultUsername}
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
{...this.getServerProperties()}
|
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||||
/>
|
fragmentAfterLogin={fragmentAfterLogin}
|
||||||
);
|
mobileRegister={this.state.isMobileRegistration}
|
||||||
} else if (this.state.view === Views.SOFT_LOGOUT) {
|
{...this.getServerProperties()}
|
||||||
view = (
|
/>
|
||||||
<SoftLogout
|
);
|
||||||
urlParams={this.props.urlParams}
|
case Views.FORGOT_PASSWORD:
|
||||||
onTokenLoginCompleted={this.props.onTokenLoginCompleted}
|
return (
|
||||||
fragmentAfterLogin={fragmentAfterLogin}
|
<ForgotPassword
|
||||||
/>
|
onComplete={this.onLoginClick}
|
||||||
);
|
onLoginClick={this.onLoginClick}
|
||||||
} else if (this.state.view === Views.LOCK_STOLEN) {
|
{...this.getServerProperties()}
|
||||||
view = <SessionLockStolenView />;
|
/>
|
||||||
} else {
|
);
|
||||||
logger.error(`Unknown view ${this.state.view}`);
|
case Views.LOGIN:
|
||||||
return null;
|
return (
|
||||||
|
<Login
|
||||||
|
isSyncing={this.state.pendingInitialSync}
|
||||||
|
onLoggedIn={this.onUserCompletedLoginFlow}
|
||||||
|
onRegisterClick={this.onRegisterClick}
|
||||||
|
fallbackHsUrl={this.getFallbackHsUrl()}
|
||||||
|
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||||
|
onForgotPasswordClick={
|
||||||
|
SettingsStore.getValue(UIFeature.PasswordReset) ? this.onForgotPasswordClick : undefined
|
||||||
|
}
|
||||||
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
|
fragmentAfterLogin={fragmentAfterLogin}
|
||||||
|
defaultUsername={this.props.urlParams?.defaults?.defaultUsername}
|
||||||
|
{...this.getServerProperties()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case Views.SOFT_LOGOUT:
|
||||||
|
return (
|
||||||
|
<SoftLogout
|
||||||
|
urlParams={this.props.urlParams}
|
||||||
|
onTokenLoginCompleted={this.props.onTokenLoginCompleted}
|
||||||
|
fragmentAfterLogin={fragmentAfterLogin}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case Views.LOCK_STOLEN:
|
||||||
|
return <SessionLockStolenView />;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): React.ReactNode {
|
||||||
|
const view = this.getView();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|||||||
Reference in New Issue
Block a user