Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
1576 changed files with 65385 additions and 62478 deletions

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import classNames from "classnames";
import React, { PropsWithChildren } from 'react';
import React, { PropsWithChildren } from "react";
interface Props {
className?: string;
@ -23,7 +23,5 @@ interface Props {
}
export default function AuthBody({ flex, className, children }: PropsWithChildren<Props>) {
return <main className={classNames("mx_AuthBody", className, { "mx_AuthBody_flex": flex })}>
{ children }
</main>;
return <main className={classNames("mx_AuthBody", className, { mx_AuthBody_flex: flex })}>{children}</main>;
}

View file

@ -16,15 +16,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import { _t } from '../../../languageHandler';
import { _t } from "../../../languageHandler";
export default class AuthFooter extends React.Component {
public render(): React.ReactNode {
return (
<footer className="mx_AuthFooter" role="contentinfo">
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t("powered by Matrix") }</a>
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
{_t("powered by Matrix")}
</a>
</footer>
);
}

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import AuthHeaderLogo from "./AuthHeaderLogo";
import LanguageSelector from "./LanguageSelector";

View file

@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
export default class AuthHeaderLogo extends React.PureComponent {
public render(): React.ReactNode {
return <aside className="mx_AuthHeaderLogo">
Matrix
</aside>;
return <aside className="mx_AuthHeaderLogo">Matrix</aside>;
}
}

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import AuthFooter from "./AuthFooter";
@ -24,9 +24,7 @@ export default class AuthPage extends React.PureComponent {
public render(): React.ReactNode {
return (
<div className="mx_AuthPage">
<div className="mx_AuthPage_modal">
{ this.props.children }
</div>
<div className="mx_AuthPage_modal">{this.props.children}</div>
<AuthFooter />
</div>
);

View file

@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef } from 'react';
import React, { createRef } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from '../../../languageHandler';
import { _t } from "../../../languageHandler";
const DIV_ID = 'mx_recaptcha';
const DIV_ID = "mx_recaptcha";
interface ICaptchaFormProps {
sitePublicKey: string;
@ -28,7 +28,6 @@ interface ICaptchaFormProps {
interface ICaptchaFormState {
errorText?: string;
}
/**
@ -58,10 +57,13 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
this.onCaptchaLoaded();
} else {
logger.log("Loading recaptcha script...");
window.mxOnRecaptchaLoaded = () => { this.onCaptchaLoaded(); };
const scriptTag = document.createElement('script');
window.mxOnRecaptchaLoaded = () => {
this.onCaptchaLoaded();
};
const scriptTag = document.createElement("script");
scriptTag.setAttribute(
'src', `https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit`,
"src",
`https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit`,
);
this.recaptchaContainer.current.appendChild(scriptTag);
}
@ -73,9 +75,11 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
// Borrowed directly from: https://github.com/codeep/react-recaptcha-google/commit/e118fa5670fa268426969323b2e7fe77698376ba
private isRecaptchaReady(): boolean {
return typeof window !== "undefined" &&
return (
typeof window !== "undefined" &&
typeof global.grecaptcha !== "undefined" &&
typeof global.grecaptcha.render === 'function';
typeof global.grecaptcha.render === "function"
);
}
private renderRecaptcha(divId: string) {
@ -87,9 +91,7 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
const publicKey = this.props.sitePublicKey;
if (!publicKey) {
logger.error("No public key for recaptcha!");
throw new Error(
"This server has not supplied enough information for Recaptcha "
+ "authentication");
throw new Error("This server has not supplied enough information for Recaptcha " + "authentication");
}
logger.info("Rendering to %s", divId);
@ -123,20 +125,14 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
render() {
let error = null;
if (this.state.errorText) {
error = (
<div className="error">
{ this.state.errorText }
</div>
);
error = <div className="error">{this.state.errorText}</div>;
}
return (
<div ref={this.recaptchaContainer}>
<p>{ _t(
"This homeserver would like to make sure you are not a robot.",
) }</p>
<p>{_t("This homeserver would like to make sure you are not a robot.")}</p>
<div id={DIV_ID} />
{ error }
{error}
</div>
);
}

View file

@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
export default class CompleteSecurityBody extends React.PureComponent {
public render(): React.ReactNode {
return <div className="mx_CompleteSecurityBody">
{ this.props.children }
</div>;
return <div className="mx_CompleteSecurityBody">{this.props.children}</div>;
}
}

View file

@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import { COUNTRIES, getEmojiFlag, PhoneNumberCountryDefinition } from '../../../phonenumber';
import { COUNTRIES, getEmojiFlag, PhoneNumberCountryDefinition } from "../../../phonenumber";
import SdkConfig from "../../../SdkConfig";
import { _t } from "../../../languageHandler";
import Dropdown from "../elements/Dropdown";
@ -28,7 +28,7 @@ for (const c of COUNTRIES) {
function countryMatchesSearchQuery(query: string, country: PhoneNumberCountryDefinition): boolean {
// Remove '+' if present (when searching for a prefix)
if (query[0] === '+') {
if (query[0] === "+") {
query = query.slice(1);
}
@ -59,12 +59,12 @@ export default class CountryDropdown extends React.Component<IProps, IState> {
let defaultCountry: PhoneNumberCountryDefinition = COUNTRIES[0];
const defaultCountryCode = SdkConfig.get("default_country_code");
if (defaultCountryCode) {
const country = COUNTRIES.find(c => c.iso2 === defaultCountryCode.toUpperCase());
const country = COUNTRIES.find((c) => c.iso2 === defaultCountryCode.toUpperCase());
if (country) defaultCountry = country;
}
this.state = {
searchQuery: '',
searchQuery: "",
defaultCountry,
};
}
@ -89,7 +89,7 @@ export default class CountryDropdown extends React.Component<IProps, IState> {
};
private flagImgForIso2(iso2: string): React.ReactNode {
return <div className="mx_Dropdown_option_emoji">{ getEmojiFlag(iso2) }</div>;
return <div className="mx_Dropdown_option_emoji">{getEmojiFlag(iso2)}</div>;
}
private getShortOption = (iso2: string): React.ReactNode => {
@ -98,24 +98,21 @@ export default class CountryDropdown extends React.Component<IProps, IState> {
}
let countryPrefix;
if (this.props.showPrefix) {
countryPrefix = '+' + COUNTRIES_BY_ISO2[iso2].prefix;
countryPrefix = "+" + COUNTRIES_BY_ISO2[iso2].prefix;
}
return <span className="mx_CountryDropdown_shortOption">
{ this.flagImgForIso2(iso2) }
{ countryPrefix }
</span>;
return (
<span className="mx_CountryDropdown_shortOption">
{this.flagImgForIso2(iso2)}
{countryPrefix}
</span>
);
};
public render(): React.ReactNode {
let displayedCountries;
if (this.state.searchQuery) {
displayedCountries = COUNTRIES.filter(
countryMatchesSearchQuery.bind(this, this.state.searchQuery),
);
if (
this.state.searchQuery.length == 2 &&
COUNTRIES_BY_ISO2[this.state.searchQuery.toUpperCase()]
) {
displayedCountries = COUNTRIES.filter(countryMatchesSearchQuery.bind(this, this.state.searchQuery));
if (this.state.searchQuery.length == 2 && COUNTRIES_BY_ISO2[this.state.searchQuery.toUpperCase()]) {
// exact ISO2 country name match: make the first result the matches ISO2
const matched = COUNTRIES_BY_ISO2[this.state.searchQuery.toUpperCase()];
displayedCountries = displayedCountries.filter((c) => {
@ -128,29 +125,33 @@ export default class CountryDropdown extends React.Component<IProps, IState> {
}
const options = displayedCountries.map((country) => {
return <div className="mx_CountryDropdown_option" key={country.iso2}>
{ this.flagImgForIso2(country.iso2) }
{ _t(country.name) } (+{ country.prefix })
</div>;
return (
<div className="mx_CountryDropdown_option" key={country.iso2}>
{this.flagImgForIso2(country.iso2)}
{_t(country.name)} (+{country.prefix})
</div>
);
});
// default value here too, otherwise we need to handle null / undefined
// values between mounting and the initial value propagating
const value = this.props.value || this.state.defaultCountry.iso2;
return <Dropdown
id="mx_CountryDropdown"
className={this.props.className + " mx_CountryDropdown"}
onOptionChange={this.onOptionChange}
onSearchChange={this.onSearchChange}
menuWidth={298}
getShortOption={this.getShortOption}
value={value}
searchEnabled={true}
disabled={this.props.disabled}
label={_t("Country Dropdown")}
>
{ options }
</Dropdown>;
return (
<Dropdown
id="mx_CountryDropdown"
className={this.props.className + " mx_CountryDropdown"}
onOptionChange={this.onOptionChange}
onSearchChange={this.onSearchChange}
menuWidth={298}
getShortOption={this.getShortOption}
value={value}
searchEnabled={true}
disabled={this.props.disabled}
label={_t("Country Dropdown")}
>
{options}
</Dropdown>
);
}
}

View file

@ -75,16 +75,18 @@ class EmailField extends PureComponent<IProps> {
};
render() {
return <Field
id={this.props.id}
ref={this.props.fieldRef}
type="text"
label={_t(this.props.label)}
value={this.props.value}
autoFocus={this.props.autoFocus}
onChange={this.props.onChange}
onValidate={this.onValidate}
/>;
return (
<Field
id={this.props.id}
ref={this.props.fieldRef}
type="text"
label={_t(this.props.label)}
value={this.props.value}
autoFocus={this.props.autoFocus}
onChange={this.props.onChange}
onValidate={this.onValidate}
/>
);
}
}

View file

@ -14,20 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import classNames from 'classnames';
import classNames from "classnames";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { AuthType, IAuthDict, IInputs, IStageStatus } from 'matrix-js-sdk/src/interactive-auth';
import { AuthType, IAuthDict, IInputs, IStageStatus } from "matrix-js-sdk/src/interactive-auth";
import { logger } from "matrix-js-sdk/src/logger";
import React, { ChangeEvent, createRef, FormEvent, Fragment, MouseEvent } from 'react';
import React, { ChangeEvent, createRef, FormEvent, Fragment, MouseEvent } from "react";
import EmailPromptIcon from '../../../../res/img/element-icons/email-prompt.svg';
import { _t } from '../../../languageHandler';
import EmailPromptIcon from "../../../../res/img/element-icons/email-prompt.svg";
import { _t } from "../../../languageHandler";
import SettingsStore from "../../../settings/SettingsStore";
import { LocalisedPolicy, Policies } from '../../../Terms';
import { LocalisedPolicy, Policies } from "../../../Terms";
import { AuthHeaderModifier } from "../../structures/auth/header/AuthHeaderModifier";
import AccessibleButton from "../elements/AccessibleButton";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import Field from '../elements/Field';
import Field from "../elements/Field";
import Spinner from "../elements/Spinner";
import { Alignment } from "../elements/Tooltip";
import CaptchaForm from "./CaptchaForm";
@ -138,7 +138,7 @@ export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswor
render() {
const passwordBoxClass = classNames({
"error": this.props.errorText,
error: this.props.errorText,
});
let submitButtonOrSpinner;
@ -146,7 +146,8 @@ export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswor
submitButtonOrSpinner = <Spinner />;
} else {
submitButtonOrSpinner = (
<input type="submit"
<input
type="submit"
className="mx_Dialog_primary"
disabled={!this.state.password}
value={_t("Continue")}
@ -158,28 +159,26 @@ export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswor
if (this.props.errorText) {
errorSection = (
<div className="error" role="alert">
{ this.props.errorText }
{this.props.errorText}
</div>
);
}
return (
<div>
<p>{ _t("Confirm your identity by entering your account password below.") }</p>
<p>{_t("Confirm your identity by entering your account password below.")}</p>
<form onSubmit={this.onSubmit} className="mx_InteractiveAuthEntryComponents_passwordSection">
<Field
className={passwordBoxClass}
type="password"
name="passwordField"
label={_t('Password')}
label={_t("Password")}
autoFocus={true}
value={this.state.password}
onChange={this.onPasswordFieldChange}
/>
{ errorSection }
<div className="mx_button_row">
{ submitButtonOrSpinner }
</div>
{errorSection}
<div className="mx_button_row">{submitButtonOrSpinner}</div>
</form>
</div>
);
@ -210,9 +209,7 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps
render() {
if (this.props.busy) {
return (
<Spinner />
);
return <Spinner />;
}
let errorText = this.props.errorText;
@ -221,7 +218,7 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps
if (!this.props.stageParams || !this.props.stageParams.public_key) {
errorText = _t(
"Missing captcha public key in homeserver configuration. Please report " +
"this to your homeserver administrator.",
"this to your homeserver administrator.",
);
} else {
sitePublicKey = this.props.stageParams.public_key;
@ -231,17 +228,15 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps
if (errorText) {
errorSection = (
<div className="error" role="alert">
{ errorText }
{errorText}
</div>
);
}
return (
<div>
<CaptchaForm sitePublicKey={sitePublicKey}
onCaptchaResponse={this.onCaptchaResponse}
/>
{ errorSection }
<CaptchaForm sitePublicKey={sitePublicKey} onCaptchaResponse={this.onCaptchaResponse} />
{errorSection}
</div>
);
}
@ -305,7 +300,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
if (!langPolicy) langPolicy = policy["en"];
if (!langPolicy) {
// last resort
const firstLang = Object.keys(policy).find(e => e !== "version");
const firstLang = Object.keys(policy).find((e) => e !== "version");
langPolicy = policy[firstLang];
}
if (!langPolicy) throw new Error("Failed to find a policy to show the user");
@ -337,7 +332,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
newToggles[policy.id] = checked;
}
this.setState({ "toggledPolicies": newToggles });
this.setState({ toggledPolicies: newToggles });
}
private trySubmit = () => {
@ -356,9 +351,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
render() {
if (this.props.busy) {
return (
<Spinner />
);
return <Spinner />;
}
const checkboxes = [];
@ -371,7 +364,9 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
// XXX: replace with StyledCheckbox
<label key={"policy_checkbox_" + policy.id} className="mx_InteractiveAuthEntryComponents_termsPolicy">
<input type="checkbox" onChange={() => this.togglePolicy(policy.id)} checked={checked} />
<a href={policy.url} target="_blank" rel="noreferrer noopener">{ policy.name }</a>
<a href={policy.url} target="_blank" rel="noreferrer noopener">
{policy.name}
</a>
</label>,
);
}
@ -380,7 +375,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
if (this.props.errorText || this.state.errorText) {
errorSection = (
<div className="error" role="alert">
{ this.props.errorText || this.state.errorText }
{this.props.errorText || this.state.errorText}
</div>
);
}
@ -388,18 +383,23 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
let submitButton;
if (this.props.showContinue !== false) {
// XXX: button classes
submitButton = <button
className="mx_InteractiveAuthEntryComponents_termsSubmit mx_GeneralButton"
onClick={this.trySubmit}
disabled={!allChecked}>{ _t("Accept") }</button>;
submitButton = (
<button
className="mx_InteractiveAuthEntryComponents_termsSubmit mx_GeneralButton"
onClick={this.trySubmit}
disabled={!allChecked}
>
{_t("Accept")}
</button>
);
}
return (
<div>
<p>{ _t("Please review and accept the policies of this homeserver:") }</p>
{ checkboxes }
{ errorSection }
{ submitButton }
<p>{_t("Please review and accept the policies of this homeserver:")}</p>
{checkboxes}
{errorSection}
{submitButton}
</div>
);
}
@ -419,8 +419,10 @@ interface IEmailIdentityAuthEntryState {
requesting: boolean;
}
export class EmailIdentityAuthEntry extends
React.Component<IEmailIdentityAuthEntryProps, IEmailIdentityAuthEntryState> {
export class EmailIdentityAuthEntry extends React.Component<
IEmailIdentityAuthEntryProps,
IEmailIdentityAuthEntryState
> {
static LOGIN_TYPE = AuthType.Email;
constructor(props: IEmailIdentityAuthEntryProps) {
@ -442,7 +444,7 @@ export class EmailIdentityAuthEntry extends
if (this.props.errorText && this.props.errorCode !== "M_UNAUTHORIZED") {
errorSection = (
<div className="error" role="alert">
{ this.props.errorText }
{this.props.errorText}
</div>
);
}
@ -466,49 +468,65 @@ export class EmailIdentityAuthEntry extends
<div className="mx_InteractiveAuthEntryComponents_emailWrapper">
<AuthHeaderModifier
title={_t("Check your email to continue")}
icon={<img
src={EmailPromptIcon}
alt={_t("Unread email icon")}
width={16}
/>}
icon={<img src={EmailPromptIcon} alt={_t("Unread email icon")} width={16} />}
hideServerPicker={true}
/>
<p>{ _t("To create your account, open the link in the email we just sent to %(emailAddress)s.",
{ emailAddress: <b>{ this.props.inputs.emailAddress }</b> },
) }</p>
{ this.state.requesting ? (
<p className="secondary">{ _t("Did not receive it? <a>Resend it</a>", {}, {
a: (text: string) => <Fragment>
<AccessibleButton
kind='link_inline'
onClick={() => null}
disabled
>{ text } <Spinner w={14} h={14} /></AccessibleButton>
</Fragment>,
}) }</p>
) : <p className="secondary">{ _t("Did not receive it? <a>Resend it</a>", {}, {
a: (text: string) => <AccessibleTooltipButton
kind='link_inline'
title={this.state.requested
? _t("Resent!")
: _t("Resend")}
alignment={Alignment.Right}
onHideTooltip={this.state.requested
? () => this.setState({ requested: false })
: undefined}
onClick={async () => {
this.setState({ requesting: true });
try {
await this.props.requestEmailToken?.();
} catch (e) {
logger.warn("Email token request failed: ", e);
} finally {
this.setState({ requested: true, requesting: false });
}
}}
>{ text }</AccessibleTooltipButton>,
}) }</p> }
{ errorSection }
<p>
{_t("To create your account, open the link in the email we just sent to %(emailAddress)s.", {
emailAddress: <b>{this.props.inputs.emailAddress}</b>,
})}
</p>
{this.state.requesting ? (
<p className="secondary">
{_t(
"Did not receive it? <a>Resend it</a>",
{},
{
a: (text: string) => (
<Fragment>
<AccessibleButton kind="link_inline" onClick={() => null} disabled>
{text} <Spinner w={14} h={14} />
</AccessibleButton>
</Fragment>
),
},
)}
</p>
) : (
<p className="secondary">
{_t(
"Did not receive it? <a>Resend it</a>",
{},
{
a: (text: string) => (
<AccessibleTooltipButton
kind="link_inline"
title={this.state.requested ? _t("Resent!") : _t("Resend")}
alignment={Alignment.Right}
onHideTooltip={
this.state.requested
? () => this.setState({ requested: false })
: undefined
}
onClick={async () => {
this.setState({ requesting: true });
try {
await this.props.requestEmailToken?.();
} catch (e) {
logger.warn("Email token request failed: ", e);
} finally {
this.setState({ requested: true, requesting: false });
}
}}
>
{text}
</AccessibleTooltipButton>
),
},
)}
</p>
)}
{errorSection}
</div>
);
}
@ -541,9 +559,9 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
super(props);
this.state = {
token: '',
token: "",
requestingToken: false,
errorText: '',
errorText: "",
};
}
@ -551,27 +569,31 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
this.props.onPhaseChange(DEFAULT_PHASE);
this.setState({ requestingToken: true });
this.requestMsisdnToken().catch((e) => {
this.props.fail(e);
}).finally(() => {
this.setState({ requestingToken: false });
});
this.requestMsisdnToken()
.catch((e) => {
this.props.fail(e);
})
.finally(() => {
this.setState({ requestingToken: false });
});
}
/*
* Requests a verification token by SMS.
*/
private requestMsisdnToken(): Promise<void> {
return this.props.matrixClient.requestRegisterMsisdnToken(
this.props.inputs.phoneCountry,
this.props.inputs.phoneNumber,
this.props.clientSecret,
1, // TODO: Multiple send attempts?
).then((result) => {
this.submitUrl = result.submit_url;
this.sid = result.sid;
this.msisdn = result.msisdn;
});
return this.props.matrixClient
.requestRegisterMsisdnToken(
this.props.inputs.phoneCountry,
this.props.inputs.phoneNumber,
this.props.clientSecret,
1, // TODO: Multiple send attempts?
)
.then((result) => {
this.submitUrl = result.submit_url;
this.sid = result.sid;
this.msisdn = result.msisdn;
});
}
private onTokenChange = (e: ChangeEvent<HTMLInputElement>) => {
@ -582,7 +604,7 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
private onFormSubmit = async (e: FormEvent) => {
e.preventDefault();
if (this.state.token == '') return;
if (this.state.token == "") return;
this.setState({
errorText: null,
@ -592,7 +614,10 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
let result;
if (this.submitUrl) {
result = await this.props.matrixClient.submitMsisdnTokenOtherUrl(
this.submitUrl, this.sid, this.props.clientSecret, this.state.token,
this.submitUrl,
this.sid,
this.props.clientSecret,
this.state.token,
);
} else {
throw new Error("The registration with MSISDN flow is misconfigured");
@ -623,9 +648,7 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
render() {
if (this.state.requestingToken) {
return (
<Spinner />
);
return <Spinner />;
} else {
const enableSubmit = Boolean(this.state.token);
const submitClasses = classNames({
@ -636,20 +659,18 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
if (this.state.errorText) {
errorSection = (
<div className="error" role="alert">
{ this.state.errorText }
{this.state.errorText}
</div>
);
}
return (
<div>
<p>{ _t("A text message has been sent to %(msisdn)s",
{ msisdn: <i>{ this.msisdn }</i> },
) }
</p>
<p>{ _t("Please enter the code it contains:") }</p>
<p>{_t("A text message has been sent to %(msisdn)s", { msisdn: <i>{this.msisdn}</i> })}</p>
<p>{_t("Please enter the code it contains:")}</p>
<div className="mx_InteractiveAuthEntryComponents_msisdnWrapper">
<form onSubmit={this.onFormSubmit}>
<input type="text"
<input
type="text"
className="mx_InteractiveAuthEntryComponents_msisdnEntry"
value={this.state.token}
onChange={this.onTokenChange}
@ -663,7 +684,7 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
disabled={!enableSubmit}
/>
</form>
{ errorSection }
{errorSection}
</div>
</div>
);
@ -697,10 +718,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
// We actually send the user through fallback auth so we don't have to
// 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);
this.popupWindow = null;
window.addEventListener("message", this.onReceiveMessage);
@ -757,22 +775,22 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
const cancelButton = (
<AccessibleButton
onClick={this.props.onCancel}
kind={this.props.continueKind ? (this.props.continueKind + '_outline') : 'primary_outline'}
>{ _t("Cancel") }</AccessibleButton>
kind={this.props.continueKind ? this.props.continueKind + "_outline" : "primary_outline"}
>
{_t("Cancel")}
</AccessibleButton>
);
if (this.state.phase === SSOAuthEntry.PHASE_PREAUTH) {
continueButton = (
<AccessibleButton
onClick={this.onStartAuthClick}
kind={this.props.continueKind || 'primary'}
>{ this.props.continueText || _t("Single Sign On") }</AccessibleButton>
<AccessibleButton onClick={this.onStartAuthClick} kind={this.props.continueKind || "primary"}>
{this.props.continueText || _t("Single Sign On")}
</AccessibleButton>
);
} else {
continueButton = (
<AccessibleButton
onClick={this.onConfirmClick}
kind={this.props.continueKind || 'primary'}
>{ this.props.continueText || _t("Confirm") }</AccessibleButton>
<AccessibleButton onClick={this.onConfirmClick} kind={this.props.continueKind || "primary"}>
{this.props.continueText || _t("Confirm")}
</AccessibleButton>
);
}
@ -780,23 +798,23 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
if (this.props.errorText) {
errorSection = (
<div className="error" role="alert">
{ this.props.errorText }
{this.props.errorText}
</div>
);
} else if (this.state.attemptFailed) {
errorSection = (
<div className="error" role="alert">
{ _t("Something went wrong in confirming your identity. Cancel and try again.") }
{_t("Something went wrong in confirming your identity. Cancel and try again.")}
</div>
);
}
return (
<Fragment>
{ errorSection }
{errorSection}
<div className="mx_InteractiveAuthEntryComponents_sso_buttons">
{ cancelButton }
{ continueButton }
{cancelButton}
{continueButton}
</div>
</Fragment>
);
@ -837,18 +855,12 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
e.preventDefault();
e.stopPropagation();
const url = this.props.matrixClient.getFallbackAuthUrl(
this.props.loginType,
this.props.authSessionId,
);
const url = this.props.matrixClient.getFallbackAuthUrl(this.props.loginType, this.props.authSessionId);
this.popupWindow = window.open(url, "_blank");
};
private onReceiveMessage = (event: MessageEvent) => {
if (
event.data === "authDone" &&
event.origin === this.props.matrixClient.getHomeserverUrl()
) {
if (event.data === "authDone" && event.origin === this.props.matrixClient.getHomeserverUrl()) {
this.props.submitAuthDict({});
}
};
@ -858,16 +870,16 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
if (this.props.errorText) {
errorSection = (
<div className="error" role="alert">
{ this.props.errorText }
{this.props.errorText}
</div>
);
}
return (
<div>
<AccessibleButton kind='link' inputRef={this.fallbackButton} onClick={this.onShowFallbackClick}>{
_t("Start authentication")
}</AccessibleButton>
{ errorSection }
<AccessibleButton kind="link" inputRef={this.fallbackButton} onClick={this.onShowFallbackClick}>
{_t("Start authentication")}
</AccessibleButton>
{errorSection}
</div>
);
}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import SdkConfig from "../../../SdkConfig";
import { getCurrentLanguage } from "../../../languageHandler";
@ -36,10 +36,12 @@ interface IProps {
export default function LanguageSelector({ disabled }: IProps): JSX.Element {
if (SdkConfig.get("disable_login_language_selector")) return <div />;
return <LanguageDropdown
className="mx_AuthBody_language"
onOptionChange={onChange}
value={getCurrentLanguage()}
disabled={disabled}
/>;
return (
<LanguageDropdown
className="mx_AuthBody_language"
onOptionChange={onChange}
value={getCurrentLanguage()}
disabled={disabled}
/>
);
}

View file

@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { MSC3906Rendezvous, MSC3906RendezvousPayload, RendezvousFailureReason } from 'matrix-js-sdk/src/rendezvous';
import { MSC3886SimpleHttpRendezvousTransport } from 'matrix-js-sdk/src/rendezvous/transports';
import { MSC3903ECDHPayload, MSC3903ECDHv1RendezvousChannel } from 'matrix-js-sdk/src/rendezvous/channels';
import { logger } from 'matrix-js-sdk/src/logger';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import React from "react";
import { MSC3906Rendezvous, MSC3906RendezvousPayload, RendezvousFailureReason } from "matrix-js-sdk/src/rendezvous";
import { MSC3886SimpleHttpRendezvousTransport } from "matrix-js-sdk/src/rendezvous/transports";
import { MSC3903ECDHPayload, MSC3903ECDHv1RendezvousChannel } from "matrix-js-sdk/src/rendezvous/channels";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { _t } from "../../../languageHandler";
import { wrapRequestWithDialog } from '../../../utils/UserInteractiveAuth';
import LoginWithQRFlow from './LoginWithQRFlow';
import { wrapRequestWithDialog } from "../../../utils/UserInteractiveAuth";
import LoginWithQRFlow from "./LoginWithQRFlow";
/**
* The intention of this enum is to have a mode that scans a QR code instead of generating one.
@ -117,7 +117,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
private approveLogin = async (): Promise<void> => {
if (!this.state.rendezvous) {
throw new Error('Rendezvous not found');
throw new Error("Rendezvous not found");
}
this.setState({ phase: Phase.Loading });
@ -145,7 +145,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
await this.state.rendezvous.verifyNewDeviceOnExistingDevice();
this.props.onFinished(true);
} catch (e) {
logger.error('Error whilst approving sign in', e);
logger.error("Error whilst approving sign in", e);
this.setState({ phase: Phase.Error, failureReason: RendezvousFailureReason.Unknown });
}
};
@ -159,7 +159,9 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
});
const channel = new MSC3903ECDHv1RendezvousChannel<MSC3906RendezvousPayload>(
transport, undefined, this.onFailure,
transport,
undefined,
this.onFailure,
);
rendezvous = new MSC3906Rendezvous(channel, this.props.client, this.onFailure);
@ -171,7 +173,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
failureReason: undefined,
});
} catch (e) {
logger.error('Error whilst generating QR code', e);
logger.error("Error whilst generating QR code", e);
this.setState({ phase: Phase.Error, failureReason: RendezvousFailureReason.HomeserverLacksSupport });
return;
}
@ -180,7 +182,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
const confirmationDigits = await rendezvous.startAfterShowingCode();
this.setState({ phase: Phase.Connected, confirmationDigits });
} catch (e) {
logger.error('Error whilst doing QR login', e);
logger.error("Error whilst doing QR login", e);
// only set to error phase if it hasn't already been set by onFailure or similar
if (this.state.phase !== Phase.Error) {
this.setState({ phase: Phase.Error, failureReason: RendezvousFailureReason.Unknown });

View file

@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { RendezvousFailureReason } from 'matrix-js-sdk/src/rendezvous';
import React from "react";
import { RendezvousFailureReason } from "matrix-js-sdk/src/rendezvous";
import { _t } from "../../../languageHandler";
import AccessibleButton from '../elements/AccessibleButton';
import QRCode from '../elements/QRCode';
import Spinner from '../elements/Spinner';
import AccessibleButton from "../elements/AccessibleButton";
import QRCode from "../elements/QRCode";
import Spinner from "../elements/Spinner";
import { Icon as BackButtonIcon } from "../../../../res/img/element-icons/back.svg";
import { Icon as DevicesIcon } from "../../../../res/img/element-icons/devices.svg";
import { Icon as WarningBadge } from "../../../../res/img/element-icons/warning-badge.svg";
import { Icon as InfoIcon } from "../../../../res/img/element-icons/i.svg";
import { Click, Phase } from './LoginWithQR';
import { Click, Phase } from "./LoginWithQR";
interface IProps {
phase: Phase;
@ -52,25 +52,25 @@ export default class LoginWithQRFlow extends React.Component<IProps> {
};
};
private cancelButton = () => <AccessibleButton
data-testid="cancel-button"
kind="primary_outline"
onClick={this.handleClick(Click.Cancel)}
>
{ _t("Cancel") }
</AccessibleButton>;
private cancelButton = () => (
<AccessibleButton data-testid="cancel-button" kind="primary_outline" onClick={this.handleClick(Click.Cancel)}>
{_t("Cancel")}
</AccessibleButton>
);
private simpleSpinner = (description?: string): JSX.Element => {
return <div className="mx_LoginWithQR_spinner">
<div>
<Spinner />
{ description && <p>{ description }</p> }
return (
<div className="mx_LoginWithQR_spinner">
<div>
<Spinner />
{description && <p>{description}</p>}
</div>
</div>
</div>;
);
};
public render() {
let title = '';
let title = "";
let titleIcon: JSX.Element | undefined;
let main: JSX.Element | undefined;
let buttons: JSX.Element | undefined;
@ -116,67 +116,80 @@ export default class LoginWithQRFlow extends React.Component<IProps> {
centreTitle = true;
titleIcon = <WarningBadge className="error" />;
backButton = false;
main = <p data-testid="cancellation-message">{ cancellationMessage }</p>;
buttons = <>
<AccessibleButton
data-testid="try-again-button"
kind="primary"
onClick={this.handleClick(Click.TryAgain)}
>
{ _t("Try again") }
</AccessibleButton>
{ this.cancelButton() }
</>;
main = <p data-testid="cancellation-message">{cancellationMessage}</p>;
buttons = (
<>
<AccessibleButton
data-testid="try-again-button"
kind="primary"
onClick={this.handleClick(Click.TryAgain)}
>
{_t("Try again")}
</AccessibleButton>
{this.cancelButton()}
</>
);
break;
case Phase.Connected:
title = _t("Devices connected");
titleIcon = <DevicesIcon className="normal" />;
backButton = false;
main = <>
<p>{ _t("Check that the code below matches with your other device:") }</p>
<div className="mx_LoginWithQR_confirmationDigits">
{ this.props.confirmationDigits }
</div>
<div className="mx_LoginWithQR_confirmationAlert">
<div>
<InfoIcon />
main = (
<>
<p>{_t("Check that the code below matches with your other device:")}</p>
<div className="mx_LoginWithQR_confirmationDigits">{this.props.confirmationDigits}</div>
<div className="mx_LoginWithQR_confirmationAlert">
<div>
<InfoIcon />
</div>
<div>
{_t("By approving access for this device, it will have full access to your account.")}
</div>
</div>
<div>{ _t("By approving access for this device, it will have full access to your account.") }</div>
</div>
</>;
</>
);
buttons = <>
<AccessibleButton
data-testid="decline-login-button"
kind="primary_outline"
onClick={this.handleClick(Click.Decline)}
>
{ _t("Cancel") }
</AccessibleButton>
<AccessibleButton
data-testid="approve-login-button"
kind="primary"
onClick={this.handleClick(Click.Approve)}
>
{ _t("Approve") }
</AccessibleButton>
</>;
buttons = (
<>
<AccessibleButton
data-testid="decline-login-button"
kind="primary_outline"
onClick={this.handleClick(Click.Decline)}
>
{_t("Cancel")}
</AccessibleButton>
<AccessibleButton
data-testid="approve-login-button"
kind="primary"
onClick={this.handleClick(Click.Approve)}
>
{_t("Approve")}
</AccessibleButton>
</>
);
break;
case Phase.ShowingQR:
title =_t("Sign in with QR code");
title = _t("Sign in with QR code");
if (this.props.code) {
const code = <div className="mx_LoginWithQR_qrWrapper">
<QRCode data={[{ data: Buffer.from(this.props.code ?? ''), mode: 'byte' }]} className="mx_QRCode" />
</div>;
main = <>
<p>{ _t("Scan the QR code below with your device that's signed out.") }</p>
<ol>
<li>{ _t("Start at the sign in screen") }</li>
<li>{ _t("Select 'Scan QR code'") }</li>
<li>{ _t("Review and approve the sign in") }</li>
</ol>
{ code }
</>;
const code = (
<div className="mx_LoginWithQR_qrWrapper">
<QRCode
data={[{ data: Buffer.from(this.props.code ?? ""), mode: "byte" }]}
className="mx_QRCode"
/>
</div>
);
main = (
<>
<p>{_t("Scan the QR code below with your device that's signed out.")}</p>
<ol>
<li>{_t("Start at the sign in screen")}</li>
<li>{_t("Select 'Scan QR code'")}</li>
<li>{_t("Review and approve the sign in")}</li>
</ol>
{code}
</>
);
} else {
main = this.simpleSpinner();
buttons = this.cancelButton();
@ -203,7 +216,7 @@ export default class LoginWithQRFlow extends React.Component<IProps> {
return (
<div data-testid="login-with-qr" className="mx_LoginWithQR">
<div className={centreTitle ? "mx_LoginWithQR_centreTitle" : ""}>
{ backButton ?
{backButton ? (
<AccessibleButton
data-testid="back-button"
className="mx_LoginWithQR_BackButton"
@ -212,15 +225,14 @@ export default class LoginWithQRFlow extends React.Component<IProps> {
>
<BackButtonIcon />
</AccessibleButton>
: null }
<h1>{ titleIcon }{ title }</h1>
</div>
<div className="mx_LoginWithQR_main">
{ main }
</div>
<div className="mx_LoginWithQR_buttons">
{ buttons }
) : null}
<h1>
{titleIcon}
{title}
</h1>
</div>
<div className="mx_LoginWithQR_main">{main}</div>
<div className="mx_LoginWithQR_buttons">{buttons}</div>
</div>
);
}

View file

@ -66,16 +66,18 @@ class PassphraseConfirmField extends PureComponent<IProps> {
};
render() {
return <Field
id={this.props.id}
ref={this.props.fieldRef}
type="password"
label={_t(this.props.label)}
autoComplete={this.props.autoComplete}
value={this.props.value}
onChange={this.props.onChange}
onValidate={this.onValidate}
/>;
return (
<Field
id={this.props.id}
ref={this.props.fieldRef}
type="password"
label={_t(this.props.label)}
autoComplete={this.props.autoComplete}
value={this.props.value}
onChange={this.props.onChange}
onValidate={this.onValidate}
/>
);
}
}

View file

@ -49,13 +49,13 @@ class PassphraseField extends PureComponent<IProps> {
};
public readonly validate = withValidation<this, zxcvbn.ZXCVBNResult>({
description: function(complexity) {
description: function (complexity) {
const score = complexity ? complexity.score : 0;
return <progress className="mx_PassphraseField_progress" max={4} value={score} />;
},
deriveData: async ({ value }) => {
if (!value) return null;
const { scorePassword } = await import('../../../utils/PasswordScorer');
const { scorePassword } = await import("../../../utils/PasswordScorer");
return scorePassword(value);
},
rules: [
@ -66,7 +66,7 @@ class PassphraseField extends PureComponent<IProps> {
},
{
key: "complexity",
test: async function({ value }, complexity) {
test: async function ({ value }, complexity) {
if (!value) {
return false;
}
@ -74,7 +74,7 @@ class PassphraseField extends PureComponent<IProps> {
const allowUnsafe = SdkConfig.get("dangerously_allow_unsafe_and_insecure_passwords");
return allowUnsafe || safe;
},
valid: function(complexity) {
valid: function (complexity) {
// Unsafe passwords that are valid are only possible through a
// configuration flag. We'll print some helper text to signal
// to the user that their password is allowed, but unsafe.
@ -83,7 +83,7 @@ class PassphraseField extends PureComponent<IProps> {
}
return _t(this.props.labelAllowedButUnsafe);
},
invalid: function(complexity) {
invalid: function (complexity) {
if (!complexity) {
return null;
}
@ -103,18 +103,20 @@ class PassphraseField extends PureComponent<IProps> {
};
render() {
return <Field
id={this.props.id}
autoFocus={this.props.autoFocus}
className={classNames("mx_PassphraseField", this.props.className)}
ref={this.props.fieldRef}
type="password"
autoComplete="new-password"
label={_t(this.props.label)}
value={this.props.value}
onChange={this.props.onChange}
onValidate={this.onValidate}
/>;
return (
<Field
id={this.props.id}
autoFocus={this.props.autoFocus}
className={classNames("mx_PassphraseField", this.props.className)}
ref={this.props.fieldRef}
type="password"
autoComplete="new-password"
label={_t(this.props.label)}
value={this.props.value}
onChange={this.props.onChange}
onValidate={this.onValidate}
/>
);
}
}

View file

@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import classNames from 'classnames';
import React from "react";
import classNames from "classnames";
import { _t } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig';
import { ValidatedServerConfig } from '../../../utils/ValidatedServerConfig';
import { _t } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
import AccessibleButton from "../elements/AccessibleButton";
import withValidation, { IValidationResult } from "../elements/Validation";
import Field from "../elements/Field";
@ -67,10 +67,10 @@ enum LoginField {
*/
export default class PasswordLogin extends React.PureComponent<IProps, IState> {
static defaultProps = {
onUsernameChanged: function() {},
onUsernameBlur: function() {},
onPhoneCountryChanged: function() {},
onPhoneNumberChanged: function() {},
onUsernameChanged: function () {},
onUsernameBlur: function () {},
onPhoneCountryChanged: function () {},
onPhoneNumberChanged: function () {},
loginIncorrect: false,
disableSubmit: false,
};
@ -85,13 +85,13 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
};
}
private onForgotPasswordClick = ev => {
private onForgotPasswordClick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
this.props.onForgotPasswordClick();
};
private onSubmitForm = async ev => {
private onSubmitForm = async (ev) => {
ev.preventDefault();
const allFieldsValid = await this.verifyFieldsBeforeSubmit();
@ -99,7 +99,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
return;
}
let username = ''; // XXX: Synapse breaks if you send null here:
let username = ""; // XXX: Synapse breaks if you send null here:
let phoneCountry = null;
let phoneNumber = null;
@ -117,29 +117,29 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
this.props.onSubmit(username, phoneCountry, phoneNumber, this.state.password);
};
private onUsernameChanged = ev => {
private onUsernameChanged = (ev) => {
this.props.onUsernameChanged(ev.target.value);
};
private onUsernameBlur = ev => {
private onUsernameBlur = (ev) => {
this.props.onUsernameBlur(ev.target.value);
};
private onLoginTypeChange = ev => {
private onLoginTypeChange = (ev) => {
const loginType = ev.target.value;
this.setState({ loginType });
this.props.onUsernameChanged(""); // Reset because email and username use the same state
};
private onPhoneCountryChanged = country => {
private onPhoneCountryChanged = (country) => {
this.props.onPhoneCountryChanged(country.iso2);
};
private onPhoneNumberChanged = ev => {
private onPhoneNumberChanged = (ev) => {
this.props.onPhoneNumberChanged(ev.target.value);
};
private onPasswordChanged = ev => {
private onPasswordChanged = (ev) => {
this.setState({ password: ev.target.value });
};
@ -151,10 +151,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
activeElement.blur();
}
const fieldIDsInDisplayOrder = [
this.state.loginType,
LoginField.Password,
];
const fieldIDsInDisplayOrder = [this.state.loginType, LoginField.Password];
// Run all fields with stricter validation that no longer allows empty
// values for required fields.
@ -172,7 +169,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
// Validation and state updates are async, so we need to wait for them to complete
// first. Queue a `setState` callback and wait for it to resolve.
await new Promise<void>(resolve => this.setState({}, resolve));
await new Promise<void>((resolve) => this.setState({}, resolve));
if (this.allFieldsValid()) {
return true;
@ -242,7 +239,8 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
return allowEmpty || !!value;
},
invalid: () => _t("Enter phone number"),
}, {
},
{
key: "number",
test: ({ value }) => !value || PHONE_NUMBER_REGEX.test(value),
invalid: () => _t("That phone number doesn't look quite right, please check and try again"),
@ -282,67 +280,75 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
switch (loginType) {
case LoginField.Email:
classes.error = this.props.loginIncorrect && !this.props.username;
return <EmailField
id="mx_LoginForm_email"
className={classNames(classes)}
name="username" // make it a little easier for browser's remember-password
autoComplete="email"
type="email"
key="email_input"
placeholder="joe@example.com"
value={this.props.username}
onChange={this.onUsernameChanged}
onBlur={this.onUsernameBlur}
disabled={this.props.busy}
autoFocus={autoFocus}
onValidate={this.onEmailValidate}
fieldRef={field => this[LoginField.Email] = field}
/>;
return (
<EmailField
id="mx_LoginForm_email"
className={classNames(classes)}
name="username" // make it a little easier for browser's remember-password
autoComplete="email"
type="email"
key="email_input"
placeholder="joe@example.com"
value={this.props.username}
onChange={this.onUsernameChanged}
onBlur={this.onUsernameBlur}
disabled={this.props.busy}
autoFocus={autoFocus}
onValidate={this.onEmailValidate}
fieldRef={(field) => (this[LoginField.Email] = field)}
/>
);
case LoginField.MatrixId:
classes.error = this.props.loginIncorrect && !this.props.username;
return <Field
id="mx_LoginForm_username"
className={classNames(classes)}
name="username" // make it a little easier for browser's remember-password
autoComplete="username"
key="username_input"
type="text"
label={_t("Username")}
placeholder={_t("Username").toLocaleLowerCase()}
value={this.props.username}
onChange={this.onUsernameChanged}
onBlur={this.onUsernameBlur}
disabled={this.props.busy}
autoFocus={autoFocus}
onValidate={this.onUsernameValidate}
ref={field => this[LoginField.MatrixId] = field}
/>;
return (
<Field
id="mx_LoginForm_username"
className={classNames(classes)}
name="username" // make it a little easier for browser's remember-password
autoComplete="username"
key="username_input"
type="text"
label={_t("Username")}
placeholder={_t("Username").toLocaleLowerCase()}
value={this.props.username}
onChange={this.onUsernameChanged}
onBlur={this.onUsernameBlur}
disabled={this.props.busy}
autoFocus={autoFocus}
onValidate={this.onUsernameValidate}
ref={(field) => (this[LoginField.MatrixId] = field)}
/>
);
case LoginField.Phone: {
classes.error = this.props.loginIncorrect && !this.props.phoneNumber;
const phoneCountry = <CountryDropdown
value={this.props.phoneCountry}
isSmall={true}
showPrefix={true}
onOptionChange={this.onPhoneCountryChanged}
/>;
const phoneCountry = (
<CountryDropdown
value={this.props.phoneCountry}
isSmall={true}
showPrefix={true}
onOptionChange={this.onPhoneCountryChanged}
/>
);
return <Field
id="mx_LoginForm_phone"
className={classNames(classes)}
name="phoneNumber"
autoComplete="tel-national"
key="phone_input"
type="text"
label={_t("Phone")}
value={this.props.phoneNumber}
prefixComponent={phoneCountry}
onChange={this.onPhoneNumberChanged}
disabled={this.props.busy}
autoFocus={autoFocus}
onValidate={this.onPhoneNumberValidate}
ref={field => this[LoginField.Password] = field}
/>;
return (
<Field
id="mx_LoginForm_phone"
className={classNames(classes)}
name="phoneNumber"
autoComplete="tel-national"
key="phone_input"
type="text"
label={_t("Phone")}
value={this.props.phoneNumber}
prefixComponent={phoneCountry}
onChange={this.onPhoneNumberChanged}
disabled={this.props.busy}
autoFocus={autoFocus}
onValidate={this.onPhoneNumberValidate}
ref={(field) => (this[LoginField.Password] = field)}
/>
);
}
}
}
@ -361,14 +367,16 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
let forgotPasswordJsx;
if (this.props.onForgotPasswordClick) {
forgotPasswordJsx = <AccessibleButton
className="mx_Login_forgot"
disabled={this.props.busy}
kind="link"
onClick={this.onForgotPasswordClick}
>
{ _t("Forgot password?") }
</AccessibleButton>;
forgotPasswordJsx = (
<AccessibleButton
className="mx_Login_forgot"
disabled={this.props.busy}
kind="link"
onClick={this.onForgotPasswordClick}
>
{_t("Forgot password?")}
</AccessibleButton>
);
}
const pwFieldClass = classNames({
@ -384,7 +392,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
if (!SdkConfig.get().disable_3pid_login) {
loginType = (
<div className="mx_Login_type_container">
<label className="mx_Login_type_label">{ _t('Sign in with') }</label>
<label className="mx_Login_type_label">{_t("Sign in with")}</label>
<Field
element="select"
value={this.state.loginType}
@ -392,16 +400,13 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
disabled={this.props.busy}
>
<option key={LoginField.MatrixId} value={LoginField.MatrixId}>
{ _t('Username') }
{_t("Username")}
</option>
<option
key={LoginField.Email}
value={LoginField.Email}
>
{ _t('Email address') }
<option key={LoginField.Email} value={LoginField.Email}>
{_t("Email address")}
</option>
<option key={LoginField.Password} value={LoginField.Password}>
{ _t('Phone') }
{_t("Phone")}
</option>
</Field>
</div>
@ -411,28 +416,31 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
return (
<div>
<form onSubmit={this.onSubmitForm}>
{ loginType }
{ loginField }
{loginType}
{loginField}
<Field
id="mx_LoginForm_password"
className={pwFieldClass}
autoComplete="current-password"
type="password"
name="password"
label={_t('Password')}
label={_t("Password")}
value={this.state.password}
onChange={this.onPasswordChanged}
disabled={this.props.busy}
autoFocus={autoFocusPassword}
onValidate={this.onPasswordValidate}
ref={field => this[LoginField.Password] = field}
ref={(field) => (this[LoginField.Password] = field)}
/>
{ forgotPasswordJsx }
{ !this.props.busy && <input className="mx_Login_submit"
type="submit"
value={_t('Sign in')}
disabled={this.props.disableSubmit}
/> }
{forgotPasswordJsx}
{!this.props.busy && (
<input
className="mx_Login_submit"
type="submit"
value={_t("Sign in")}
disabled={this.props.disableSubmit}
/>
)}
</form>
</div>
);

View file

@ -15,26 +15,26 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from 'matrix-js-sdk/src/matrix';
import { MatrixError } from "matrix-js-sdk/src/matrix";
import * as Email from '../../../email';
import { looksValid as phoneNumberLooksValid } from '../../../phonenumber';
import Modal from '../../../Modal';
import { _t, _td } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig';
import { SAFE_LOCALPART_REGEX } from '../../../Registration';
import withValidation, { IValidationResult } from '../elements/Validation';
import { ValidatedServerConfig } from '../../../utils/ValidatedServerConfig';
import * as Email from "../../../email";
import { looksValid as phoneNumberLooksValid } from "../../../phonenumber";
import Modal from "../../../Modal";
import { _t, _td } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
import { SAFE_LOCALPART_REGEX } from "../../../Registration";
import withValidation, { IValidationResult } from "../elements/Validation";
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
import EmailField from "./EmailField";
import PassphraseField from "./PassphraseField";
import Field from '../elements/Field';
import RegistrationEmailPromptDialog from '../dialogs/RegistrationEmailPromptDialog';
import Field from "../elements/Field";
import RegistrationEmailPromptDialog from "../dialogs/RegistrationEmailPromptDialog";
import CountryDropdown from "./CountryDropdown";
import PassphraseConfirmField from "./PassphraseConfirmField";
import { PosthogAnalytics } from '../../../PosthogAnalytics';
import { PosthogAnalytics } from "../../../PosthogAnalytics";
enum RegistrationField {
Email = "field_email",
@ -115,7 +115,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
};
}
private onSubmit = async ev => {
private onSubmit = async (ev) => {
ev.preventDefault();
ev.persist();
@ -126,16 +126,19 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
return;
}
if (this.state.email === '') {
if (this.state.email === "") {
if (this.showEmail()) {
Modal.createDialog(RegistrationEmailPromptDialog, {
onFinished: async (confirmed: boolean, email?: string) => {
if (confirmed) {
this.setState({
email,
}, () => {
this.doSubmit(ev);
});
this.setState(
{
email,
},
() => {
this.doSubmit(ev);
},
);
}
},
});
@ -164,7 +167,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
if (promise) {
ev.target.disabled = true;
promise.finally(function() {
promise.finally(function () {
ev.target.disabled = false;
});
}
@ -202,7 +205,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
// Validation and state updates are async, so we need to wait for them to complete
// first. Queue a `setState` callback and wait for it to resolve.
await new Promise<void>(resolve => this.setState({}, resolve));
await new Promise<void>((resolve) => this.setState({}, resolve));
if (this.allFieldsValid()) {
return true;
@ -245,7 +248,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
});
}
private onEmailChange = ev => {
private onEmailChange = (ev) => {
this.setState({
email: ev.target.value.trim(),
});
@ -262,7 +265,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
{
key: "required",
test(this: RegistrationForm, { value, allowEmpty }) {
return allowEmpty || !this.authStepIsRequired('m.login.email.identity') || !!value;
return allowEmpty || !this.authStepIsRequired("m.login.email.identity") || !!value;
},
invalid: () => _t("Enter email address (required on this homeserver)"),
},
@ -274,17 +277,17 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
],
});
private onPasswordChange = ev => {
private onPasswordChange = (ev) => {
this.setState({
password: ev.target.value,
});
};
private onPasswordValidate = result => {
private onPasswordValidate = (result) => {
this.markFieldValid(RegistrationField.Password, result.valid);
};
private onPasswordConfirmChange = ev => {
private onPasswordConfirmChange = (ev) => {
this.setState({
passwordConfirm: ev.target.value,
});
@ -294,19 +297,19 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
this.markFieldValid(RegistrationField.PasswordConfirm, result.valid);
};
private onPhoneCountryChange = newVal => {
private onPhoneCountryChange = (newVal) => {
this.setState({
phoneCountry: newVal.iso2,
});
};
private onPhoneNumberChange = ev => {
private onPhoneNumberChange = (ev) => {
this.setState({
phoneNumber: ev.target.value,
});
};
private onPhoneNumberValidate = async fieldState => {
private onPhoneNumberValidate = async (fieldState) => {
const result = await this.validatePhoneNumberRules(fieldState);
this.markFieldValid(RegistrationField.PhoneNumber, result.valid);
return result;
@ -319,7 +322,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
{
key: "required",
test(this: RegistrationForm, { value, allowEmpty }) {
return allowEmpty || !this.authStepIsRequired('m.login.msisdn') || !!value;
return allowEmpty || !this.authStepIsRequired("m.login.msisdn") || !!value;
},
invalid: () => _t("Enter phone number (required on this homeserver)"),
},
@ -331,13 +334,13 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
],
});
private onUsernameChange = ev => {
private onUsernameChange = (ev) => {
this.setState({
username: ev.target.value,
});
};
private onUsernameValidate = async fieldState => {
private onUsernameValidate = async (fieldState) => {
const result = await this.validateUsernameRules(fieldState);
this.markFieldValid(RegistrationField.Username, result.valid);
return result;
@ -373,8 +376,9 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
},
{
key: "safeLocalpart",
test: ({ value }, usernameAvailable) => (!value || SAFE_LOCALPART_REGEX.test(value))
&& usernameAvailable !== UsernameAvailableStatus.Invalid,
test: ({ value }, usernameAvailable) =>
(!value || SAFE_LOCALPART_REGEX.test(value)) &&
usernameAvailable !== UsernameAvailableStatus.Invalid,
invalid: () => _t("Some characters not allowed"),
},
{
@ -387,9 +391,10 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
return usernameAvailable === UsernameAvailableStatus.Available;
},
invalid: (usernameAvailable) => usernameAvailable === UsernameAvailableStatus.Error
? _t("Unable to check if username has been taken. Try again later.")
: _t("Someone already has that username. Try another or if it is you, sign in below."),
invalid: (usernameAvailable) =>
usernameAvailable === UsernameAvailableStatus.Error
? _t("Unable to check if username has been taken. Try again later.")
: _t("Someone already has that username. Try another or if it is you, sign in below."),
},
],
});
@ -419,7 +424,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
}
private showEmail() {
if (!this.authStepIsUsed('m.login.email.identity')) {
if (!this.authStepIsUsed("m.login.email.identity")) {
return false;
}
return true;
@ -427,7 +432,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
private showPhoneNumber() {
const threePidLogin = !SdkConfig.get().disable_3pid_login;
if (!threePidLogin || !this.authStepIsUsed('m.login.msisdn')) {
if (!threePidLogin || !this.authStepIsUsed("m.login.msisdn")) {
return false;
}
return true;
@ -437,78 +442,86 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
if (!this.showEmail()) {
return null;
}
const emailLabel = this.authStepIsRequired('m.login.email.identity') ?
_td("Email") :
_td("Email (optional)");
return <EmailField
fieldRef={field => this[RegistrationField.Email] = field}
label={emailLabel}
value={this.state.email}
validationRules={this.validateEmailRules.bind(this)}
onChange={this.onEmailChange}
onValidate={this.onEmailValidate}
/>;
const emailLabel = this.authStepIsRequired("m.login.email.identity") ? _td("Email") : _td("Email (optional)");
return (
<EmailField
fieldRef={(field) => (this[RegistrationField.Email] = field)}
label={emailLabel}
value={this.state.email}
validationRules={this.validateEmailRules.bind(this)}
onChange={this.onEmailChange}
onValidate={this.onEmailValidate}
/>
);
}
private renderPassword() {
return <PassphraseField
id="mx_RegistrationForm_password"
fieldRef={field => this[RegistrationField.Password] = field}
minScore={PASSWORD_MIN_SCORE}
value={this.state.password}
onChange={this.onPasswordChange}
onValidate={this.onPasswordValidate}
/>;
return (
<PassphraseField
id="mx_RegistrationForm_password"
fieldRef={(field) => (this[RegistrationField.Password] = field)}
minScore={PASSWORD_MIN_SCORE}
value={this.state.password}
onChange={this.onPasswordChange}
onValidate={this.onPasswordValidate}
/>
);
}
renderPasswordConfirm() {
return <PassphraseConfirmField
id="mx_RegistrationForm_passwordConfirm"
fieldRef={field => this[RegistrationField.PasswordConfirm] = field}
autoComplete="new-password"
value={this.state.passwordConfirm}
password={this.state.password}
onChange={this.onPasswordConfirmChange}
onValidate={this.onPasswordConfirmValidate}
/>;
return (
<PassphraseConfirmField
id="mx_RegistrationForm_passwordConfirm"
fieldRef={(field) => (this[RegistrationField.PasswordConfirm] = field)}
autoComplete="new-password"
value={this.state.passwordConfirm}
password={this.state.password}
onChange={this.onPasswordConfirmChange}
onValidate={this.onPasswordConfirmValidate}
/>
);
}
renderPhoneNumber() {
if (!this.showPhoneNumber()) {
return null;
}
const phoneLabel = this.authStepIsRequired('m.login.msisdn') ?
_t("Phone") :
_t("Phone (optional)");
const phoneCountry = <CountryDropdown
value={this.state.phoneCountry}
isSmall={true}
showPrefix={true}
onOptionChange={this.onPhoneCountryChange}
/>;
return <Field
ref={field => this[RegistrationField.PhoneNumber] = field}
type="text"
label={phoneLabel}
value={this.state.phoneNumber}
prefixComponent={phoneCountry}
onChange={this.onPhoneNumberChange}
onValidate={this.onPhoneNumberValidate}
/>;
const phoneLabel = this.authStepIsRequired("m.login.msisdn") ? _t("Phone") : _t("Phone (optional)");
const phoneCountry = (
<CountryDropdown
value={this.state.phoneCountry}
isSmall={true}
showPrefix={true}
onOptionChange={this.onPhoneCountryChange}
/>
);
return (
<Field
ref={(field) => (this[RegistrationField.PhoneNumber] = field)}
type="text"
label={phoneLabel}
value={this.state.phoneNumber}
prefixComponent={phoneCountry}
onChange={this.onPhoneNumberChange}
onValidate={this.onPhoneNumberValidate}
/>
);
}
renderUsername() {
return <Field
id="mx_RegistrationForm_username"
ref={field => this[RegistrationField.Username] = field}
type="text"
autoFocus={true}
label={_t("Username")}
placeholder={_t("Username").toLocaleLowerCase()}
value={this.state.username}
onChange={this.onUsernameChange}
onValidate={this.onUsernameValidate}
/>;
return (
<Field
id="mx_RegistrationForm_username"
ref={(field) => (this[RegistrationField.Username] = field)}
type="text"
autoFocus={true}
label={_t("Username")}
placeholder={_t("Username").toLocaleLowerCase()}
value={this.state.username}
onChange={this.onUsernameChange}
onValidate={this.onUsernameValidate}
/>
);
}
render() {
@ -519,40 +532,36 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
let emailHelperText = null;
if (this.showEmail()) {
if (this.showPhoneNumber()) {
emailHelperText = <div>
{
_t("Add an email to be able to reset your password.")
} {
_t("Use email or phone to optionally be discoverable by existing contacts.")
}
</div>;
emailHelperText = (
<div>
{_t("Add an email to be able to reset your password.")}{" "}
{_t("Use email or phone to optionally be discoverable by existing contacts.")}
</div>
);
} else {
emailHelperText = <div>
{
_t("Add an email to be able to reset your password.")
} {
_t("Use email to optionally be discoverable by existing contacts.")
}
</div>;
emailHelperText = (
<div>
{_t("Add an email to be able to reset your password.")}{" "}
{_t("Use email to optionally be discoverable by existing contacts.")}
</div>
);
}
}
return (
<div>
<form onSubmit={this.onSubmit}>
<div className="mx_AuthBody_fieldRow">{this.renderUsername()}</div>
<div className="mx_AuthBody_fieldRow">
{ this.renderUsername() }
{this.renderPassword()}
{this.renderPasswordConfirm()}
</div>
<div className="mx_AuthBody_fieldRow">
{ this.renderPassword() }
{ this.renderPasswordConfirm() }
{this.renderEmail()}
{this.renderPhoneNumber()}
</div>
<div className="mx_AuthBody_fieldRow">
{ this.renderEmail() }
{ this.renderPhoneNumber() }
</div>
{ emailHelperText }
{ registerButton }
{emailHelperText}
{registerButton}
</form>
</div>
);

View file

@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import classNames from "classnames";
import SdkConfig from '../../../SdkConfig';
import SdkConfig from "../../../SdkConfig";
import AuthPage from "./AuthPage";
import { _td } from "../../../languageHandler";
import SettingsStore from "../../../settings/SettingsStore";
@ -29,9 +29,7 @@ import { MATRIX_LOGO_HTML } from "../../structures/static-page-vars";
// translatable strings for Welcome pages
_td("Sign in with SSO");
interface IProps {
}
interface IProps {}
export default class Welcome extends React.PureComponent<IProps> {
public render(): React.ReactNode {
@ -41,14 +39,16 @@ export default class Welcome extends React.PureComponent<IProps> {
pageUrl = pagesConfig.get("welcome_url");
}
if (!pageUrl) {
pageUrl = 'welcome.html';
pageUrl = "welcome.html";
}
return (
<AuthPage>
<div className={classNames("mx_Welcome", {
mx_WelcomePage_registrationDisabled: !SettingsStore.getValue(UIFeature.Registration),
})}>
<div
className={classNames("mx_Welcome", {
mx_WelcomePage_registrationDisabled: !SettingsStore.getValue(UIFeature.Registration),
})}
>
<EmbeddedPage
className="mx_WelcomePage"
url={pageUrl}