diff --git a/package.json b/package.json index 193fb86218..440fe3beac 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "@types/classnames": "^2.2.10", "@types/flux": "^3.1.9", "@types/modernizr": "^3.5.3", + "@types/node": "^12.12.41", "@types/qrcode": "^1.3.4", "@types/react": "16.9", "@types/zxcvbn": "^4.4.0", diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index e6e339d067..6c62000143 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -15,6 +15,7 @@ limitations under the License. */ import * as ModernizrStatic from "modernizr"; +import ToastStore from "../stores/ToastStore"; declare global { interface Window { @@ -22,6 +23,8 @@ declare global { Olm: { init: () => Promise; }; + + mx_ToastStore: ToastStore; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 48dc72f4fa..89db30c7b4 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1559,7 +1559,7 @@ export default class MatrixChat extends React.PureComponent { icon: "verification", props: {request}, component: sdk.getComponent("toasts.VerificationRequestToast"), - priority: ToastStore.PRIORITY_REALTIME, + priority: 95, }); } }); diff --git a/src/components/structures/ToastContainer.js b/src/components/structures/ToastContainer.tsx similarity index 87% rename from src/components/structures/ToastContainer.js rename to src/components/structures/ToastContainer.tsx index 283fbdd96a..9440aa3463 100644 --- a/src/components/structures/ToastContainer.js +++ b/src/components/structures/ToastContainer.tsx @@ -16,13 +16,19 @@ limitations under the License. import * as React from "react"; import { _t } from '../../languageHandler'; -import ToastStore from "../../stores/ToastStore"; +import ToastStore, {IToast} from "../../stores/ToastStore"; import classNames from "classnames"; -export default class ToastContainer extends React.Component { - constructor() { - super(); - this.state = {toasts: ToastStore.sharedInstance().getToasts()}; +interface IState { + toasts: IToast[]; +} + +export default class ToastContainer extends React.Component<{}, IState> { + constructor(props, context) { + super(props, context); + this.state = { + toasts: ToastStore.sharedInstance().getToasts(), + }; // Start listening here rather than in componentDidMount because // toasts may dismiss themselves in their didMount if they find diff --git a/src/stores/ToastStore.js b/src/stores/ToastStore.js deleted file mode 100644 index 8901736739..0000000000 --- a/src/stores/ToastStore.js +++ /dev/null @@ -1,73 +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 EventEmitter from 'events'; - -/** - * Holds the active toasts - */ -export default class ToastStore extends EventEmitter { - static PRIORITY_REALTIME = 0; - static PRIORITY_DEFAULT = 1; - static PRIORITY_LOW = 2; - - static sharedInstance() { - if (!global.mx_ToastStore) global.mx_ToastStore = new ToastStore(); - return global.mx_ToastStore; - } - - constructor() { - super(); - this._dispatcherRef = null; - this._toasts = []; - } - - reset() { - this._toasts = []; - } - - /** - * Add or replace a toast - * If a toast with the same toastKey already exists, the given toast will replace it - * Toasts are always added underneath any toasts of the same priority, so existing - * toasts stay at the top unless a higher priority one arrives (better to not change the - * toast unless necessary). - * - * @param {boject} newToast The new toast - */ - addOrReplaceToast(newToast) { - if (newToast.priority === undefined) newToast.priority = ToastStore.PRIORITY_DEFAULT; - - const oldIndex = this._toasts.findIndex(t => t.key === newToast.key); - if (oldIndex === -1) { - let newIndex = this._toasts.length; - while (newIndex > 0 && this._toasts[newIndex - 1].priority > newToast.priority) --newIndex; - this._toasts.splice(newIndex, 0, 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; - } -} diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts new file mode 100644 index 0000000000..4f6d2963c5 --- /dev/null +++ b/src/stores/ToastStore.ts @@ -0,0 +1,77 @@ +/* +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"; +import React, {JSXElementConstructor} from "react"; + +export interface IToast> { + key: string; + // higher priority number will be shown on top of lower priority + priority: number; + title: string; + icon?: string; + component: C; + props?: React.ComponentProps; +} + +/** + * Holds the active toasts + */ +export default class ToastStore extends EventEmitter { + private toasts: IToast[] = []; + + static sharedInstance() { + if (!window.mx_ToastStore) window.mx_ToastStore = new ToastStore(); + return window.mx_ToastStore; + } + + reset() { + this.toasts = []; + } + + /** + * Add or replace a toast + * If a toast with the same toastKey already exists, the given toast will replace it + * Toasts are always added underneath any toasts of the same priority, so existing + * toasts stay at the top unless a higher priority one arrives (better to not change the + * toast unless necessary). + * + * @param {object} newToast The new toast + */ + addOrReplaceToast>(newToast: IToast) { + const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); + if (oldIndex === -1) { + let newIndex = this.toasts.length; + while (newIndex > 0 && this.toasts[newIndex - 1].priority > newToast.priority) --newIndex; + this.toasts.splice(newIndex, 0, newToast); + } else { + this.toasts[oldIndex] = newToast; + } + this.emit('update'); + } + + dismissToast(key) { + const length = this.toasts.length; + this.toasts = this.toasts.filter(t => t.key !== key); + if (length !== this.toasts.length) { + this.emit('update'); + } + } + + getToasts() { + return this.toasts; + } +} diff --git a/yarn.lock b/yarn.lock index 6cdc771c5b..340d0b4454 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1280,6 +1280,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" integrity sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ== +"@types/node@^12.12.41": + version "12.12.42" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.42.tgz#d0d1149336bd07540dd1ea576692829d575dec34" + integrity sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg== + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"