Apply prettier formatting
This commit is contained in:
parent
1cac306093
commit
526645c791
1576 changed files with 65385 additions and 62478 deletions
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue