From 08fadb092c930cc25323bba29ec8f5979f4dd9e2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 21:43:15 +0100 Subject: [PATCH 01/50] Remove redundant component Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../settings/EnableNotificationsButton.js | 75 ------------------- 1 file changed, 75 deletions(-) delete mode 100644 src/components/views/settings/EnableNotificationsButton.js diff --git a/src/components/views/settings/EnableNotificationsButton.js b/src/components/views/settings/EnableNotificationsButton.js deleted file mode 100644 index e4b348dfbd..0000000000 --- a/src/components/views/settings/EnableNotificationsButton.js +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket 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. -*/ - -import React from "react"; -import createReactClass from 'create-react-class'; -import Notifier from "../../../Notifier"; -import dis from "../../../dispatcher/dispatcher"; -import { _t } from '../../../languageHandler'; - -export default createReactClass({ - displayName: 'EnableNotificationsButton', - - componentDidMount: function() { - this.dispatcherRef = dis.register(this.onAction); - }, - - componentWillUnmount: function() { - dis.unregister(this.dispatcherRef); - }, - - onAction: function(payload) { - if (payload.action !== "notifier_enabled") { - return; - } - this.forceUpdate(); - }, - - enabled: function() { - return Notifier.isEnabled(); - }, - - onClick: function() { - const self = this; - if (!Notifier.supportsDesktopNotifications()) { - return; - } - if (!Notifier.isEnabled()) { - Notifier.setEnabled(true, function() { - self.forceUpdate(); - }); - } else { - Notifier.setEnabled(false); - } - this.forceUpdate(); - }, - - render: function() { - if (this.enabled()) { - return ( - - ); - } else { - return ( - - ); - } - }, -}); From a977b8c4ca12570919a1cf5deceaf71652648510 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 21:55:43 +0100 Subject: [PATCH 02/50] Fix lifecycle to reset things before it starts them Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Lifecycle.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 22c5d48317..5fb54ede7f 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -575,10 +575,12 @@ async function startMatrixClient(startSyncing=true) { // to work). dis.dispatch({action: 'will_start_client'}, true); + // reset things first just in case + TypingStore.sharedInstance().reset(); + ToastStore.sharedInstance().reset(); + Notifier.start(); UserActivity.sharedInstance().start(); - TypingStore.sharedInstance().reset(); // just in case - ToastStore.sharedInstance().reset(); DMRoomMap.makeShared().start(); IntegrationManagers.sharedInstance().startWatching(); ActiveWidgetStore.start(); From 3732d1f5a5d0ad2fe22c73db924d612651967c79 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 21:56:25 +0100 Subject: [PATCH 03/50] Migrate Desktop Notifications MatrixToolbar to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 15 +++--- src/components/structures/LoggedInView.tsx | 7 +-- src/components/structures/MatrixChat.tsx | 6 --- src/components/views/globals/MatrixToolbar.js | 45 ----------------- src/toasts/DesktopNotificationsToast.ts | 50 +++++++++++++++++++ 5 files changed, 60 insertions(+), 63 deletions(-) delete mode 100644 src/components/views/globals/MatrixToolbar.js create mode 100644 src/toasts/DesktopNotificationsToast.ts diff --git a/src/Notifier.js b/src/Notifier.js index 2ffa92452b..cc804904e2 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -26,6 +26,10 @@ import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; +import { + showToast as showNotificationsToast, + hideToast as hideNotificationsToast, +} from "./toasts/DesktopNotificationsToast"; /* * Dispatches: @@ -184,6 +188,10 @@ const Notifier = { MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange); this.toolbarHidden = false; this.isSyncing = false; + + if (this.shouldShowToolbar()) { + showNotificationsToast(); + } }, stop: function() { @@ -278,12 +286,7 @@ const Notifier = { Analytics.trackEvent('Notifier', 'Set Toolbar Hidden', hidden); - // XXX: why are we dispatching this here? - // this is nothing to do with notifier_enabled - dis.dispatch({ - action: "notifier_enabled", - value: this.isEnabled(), - }); + hideNotificationsToast(); // update the info to localStorage for persistent settings if (persistent && global.localStorage) { diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 148d10fe8d..0d3eda759e 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -68,7 +68,6 @@ interface IProps { showCookieBar: boolean; hasNewVersion: boolean; userHasGeneratedPassword: boolean; - showNotifierToolbar: boolean; page_type: string; autoJoin: boolean; thirdPartyInvite?: object; @@ -184,8 +183,7 @@ class LoggedInView extends React.PureComponent { if ( (prevProps.showCookieBar !== this.props.showCookieBar) || (prevProps.hasNewVersion !== this.props.hasNewVersion) || - (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) || - (prevProps.showNotifierToolbar !== this.props.showNotifierToolbar) + (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) ) { this.props.resizeNotifier.notifyBannersChanged(); } @@ -599,7 +597,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar'); const CookieBar = sdk.getComponent('globals.CookieBar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); @@ -680,8 +677,6 @@ class LoggedInView extends React.PureComponent { topBar = ; } else if (this.state.userHasGeneratedPassword) { topBar = ; - } else if (this.props.showNotifierToolbar) { - topBar = ; } let bodyClasses = 'mx_MatrixChat'; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index e6a56c683f..120497e5ef 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -184,7 +184,6 @@ interface IState { hideToSRUsers: boolean; syncError?: Error; resizeNotifier: ResizeNotifier; - showNotifierToolbar: boolean; serverConfig?: ValidatedServerConfig; ready: boolean; thirdPartyInvite?: object; @@ -238,7 +237,6 @@ export default class MatrixChat extends React.PureComponent { syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. resizeNotifier: new ResizeNotifier(), - showNotifierToolbar: false, ready: false, }; @@ -686,9 +684,6 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({action: 'view_my_groups'}); } break; - case 'notifier_enabled': - this.setState({showNotifierToolbar: Notifier.shouldShowToolbar()}); - break; case 'hide_left_panel': this.setState({ collapseLhs: true, @@ -1381,7 +1376,6 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({action: 'focus_composer'}); this.setState({ ready: true, - showNotifierToolbar: Notifier.shouldShowToolbar(), }); }); cli.on('Call.incoming', function(call) { diff --git a/src/components/views/globals/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js deleted file mode 100644 index 758e4d62aa..0000000000 --- a/src/components/views/globals/MatrixToolbar.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket 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. -*/ - -import React from 'react'; -import createReactClass from 'create-react-class'; -import { _t } from '../../../languageHandler'; -import Notifier from '../../../Notifier'; -import AccessibleButton from '../../../components/views/elements/AccessibleButton'; - -export default createReactClass({ - displayName: 'MatrixToolbar', - - hideToolbar: function() { - Notifier.setToolbarHidden(true); - }, - - onClick: function() { - Notifier.setEnabled(true); - }, - - render: function() { - return ( -
- -
- { _t('You are not receiving desktop notifications') } { _t('Enable them now') } -
- {_t('Close')} -
- ); - }, -}); diff --git a/src/toasts/DesktopNotificationsToast.ts b/src/toasts/DesktopNotificationsToast.ts new file mode 100644 index 0000000000..02f0730759 --- /dev/null +++ b/src/toasts/DesktopNotificationsToast.ts @@ -0,0 +1,50 @@ +/* +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 { _t } from "../languageHandler"; +import Notifier from "../Notifier"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; + +const onAccept = () => { + Notifier.setEnabled(true); +}; + +const onReject = () => { + Notifier.setToolbarHidden(true); +}; + +const TOAST_KEY = "desktopnotifications"; + +export const showToast = () => { + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Notifications"), + props: { + description: _t("You are not receiving desktop notifications"), + acceptLabel: _t("Enable them now"), + onAccept, + rejectLabel: _t("Close"), + onReject, + }, + component: GenericToast, + priority: 20, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 89292ca47be3fc0a714024a708452452e9938ee6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:03:16 +0100 Subject: [PATCH 04/50] Fix toast priority sorting to put the highest priority into slot[0] Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/ToastStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index 4f6d2963c5..23317a0ad3 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -55,7 +55,7 @@ export default class ToastStore extends EventEmitter { 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; + while (newIndex > 0 && this.toasts[newIndex - 1].priority < newToast.priority) --newIndex; this.toasts.splice(newIndex, 0, newToast); } else { this.toasts[oldIndex] = newToast; From c91f8c2631799d92cc582dbe6eebcdec3e0a2cb4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:04:21 +0100 Subject: [PATCH 05/50] Migrate Analytics Banner to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 9 -- src/components/structures/MatrixChat.tsx | 28 ++---- src/components/views/globals/CookieBar.js | 103 --------------------- src/i18n/strings/en_EN.json | 14 +-- src/toasts/AnalyticsToast.tsx | 75 +++++++++++++++ 5 files changed, 92 insertions(+), 137 deletions(-) delete mode 100644 src/components/views/globals/CookieBar.js create mode 100644 src/toasts/AnalyticsToast.tsx diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 0d3eda759e..88b64f0cbc 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -65,7 +65,6 @@ interface IProps { initialEventPixelOffset: number; leftDisabled: boolean; rightDisabled: boolean; - showCookieBar: boolean; hasNewVersion: boolean; userHasGeneratedPassword: boolean; page_type: string; @@ -181,7 +180,6 @@ class LoggedInView extends React.PureComponent { componentDidUpdate(prevProps, prevState) { // attempt to guess when a banner was opened or closed if ( - (prevProps.showCookieBar !== this.props.showCookieBar) || (prevProps.hasNewVersion !== this.props.hasNewVersion) || (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) ) { @@ -597,7 +595,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const CookieBar = sdk.getComponent('globals.CookieBar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar'); @@ -663,12 +660,6 @@ class LoggedInView extends React.PureComponent { adminContact={usageLimitEvent.getContent().admin_contact} limitType={usageLimitEvent.getContent().limit_type} />; - } else if (this.props.showCookieBar && - this.props.config.piwik && - navigator.doNotTrack !== "1" - ) { - const policyUrl = this.props.config.piwik.policyUrl || null; - topBar = ; } else if (this.props.hasNewVersion) { topBar = { newVersionReleaseNotes: null, checkingForUpdate: null, - showCookieBar: false, - hideToSRUsers: false, syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. @@ -337,12 +338,6 @@ export default class MatrixChat extends React.PureComponent { }); } - if (SettingsStore.getValue("showCookieBar")) { - this.setState({ - showCookieBar: true, - }); - } - if (SettingsStore.getValue("analyticsOptIn")) { Analytics.enable(); } @@ -756,19 +751,13 @@ export default class MatrixChat extends React.PureComponent { case 'accept_cookies': SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, true); SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false); - - this.setState({ - showCookieBar: false, - }); + hideAnalyticsToast(); Analytics.enable(); break; case 'reject_cookies': SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, false); SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false); - - this.setState({ - showCookieBar: false, - }); + hideAnalyticsToast(); break; } }; @@ -1246,6 +1235,10 @@ export default class MatrixChat extends React.PureComponent { } StorageManager.tryPersistStorage(); + + if (SettingsStore.getValue("showCookieBar") && this.props.config.piwik && navigator.doNotTrack !== "1") { + showAnalyticsToast(this.props.config.piwik && this.props.config.piwik.policyUrl); + } } private showScreenAfterLogin() { @@ -2031,7 +2024,6 @@ export default class MatrixChat extends React.PureComponent { onCloseAllSettings={this.onCloseAllSettings} onRegistered={this.onRegistered} currentRoomId={this.state.currentRoomId} - showCookieBar={this.state.showCookieBar} /> ); } else { diff --git a/src/components/views/globals/CookieBar.js b/src/components/views/globals/CookieBar.js deleted file mode 100644 index bf264686d0..0000000000 --- a/src/components/views/globals/CookieBar.js +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2018 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. -*/ - -import React from 'react'; -import PropTypes from 'prop-types'; -import dis from '../../../dispatcher/dispatcher'; -import { _t } from '../../../languageHandler'; -import * as sdk from '../../../index'; -import Analytics from '../../../Analytics'; - -export default class CookieBar extends React.Component { - static propTypes = { - policyUrl: PropTypes.string, - } - - constructor() { - super(); - } - - onUsageDataClicked(e) { - e.stopPropagation(); - e.preventDefault(); - Analytics.showDetailsModal(); - } - - onAccept() { - dis.dispatch({ - action: 'accept_cookies', - }); - } - - onReject() { - dis.dispatch({ - action: 'reject_cookies', - }); - } - - render() { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const toolbarClasses = "mx_MatrixToolbar"; - return ( -
- -
- { this.props.policyUrl ? _t( - "Please help improve Riot.im by sending anonymous usage data. " + - "This will use a cookie " + - "(please see our Cookie Policy).", - {}, - { - 'UsageDataLink': (sub) => - { sub } - , - // XXX: We need to link to the page that explains our cookies - 'PolicyLink': (sub) => - { sub } - - , - }, - ) : _t( - "Please help improve Riot.im by sending anonymous usage data. " + - "This will use a cookie.", - {}, - { - 'UsageDataLink': (sub) => - { sub } - , - }, - ) } -
- - { _t("Yes, I want to help!") } - - - {_t('Close')} - -
- ); - } -} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8ac05bf429..a79ee97109 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -391,10 +391,17 @@ "Common names and surnames are easy to guess": "Common names and surnames are easy to guess", "Straight rows of keys are easy to guess": "Straight rows of keys are easy to guess", "Short keyboard patterns are easy to guess": "Short keyboard patterns are easy to guess", + "Notifications": "Notifications", + "Send anonymous usage data which helps us improve Riot. This will use a cookie.": "Send anonymous usage data which helps us improve Riot. This will use a cookie.", + "I want to help": "I want to help", + "No": "No", "Review where you’re logged in": "Review where you’re logged in", "Verify all your sessions to ensure your account & messages are safe": "Verify all your sessions to ensure your account & messages are safe", "Review": "Review", "Later": "Later", + "You are not receiving desktop notifications": "You are not receiving desktop notifications", + "Enable them now": "Enable them now", + "Close": "Close", "Set up encryption": "Set up encryption", "Encryption upgrade available": "Encryption upgrade available", "Verify this session": "Verify this session", @@ -643,8 +650,6 @@ "Last seen": "Last seen", "Failed to set display name": "Failed to set display name", "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.", - "Disable Notifications": "Disable Notifications", - "Enable Notifications": "Enable Notifications", "Securely cache encrypted messages locally for them to appear in search results, using ": "Securely cache encrypted messages locally for them to appear in search results, using ", " to store messages from ": " to store messages from ", "rooms.": "rooms.", @@ -815,7 +820,6 @@ "Ban list rules - %(roomName)s": "Ban list rules - %(roomName)s", "Server rules": "Server rules", "User rules": "User rules", - "Close": "Close", "You have not ignored anyone.": "You have not ignored anyone.", "You are currently ignoring:": "You are currently ignoring:", "You are not subscribed to any lists": "You are not subscribed to any lists", @@ -836,7 +840,6 @@ "If this isn't what you want, please use a different tool to ignore users.": "If this isn't what you want, please use a different tool to ignore users.", "Room ID or alias of ban list": "Room ID or alias of ban list", "Subscribe": "Subscribe", - "Notifications": "Notifications", "Start automatically after system login": "Start automatically after system login", "Always show the window menu bar": "Always show the window menu bar", "Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close", @@ -1287,7 +1290,6 @@ "Verify by emoji": "Verify by emoji", "Almost there! Is your other session showing the same shield?": "Almost there! Is your other session showing the same shield?", "Almost there! Is %(displayName)s showing the same shield?": "Almost there! Is %(displayName)s showing the same shield?", - "No": "No", "Yes": "Yes", "Verify all users in a room to ensure it's secure.": "Verify all users in a room to ensure it's secure.", "In encrypted rooms, verify all users to ensure it’s secure.": "In encrypted rooms, verify all users to ensure it’s secure.", @@ -1384,8 +1386,6 @@ "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).", "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.", "Yes, I want to help!": "Yes, I want to help!", - "You are not receiving desktop notifications": "You are not receiving desktop notifications", - "Enable them now": "Enable them now", "What's New": "What's New", "Update": "Update", "What's new?": "What's new?", diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx new file mode 100644 index 0000000000..5b53a903fe --- /dev/null +++ b/src/toasts/AnalyticsToast.tsx @@ -0,0 +1,75 @@ +/* +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 { _t } from "../languageHandler"; +import dis from "../dispatcher/dispatcher"; +import Analytics from "../Analytics"; +import AccessibleButton from "../components/views/elements/AccessibleButton"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; + +const onAccept = () => { + dis.dispatch({ + action: 'accept_cookies', + }); +}; + +const onReject = () => { + dis.dispatch({ + action: "reject_cookies", + }); +}; + +const onUsageDataClicked = () => { + Analytics.showDetailsModal(); +}; + +const TOAST_KEY = "analytics"; + +export const showToast = (policyUrl?: string) => { + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Notifications"), + props: { + description: _t( + "Send anonymous usage data which helps us improve Riot. " + + "This will use a cookie.", + {}, + { + "UsageDataLink": (sub) => ( + { sub } + ), + // XXX: We need to link to the page that explains our cookies + "PolicyLink": (sub) => policyUrl ? ( + { sub } + ) : sub, + }, + ), + acceptLabel: _t("I want to help"), + onAccept, + rejectLabel: _t("No"), + onReject, + }, + component: GenericToast, + priority: 10, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 5d0040b8b36d1474db807748d1191844b424a569 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:15:22 +0100 Subject: [PATCH 06/50] Migrate Password Nag Bar Banner to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 22 ++++---- .../views/globals/PasswordNagBar.js | 53 ------------------- src/toasts/SetPasswordToast.ts | 47 ++++++++++++++++ 3 files changed, 58 insertions(+), 64 deletions(-) delete mode 100644 src/components/views/globals/PasswordNagBar.js create mode 100644 src/toasts/SetPasswordToast.ts diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 88b64f0cbc..131e6a3867 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -43,6 +43,11 @@ import ResizeNotifier from "../../utils/ResizeNotifier"; import PlatformPeg from "../../PlatformPeg"; import { RoomListStoreTempProxy } from "../../stores/room-list/RoomListStoreTempProxy"; import { DefaultTagID } from "../../stores/room-list/models"; +import { + showToast as showSetPasswordToast, + hideToast as hideSetPasswordToast +} from "../../toasts/SetPasswordToast"; + // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. // NB. this is just for server notices rather than pinned messages in general. @@ -66,7 +71,6 @@ interface IProps { leftDisabled: boolean; rightDisabled: boolean; hasNewVersion: boolean; - userHasGeneratedPassword: boolean; page_type: string; autoJoin: boolean; thirdPartyInvite?: object; @@ -96,7 +100,6 @@ interface IState { syncErrorData: any; useCompactLayout: boolean; serverNoticeEvents: MatrixEvent[]; - userHasGeneratedPassword: boolean; } /** @@ -139,7 +142,6 @@ class LoggedInView extends React.PureComponent { this.state = { mouseDown: undefined, syncErrorData: undefined, - userHasGeneratedPassword: false, // use compact timeline view useCompactLayout: SettingsStore.getValue('useCompactLayout'), // any currently active server notice events @@ -180,8 +182,7 @@ class LoggedInView extends React.PureComponent { componentDidUpdate(prevProps, prevState) { // attempt to guess when a banner was opened or closed if ( - (prevProps.hasNewVersion !== this.props.hasNewVersion) || - (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) + (prevProps.hasNewVersion !== this.props.hasNewVersion) ) { this.props.resizeNotifier.notifyBannersChanged(); } @@ -216,9 +217,11 @@ class LoggedInView extends React.PureComponent { }; _setStateFromSessionStore = () => { - this.setState({ - userHasGeneratedPassword: Boolean(this._sessionStore.getCachedPassword()), - }); + if (this._sessionStore.getCachedPassword()) { + showSetPasswordToast(); + } else { + hideSetPasswordToast(); + } }; _createResizer() { @@ -597,7 +600,6 @@ class LoggedInView extends React.PureComponent { const ToastContainer = sdk.getComponent('structures.ToastContainer'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); - const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar'); const ServerLimitBar = sdk.getComponent('globals.ServerLimitBar'); let pageElement; @@ -666,8 +668,6 @@ class LoggedInView extends React.PureComponent { />; } else if (this.props.checkingForUpdate) { topBar = ; - } else if (this.state.userHasGeneratedPassword) { - topBar = ; } let bodyClasses = 'mx_MatrixChat'; diff --git a/src/components/views/globals/PasswordNagBar.js b/src/components/views/globals/PasswordNagBar.js deleted file mode 100644 index 74735ca5ea..0000000000 --- a/src/components/views/globals/PasswordNagBar.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2017 Vector Creations Ltd -Copyright 2018 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. -*/ - -import React from 'react'; -import createReactClass from 'create-react-class'; -import * as sdk from '../../../index'; -import Modal from '../../../Modal'; -import { _t } from '../../../languageHandler'; - -export default createReactClass({ - onUpdateClicked: function() { - const SetPasswordDialog = sdk.getComponent('dialogs.SetPasswordDialog'); - Modal.createTrackedDialog('Set Password Dialog', 'Password Nag Bar', SetPasswordDialog); - }, - - render: function() { - const toolbarClasses = "mx_MatrixToolbar mx_MatrixToolbar_clickable"; - return ( -
- -
- { _t( - "To return to your account in future you need to set a password", - {}, - { 'u': (sub) => { sub } }, - ) } -
- -
- ); - }, -}); diff --git a/src/toasts/SetPasswordToast.ts b/src/toasts/SetPasswordToast.ts new file mode 100644 index 0000000000..88cc317978 --- /dev/null +++ b/src/toasts/SetPasswordToast.ts @@ -0,0 +1,47 @@ +/* +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 { _t } from "../languageHandler"; +import Modal from "../Modal"; +import SetPasswordDialog from "../components/views/dialogs/SetPasswordDialog"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; + +const onAccept = () => { + Modal.createTrackedDialog('Set Password Dialog', 'Password Nag Bar', SetPasswordDialog); +}; + +const TOAST_KEY = "setpassword"; + +export const showToast = () => { + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Set password"), + props: { + description: _t("To return to your account in future you need to set a password"), + acceptLabel: _t("Set Password"), + onAccept, + rejectLabel: _t("Later"), + onReject: hideToast, // it'll return on reload + }, + component: GenericToast, + priority: 60, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From ccf9e6512341bb7395b1eba3841b0ce475379da5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:27:19 +0100 Subject: [PATCH 07/50] Migrate Server Limit Bar Banner to a Toast Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 56 ++++++----- .../views/globals/ServerLimitBar.js | 99 ------------------- src/toasts/ServerLimitToast.tsx | 50 ++++++++++ 3 files changed, 80 insertions(+), 125 deletions(-) delete mode 100644 src/components/views/globals/ServerLimitBar.js create mode 100644 src/toasts/ServerLimitToast.tsx diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 131e6a3867..edb2482aa3 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -47,6 +47,10 @@ import { showToast as showSetPasswordToast, hideToast as hideSetPasswordToast } from "../../toasts/SetPasswordToast"; +import { + showToast as showServerLimitToast, + hideToast as hideServerLimitToast +} from "../../toasts/ServerLimitToast"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -99,7 +103,6 @@ interface IState { }; syncErrorData: any; useCompactLayout: boolean; - serverNoticeEvents: MatrixEvent[]; } /** @@ -144,8 +147,6 @@ class LoggedInView extends React.PureComponent { syncErrorData: undefined, // use compact timeline view useCompactLayout: SettingsStore.getValue('useCompactLayout'), - // any currently active server notice events - serverNoticeEvents: [], }; // stash the MatrixClient in case we log out before we are unmounted @@ -293,6 +294,8 @@ class LoggedInView extends React.PureComponent { if (oldSyncState === 'PREPARED' && syncState === 'SYNCING') { this._updateServerNoticeEvents(); + } else { + this._calculateServerLimitToast(data); } }; @@ -303,11 +306,24 @@ class LoggedInView extends React.PureComponent { } }; + _calculateServerLimitToast(syncErrorData, usageLimitEventContent?) { + const error = syncErrorData && syncErrorData.error && syncErrorData.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED"; + if (error) { + usageLimitEventContent = syncErrorData.error.data; + } + + if (usageLimitEventContent) { + showServerLimitToast(usageLimitEventContent.limit_type, usageLimitEventContent.admin_contact, error); + } else { + hideServerLimitToast(); + } + } + _updateServerNoticeEvents = async () => { const roomLists = RoomListStoreTempProxy.getRoomLists(); if (!roomLists[DefaultTagID.ServerNotice]) return []; - const pinnedEvents = []; + const events = []; for (const room of roomLists[DefaultTagID.ServerNotice]) { const pinStateEvent = room.currentState.getStateEvents("m.room.pinned_events", ""); @@ -317,12 +333,18 @@ class LoggedInView extends React.PureComponent { for (const eventId of pinnedEventIds) { const timeline = await this._matrixClient.getEventTimeline(room.getUnfilteredTimelineSet(), eventId, 0); const event = timeline.getEvents().find(ev => ev.getId() === eventId); - if (event) pinnedEvents.push(event); + if (event) events.push(event); } } - this.setState({ - serverNoticeEvents: pinnedEvents, + + const usageLimitEvent = events.find((e) => { + return ( + e && e.getType() === 'm.room.message' && + e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached' + ); }); + + this._calculateServerLimitToast(this.state.syncErrorData, usageLimitEvent && usageLimitEvent.getContent()); }; _onPaste = (ev) => { @@ -600,7 +622,6 @@ class LoggedInView extends React.PureComponent { const ToastContainer = sdk.getComponent('structures.ToastContainer'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); - const ServerLimitBar = sdk.getComponent('globals.ServerLimitBar'); let pageElement; @@ -644,25 +665,8 @@ class LoggedInView extends React.PureComponent { break; } - const usageLimitEvent = this.state.serverNoticeEvents.find((e) => { - return ( - e && e.getType() === 'm.room.message' && - e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached' - ); - }); - let topBar; - if (this.state.syncErrorData && this.state.syncErrorData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') { - topBar = ; - } else if (usageLimitEvent) { - topBar = ; - } else if (this.props.hasNewVersion) { + if (this.props.hasNewVersion) { topBar = ; diff --git a/src/components/views/globals/ServerLimitBar.js b/src/components/views/globals/ServerLimitBar.js deleted file mode 100644 index 7d414a2826..0000000000 --- a/src/components/views/globals/ServerLimitBar.js +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2018 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. -*/ - -import React from 'react'; -import PropTypes from 'prop-types'; -import createReactClass from 'create-react-class'; -import classNames from 'classnames'; -import { _td } from '../../../languageHandler'; -import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; - -export default createReactClass({ - propTypes: { - // 'hard' if the logged in user has been locked out, 'soft' if they haven't - kind: PropTypes.string, - adminContact: PropTypes.string, - // The type of limit that has been hit. - limitType: PropTypes.string.isRequired, - }, - - getDefaultProps: function() { - return { - kind: 'hard', - }; - }, - - render: function() { - const toolbarClasses = { - 'mx_MatrixToolbar': true, - }; - - let adminContact; - let limitError; - if (this.props.kind === 'hard') { - toolbarClasses['mx_MatrixToolbar_error'] = true; - - adminContact = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - '': _td("Please contact your service administrator to continue using the service."), - }, - ); - limitError = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - 'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."), - '': _td("This homeserver has exceeded one of its resource limits."), - }, - ); - } else { - toolbarClasses['mx_MatrixToolbar_info'] = true; - adminContact = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - '': _td("Please contact your service administrator to get this limit increased."), - }, - ); - limitError = messageForResourceLimitError( - this.props.limitType, - this.props.adminContact, - { - 'monthly_active_user': _td( - "This homeserver has hit its Monthly Active User limit so " + - "some users will not be able to log in.", - ), - '': _td( - "This homeserver has exceeded one of its resource limits so " + - "some users will not be able to log in.", - ), - }, - {'b': sub => {sub}}, - ); - } - return ( -
-
- {limitError} - {' '} - {adminContact} -
-
- ); - }, -}); diff --git a/src/toasts/ServerLimitToast.tsx b/src/toasts/ServerLimitToast.tsx new file mode 100644 index 0000000000..f2de9e3499 --- /dev/null +++ b/src/toasts/ServerLimitToast.tsx @@ -0,0 +1,50 @@ +/* +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 { _t, _td } from "../languageHandler"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; +import {messageForResourceLimitError} from "../utils/ErrorUtils"; + +const TOAST_KEY = "serverlimit"; + +export const showToast = (limitType: string, adminContact?: string, syncError?: boolean) => { + const errorText = messageForResourceLimitError(limitType, adminContact, { + 'monthly_active_user': _td("Your homeserver has exceeded its user limit."), + '': _td("Your homeserver has exceeded one of its resource limits."), + }); + const contactText = messageForResourceLimitError(limitType, adminContact, { + '': _td("Contact your server admin."), + }); + + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Notifications"), + props: { + description: {errorText} {contactText}, + acceptLabel: _t("Ok"), + onAccept: hideToast, + }, + component: GenericToast, + priority: 20, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 29cfb47a83f5fc016f941a8387efb8f5a3e7463f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:29:09 +0100 Subject: [PATCH 08/50] fix copy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/AnalyticsToast.tsx | 2 +- src/toasts/ServerLimitToast.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx index 5b53a903fe..b186a65d9d 100644 --- a/src/toasts/AnalyticsToast.tsx +++ b/src/toasts/AnalyticsToast.tsx @@ -44,7 +44,7 @@ const TOAST_KEY = "analytics"; export const showToast = (policyUrl?: string) => { ToastStore.sharedInstance().addOrReplaceToast({ key: TOAST_KEY, - title: _t("Notifications"), + title: _t("Help us improve Riot"), props: { description: _t( "Send anonymous usage data which helps us improve Riot. " + diff --git a/src/toasts/ServerLimitToast.tsx b/src/toasts/ServerLimitToast.tsx index f2de9e3499..d35140be3d 100644 --- a/src/toasts/ServerLimitToast.tsx +++ b/src/toasts/ServerLimitToast.tsx @@ -34,14 +34,14 @@ export const showToast = (limitType: string, adminContact?: string, syncError?: ToastStore.sharedInstance().addOrReplaceToast({ key: TOAST_KEY, - title: _t("Notifications"), + title: _t("Warning"), props: { description: {errorText} {contactText}, acceptLabel: _t("Ok"), onAccept: hideToast, }, component: GenericToast, - priority: 20, + priority: 70, }); }; From 9f060d113259d3cf22d5e9ee4f6a98eb91c0d5d7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 22:34:32 +0100 Subject: [PATCH 09/50] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a79ee97109..72c19587c8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -391,7 +391,7 @@ "Common names and surnames are easy to guess": "Common names and surnames are easy to guess", "Straight rows of keys are easy to guess": "Straight rows of keys are easy to guess", "Short keyboard patterns are easy to guess": "Short keyboard patterns are easy to guess", - "Notifications": "Notifications", + "Help us improve Riot": "Help us improve Riot", "Send anonymous usage data which helps us improve Riot. This will use a cookie.": "Send anonymous usage data which helps us improve Riot. This will use a cookie.", "I want to help": "I want to help", "No": "No", @@ -399,9 +399,18 @@ "Verify all your sessions to ensure your account & messages are safe": "Verify all your sessions to ensure your account & messages are safe", "Review": "Review", "Later": "Later", + "Notifications": "Notifications", "You are not receiving desktop notifications": "You are not receiving desktop notifications", "Enable them now": "Enable them now", "Close": "Close", + "Your homeserver has exceeded its user limit.": "Your homeserver has exceeded its user limit.", + "Your homeserver has exceeded one of its resource limits.": "Your homeserver has exceeded one of its resource limits.", + "Contact your server admin.": "Contact your server admin.", + "Warning": "Warning", + "Ok": "Ok", + "Set password": "Set password", + "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", + "Set Password": "Set Password", "Set up encryption": "Set up encryption", "Encryption upgrade available": "Encryption upgrade available", "Verify this session": "Verify this session", @@ -781,7 +790,6 @@ "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", "Deactivate Account": "Deactivate Account", - "Warning": "Warning", "General": "General", "Discovery": "Discovery", "Deactivate account": "Deactivate account", @@ -1383,18 +1391,10 @@ "Something went wrong when trying to get your communities.": "Something went wrong when trying to get your communities.", "Display your community flair in rooms configured to show it.": "Display your community flair in rooms configured to show it.", "You're not currently a member of any communities.": "You're not currently a member of any communities.", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).", - "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.", - "Yes, I want to help!": "Yes, I want to help!", "What's New": "What's New", "Update": "Update", "What's new?": "What's new?", "A new version of Riot is available.": "A new version of Riot is available.", - "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", - "Set Password": "Set Password", - "Please contact your service administrator to get this limit increased.": "Please contact your service administrator to get this limit increased.", - "This homeserver has hit its Monthly Active User limit so some users will not be able to log in.": "This homeserver has hit its Monthly Active User limit so some users will not be able to log in.", - "This homeserver has exceeded one of its resource limits so some users will not be able to log in.": "This homeserver has exceeded one of its resource limits so some users will not be able to log in.", "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", "Checking for an update...": "Checking for an update...", "No update available.": "No update available.", From 891ba1bbe3c34bf5651846d425c07d969fec7cb7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 23:08:45 +0100 Subject: [PATCH 10/50] Replace New Version Bar with a Toast discards the `new_version` dispatch Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/globals/_MatrixToolbar.scss | 4 - src/components/structures/LoggedInView.tsx | 11 +- src/components/structures/MatrixChat.tsx | 21 ---- src/components/views/globals/NewVersionBar.js | 108 ------------------ src/i18n/strings/en_EN.json | 10 +- src/toasts/UpdateToast.tsx | 90 +++++++++++++++ 6 files changed, 99 insertions(+), 145 deletions(-) delete mode 100644 src/components/views/globals/NewVersionBar.js create mode 100644 src/toasts/UpdateToast.tsx diff --git a/res/css/views/globals/_MatrixToolbar.scss b/res/css/views/globals/_MatrixToolbar.scss index 5fdf572f99..07b92a7235 100644 --- a/res/css/views/globals/_MatrixToolbar.scss +++ b/res/css/views/globals/_MatrixToolbar.scss @@ -67,7 +67,3 @@ limitations under the License. .mx_MatrixToolbar_action { margin-right: 16px; } - -.mx_MatrixToolbar_changelog { - white-space: pre; -} diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index edb2482aa3..2a17233ec6 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -74,7 +74,6 @@ interface IProps { initialEventPixelOffset: number; leftDisabled: boolean; rightDisabled: boolean; - hasNewVersion: boolean; page_type: string; autoJoin: boolean; thirdPartyInvite?: object; @@ -96,6 +95,7 @@ interface IProps { newVersion?: string; newVersionReleaseNotes?: string; } + interface IState { mouseDown?: { x: number; @@ -183,7 +183,7 @@ class LoggedInView extends React.PureComponent { componentDidUpdate(prevProps, prevState) { // attempt to guess when a banner was opened or closed if ( - (prevProps.hasNewVersion !== this.props.hasNewVersion) + (prevProps.checkingForUpdate !== this.props.checkingForUpdate) ) { this.props.resizeNotifier.notifyBannersChanged(); } @@ -620,7 +620,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); let pageElement; @@ -666,11 +665,7 @@ class LoggedInView extends React.PureComponent { } let topBar; - if (this.props.hasNewVersion) { - topBar = ; - } else if (this.props.checkingForUpdate) { + if (this.props.checkingForUpdate) { topBar = ; } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index c69911fe04..f4d31708fe 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -173,10 +173,6 @@ interface IState { leftDisabled: boolean; middleDisabled: boolean; // the right panel's disabled state is tracked in its store. - version?: string; - newVersion?: string; - hasNewVersion: boolean; - newVersionReleaseNotes?: string; checkingForUpdate?: string; // updateCheckStatusEnum // Parameters used in the registration dance with the IS register_client_secret?: string; @@ -230,7 +226,6 @@ export default class MatrixChat extends React.PureComponent { leftDisabled: false, middleDisabled: false, - hasNewVersion: false, newVersionReleaseNotes: null, checkingForUpdate: null, @@ -726,12 +721,6 @@ export default class MatrixChat extends React.PureComponent { case 'client_started': this.onClientStarted(); break; - case 'new_version': - this.onVersion( - payload.currentVersion, payload.newVersion, - payload.releaseNotes, - ); - break; case 'check_updates': this.setState({ checkingForUpdate: payload.value }); break; @@ -1820,16 +1809,6 @@ export default class MatrixChat extends React.PureComponent { this.showScreen("settings"); }; - onVersion(current: string, latest: string, releaseNotes?: string) { - this.setState({ - version: current, - newVersion: latest, - hasNewVersion: current !== latest, - newVersionReleaseNotes: releaseNotes, - checkingForUpdate: null, - }); - } - onSendEvent(roomId: string, event: MatrixEvent) { const cli = MatrixClientPeg.get(); if (!cli) { diff --git a/src/components/views/globals/NewVersionBar.js b/src/components/views/globals/NewVersionBar.js deleted file mode 100644 index dedccdc6b6..0000000000 --- a/src/components/views/globals/NewVersionBar.js +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> - -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 createReactClass from 'create-react-class'; -import * as sdk from '../../../index'; -import Modal from '../../../Modal'; -import PlatformPeg from '../../../PlatformPeg'; -import { _t } from '../../../languageHandler'; - -/** - * Check a version string is compatible with the Changelog - * dialog ([vectorversion]-react-[react-sdk-version]-js-[js-sdk-version]) - */ -function checkVersion(ver) { - const parts = ver.split('-'); - return parts.length == 5 && parts[1] == 'react' && parts[3] == 'js'; -} - -export default createReactClass({ - propTypes: { - version: PropTypes.string.isRequired, - newVersion: PropTypes.string.isRequired, - releaseNotes: PropTypes.string, - }, - - displayReleaseNotes: function(releaseNotes) { - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); - Modal.createTrackedDialog('Display release notes', '', QuestionDialog, { - title: _t("What's New"), - description:
{releaseNotes}
, - button: _t("Update"), - onFinished: (update) => { - if (update && PlatformPeg.get()) { - PlatformPeg.get().installUpdate(); - } - }, - }); - }, - - displayChangelog: function() { - const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog'); - Modal.createTrackedDialog('Display Changelog', '', ChangelogDialog, { - version: this.props.version, - newVersion: this.props.newVersion, - onFinished: (update) => { - if (update && PlatformPeg.get()) { - PlatformPeg.get().installUpdate(); - } - }, - }); - }, - - onUpdateClicked: function() { - PlatformPeg.get().installUpdate(); - }, - - render: function() { - let action_button; - // If we have release notes to display, we display them. Otherwise, - // we display the Changelog Dialog which takes two versions and - // automatically tells you what's changed (provided the versions - // are in the right format) - if (this.props.releaseNotes) { - action_button = ( - - ); - } else if (checkVersion(this.props.version) && checkVersion(this.props.newVersion)) { - action_button = ( - - ); - } else if (PlatformPeg.get()) { - action_button = ( - - ); - } - return ( -
- -
- {_t("A new version of Riot is available.")} -
- {action_button} -
- ); - }, -}); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 72c19587c8..0170aee17a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -421,6 +421,12 @@ "Other users may not trust it": "Other users may not trust it", "New login. Was this you?": "New login. Was this you?", "Verify the new login accessing your account: %(name)s": "Verify the new login accessing your account: %(name)s", + "What's New": "What's New", + "Update": "Update", + "What's new?": "What's new?", + "Restart": "Restart", + "Upgrade your Riot": "Upgrade your Riot", + "A new version of Riot is available!": "A new version of Riot is available!", "There was an error joining the room": "There was an error joining the room", "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", @@ -1391,10 +1397,6 @@ "Something went wrong when trying to get your communities.": "Something went wrong when trying to get your communities.", "Display your community flair in rooms configured to show it.": "Display your community flair in rooms configured to show it.", "You're not currently a member of any communities.": "You're not currently a member of any communities.", - "What's New": "What's New", - "Update": "Update", - "What's new?": "What's new?", - "A new version of Riot is available.": "A new version of Riot is available.", "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", "Checking for an update...": "Checking for an update...", "No update available.": "No update available.", diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx new file mode 100644 index 0000000000..3d4b55a4ff --- /dev/null +++ b/src/toasts/UpdateToast.tsx @@ -0,0 +1,90 @@ +/* +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 { _t } from "../languageHandler"; +import GenericToast from "../components/views/toasts/GenericToast"; +import ToastStore from "../stores/ToastStore"; +import QuestionDialog from "../components/views/dialogs/QuestionDialog"; +import ChangelogDialog from "../components/views/dialogs/ChangelogDialog"; +import PlatformPeg from "../PlatformPeg"; +import Modal from "../Modal"; + +const TOAST_KEY = "update"; + +/* + * Check a version string is compatible with the Changelog + * dialog ([riot-version]-react-[react-sdk-version]-js-[js-sdk-version]) + */ +function checkVersion(ver) { + const parts = ver.split('-'); + return parts.length === 5 && parts[1] === 'react' && parts[3] === 'js'; +} + +function installUpdate() { + PlatformPeg.get().installUpdate(); +} + +export const showToast = (version: string, newVersion: string, releaseNotes?: string) => { + let onAccept; + let acceptLabel = _t("What's new?"); + if (releaseNotes) { + onAccept = () => { + Modal.createTrackedDialog('Display release notes', '', QuestionDialog, { + title: _t("What's New"), + description:
{releaseNotes}
, + button: _t("Update"), + onFinished: (update) => { + if (update && PlatformPeg.get()) { + PlatformPeg.get().installUpdate(); + } + }, + }); + }; + } else if (checkVersion(version) && checkVersion(newVersion)) { + onAccept = () => { + Modal.createTrackedDialog('Display Changelog', '', ChangelogDialog, { + version, + newVersion, + onFinished: (update) => { + if (update && PlatformPeg.get()) { + PlatformPeg.get().installUpdate(); + } + }, + }); + }; + } else { + onAccept = installUpdate; + acceptLabel = _t("Restart"); + } + + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("Upgrade your Riot"), + props: { + description: _t("A new version of Riot is available!"), + acceptLabel, + onAccept, + }, + component: GenericToast, + priority: 20, + }); +}; + +export const hideToast = () => { + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +}; From 41934268087fa0b9be828f1714eb4a5e3ab6f28a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 23:14:33 +0100 Subject: [PATCH 11/50] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 3 --- src/components/structures/MatrixChat.tsx | 1 - 2 files changed, 4 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 2a17233ec6..df21768da5 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -91,9 +91,6 @@ interface IProps { currentUserId?: string; currentGroupId?: string; currentGroupIsNew?: boolean; - version?: string; - newVersion?: string; - newVersionReleaseNotes?: string; } interface IState { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f4d31708fe..e6db42af1d 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -226,7 +226,6 @@ export default class MatrixChat extends React.PureComponent { leftDisabled: false, middleDisabled: false, - newVersionReleaseNotes: null, checkingForUpdate: null, hideToSRUsers: false, From 3d2b56aecd38ea399f2666b688c4007438384411 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 May 2020 23:23:23 +0100 Subject: [PATCH 12/50] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0170aee17a..97ada284b0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -421,9 +421,9 @@ "Other users may not trust it": "Other users may not trust it", "New login. Was this you?": "New login. Was this you?", "Verify the new login accessing your account: %(name)s": "Verify the new login accessing your account: %(name)s", + "What's new?": "What's new?", "What's New": "What's New", "Update": "Update", - "What's new?": "What's new?", "Restart": "Restart", "Upgrade your Riot": "Upgrade your Riot", "A new version of Riot is available!": "A new version of Riot is available!", From 066dd4b61140b14db91d52ae63f8b59caa32c861 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 23 May 2020 08:48:28 +0100 Subject: [PATCH 13/50] Update Modular hosting link Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/auth/ModularServerConfig.js | 3 ++- src/components/views/auth/ServerTypeSelector.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js index 1216202a23..591c30ee7c 100644 --- a/src/components/views/auth/ModularServerConfig.js +++ b/src/components/views/auth/ModularServerConfig.js @@ -23,7 +23,8 @@ import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils"; import * as ServerType from '../../views/auth/ServerTypeSelector'; import ServerConfig from "./ServerConfig"; -const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; +const MODULAR_URL = 'https://modular.im/services/matrix-hosting-riot' + + '?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; // TODO: TravisR - Can this extend ServerConfig for most things? diff --git a/src/components/views/auth/ServerTypeSelector.js b/src/components/views/auth/ServerTypeSelector.js index fe29b7f76c..a8a1dda968 100644 --- a/src/components/views/auth/ServerTypeSelector.js +++ b/src/components/views/auth/ServerTypeSelector.js @@ -22,7 +22,8 @@ import classnames from 'classnames'; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; import {makeType} from "../../../utils/TypeUtils"; -const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; +const MODULAR_URL = 'https://modular.im/services/matrix-hosting-riot' + + '?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; export const FREE = 'Free'; export const PREMIUM = 'Premium'; From fb73bac4cf7c847d088be2f02414c82c4eeee041 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 26 May 2020 13:53:28 +0100 Subject: [PATCH 14/50] Update UpdateToast's priority to 30 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/UpdateToast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx index 3d4b55a4ff..55c128e86a 100644 --- a/src/toasts/UpdateToast.tsx +++ b/src/toasts/UpdateToast.tsx @@ -81,7 +81,7 @@ export const showToast = (version: string, newVersion: string, releaseNotes?: st onAccept, }, component: GenericToast, - priority: 20, + priority: 30, }); }; From fcbd197e7df2c7f32b98c3cf4c704f4d2f90e979 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 26 May 2020 15:56:16 +0100 Subject: [PATCH 15/50] Fix file list regression --- res/css/structures/_NotificationPanel.scss | 4 ++++ src/components/structures/MessagePanel.js | 22 ++++------------------ src/components/structures/RoomView.js | 21 ++++++++++++++++++++- src/components/structures/TimelinePanel.js | 4 ++++ 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/res/css/structures/_NotificationPanel.scss b/res/css/structures/_NotificationPanel.scss index 44205b1f01..561ab1446f 100644 --- a/res/css/structures/_NotificationPanel.scss +++ b/res/css/structures/_NotificationPanel.scss @@ -63,6 +63,10 @@ limitations under the License. padding-left: 32px; padding-top: 8px; position: relative; + + a { + display: flex; + } } .mx_NotificationPanel .mx_EventTile_roomName a, diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 93e4668f66..ef738db2fd 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -108,6 +108,9 @@ export default class MessagePanel extends React.Component { // whether to show reactions for an event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }; // Force props to be loaded for useIRCLayout @@ -119,7 +122,6 @@ export default class MessagePanel extends React.Component { // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), - useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -172,8 +174,6 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); - - this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); } componentDidMount() { @@ -183,7 +183,6 @@ export default class MessagePanel extends React.Component { componentWillUnmount() { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); - SettingsStore.unwatchSetting(this._layoutWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -202,17 +201,6 @@ export default class MessagePanel extends React.Component { }); }; - onLayoutChange = () => { - this.setState({ - useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), - }); - } - - useIRCLayout(ircLayoutSelected) { - // if room is null we are not in a normal room list - return ircLayoutSelected && this.props.room; - } - /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -614,7 +602,7 @@ export default class MessagePanel extends React.Component { isSelectedEvent={highlight} getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} - useIRCLayout={this.state.useIRCLayout} + useIRCLayout={this.props.useIRCLayout} /> , @@ -797,8 +785,6 @@ export default class MessagePanel extends React.Component { this.props.className, { "mx_MessagePanel_alwaysShowTimestamps": this.props.alwaysShowTimestamps, - "mx_IRCLayout": this.state.useIRCLayout, - "mx_GroupLayout": !this.state.useIRCLayout, }, ); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 635597db74..b9daac17b4 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -164,6 +164,7 @@ export default createReactClass({ canReact: false, canReply: false, + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), }; }, @@ -193,6 +194,8 @@ export default createReactClass({ this._roomView = createRef(); this._searchResultsPanel = createRef(); + + this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); }, _onReadReceiptsChange: function() { @@ -532,6 +535,14 @@ export default createReactClass({ // no need to do this as Dir & Settings are now overlays. It just burnt CPU. // console.log("Tinter.tint from RoomView.unmount"); // Tinter.tint(); // reset colourscheme + + SettingsStore.unwatchSetting(this._layoutWatcherRef); + }, + + onLayoutChange: function() { + this.setState({ + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + }); }, _onRightPanelStoreUpdate: function() { @@ -1980,6 +1991,13 @@ export default createReactClass({ highlightedEventId = this.state.initialEventId; } + const messagePanelClassNames = classNames( + "mx_RoomView_messagePanel", + { + "mx_IRCLayout": this.state.useIRCLayout, + "mx_GroupLayout": !this.state.useIRCLayout, + }); + // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); const messagePanel = ( ); let topUnreadMessagesBar = null; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index da1369c45f..95dc42fcee 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -112,6 +112,9 @@ const TimelinePanel = createReactClass({ // whether to show reactions for an event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }, statics: { @@ -1447,6 +1450,7 @@ const TimelinePanel = createReactClass({ getRelationsForEvent={this.getRelationsForEvent} editState={this.state.editState} showReactions={this.props.showReactions} + useIRCLayout={this.props.useIRCLayout} /> ); }, From 254ed4b1b60c1bf8964a67febf2761a22773802b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 11:33:42 +0100 Subject: [PATCH 16/50] Add debug lined to figure out e2e test failure Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/ToastStore.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index e963ecd736..a26c5e0794 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -56,6 +56,7 @@ export default class ToastStore extends EventEmitter { * @param {object} newToast The new toast */ addOrReplaceToast>(newToast: IToast) { + console.log("DEBUG addOrReplaceToast", newToast.key, JSON.stringify(newToast)); const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); if (oldIndex === -1) { let newIndex = this.toasts.length; @@ -68,6 +69,7 @@ export default class ToastStore extends EventEmitter { } dismissToast(key) { + console.log("DEBUG dismissToast", key); const length = this.toasts.length; this.toasts = this.toasts.filter(t => t.key !== key); if (length !== this.toasts.length) { From 3dade4e759bc4d01af126e1f3a5d64c4057b1d20 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 11:48:48 +0100 Subject: [PATCH 17/50] tweak Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/ToastStore.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index a26c5e0794..2f5a1f74b0 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -70,13 +70,15 @@ export default class ToastStore extends EventEmitter { dismissToast(key) { console.log("DEBUG dismissToast", key); + if (this.toasts[0] && this.toasts[0].key === key) { + this.countSeen++; + } + const length = this.toasts.length; this.toasts = this.toasts.filter(t => t.key !== key); if (length !== this.toasts.length) { if (this.toasts.length === 0) { this.countSeen = 0; - } else { - this.countSeen++; } this.emit('update'); From efa47f1a8e948e2920aa640cccfad94caa9942b5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:02:51 +0100 Subject: [PATCH 18/50] Change out debug Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 1 + src/stores/ToastStore.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f2d7e81d85..f8c96ee393 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -624,6 +624,7 @@ export default class MatrixChat extends React.PureComponent { break; } case 'view_room_directory': { + console.log("DEBUG view_room_directory"); const RoomDirectory = sdk.getComponent("structures.RoomDirectory"); Modal.createTrackedDialog('Room directory', '', RoomDirectory, {}, 'mx_RoomDirectory_dialogWrapper', false, true); diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index 2f5a1f74b0..55c48c3937 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -56,7 +56,6 @@ export default class ToastStore extends EventEmitter { * @param {object} newToast The new toast */ addOrReplaceToast>(newToast: IToast) { - console.log("DEBUG addOrReplaceToast", newToast.key, JSON.stringify(newToast)); const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); if (oldIndex === -1) { let newIndex = this.toasts.length; @@ -69,7 +68,6 @@ export default class ToastStore extends EventEmitter { } dismissToast(key) { - console.log("DEBUG dismissToast", key); if (this.toasts[0] && this.toasts[0].key === key) { this.countSeen++; } From 48de17457663867359b511344210d5d962200d29 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:20:20 +0100 Subject: [PATCH 19/50] remove debug Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f8c96ee393..f2d7e81d85 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -624,7 +624,6 @@ export default class MatrixChat extends React.PureComponent { break; } case 'view_room_directory': { - console.log("DEBUG view_room_directory"); const RoomDirectory = sdk.getComponent("structures.RoomDirectory"); Modal.createTrackedDialog('Room directory', '', RoomDirectory, {}, 'mx_RoomDirectory_dialogWrapper', false, true); From b7c688d3282d5156c74f9ac50e0780908c895965 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:40:38 +0100 Subject: [PATCH 20/50] test e2e tests workaround Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/join.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/end-to-end-tests/src/usecases/join.js b/test/end-to-end-tests/src/usecases/join.js index 655c0be686..10a48bd71c 100644 --- a/test/end-to-end-tests/src/usecases/join.js +++ b/test/end-to-end-tests/src/usecases/join.js @@ -19,6 +19,15 @@ const {openRoomDirectory} = require('./create-room'); module.exports = async function join(session, roomName) { session.log.step(`joins room "${roomName}"`); + while (true) { + try { + const toastDismissButton = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } + await openRoomDirectory(session); const roomInput = await session.query('.mx_DirectorySearchBox input'); await session.replaceInputText(roomInput, roomName); From 7b6d49c416f63576fcdf3be088b80cc2b87bde71 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 12:57:45 +0100 Subject: [PATCH 21/50] e2e test toasts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenario.js | 2 + test/end-to-end-tests/src/scenarios/toast.js | 23 ++++++++++ test/end-to-end-tests/src/usecases/join.js | 9 ---- test/end-to-end-tests/src/usecases/toasts.js | 45 ++++++++++++++++++++ 4 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 test/end-to-end-tests/src/scenarios/toast.js create mode 100644 test/end-to-end-tests/src/usecases/toasts.js diff --git a/test/end-to-end-tests/src/scenario.js b/test/end-to-end-tests/src/scenario.js index f575fb392e..2191d630ac 100644 --- a/test/end-to-end-tests/src/scenario.js +++ b/test/end-to-end-tests/src/scenario.js @@ -17,6 +17,7 @@ limitations under the License. const {range} = require('./util'); const signup = require('./usecases/signup'); +const toastScenarios = require('./scenarios/toast'); const roomDirectoryScenarios = require('./scenarios/directory'); const lazyLoadingScenarios = require('./scenarios/lazy-loading'); const e2eEncryptionScenarios = require('./scenarios/e2e-encryption'); @@ -37,6 +38,7 @@ module.exports = async function scenario(createSession, restCreator) { const alice = await createUser("alice"); const bob = await createUser("bob"); + await toastScenarios(alice, bob); await roomDirectoryScenarios(alice, bob); await e2eEncryptionScenarios(alice, bob); console.log("create REST users:"); diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js new file mode 100644 index 0000000000..91c6ed750f --- /dev/null +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -0,0 +1,23 @@ +/* +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. +*/ + +const {acceptToast} = require("../usecases/toasts"); + +module.exports = async function toastScenarios(alice, bob) { + console.log(" checking and clearing all toasts:"); + await acceptToast(alice, "Help us improve Riot"); + await acceptToast(bob, "Help us improve Riot"); +}; diff --git a/test/end-to-end-tests/src/usecases/join.js b/test/end-to-end-tests/src/usecases/join.js index 10a48bd71c..655c0be686 100644 --- a/test/end-to-end-tests/src/usecases/join.js +++ b/test/end-to-end-tests/src/usecases/join.js @@ -19,15 +19,6 @@ const {openRoomDirectory} = require('./create-room'); module.exports = async function join(session, roomName) { session.log.step(`joins room "${roomName}"`); - while (true) { - try { - const toastDismissButton = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } - await openRoomDirectory(session); const roomInput = await session.query('.mx_DirectorySearchBox input'); await session.replaceInputText(roomInput, roomName); diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js new file mode 100644 index 0000000000..663484ce6e --- /dev/null +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -0,0 +1,45 @@ +/* +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. +*/ + +const assert = require('assert'); + +async function assertToast(session, expectedTitle) { + const h2Element = await session.query('.mx_Toast_title h2'); + const toastTitle = await session.innerText(h2Element); + assert(toastTitle, expectedTitle); +} + +async function acceptToast(session, expectedTitle) { + const foundToast = await assertToast(session, expectedTitle); + if (!foundToast) { + throw new Error("could not find expected toast"); + } + + const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_primary'); + await btn.click(); +} + +async function rejectToast(session, expectedTitle) { + const foundToast = await assertToast(session, expectedTitle); + if (!foundToast) { + throw new Error("could not find expected toast"); + } + + const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await btn.click(); +} + +module.exports = {assertToast, acceptToast, rejectToast}; From d9552c7f5c4296ee92258614c261cb3d01352d4f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:10:28 +0100 Subject: [PATCH 22/50] e2e test toasts v2 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 12 ++++++++++-- test/end-to-end-tests/src/usecases/toasts.js | 12 ++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 91c6ed750f..9a0594cc31 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -14,10 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -const {acceptToast} = require("../usecases/toasts"); +const {acceptToast, rejectToast} = require("../usecases/toasts"); module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing all toasts:"); + + alice.log.startGroup(`clears toasts`); + await acceptToast(alice, "Notifications"); await acceptToast(alice, "Help us improve Riot"); - await acceptToast(bob, "Help us improve Riot"); + alice.log.endGroup(); + + bob.log.startGroup(`clears toasts`); + await rejectToast(bob, "Notifications"); + await rejectToast(bob, "Help us improve Riot"); + bob.log.endGroup(); }; diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 663484ce6e..991d3cc39c 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -23,21 +23,13 @@ async function assertToast(session, expectedTitle) { } async function acceptToast(session, expectedTitle) { - const foundToast = await assertToast(session, expectedTitle); - if (!foundToast) { - throw new Error("could not find expected toast"); - } - + await assertToast(session, expectedTitle); const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_primary'); await btn.click(); } async function rejectToast(session, expectedTitle) { - const foundToast = await assertToast(session, expectedTitle); - if (!foundToast) { - throw new Error("could not find expected toast"); - } - + await assertToast(session, expectedTitle); const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); await btn.click(); } From 37c875b8635107eb966a692141deed915d14669e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:23:49 +0100 Subject: [PATCH 23/50] improve end to end tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 10 ++++++++-- test/end-to-end-tests/src/usecases/toasts.js | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 9a0594cc31..b5b7fa4494 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -14,18 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -const {acceptToast, rejectToast} = require("../usecases/toasts"); +const {assertNoToasts, acceptToast, rejectToast} = require("../usecases/toasts"); module.exports = async function toastScenarios(alice, bob) { - console.log(" checking and clearing all toasts:"); + console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); + alice.log.step(`accepts desktop notifications toast`); await acceptToast(alice, "Notifications"); + alice.log.step(`accepts analytics toast`); await acceptToast(alice, "Help us improve Riot"); + await assertNoToasts(alice); alice.log.endGroup(); bob.log.startGroup(`clears toasts`); + alice.log.step(`reject desktop notifications toast`); await rejectToast(bob, "Notifications"); + alice.log.step(`reject analytics toast`); await rejectToast(bob, "Help us improve Riot"); + await assertNoToasts(bob); bob.log.endGroup(); }; diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 991d3cc39c..636714d66b 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -16,6 +16,11 @@ limitations under the License. const assert = require('assert'); +async function assertNoToasts(session) { + const toast = await session.query('.mx_Toast_toast'); + assert(!toast, 'toast found when none expected'); +} + async function assertToast(session, expectedTitle) { const h2Element = await session.query('.mx_Toast_title h2'); const toastTitle = await session.innerText(h2Element); @@ -34,4 +39,4 @@ async function rejectToast(session, expectedTitle) { await btn.click(); } -module.exports = {assertToast, acceptToast, rejectToast}; +module.exports = {assertNoToasts, assertToast, acceptToast, rejectToast}; From faaca43a75f5281ceb335ef1e9514836c0d8c396 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:35:15 +0100 Subject: [PATCH 24/50] more loggin' Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index b5b7fa4494..ee3f2954f3 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,18 +20,32 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); + alice.log.step(`accepts desktop notifications toast`); await acceptToast(alice, "Notifications"); + alice.log.done(); + alice.log.step(`accepts analytics toast`); await acceptToast(alice, "Help us improve Riot"); + alice.log.done(); + + alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); + alice.log.done(); alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - alice.log.step(`reject desktop notifications toast`); + + bob.log.step(`reject desktop notifications toast`); await rejectToast(bob, "Notifications"); - alice.log.step(`reject analytics toast`); + bob.log.done(); + + bob.log.step(`reject analytics toast`); await rejectToast(bob, "Help us improve Riot"); + bob.log.done(); + + bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); + bob.log.done(); bob.log.endGroup(); }; From 841ce74ac2fde0201b1b698a0b096fd5f5158467 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:39:00 +0100 Subject: [PATCH 25/50] Fix assert vs assert.equal Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/dialog.js | 2 +- test/end-to-end-tests/src/usecases/toasts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/end-to-end-tests/src/usecases/dialog.js b/test/end-to-end-tests/src/usecases/dialog.js index d4ae97dff9..15ac50bb18 100644 --- a/test/end-to-end-tests/src/usecases/dialog.js +++ b/test/end-to-end-tests/src/usecases/dialog.js @@ -20,7 +20,7 @@ const assert = require('assert'); async function assertDialog(session, expectedTitle) { const titleElement = await session.query(".mx_Dialog .mx_Dialog_title"); const dialogHeader = await session.innerText(titleElement); - assert(dialogHeader, expectedTitle); + assert.equal(dialogHeader, expectedTitle); } async function acceptDialog(session, expectedTitle) { diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 636714d66b..a82c910eea 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -24,7 +24,7 @@ async function assertNoToasts(session) { async function assertToast(session, expectedTitle) { const h2Element = await session.query('.mx_Toast_title h2'); const toastTitle = await session.innerText(h2Element); - assert(toastTitle, expectedTitle); + assert.equal(toastTitle, expectedTitle); } async function acceptToast(session, expectedTitle) { From 627b5685887d02ed31f8c1fbecc45f48d8edbbbe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 13:48:20 +0100 Subject: [PATCH 26/50] check if it is a race Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index ee3f2954f3..980ee4b33f 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -29,6 +29,7 @@ module.exports = async function toastScenarios(alice, bob) { await acceptToast(alice, "Help us improve Riot"); alice.log.done(); + await alice.delay(300); alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); alice.log.done(); @@ -44,6 +45,7 @@ module.exports = async function toastScenarios(alice, bob) { await rejectToast(bob, "Help us improve Riot"); bob.log.done(); + await bob.delay(300); bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); bob.log.done(); From 61066b0c4c915285f2bb0e5bf8c123cadbcab1f6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:27:09 +0100 Subject: [PATCH 27/50] test it again Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 22 ++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 980ee4b33f..34b979f09a 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,7 +20,6 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - alice.log.step(`accepts desktop notifications toast`); await acceptToast(alice, "Notifications"); alice.log.done(); @@ -29,14 +28,21 @@ module.exports = async function toastScenarios(alice, bob) { await acceptToast(alice, "Help us improve Riot"); alice.log.done(); - await alice.delay(300); + while (true) { + try { + const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } + alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); alice.log.done(); alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - bob.log.step(`reject desktop notifications toast`); await rejectToast(bob, "Notifications"); bob.log.done(); @@ -45,7 +51,15 @@ module.exports = async function toastScenarios(alice, bob) { await rejectToast(bob, "Help us improve Riot"); bob.log.done(); - await bob.delay(300); + while (true) { + try { + const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } + bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); bob.log.done(); From 91811581bc9412d48842a7341c8f3c33a5623170 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:35:59 +0100 Subject: [PATCH 28/50] fix assertNoToasts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/toasts.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index a82c910eea..2e88677e4b 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -17,8 +17,12 @@ limitations under the License. const assert = require('assert'); async function assertNoToasts(session) { - const toast = await session.query('.mx_Toast_toast'); - assert(!toast, 'toast found when none expected'); + try { + await session.query('.mx_Toast_toast'); + } catch (e) { + return; + } + throw new Error('toast found when none expected'); } async function assertToast(session, expectedTitle) { From f3dfdbe74644f974b855a8d494ecbc4d205fd93b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:36:44 +0100 Subject: [PATCH 29/50] debug Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 34b979f09a..f528e993c3 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -30,6 +30,9 @@ module.exports = async function toastScenarios(alice, bob) { while (true) { try { + const h2Element = await alice.query('.mx_Toast_title h2'); + const toastTitle = await alice.innerText(h2Element); + console.log("DEBUG closing", toastTitle); const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); await toastDismissButton.click(); } catch (e) { @@ -53,6 +56,9 @@ module.exports = async function toastScenarios(alice, bob) { while (true) { try { + const h2Element = await bob.query('.mx_Toast_title h2'); + const toastTitle = await bob.innerText(h2Element); + console.log("DEBUG closing", toastTitle); const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); await toastDismissButton.click(); } catch (e) { From 63ac1cb4ab583de0eb2869be09f6fc76877f0f37 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 14:56:24 +0100 Subject: [PATCH 30/50] debug some more Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index f528e993c3..97b5caa14f 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,13 +20,13 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - alice.log.step(`accepts desktop notifications toast`); - await acceptToast(alice, "Notifications"); - alice.log.done(); - - alice.log.step(`accepts analytics toast`); - await acceptToast(alice, "Help us improve Riot"); - alice.log.done(); + // alice.log.step(`accepts desktop notifications toast`); + // await acceptToast(alice, "Notifications"); + // alice.log.done(); + // + // alice.log.step(`accepts analytics toast`); + // await acceptToast(alice, "Help us improve Riot"); + // alice.log.done(); while (true) { try { @@ -46,13 +46,13 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - bob.log.step(`reject desktop notifications toast`); - await rejectToast(bob, "Notifications"); - bob.log.done(); - - bob.log.step(`reject analytics toast`); - await rejectToast(bob, "Help us improve Riot"); - bob.log.done(); + // bob.log.step(`reject desktop notifications toast`); + // await rejectToast(bob, "Notifications"); + // bob.log.done(); + // + // bob.log.step(`reject analytics toast`); + // await rejectToast(bob, "Help us improve Riot"); + // bob.log.done(); while (true) { try { From cb07fa53f4282ebfe8f12d63f93bba8344f62b9e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 15:05:40 +0100 Subject: [PATCH 31/50] test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 48 +++++--------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 97b5caa14f..02496f398d 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,25 +20,13 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - // alice.log.step(`accepts desktop notifications toast`); - // await acceptToast(alice, "Notifications"); - // alice.log.done(); - // - // alice.log.step(`accepts analytics toast`); - // await acceptToast(alice, "Help us improve Riot"); - // alice.log.done(); + alice.log.step(`accepts desktop notifications toast`); + await acceptToast(alice, "Notifications"); + alice.log.done(); - while (true) { - try { - const h2Element = await alice.query('.mx_Toast_title h2'); - const toastTitle = await alice.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + alice.log.step(`rejects analytics toast`); + await rejectToast(alice, "Help us improve Riot"); + alice.log.done(); alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); @@ -46,25 +34,13 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - // bob.log.step(`reject desktop notifications toast`); - // await rejectToast(bob, "Notifications"); - // bob.log.done(); - // - // bob.log.step(`reject analytics toast`); - // await rejectToast(bob, "Help us improve Riot"); - // bob.log.done(); + bob.log.step(`reject desktop notifications toast`); + await rejectToast(bob, "Notifications"); + bob.log.done(); - while (true) { - try { - const h2Element = await bob.query('.mx_Toast_title h2'); - const toastTitle = await bob.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + bob.log.step(`reject analytics toast`); + await rejectToast(bob, "Help us improve Riot"); + bob.log.done(); bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); From e35c9d5bbf5eec8e55a6485b275559ef39d7625f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 15:18:43 +0100 Subject: [PATCH 32/50] more testing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/AnalyticsToast.tsx | 2 + test/end-to-end-tests/src/scenarios/toast.js | 48 +++++++++++++++----- test/end-to-end-tests/src/usecases/toasts.js | 5 +- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx index b186a65d9d..7cd59222dd 100644 --- a/src/toasts/AnalyticsToast.tsx +++ b/src/toasts/AnalyticsToast.tsx @@ -24,12 +24,14 @@ import GenericToast from "../components/views/toasts/GenericToast"; import ToastStore from "../stores/ToastStore"; const onAccept = () => { + console.log("DEBUG onAccept AnalyticsToast"); dis.dispatch({ action: 'accept_cookies', }); }; const onReject = () => { + console.log("DEBUG onReject AnalyticsToast"); dis.dispatch({ action: "reject_cookies", }); diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 02496f398d..97b5caa14f 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,13 +20,25 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - alice.log.step(`accepts desktop notifications toast`); - await acceptToast(alice, "Notifications"); - alice.log.done(); + // alice.log.step(`accepts desktop notifications toast`); + // await acceptToast(alice, "Notifications"); + // alice.log.done(); + // + // alice.log.step(`accepts analytics toast`); + // await acceptToast(alice, "Help us improve Riot"); + // alice.log.done(); - alice.log.step(`rejects analytics toast`); - await rejectToast(alice, "Help us improve Riot"); - alice.log.done(); + while (true) { + try { + const h2Element = await alice.query('.mx_Toast_title h2'); + const toastTitle = await alice.innerText(h2Element); + console.log("DEBUG closing", toastTitle); + const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); @@ -34,13 +46,25 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - bob.log.step(`reject desktop notifications toast`); - await rejectToast(bob, "Notifications"); - bob.log.done(); + // bob.log.step(`reject desktop notifications toast`); + // await rejectToast(bob, "Notifications"); + // bob.log.done(); + // + // bob.log.step(`reject analytics toast`); + // await rejectToast(bob, "Help us improve Riot"); + // bob.log.done(); - bob.log.step(`reject analytics toast`); - await rejectToast(bob, "Help us improve Riot"); - bob.log.done(); + while (true) { + try { + const h2Element = await bob.query('.mx_Toast_title h2'); + const toastTitle = await bob.innerText(h2Element); + console.log("DEBUG closing", toastTitle); + const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); + await toastDismissButton.click(); + } catch (e) { + break; + } + } bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 2e88677e4b..75142ed08f 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -22,7 +22,10 @@ async function assertNoToasts(session) { } catch (e) { return; } - throw new Error('toast found when none expected'); + + const h2Element = await session.query('.mx_Toast_title h2'); + const toastTitle = await session.innerText(h2Element); + throw new Error(`"${toastTitle}" toast found when none expected`); } async function assertToast(session, expectedTitle) { From 7486338efaa31e9e2657a5458e86388c2a9e5536 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 16:51:04 +0100 Subject: [PATCH 33/50] Fix.the.tests. Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/scenarios/toast.js | 48 +++++--------------- test/end-to-end-tests/src/session.js | 4 +- test/end-to-end-tests/src/usecases/toasts.js | 9 ++-- 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/test/end-to-end-tests/src/scenarios/toast.js b/test/end-to-end-tests/src/scenarios/toast.js index 97b5caa14f..1206ef40b0 100644 --- a/test/end-to-end-tests/src/scenarios/toast.js +++ b/test/end-to-end-tests/src/scenarios/toast.js @@ -20,25 +20,13 @@ module.exports = async function toastScenarios(alice, bob) { console.log(" checking and clearing toasts:"); alice.log.startGroup(`clears toasts`); - // alice.log.step(`accepts desktop notifications toast`); - // await acceptToast(alice, "Notifications"); - // alice.log.done(); - // - // alice.log.step(`accepts analytics toast`); - // await acceptToast(alice, "Help us improve Riot"); - // alice.log.done(); + alice.log.step(`reject desktop notifications toast`); + await rejectToast(alice, "Notifications"); + alice.log.done(); - while (true) { - try { - const h2Element = await alice.query('.mx_Toast_title h2'); - const toastTitle = await alice.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await alice.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + alice.log.step(`accepts analytics toast`); + await acceptToast(alice, "Help us improve Riot"); + alice.log.done(); alice.log.step(`checks no remaining toasts`); await assertNoToasts(alice); @@ -46,25 +34,13 @@ module.exports = async function toastScenarios(alice, bob) { alice.log.endGroup(); bob.log.startGroup(`clears toasts`); - // bob.log.step(`reject desktop notifications toast`); - // await rejectToast(bob, "Notifications"); - // bob.log.done(); - // - // bob.log.step(`reject analytics toast`); - // await rejectToast(bob, "Help us improve Riot"); - // bob.log.done(); + bob.log.step(`reject desktop notifications toast`); + await rejectToast(bob, "Notifications"); + bob.log.done(); - while (true) { - try { - const h2Element = await bob.query('.mx_Toast_title h2'); - const toastTitle = await bob.innerText(h2Element); - console.log("DEBUG closing", toastTitle); - const toastDismissButton = await bob.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger'); - await toastDismissButton.click(); - } catch (e) { - break; - } - } + bob.log.step(`reject analytics toast`); + await rejectToast(bob, "Help us improve Riot"); + bob.log.done(); bob.log.step(`checks no remaining toasts`); await assertNoToasts(bob); diff --git a/test/end-to-end-tests/src/session.js b/test/end-to-end-tests/src/session.js index 55c2ed440c..907ee2fb8e 100644 --- a/test/end-to-end-tests/src/session.js +++ b/test/end-to-end-tests/src/session.js @@ -122,8 +122,8 @@ module.exports = class RiotSession { await input.type(text); } - query(selector, timeout = DEFAULT_TIMEOUT) { - return this.page.waitForSelector(selector, {visible: true, timeout}); + query(selector, timeout = DEFAULT_TIMEOUT, hidden = false) { + return this.page.waitForSelector(selector, {visible: true, timeout, hidden}); } async queryAll(selector) { diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 75142ed08f..204ed2b983 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -18,14 +18,13 @@ const assert = require('assert'); async function assertNoToasts(session) { try { - await session.query('.mx_Toast_toast'); + await session.query('.mx_Toast_toast', 1000, true); } catch (e) { - return; + const h2Element = await session.query('.mx_Toast_title h2', 1000); + const toastTitle = await session.innerText(h2Element); + throw new Error(`"${toastTitle}" toast found when none expected`); } - const h2Element = await session.query('.mx_Toast_title h2'); - const toastTitle = await session.innerText(h2Element); - throw new Error(`"${toastTitle}" toast found when none expected`); } async function assertToast(session, expectedTitle) { From 71108fcf393e4648d1dfc1fd17e0b50c8423ed12 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 27 May 2020 17:02:32 +0100 Subject: [PATCH 34/50] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/toasts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/end-to-end-tests/src/usecases/toasts.js b/test/end-to-end-tests/src/usecases/toasts.js index 204ed2b983..db78352f2b 100644 --- a/test/end-to-end-tests/src/usecases/toasts.js +++ b/test/end-to-end-tests/src/usecases/toasts.js @@ -24,7 +24,6 @@ async function assertNoToasts(session) { const toastTitle = await session.innerText(h2Element); throw new Error(`"${toastTitle}" toast found when none expected`); } - } async function assertToast(session, expectedTitle) { From 73c35ff80e0a1d9a6323cbdd49df5a7fe4f83cbc Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 28 May 2020 00:05:45 -0400 Subject: [PATCH 35/50] set the client's pickle key if the platform can store one --- src/BasePlatform.js | 31 +++++++++++++++++++++++++++++++ src/Lifecycle.js | 15 ++++++++++++--- src/MatrixClientPeg.js | 1 + 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 8a950dc2e3..f4fa43cb10 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -190,4 +190,35 @@ export default class BasePlatform { onKeyDown(ev: KeyboardEvent): boolean { return false; // no shortcuts implemented } + + /** + * Get a previously stored pickle key. The pickle key is used for + * encrypting libolm objects. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + * @returns {string|null} the previously stored pickle key, or null if no + * pickle key has been stored. + */ + async getPickleKey(userId: string, deviceId: string): string | null { + return null; + } + + /** + * Create and store a pickle key for encrypting libolm objects. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + * @returns {string|null} the pickle key, or null if the platform does not + * support storing pickle keys. + */ + async createPickleKey(userId: string, deviceId: string): string | null { + return null; + } + + /** + * Delete a previously stored pickle key from storage. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + */ + async destroyPickleKey(userId: string, deviceId: string) { + } } diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 1baa6c8e0c..598624293b 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -298,6 +298,8 @@ async function _restoreFromLocalStorage(opts) { return false; } + const pickleKey = await PlatformPeg.get().getPickleKey(userId, deviceId); + console.log(`Restoring session for ${userId}`); await _doSetLoggedIn({ userId: userId, @@ -306,6 +308,7 @@ async function _restoreFromLocalStorage(opts) { homeserverUrl: hsUrl, identityServerUrl: isUrl, guest: isGuest, + pickleKey: pickleKey, }, false); return true; } else { @@ -348,9 +351,13 @@ async function _handleLoadSessionFailure(e) { * * @returns {Promise} promise which resolves to the new MatrixClient once it has been started */ -export function setLoggedIn(credentials) { +export async function setLoggedIn(credentials) { stopMatrixClient(); - return _doSetLoggedIn(credentials, true); + const pickleKey = credentials.userId && credentials.deviceId + ? await PlatformPeg.get().createPickleKey(credentials.userId, credentials.deviceId) + : null; + + return _doSetLoggedIn(Object.assign({}, credentials, {pickleKey}), true); } /** @@ -516,7 +523,9 @@ export function logout() { } _isLoggingOut = true; - MatrixClientPeg.get().logout().then(onLoggedOut, + const client = MatrixClientPeg.get(); + PlatformPeg.get().destroyPickleKey(client.getUserId(), client.getDeviceId()); + client.logout().then(onLoggedOut, (err) => { // Just throwing an error here is going to be very unhelpful // if you're trying to log out because your server's down and diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 21f05b9759..af43705227 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -218,6 +218,7 @@ class _MatrixClientPeg { accessToken: creds.accessToken, userId: creds.userId, deviceId: creds.deviceId, + pickleKey: creds.pickleKey, timelineSupport: true, forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), fallbackICEServerAllowed: !!SettingsStore.getValue('fallbackICEServerAllowed'), From 03058bacbd07dfda8bb1ef34926a7861d51d3ee4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 28 May 2020 16:48:50 +0100 Subject: [PATCH 36/50] Dialog wrap title instead of taking same space as the close/cancel button Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_common.scss | 3 +++ src/components/views/dialogs/BaseDialog.js | 1 + 2 files changed, 4 insertions(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 03442ca510..ebeeb381e6 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -335,6 +335,9 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { .mx_Dialog_header.mx_Dialog_headerWithButton > .mx_Dialog_title { text-align: center; } +.mx_Dialog_header.mx_Dialog_headerWithCancel > .mx_Dialog_title { + margin-right: 20px; // leave space for the 'X' cancel button +} .mx_Dialog_title.danger { color: $warning-color; diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js index 67d70aabe4..e59b6bbaf5 100644 --- a/src/components/views/dialogs/BaseDialog.js +++ b/src/components/views/dialogs/BaseDialog.js @@ -144,6 +144,7 @@ export default createReactClass({ >
{headerImage} From 0859827910a250d9d72bf16523376a13a2375323 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 28 May 2020 11:56:48 -0400 Subject: [PATCH 37/50] fix types --- src/BasePlatform.ts | 6 +++--- src/MatrixClientPeg.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 14682728d0..ed04c67803 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -189,7 +189,7 @@ export default abstract class BasePlatform { * @returns {string|null} the previously stored pickle key, or null if no * pickle key has been stored. */ - async getPickleKey(userId: string, deviceId: string): string | null { + async getPickleKey(userId: string, deviceId: string): Promise { return null; } @@ -200,7 +200,7 @@ export default abstract class BasePlatform { * @returns {string|null} the pickle key, or null if the platform does not * support storing pickle keys. */ - async createPickleKey(userId: string, deviceId: string): string | null { + async createPickleKey(userId: string, deviceId: string): Promise { return null; } @@ -209,6 +209,6 @@ export default abstract class BasePlatform { * @param {string} userId the user ID for the user that the pickle key is for. * @param {string} userId the device ID that the pickle key is for. */ - async destroyPickleKey(userId: string, deviceId: string) { + async destroyPickleKey(userId: string, deviceId: string): Promise { } } diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index ddeff39216..e875a053b5 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -41,6 +41,7 @@ export interface IMatrixClientCreds { deviceId: string, accessToken: string, guest: boolean, + pickleKey: string, } // TODO: Move this to the js-sdk From dc4a2191c1b76b83ae4bb75f80d2de6a1c65557b Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 28 May 2020 12:04:32 -0400 Subject: [PATCH 38/50] really fix types --- src/MatrixClientPeg.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index e875a053b5..c6ee6c546f 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -41,7 +41,7 @@ export interface IMatrixClientCreds { deviceId: string, accessToken: string, guest: boolean, - pickleKey: string, + pickleKey?: string, } // TODO: Move this to the js-sdk From c4d6367a178b2df35d628e4060028fd3aa2eb93d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 28 May 2020 17:30:43 +0100 Subject: [PATCH 39/50] Swap priorities of Desktop Notifications and Update toasts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/toasts/DesktopNotificationsToast.ts | 2 +- src/toasts/UpdateToast.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/toasts/DesktopNotificationsToast.ts b/src/toasts/DesktopNotificationsToast.ts index 02f0730759..413e82e20b 100644 --- a/src/toasts/DesktopNotificationsToast.ts +++ b/src/toasts/DesktopNotificationsToast.ts @@ -41,7 +41,7 @@ export const showToast = () => { onReject, }, component: GenericToast, - priority: 20, + priority: 30, }); }; diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx index 55c128e86a..3d4b55a4ff 100644 --- a/src/toasts/UpdateToast.tsx +++ b/src/toasts/UpdateToast.tsx @@ -81,7 +81,7 @@ export const showToast = (version: string, newVersion: string, releaseNotes?: st onAccept, }, component: GenericToast, - priority: 30, + priority: 20, }); }; From 2530ec841947dacbc40db1ead96fe05041fc1219 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 28 May 2020 19:03:42 +0100 Subject: [PATCH 40/50] Try and fix the Notifier race Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 7 +------ src/components/structures/MatrixChat.tsx | 5 +++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index cc804904e2..fac6f4e854 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -27,8 +27,7 @@ import { _t } from './languageHandler'; import Modal from './Modal'; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; import { - showToast as showNotificationsToast, - hideToast as hideNotificationsToast, + hideToast as hideNotificationsToast, showToast as showNotificationsToast, } from "./toasts/DesktopNotificationsToast"; /* @@ -188,10 +187,6 @@ const Notifier = { MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange); this.toolbarHidden = false; this.isSyncing = false; - - if (this.shouldShowToolbar()) { - showNotificationsToast(); - } }, stop: function() { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index fe50b80140..b70d6ed3eb 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -71,6 +71,7 @@ import { showToast as showAnalyticsToast, hideToast as hideAnalyticsToast } from "../../toasts/AnalyticsToast"; +import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast"; /** constants for MatrixChat.state.view */ export enum Views { @@ -1364,6 +1365,10 @@ export default class MatrixChat extends React.PureComponent { this.firstSyncComplete = true; this.firstSyncPromise.resolve(); + if (Notifier.shouldShowToolbar()) { + showNotificationsToast(); + } + dis.dispatch({action: 'focus_composer'}); this.setState({ ready: true, From 227fc24d3aedba79a30eaf3963a9e9e8fefcba2a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 28 May 2020 19:04:34 +0100 Subject: [PATCH 41/50] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Notifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Notifier.js b/src/Notifier.js index fac6f4e854..cd328ba565 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -27,7 +27,7 @@ import { _t } from './languageHandler'; import Modal from './Modal'; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; import { - hideToast as hideNotificationsToast, showToast as showNotificationsToast, + hideToast as hideNotificationsToast, } from "./toasts/DesktopNotificationsToast"; /* From 6559fd499e45e94059bbabfb9e59cb465e111d87 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 28 May 2020 21:09:42 +0100 Subject: [PATCH 42/50] Fix field placeholder regression --- src/components/views/auth/PasswordLogin.js | 2 +- src/components/views/auth/RegistrationForm.js | 2 +- src/components/views/elements/RoomAliasField.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index 790c837497..3bd9b557bc 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -238,7 +238,7 @@ export default class PasswordLogin extends React.Component { type="text" label={_t("Phone")} value={this.state.phoneNumber} - prefix={phoneCountry} + prefixComponent={phoneCountry} onChange={this.onPhoneNumberChanged} onBlur={this.onPhoneNumberBlur} disabled={this.props.disableSubmit} diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 7bbd15d8d3..17c65fa94e 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -473,7 +473,7 @@ export default createReactClass({ type="text" label={phoneLabel} value={this.state.phoneNumber} - prefix={phoneCountry} + prefixComponent={phoneCountry} onChange={this.onPhoneNumberChange} onValidate={this.onPhoneNumberValidate} />; diff --git a/src/components/views/elements/RoomAliasField.js b/src/components/views/elements/RoomAliasField.js index ee18913971..04bbe1c3de 100644 --- a/src/components/views/elements/RoomAliasField.js +++ b/src/components/views/elements/RoomAliasField.js @@ -47,8 +47,8 @@ export default class RoomAliasField extends React.PureComponent { this._fieldRef = ref} onValidate={this._onValidate} placeholder={_t("e.g. my-room")} From ea064afca41f1f830d0c1391fa5bd78051c10478 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 May 2020 08:23:59 -0600 Subject: [PATCH 43/50] Fix/document a number of UIA oddities Edition 2 of https://github.com/matrix-org/matrix-react-sdk/pull/3211 Fixes https://github.com/vector-im/riot-web/issues/13837 --- src/PasswordReset.js | 6 ++++++ .../dialogs/secretstorage/CreateSecretStorageDialog.js | 3 ++- src/components/views/auth/InteractiveAuthEntryComponents.js | 1 + src/components/views/settings/ChangePassword.js | 6 ++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/PasswordReset.js b/src/PasswordReset.js index 320599f6d9..9472ddc633 100644 --- a/src/PasswordReset.js +++ b/src/PasswordReset.js @@ -84,8 +84,14 @@ export default class PasswordReset { try { await this.client.setPassword({ + // Note: Though this sounds like a login type for identity servers only, it + // has a dual purpose of being used for homeservers too. type: "m.login.email.identity", + // TODO: Remove `threepid_creds` once servers support proper UIA + // See https://github.com/matrix-org/synapse/issues/5665 + // See https://github.com/matrix-org/matrix-doc/issues/2220 threepid_creds: creds, + threepidCreds: creds, }, this.password); } catch (err) { if (err.httpStatus === 401) { diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index e6ab07c449..d7b79c2cfa 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -201,7 +201,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { type: 'm.id.user', user: MatrixClientPeg.get().getUserId(), }, - // https://github.com/matrix-org/synapse/issues/5665 + // TODO: Remove `user` once servers support proper UIA + // See https://github.com/matrix-org/synapse/issues/5665 user: MatrixClientPeg.get().getUserId(), password: this.state.accountPassword, }); diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js index 655452fcee..c6d61e38b3 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -538,6 +538,7 @@ export const MsisdnAuthEntry = createReactClass({ type: MsisdnAuthEntry.LOGIN_TYPE, // TODO: Remove `threepid_creds` once servers support proper UIA // See https://github.com/vector-im/riot-web/issues/10312 + // See https://github.com/matrix-org/matrix-doc/issues/2220 threepid_creds: creds, threepidCreds: creds, }); diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index c7eccf2145..a4ff65d8f9 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -141,6 +141,12 @@ export default createReactClass({ _changePassword: function(cli, oldPassword, newPassword) { const authDict = { type: 'm.login.password', + identifier: { + type: 'm.id.user', + user: cli.credentials.userId, + }, + // TODO: Remove `user` once servers support proper UIA + // See https://github.com/matrix-org/synapse/issues/5665 user: cli.credentials.userId, password: oldPassword, }; From efd0da44a1b1fb8f58e8bdac4ea66fa77af2cc32 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 May 2020 18:24:45 +0100 Subject: [PATCH 44/50] Give contextual feedback for manual update check instead of banner Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .eslintignore.errorfiles | 1 - res/css/_components.scss | 2 +- res/css/structures/_MatrixChat.scss | 4 - res/css/views/globals/_MatrixToolbar.scss | 69 -------------- .../views/settings/_UpdateCheckButton.scss | 23 +++++ src/BasePlatform.ts | 24 +++++ src/components/structures/LoggedInView.tsx | 20 ---- src/components/structures/MatrixChat.tsx | 6 -- .../views/globals/UpdateCheckBar.js | 91 ------------------- .../views/settings/UpdateCheckButton.tsx | 90 ++++++++++++++++++ .../settings/tabs/user/HelpUserSettingsTab.js | 8 +- src/dispatcher/actions.ts | 7 +- .../payloads/CheckUpdatesPayload.ts | 33 +++++++ src/hooks/useDispatcher.ts | 40 ++++++++ src/i18n/strings/en_EN.json | 11 ++- src/utils/ResizeNotifier.js | 5 - 16 files changed, 225 insertions(+), 209 deletions(-) delete mode 100644 res/css/views/globals/_MatrixToolbar.scss create mode 100644 res/css/views/settings/_UpdateCheckButton.scss delete mode 100644 src/components/views/globals/UpdateCheckBar.js create mode 100644 src/components/views/settings/UpdateCheckButton.tsx create mode 100644 src/dispatcher/payloads/CheckUpdatesPayload.ts create mode 100644 src/hooks/useDispatcher.ts diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index ffc3b21181..7e88ebff7f 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -16,7 +16,6 @@ src/components/views/elements/MemberEventListSummary.js src/components/views/elements/UserSelector.js src/components/views/globals/MatrixToolbar.js src/components/views/globals/NewVersionBar.js -src/components/views/globals/UpdateCheckBar.js src/components/views/messages/MFileBody.js src/components/views/messages/TextualBody.js src/components/views/room_settings/ColorSettings.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 44c63b9df7..bcf7b97683 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -123,7 +123,6 @@ @import "./views/elements/_TooltipButton.scss"; @import "./views/elements/_Validation.scss"; @import "./views/emojipicker/_EmojiPicker.scss"; -@import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; @import "./views/groups/_GroupRoomList.scss"; @import "./views/groups/_GroupUserSettings.scss"; @@ -202,6 +201,7 @@ @import "./views/settings/_ProfileSettings.scss"; @import "./views/settings/_SetIdServer.scss"; @import "./views/settings/_SetIntegrationManager.scss"; +@import "./views/settings/_UpdateCheckButton.scss"; @import "./views/settings/tabs/_SettingsTab.scss"; @import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index c5a5d50068..05c703ab6d 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -41,10 +41,6 @@ limitations under the License. height: 40px; } -.mx_MatrixChat_toolbarShowing { - height: auto; -} - .mx_MatrixChat { width: 100%; height: 100%; diff --git a/res/css/views/globals/_MatrixToolbar.scss b/res/css/views/globals/_MatrixToolbar.scss deleted file mode 100644 index 07b92a7235..0000000000 --- a/res/css/views/globals/_MatrixToolbar.scss +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket 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. -*/ - -.mx_MatrixToolbar { - background-color: $accent-color; - color: $accent-fg-color; - - display: flex; - align-items: center; -} - -.mx_MatrixToolbar_warning { - margin-left: 16px; - margin-right: 8px; - margin-top: -2px; -} - -.mx_MatrixToolbar_info { - padding-left: 16px; - padding-right: 8px; - background-color: $info-bg-color; -} - -.mx_MatrixToolbar_error { - padding-left: 16px; - padding-right: 8px; - background-color: $warning-bg-color; -} - -.mx_MatrixToolbar_content { - flex: 1; -} - -.mx_MatrixToolbar_link { - color: $accent-fg-color !important; - text-decoration: underline !important; - cursor: pointer; -} - -.mx_MatrixToolbar_clickable { - cursor: pointer; -} - -.mx_MatrixToolbar_close { - cursor: pointer; -} - -.mx_MatrixToolbar_close img { - display: block; - float: right; - margin-right: 10px; -} - -.mx_MatrixToolbar_action { - margin-right: 16px; -} diff --git a/res/css/views/settings/_UpdateCheckButton.scss b/res/css/views/settings/_UpdateCheckButton.scss new file mode 100644 index 0000000000..f35a023ac1 --- /dev/null +++ b/res/css/views/settings/_UpdateCheckButton.scss @@ -0,0 +1,23 @@ +/* +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. +*/ + +.mx_UpdateCheckButton_summary { + margin-left: 16px; + + .mx_AccessibleButton_kind_link { + padding: 0; + } +} diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index ed04c67803..0465b434d0 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -21,6 +21,16 @@ import {MatrixClient} from "matrix-js-sdk/src/client"; import dis from './dispatcher/dispatcher'; import BaseEventIndexManager from './indexing/BaseEventIndexManager'; import {ActionPayload} from "./dispatcher/payloads"; +import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload"; +import {Action} from "./dispatcher/actions"; + +export enum UpdateCheckStatus { + Checking = "CHECKING", + Error = "ERROR", + NotAvailable = "NOTAVAILABLE", + Downloading = "DOWNLOADING", + Ready = "READY", +} /** * Base class for classes that provide platform-specific functionality @@ -56,6 +66,20 @@ export default abstract class BasePlatform { this.errorDidOccur = errorDidOccur; } + /** + * Whether we can call checkForUpdate on this platform build + */ + async canSelfUpdate(): Promise { + return false; + } + + startUpdateCheck() { + dis.dispatch({ + action: Action.CheckUpdates, + status: UpdateCheckStatus.Checking, + }); + } + /** * Returns true if the platform supports displaying * notifications, otherwise false. diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 06ba3e49c9..1ad38c6f04 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -81,7 +81,6 @@ interface IProps { currentRoomId: string; ConferenceHandler?: object; collapseLhs: boolean; - checkingForUpdate: boolean; config: { piwik: { policyUrl: string; @@ -177,15 +176,6 @@ class LoggedInView extends React.PureComponent { this._loadResizerPreferences(); } - componentDidUpdate(prevProps, prevState) { - // attempt to guess when a banner was opened or closed - if ( - (prevProps.checkingForUpdate !== this.props.checkingForUpdate) - ) { - this.props.resizeNotifier.notifyBannersChanged(); - } - } - componentWillUnmount() { document.removeEventListener('keydown', this._onNativeKeyDown, false); this._matrixClient.removeListener("accountData", this.onAccountData); @@ -617,7 +607,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); let pageElement; @@ -661,15 +650,7 @@ class LoggedInView extends React.PureComponent { break; } - let topBar; - if (this.props.checkingForUpdate) { - topBar = ; - } - let bodyClasses = 'mx_MatrixChat'; - if (topBar) { - bodyClasses += ' mx_MatrixChat_toolbarShowing'; - } if (this.state.useCompactLayout) { bodyClasses += ' mx_MatrixChat_useCompactLayout'; } @@ -684,7 +665,6 @@ class LoggedInView extends React.PureComponent { onMouseDown={this._onMouseDown} onMouseUp={this._onMouseUp} > - { topBar }
diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index b70d6ed3eb..058a7ba50b 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -173,7 +173,6 @@ interface IState { leftDisabled: boolean; middleDisabled: boolean; // the right panel's disabled state is tracked in its store. - checkingForUpdate?: string; // updateCheckStatusEnum // Parameters used in the registration dance with the IS register_client_secret?: string; register_session_id?: string; @@ -226,8 +225,6 @@ export default class MatrixChat extends React.PureComponent { leftDisabled: false, middleDisabled: false, - checkingForUpdate: null, - hideToSRUsers: false, syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. @@ -720,9 +717,6 @@ export default class MatrixChat extends React.PureComponent { case 'client_started': this.onClientStarted(); break; - case 'check_updates': - this.setState({ checkingForUpdate: payload.value }); - break; case 'send_event': this.onSendEvent(payload.room_id, payload.event); break; diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js deleted file mode 100644 index 32b38ff5b0..0000000000 --- a/src/components/views/globals/UpdateCheckBar.js +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2017, 2019 Michael Telatynski <7t3chguy@gmail.com> - -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 createReactClass from 'create-react-class'; -import { _t } from '../../../languageHandler'; -import PlatformPeg from '../../../PlatformPeg'; -import AccessibleButton from '../../../components/views/elements/AccessibleButton'; - -export default createReactClass({ - propTypes: { - status: PropTypes.string.isRequired, - // Currently for error detail but will be usable for download progress - // once that is a thing that squirrel passes through electron. - detail: PropTypes.string, - }, - - getDefaultProps: function() { - return { - detail: '', - }; - }, - - getStatusText: function() { - // we can't import the enum from riot-web as we don't want matrix-react-sdk - // to depend on riot-web. so we grab it as a normal object via API instead. - const updateCheckStatusEnum = PlatformPeg.get().getUpdateCheckStatusEnum(); - switch (this.props.status) { - case updateCheckStatusEnum.ERROR: - return _t('Error encountered (%(errorDetail)s).', { errorDetail: this.props.detail }); - case updateCheckStatusEnum.CHECKING: - return _t('Checking for an update...'); - case updateCheckStatusEnum.NOTAVAILABLE: - return _t('No update available.'); - case updateCheckStatusEnum.DOWNLOADING: - return _t('Downloading update...'); - } - }, - - hideToolbar: function() { - PlatformPeg.get().stopUpdateCheck(); - }, - - render: function() { - const message = this.getStatusText(); - const warning = _t('Warning'); - - if (!('getUpdateCheckStatusEnum' in PlatformPeg.get())) { - return
; - } - - const updateCheckStatusEnum = PlatformPeg.get().getUpdateCheckStatusEnum(); - const doneStatuses = [ - updateCheckStatusEnum.ERROR, - updateCheckStatusEnum.NOTAVAILABLE, - ]; - - let image; - if (doneStatuses.includes(this.props.status)) { - image = ; - } else { - image = ; - } - - return ( -
- {image} -
- {message} -
- - {_t('Close')} - -
- ); - }, -}); diff --git a/src/components/views/settings/UpdateCheckButton.tsx b/src/components/views/settings/UpdateCheckButton.tsx new file mode 100644 index 0000000000..3f7c11dfc8 --- /dev/null +++ b/src/components/views/settings/UpdateCheckButton.tsx @@ -0,0 +1,90 @@ +/* +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, {useState} from "react"; + +import {UpdateCheckStatus} from "../../../BasePlatform"; +import PlatformPeg from "../../../PlatformPeg"; +import {hideToast as hideUpdateToast} from "../../../toasts/UpdateToast"; +import {useDispatcher} from "../../../hooks/useDispatcher"; +import dis from "../../../dispatcher/dispatcher"; +import {Action} from "../../../dispatcher/actions"; +import {_t} from "../../../languageHandler"; +import InlineSpinner from "../../../components/views/elements/InlineSpinner"; +import AccessibleButton from "../../../components/views/elements/AccessibleButton"; +import {CheckUpdatesPayload} from "../../../dispatcher/payloads/CheckUpdatesPayload"; + +function installUpdate() { + PlatformPeg.get().installUpdate(); +} + +function getStatusText(status: UpdateCheckStatus, errorDetail?: string) { + switch (status) { + case UpdateCheckStatus.Error: + return _t('Error encountered (%(errorDetail)s).', { errorDetail }); + case UpdateCheckStatus.Checking: + return _t('Checking for an update...'); + case UpdateCheckStatus.NotAvailable: + return _t('No update available.'); + case UpdateCheckStatus.Downloading: + return _t('Downloading update...'); + case UpdateCheckStatus.Ready: + return _t("New version available. Update now.", {}, { + a: sub => {sub} + }); + } +} + +const doneStatuses = [ + UpdateCheckStatus.Ready, + UpdateCheckStatus.Error, + UpdateCheckStatus.NotAvailable, +]; + +const UpdateCheckButton = () => { + const [state, setState] = useState(null); + + const onCheckForUpdateClick = () => { + setState(null); + PlatformPeg.get().startUpdateCheck(); + hideUpdateToast(); + }; + + useDispatcher(dis, ({action, ...params}) => { + if (action === Action.CheckUpdates) { + setState(params as CheckUpdatesPayload); + } + }); + + const busy = state && !doneStatuses.includes(state.status); + + let suffix; + if (state) { + suffix = + {getStatusText(state.status, state.detail)} + {busy && } + ; + } + + return + + {_t("Check for update")} + + { suffix } + ; +}; + +export default UpdateCheckButton; diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js index 146d841d58..bec79b97c4 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js @@ -25,6 +25,7 @@ import Modal from "../../../../../Modal"; import * as sdk from "../../../../../"; import PlatformPeg from "../../../../../PlatformPeg"; import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts"; +import UpdateCheckButton from "../../UpdateCheckButton"; export default class HelpUserSettingsTab extends React.Component { static propTypes = { @@ -177,12 +178,7 @@ export default class HelpUserSettingsTab extends React.Component { let updateButton = null; if (this.state.canUpdate) { - const platform = PlatformPeg.get(); - updateButton = ( - - {_t('Check for update')} - - ); + updateButton = ; } return ( diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index ba72daa00d..c1d6fd5c54 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -45,8 +45,13 @@ export enum Action { ViewTooltip = "view_tooltip", /** - * Forces the theme to reload. No additional payload information required. + * Forces the theme to reload. No additional payload information required. */ RecheckTheme = "recheck_theme", + + /** + * Response to manual update check, will fire multiple times during the update check. + */ + CheckUpdates = "check_updates", } diff --git a/src/dispatcher/payloads/CheckUpdatesPayload.ts b/src/dispatcher/payloads/CheckUpdatesPayload.ts new file mode 100644 index 0000000000..0f0f9a01e5 --- /dev/null +++ b/src/dispatcher/payloads/CheckUpdatesPayload.ts @@ -0,0 +1,33 @@ +/* +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 { ActionPayload } from "../payloads"; +import { Action } from "../actions"; +import {UpdateCheckStatus} from "../../BasePlatform"; + +export interface CheckUpdatesPayload extends ActionPayload { + action: Action.CheckUpdates, + + /** + * The current phase of the manual update check. + */ + status: UpdateCheckStatus; + + /** + * Detail string relating to the current status, typically for error details. + */ + detail?: string; +} diff --git a/src/hooks/useDispatcher.ts b/src/hooks/useDispatcher.ts new file mode 100644 index 0000000000..004b15fcef --- /dev/null +++ b/src/hooks/useDispatcher.ts @@ -0,0 +1,40 @@ +/* +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 {useEffect, useRef} from "react"; + +import {ActionPayload} from "../dispatcher/payloads"; +import {Dispatcher} from "flux"; + +// Hook to simplify listening to flux dispatches +export const useDispatcher = (dispatcher: Dispatcher, handler: (payload: ActionPayload) => void) => { + // Create a ref that stores handler + const savedHandler = useRef((payload: ActionPayload) => {}); + + // Update ref.current value if handler changes. + useEffect(() => { + savedHandler.current = handler; + }, [handler]); + + useEffect(() => { + // Create event listener that calls handler function stored in ref + const ref = dispatcher.register((payload) => savedHandler.current(payload)); + // Remove event listener on cleanup + return () => { + dispatcher.unregister(ref); + }; + }, [dispatcher]); +}; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ba55ee9d64..8f7e8ea6b6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -772,6 +772,12 @@ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use an Integration Manager to manage bots, widgets, and sticker packs.", "Manage integrations": "Manage integrations", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.", + "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", + "Checking for an update...": "Checking for an update...", + "No update available.": "No update available.", + "Downloading update...": "Downloading update...", + "New version available. Update now.": "New version available. Update now.", + "Check for update": "Check for update", "Size must be a number": "Size must be a number", "Custom font size can only be between %(min)s pt and %(max)s pt": "Custom font size can only be between %(min)s pt and %(max)s pt", "Use between %(min)s pt and %(max)s pt": "Use between %(min)s pt and %(max)s pt", @@ -804,7 +810,6 @@ "For help with using Riot, click here.": "For help with using Riot, click here.", "For help with using Riot, click here or start a chat with our bot using the button below.": "For help with using Riot, click here or start a chat with our bot using the button below.", "Chat with Riot Bot": "Chat with Riot Bot", - "Check for update": "Check for update", "Help & About": "Help & About", "Bug reporting": "Bug reporting", "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.", @@ -1397,10 +1402,6 @@ "Something went wrong when trying to get your communities.": "Something went wrong when trying to get your communities.", "Display your community flair in rooms configured to show it.": "Display your community flair in rooms configured to show it.", "You're not currently a member of any communities.": "You're not currently a member of any communities.", - "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", - "Checking for an update...": "Checking for an update...", - "No update available.": "No update available.", - "Downloading update...": "Downloading update...", "Frequently Used": "Frequently Used", "Smileys & People": "Smileys & People", "Animals & Nature": "Animals & Nature", diff --git a/src/utils/ResizeNotifier.js b/src/utils/ResizeNotifier.js index 35ec1a0269..d65bc4bd07 100644 --- a/src/utils/ResizeNotifier.js +++ b/src/utils/ResizeNotifier.js @@ -29,11 +29,6 @@ export default class ResizeNotifier extends EventEmitter { this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200); } - notifyBannersChanged() { - this.emit("leftPanelResized"); - this.emit("middlePanelResized"); - } - // can be called in quick succession notifyLeftHandleResized() { // don't emit event for own region From f45c584c8a1993d045373e047bb1af300f14f18d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 May 2020 19:13:59 +0100 Subject: [PATCH 45/50] Correct the GenericToast props to show the two modes of operation Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/@types/common.ts | 19 +++++++++++++++++++ src/components/views/toasts/GenericToast.tsx | 10 +++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/@types/common.ts diff --git a/src/@types/common.ts b/src/@types/common.ts new file mode 100644 index 0000000000..26e5317aa3 --- /dev/null +++ b/src/@types/common.ts @@ -0,0 +1,19 @@ +/* +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. +*/ + +// Based on https://stackoverflow.com/a/53229857/3532235 +export type Without = {[P in Exclude] ? : never} +export type XOR = (T | U) extends object ? (Without & U) | (Without & T) : T | U; diff --git a/src/components/views/toasts/GenericToast.tsx b/src/components/views/toasts/GenericToast.tsx index 9d69330857..ea12641948 100644 --- a/src/components/views/toasts/GenericToast.tsx +++ b/src/components/views/toasts/GenericToast.tsx @@ -17,17 +17,21 @@ limitations under the License. import React, {ReactChild} from "react"; import FormButton from "../elements/FormButton"; +import {XOR} from "../../../@types/common"; interface IProps { description: ReactChild; acceptLabel: string; - rejectLabel?: string; onAccept(); - onReject?(); } -const GenericToast: React.FC = ({description, acceptLabel, rejectLabel, onAccept, onReject}) => { +interface IPropsExtended extends IProps { + rejectLabel: string; + onReject(); +} + +const GenericToast: React.FC> = ({description, acceptLabel, rejectLabel, onAccept, onReject}) => { return
{ description } From 9431393bdaa107be1505a698963ac79eba25e917 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 May 2020 19:59:47 +0100 Subject: [PATCH 46/50] Allow deferring of Update Toast until the next morning Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.ts | 36 +++++++++++++++++++ .../views/settings/UpdateCheckButton.tsx | 2 -- src/toasts/UpdateToast.tsx | 6 ++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 0465b434d0..c5f58f7f0c 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -23,6 +23,7 @@ import BaseEventIndexManager from './indexing/BaseEventIndexManager'; import {ActionPayload} from "./dispatcher/payloads"; import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload"; import {Action} from "./dispatcher/actions"; +import {hideToast as hideUpdateToast} from "./toasts/UpdateToast"; export enum UpdateCheckStatus { Checking = "CHECKING", @@ -32,6 +33,8 @@ export enum UpdateCheckStatus { Ready = "READY", } +const UPDATE_DEFER_KEY = "mx_defer_update"; + /** * Base class for classes that provide platform-specific functionality * eg. Setting an application badge or displaying notifications @@ -74,12 +77,45 @@ export default abstract class BasePlatform { } startUpdateCheck() { + hideUpdateToast(); + localStorage.removeItem(UPDATE_DEFER_KEY); dis.dispatch({ action: Action.CheckUpdates, status: UpdateCheckStatus.Checking, }); } + /** + * Update the currently running app to the latest available version + * and replace this instance of the app with the new version. + */ + installUpdate() { + } + + /** + * Check if the version update has been deferred and that deferment is still in effect + * @param newVersion the version string to check + */ + protected shouldShowUpdate(newVersion: string): boolean { + try { + const [version, deferUntil] = JSON.parse(localStorage.getItem(UPDATE_DEFER_KEY)); + return newVersion !== version || Date.now() > deferUntil; + } catch (e) { + return true; + } + } + + /** + * Ignore the pending update and don't prompt about this version + * until the next morning (8am). + */ + deferUpdate(newVersion: string) { + const date = new Date(Date.now() + 24 * 60 * 60 * 1000); + date.setHours(8, 0, 0, 0); // set to next 8am + localStorage.setItem(UPDATE_DEFER_KEY, JSON.stringify([newVersion, date.getTime()])); + hideUpdateToast(); + } + /** * Returns true if the platform supports displaying * notifications, otherwise false. diff --git a/src/components/views/settings/UpdateCheckButton.tsx b/src/components/views/settings/UpdateCheckButton.tsx index 3f7c11dfc8..10e0e29f31 100644 --- a/src/components/views/settings/UpdateCheckButton.tsx +++ b/src/components/views/settings/UpdateCheckButton.tsx @@ -18,7 +18,6 @@ import React, {useState} from "react"; import {UpdateCheckStatus} from "../../../BasePlatform"; import PlatformPeg from "../../../PlatformPeg"; -import {hideToast as hideUpdateToast} from "../../../toasts/UpdateToast"; import {useDispatcher} from "../../../hooks/useDispatcher"; import dis from "../../../dispatcher/dispatcher"; import {Action} from "../../../dispatcher/actions"; @@ -60,7 +59,6 @@ const UpdateCheckButton = () => { const onCheckForUpdateClick = () => { setState(null); PlatformPeg.get().startUpdateCheck(); - hideUpdateToast(); }; useDispatcher(dis, ({action, ...params}) => { diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx index 3d4b55a4ff..7a8d3671db 100644 --- a/src/toasts/UpdateToast.tsx +++ b/src/toasts/UpdateToast.tsx @@ -40,6 +40,10 @@ function installUpdate() { } export const showToast = (version: string, newVersion: string, releaseNotes?: string) => { + function onReject() { + PlatformPeg.get().deferUpdate(newVersion); + } + let onAccept; let acceptLabel = _t("What's new?"); if (releaseNotes) { @@ -79,6 +83,8 @@ export const showToast = (version: string, newVersion: string, releaseNotes?: st description: _t("A new version of Riot is available!"), acceptLabel, onAccept, + rejectLabel: _t("Later"), + onReject, }, component: GenericToast, priority: 20, From 0bbf971bac5e2c47909a84d9d95418ee3ce9ca2d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 May 2020 20:50:47 +0100 Subject: [PATCH 47/50] Update src/dispatcher/actions.ts Co-authored-by: Travis Ralston --- src/dispatcher/actions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index c1d6fd5c54..7e76ea5ccb 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -50,8 +50,7 @@ export enum Action { RecheckTheme = "recheck_theme", /** - * Response to manual update check, will fire multiple times during the update check. + * Provide status information for an ongoing update check. Should be used with a CheckUpdatesPayload. */ CheckUpdates = "check_updates", } - From f3fedc5fc4f8315a83ed46ab3891eb25b7ab8e39 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 30 May 2020 00:42:23 +0100 Subject: [PATCH 48/50] fix url --- code_style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_style.md b/code_style.md index 01c1f37146..8c8e40fa18 100644 --- a/code_style.md +++ b/code_style.md @@ -4,7 +4,7 @@ Matrix JavaScript/ECMAScript Style Guide The intention of this guide is to make Matrix's JavaScript codebase clean, consistent with other popular JavaScript styles and consistent with the rest of the Matrix codebase. For reference, the Matrix Python style guide can be found -at https://github.com/matrix-org/synapse/blob/master/docs/code_style.rst +at https://github.com/matrix-org/synapse/blob/master/docs/code_style.md This document reflects how we would like Matrix JavaScript code to look, with acknowledgement that a significant amount of code is written to older From 7750c95a028b8703fd9b4a685b08d599cdede3af Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 30 May 2020 00:43:55 +0100 Subject: [PATCH 49/50] typoe --- code_style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_style.md b/code_style.md index 8c8e40fa18..fe04d2cc3d 100644 --- a/code_style.md +++ b/code_style.md @@ -17,7 +17,7 @@ writing in modern ECMAScript and using a transpile step to generate the file that applications can then include. There are significant benefits in being able to use modern ECMAScript, although the tooling for doing so can be awkward for library code, especially with regard to translating source maps and line -number throgh from the original code to the final application. +number through from the original code to the final application. General Style ------------- From 5b31fdd30846c7b3d56c08ecabc09d56584188ec Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 30 May 2020 12:36:17 +0100 Subject: [PATCH 50/50] remove stale references to MatrixToolbar Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .eslintignore.errorfiles | 1 - test/skinned-sdk.js | 1 - 2 files changed, 2 deletions(-) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 7e88ebff7f..db12611ade 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -14,7 +14,6 @@ src/components/views/elements/AddressSelector.js src/components/views/elements/DirectorySearchBox.js src/components/views/elements/MemberEventListSummary.js src/components/views/elements/UserSelector.js -src/components/views/globals/MatrixToolbar.js src/components/views/globals/NewVersionBar.js src/components/views/messages/MFileBody.js src/components/views/messages/TextualBody.js diff --git a/test/skinned-sdk.js b/test/skinned-sdk.js index bc13d78815..876a188cc0 100644 --- a/test/skinned-sdk.js +++ b/test/skinned-sdk.js @@ -16,7 +16,6 @@ const components = {}; components['structures.LeftPanel'] = stubComponent(); components['structures.RightPanel'] = stubComponent(); components['structures.RoomDirectory'] = stubComponent(); -components['views.globals.MatrixToolbar'] = stubComponent(); components['views.globals.GuestWarningBar'] = stubComponent(); components['views.globals.NewVersionBar'] = stubComponent(); components['views.elements.Spinner'] = stubComponent({displayName: 'Spinner'});