Apply strictNullChecks
to src/components/views/auth/*
(#10299
* Apply `strictNullChecks` to src/components/views/auth/* * Iterate PR
This commit is contained in:
parent
c79eff2292
commit
32aa18ff2e
16 changed files with 127 additions and 122 deletions
|
@ -48,16 +48,6 @@ export type RecursivePartial<T> = {
|
||||||
: T[P];
|
: T[P];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inspired by https://stackoverflow.com/a/60206860
|
|
||||||
export type KeysWithObjectShape<Input> = {
|
|
||||||
[P in keyof Input]: Input[P] extends object
|
|
||||||
? // Arrays are counted as objects - exclude them
|
|
||||||
Input[P] extends Array<unknown>
|
|
||||||
? never
|
|
||||||
: P
|
|
||||||
: never;
|
|
||||||
}[keyof Input];
|
|
||||||
|
|
||||||
export type KeysStartingWith<Input extends object, Str extends string> = {
|
export type KeysStartingWith<Input extends object, Str extends string> = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
[P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X
|
[P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X
|
||||||
|
|
|
@ -19,7 +19,6 @@ import { Optional } from "matrix-events-sdk";
|
||||||
|
|
||||||
import { SnakedObject } from "./utils/SnakedObject";
|
import { SnakedObject } from "./utils/SnakedObject";
|
||||||
import { IConfigOptions, ISsoRedirectOptions } from "./IConfigOptions";
|
import { IConfigOptions, ISsoRedirectOptions } from "./IConfigOptions";
|
||||||
import { KeysWithObjectShape } from "./@types/common";
|
|
||||||
|
|
||||||
// see element-web config.md for docs, or the IConfigOptions interface for dev docs
|
// see element-web config.md for docs, or the IConfigOptions interface for dev docs
|
||||||
export const DEFAULTS: IConfigOptions = {
|
export const DEFAULTS: IConfigOptions = {
|
||||||
|
@ -78,10 +77,10 @@ export default class SdkConfig {
|
||||||
return SdkConfig.fallback.get(key, altCaseName);
|
return SdkConfig.fallback.get(key, altCaseName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getObject<K extends KeysWithObjectShape<IConfigOptions>>(
|
public static getObject<K extends keyof IConfigOptions>(
|
||||||
key: K,
|
key: K,
|
||||||
altCaseName?: string,
|
altCaseName?: string,
|
||||||
): Optional<SnakedObject<IConfigOptions[K]>> {
|
): Optional<SnakedObject<NonNullable<IConfigOptions[K]>>> {
|
||||||
const val = SdkConfig.get(key, altCaseName);
|
const val = SdkConfig.get(key, altCaseName);
|
||||||
if (val !== null && val !== undefined) {
|
if (val !== null && val !== undefined) {
|
||||||
return new SnakedObject(val);
|
return new SnakedObject(val);
|
||||||
|
|
|
@ -80,7 +80,7 @@ interface IProps {
|
||||||
// Called when the stage changes, or the stage's phase changes. First
|
// Called when the stage changes, or the stage's phase changes. First
|
||||||
// argument is the stage, second is the phase. Some stages do not have
|
// argument is the stage, second is the phase. Some stages do not have
|
||||||
// phases and will be counted as 0 (numeric).
|
// phases and will be counted as 0 (numeric).
|
||||||
onStagePhaseChange?(stage: AuthType, phase: number): void;
|
onStagePhaseChange?(stage: AuthType | null, phase: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -170,7 +170,8 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
|
||||||
busy: true,
|
busy: true,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
return await this.props.requestEmailToken(email, secret, attempt, session);
|
// We know this method only gets called on flows where requestEmailToken is passed but types don't
|
||||||
|
return await this.props.requestEmailToken!(email, secret, attempt, session);
|
||||||
} finally {
|
} finally {
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
|
@ -231,7 +232,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPhaseChange = (newPhase: number): void => {
|
private onPhaseChange = (newPhase: number): void => {
|
||||||
this.props.onStagePhaseChange?.(this.state.authStage, newPhase || 0);
|
this.props.onStagePhaseChange?.(this.state.authStage ?? null, newPhase || 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onStageCancel = (): void => {
|
private onStageCancel = (): void => {
|
||||||
|
|
|
@ -2015,7 +2015,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const fragmentAfterLogin = this.getFragmentAfterLogin();
|
const fragmentAfterLogin = this.getFragmentAfterLogin();
|
||||||
let view = null;
|
let view: JSX.Element;
|
||||||
|
|
||||||
if (this.state.view === Views.LOADING) {
|
if (this.state.view === Views.LOADING) {
|
||||||
view = (
|
view = (
|
||||||
|
@ -2132,6 +2132,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
view = <UseCaseSelection onFinished={(useCase): Promise<void> => this.onShowPostLoginScreen(useCase)} />;
|
view = <UseCaseSelection onFinished={(useCase): Promise<void> => this.onShowPostLoginScreen(useCase)} />;
|
||||||
} else {
|
} else {
|
||||||
logger.error(`Unknown view ${this.state.view}`);
|
logger.error(`Unknown view ${this.state.view}`);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -78,9 +78,9 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
// true if we're waiting for the user to complete
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
errorText?: ReactNode;
|
errorText?: ReactNode;
|
||||||
// true if we're waiting for the user to complete
|
|
||||||
// We remember the values entered by the user because
|
// We remember the values entered by the user because
|
||||||
// the registration form will be unmounted during the
|
// the registration form will be unmounted during the
|
||||||
// course of registration, but if there's an error we
|
// course of registration, but if there's an error we
|
||||||
|
@ -88,7 +88,7 @@ interface IState {
|
||||||
// values the user entered still in it. We can keep
|
// values the user entered still in it. We can keep
|
||||||
// them in this component's state since this component
|
// them in this component's state since this component
|
||||||
// persist for the duration of the registration process.
|
// persist for the duration of the registration process.
|
||||||
formVals: Record<string, string>;
|
formVals: Record<string, string | undefined>;
|
||||||
// user-interactive auth
|
// user-interactive auth
|
||||||
// If we've been given a session ID, we're resuming
|
// If we've been given a session ID, we're resuming
|
||||||
// straight back into UI auth
|
// straight back into UI auth
|
||||||
|
@ -96,9 +96,11 @@ interface IState {
|
||||||
// If set, we've registered but are not going to log
|
// If set, we've registered but are not going to log
|
||||||
// the user in to their new account automatically.
|
// the user in to their new account automatically.
|
||||||
completedNoSignin: boolean;
|
completedNoSignin: boolean;
|
||||||
flows: {
|
flows:
|
||||||
stages: string[];
|
| {
|
||||||
}[];
|
stages: string[];
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
// We perform liveliness checks later, but for now suppress the errors.
|
// We perform liveliness checks later, but for now suppress the errors.
|
||||||
// We also track the server dead errors independently of the regular errors so
|
// We also track the server dead errors independently of the regular errors so
|
||||||
// that we can render it differently, and override any other error the user may
|
// that we can render it differently, and override any other error the user may
|
||||||
|
@ -158,7 +160,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
window.removeEventListener("beforeunload", this.unloadCallback);
|
window.removeEventListener("beforeunload", this.unloadCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unloadCallback = (event: BeforeUnloadEvent): string => {
|
private unloadCallback = (event: BeforeUnloadEvent): string | undefined => {
|
||||||
if (this.state.doingUIAuth) {
|
if (this.state.doingUIAuth) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.returnValue = "";
|
event.returnValue = "";
|
||||||
|
@ -215,7 +217,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
this.loginLogic.setHomeserverUrl(hsUrl);
|
this.loginLogic.setHomeserverUrl(hsUrl);
|
||||||
this.loginLogic.setIdentityServerUrl(isUrl);
|
this.loginLogic.setIdentityServerUrl(isUrl);
|
||||||
|
|
||||||
let ssoFlow: ISSOFlow;
|
let ssoFlow: ISSOFlow | undefined;
|
||||||
try {
|
try {
|
||||||
const loginFlows = await this.loginLogic.getFlows();
|
const loginFlows = await this.loginLogic.getFlows();
|
||||||
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
if (serverConfig !== this.latestServerConfig) return; // discard, serverConfig changed from under us
|
||||||
|
@ -289,6 +291,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
sendAttempt: number,
|
sendAttempt: number,
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
): Promise<IRequestTokenResponse> => {
|
): Promise<IRequestTokenResponse> => {
|
||||||
|
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
||||||
return this.state.matrixClient.requestRegisterEmailToken(
|
return this.state.matrixClient.requestRegisterEmailToken(
|
||||||
emailAddress,
|
emailAddress,
|
||||||
clientSecret,
|
clientSecret,
|
||||||
|
@ -303,6 +306,8 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUIAuthFinished: InteractiveAuthCallback = async (success, response): Promise<void> => {
|
private onUIAuthFinished: InteractiveAuthCallback = async (success, response): Promise<void> => {
|
||||||
|
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
||||||
|
|
||||||
debuglog("Registration: ui authentication finished: ", { success, response });
|
debuglog("Registration: ui authentication finished: ", { success, response });
|
||||||
if (!success) {
|
if (!success) {
|
||||||
let errorText: ReactNode = (response as Error).message || (response as Error).toString();
|
let errorText: ReactNode = (response as Error).message || (response as Error).toString();
|
||||||
|
@ -327,10 +332,8 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if ((response as IAuthData).required_stages?.includes(AuthType.Msisdn)) {
|
} else if ((response as IAuthData).required_stages?.includes(AuthType.Msisdn)) {
|
||||||
let msisdnAvailable = false;
|
const flows = (response as IAuthData).available_flows ?? [];
|
||||||
for (const flow of (response as IAuthData).available_flows) {
|
const msisdnAvailable = flows.some((flow) => flow.stages.includes(AuthType.Msisdn));
|
||||||
msisdnAvailable = msisdnAvailable || flow.stages.includes(AuthType.Msisdn);
|
|
||||||
}
|
|
||||||
if (!msisdnAvailable) {
|
if (!msisdnAvailable) {
|
||||||
errorText = _t("This server does not support authentication with a phone number.");
|
errorText = _t("This server does not support authentication with a phone number.");
|
||||||
}
|
}
|
||||||
|
@ -348,12 +351,16 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixClientPeg.setJustRegisteredUserId((response as IAuthData).user_id);
|
const userId = (response as IAuthData).user_id;
|
||||||
|
const accessToken = (response as IAuthData).access_token;
|
||||||
|
if (!userId || !accessToken) throw new Error("Registration failed");
|
||||||
|
|
||||||
|
MatrixClientPeg.setJustRegisteredUserId(userId);
|
||||||
|
|
||||||
const newState: Partial<IState> = {
|
const newState: Partial<IState> = {
|
||||||
doingUIAuth: false,
|
doingUIAuth: false,
|
||||||
registeredUsername: (response as IAuthData).user_id,
|
registeredUsername: (response as IAuthData).user_id,
|
||||||
differentLoggedInUserId: null,
|
differentLoggedInUserId: undefined,
|
||||||
completedNoSignin: false,
|
completedNoSignin: false,
|
||||||
// we're still busy until we get unmounted: don't show the registration form again
|
// we're still busy until we get unmounted: don't show the registration form again
|
||||||
busy: true,
|
busy: true,
|
||||||
|
@ -393,13 +400,13 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
// the email, not the client that started the registration flow
|
// the email, not the client that started the registration flow
|
||||||
await this.props.onLoggedIn(
|
await this.props.onLoggedIn(
|
||||||
{
|
{
|
||||||
userId: (response as IAuthData).user_id,
|
userId,
|
||||||
deviceId: (response as IAuthData).device_id,
|
deviceId: (response as IAuthData).device_id,
|
||||||
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
|
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
|
||||||
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(),
|
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(),
|
||||||
accessToken: (response as IAuthData).access_token,
|
accessToken,
|
||||||
},
|
},
|
||||||
this.state.formVals.password,
|
this.state.formVals.password!,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.setupPushers();
|
this.setupPushers();
|
||||||
|
@ -457,6 +464,8 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private makeRegisterRequest = (auth: IAuthData | null): Promise<IAuthData> => {
|
private makeRegisterRequest = (auth: IAuthData | null): Promise<IAuthData> => {
|
||||||
|
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
||||||
|
|
||||||
const registerParams: IRegisterRequestParams = {
|
const registerParams: IRegisterRequestParams = {
|
||||||
username: this.state.formVals.username,
|
username: this.state.formVals.username,
|
||||||
password: this.state.formVals.password,
|
password: this.state.formVals.password,
|
||||||
|
@ -494,7 +503,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
return sessionLoaded;
|
return sessionLoaded;
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderRegisterComponent(): JSX.Element {
|
private renderRegisterComponent(): ReactNode {
|
||||||
if (this.state.matrixClient && this.state.doingUIAuth) {
|
if (this.state.matrixClient && this.state.doingUIAuth) {
|
||||||
return (
|
return (
|
||||||
<InteractiveAuth
|
<InteractiveAuth
|
||||||
|
@ -517,8 +526,8 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (this.state.flows.length) {
|
} else if (this.state.matrixClient && this.state.flows.length) {
|
||||||
let ssoSection;
|
let ssoSection: JSX.Element | undefined;
|
||||||
if (this.state.ssoFlow) {
|
if (this.state.ssoFlow) {
|
||||||
let continueWithSection;
|
let continueWithSection;
|
||||||
const providers = this.state.ssoFlow.identity_providers || [];
|
const providers = this.state.ssoFlow.identity_providers || [];
|
||||||
|
@ -571,6 +580,8 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
|
|
|
@ -83,7 +83,7 @@ export const DEFAULT_PHASE = 0;
|
||||||
interface IAuthEntryProps {
|
interface IAuthEntryProps {
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
loginType: string;
|
loginType: string;
|
||||||
authSessionId: string;
|
authSessionId?: string;
|
||||||
errorText?: string;
|
errorText?: string;
|
||||||
errorCode?: string;
|
errorCode?: string;
|
||||||
// Is the auth logic currently waiting for something to happen?
|
// Is the auth logic currently waiting for something to happen?
|
||||||
|
@ -120,7 +120,7 @@ export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswor
|
||||||
type: AuthType.Password,
|
type: AuthType.Password,
|
||||||
// TODO: Remove `user` once servers support proper UIA
|
// TODO: Remove `user` once servers support proper UIA
|
||||||
// See https://github.com/vector-im/element-web/issues/10312
|
// See https://github.com/vector-im/element-web/issues/10312
|
||||||
user: this.props.matrixClient.credentials.userId,
|
user: this.props.matrixClient.credentials.userId ?? undefined,
|
||||||
identifier: {
|
identifier: {
|
||||||
type: "m.id.user",
|
type: "m.id.user",
|
||||||
user: this.props.matrixClient.credentials.userId,
|
user: this.props.matrixClient.credentials.userId,
|
||||||
|
@ -286,7 +286,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const allPolicies = this.props.stageParams.policies || {};
|
const allPolicies = this.props.stageParams?.policies || {};
|
||||||
const prefLang = SettingsStore.getValue("language");
|
const prefLang = SettingsStore.getValue("language");
|
||||||
const initToggles: Record<string, boolean> = {};
|
const initToggles: Record<string, boolean> = {};
|
||||||
const pickedPolicies: {
|
const pickedPolicies: {
|
||||||
|
@ -300,12 +300,12 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
|
||||||
// Pick a language based on the user's language, falling back to english,
|
// Pick a language based on the user's language, falling back to english,
|
||||||
// and finally to the first language available. If there's still no policy
|
// and finally to the first language available. If there's still no policy
|
||||||
// available then the homeserver isn't respecting the spec.
|
// available then the homeserver isn't respecting the spec.
|
||||||
let langPolicy = policy[prefLang];
|
let langPolicy: LocalisedPolicy | undefined = policy[prefLang];
|
||||||
if (!langPolicy) langPolicy = policy["en"];
|
if (!langPolicy) langPolicy = policy["en"];
|
||||||
if (!langPolicy) {
|
if (!langPolicy) {
|
||||||
// last resort
|
// last resort
|
||||||
const firstLang = Object.keys(policy).find((e) => e !== "version");
|
const firstLang = Object.keys(policy).find((e) => e !== "version");
|
||||||
langPolicy = policy[firstLang];
|
langPolicy = firstLang ? policy[firstLang] : undefined;
|
||||||
}
|
}
|
||||||
if (!langPolicy) throw new Error("Failed to find a policy to show the user");
|
if (!langPolicy) throw new Error("Failed to find a policy to show the user");
|
||||||
|
|
||||||
|
@ -358,7 +358,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
|
||||||
return <Spinner />;
|
return <Spinner />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkboxes = [];
|
const checkboxes: JSX.Element[] = [];
|
||||||
let allChecked = true;
|
let allChecked = true;
|
||||||
for (const policy of this.state.policies) {
|
for (const policy of this.state.policies) {
|
||||||
const checked = this.state.toggledPolicies[policy.id];
|
const checked = this.state.toggledPolicies[policy.id];
|
||||||
|
@ -384,7 +384,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let submitButton;
|
let submitButton: JSX.Element | undefined;
|
||||||
if (this.props.showContinue !== false) {
|
if (this.props.showContinue !== false) {
|
||||||
// XXX: button classes
|
// XXX: button classes
|
||||||
submitButton = (
|
submitButton = (
|
||||||
|
@ -462,7 +462,7 @@ export class EmailIdentityAuthEntry extends React.Component<
|
||||||
// We only have a session ID if the user has clicked the link in their email,
|
// We only have a session ID if the user has clicked the link in their email,
|
||||||
// so show a loading state instead of "an email has been sent to..." because
|
// so show a loading state instead of "an email has been sent to..." because
|
||||||
// that's confusing when you've already read that email.
|
// that's confusing when you've already read that email.
|
||||||
if (this.props.inputs.emailAddress === undefined || this.props.stageState?.emailSid) {
|
if (this.props.inputs?.emailAddress === undefined || this.props.stageState?.emailSid) {
|
||||||
if (errorSection) {
|
if (errorSection) {
|
||||||
return errorSection;
|
return errorSection;
|
||||||
}
|
}
|
||||||
|
@ -549,13 +549,13 @@ interface IMsisdnAuthEntryProps extends IAuthEntryProps {
|
||||||
interface IMsisdnAuthEntryState {
|
interface IMsisdnAuthEntryState {
|
||||||
token: string;
|
token: string;
|
||||||
requestingToken: boolean;
|
requestingToken: boolean;
|
||||||
errorText: string;
|
errorText: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsisdnAuthEntryState> {
|
export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsisdnAuthEntryState> {
|
||||||
public static LOGIN_TYPE = AuthType.Msisdn;
|
public static LOGIN_TYPE = AuthType.Msisdn;
|
||||||
|
|
||||||
private submitUrl: string;
|
private submitUrl?: string;
|
||||||
private sid: string;
|
private sid: string;
|
||||||
private msisdn: string;
|
private msisdn: string;
|
||||||
|
|
||||||
|
@ -798,11 +798,13 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
|
||||||
public static PHASE_POSTAUTH = 2; // button to confirm SSO completed
|
public static PHASE_POSTAUTH = 2; // button to confirm SSO completed
|
||||||
|
|
||||||
private ssoUrl: string;
|
private ssoUrl: string;
|
||||||
private popupWindow: Window;
|
private popupWindow: Window | null;
|
||||||
|
|
||||||
public constructor(props: ISSOAuthEntryProps) {
|
public constructor(props: ISSOAuthEntryProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
if (!this.props.authSessionId) throw new Error("This UIA flow requires an authSessionId");
|
||||||
|
|
||||||
// We actually send the user through fallback auth so we don't have to
|
// We actually send the user through fallback auth so we don't have to
|
||||||
// deal with a redirect back to us, losing application context.
|
// deal with a redirect back to us, losing application context.
|
||||||
this.ssoUrl = props.matrixClient.getFallbackAuthUrl(this.props.loginType, this.props.authSessionId);
|
this.ssoUrl = props.matrixClient.getFallbackAuthUrl(this.props.loginType, this.props.authSessionId);
|
||||||
|
@ -858,10 +860,10 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let continueButton = null;
|
let continueButton: JSX.Element;
|
||||||
const cancelButton = (
|
const cancelButton = (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
onClick={this.props.onCancel}
|
onClick={this.props.onCancel ?? null}
|
||||||
kind={this.props.continueKind ? this.props.continueKind + "_outline" : "primary_outline"}
|
kind={this.props.continueKind ? this.props.continueKind + "_outline" : "primary_outline"}
|
||||||
>
|
>
|
||||||
{_t("Cancel")}
|
{_t("Cancel")}
|
||||||
|
@ -909,7 +911,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
||||||
private popupWindow: Window;
|
private popupWindow: Window | null;
|
||||||
private fallbackButton = createRef<HTMLButtonElement>();
|
private fallbackButton = createRef<HTMLButtonElement>();
|
||||||
|
|
||||||
public constructor(props: IAuthEntryProps) {
|
public constructor(props: IAuthEntryProps) {
|
||||||
|
@ -927,18 +929,16 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
window.removeEventListener("message", this.onReceiveMessage);
|
window.removeEventListener("message", this.onReceiveMessage);
|
||||||
if (this.popupWindow) {
|
this.popupWindow?.close();
|
||||||
this.popupWindow.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public focus = (): void => {
|
public focus = (): void => {
|
||||||
if (this.fallbackButton.current) {
|
this.fallbackButton.current?.focus();
|
||||||
this.fallbackButton.current.focus();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private onShowFallbackClick = (e: MouseEvent): void => {
|
private onShowFallbackClick = (e: MouseEvent): void => {
|
||||||
|
if (!this.props.authSessionId) return;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import LanguageDropdown from "../elements/LanguageDropdown";
|
||||||
function onChange(newLang: string): void {
|
function onChange(newLang: string): void {
|
||||||
if (getCurrentLanguage() !== newLang) {
|
if (getCurrentLanguage() !== newLang) {
|
||||||
SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLang);
|
SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLang);
|
||||||
PlatformPeg.get().reload();
|
PlatformPeg.get()?.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,16 @@ import Field, { IInputProps } from "../elements/Field";
|
||||||
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
|
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
|
||||||
import { _t, _td } from "../../../languageHandler";
|
import { _t, _td } from "../../../languageHandler";
|
||||||
|
|
||||||
interface IProps extends Omit<IInputProps, "onValidate"> {
|
interface IProps extends Omit<IInputProps, "onValidate" | "label"> {
|
||||||
id?: string;
|
id?: string;
|
||||||
fieldRef?: RefCallback<Field> | RefObject<Field>;
|
fieldRef?: RefCallback<Field> | RefObject<Field>;
|
||||||
autoComplete?: string;
|
autoComplete?: string;
|
||||||
value: string;
|
value: string;
|
||||||
password: string; // The password we're confirming
|
password: string; // The password we're confirming
|
||||||
|
|
||||||
labelRequired?: string;
|
label: string;
|
||||||
labelInvalid?: string;
|
labelRequired: string;
|
||||||
|
labelInvalid: string;
|
||||||
|
|
||||||
onChange(ev: React.FormEvent<HTMLElement>): void;
|
onChange(ev: React.FormEvent<HTMLElement>): void;
|
||||||
onValidate?(result: IValidationResult): void;
|
onValidate?(result: IValidationResult): void;
|
||||||
|
|
|
@ -31,10 +31,10 @@ interface IProps extends Omit<IInputProps, "onValidate"> {
|
||||||
value: string;
|
value: string;
|
||||||
fieldRef?: RefCallback<Field> | RefObject<Field>;
|
fieldRef?: RefCallback<Field> | RefObject<Field>;
|
||||||
|
|
||||||
label?: string;
|
label: string;
|
||||||
labelEnterPassword?: string;
|
labelEnterPassword: string;
|
||||||
labelStrongPassword?: string;
|
labelStrongPassword: string;
|
||||||
labelAllowedButUnsafe?: string;
|
labelAllowedButUnsafe: string;
|
||||||
|
|
||||||
onChange(ev: React.FormEvent<HTMLElement>): void;
|
onChange(ev: React.FormEvent<HTMLElement>): void;
|
||||||
onValidate?(result: IValidationResult): void;
|
onValidate?(result: IValidationResult): void;
|
||||||
|
@ -48,12 +48,12 @@ class PassphraseField extends PureComponent<IProps> {
|
||||||
labelAllowedButUnsafe: _td("Password is allowed, but unsafe"),
|
labelAllowedButUnsafe: _td("Password is allowed, but unsafe"),
|
||||||
};
|
};
|
||||||
|
|
||||||
public readonly validate = withValidation<this, zxcvbn.ZXCVBNResult>({
|
public readonly validate = withValidation<this, zxcvbn.ZXCVBNResult | null>({
|
||||||
description: function (complexity) {
|
description: function (complexity) {
|
||||||
const score = complexity ? complexity.score : 0;
|
const score = complexity ? complexity.score : 0;
|
||||||
return <progress className="mx_PassphraseField_progress" max={4} value={score} />;
|
return <progress className="mx_PassphraseField_progress" max={4} value={score} />;
|
||||||
},
|
},
|
||||||
deriveData: async ({ value }): Promise<zxcvbn.ZXCVBNResult> => {
|
deriveData: async ({ value }): Promise<zxcvbn.ZXCVBNResult | null> => {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
const { scorePassword } = await import("../../../utils/PasswordScorer");
|
const { scorePassword } = await import("../../../utils/PasswordScorer");
|
||||||
return scorePassword(value);
|
return scorePassword(value);
|
||||||
|
@ -67,7 +67,7 @@ class PassphraseField extends PureComponent<IProps> {
|
||||||
{
|
{
|
||||||
key: "complexity",
|
key: "complexity",
|
||||||
test: async function ({ value }, complexity): Promise<boolean> {
|
test: async function ({ value }, complexity): Promise<boolean> {
|
||||||
if (!value) {
|
if (!value || !complexity) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const safe = complexity.score >= this.props.minScore;
|
const safe = complexity.score >= this.props.minScore;
|
||||||
|
@ -78,7 +78,7 @@ class PassphraseField extends PureComponent<IProps> {
|
||||||
// Unsafe passwords that are valid are only possible through a
|
// Unsafe passwords that are valid are only possible through a
|
||||||
// configuration flag. We'll print some helper text to signal
|
// configuration flag. We'll print some helper text to signal
|
||||||
// to the user that their password is allowed, but unsafe.
|
// to the user that their password is allowed, but unsafe.
|
||||||
if (complexity.score >= this.props.minScore) {
|
if (complexity && complexity.score >= this.props.minScore) {
|
||||||
return _t(this.props.labelStrongPassword);
|
return _t(this.props.labelStrongPassword);
|
||||||
}
|
}
|
||||||
return _t(this.props.labelAllowedButUnsafe);
|
return _t(this.props.labelAllowedButUnsafe);
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { BaseSyntheticEvent } from "react";
|
import React, { BaseSyntheticEvent, ReactNode } from "react";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { MatrixError } from "matrix-js-sdk/src/matrix";
|
import { MatrixError } from "matrix-js-sdk/src/matrix";
|
||||||
|
@ -82,7 +82,7 @@ interface IState {
|
||||||
// Field error codes by field ID
|
// Field error codes by field ID
|
||||||
fieldValid: Partial<Record<RegistrationField, boolean>>;
|
fieldValid: Partial<Record<RegistrationField, boolean>>;
|
||||||
// The ISO2 country code selected in the phone number entry
|
// The ISO2 country code selected in the phone number entry
|
||||||
phoneCountry: string;
|
phoneCountry?: string;
|
||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
phoneNumber: string;
|
phoneNumber: string;
|
||||||
|
@ -95,11 +95,11 @@ interface IState {
|
||||||
* A pure UI component which displays a registration form.
|
* A pure UI component which displays a registration form.
|
||||||
*/
|
*/
|
||||||
export default class RegistrationForm extends React.PureComponent<IProps, IState> {
|
export default class RegistrationForm extends React.PureComponent<IProps, IState> {
|
||||||
private [RegistrationField.Email]: Field;
|
private [RegistrationField.Email]: Field | null;
|
||||||
private [RegistrationField.Password]: Field;
|
private [RegistrationField.Password]: Field | null;
|
||||||
private [RegistrationField.PasswordConfirm]: Field;
|
private [RegistrationField.PasswordConfirm]: Field | null;
|
||||||
private [RegistrationField.Username]: Field;
|
private [RegistrationField.Username]: Field | null;
|
||||||
private [RegistrationField.PhoneNumber]: Field;
|
private [RegistrationField.PhoneNumber]: Field | null;
|
||||||
|
|
||||||
public static defaultProps = {
|
public static defaultProps = {
|
||||||
onValidationChange: logger.error,
|
onValidationChange: logger.error,
|
||||||
|
@ -117,7 +117,6 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
phoneNumber: this.props.defaultPhoneNumber || "",
|
phoneNumber: this.props.defaultPhoneNumber || "",
|
||||||
password: this.props.defaultPassword || "",
|
password: this.props.defaultPassword || "",
|
||||||
passwordConfirm: this.props.defaultPassword || "",
|
passwordConfirm: this.props.defaultPassword || "",
|
||||||
passwordComplexity: null,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +137,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
if (this.showEmail()) {
|
if (this.showEmail()) {
|
||||||
Modal.createDialog(RegistrationEmailPromptDialog, {
|
Modal.createDialog(RegistrationEmailPromptDialog, {
|
||||||
onFinished: async (confirmed: boolean, email?: string): Promise<void> => {
|
onFinished: async (confirmed: boolean, email?: string): Promise<void> => {
|
||||||
if (confirmed) {
|
if (confirmed && email !== undefined) {
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
email,
|
email,
|
||||||
|
@ -265,7 +264,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
};
|
};
|
||||||
|
|
||||||
private onEmailValidate = (result: IValidationResult): void => {
|
private onEmailValidate = (result: IValidationResult): void => {
|
||||||
this.markFieldValid(RegistrationField.Email, result.valid);
|
this.markFieldValid(RegistrationField.Email, !!result.valid);
|
||||||
};
|
};
|
||||||
|
|
||||||
private validateEmailRules = withValidation({
|
private validateEmailRules = withValidation({
|
||||||
|
@ -294,7 +293,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPasswordValidate = (result: IValidationResult): void => {
|
private onPasswordValidate = (result: IValidationResult): void => {
|
||||||
this.markFieldValid(RegistrationField.Password, result.valid);
|
this.markFieldValid(RegistrationField.Password, !!result.valid);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPasswordConfirmChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
private onPasswordConfirmChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
@ -304,7 +303,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPasswordConfirmValidate = (result: IValidationResult): void => {
|
private onPasswordConfirmValidate = (result: IValidationResult): void => {
|
||||||
this.markFieldValid(RegistrationField.PasswordConfirm, result.valid);
|
this.markFieldValid(RegistrationField.PasswordConfirm, !!result.valid);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPhoneCountryChange = (newVal: PhoneNumberCountryDefinition): void => {
|
private onPhoneCountryChange = (newVal: PhoneNumberCountryDefinition): void => {
|
||||||
|
@ -321,7 +320,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
|
|
||||||
private onPhoneNumberValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
private onPhoneNumberValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
||||||
const result = await this.validatePhoneNumberRules(fieldState);
|
const result = await this.validatePhoneNumberRules(fieldState);
|
||||||
this.markFieldValid(RegistrationField.PhoneNumber, result.valid);
|
this.markFieldValid(RegistrationField.PhoneNumber, !!result.valid);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -352,14 +351,14 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
|
|
||||||
private onUsernameValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
private onUsernameValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
||||||
const result = await this.validateUsernameRules(fieldState);
|
const result = await this.validateUsernameRules(fieldState);
|
||||||
this.markFieldValid(RegistrationField.Username, result.valid);
|
this.markFieldValid(RegistrationField.Username, !!result.valid);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
private validateUsernameRules = withValidation<this, UsernameAvailableStatus>({
|
private validateUsernameRules = withValidation<this, UsernameAvailableStatus>({
|
||||||
description: (_, results) => {
|
description: (_, results) => {
|
||||||
// omit the description if the only failing result is the `available` one as it makes no sense for it.
|
// omit the description if the only failing result is the `available` one as it makes no sense for it.
|
||||||
if (results.every(({ key, valid }) => key === "available" || valid)) return;
|
if (results.every(({ key, valid }) => key === "available" || valid)) return null;
|
||||||
return _t("Use lowercase letters, numbers, dashes and underscores only");
|
return _t("Use lowercase letters, numbers, dashes and underscores only");
|
||||||
},
|
},
|
||||||
hideDescriptionIfValid: true,
|
hideDescriptionIfValid: true,
|
||||||
|
@ -448,7 +447,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderEmail(): JSX.Element {
|
private renderEmail(): ReactNode {
|
||||||
if (!this.showEmail()) {
|
if (!this.showEmail()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -492,7 +491,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderPhoneNumber(): JSX.Element {
|
public renderPhoneNumber(): ReactNode {
|
||||||
if (!this.showPhoneNumber()) {
|
if (!this.showPhoneNumber()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -518,7 +517,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderUsername(): JSX.Element {
|
public renderUsername(): ReactNode {
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
id="mx_RegistrationForm_username"
|
id="mx_RegistrationForm_username"
|
||||||
|
@ -534,12 +533,12 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): ReactNode {
|
||||||
const registerButton = (
|
const registerButton = (
|
||||||
<input className="mx_Login_submit" type="submit" value={_t("Register")} disabled={!this.props.canSubmit} />
|
<input className="mx_Login_submit" type="submit" value={_t("Register")} disabled={!this.props.canSubmit} />
|
||||||
);
|
);
|
||||||
|
|
||||||
let emailHelperText = null;
|
let emailHelperText: JSX.Element | undefined;
|
||||||
if (this.showEmail()) {
|
if (this.showEmail()) {
|
||||||
if (this.showPhoneNumber()) {
|
if (this.showPhoneNumber()) {
|
||||||
emailHelperText = (
|
emailHelperText = (
|
||||||
|
|
|
@ -34,7 +34,7 @@ interface IProps {}
|
||||||
export default class Welcome extends React.PureComponent<IProps> {
|
export default class Welcome extends React.PureComponent<IProps> {
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const pagesConfig = SdkConfig.getObject("embedded_pages");
|
const pagesConfig = SdkConfig.getObject("embedded_pages");
|
||||||
let pageUrl!: string;
|
let pageUrl: string | undefined;
|
||||||
if (pagesConfig) {
|
if (pagesConfig) {
|
||||||
pageUrl = pagesConfig.get("welcome_url");
|
pageUrl = pagesConfig.get("welcome_url");
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,15 +44,15 @@ interface IProps {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
shouldErase: boolean;
|
shouldErase: boolean;
|
||||||
errStr: string;
|
errStr: string | null;
|
||||||
authData: any; // for UIA
|
authData: any; // for UIA
|
||||||
authEnabled: boolean; // see usages for information
|
authEnabled: boolean; // see usages for information
|
||||||
|
|
||||||
// A few strings that are passed to InteractiveAuth for design or are displayed
|
// A few strings that are passed to InteractiveAuth for design or are displayed
|
||||||
// next to the InteractiveAuth component.
|
// next to the InteractiveAuth component.
|
||||||
bodyText: string;
|
bodyText?: string;
|
||||||
continueText: string;
|
continueText?: string;
|
||||||
continueKind: string;
|
continueKind?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DeactivateAccountDialog extends React.Component<IProps, IState> {
|
export default class DeactivateAccountDialog extends React.Component<IProps, IState> {
|
||||||
|
@ -64,12 +64,6 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
||||||
errStr: null,
|
errStr: null,
|
||||||
authData: null, // for UIA
|
authData: null, // for UIA
|
||||||
authEnabled: true, // see usages for information
|
authEnabled: true, // see usages for information
|
||||||
|
|
||||||
// A few strings that are passed to InteractiveAuth for design or are displayed
|
|
||||||
// next to the InteractiveAuth component.
|
|
||||||
bodyText: null,
|
|
||||||
continueText: null,
|
|
||||||
continueKind: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.initAuth(/* shouldErase= */ false);
|
this.initAuth(/* shouldErase= */ false);
|
||||||
|
@ -101,14 +95,16 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
||||||
};
|
};
|
||||||
|
|
||||||
const aesthetics = DEACTIVATE_AESTHETICS[stage];
|
const aesthetics = DEACTIVATE_AESTHETICS[stage];
|
||||||
let bodyText = null;
|
let bodyText: string | undefined;
|
||||||
let continueText = null;
|
let continueText: string | undefined;
|
||||||
let continueKind = null;
|
let continueKind: string | undefined;
|
||||||
if (aesthetics) {
|
if (aesthetics) {
|
||||||
const phaseAesthetics = aesthetics[phase];
|
const phaseAesthetics = aesthetics[phase];
|
||||||
if (phaseAesthetics?.body) bodyText = phaseAesthetics.body;
|
if (phaseAesthetics) {
|
||||||
if (phaseAesthetics?.continueText) continueText = phaseAesthetics.continueText;
|
if (phaseAesthetics.body) bodyText = phaseAesthetics.body;
|
||||||
if (phaseAesthetics?.continueKind) continueKind = phaseAesthetics.continueKind;
|
if (phaseAesthetics.continueText) continueText = phaseAesthetics.continueText;
|
||||||
|
if (phaseAesthetics.continueKind) continueKind = phaseAesthetics.continueKind;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.setState({ bodyText, continueText, continueKind });
|
this.setState({ bodyText, continueText, continueKind });
|
||||||
};
|
};
|
||||||
|
@ -183,7 +179,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let error = null;
|
let error: JSX.Element | undefined;
|
||||||
if (this.state.errStr) {
|
if (this.state.errStr) {
|
||||||
error = <div className="error">{this.state.errStr}</div>;
|
error = <div className="error">{this.state.errStr}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ export interface InteractiveAuthDialogProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
authError: Error;
|
authError: Error | null;
|
||||||
|
|
||||||
// See _onUpdateStagePhase()
|
// See _onUpdateStagePhase()
|
||||||
uiaStage: AuthType | null;
|
uiaStage: AuthType | null;
|
||||||
|
@ -147,16 +147,22 @@ export default class InteractiveAuthDialog extends React.Component<InteractiveAu
|
||||||
|
|
||||||
let title = this.state.authError ? "Error" : this.props.title || _t("Authentication");
|
let title = this.state.authError ? "Error" : this.props.title || _t("Authentication");
|
||||||
let body = this.state.authError ? null : this.props.body;
|
let body = this.state.authError ? null : this.props.body;
|
||||||
let continueText = null;
|
let continueText: string | undefined;
|
||||||
let continueKind = null;
|
let continueKind: string | undefined;
|
||||||
const dialogAesthetics = this.props.aestheticsForStagePhases || this.getDefaultDialogAesthetics();
|
const dialogAesthetics = this.props.aestheticsForStagePhases || this.getDefaultDialogAesthetics();
|
||||||
if (!this.state.authError && dialogAesthetics) {
|
if (!this.state.authError && dialogAesthetics) {
|
||||||
if (dialogAesthetics[this.state.uiaStage]) {
|
if (
|
||||||
const aesthetics = dialogAesthetics[this.state.uiaStage][this.state.uiaStagePhase];
|
this.state.uiaStage !== null &&
|
||||||
if (aesthetics?.title) title = aesthetics.title;
|
this.state.uiaStagePhase !== null &&
|
||||||
if (aesthetics?.body) body = aesthetics.body;
|
dialogAesthetics[this.state.uiaStage]
|
||||||
if (aesthetics?.continueText) continueText = aesthetics.continueText;
|
) {
|
||||||
if (aesthetics?.continueKind) continueKind = aesthetics.continueKind;
|
const aesthetics = dialogAesthetics[this.state.uiaStage]![this.state.uiaStagePhase];
|
||||||
|
if (aesthetics) {
|
||||||
|
if (aesthetics.title) title = aesthetics.title;
|
||||||
|
if (aesthetics.body) body = aesthetics.body;
|
||||||
|
if (aesthetics.continueText) continueText = aesthetics.continueText;
|
||||||
|
if (aesthetics.continueKind) continueKind = aesthetics.continueKind;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ import DialogButtons from "../elements/DialogButtons";
|
||||||
import EmailField from "../auth/EmailField";
|
import EmailField from "../auth/EmailField";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onFinished(continued: boolean, email?: string): void;
|
onFinished(continued: false, email?: undefined): void;
|
||||||
|
onFinished(continued: true, email: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
|
const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
type Data = Pick<IFieldState, "value" | "allowEmpty">;
|
type Data = Pick<IFieldState, "value" | "allowEmpty">;
|
||||||
|
@ -31,13 +31,13 @@ interface IRule<T, D = undefined> {
|
||||||
final?: boolean;
|
final?: boolean;
|
||||||
skip?(this: T, data: Data, derivedData: D): boolean;
|
skip?(this: T, data: Data, derivedData: D): boolean;
|
||||||
test(this: T, data: Data, derivedData: D): boolean | Promise<boolean>;
|
test(this: T, data: Data, derivedData: D): boolean | Promise<boolean>;
|
||||||
valid?(this: T, derivedData: D): string;
|
valid?(this: T, derivedData: D): string | null;
|
||||||
invalid?(this: T, derivedData: D): string;
|
invalid?(this: T, derivedData: D): string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IArgs<T, D = void> {
|
interface IArgs<T, D = void> {
|
||||||
rules: IRule<T, D>[];
|
rules: IRule<T, D>[];
|
||||||
description?(this: T, derivedData: D, results: IResult[]): React.ReactChild;
|
description?(this: T, derivedData: D, results: IResult[]): ReactNode;
|
||||||
hideDescriptionIfValid?: boolean;
|
hideDescriptionIfValid?: boolean;
|
||||||
deriveData?(data: Data): Promise<D>;
|
deriveData?(data: Data): Promise<D>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,12 @@ import { _t, _td, Tags, TranslatedString } from "../languageHandler";
|
||||||
* @returns {*} Translated string or react component
|
* @returns {*} Translated string or react component
|
||||||
*/
|
*/
|
||||||
export function messageForResourceLimitError(
|
export function messageForResourceLimitError(
|
||||||
limitType: string,
|
limitType: string | undefined,
|
||||||
adminContact: string | undefined,
|
adminContact: string | undefined,
|
||||||
strings: Record<string, string>,
|
strings: Record<string, string>,
|
||||||
extraTranslations?: Tags,
|
extraTranslations?: Tags,
|
||||||
): TranslatedString {
|
): TranslatedString {
|
||||||
let errString = strings[limitType];
|
let errString = limitType ? strings[limitType] : undefined;
|
||||||
if (errString === undefined) errString = strings[""];
|
if (errString === undefined) errString = strings[""];
|
||||||
|
|
||||||
const linkSub = (sub: string): ReactNode => {
|
const linkSub = (sub: string): ReactNode => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue