Add a ToastStore
To store toast. Rather than them being stored in the state of the ToastContainer component, they now have a dedicated store. This mostly fixes problems involving showing toasts when the app loaded because we would otherwise have a race condition where something tries to show a toast before the ToastContainer is mounted.
This commit is contained in:
parent
3ed7beac78
commit
83b1505401
4 changed files with 71 additions and 31 deletions
|
@ -64,6 +64,7 @@ import { ThemeWatcher } from "../../theme";
|
||||||
import { storeRoomAliasInCache } from '../../RoomAliasCache';
|
import { storeRoomAliasInCache } from '../../RoomAliasCache';
|
||||||
import { defer } from "../../utils/promise";
|
import { defer } from "../../utils/promise";
|
||||||
import KeyVerificationStateObserver from '../../utils/KeyVerificationStateObserver';
|
import KeyVerificationStateObserver from '../../utils/KeyVerificationStateObserver';
|
||||||
|
import ToastStore from "../../stores/ToastStore";
|
||||||
|
|
||||||
/** constants for MatrixChat.state.view */
|
/** constants for MatrixChat.state.view */
|
||||||
export const VIEWS = {
|
export const VIEWS = {
|
||||||
|
@ -1458,15 +1459,12 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!requestObserver || requestObserver.pending) {
|
if (!requestObserver || requestObserver.pending) {
|
||||||
dis.dispatch({
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
action: "show_toast",
|
key: 'verifreq_' + request.event.getId(),
|
||||||
toast: {
|
|
||||||
key: request.event.getId(),
|
|
||||||
title: _t("Verification Request"),
|
title: _t("Verification Request"),
|
||||||
icon: "verification",
|
icon: "verification",
|
||||||
props: {request, requestObserver},
|
props: {request, requestObserver},
|
||||||
component: sdk.getComponent("toasts.VerificationRequestToast"),
|
component: sdk.getComponent("toasts.VerificationRequestToast"),
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,8 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import dis from "../../dispatcher";
|
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
import ToastStore from "../../stores/ToastStore";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
export default class ToastContainer extends React.Component {
|
export default class ToastContainer extends React.Component {
|
||||||
|
@ -26,26 +26,15 @@ export default class ToastContainer extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this._dispatcherRef = dis.register(this.onAction);
|
ToastStore.sharedInstance().on('update', this._onToastStoreUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
dis.unregister(this._dispatcherRef);
|
ToastStore.sharedInstance().removeListener('update', this._onToastStoreUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction = (payload) => {
|
_onToastStoreUpdate = () => {
|
||||||
if (payload.action === "show_toast") {
|
this.setState({toasts: ToastStore.sharedInstance().getToasts()});
|
||||||
this._addToast(payload.toast);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_addToast(toast) {
|
|
||||||
this.setState({toasts: this.state.toasts.concat(toast)});
|
|
||||||
}
|
|
||||||
|
|
||||||
dismissTopToast = () => {
|
|
||||||
const [, ...remaining] = this.state.toasts;
|
|
||||||
this.setState({toasts: remaining});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -62,8 +51,8 @@ export default class ToastContainer extends React.Component {
|
||||||
const countIndicator = isStacked ? _t(" (1/%(totalCount)s)", {totalCount}) : null;
|
const countIndicator = isStacked ? _t(" (1/%(totalCount)s)", {totalCount}) : null;
|
||||||
|
|
||||||
const toastProps = Object.assign({}, props, {
|
const toastProps = Object.assign({}, props, {
|
||||||
dismiss: this.dismissTopToast,
|
|
||||||
key,
|
key,
|
||||||
|
toastKey: key,
|
||||||
});
|
});
|
||||||
toast = (<div className={toastClasses}>
|
toast = (<div className={toastClasses}>
|
||||||
<h2>{title}{countIndicator}</h2>
|
<h2>{title}{countIndicator}</h2>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||||
import KeyVerificationStateObserver, {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver";
|
import KeyVerificationStateObserver, {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver";
|
||||||
import dis from "../../../dispatcher";
|
import dis from "../../../dispatcher";
|
||||||
|
import ToastStore from "../../../stores/ToastStore";
|
||||||
|
|
||||||
export default class VerificationRequestToast extends React.PureComponent {
|
export default class VerificationRequestToast extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -63,12 +64,12 @@ export default class VerificationRequestToast extends React.PureComponent {
|
||||||
|
|
||||||
_checkRequestIsPending = () => {
|
_checkRequestIsPending = () => {
|
||||||
if (!this.props.requestObserver.pending) {
|
if (!this.props.requestObserver.pending) {
|
||||||
this.props.dismiss();
|
ToastStore.sharedInstance().dismissToast(this.props.toastKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel = () => {
|
cancel = () => {
|
||||||
this.props.dismiss();
|
ToastStore.sharedInstance().dismissToast(this.props.toastKey);
|
||||||
try {
|
try {
|
||||||
this.props.request.cancel();
|
this.props.request.cancel();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -77,7 +78,7 @@ export default class VerificationRequestToast extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
accept = () => {
|
accept = () => {
|
||||||
this.props.dismiss();
|
ToastStore.sharedInstance().dismissToast(this.props.toastKey);
|
||||||
const {event} = this.props.request;
|
const {event} = this.props.request;
|
||||||
// no room id for to_device requests
|
// no room id for to_device requests
|
||||||
if (event.getRoomId()) {
|
if (event.getRoomId()) {
|
||||||
|
@ -119,7 +120,7 @@ export default class VerificationRequestToast extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
VerificationRequestToast.propTypes = {
|
VerificationRequestToast.propTypes = {
|
||||||
dismiss: PropTypes.func.isRequired,
|
|
||||||
request: PropTypes.object.isRequired,
|
request: PropTypes.object.isRequired,
|
||||||
requestObserver: PropTypes.instanceOf(KeyVerificationStateObserver),
|
requestObserver: PropTypes.instanceOf(KeyVerificationStateObserver),
|
||||||
|
toastKey: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
52
src/stores/ToastStore.js
Normal file
52
src/stores/ToastStore.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
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 EventEmitter from 'events';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the active toasts
|
||||||
|
*/
|
||||||
|
export default class ToastStore extends EventEmitter {
|
||||||
|
static sharedInstance() {
|
||||||
|
if (!global.mx_ToastStore) global.mx_ToastStore = new ToastStore();
|
||||||
|
return global.mx_ToastStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._dispatcherRef = null;
|
||||||
|
this._toasts = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addOrReplaceToast(newToast) {
|
||||||
|
const oldIndex = this._toasts.findIndex(t => t.key === newToast.key);
|
||||||
|
if (oldIndex === -1) {
|
||||||
|
this._toasts.push(newToast);
|
||||||
|
} else {
|
||||||
|
this._toasts[oldIndex] = newToast;
|
||||||
|
}
|
||||||
|
this.emit('update');
|
||||||
|
}
|
||||||
|
|
||||||
|
dismissToast(key) {
|
||||||
|
this._toasts = this._toasts.filter(t => t.key !== key);
|
||||||
|
this.emit('update');
|
||||||
|
}
|
||||||
|
|
||||||
|
getToasts() {
|
||||||
|
return this._toasts;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue