From ea99eebb7bfbca43b6726742f8ae4ef7e5f4cf41 Mon Sep 17 00:00:00 2001 From: Zoe Date: Tue, 17 Mar 2020 10:20:58 +0000 Subject: [PATCH 01/14] Report to the user when a key signature upload fails --- src/components/structures/MatrixChat.js | 10 ++ .../dialogs/KeySignatureUploadFailedDialog.js | 108 ++++++++++++++++++ src/i18n/strings/en_EN.json | 10 ++ 3 files changed, 128 insertions(+) create mode 100644 src/components/views/dialogs/KeySignatureUploadFailedDialog.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index f1a5a372be..e3a239663e 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1495,6 +1495,16 @@ export default createReactClass({ } }); + cli.on("crypto.keySignatureUploadFailure", (failures, source, continuation) => { + const KeySignatureUploadFailedDialog = + sdk.getComponent('views.dialogs.KeySignatureUploadFailedDialog'); + Modal.createTrackedDialog( + 'Failed to upload key signatures', + 'Failed to upload key signatures', + KeySignatureUploadFailedDialog, + { failures, source, continuation }); + }); + cli.on("crypto.verification.request", request => { const isFlagOn = SettingsStore.isFeatureEnabled("feature_cross_signing"); diff --git a/src/components/views/dialogs/KeySignatureUploadFailedDialog.js b/src/components/views/dialogs/KeySignatureUploadFailedDialog.js new file mode 100644 index 0000000000..a04c4a389f --- /dev/null +++ b/src/components/views/dialogs/KeySignatureUploadFailedDialog.js @@ -0,0 +1,108 @@ +/* +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, useCallback, useRef} from 'react'; +import * as sdk from '../../../index'; +import { _t } from '../../../languageHandler'; + +export default function KeySignatureUploadFailedDialog({ + failures, + source, + continuation, + onFinished, + }) { + const RETRIES = 2; + const BaseDialog = sdk.getComponent('dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + const Spinner = sdk.getComponent('elements.Spinner'); + const [retry, setRetry] = useState(RETRIES); + const [cancelled, setCancelled] = useState(false); + const [retrying, setRetrying] = useState(false); + const [success, setSuccess] = useState(false); + const onCancel = useRef(onFinished); + + const causes = new Map([ + ["_afterCrossSigningLocalKeyChange", _t("a new master key signature")], + ["checkOwnCrossSigningTrust", _t("a new cross-signing key signature")], + ["setDeviceVerification", _t("a device cross-signing signature")], + ]); + const defaultCause = _t("a key signature"); + + const onRetry = useCallback(async () => { + try { + setRetrying(true); + const cancel = new Promise((resolve, reject) => { + onCancel.current = reject; + }).finally(() => { + setCancelled(true); + }); + await Promise.race([ + continuation(), + cancel, + ]); + setSuccess(true); + } catch (e) { + setRetry(r => r-1); + } finally { + onCancel.current = onFinished; + setRetrying(false); + } + }, [continuation, onFinished]); + + let body; + if (!success && !cancelled && continuation && retry > 0) { + const reason = causes.get(source) || defaultCause; + + body = (
+

{_t("Riot encountered an error during upload of:")}

+

{reason}

+ {retrying && } +
{JSON.stringify(failures, null, 2)}
+ +
); + } else { + body = (
+ {success ? + {_t("Upload completed")} : + cancelled ? + {_t("Cancelled signature upload")} : + {_t("Unabled to upload")}} + +
); + } + + return ( + {}} + > + {body} + + ); +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5f3ead1490..971e6037e4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1577,6 +1577,16 @@ "Ignore request": "Ignore request", "Loading session info...": "Loading session info...", "Encryption key request": "Encryption key request", + "a new master key signature": "a new master key signature", + "a new cross-signing key signature": "a new cross-signing key signature", + "a device cross-signing signature": "a device cross-signing signature", + "a key signature": "a key signature", + "Riot encountered an error during upload of:": "Riot encountered an error during upload of:", + "Upload completed": "Upload completed", + "Cancelled signature upload": "Cancelled signature upload", + "Unabled to upload": "Unabled to upload", + "Signature upload success": "Signature upload success", + "Signature upload failed": "Signature upload failed", "You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.": "You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.", "If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.", "Incompatible local cache": "Incompatible local cache", From 22c8df9f2309842708ea9b567d73f73017a9feb5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 18 Mar 2020 21:08:37 +0000 Subject: [PATCH 02/14] Update DM invite copy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/dialogs/InviteDialog.js | 9 +++++---- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/views/dialogs/InviteDialog.js b/src/components/views/dialogs/InviteDialog.js index d27a66165e..84ce69e174 100644 --- a/src/components/views/dialogs/InviteDialog.js +++ b/src/components/views/dialogs/InviteDialog.js @@ -1060,10 +1060,11 @@ export default class InviteDialog extends React.PureComponent { title = _t("Direct Messages"); helpText = _t( - "If you can't find someone, ask them for their username, share your " + - "username (%(userId)s) or profile link.", - {userId}, - {a: (sub) => {sub}}, + "Start a conversation with someone using their name, username (like ) or email address.", + {}, + {userId: () => { + return {userId}; + }}, ); buttonText = _t("Go"); goButtonFn = this._startDm; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f8c8ad0200..2f800f8d21 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1584,7 +1584,7 @@ "Recent Conversations": "Recent Conversations", "Suggestions": "Suggestions", "Recently Direct Messaged": "Recently Direct Messaged", - "If you can't find someone, ask them for their username, share your username (%(userId)s) or profile link.": "If you can't find someone, ask them for their username, share your username (%(userId)s) or profile link.", + "Start a conversation with someone using their name, username (like ) or email address.": "Start a conversation with someone using their name, username (like ) or email address.", "Go": "Go", "If you can't find someone, ask them for their username (e.g. @user:server.com) or share this room.": "If you can't find someone, ask them for their username (e.g. @user:server.com) or share this room.", "You added a new session '%(displayName)s', which is requesting encryption keys.": "You added a new session '%(displayName)s', which is requesting encryption keys.", From dd9ead0166181da4c7dd5471a84d7feb42dbb7e6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 18 Mar 2020 21:09:52 +0000 Subject: [PATCH 03/14] Invite dialog: backspace on empty field remove right-most target Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/dialogs/InviteDialog.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/InviteDialog.js b/src/components/views/dialogs/InviteDialog.js index 84ce69e174..8cf3967ac5 100644 --- a/src/components/views/dialogs/InviteDialog.js +++ b/src/components/views/dialogs/InviteDialog.js @@ -35,6 +35,7 @@ import createRoom, {canEncryptToAllUsers} from "../../../createRoom"; import {inviteMultipleToRoom} from "../../../RoomInvite"; import SettingsStore from '../../../settings/SettingsStore'; import RoomListStore, {TAG_DM} from "../../../stores/RoomListStore"; +import {Key} from "../../../Keyboard"; export const KIND_DM = "dm"; export const KIND_INVITE = "invite"; @@ -647,6 +648,14 @@ export default class InviteDialog extends React.PureComponent { this.props.onFinished(); }; + _onKeyDown = (e) => { + // when the field is empty and the user hits backspace remove the right-most target + if (!e.target.value && this.state.targets.length > 0 && e.key === Key.BACKSPACE && !e.ctrlKey && !e.shiftKey) { + e.preventDefault(); + this._removeMember(this.state.targets[this.state.targets.length - 1]); + } + }; + _updateFilter = (e) => { const term = e.target.value; this.setState({filterText: term}); @@ -988,8 +997,8 @@ export default class InviteDialog extends React.PureComponent { )); const input = (