Merge pull request #6315 from matrix-org/gsouquet/ts-1

This commit is contained in:
Germain 2021-07-06 12:04:18 +01:00 committed by GitHub
commit 7d6342fa40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 268 additions and 262 deletions

View file

@ -46,6 +46,7 @@ import { VoiceRecordingStore } from "../stores/VoiceRecordingStore";
import PerformanceMonitor from "../performance"; import PerformanceMonitor from "../performance";
import UIStore from "../stores/UIStore"; import UIStore from "../stores/UIStore";
import { SetupEncryptionStore } from "../stores/SetupEncryptionStore"; import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
import { RoomScrollStateStore } from "../stores/RoomScrollStateStore";
declare global { declare global {
interface Window { interface Window {
@ -87,6 +88,7 @@ declare global {
mxPerformanceEntryNames: any; mxPerformanceEntryNames: any;
mxUIStore: UIStore; mxUIStore: UIStore;
mxSetupEncryptionStore?: SetupEncryptionStore; mxSetupEncryptionStore?: SetupEncryptionStore;
mxRoomScrollStateStore?: RoomScrollStateStore;
} }
interface Document { interface Document {

View file

@ -42,7 +42,7 @@ import eventSearch, { searchPagination } from '../../Searching';
import MainSplit from './MainSplit'; import MainSplit from './MainSplit';
import RightPanel from './RightPanel'; import RightPanel from './RightPanel';
import RoomViewStore from '../../stores/RoomViewStore'; import RoomViewStore from '../../stores/RoomViewStore';
import RoomScrollStateStore from '../../stores/RoomScrollStateStore'; import RoomScrollStateStore, { ScrollState } from '../../stores/RoomScrollStateStore';
import WidgetEchoStore from '../../stores/WidgetEchoStore'; import WidgetEchoStore from '../../stores/WidgetEchoStore';
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import { Layout } from "../../settings/Layout"; import { Layout } from "../../settings/Layout";
@ -1577,7 +1577,7 @@ export default class RoomView extends React.Component<IProps, IState> {
// get the current scroll position of the room, so that it can be // get the current scroll position of the room, so that it can be
// restored when we switch back to it. // restored when we switch back to it.
// //
private getScrollState() { private getScrollState(): ScrollState {
const messagePanel = this.messagePanel; const messagePanel = this.messagePanel;
if (!messagePanel) return null; if (!messagePanel) return null;

View file

@ -15,39 +15,42 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore'; import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore';
import SetupEncryptionBody from "./SetupEncryptionBody"; import SetupEncryptionBody from "./SetupEncryptionBody";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("structures.auth.CompleteSecurity") interface IProps {
export default class CompleteSecurity extends React.Component { onFinished: () => void;
static propTypes = { }
onFinished: PropTypes.func.isRequired,
};
constructor() { interface IState {
super(); phase: Phase;
}
@replaceableComponent("structures.auth.CompleteSecurity")
export default class CompleteSecurity extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
const store = SetupEncryptionStore.sharedInstance(); const store = SetupEncryptionStore.sharedInstance();
store.on("update", this._onStoreUpdate); store.on("update", this.onStoreUpdate);
store.start(); store.start();
this.state = { phase: store.phase }; this.state = { phase: store.phase };
} }
_onStoreUpdate = () => { private onStoreUpdate = (): void => {
const store = SetupEncryptionStore.sharedInstance(); const store = SetupEncryptionStore.sharedInstance();
this.setState({ phase: store.phase }); this.setState({ phase: store.phase });
}; };
componentWillUnmount() { public componentWillUnmount(): void {
const store = SetupEncryptionStore.sharedInstance(); const store = SetupEncryptionStore.sharedInstance();
store.off("update", this._onStoreUpdate); store.off("update", this.onStoreUpdate);
store.stop(); store.stop();
} }
render() { public render() {
const AuthPage = sdk.getComponent("auth.AuthPage"); const AuthPage = sdk.getComponent("auth.AuthPage");
const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody"); const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody");
const { phase } = this.state; const { phase } = this.state;

View file

@ -15,20 +15,19 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import AuthPage from '../../views/auth/AuthPage'; import AuthPage from '../../views/auth/AuthPage';
import CompleteSecurityBody from '../../views/auth/CompleteSecurityBody'; import CompleteSecurityBody from '../../views/auth/CompleteSecurityBody';
import CreateCrossSigningDialog from '../../views/dialogs/security/CreateCrossSigningDialog'; import CreateCrossSigningDialog from '../../views/dialogs/security/CreateCrossSigningDialog';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("structures.auth.E2eSetup") interface IProps {
export default class E2eSetup extends React.Component { onFinished: () => void;
static propTypes = { accountPassword?: string;
onFinished: PropTypes.func.isRequired, tokenLogin?: boolean;
accountPassword: PropTypes.string, }
tokenLogin: PropTypes.bool,
};
@replaceableComponent("structures.auth.E2eSetup")
export default class E2eSetup extends React.Component<IProps> {
render() { render() {
return ( return (
<AuthPage> <AuthPage>

View file

@ -17,7 +17,6 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import Modal from "../../../Modal"; import Modal from "../../../Modal";
@ -31,27 +30,50 @@ import PassphraseField from '../../views/auth/PassphraseField';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm'; import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
// Phases import { IValidationResult } from "../../views/elements/Validation";
// Show the forgot password inputs
const PHASE_FORGOT = 1; enum Phase {
// Email is in the process of being sent // Show the forgot password inputs
const PHASE_SENDING_EMAIL = 2; Forgot = 1,
// Email has been sent // Email is in the process of being sent
const PHASE_EMAIL_SENT = 3; SendingEmail = 2,
// User has clicked the link in email and completed reset // Email has been sent
const PHASE_DONE = 4; EmailSent = 3,
// User has clicked the link in email and completed reset
Done = 4,
}
interface IProps {
serverConfig: ValidatedServerConfig;
onServerConfigChange: () => void;
onLoginClick?: () => void;
onComplete: () => void;
}
interface IState {
phase: Phase;
email: string;
password: string;
password2: string;
errorText: string;
// We perform liveliness checks later, but for now suppress the errors.
// We also track the server dead errors independently of the regular errors so
// that we can render it differently, and override any other error the user may
// be seeing.
serverIsAlive: boolean;
serverErrorIsFatal: boolean;
serverDeadError: string;
passwordFieldValid: boolean;
}
@replaceableComponent("structures.auth.ForgotPassword") @replaceableComponent("structures.auth.ForgotPassword")
export default class ForgotPassword extends React.Component { export default class ForgotPassword extends React.Component<IProps, IState> {
static propTypes = { private reset: PasswordReset;
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
onServerConfigChange: PropTypes.func.isRequired,
onLoginClick: PropTypes.func,
onComplete: PropTypes.func.isRequired,
};
state = { state = {
phase: PHASE_FORGOT, phase: Phase.Forgot,
email: "", email: "",
password: "", password: "",
password2: "", password2: "",
@ -64,30 +86,31 @@ export default class ForgotPassword extends React.Component {
serverIsAlive: true, serverIsAlive: true,
serverErrorIsFatal: false, serverErrorIsFatal: false,
serverDeadError: "", serverDeadError: "",
passwordFieldValid: false,
}; };
constructor(props) { constructor(props: IProps) {
super(props); super(props);
CountlyAnalytics.instance.track("onboarding_forgot_password_begin"); CountlyAnalytics.instance.track("onboarding_forgot_password_begin");
} }
componentDidMount() { public componentDidMount() {
this.reset = null; this.reset = null;
this._checkServerLiveliness(this.props.serverConfig); this.checkServerLiveliness(this.props.serverConfig);
} }
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event // TODO: [REACT-WARNING] Replace with appropriate lifecycle event
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(newProps) { public UNSAFE_componentWillReceiveProps(newProps: IProps): void {
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl && if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return; newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
// Do a liveliness check on the new URLs // Do a liveliness check on the new URLs
this._checkServerLiveliness(newProps.serverConfig); this.checkServerLiveliness(newProps.serverConfig);
} }
async _checkServerLiveliness(serverConfig) { private async checkServerLiveliness(serverConfig): Promise<void> {
try { try {
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls( await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
serverConfig.hsUrl, serverConfig.hsUrl,
@ -98,28 +121,28 @@ export default class ForgotPassword extends React.Component {
serverIsAlive: true, serverIsAlive: true,
}); });
} catch (e) { } catch (e) {
this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password")); this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password") as IState);
} }
} }
submitPasswordReset(email, password) { public submitPasswordReset(email: string, password: string): void {
this.setState({ this.setState({
phase: PHASE_SENDING_EMAIL, phase: Phase.SendingEmail,
}); });
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl); this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
this.reset.resetPassword(email, password).then(() => { this.reset.resetPassword(email, password).then(() => {
this.setState({ this.setState({
phase: PHASE_EMAIL_SENT, phase: Phase.EmailSent,
}); });
}, (err) => { }, (err) => {
this.showErrorDialog(_t('Failed to send email') + ": " + err.message); this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
this.setState({ this.setState({
phase: PHASE_FORGOT, phase: Phase.Forgot,
}); });
}); });
} }
onVerify = async ev => { private onVerify = async (ev: React.MouseEvent): Promise<void> => {
ev.preventDefault(); ev.preventDefault();
if (!this.reset) { if (!this.reset) {
console.error("onVerify called before submitPasswordReset!"); console.error("onVerify called before submitPasswordReset!");
@ -127,17 +150,17 @@ export default class ForgotPassword extends React.Component {
} }
try { try {
await this.reset.checkEmailLinkClicked(); await this.reset.checkEmailLinkClicked();
this.setState({ phase: PHASE_DONE }); this.setState({ phase: Phase.Done });
} catch (err) { } catch (err) {
this.showErrorDialog(err.message); this.showErrorDialog(err.message);
} }
}; };
onSubmitForm = async ev => { private onSubmitForm = async (ev: React.FormEvent): Promise<void> => {
ev.preventDefault(); ev.preventDefault();
// refresh the server errors, just in case the server came back online // refresh the server errors, just in case the server came back online
await this._checkServerLiveliness(this.props.serverConfig); await this.checkServerLiveliness(this.props.serverConfig);
await this['password_field'].validate({ allowEmpty: false }); await this['password_field'].validate({ allowEmpty: false });
@ -172,27 +195,27 @@ export default class ForgotPassword extends React.Component {
} }
}; };
onInputChanged = (stateKey, ev) => { private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>) => {
this.setState({ this.setState({
[stateKey]: ev.target.value, [stateKey]: ev.currentTarget.value,
}); } as any);
}; };
onLoginClick = ev => { private onLoginClick = (ev: React.MouseEvent): void => {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
this.props.onLoginClick(); this.props.onLoginClick();
}; };
showErrorDialog(body, title) { public showErrorDialog(description: string, title?: string) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, { Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
title: title, title,
description: body, description,
}); });
} }
onPasswordValidate(result) { private onPasswordValidate(result: IValidationResult) {
this.setState({ this.setState({
passwordFieldValid: result.valid, passwordFieldValid: result.valid,
}); });
@ -316,16 +339,16 @@ export default class ForgotPassword extends React.Component {
let resetPasswordJsx; let resetPasswordJsx;
switch (this.state.phase) { switch (this.state.phase) {
case PHASE_FORGOT: case Phase.Forgot:
resetPasswordJsx = this.renderForgot(); resetPasswordJsx = this.renderForgot();
break; break;
case PHASE_SENDING_EMAIL: case Phase.SendingEmail:
resetPasswordJsx = this.renderSendingEmail(); resetPasswordJsx = this.renderSendingEmail();
break; break;
case PHASE_EMAIL_SENT: case Phase.EmailSent:
resetPasswordJsx = this.renderEmailSent(); resetPasswordJsx = this.renderEmailSent();
break; break;
case PHASE_DONE: case Phase.Done:
resetPasswordJsx = this.renderDone(); resetPasswordJsx = this.renderDone();
break; break;
} }

View file

@ -17,15 +17,25 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { IntegrationManagers } from "../../../integrations/IntegrationManagers"; import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import { IntegrationManagerInstance } from "../../../integrations/IntegrationManagerInstance";
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { SettingLevel } from "../../../settings/SettingLevel"; import { SettingLevel } from "../../../settings/SettingLevel";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
}
interface IState {
currentManager: IntegrationManagerInstance;
provisioningEnabled: boolean;
}
@replaceableComponent("views.settings.SetIntegrationManager") @replaceableComponent("views.settings.SetIntegrationManager")
export default class SetIntegrationManager extends React.Component { export default class SetIntegrationManager extends React.Component<IProps, IState> {
constructor() { constructor(props: IProps) {
super(); super(props);
const currentManager = IntegrationManagers.sharedInstance().getPrimaryManager(); const currentManager = IntegrationManagers.sharedInstance().getPrimaryManager();
@ -35,7 +45,7 @@ export default class SetIntegrationManager extends React.Component {
}; };
} }
onProvisioningToggled = () => { private onProvisioningToggled = (): void => {
const current = this.state.provisioningEnabled; const current = this.state.provisioningEnabled;
SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => { SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => {
console.error("Error changing integration manager provisioning"); console.error("Error changing integration manager provisioning");
@ -46,7 +56,7 @@ export default class SetIntegrationManager extends React.Component {
this.setState({ provisioningEnabled: !current }); this.setState({ provisioningEnabled: !current });
}; };
render() { public render(): React.ReactNode {
const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch"); const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch");
const currentManager = this.state.currentManager; const currentManager = this.state.currentManager;

View file

@ -15,39 +15,48 @@ limitations under the License.
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types";
import { _t, pickBestLanguage } from "../../../languageHandler"; import { _t, pickBestLanguage } from "../../../languageHandler";
import * as sdk from "../../.."; import * as sdk from "../../..";
import { objectClone } from "../../../utils/objects"; import { objectClone } from "../../../utils/objects";
import StyledCheckbox from "../elements/StyledCheckbox"; import StyledCheckbox from "../elements/StyledCheckbox";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
policiesAndServicePairs: any[];
onFinished: (string) => void;
agreedUrls: string[]; // array of URLs the user has accepted
introElement: Node;
}
interface IState {
policies: Policy[];
busy: boolean;
}
interface Policy {
checked: boolean;
url: string;
name: string;
}
@replaceableComponent("views.terms.InlineTermsAgreement") @replaceableComponent("views.terms.InlineTermsAgreement")
export default class InlineTermsAgreement extends React.Component { export default class InlineTermsAgreement extends React.Component<IProps, IState> {
static propTypes = { constructor(props: IProps) {
policiesAndServicePairs: PropTypes.array.isRequired, // array of service/policy pairs super(props);
agreedUrls: PropTypes.array.isRequired, // array of URLs the user has accepted
onFinished: PropTypes.func.isRequired, // takes an argument of accepted URLs
introElement: PropTypes.node,
};
constructor() {
super();
this.state = { this.state = {
policies: [], policies: [],
busy: false, busy: false,
}; };
} }
componentDidMount() { public componentDidMount(): void {
// Build all the terms the user needs to accept // Build all the terms the user needs to accept
const policies = []; // { checked, url, name } const policies = []; // { checked, url, name }
for (const servicePolicies of this.props.policiesAndServicePairs) { for (const servicePolicies of this.props.policiesAndServicePairs) {
const availablePolicies = Object.values(servicePolicies.policies); const availablePolicies = Object.values(servicePolicies.policies);
for (const policy of availablePolicies) { for (const policy of availablePolicies) {
const language = pickBestLanguage(Object.keys(policy).filter(p => p !== 'version')); const language = pickBestLanguage(Object.keys(policy).filter(p => p !== 'version'));
const renderablePolicy = { const renderablePolicy: Policy = {
checked: false, checked: false,
url: policy[language].url, url: policy[language].url,
name: policy[language].name, name: policy[language].name,
@ -59,13 +68,13 @@ export default class InlineTermsAgreement extends React.Component {
this.setState({ policies }); this.setState({ policies });
} }
_togglePolicy = (index) => { private togglePolicy = (index: number): void => {
const policies = objectClone(this.state.policies); const policies = objectClone(this.state.policies);
policies[index].checked = !policies[index].checked; policies[index].checked = !policies[index].checked;
this.setState({ policies }); this.setState({ policies });
}; };
_onContinue = () => { private onContinue = (): void => {
const hasUnchecked = !!this.state.policies.some(p => !p.checked); const hasUnchecked = !!this.state.policies.some(p => !p.checked);
if (hasUnchecked) return; if (hasUnchecked) return;
@ -73,7 +82,7 @@ export default class InlineTermsAgreement extends React.Component {
this.props.onFinished(this.state.policies.map(p => p.url)); this.props.onFinished(this.state.policies.map(p => p.url));
}; };
_renderCheckboxes() { private renderCheckboxes(): React.ReactNode[] {
const rendered = []; const rendered = [];
for (let i = 0; i < this.state.policies.length; i++) { for (let i = 0; i < this.state.policies.length; i++) {
const policy = this.state.policies[i]; const policy = this.state.policies[i];
@ -93,7 +102,7 @@ export default class InlineTermsAgreement extends React.Component {
<div key={i} className='mx_InlineTermsAgreement_cbContainer'> <div key={i} className='mx_InlineTermsAgreement_cbContainer'>
<div>{introText}</div> <div>{introText}</div>
<div className='mx_InlineTermsAgreement_checkbox'> <div className='mx_InlineTermsAgreement_checkbox'>
<StyledCheckbox onChange={() => this._togglePolicy(i)} checked={policy.checked}> <StyledCheckbox onChange={() => this.togglePolicy(i)} checked={policy.checked}>
{_t("Accept")} {_t("Accept")}
</StyledCheckbox> </StyledCheckbox>
</div> </div>
@ -103,16 +112,16 @@ export default class InlineTermsAgreement extends React.Component {
return rendered; return rendered;
} }
render() { public render(): React.ReactNode {
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton"); const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
const hasUnchecked = !!this.state.policies.some(p => !p.checked); const hasUnchecked = !!this.state.policies.some(p => !p.checked);
return ( return (
<div> <div>
{this.props.introElement} {this.props.introElement}
{this._renderCheckboxes()} {this.renderCheckboxes()}
<AccessibleButton <AccessibleButton
onClick={this._onContinue} onClick={this.onContinue}
disabled={hasUnchecked || this.state.busy} disabled={hasUnchecked || this.state.busy}
kind="primary_sm" kind="primary_sm"
> >

View file

@ -15,18 +15,17 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.verification.VerificationCancelled") interface IProps {
export default class VerificationCancelled extends React.Component { onDone: () => void;
static propTypes = { }
onDone: PropTypes.func.isRequired,
}
render() { @replaceableComponent("views.verification.VerificationCancelled")
export default class VerificationCancelled extends React.Component<IProps> {
public render(): React.ReactNode {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div> return <div>
<p>{_t( <p>{_t(

View file

@ -15,18 +15,17 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.verification.VerificationComplete") interface IProps {
export default class VerificationComplete extends React.Component { onDone: () => void;
static propTypes = { }
onDone: PropTypes.func.isRequired,
}
render() { @replaceableComponent("views.verification.VerificationComplete")
export default class VerificationComplete extends React.Component<IProps> {
public render(): React.ReactNode {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div> return <div>
<h2>{_t("Verified!")}</h2> <h2>{_t("Verified!")}</h2>

View file

@ -1,68 +0,0 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import AccessibleButton from "../elements/AccessibleButton";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import VerificationQRCode from "../elements/crypto/VerificationQRCode";
import Spinner from "../elements/Spinner";
import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
@replaceableComponent("views.verification.VerificationQREmojiOptions")
export default class VerificationQREmojiOptions extends React.Component {
static propTypes = {
request: PropTypes.object.isRequired,
onCancel: PropTypes.func.isRequired,
onStartEmoji: PropTypes.func.isRequired,
};
render() {
const { request } = this.props;
const showQR = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
let qrCode;
if (showQR) {
qrCode = <VerificationQRCode qrCodeData={request.qrCodeData} />;
} else {
qrCode = <div className='mx_VerificationQREmojiOptions_noQR'><Spinner /></div>;
}
return (
<div>
{_t("Verify this session by completing one of the following:")}
<div className='mx_IncomingSasDialog_startOptions'>
<div className='mx_IncomingSasDialog_startOption'>
<p>{_t("Scan this unique code")}</p>
{qrCode}
</div>
<div className='mx_IncomingSasDialog_betweenText'>{_t("or")}</div>
<div className='mx_IncomingSasDialog_startOption'>
<p>{_t("Compare unique emoji")}</p>
<span className='mx_IncomingSasDialog_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span>
<AccessibleButton onClick={this.props.onStartEmoji} kind='primary'>
{_t("Start")}
</AccessibleButton>
</div>
</div>
<AccessibleButton onClick={this.props.onCancel} kind='danger'>
{_t("Cancel")}
</AccessibleButton>
</div>
);
}
}

View file

@ -15,7 +15,8 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import { SAS } from "matrix-js-sdk/src/crypto/verification/SAS";
import { DeviceInfo } from "matrix-js-sdk/src//crypto/deviceinfo";
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import { PendingActionSpinner } from "../right_panel/EncryptionInfo"; import { PendingActionSpinner } from "../right_panel/EncryptionInfo";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
@ -23,24 +24,29 @@ import DialogButtons from "../elements/DialogButtons";
import { fixupColorFonts } from '../../../utils/FontManager'; import { fixupColorFonts } from '../../../utils/FontManager';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
pending?: boolean;
displayName?: string; // required if pending is true
device?: DeviceInfo;
onDone: () => void;
onCancel: () => void;
sas: SAS.sas;
isSelf?: boolean;
inDialog?: boolean; // whether this component is being shown in a dialog and to use DialogButtons
}
interface IState {
pending: boolean;
cancelling?: boolean;
}
function capFirst(s) { function capFirst(s) {
return s.charAt(0).toUpperCase() + s.slice(1); return s.charAt(0).toUpperCase() + s.slice(1);
} }
@replaceableComponent("views.verification.VerificationShowSas") @replaceableComponent("views.verification.VerificationShowSas")
export default class VerificationShowSas extends React.Component { export default class VerificationShowSas extends React.Component<IProps, IState> {
static propTypes = { constructor(props: IProps) {
pending: PropTypes.bool,
displayName: PropTypes.string, // required if pending is true
device: PropTypes.object,
onDone: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
sas: PropTypes.object.isRequired,
isSelf: PropTypes.bool,
inDialog: PropTypes.bool, // whether this component is being shown in a dialog and to use DialogButtons
};
constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -48,19 +54,19 @@ export default class VerificationShowSas extends React.Component {
}; };
} }
componentWillMount() { public componentWillMount(): void {
// As this component is also used before login (during complete security), // As this component is also used before login (during complete security),
// also make sure we have a working emoji font to display the SAS emojis here. // also make sure we have a working emoji font to display the SAS emojis here.
// This is also done from LoggedInView. // This is also done from LoggedInView.
fixupColorFonts(); fixupColorFonts();
} }
onMatchClick = () => { private onMatchClick = (): void => {
this.setState({ pending: true }); this.setState({ pending: true });
this.props.onDone(); this.props.onDone();
}; };
onDontMatchClick = () => { private onDontMatchClick = (): void => {
this.setState({ cancelling: true }); this.setState({ cancelling: true });
this.props.onCancel(); this.props.onCancel();
}; };

View file

@ -923,12 +923,6 @@
"You've successfully verified this user.": "You've successfully verified this user.", "You've successfully verified this user.": "You've successfully verified this user.",
"Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.", "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.",
"Got It": "Got It", "Got It": "Got It",
"Verify this session by completing one of the following:": "Verify this session by completing one of the following:",
"Scan this unique code": "Scan this unique code",
"or": "or",
"Compare unique emoji": "Compare unique emoji",
"Compare a unique set of emoji if you don't have a camera on either device": "Compare a unique set of emoji if you don't have a camera on either device",
"Start": "Start",
"Confirm the emoji below are displayed on both sessions, in the same order:": "Confirm the emoji below are displayed on both sessions, in the same order:", "Confirm the emoji below are displayed on both sessions, in the same order:": "Confirm the emoji below are displayed on both sessions, in the same order:",
"Verify this user by confirming the following emoji appear on their screen.": "Verify this user by confirming the following emoji appear on their screen.", "Verify this user by confirming the following emoji appear on their screen.": "Verify this user by confirming the following emoji appear on their screen.",
"Verify this session by confirming the following number appears on its screen.": "Verify this session by confirming the following number appears on its screen.", "Verify this session by confirming the following number appears on its screen.": "Verify this session by confirming the following number appears on its screen.",
@ -1833,6 +1827,12 @@
"Edit devices": "Edit devices", "Edit devices": "Edit devices",
"Security": "Security", "Security": "Security",
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.", "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.",
"Scan this unique code": "Scan this unique code",
"Compare unique emoji": "Compare unique emoji",
"Compare a unique set of emoji if you don't have a camera on either device": "Compare a unique set of emoji if you don't have a camera on either device",
"Start": "Start",
"or": "or",
"Verify this session by completing one of the following:": "Verify this session by completing one of the following:",
"Verify by scanning": "Verify by scanning", "Verify by scanning": "Verify by scanning",
"Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:", "Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:",
"If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.", "If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.",

View file

@ -20,15 +20,25 @@ import { StandardActions } from "./StandardActions";
import { PushRuleVectorState } from "./PushRuleVectorState"; import { PushRuleVectorState } from "./PushRuleVectorState";
import { NotificationUtils } from "./NotificationUtils"; import { NotificationUtils } from "./NotificationUtils";
interface IProps {
kind: Kind;
description: string;
vectorStateToActions: Action;
}
class VectorPushRuleDefinition { class VectorPushRuleDefinition {
constructor(opts) { private kind: Kind;
private description: string;
private vectorStateToActions: Action;
constructor(opts: IProps) {
this.kind = opts.kind; this.kind = opts.kind;
this.description = opts.description; this.description = opts.description;
this.vectorStateToActions = opts.vectorStateToActions; this.vectorStateToActions = opts.vectorStateToActions;
} }
// Translate the rule actions and its enabled value into vector state // Translate the rule actions and its enabled value into vector state
ruleToVectorState(rule) { public ruleToVectorState(rule): VectorPushRuleDefinition {
let enabled = false; let enabled = false;
if (rule) { if (rule) {
enabled = rule.enabled; enabled = rule.enabled;
@ -63,13 +73,24 @@ class VectorPushRuleDefinition {
} }
} }
enum Kind {
Override = "override",
Underride = "underride",
}
interface Action {
on: StandardActions;
loud: StandardActions;
off: StandardActions;
}
/** /**
* The descriptions of rules managed by the Vector UI. * The descriptions of rules managed by the Vector UI.
*/ */
export const VectorPushRulesDefinitions = { export const VectorPushRulesDefinitions = {
// Messages containing user's display name // Messages containing user's display name
".m.rule.contains_display_name": new VectorPushRuleDefinition({ ".m.rule.contains_display_name": new VectorPushRuleDefinition({
kind: "override", kind: Kind.Override,
description: _td("Messages containing my display name"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Messages containing my display name"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { // The actions for each vector state, or null to disable the rule. vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -80,7 +101,7 @@ export const VectorPushRulesDefinitions = {
// Messages containing user's username (localpart/MXID) // Messages containing user's username (localpart/MXID)
".m.rule.contains_user_name": new VectorPushRuleDefinition({ ".m.rule.contains_user_name": new VectorPushRuleDefinition({
kind: "override", kind: Kind.Override,
description: _td("Messages containing my username"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Messages containing my username"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { // The actions for each vector state, or null to disable the rule. vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -91,7 +112,7 @@ export const VectorPushRulesDefinitions = {
// Messages containing @room // Messages containing @room
".m.rule.roomnotif": new VectorPushRuleDefinition({ ".m.rule.roomnotif": new VectorPushRuleDefinition({
kind: "override", kind: Kind.Override,
description: _td("Messages containing @room"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Messages containing @room"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { // The actions for each vector state, or null to disable the rule. vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -102,7 +123,7 @@ export const VectorPushRulesDefinitions = {
// Messages just sent to the user in a 1:1 room // Messages just sent to the user in a 1:1 room
".m.rule.room_one_to_one": new VectorPushRuleDefinition({ ".m.rule.room_one_to_one": new VectorPushRuleDefinition({
kind: "underride", kind: Kind.Underride,
description: _td("Messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -113,7 +134,7 @@ export const VectorPushRulesDefinitions = {
// Encrypted messages just sent to the user in a 1:1 room // Encrypted messages just sent to the user in a 1:1 room
".m.rule.encrypted_room_one_to_one": new VectorPushRuleDefinition({ ".m.rule.encrypted_room_one_to_one": new VectorPushRuleDefinition({
kind: "underride", kind: Kind.Underride,
description: _td("Encrypted messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Encrypted messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -126,7 +147,7 @@ export const VectorPushRulesDefinitions = {
// 1:1 room messages are catched by the .m.rule.room_one_to_one rule if any defined // 1:1 room messages are catched by the .m.rule.room_one_to_one rule if any defined
// By opposition, all other room messages are from group chat rooms. // By opposition, all other room messages are from group chat rooms.
".m.rule.message": new VectorPushRuleDefinition({ ".m.rule.message": new VectorPushRuleDefinition({
kind: "underride", kind: Kind.Underride,
description: _td("Messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -139,7 +160,7 @@ export const VectorPushRulesDefinitions = {
// Encrypted 1:1 room messages are catched by the .m.rule.encrypted_room_one_to_one rule if any defined // Encrypted 1:1 room messages are catched by the .m.rule.encrypted_room_one_to_one rule if any defined
// By opposition, all other room messages are from group chat rooms. // By opposition, all other room messages are from group chat rooms.
".m.rule.encrypted": new VectorPushRuleDefinition({ ".m.rule.encrypted": new VectorPushRuleDefinition({
kind: "underride", kind: Kind.Underride,
description: _td("Encrypted messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Encrypted messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -150,7 +171,7 @@ export const VectorPushRulesDefinitions = {
// Invitation for the user // Invitation for the user
".m.rule.invite_for_me": new VectorPushRuleDefinition({ ".m.rule.invite_for_me": new VectorPushRuleDefinition({
kind: "underride", kind: Kind.Underride,
description: _td("When I'm invited to a room"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("When I'm invited to a room"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -161,7 +182,7 @@ export const VectorPushRulesDefinitions = {
// Incoming call // Incoming call
".m.rule.call": new VectorPushRuleDefinition({ ".m.rule.call": new VectorPushRuleDefinition({
kind: "underride", kind: Kind.Underride,
description: _td("Call invitation"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Call invitation"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { vectorStateToActions: {
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,
@ -172,7 +193,7 @@ export const VectorPushRulesDefinitions = {
// Notifications from bots // Notifications from bots
".m.rule.suppress_notices": new VectorPushRuleDefinition({ ".m.rule.suppress_notices": new VectorPushRuleDefinition({
kind: "override", kind: Kind.Override,
description: _td("Messages sent by bot"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("Messages sent by bot"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { vectorStateToActions: {
// .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI // .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI
@ -184,7 +205,7 @@ export const VectorPushRulesDefinitions = {
// Room upgrades (tombstones) // Room upgrades (tombstones)
".m.rule.tombstone": new VectorPushRuleDefinition({ ".m.rule.tombstone": new VectorPushRuleDefinition({
kind: "override", kind: Kind.Override,
description: _td("When rooms are upgraded"), // passed through _t() translation in src/components/views/settings/Notifications.js description: _td("When rooms are upgraded"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { // The actions for each vector state, or null to disable the rule. vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
on: StandardActions.ACTION_NOTIFY, on: StandardActions.ACTION_NOTIFY,

View file

@ -1,50 +0,0 @@
/*
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* Stores where the user has scrolled to in each room
*/
class RoomScrollStateStore {
constructor() {
// A map from room id to scroll state.
//
// If there is no special scroll state (ie, we are following the live
// timeline), the scroll state is null. Otherwise, it is an object with
// the following properties:
//
// focussedEvent: the ID of the 'focussed' event. Typically this is
// the last event fully visible in the viewport, though if we
// have done an explicit scroll to an explicit event, it will be
// that event.
//
// pixelOffset: the number of pixels the window is scrolled down
// from the focussedEvent.
this._scrollStateMap = {};
}
getScrollState(roomId) {
return this._scrollStateMap[roomId];
}
setScrollState(roomId, scrollState) {
this._scrollStateMap[roomId] = scrollState;
}
}
if (global.mx_RoomScrollStateStore === undefined) {
global.mx_RoomScrollStateStore = new RoomScrollStateStore();
}
export default global.mx_RoomScrollStateStore;

View file

@ -0,0 +1,53 @@
/*
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export interface ScrollState {
focussedEvent: string;
pixelOffset: number;
}
/**
* Stores where the user has scrolled to in each room
*/
export class RoomScrollStateStore {
// A map from room id to scroll state.
//
// If there is no special scroll state (ie, we are following the live
// timeline), the scroll state is null. Otherwise, it is an object with
// the following properties:
//
// focussedEvent: the ID of the 'focussed' event. Typically this is
// the last event fully visible in the viewport, though if we
// have done an explicit scroll to an explicit event, it will be
// that event.
//
// pixelOffset: the number of pixels the window is scrolled down
// from the focussedEvent.
private scrollStateMap = new Map<string, ScrollState>();
public getScrollState(roomId: string): ScrollState {
return this.scrollStateMap.get(roomId);
}
setScrollState(roomId: string, scrollState: ScrollState): void {
this.scrollStateMap.set(roomId, scrollState);
}
}
if (window.mxRoomScrollStateStore === undefined) {
window.mxRoomScrollStateStore = new RoomScrollStateStore();
}
export default window.mxRoomScrollStateStore;