From ce3404a5169f3ebfd651b4a5d0048f8d0db2a695 Mon Sep 17 00:00:00 2001 From: miruka Date: Wed, 28 Aug 2019 11:42:52 -0400 Subject: [PATCH] Show import keys error in the UI --- TODO.md | 8 +--- src/icons/light-thin/retry.svg | 1 + src/python/matrix_client.py | 14 +++++-- src/python/models/items.py | 7 ++-- src/qml/LoadingScreen.qml | 2 +- src/qml/Pages/EditAccount/Encryption.qml | 39 ++++++++++++++----- src/qml/Pages/EditAccount/ImportError.qml | 33 ++++++++++++++++ .../Pages/EditAccount/ImportExportKeys.qml | 15 +------ 8 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 src/icons/light-thin/retry.svg create mode 100644 src/qml/Pages/EditAccount/ImportError.qml diff --git a/TODO.md b/TODO.md index b1491fcd..8b5ce882 100644 --- a/TODO.md +++ b/TODO.md @@ -22,9 +22,7 @@ translated arg for avatar upload and login - Fixes - Restore previous focus after closing right click context menu - - Run import in thread and AsyncClient.olm functions, they block async loop - - Handle import keys errors - - Show error box if uploading avatar fails + - Show error if uploading avatar fails - Don't strip user spacing in html - Do something when access token is invalid @@ -35,9 +33,7 @@ translated arg for avatar upload and login - Terrible performance using `QT_QPA_PLATFORM=wayland-egl`, must use `xcb` - UI - - When starting a long task, e.g. importing keys, quitting the page, - and coming back, show the buttons as still loading until operation is done - + - Decrypt messages again after importing keys - Choose a better default easing type for animations - Make invite icon blink if there's no one but ourself in the room, but never do it again once the user hovered it long enough to show tooltip diff --git a/src/icons/light-thin/retry.svg b/src/icons/light-thin/retry.svg new file mode 100644 index 00000000..55c7ccc1 --- /dev/null +++ b/src/icons/light-thin/retry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/python/matrix_client.py b/src/python/matrix_client.py index a4bc6bb5..8a9b098e 100644 --- a/src/python/matrix_client.py +++ b/src/python/matrix_client.py @@ -304,17 +304,21 @@ class MatrixClient(nio.AsyncClient): return True - async def import_keys(self, infile: str, passphrase: str) -> Optional[str]: + async def import_keys(self, infile: str, passphrase: str) -> None: # Reimplemented until better solutions are worked on in nio + await self.clear_import_error() + loop = asyncio.get_event_loop() + account = self.models[Account][self.user_id] import_keys = partial(self.olm.import_keys_static, infile, passphrase) + try: sessions = await loop.run_in_executor(None, import_keys) except nio.EncryptionError as err: - return str(err) + account.import_error = (infile, passphrase, str(err)) + return - account = self.models[Account][self.user_id] account.importing_key = 0 account.total_keys_to_import = len(sessions) @@ -330,6 +334,10 @@ class MatrixClient(nio.AsyncClient): return None + async def clear_import_error(self) -> None: + self.models[Account][self.user_id].import_error = ("", "", "") + + # Functions to register data into models async def event_is_past(self, ev: Union[nio.Event, Event]) -> bool: diff --git a/src/python/models/items.py b/src/python/models/items.py index 27e6be46..258097d2 100644 --- a/src/python/models/items.py +++ b/src/python/models/items.py @@ -1,7 +1,7 @@ import re from dataclasses import dataclass, field from datetime import datetime -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple from ..html_filter import HTML_FILTER from ..utils import AutoStrEnum, auto @@ -16,8 +16,9 @@ class Account(ModelItem): first_sync_done: bool = False profile_updated: Optional[datetime] = None - importing_key: int = 0 - total_keys_to_import: int = 0 + importing_key: int = 0 + total_keys_to_import: int = 0 + import_error: Tuple[str, str, str] = ("", "", "") # path,pw,err def __lt__(self, other: "Account") -> bool: name = self.display_name or self.user_id[1:] diff --git a/src/qml/LoadingScreen.qml b/src/qml/LoadingScreen.qml index c68c7757..d6d06577 100644 --- a/src/qml/LoadingScreen.qml +++ b/src/qml/LoadingScreen.qml @@ -10,7 +10,7 @@ Item { rotation: 45 * 3 gradient: Gradient { GradientStop { position: 0.0; color: Qt.hsla(0.73, 0.25, 0.25, 1) } - GradientStop { position: 1.0; color: Qt.hsla(0.52, 1, 0.08, 1) } + GradientStop { position: 1.0; color: Qt.hsla(0.52, 1, 0.06, 1) } } } diff --git a/src/qml/Pages/EditAccount/Encryption.qml b/src/qml/Pages/EditAccount/Encryption.qml index af80436b..385fb974 100644 --- a/src/qml/Pages/EditAccount/Encryption.qml +++ b/src/qml/Pages/EditAccount/Encryption.qml @@ -2,18 +2,39 @@ import QtQuick 2.12 import "../../Base" HLoader { - id: loader - source: accountInfo.total_keys_to_import ? - "ImportingKeys.qml" : "ImportExportKeys.qml" + property bool importing: false + + + function importKeys(file, passphrase, button=null) { + if (button) button.loading = true + importing = true + + let path = file.toString().replace(/^file:\/\//, "") + + py.callClientCoro( + editAccount.userId, "import_keys", [path, passphrase], () => { + importing = false + if (button) button.loading = false + } + ) + } + + + id: encryptionUI + source: + accountInfo.import_error[0] ? "ImportError.qml" : + importing || accountInfo.total_keys_to_import ? "ImportingKeys.qml" : + "ImportExportKeys.qml" onSourceChanged: animation.running = true - HNumberAnimation { + SequentialAnimation { id: animation - target: loader.item - property: "scale" - from: 0 - to: 1 - overshoot: 3 + HNumberAnimation { + target: encryptionUI; property: "scale"; to: 0; + } + HNumberAnimation { + target: encryptionUI; property: "scale"; to: 1; overshoot: 3; + } } } diff --git a/src/qml/Pages/EditAccount/ImportError.qml b/src/qml/Pages/EditAccount/ImportError.qml new file mode 100644 index 00000000..78621920 --- /dev/null +++ b/src/qml/Pages/EditAccount/ImportError.qml @@ -0,0 +1,33 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import "../../Base" +import "../../utils.js" as Utils + +HInterfaceBox { + buttonModel: [ + { name: "retry", text: qsTr("Retry"), iconName: "retry" }, + { name: "cancel", text: qsTr("Cancel"), iconName: "cancel" }, + ] + + buttonCallbacks: ({ + retry: button => { + encryptionUI.importKeys( + accountInfo.import_error[0], + accountInfo.import_error[1], + button, + ) + }, + cancel: button => { py.callClientCoro(userId, "clear_import_error") }, + }) + + + HLabel { + color: theme.colors.errorText + wrapMode: Text.Wrap + text: qsTr("Couldn't import decryption keys file: %1") + .arg(qsTr(accountInfo.import_error[2])) + + Layout.fillWidth: true + } +} diff --git a/src/qml/Pages/EditAccount/ImportExportKeys.qml b/src/qml/Pages/EditAccount/ImportExportKeys.qml index 055ebe74..4ac099dc 100644 --- a/src/qml/Pages/EditAccount/ImportExportKeys.qml +++ b/src/qml/Pages/EditAccount/ImportExportKeys.qml @@ -5,18 +5,6 @@ import "../../Base" import "../../utils.js" as Utils HColumnLayout { - function importKeys(file, passphrase) { - importButton.loading = true - - let path = file.toString().replace(/^file:\/\//, "") - - py.callClientCoro( - editAccount.userId, "import_keys", [path, passphrase], () => { - if (importButton) importButton.loading = false - } - ) - } - HLabel { wrapMode: Text.Wrap text: qsTr( @@ -75,6 +63,7 @@ HColumnLayout { label.text: qsTr( "Please enter the passphrase that was used to protect this file:" ) - onAcceptedPasswordChanged: importKeys(file, acceptedPassword) + onAcceptedPasswordChanged: + encryptionUI.importKeys(file, acceptedPassword, importButton) } }