Merge branch 'develop' into t3chguy/wat/230.1
This commit is contained in:
commit
3620c5ac62
32 changed files with 898 additions and 244 deletions
|
@ -63,6 +63,19 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
|
|||
|
||||
public componentWillUnmount(): void {
|
||||
this.resetRecaptcha();
|
||||
// Resettting the captcha does not clear the challenge overlay from the body in android webviews.
|
||||
// Search for an iframe with the challenge src and remove it's topmost ancestor from the body.
|
||||
// TODO: Remove this when the "mobile_register" page is retired.
|
||||
const iframes = document.querySelectorAll("iframe");
|
||||
for (const iframe of iframes) {
|
||||
if (iframe.src.includes("https://www.recaptcha.net/recaptcha/api2/bframe")) {
|
||||
let parentBeforeBody: HTMLElement | null = iframe;
|
||||
do {
|
||||
parentBeforeBody = parentBeforeBody.parentElement;
|
||||
} while (parentBeforeBody?.parentElement && parentBeforeBody.parentElement != document.body);
|
||||
parentBeforeBody?.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed directly from: https://github.com/codeep/react-recaptcha-google/commit/e118fa5670fa268426969323b2e7fe77698376ba
|
||||
|
|
|
@ -12,6 +12,7 @@ import Field, { IInputProps } from "../elements/Field";
|
|||
import { _t, _td, TranslationKey } from "../../../languageHandler";
|
||||
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
|
||||
import * as Email from "../../../email";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
||||
id?: string;
|
||||
|
@ -22,6 +23,7 @@ interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
|||
label: TranslationKey;
|
||||
labelRequired: TranslationKey;
|
||||
labelInvalid: TranslationKey;
|
||||
tooltipAlignment?: Alignment;
|
||||
|
||||
// When present, completely overrides the default validation rules.
|
||||
validationRules?: (fieldState: IFieldState) => Promise<IValidationResult>;
|
||||
|
@ -77,6 +79,7 @@ class EmailField extends PureComponent<IProps> {
|
|||
autoFocus={this.props.autoFocus}
|
||||
onChange={this.props.onChange}
|
||||
onValidate={this.onValidate}
|
||||
tooltipAlignment={this.props.tooltipAlignment}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import React, { PureComponent, RefCallback, RefObject } from "react";
|
|||
import Field, { IInputProps } from "../elements/Field";
|
||||
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
|
||||
import { _t, _td, TranslationKey } from "../../../languageHandler";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
|
||||
id?: string;
|
||||
|
@ -22,7 +23,7 @@ interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
|
|||
label: TranslationKey;
|
||||
labelRequired: TranslationKey;
|
||||
labelInvalid: TranslationKey;
|
||||
|
||||
tooltipAlignment?: Alignment;
|
||||
onChange(ev: React.FormEvent<HTMLElement>): void;
|
||||
onValidate?(result: IValidationResult): void;
|
||||
}
|
||||
|
@ -70,6 +71,7 @@ class PassphraseConfirmField extends PureComponent<IProps> {
|
|||
onChange={this.props.onChange}
|
||||
onValidate={this.onValidate}
|
||||
autoFocus={this.props.autoFocus}
|
||||
tooltipAlignment={this.props.tooltipAlignment}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import withValidation, { IFieldState, IValidationResult } from "../elements/Vali
|
|||
import { _t, _td, TranslationKey } from "../../../languageHandler";
|
||||
import Field, { IInputProps } from "../elements/Field";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
||||
autoFocus?: boolean;
|
||||
|
@ -30,6 +31,7 @@ interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
|||
labelEnterPassword: TranslationKey;
|
||||
labelStrongPassword: TranslationKey;
|
||||
labelAllowedButUnsafe: TranslationKey;
|
||||
tooltipAlignment?: Alignment;
|
||||
|
||||
onChange(ev: React.FormEvent<HTMLElement>): void;
|
||||
onValidate?(result: IValidationResult): void;
|
||||
|
@ -111,6 +113,7 @@ class PassphraseField extends PureComponent<IProps> {
|
|||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
onValidate={this.onValidate}
|
||||
tooltipAlignment={this.props.tooltipAlignment}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import RegistrationEmailPromptDialog from "../dialogs/RegistrationEmailPromptDia
|
|||
import CountryDropdown from "./CountryDropdown";
|
||||
import PassphraseConfirmField from "./PassphraseConfirmField";
|
||||
import { PosthogAnalytics } from "../../../PosthogAnalytics";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
enum RegistrationField {
|
||||
Email = "field_email",
|
||||
|
@ -58,6 +59,7 @@ interface IProps {
|
|||
serverConfig: ValidatedServerConfig;
|
||||
canSubmit?: boolean;
|
||||
matrixClient: MatrixClient;
|
||||
mobileRegister?: boolean;
|
||||
|
||||
onRegisterClick(params: {
|
||||
username: string;
|
||||
|
@ -439,6 +441,13 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
return true;
|
||||
}
|
||||
|
||||
private tooltipAlignment(): Alignment | undefined {
|
||||
if (this.props.mobileRegister) {
|
||||
return Alignment.Bottom;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private renderEmail(): ReactNode {
|
||||
if (!this.showEmail()) {
|
||||
return null;
|
||||
|
@ -454,6 +463,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
validationRules={this.validateEmailRules.bind(this)}
|
||||
onChange={this.onEmailChange}
|
||||
onValidate={this.onEmailValidate}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -468,6 +478,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
onChange={this.onPasswordChange}
|
||||
onValidate={this.onPasswordValidate}
|
||||
userInputs={[this.state.username]}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -482,6 +493,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
password={this.state.password}
|
||||
onChange={this.onPasswordConfirmChange}
|
||||
onValidate={this.onPasswordConfirmValidate}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -526,6 +538,9 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
value={this.state.username}
|
||||
onChange={this.onUsernameChange}
|
||||
onValidate={this.onUsernameValidate}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
autoCorrect="off"
|
||||
autoCapitalize="none"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -557,14 +572,28 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
}
|
||||
}
|
||||
|
||||
let passwordFields: JSX.Element | undefined;
|
||||
if (this.props.mobileRegister) {
|
||||
passwordFields = (
|
||||
<>
|
||||
<div className="mx_AuthBody_fieldRow">{this.renderPassword()}</div>
|
||||
<div className="mx_AuthBody_fieldRow">{this.renderPasswordConfirm()}</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
passwordFields = (
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
{this.renderPassword()}
|
||||
{this.renderPasswordConfirm()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onSubmit}>
|
||||
<div className="mx_AuthBody_fieldRow">{this.renderUsername()}</div>
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
{this.renderPassword()}
|
||||
{this.renderPasswordConfirm()}
|
||||
</div>
|
||||
{passwordFields}
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
{this.renderEmail()}
|
||||
{this.renderPhoneNumber()}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { debounce } from "lodash";
|
|||
import classNames from "classnames";
|
||||
import React, { ChangeEvent, FormEvent } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { decodeRecoveryKey } from "matrix-js-sdk/src/crypto-api";
|
||||
import { SecretStorage } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
|
@ -100,7 +101,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
try {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const decodedKey = cli.keyBackupKeyFromRecoveryKey(this.state.recoveryKey);
|
||||
const decodedKey = decodeRecoveryKey(this.state.recoveryKey);
|
||||
const correct = await cli.checkSecretStorageKey(decodedKey, this.props.keyInfo);
|
||||
this.setState({
|
||||
recoveryKeyValid: true,
|
||||
|
|
|
@ -9,9 +9,9 @@ Please see LICENSE files in the repository root for full details.
|
|||
|
||||
import React, { ChangeEvent } from "react";
|
||||
import { MatrixClient, MatrixError, SecretStorage } from "matrix-js-sdk/src/matrix";
|
||||
import { decodeRecoveryKey, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
import { IKeyBackupRestoreResult } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
|
@ -118,10 +118,24 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
|
|||
accessSecretStorage(async (): Promise<void> => {}, /* forceReset = */ true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the recovery key is valid
|
||||
* @param recoveryKey
|
||||
* @private
|
||||
*/
|
||||
private isValidRecoveryKey(recoveryKey: string): boolean {
|
||||
try {
|
||||
decodeRecoveryKey(recoveryKey);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private onRecoveryKeyChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({
|
||||
recoveryKey: e.target.value,
|
||||
recoveryKeyValid: MatrixClientPeg.safeGet().isValidRecoveryKey(e.target.value),
|
||||
recoveryKeyValid: this.isValidRecoveryKey(e.target.value),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -184,7 +198,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
|
|||
{ progressCallback: this.progressCallback },
|
||||
);
|
||||
if (this.props.keyCallback) {
|
||||
const key = MatrixClientPeg.safeGet().keyBackupKeyFromRecoveryKey(this.state.recoveryKey);
|
||||
const key = decodeRecoveryKey(this.state.recoveryKey);
|
||||
this.props.keyCallback(key);
|
||||
}
|
||||
if (!this.props.showSummary) {
|
||||
|
|
|
@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
import React, { FunctionComponent, useEffect, useRef } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import ICanvasEffect from "../../../effects/ICanvasEffect";
|
||||
|
@ -44,9 +45,10 @@ const EffectsOverlay: FunctionComponent<IProps> = ({ roomWidth }) => {
|
|||
canvasRef.current.height = UIStore.instance.windowHeight;
|
||||
}
|
||||
};
|
||||
const onAction = (payload: { action: string }): void => {
|
||||
const onAction = (payload: { action: string; event?: MatrixEvent }): void => {
|
||||
const actionPrefix = "effects.";
|
||||
if (canvasRef.current && payload.action.startsWith(actionPrefix)) {
|
||||
const isOutdated = isEventOutdated(payload.event);
|
||||
if (canvasRef.current && payload.action.startsWith(actionPrefix) && !isOutdated) {
|
||||
const effect = payload.action.slice(actionPrefix.length);
|
||||
lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current!));
|
||||
}
|
||||
|
@ -88,3 +90,19 @@ const EffectsOverlay: FunctionComponent<IProps> = ({ roomWidth }) => {
|
|||
};
|
||||
|
||||
export default EffectsOverlay;
|
||||
|
||||
// 48 hours
|
||||
// 48h * 60m * 60s * 1000ms
|
||||
const OUTDATED_EVENT_THRESHOLD = 48 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Return true if the event is older than 48h.
|
||||
* @param event
|
||||
*/
|
||||
function isEventOutdated(event?: MatrixEvent): boolean {
|
||||
if (!event) return false;
|
||||
|
||||
const nowTs = Date.now();
|
||||
const eventTs = event.getTs();
|
||||
return nowTs - eventTs > OUTDATED_EVENT_THRESHOLD;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import classNames from "classnames";
|
|||
import { debounce } from "lodash";
|
||||
|
||||
import { IFieldState, IValidationResult } from "./Validation";
|
||||
import Tooltip from "./Tooltip";
|
||||
import Tooltip, { Alignment } from "./Tooltip";
|
||||
import { Key } from "../../../Keyboard";
|
||||
|
||||
// Invoke validation from user input (when typing, etc.) at most once every N ms.
|
||||
|
@ -60,6 +60,8 @@ interface IProps {
|
|||
tooltipContent?: React.ReactNode;
|
||||
// If specified the tooltip will be shown regardless of feedback
|
||||
forceTooltipVisible?: boolean;
|
||||
// If specified, the tooltip with be aligned accorindly with the field, defaults to Right.
|
||||
tooltipAlignment?: Alignment;
|
||||
// If specified alongside tooltipContent, the class name to apply to the
|
||||
// tooltip itself.
|
||||
tooltipClassName?: string;
|
||||
|
@ -261,6 +263,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
|||
validateOnFocus,
|
||||
usePlaceholderAsHint,
|
||||
forceTooltipVisible,
|
||||
tooltipAlignment,
|
||||
...inputProps
|
||||
} = this.props;
|
||||
|
||||
|
@ -286,7 +289,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
|||
tooltipClassName={classNames("mx_Field_tooltip", "mx_Tooltip_noMargin", tooltipClassName)}
|
||||
visible={visible}
|
||||
label={tooltipContent || this.state.feedback}
|
||||
alignment={Tooltip.Alignment.Right}
|
||||
alignment={tooltipAlignment || Alignment.Right}
|
||||
role={role}
|
||||
/>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue