Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into joriks/irc-ui

This commit is contained in:
Jorik Schellekens 2020-05-19 18:03:44 +01:00
commit 323d0762fc
189 changed files with 2091 additions and 976 deletions

View file

@ -18,7 +18,7 @@ import React from 'react';
import CustomRoomTagStore from '../../stores/CustomRoomTagStore';
import AutoHideScrollbar from './AutoHideScrollbar';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import classNames from 'classnames';
import * as FormattingUtils from '../../utils/FormattingUtils';

View file

@ -23,7 +23,7 @@ import PropTypes from 'prop-types';
import request from 'browser-request';
import { _t } from '../../languageHandler';
import sanitizeHtml from 'sanitize-html';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import {MatrixClientPeg} from '../../MatrixClientPeg';
import classnames from 'classnames';
import MatrixClientContext from "../../contexts/MatrixClientContext";

View file

@ -21,7 +21,7 @@ import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import {MatrixClientPeg} from '../../MatrixClientPeg';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import { getHostingLink } from '../../utils/HostingLink';
import { sanitizedHtmlNode } from '../../HtmlUtils';
import { _t, _td } from '../../languageHandler';

View file

@ -21,7 +21,7 @@ import { getHomePageUrl } from "../../utils/pages";
import { _t } from "../../languageHandler";
import SdkConfig from "../../SdkConfig";
import * as sdk from "../../index";
import dis from "../../dispatcher";
import dis from "../../dispatcher/dispatcher";
const onClickSendDm = () => dis.dispatch({action: 'view_create_chat'});
const onClickExplore = () => dis.dispatch({action: 'view_room_directory'});

View file

@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Key } from '../../Keyboard';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import * as VectorConferenceHandler from '../../VectorConferenceHandler';
import SettingsStore from '../../settings/SettingsStore';
import {_t} from "../../languageHandler";

View file

@ -27,7 +27,7 @@ import PageTypes from '../../PageTypes';
import CallMediaHandler from '../../CallMediaHandler';
import { fixupColorFonts } from '../../utils/FontManager';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import sessionStore from '../../stores/SessionStore';
import {MatrixClientPeg, MatrixClientCreds} from '../../MatrixClientPeg';
import SettingsStore from "../../settings/SettingsStore";

View file

@ -17,12 +17,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import {InvalidStoreError} from "matrix-js-sdk/src/errors";
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import React, { createRef } from 'react';
import { InvalidStoreError } from "matrix-js-sdk/src/errors";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { isCryptoAvailable } from 'matrix-js-sdk/src/crypto';
// focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss
import 'focus-visible';
// what-input helps improve keyboard accessibility
@ -30,17 +29,17 @@ import 'what-input';
import Analytics from "../../Analytics";
import { DecryptionFailureTracker } from "../../DecryptionFailureTracker";
import {MatrixClientPeg} from "../../MatrixClientPeg";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import PlatformPeg from "../../PlatformPeg";
import SdkConfig from "../../SdkConfig";
import * as RoomListSorter from "../../RoomListSorter";
import dis from "../../dispatcher";
import dis from "../../dispatcher/dispatcher";
import Notifier from '../../Notifier';
import Modal from "../../Modal";
import Tinter from "../../Tinter";
import * as sdk from '../../index';
import { showStartChatInviteDialog, showRoomInviteDialog } from '../../RoomInvite';
import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite';
import * as Rooms from '../../Rooms';
import linkifyMatrix from "../../linkify-matrix";
import * as Lifecycle from '../../Lifecycle';
@ -52,21 +51,22 @@ import { getHomePageUrl } from '../../utils/pages';
import createRoom from "../../createRoom";
import KeyRequestHandler from '../../KeyRequestHandler';
import { _t, getCurrentLanguage } from '../../languageHandler';
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
import SettingsStore, { SettingLevel } from "../../settings/SettingsStore";
import ThemeController from "../../settings/controllers/ThemeController";
import { startAnyRegistrationFlow } from "../../Registration.js";
import { messageForSyncError } from '../../utils/ErrorUtils';
import ResizeNotifier from "../../utils/ResizeNotifier";
import { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils";
import AutoDiscoveryUtils from "../../utils/AutoDiscoveryUtils";
import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils";
import DMRoomMap from '../../utils/DMRoomMap';
import { countRoomsWithNotif } from '../../RoomNotifs';
import { ThemeWatcher } from "../../theme";
import { storeRoomAliasInCache } from '../../RoomAliasCache';
import {defer, IDeferred} from "../../utils/promise";
import { defer, IDeferred } from "../../utils/promise";
import ToastStore from "../../stores/ToastStore";
import * as StorageManager from "../../utils/StorageManager";
import type LoggedInViewType from "./LoggedInView";
import { ViewUserPayload } from "../../dispatcher/payloads/ViewUserPayload";
import { Action } from "../../dispatcher/actions";
/** constants for MatrixChat.state.view */
export enum Views {
@ -107,7 +107,7 @@ export enum Views {
// re-dispatched. NOTE: some actions are non-trivial and would require
// re-factoring to be included in this list in future.
const ONBOARDING_FLOW_STARTERS = [
'view_user_settings',
Action.ViewUserSettings,
'view_create_chat',
'view_create_room',
'view_create_group',
@ -613,7 +613,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
case 'view_indexed_room':
this.viewIndexedRoom(payload.roomIndex);
break;
case 'view_user_settings': {
case Action.ViewUserSettings: {
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {},
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
@ -1621,9 +1621,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
action: 'view_create_room',
});
} else if (screen === 'settings') {
dis.dispatch({
action: 'view_user_settings',
});
dis.fire(Action.ViewUserSettings);
} else if (screen === 'welcome') {
dis.dispatch({
action: 'view_welcome_page',
@ -1755,8 +1753,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const member = new RoomMember(null, userId);
if (!member) { return; }
dis.dispatch({
action: 'view_user',
dis.dispatch<ViewUserPayload>({
action: Action.ViewUser,
member: member,
});
}
@ -1973,6 +1971,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
render() {
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
let fragmentAfterLogin = "";
if (this.props.initialScreenAfterLogin) {
fragmentAfterLogin = `/${this.props.initialScreenAfterLogin.screen}`;
}
let view;
if (this.state.view === Views.LOADING) {
@ -2052,7 +2055,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
} else if (this.state.view === Views.WELCOME) {
const Welcome = sdk.getComponent('auth.Welcome');
view = <Welcome {...this.getServerProperties()} />;
view = <Welcome {...this.getServerProperties()} fragmentAfterLogin={fragmentAfterLogin} />;
} else if (this.state.view === Views.REGISTER) {
const Registration = sdk.getComponent('structures.auth.Registration');
view = (
@ -2091,6 +2094,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
onForgotPasswordClick={this.onForgotPasswordClick}
onServerConfigChange={this.onServerConfigChange}
fragmentAfterLogin={fragmentAfterLogin}
{...this.getServerProperties()}
/>
);
@ -2100,6 +2104,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
<SoftLogout
realQueryParams={this.props.realQueryParams}
onTokenLoginCompleted={this.props.onTokenLoginCompleted}
fragmentAfterLogin={fragmentAfterLogin}
/>
);
} else {

View file

@ -19,7 +19,7 @@ import React from 'react';
import createReactClass from 'create-react-class';
import * as sdk from '../../index';
import { _t } from '../../languageHandler';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import AccessibleButton from '../views/elements/AccessibleButton';
import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar";

View file

@ -22,7 +22,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import RateLimitedFunc from '../../ratelimitedfunc';
import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker';
import GroupStore from '../../stores/GroupStore';
@ -30,6 +30,7 @@ import SettingsStore from "../../settings/SettingsStore";
import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases";
import RightPanelStore from "../../stores/RightPanelStore";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import {Action} from "../../dispatcher/actions";
export default class RightPanel extends React.Component {
static get propTypes() {
@ -237,7 +238,7 @@ export default class RightPanel extends React.Component {
// within a room, so go back to the member panel if we were in the encryption panel,
// or the member list if we were in the member panel... phew.
dis.dispatch({
action: "view_user",
action: Action.ViewUser,
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ?
this.state.member : null,
});
@ -266,7 +267,7 @@ export default class RightPanel extends React.Component {
if (SettingsStore.getValue("feature_cross_signing")) {
const onClose = () => {
dis.dispatch({
action: "view_user",
action: Action.ViewUser,
member: null,
});
};

View file

@ -20,7 +20,7 @@ import React from 'react';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../MatrixClientPeg";
import * as sdk from "../../index";
import dis from "../../dispatcher";
import dis from "../../dispatcher/dispatcher";
import Modal from "../../Modal";
import { linkifyAndSanitizeHtml } from '../../HtmlUtils';
import PropTypes from 'prop-types';

View file

@ -25,7 +25,7 @@ import * as sdk from '../../index';
import {MatrixClientPeg} from '../../MatrixClientPeg';
import Resend from '../../Resend';
import * as cryptodevices from '../../cryptodevices';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils';
const STATUS_BAR_HIDDEN = 0;

View file

@ -20,7 +20,7 @@ limitations under the License.
import React, {createRef} from 'react';
import classNames from 'classnames';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import * as Unread from '../../Unread';
import * as RoomNotifs from '../../RoomNotifs';
import * as FormattingUtils from '../../utils/FormattingUtils';

View file

@ -34,14 +34,14 @@ import ContentMessages from '../../ContentMessages';
import Modal from '../../Modal';
import * as sdk from '../../index';
import CallHandler from '../../CallHandler';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import Tinter from '../../Tinter';
import rate_limited_func from '../../ratelimitedfunc';
import * as ObjectUtils from '../../ObjectUtils';
import * as Rooms from '../../Rooms';
import eventSearch from '../../Searching';
import {isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard';
import {isOnlyCtrlOrCmdIgnoreShiftKeyEvent, isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard';
import MainSplit from './MainSplit';
import RightPanel from './RightPanel';
@ -588,6 +588,18 @@ export default createReactClass({
handled = true;
}
break;
case Key.PAGE_UP:
if (!ev.altKey && !ev.ctrlKey && ev.shiftKey && !ev.metaKey) {
this.jumpToReadMarker();
handled = true;
}
break;
case Key.U.toUpperCase():
if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) && ev.shiftKey) {
dis.dispatch({ action: "upload_file" })
handled = true;
}
break;
}
if (handled) {

View file

@ -19,7 +19,7 @@ import React, {createRef} from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { Key } from '../../Keyboard';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import { throttle } from 'lodash';
import AccessibleButton from '../../components/views/elements/AccessibleButton';
import classNames from 'classnames';

View file

@ -22,7 +22,7 @@ import TagOrderStore from '../../stores/TagOrderStore';
import GroupActions from '../../actions/GroupActions';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import { _t } from '../../languageHandler';
import { Droppable } from 'react-beautiful-dnd';

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react';
import createReactClass from 'create-react-class';
import * as sdk from '../../index';
import dis from '../../dispatcher';
import dis from '../../dispatcher/dispatcher';
import Modal from '../../Modal';
import { _t } from '../../languageHandler';

View file

@ -29,7 +29,7 @@ import {MatrixClientPeg} from "../../MatrixClientPeg";
import * as ObjectUtils from "../../ObjectUtils";
import UserActivity from "../../UserActivity";
import Modal from "../../Modal";
import dis from "../../dispatcher";
import dis from "../../dispatcher/dispatcher";
import * as sdk from "../../index";
import { Key } from '../../Keyboard';
import Timer from '../../utils/Timer';

View file

@ -22,7 +22,7 @@ import BaseAvatar from '../views/avatars/BaseAvatar';
import {MatrixClientPeg} from '../../MatrixClientPeg';
import * as Avatar from '../../Avatar';
import { _t } from '../../languageHandler';
import dis from "../../dispatcher";
import dis from "../../dispatcher/dispatcher";
import {ContextMenu, ContextMenuButton} from "./ContextMenu";
const AVATAR_SIZE = 28;

View file

@ -19,7 +19,7 @@ import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import ContentMessages from '../../ContentMessages';
import dis from "../../dispatcher";
import dis from "../../dispatcher/dispatcher";
import filesize from "filesize";
import { _t } from '../../languageHandler';

View file

@ -22,6 +22,7 @@ import {MatrixClientPeg} from "../../MatrixClientPeg";
import * as sdk from "../../index";
import Modal from '../../Modal';
import { _t } from '../../languageHandler';
import HomePage from "./HomePage";
export default class UserView extends React.Component {
static get propTypes() {
@ -79,7 +80,7 @@ export default class UserView extends React.Component {
const RightPanel = sdk.getComponent('structures.RightPanel');
const MainSplit = sdk.getComponent('structures.MainSplit');
const panel = <RightPanel user={this.state.member} />;
return (<MainSplit panel={panel}><div style={{flex: "1"}} /></MainSplit>);
return (<MainSplit panel={panel}><HomePage /></MainSplit>);
} else {
return (<div />);
}

View file

@ -355,7 +355,8 @@ export default createReactClass({
ev.preventDefault();
ev.stopPropagation();
const ssoKind = step === 'm.login.sso' ? 'sso' : 'cas';
PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind);
PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind,
this.props.fragmentAfterLogin);
} else {
// Don't intercept - just go through to the register page
this.onRegisterClick(ev);
@ -628,7 +629,9 @@ export default createReactClass({
<SSOButton
className="mx_Login_sso_link mx_Login_submit"
matrixClient={this._loginLogic.createTemporaryClient()}
loginType={loginType} />
loginType={loginType}
fragmentAfterLogin={this.props.fragmentAfterLogin}
/>
</div>
);
},

View file

@ -32,7 +32,7 @@ import * as Lifecycle from '../../../Lifecycle';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import AuthPage from "../../views/auth/AuthPage";
import Login from "../../../Login";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
// Phases
// Show controls to configure server details
@ -243,10 +243,15 @@ export default createReactClass({
});
};
try {
await this._makeRegisterRequest({});
// This should never succeed since we specified an empty
// auth object.
console.log("Expecting 401 from register request but got success!");
// We do the first registration request ourselves to discover whether we need to
// do SSO instead. If we've already started the UI Auth process though, we don't
// need to.
if (!this.state.doingUIAuth) {
await this._makeRegisterRequest({});
// This should never succeed since we specified an empty
// auth object.
console.log("Expecting 401 from register request but got success!");
}
} catch (e) {
if (e.httpStatus === 401) {
this.setState({

View file

@ -18,7 +18,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import {_t} from '../../../languageHandler';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import * as Lifecycle from '../../../Lifecycle';
import Modal from '../../../Modal';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
@ -244,7 +244,9 @@ export default class SoftLogout extends React.Component {
<p>{introText}</p>
<SSOButton
matrixClient={MatrixClientPeg.get()}
loginType={this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso"} />
loginType={this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso"}
fragmentAfterLogin={this.props.fragmentAfterLogin}
/>
</div>
);
}

View file

@ -412,14 +412,14 @@ export const EmailIdentityAuthEntry = createReactClass({
this.props.onPhaseChange(DEFAULT_PHASE);
},
getInitialState: function() {
return {
requestingToken: false,
};
},
render: function() {
if (this.state.requestingToken) {
// This component is now only displayed once the token has been requested,
// so we know the email has been sent. It can also get loaded after the user
// has clicked the validation link if the server takes a while to propagate
// the validation internally. If we're in the session spawned from clicking
// the validation link, we won't know the email address, so if we don't have it,
// assume that the link has been clicked and the server will realise when we poll.
if (this.props.inputs.emailAddress === undefined) {
const Loader = sdk.getComponent("elements.Spinner");
return <Loader />;
} else {

View file

@ -0,0 +1,125 @@
/*
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, {PureComponent, RefCallback, RefObject} from "react";
import classNames from "classnames";
import zxcvbn from "zxcvbn";
import SdkConfig from "../../../SdkConfig";
import withValidation, {IFieldState, IValidationResult} from "../elements/Validation";
import {_t, _td} from "../../../languageHandler";
import Field from "../elements/Field";
interface IProps {
autoFocus?: boolean;
id?: string;
className?: string;
minScore: 0 | 1 | 2 | 3 | 4;
value: string;
fieldRef?: RefCallback<Field> | RefObject<Field>;
label?: string;
labelEnterPassword?: string;
labelStrongPassword?: string;
labelAllowedButUnsafe?: string;
onChange(ev: KeyboardEvent);
onValidate(result: IValidationResult);
}
interface IState {
complexity: zxcvbn.ZXCVBNResult;
}
class PassphraseField extends PureComponent<IProps, IState> {
static defaultProps = {
label: _td("Password"),
labelEnterPassword: _td("Enter password"),
labelStrongPassword: _td("Nice, strong password!"),
labelAllowedButUnsafe: _td("Password is allowed, but unsafe"),
};
state = { complexity: null };
public readonly validate = withValidation<this>({
description: function() {
const complexity = this.state.complexity;
const score = complexity ? complexity.score : 0;
return <progress className="mx_PassphraseField_progress" max={4} value={score} />;
},
rules: [
{
key: "required",
test: ({ value, allowEmpty }) => allowEmpty || !!value,
invalid: () => _t(this.props.labelEnterPassword),
},
{
key: "complexity",
test: async function({ value }) {
if (!value) {
return false;
}
const { scorePassword } = await import('../../../utils/PasswordScorer');
const complexity = scorePassword(value);
this.setState({ complexity });
const safe = complexity.score >= this.props.minScore;
const allowUnsafe = SdkConfig.get()["dangerously_allow_unsafe_and_insecure_passwords"];
return allowUnsafe || safe;
},
valid: function() {
// 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.
if (this.state.complexity.score >= this.props.minScore) {
return _t(this.props.labelStrongPassword);
}
return _t(this.props.labelAllowedButUnsafe);
},
invalid: function() {
const complexity = this.state.complexity;
if (!complexity) {
return null;
}
const { feedback } = complexity;
return feedback.warning || feedback.suggestions[0] || _t("Keep going...");
},
},
],
});
onValidate = async (fieldState: IFieldState) => {
const result = await this.validate(fieldState);
this.props.onValidate(result);
return result;
};
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}
/>
}
}
export default PassphraseField;

View file

@ -29,6 +29,7 @@ import SdkConfig from '../../../SdkConfig';
import { SAFE_LOCALPART_REGEX } from '../../../Registration';
import withValidation from '../elements/Validation';
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import PassphraseField from "./PassphraseField";
const FIELD_EMAIL = 'field_email';
const FIELD_PHONE_NUMBER = 'field_phone_number';
@ -78,7 +79,6 @@ export default createReactClass({
password: this.props.defaultPassword || "",
passwordConfirm: this.props.defaultPassword || "",
passwordComplexity: null,
passwordSafe: false,
};
},
@ -264,65 +264,10 @@ export default createReactClass({
});
},
async onPasswordValidate(fieldState) {
const result = await this.validatePasswordRules(fieldState);
onPasswordValidate(result) {
this.markFieldValid(FIELD_PASSWORD, result.valid);
return result;
},
validatePasswordRules: withValidation({
description: function() {
const complexity = this.state.passwordComplexity;
const score = complexity ? complexity.score : 0;
return <progress
className="mx_AuthBody_passwordScore"
max={PASSWORD_MIN_SCORE}
value={score}
/>;
},
rules: [
{
key: "required",
test: ({ value, allowEmpty }) => allowEmpty || !!value,
invalid: () => _t("Enter password"),
},
{
key: "complexity",
test: async function({ value }) {
if (!value) {
return false;
}
const { scorePassword } = await import('../../../utils/PasswordScorer');
const complexity = scorePassword(value);
const safe = complexity.score >= PASSWORD_MIN_SCORE;
const allowUnsafe = SdkConfig.get()["dangerously_allow_unsafe_and_insecure_passwords"];
this.setState({
passwordComplexity: complexity,
passwordSafe: safe,
});
return allowUnsafe || safe;
},
valid: function() {
// 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.
if (!this.state.passwordSafe) {
return _t("Password is allowed, but unsafe");
}
return _t("Nice, strong password!");
},
invalid: function() {
const complexity = this.state.passwordComplexity;
if (!complexity) {
return null;
}
const { feedback } = complexity;
return feedback.warning || feedback.suggestions[0] || _t("Keep going...");
},
},
],
}),
onPasswordConfirmChange(ev) {
this.setState({
passwordConfirm: ev.target.value,
@ -484,13 +429,10 @@ export default createReactClass({
},
renderPassword() {
const Field = sdk.getComponent('elements.Field');
return <Field
return <PassphraseField
id="mx_RegistrationForm_password"
ref={field => this[FIELD_PASSWORD] = field}
type="password"
autoComplete="new-password"
label={_t("Password")}
fieldRef={field => this[FIELD_PASSWORD] = field}
minScore={PASSWORD_MIN_SCORE}
value={this.state.password}
onChange={this.onPasswordChange}
onValidate={this.onPasswordValidate}

View file

@ -45,7 +45,8 @@ export default class Welcome extends React.PureComponent {
idBaseUrl: isUrl,
});
const plaf = PlatformPeg.get();
const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl());
const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl(),
this.props.fragmentAfterLogin);
return (
<AuthPage>

View file

@ -20,7 +20,8 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as Avatar from '../../../Avatar';
import * as sdk from "../../../index";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import {Action} from "../../../dispatcher/actions";
export default createReactClass({
displayName: 'MemberAvatar',
@ -33,7 +34,7 @@ export default createReactClass({
resizeMethod: PropTypes.string,
// The onClick to give the avatar
onClick: PropTypes.func,
// Whether the onClick of the avatar should be overriden to dispatch 'view_user'
// Whether the onClick of the avatar should be overriden to dispatch `Action.ViewUser`
viewUserOnClick: PropTypes.bool,
title: PropTypes.string,
},
@ -85,7 +86,7 @@ export default createReactClass({
if (viewUserOnClick) {
onClick = () => {
dis.dispatch({
action: 'view_user',
action: Action.ViewUser,
member: this.props.member,
});
};

View file

@ -23,7 +23,7 @@ import createReactClass from 'create-react-class';
import {EventStatus} from 'matrix-js-sdk';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';

View file

@ -24,7 +24,7 @@ import classNames from 'classnames';
import * as sdk from '../../../index';
import { _t, _td } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import DMRoomMap from '../../../utils/DMRoomMap';
import * as Rooms from '../../../Rooms';
import * as RoomNotifs from '../../../RoomNotifs';

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import TagOrderActions from '../../../actions/TagOrderActions';
import * as sdk from '../../../index';
import {MenuItem} from "../../structures/ContextMenu";

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler';
import LogoutDialog from "../dialogs/LogoutDialog";
import Modal from "../../../Modal";
@ -27,6 +27,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
import {MenuItem} from "../../structures/ContextMenu";
import * as sdk from "../../../index";
import {getHomePageUrl} from "../../../utils/pages";
import {Action} from "../../../dispatcher/actions";
export default class TopLeftMenu extends React.Component {
static propTypes = {
@ -134,7 +135,7 @@ export default class TopLeftMenu extends React.Component {
}
openSettings() {
dis.dispatch({action: 'view_user_settings'});
dis.fire(Action.ViewUserSettings);
this.closeMenu();
}

View file

@ -24,7 +24,7 @@ import createReactClass from 'create-react-class';
import { _t, _td } from '../../../languageHandler';
import * as sdk from '../../../index';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { addressTypes, getAddressType } from '../../../UserAddress.js';
import GroupStore from '../../../stores/GroupStore';
import * as Email from '../../../email';
@ -33,6 +33,7 @@ import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../
import { abbreviateUrl } from '../../../utils/UrlUtils';
import {sleep} from "../../../utils/promise";
import {Key} from "../../../Keyboard";
import {Action} from "../../../dispatcher/actions";
const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
@ -615,7 +616,7 @@ export default createReactClass({
onManageSettingsClick(e) {
e.preventDefault();
dis.dispatch({ action: 'view_user_settings' });
dis.fire(Action.ViewUserSettings);
this.onCancel();
},

View file

@ -18,7 +18,7 @@ import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';

View file

@ -16,7 +16,7 @@ limitations under the License.
import React from 'react';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';

View file

@ -34,6 +34,7 @@ export default class DeactivateAccountDialog extends React.Component {
shouldErase: false,
errStr: null,
authData: null, // for UIA
authEnabled: true, // see usages for information
// A few strings that are passed to InteractiveAuth for design or are displayed
// next to the InteractiveAuth component.
@ -42,21 +43,7 @@ export default class DeactivateAccountDialog extends React.Component {
continueKind: null,
};
MatrixClientPeg.get().deactivateAccount(null, false).then(r => {
// If we got here, oops. The server didn't require any auth.
// Our application lifecycle will catch the error and do the logout bits.
// We'll try to log something in an vain attempt to record what happened (storage
// is also obliterated on logout).
console.warn("User's account got deactivated without confirmation: Server had no auth");
this.setState({errStr: _t("Server did not require any authentication")});
}).catch(e => {
if (e && e.httpStatus === 401 && e.data) {
// Valid UIA response
this.setState({authData: e.data});
} else {
this.setState({errStr: _t("Server did not return valid authentication information.")});
}
});
this._initAuth(/* shouldErase= */false);
}
_onStagePhaseChange = (stage, phase) => {
@ -124,13 +111,40 @@ export default class DeactivateAccountDialog extends React.Component {
_onEraseFieldChange = (ev) => {
this.setState({
shouldErase: ev.target.checked,
// Disable the auth form because we're going to have to reinitialize the auth
// information. We do this because we can't modify the parameters in the UIA
// session, and the user will have selected something which changes the request.
// Therefore, we throw away the last auth session and try a new one.
authEnabled: false,
});
// As mentioned above, set up for auth again to get updated UIA session info
this._initAuth(/* shouldErase= */ev.target.checked);
};
_onCancel() {
this.props.onFinished(false);
}
_initAuth(shouldErase) {
MatrixClientPeg.get().deactivateAccount(null, shouldErase).then(r => {
// If we got here, oops. The server didn't require any auth.
// Our application lifecycle will catch the error and do the logout bits.
// We'll try to log something in an vain attempt to record what happened (storage
// is also obliterated on logout).
console.warn("User's account got deactivated without confirmation: Server had no auth");
this.setState({errStr: _t("Server did not require any authentication")});
}).catch(e => {
if (e && e.httpStatus === 401 && e.data) {
// Valid UIA response
this.setState({authData: e.data, authEnabled: true});
} else {
this.setState({errStr: _t("Server did not return valid authentication information.")});
}
});
}
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
@ -142,7 +156,7 @@ export default class DeactivateAccountDialog extends React.Component {
}
let auth = <div>{_t("Loading...")}</div>;
if (this.state.authData) {
if (this.state.authData && this.state.authEnabled) {
auth = (
<div>
{this.state.bodyText}

View file

@ -25,7 +25,7 @@ import * as FormattingUtils from '../../../utils/FormattingUtils';
import { _t } from '../../../languageHandler';
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import {ensureDMExists} from "../../../createRoom";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import SettingsStore from '../../../settings/SettingsStore';
import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
import VerificationQREmojiOptions from "../verification/VerificationQREmojiOptions";

View file

@ -18,7 +18,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import {_t} from "../../../languageHandler";
import * as sdk from "../../../index";
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import {Action} from "../../../dispatcher/actions";
export default class IntegrationsDisabledDialog extends React.Component {
static propTypes = {
@ -31,7 +32,7 @@ export default class IntegrationsDisabledDialog extends React.Component {
_onOpenSettingsClick = () => {
this.props.onFinished();
dis.dispatch({action: "view_user_settings"});
dis.fire(Action.ViewUserSettings);
};
render() {

View file

@ -27,7 +27,7 @@ import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
import * as Email from "../../../email";
import {getDefaultIdentityServerUrl, useDefaultIdentityServer} from "../../../utils/IdentityServerUtils";
import {abbreviateUrl} from "../../../utils/UrlUtils";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import IdentityAuthClient from "../../../IdentityAuthClient";
import Modal from "../../../Modal";
import {humanizeTime} from "../../../utils/humanize";
@ -36,6 +36,7 @@ import {inviteMultipleToRoom} from "../../../RoomInvite";
import SettingsStore from '../../../settings/SettingsStore';
import RoomListStore, {TAG_DM} from "../../../stores/RoomListStore";
import {Key} from "../../../Keyboard";
import {Action} from "../../../dispatcher/actions";
export const KIND_DM = "dm";
export const KIND_INVITE = "invite";
@ -902,7 +903,7 @@ export default class InviteDialog extends React.PureComponent {
_onManageSettingsClick = (e) => {
e.preventDefault();
dis.dispatch({ action: 'view_user_settings' });
dis.fire(Action.ViewUserSettings);
this.props.onFinished();
};

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';

View file

@ -27,7 +27,7 @@ import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsT
import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab";
import * as sdk from "../../../index";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import SettingsStore from "../../../settings/SettingsStore";
export default class RoomSettingsDialog extends React.Component {

View file

@ -24,7 +24,7 @@ import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import QRCode from 'qrcode-react';
import QRCode from "../elements/QRCode";
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
import * as ContextMenu from "../../structures/ContextMenu";
import {toRightOf} from "../../structures/ContextMenu";
@ -221,7 +221,7 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
<div className="mx_ShareDialog_split">
<div className="mx_ShareDialog_qrcode_container">
<QRCode value={matrixToUrl} size={256} logoWidth={48} logo={require("../../../../res/img/matrix-m.svg")} />
<QRCode data={matrixToUrl} width={256} />
</div>
<div className="mx_ShareDialog_social_container">
{ socials.map((social) => (

View file

@ -18,7 +18,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import AccessibleButton from './AccessibleButton';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import * as sdk from '../../../index';
import Analytics from '../../../Analytics';

View file

@ -31,7 +31,7 @@ import AppPermission from './AppPermission';
import AppWarning from './AppWarning';
import MessageSpinner from './MessageSpinner';
import WidgetUtils from '../../../utils/WidgetUtils';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
import classNames from 'classnames';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";

View file

@ -19,7 +19,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import FlairStore from '../../../stores/FlairStore';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import MatrixClientContext from "../../../contexts/MatrixClientContext";

View file

@ -20,7 +20,7 @@ import PropTypes from 'prop-types';
import ResizeObserver from 'resize-observer-polyfill';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
// Shamelessly ripped off Modal.js. There's probably a better way
// of doing reusable widgets like dialog boxes & menus where we go and

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import classNames from 'classnames';
import { Room, RoomMember } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
@ -26,6 +26,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
import FlairStore from "../../../stores/FlairStore";
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {Action} from "../../../dispatcher/actions";
// For URLs of matrix.to links in the timeline which have been reformatted by
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
@ -191,7 +192,7 @@ const Pill = createReactClass({
onUserPillClicked: function() {
dis.dispatch({
action: 'view_user',
action: Action.ViewUser,
member: this.state.member,
});
},

View file

@ -0,0 +1,51 @@
/*
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 * as React from "react";
import {toDataURL, QRCodeSegment, QRCodeToDataURLOptions} from "qrcode";
import classNames from "classnames";
import {_t} from "../../../languageHandler";
import Spinner from "./Spinner";
interface IProps extends QRCodeToDataURLOptions {
data: string | QRCodeSegment[];
className?: string;
}
const defaultOptions: QRCodeToDataURLOptions = {
errorCorrectionLevel: 'L', // we want it as trivial-looking as possible
};
const QRCode: React.FC<IProps> = ({data, className, ...options}) => {
const [dataUri, setUri] = React.useState<string>(null);
React.useEffect(() => {
let cancelled = false;
toDataURL(data, {...defaultOptions, ...options}).then(uri => {
if (cancelled) return;
setUri(uri);
});
return () => {
cancelled = true;
};
}, [JSON.stringify(data), options]);
return <div className={classNames("mx_QRCode", className)}>
{ dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> }
</div>;
};
export default QRCode;

View file

@ -19,7 +19,7 @@ import React from 'react';
import * as sdk from '../../../index';
import {_t} from '../../../languageHandler';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import {wantsDateSeparator} from '../../../DateUtils';
import {MatrixEvent} from 'matrix-js-sdk';
import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";

View file

@ -21,9 +21,9 @@ import PlatformPeg from "../../../PlatformPeg";
import AccessibleButton from "./AccessibleButton";
import {_t} from "../../../languageHandler";
const SSOButton = ({matrixClient, loginType, ...props}) => {
const SSOButton = ({matrixClient, loginType, fragmentAfterLogin, ...props}) => {
const onClick = () => {
PlatformPeg.get().startSingleSignOn(matrixClient, loginType);
PlatformPeg.get().startSingleSignOn(matrixClient, loginType, fragmentAfterLogin);
};
return (
@ -36,6 +36,7 @@ const SSOButton = ({matrixClient, loginType, ...props}) => {
SSOButton.propTypes = {
matrixClient: PropTypes.object.isRequired, // does not use context as may use a temporary client
loginType: PropTypes.oneOf(["sso", "cas"]), // defaults to "sso" in base-apis
fragmentAfterLogin: PropTypes.string,
};
export default SSOButton;

View file

@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { isOnlyCtrlOrCmdIgnoreShiftKeyEvent } from '../../../Keyboard';
import * as FormattingUtils from '../../../utils/FormattingUtils';

View file

@ -22,7 +22,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import classNames from 'classnames';
const MIN_TOOLTIP_HEIGHT = 25;

View file

@ -1,5 +1,6 @@
/*
Copyright 2019 New Vector Ltd
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.
@ -15,11 +16,39 @@ limitations under the License.
*/
/* eslint-disable babel/no-invalid-this */
import React from "react";
import classNames from "classnames";
import classNames from 'classnames';
type Data = Pick<IFieldState, "value" | "allowEmpty">;
interface IRule<T> {
key: string;
final?: boolean;
skip?(this: T, data: Data): boolean;
test(this: T, data: Data): boolean | Promise<boolean>;
valid?(this: T): string;
invalid?(this: T): string;
}
interface IArgs<T> {
rules: IRule<T>[];
description(this: T): React.ReactChild;
}
export interface IFieldState {
value: string;
focused: boolean;
allowEmpty: boolean;
}
export interface IValidationResult {
valid?: boolean;
feedback?: React.ReactChild;
}
/**
* Creates a validation function from a set of rules describing what to validate.
* Generic T is the "this" type passed to the rule methods
*
* @param {Function} description
* Function that returns a string summary of the kind of value that will
@ -37,8 +66,8 @@ import classNames from 'classnames';
* A validation function that takes in the current input value and returns
* the overall validity and a feedback UI that can be rendered for more detail.
*/
export default function withValidation({ description, rules }) {
return async function onValidate({ value, focused, allowEmpty = true }) {
export default function withValidation<T = undefined>({ description, rules }: IArgs<T>) {
return async function onValidate({ value, focused, allowEmpty = true }: IFieldState): Promise<IValidationResult> {
if (!value && allowEmpty) {
return {
valid: null,

View file

@ -17,8 +17,7 @@ limitations under the License.
import React from "react";
import PropTypes from "prop-types";
import {replaceableComponent} from "../../../../utils/replaceableComponent";
import Spinner from "../Spinner";
import * as QRCode from "qrcode";
import QRCode from "../QRCode";
@replaceableComponent("views.elements.crypto.VerificationQRCode")
export default class VerificationQRCode extends React.PureComponent {
@ -26,33 +25,12 @@ export default class VerificationQRCode extends React.PureComponent {
qrCodeData: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
dataUri: null,
};
this.generateQrCode();
}
componentDidUpdate(prevProps): void {
if (JSON.stringify(this.props) === JSON.stringify(prevProps)) return; // No prop change
this.generateQRCode();
}
async generateQrCode() {
// Now actually assemble the QR code's data URI
const uri = await QRCode.toDataURL([{data: this.props.qrCodeData.buffer, mode: 'byte'}], {
errorCorrectionLevel: 'L', // we want it as trivial-looking as possible
});
this.setState({dataUri: uri});
}
render() {
if (!this.state.dataUri) {
return <div className='mx_VerificationQRCode'><Spinner /></div>;
}
return <img src={this.state.dataUri} className='mx_VerificationQRCode' />;
return (
<QRCode
data={[{data: this.props.qrCodeData.buffer, mode: 'byte'}]}
className="mx_VerificationQRCode"
width={196} />
);
}
}

View file

@ -16,7 +16,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import Analytics from '../../../Analytics';

View file

@ -20,7 +20,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import {_t} from '../../../languageHandler';
import classNames from 'classnames';
import {MatrixClientPeg} from "../../../MatrixClientPeg";

View file

@ -19,7 +19,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
@ -28,6 +28,7 @@ import GroupStore from '../../../stores/GroupStore';
import AccessibleButton from '../elements/AccessibleButton';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import {Action} from "../../../dispatcher/actions";
export default createReactClass({
displayName: 'GroupMemberInfo',
@ -103,7 +104,7 @@ export default createReactClass({
).then(() => {
// return to the user list
dis.dispatch({
action: "view_user",
action: Action.ViewUser,
member: null,
});
}).catch((e) => {
@ -124,7 +125,7 @@ export default createReactClass({
_onCancel: function(e) {
// Go back to the user list
dis.dispatch({
action: "view_user",
action: Action.ViewUser,
member: null,
});
},

View file

@ -19,7 +19,7 @@ import React from 'react';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import GroupStore from '../../../stores/GroupStore';
import PropTypes from 'prop-types';
import { showGroupInviteDialog } from '../../../GroupAddressPicker';

View file

@ -20,7 +20,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { GroupMemberType } from '../../../groups';
import MatrixClientContext from "../../../contexts/MatrixClientContext";

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';

View file

@ -18,7 +18,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { GroupRoomType } from '../../../groups';
import MatrixClientContext from "../../../contexts/MatrixClientContext";

View file

@ -19,7 +19,7 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { Draggable, Droppable } from 'react-beautiful-dnd';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import FlairStore from '../../../stores/FlairStore';
import MatrixClientContext from "../../../contexts/MatrixClientContext";

View file

@ -21,7 +21,7 @@ import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import {getNameForEventRoom, userLabelForEventRoom}
from '../../../utils/KeyVerificationStateObserver';
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
export default class MKeyVerificationRequest extends React.Component {

View file

@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import Modal from '../../../Modal';
import {aboveLeftOf, ContextMenu, ContextMenuButton, useContextMenu} from '../../structures/ContextMenu';
import { isContentActionable, canEditContent } from '../../../utils/EventUtils';

View file

@ -19,7 +19,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';

View file

@ -25,7 +25,7 @@ import * as HtmlUtils from '../../../HtmlUtils';
import {formatDate} from '../../../DateUtils';
import * as sdk from '../../../index';
import Modal from '../../../Modal';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler';
import * as ContextMenu from '../../structures/ContextMenu';
import SettingsStore from "../../../settings/SettingsStore";

View file

@ -23,6 +23,8 @@ import { _t } from '../../../languageHandler';
import HeaderButton from './HeaderButton';
import HeaderButtons, {HEADER_KIND_GROUP} from './HeaderButtons';
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
import {Action} from "../../../dispatcher/actions";
import {ActionPayload} from "../../../dispatcher/payloads";
const GROUP_PHASES = [
RIGHT_PANEL_PHASES.GroupMemberInfo,
@ -40,10 +42,10 @@ export default class GroupHeaderButtons extends HeaderButtons {
this._onRoomsClicked = this._onRoomsClicked.bind(this);
}
onAction(payload) {
onAction(payload: ActionPayload) {
super.onAction(payload);
if (payload.action === "view_user") {
if (payload.action === Action.ViewUser) {
if (payload.member) {
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
} else {

View file

@ -19,7 +19,7 @@ limitations under the License.
*/
import React from 'react';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import RightPanelStore from "../../../stores/RightPanelStore";
export const HEADER_KIND_ROOM = "room";

View file

@ -23,6 +23,8 @@ import { _t } from '../../../languageHandler';
import HeaderButton from './HeaderButton';
import HeaderButtons, {HEADER_KIND_ROOM} from './HeaderButtons';
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
import {Action} from "../../../dispatcher/actions";
import {ActionPayload} from "../../../dispatcher/payloads";
const MEMBER_PHASES = [
RIGHT_PANEL_PHASES.RoomMemberList,
@ -39,9 +41,9 @@ export default class RoomHeaderButtons extends HeaderButtons {
this._onNotificationsClicked = this._onNotificationsClicked.bind(this);
}
onAction(payload) {
onAction(payload: ActionPayload) {
super.onAction(payload);
if (payload.action === "view_user") {
if (payload.action === Action.ViewUser) {
if (payload.member) {
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
} else {

View file

@ -21,7 +21,7 @@ import React, {useCallback, useMemo, useState, useEffect, useContext} from 'reac
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {Group, RoomMember, User} from 'matrix-js-sdk';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
@ -44,6 +44,7 @@ import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
import EncryptionPanel from "./EncryptionPanel";
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification';
import {Action} from "../../../dispatcher/actions";
const _disambiguateDevices = (devices) => {
const names = Object.create(null);
@ -841,7 +842,7 @@ const GroupAdminToolsSection = ({children, groupId, groupMember, startUpdating,
cli.removeUserFromGroup(groupId, groupMember.userId).then(() => {
// return to the user list
dis.dispatch({
action: "view_user",
action: Action.ViewUser,
member: null,
});
}).catch((e) => {

View file

@ -19,7 +19,7 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import Tinter from '../../../Tinter';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
const ROOM_COLORS = [

View file

@ -23,8 +23,9 @@ import createReactClass from 'create-react-class';
import * as sdk from "../../../index";
import { _t, _td } from '../../../languageHandler';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {Action} from "../../../dispatcher/actions";
export default createReactClass({
@ -37,7 +38,7 @@ export default createReactClass({
_onClickUserSettings: (e) => {
e.preventDefault();
e.stopPropagation();
dis.dispatch({action: 'view_user_settings'});
dis.fire(Action.ViewUserSettings);
},
render: function() {

View file

@ -21,7 +21,7 @@ import createReactClass from 'create-react-class';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import AppTile from '../elements/AppTile';
import Modal from '../../../Modal';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import * as sdk from '../../../index';
import * as ScalarMessaging from '../../../ScalarMessaging';
import { _t } from '../../../languageHandler';

View file

@ -20,7 +20,7 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import * as sdk from '../../../index';
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import * as ObjectUtils from '../../../ObjectUtils';
import AppsDrawer from './AppsDrawer';
import { _t } from '../../../languageHandler';

View file

@ -18,7 +18,7 @@ import React from 'react';
import * as sdk from '../../../index';
import {_t} from '../../../languageHandler';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import EditorModel from '../../../editor/model';
import {getCaretOffsetAndText} from '../../../editor/dom';
import {htmlSerializeIfNeeded, textSerialize, containsEmote, stripEmoteCommand} from '../../../editor/serialize';

View file

@ -25,7 +25,7 @@ import classNames from "classnames";
import { _t, _td } from '../../../languageHandler';
import * as TextForEvent from "../../../TextForEvent";
import * as sdk from "../../../index";
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import SettingsStore from "../../../settings/SettingsStore";
import {EventStatus} from 'matrix-js-sdk';
import {formatTime} from "../../../DateUtils";

View file

@ -19,7 +19,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import {Key} from '../../../Keyboard';

View file

@ -31,7 +31,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
@ -48,6 +48,7 @@ import E2EIcon from "./E2EIcon";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {Action} from "../../../dispatcher/actions";
export default createReactClass({
displayName: 'MemberInfo',
@ -724,7 +725,7 @@ export default createReactClass({
onCancel: function(e) {
dis.dispatch({
action: "view_user",
action: Action.ViewUser,
member: null,
});
},

View file

@ -20,7 +20,7 @@ import React from 'react';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import {isValid3pidInvite} from "../../../RoomInvite";
import rate_limited_func from "../../../ratelimitedfunc";

View file

@ -20,9 +20,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from "../../../index";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import {Action} from "../../../dispatcher/actions";
export default createReactClass({
displayName: 'MemberTile',
@ -185,7 +186,7 @@ export default createReactClass({
onClick: function(e) {
dis.dispatch({
action: 'view_user',
action: Action.ViewUser,
member: this.props.member,
});
},

View file

@ -20,7 +20,7 @@ import { _t } from '../../../languageHandler';
import CallHandler from '../../../CallHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import RoomViewStore from '../../../stores/RoomViewStore';
import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
@ -114,8 +114,19 @@ class UploadButton extends React.Component {
this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
this._uploadInput = createRef();
this._dispatcherRef = dis.register(this.onAction);
}
componentWillUnmount() {
dis.unregister(this._dispatcherRef);
}
onAction = payload => {
if (payload.action === "upload_file") {
this.onUploadClick();
}
};
onUploadClick(ev) {
if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({action: 'require_registration'});
@ -128,7 +139,7 @@ class UploadButton extends React.Component {
if (ev.target.files.length === 0) return;
// take a copy so we can safely reset the value of the form control
// (Note it is a FileList: we can't use slice or sesnible iteration).
// (Note it is a FileList: we can't use slice or sensible iteration).
const tfiles = [];
for (let i = 0; i < ev.target.files.length; ++i) {
tfiles.push(ev.target.files[i]);

View file

@ -18,7 +18,7 @@ import React from "react";
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import AccessibleButton from "../elements/AccessibleButton";
import MessageEvent from "../messages/MessageEvent";
import MemberAvatar from "../avatars/MemberAvatar";

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React from 'react';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import RoomViewStore from '../../../stores/RoomViewStore';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React, {createRef} from "react";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import AccessibleButton from '../elements/AccessibleButton';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import React from 'react';
import { _t } from '../../../languageHandler';
import PropTypes from 'prop-types';

View file

@ -35,7 +35,7 @@ import GroupStore from '../../../stores/GroupStore';
import RoomSubList from '../../structures/RoomSubList';
import ResizeHandle from '../elements/ResizeHandle';
import CallHandler from "../../../CallHandler";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import * as sdk from "../../../index";
import * as Receipt from "../../../utils/Receipt";
import {Resizer} from '../../../resizer';
@ -785,6 +785,7 @@ export default createReactClass({
label: _t('Rooms'),
incomingCall: incomingCallIfTaggedAs('im.vector.fake.recent'),
onAddRoom: () => {dis.dispatch({action: 'view_create_room'});},
addRoomLabel: _t("Create room"),
},
];
const tagSubLists = Object.keys(this.state.lists)

View file

@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import classNames from 'classnames';
import { _t } from '../../../languageHandler';
import IdentityAuthClient from '../../../IdentityAuthClient';

View file

@ -21,7 +21,7 @@ import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import DMRoomMap from '../../../utils/DMRoomMap';
import * as sdk from '../../../index';

View file

@ -16,7 +16,7 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import EditorModel from '../../../editor/model';
import {
htmlSerializeIfNeeded,

View file

@ -18,7 +18,7 @@ import {_t, _td} from '../../../languageHandler';
import AppTile from '../elements/AppTile';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import AccessibleButton from '../elements/AccessibleButton';
import WidgetUtils from '../../../utils/WidgetUtils';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';

View file

@ -19,7 +19,7 @@ import PropTypes from 'prop-types';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {MatrixEvent} from "matrix-js-sdk";
import {_t} from "../../../languageHandler";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import * as sdk from "../../../index";
import Modal from "../../../Modal";
import {isValid3pidInvite} from "../../../RoomInvite";

View file

@ -20,7 +20,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import AccessibleButton from '../elements/AccessibleButton';
import { _t } from '../../../languageHandler';
import * as sdk from "../../../index";

View file

@ -36,7 +36,6 @@ export default class CrossSigningPanel extends React.PureComponent {
userSigningPrivateKeyCached: false,
sessionBackupKeyCached: false,
secretStorageKeyInAccount: false,
secretStorageKeyNeedsUpgrade: null,
};
}
@ -88,7 +87,6 @@ export default class CrossSigningPanel extends React.PureComponent {
const homeserverSupportsCrossSigning =
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
const crossSigningReady = await cli.isCrossSigningReady();
const secretStorageKeyNeedsUpgrade = await cli.secretStorageKeyNeedsUpgrade();
this.setState({
crossSigningPublicKeysOnDevice,
@ -100,7 +98,6 @@ export default class CrossSigningPanel extends React.PureComponent {
secretStorageKeyInAccount,
homeserverSupportsCrossSigning,
crossSigningReady,
secretStorageKeyNeedsUpgrade,
});
}
@ -150,7 +147,6 @@ export default class CrossSigningPanel extends React.PureComponent {
secretStorageKeyInAccount,
homeserverSupportsCrossSigning,
crossSigningReady,
secretStorageKeyNeedsUpgrade,
} = this.state;
let errorSection;
@ -259,10 +255,6 @@ export default class CrossSigningPanel extends React.PureComponent {
<td>{_t("Homeserver feature support:")}</td>
<td>{homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}</td>
</tr>
<tr>
<td>{_t("Secret Storage key format:")}</td>
<td>{secretStorageKeyNeedsUpgrade ? _t("outdated") : _t("up to date")}</td>
</tr>
</tbody></table>
</details>
{errorSection}

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from "react";
import createReactClass from 'create-react-class';
import Notifier from "../../../Notifier";
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import { _t } from '../../../languageHandler';
export default createReactClass({

View file

@ -19,7 +19,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher';
import dis from '../../../dispatcher/dispatcher';
import {Key} from "../../../Keyboard";
export default class IntegrationManager extends React.Component {

View file

@ -21,7 +21,7 @@ import {_t} from "../../../languageHandler";
import * as sdk from '../../../index';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import Modal from '../../../Modal';
import dis from "../../../dispatcher";
import dis from "../../../dispatcher/dispatcher";
import { getThreepidsWithBindStatus } from '../../../boundThreepids';
import IdentityAuthClient from "../../../IdentityAuthClient";
import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils";

View file

@ -21,7 +21,7 @@ import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
import * as sdk from "../../../../..";
import AccessibleButton from "../../../elements/AccessibleButton";
import Modal from "../../../../../Modal";
import dis from "../../../../../dispatcher";
import dis from "../../../../../dispatcher/dispatcher";
export default class AdvancedRoomSettingsTab extends React.Component {
static propTypes = {

View file

@ -20,7 +20,7 @@ import {_t} from "../../../../../languageHandler";
import RoomProfileSettings from "../../../room_settings/RoomProfileSettings";
import * as sdk from "../../../../..";
import AccessibleButton from "../../../elements/AccessibleButton";
import dis from "../../../../../dispatcher";
import dis from "../../../../../dispatcher/dispatcher";
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
export default class GeneralRoomSettingsTab extends React.Component {

View file

@ -32,7 +32,7 @@ import PlatformPeg from "../../../../../PlatformPeg";
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
import * as sdk from "../../../../..";
import Modal from "../../../../../Modal";
import dis from "../../../../../dispatcher";
import dis from "../../../../../dispatcher/dispatcher";
import {Service, startTermsFlow} from "../../../../../Terms";
import {SERVICE_TYPES} from "matrix-js-sdk";
import IdentityAuthClient from "../../../../../IdentityAuthClient";
@ -348,7 +348,7 @@ export default class GeneralUserSettingsTab extends React.Component {
// For newer homeservers with separate 3PID add and bind methods (MSC2290),
// there is no such concern, so we can always show the HS account 3PIDs.
if (this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true) {
const emails = this.state.loading3pids || true
const emails = this.state.loading3pids
? <Spinner />
: <EmailAddresses
emails={this.state.emails}

View file

@ -25,7 +25,7 @@ import Analytics from "../../../../../Analytics";
import Modal from "../../../../../Modal";
import * as sdk from "../../../../..";
import {sleep} from "../../../../../utils/promise";
import dis from "../../../../../dispatcher";
import dis from "../../../../../dispatcher/dispatcher";
export class IgnoredUser extends React.Component {
static propTypes = {

Some files were not shown because too many files have changed in this diff Show more