Add mobile registration and dispatch event for mobile postmessage dance

This commit is contained in:
David Langley 2024-09-13 12:12:04 +01:00
parent eae9d9e248
commit a6dec86374
5 changed files with 79 additions and 19 deletions

View file

@ -94,6 +94,7 @@
@import "./structures/auth/_ConfirmSessionLockTheftView.pcss"; @import "./structures/auth/_ConfirmSessionLockTheftView.pcss";
@import "./structures/auth/_Login.pcss"; @import "./structures/auth/_Login.pcss";
@import "./structures/auth/_LoginSplashView.pcss"; @import "./structures/auth/_LoginSplashView.pcss";
@import "./structures/auth/_MobileRegistration.pcss";
@import "./structures/auth/_Registration.pcss"; @import "./structures/auth/_Registration.pcss";
@import "./structures/auth/_SessionLockStolenView.pcss"; @import "./structures/auth/_SessionLockStolenView.pcss";
@import "./structures/auth/_SetupEncryptionBody.pcss"; @import "./structures/auth/_SetupEncryptionBody.pcss";

View file

@ -0,0 +1,10 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
.mx_MobileRegister_body {
padding: 32px;
}

View file

@ -140,7 +140,7 @@ import { cleanUpDraftsIfRequired } from "../../DraftCleaner";
// legacy export // legacy export
export { default as Views } from "../../Views"; export { default as Views } from "../../Views";
const AUTH_SCREENS = ["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
// re-dispatched. NOTE: some actions are non-trivial and would require // re-dispatched. NOTE: some actions are non-trivial and would require
@ -189,6 +189,7 @@ interface IState {
register_session_id?: string; register_session_id?: string;
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
register_id_sid?: string; register_id_sid?: string;
isMobileRegistration?: boolean;
// When showing Modal dialogs we need to set aria-hidden on the root app element // When showing Modal dialogs we need to set aria-hidden on the root app element
// and disable it when there are no dialogs // and disable it when there are no dialogs
hideToSRUsers: boolean; hideToSRUsers: boolean;
@ -243,6 +244,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
currentUserId: null, currentUserId: null,
hideToSRUsers: false, hideToSRUsers: false,
isMobileRegistration: false,
syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. syncError: null, // If the current syncing status is ERROR, the error object, otherwise null.
resizeNotifier: new ResizeNotifier(), resizeNotifier: new ResizeNotifier(),
@ -650,6 +652,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
case "require_registration": case "require_registration":
startAnyRegistrationFlow(payload as any); startAnyRegistrationFlow(payload as any);
break; break;
case "start_mobile_registration":
this.startRegistration(payload.params || {}, true);
break;
case "start_registration": case "start_registration":
if (Lifecycle.isSoftLogout()) { if (Lifecycle.isSoftLogout()) {
this.onSoftLogout(); this.onSoftLogout();
@ -946,7 +951,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}); });
} }
private async startRegistration(params: { [key: string]: string }): Promise<void> { private async startRegistration(params: { [key: string]: string }, isMobileRegistration?: boolean): Promise<void> {
if (!SettingsStore.getValue(UIFeature.Registration)) { if (!SettingsStore.getValue(UIFeature.Registration)) {
this.showScreen("welcome"); this.showScreen("welcome");
return; return;
@ -976,12 +981,15 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
newState.register_client_secret = params.client_secret; newState.register_client_secret = params.client_secret;
newState.register_session_id = params.session_id; newState.register_session_id = params.session_id;
newState.register_id_sid = params.sid; newState.register_id_sid = params.sid;
newState.register_id_sid = params.sid;
} }
newState.isMobileRegistration = isMobileRegistration; //&& SettingsStore.getValue("Registration.mobileRegistrationHelper");
this.setStateForNewView(newState); this.setStateForNewView(newState);
ThemeController.isLogin = true; ThemeController.isLogin = true;
this.themeWatcher.recheck(); this.themeWatcher.recheck();
this.notifyNewScreen("register"); this.notifyNewScreen(isMobileRegistration ? "mobile_register" : "register");
} }
// switch view to the given room // switch view to the given room
@ -1721,6 +1729,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
params: params, params: params,
}); });
PerformanceMonitor.instance.start(PerformanceEntryNames.REGISTER); PerformanceMonitor.instance.start(PerformanceEntryNames.REGISTER);
} else if (screen === "mobile_register") {
dis.dispatch({
action: "start_mobile_registration",
params: params,
});
} else if (screen === "login") { } else if (screen === "login") {
dis.dispatch({ dis.dispatch({
action: "start_login", action: "start_login",
@ -2080,6 +2093,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
onServerConfigChange={this.onServerConfigChange} onServerConfigChange={this.onServerConfigChange}
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName} defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
fragmentAfterLogin={fragmentAfterLogin} fragmentAfterLogin={fragmentAfterLogin}
mobileRegister={this.state.isMobileRegistration}
{...this.getServerProperties()} {...this.getServerProperties()}
/> />
); );

View file

@ -53,6 +53,13 @@ const debuglog = (...args: any[]): void => {
} }
}; };
export interface MobileRegistrationResponse {
user_id: string;
home_server: string;
access_token: string;
device_id: string;
}
interface IProps { interface IProps {
serverConfig: ValidatedServerConfig; serverConfig: ValidatedServerConfig;
defaultDeviceDisplayName?: string; defaultDeviceDisplayName?: string;
@ -62,7 +69,7 @@ interface IProps {
sessionId?: string; sessionId?: string;
idSid?: string; idSid?: string;
fragmentAfterLogin?: string; fragmentAfterLogin?: string;
mobileRegister?: boolean;
// Called when the user has logged in. Params: // Called when the user has logged in. Params:
// - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken // - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken
// - The user's password, if available and applicable (may be cached in memory // - The user's password, if available and applicable (may be cached in memory
@ -410,18 +417,33 @@ export default class Registration extends React.Component<IProps, IState> {
debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken }); debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken });
// dont log in if we found a session for a different user // dont log in if we found a session for a different user
if (hasAccessToken && !newState.differentLoggedInUserId) { if (hasAccessToken && !newState.differentLoggedInUserId) {
await this.props.onLoggedIn( if (this.props.mobileRegister) {
{ const mobileResponse: MobileRegistrationResponse = {
userId, user_id: userId,
deviceId: (response as RegisterResponse).device_id!, home_server: this.state.matrixClient.getHomeserverUrl(),
homeserverUrl: this.state.matrixClient.getHomeserverUrl(), access_token: accessToken,
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(), device_id: (response as RegisterResponse).device_id!,
accessToken, };
}, const event = new CustomEvent<MobileRegistrationResponse>("mobileregistrationresponse", {
this.state.formVals.password!, detail: mobileResponse,
); });
document.dispatchEvent(event);
newState.busy = false;
newState.completedNoSignin = true;
} else {
await this.props.onLoggedIn(
{
userId,
deviceId: (response as RegisterResponse).device_id!,
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(),
accessToken,
},
this.state.formVals.password!,
);
this.setupPushers(); this.setupPushers();
}
} else { } else {
newState.busy = false; newState.busy = false;
newState.completedNoSignin = true; newState.completedNoSignin = true;
@ -558,7 +580,7 @@ export default class Registration extends React.Component<IProps, IState> {
); );
} else if (this.state.matrixClient && this.state.flows.length) { } else if (this.state.matrixClient && this.state.flows.length) {
let ssoSection: JSX.Element | undefined; let ssoSection: JSX.Element | undefined;
if (this.state.ssoFlow) { if (!this.props.mobileRegister && this.state.ssoFlow) {
let continueWithSection; let continueWithSection;
const providers = this.state.ssoFlow.identity_providers || []; const providers = this.state.ssoFlow.identity_providers || [];
// when there is only a single (or 0) providers we show a wide button with `Continue with X` text // when there is only a single (or 0) providers we show a wide button with `Continue with X` text
@ -591,7 +613,6 @@ export default class Registration extends React.Component<IProps, IState> {
</React.Fragment> </React.Fragment>
); );
} }
return ( return (
<React.Fragment> <React.Fragment>
{ssoSection} {ssoSection}
@ -660,7 +681,9 @@ export default class Registration extends React.Component<IProps, IState> {
let body; let body;
if (this.state.completedNoSignin) { if (this.state.completedNoSignin) {
let regDoneText; let regDoneText;
if (this.state.differentLoggedInUserId) { if (this.props.mobileRegister) {
regDoneText = undefined;
} else if (this.state.differentLoggedInUserId) {
regDoneText = ( regDoneText = (
<div> <div>
<p> <p>
@ -717,6 +740,8 @@ export default class Registration extends React.Component<IProps, IState> {
{regDoneText} {regDoneText}
</div> </div>
); );
} else if (this.props.mobileRegister) {
body = this.renderRegisterComponent();
} else { } else {
body = ( body = (
<Fragment> <Fragment>
@ -746,7 +771,13 @@ export default class Registration extends React.Component<IProps, IState> {
</Fragment> </Fragment>
); );
} }
if (this.props.mobileRegister) {
return (
<Fragment>
<div className="mx_MobileRegister_body">{body}</div>
</Fragment>
);
}
return ( return (
<AuthPage> <AuthPage>
<AuthHeader /> <AuthHeader />

View file

@ -876,6 +876,10 @@ export const SETTINGS: { [setting: string]: ISetting } = {
supportedLevels: LEVELS_ACCOUNT_SETTINGS, supportedLevels: LEVELS_ACCOUNT_SETTINGS,
default: null, default: null,
}, },
"Registration.mobileRegistrationHelper": {
supportedLevels: [SettingLevel.CONFIG],
default: false,
},
"autocompleteDelay": { "autocompleteDelay": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
default: 200, default: 200,