From 853bb350b43a8aaf5ffcbe6fc655af54f2e8df58 Mon Sep 17 00:00:00 2001 From: miruka Date: Sun, 21 Jul 2019 07:14:16 -0400 Subject: [PATCH] Make EditAccount show a spinner until ready Instead of crashing if userInfo is not yet available. statusMessage is removed for now from UserUpdated events, and the users model items will have a "loading" property. --- src/python/backend.py | 10 ++++++- src/python/config_files.py | 2 +- src/python/events/users.py | 8 +++--- src/python/matrix_client.py | 2 -- src/qml/Base/HBusyIndicator.qml | 4 +++ src/qml/EventHandlers/users.js | 4 +-- src/qml/LoadingScreen.qml | 4 +-- src/qml/Models/Users.qml | 6 ++--- src/qml/Pages/EditAccount/EditAccount.qml | 27 ++++++++----------- src/qml/Python.qml | 7 ++--- src/qml/SidePane/AccountDelegate.qml | 2 +- src/qml/UI.qml | 32 ++++++----------------- src/qml/Window.qml | 7 +++-- 13 files changed, 53 insertions(+), 62 deletions(-) create mode 100644 src/qml/Base/HBusyIndicator.qml diff --git a/src/python/backend.py b/src/python/backend.py index 0c7ab967..bf6616aa 100644 --- a/src/python/backend.py +++ b/src/python/backend.py @@ -3,7 +3,7 @@ import asyncio import random -from typing import Dict, Optional, Set, Tuple +from typing import Any, Dict, Optional, Set, Tuple from .app import App from .events import users @@ -18,6 +18,7 @@ class Backend: from . import config_files self.saved_accounts = config_files.Accounts(self) self.ui_settings = config_files.UISettings(self) + self.ui_state = config_files.UIState(self) self.clients: Dict[str, MatrixClient] = {} @@ -92,7 +93,14 @@ class Backend: # General functions + async def load_settings(self) -> Tuple[Dict[str, Any], ...]: + return (await self.ui_settings.read(), await self.ui_state.read()) + + async def request_user_update_event(self, user_id: str) -> None: + if not self.clients: + return + client = self.clients.get( user_id, random.choice(tuple(self.clients.values())) diff --git a/src/python/config_files.py b/src/python/config_files.py index 1155b9cf..0045d16f 100644 --- a/src/python/config_files.py +++ b/src/python/config_files.py @@ -18,7 +18,7 @@ WRITE_LOCK = asyncio.Lock() @dataclass class ConfigFile: - backend: Backend = field() + backend: Backend = field(repr=False) filename: str = field() use_data_dir: bool = False diff --git a/src/python/events/users.py b/src/python/events/users.py index 8ddd1d1f..d5c7b2c9 100644 --- a/src/python/events/users.py +++ b/src/python/events/users.py @@ -26,11 +26,9 @@ class AccountDeleted(Event): @dataclass class UserUpdated(Event): - user_id: str = field() - display_name: str = "" - avatar_url: str = "" - status_message: str = "" - + user_id: str = field() + display_name: str = "" + avatar_url: str = "" @classmethod def from_nio(cls, user: MatrixUser) -> "UserUpdated": diff --git a/src/python/matrix_client.py b/src/python/matrix_client.py index 528010fb..fbb6b937 100644 --- a/src/python/matrix_client.py +++ b/src/python/matrix_client.py @@ -131,7 +131,6 @@ class MatrixClient(nio.AsyncClient): async def request_user_update_event(self, user_id: str) -> None: if user_id in self.backend.pending_profile_requests: return - print("Requesting profile for", user_id) self.backend.pending_profile_requests.add(user_id) response = await self.get_profile(user_id) @@ -143,7 +142,6 @@ class MatrixClient(nio.AsyncClient): user_id = user_id, display_name = getattr(response, "displayname", "") or "", avatar_url = getattr(response, "avatar_url", "") or "", - status_message = "", # TODO ) self.backend.pending_profile_requests.discard(user_id) diff --git a/src/qml/Base/HBusyIndicator.qml b/src/qml/Base/HBusyIndicator.qml new file mode 100644 index 00000000..8599b73d --- /dev/null +++ b/src/qml/Base/HBusyIndicator.qml @@ -0,0 +1,4 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +BusyIndicator {} diff --git a/src/qml/EventHandlers/users.js b/src/qml/EventHandlers/users.js index 9d0c2f9a..40b87d9e 100644 --- a/src/qml/EventHandlers/users.js +++ b/src/qml/EventHandlers/users.js @@ -11,8 +11,8 @@ function onAccountDeleted(userId) { accounts.popWhere({userId}, 1) } -function onUserUpdated(userId, displayName, avatarUrl, statusMessage) { - users.upsert({userId}, {userId, displayName, avatarUrl, statusMessage}) +function onUserUpdated(userId, displayName, avatarUrl) { + users.upsert({userId}, {userId, displayName, avatarUrl, loading: false}) } function onDeviceUpdated(userId, deviceId, ed25519Key, trust, displayName, diff --git a/src/qml/LoadingScreen.qml b/src/qml/LoadingScreen.qml index 03879707..dc0c34dc 100644 --- a/src/qml/LoadingScreen.qml +++ b/src/qml/LoadingScreen.qml @@ -2,12 +2,12 @@ // This file is part of harmonyqml, licensed under LGPLv3. import QtQuick 2.12 -import QtQuick.Controls 2.12 +import "Base" Rectangle { color: "lightgray" - BusyIndicator { + HBusyIndicator { anchors.centerIn: parent } } diff --git a/src/qml/Models/Users.qml b/src/qml/Models/Users.qml index 465742c9..b931a9ad 100644 --- a/src/qml/Models/Users.qml +++ b/src/qml/Models/Users.qml @@ -18,9 +18,9 @@ HListModel { return { userId, - displayName: "", - avatarUrl: "", - statusMessage: "", + displayName: "", + avatarUrl: "", + loading: true, } } } diff --git a/src/qml/Pages/EditAccount/EditAccount.qml b/src/qml/Pages/EditAccount/EditAccount.qml index 14b7cc7d..75cc9077 100644 --- a/src/qml/Pages/EditAccount/EditAccount.qml +++ b/src/qml/Pages/EditAccount/EditAccount.qml @@ -15,13 +15,17 @@ HPage { property string userId: "" readonly property var userInfo: users.find(userId) + readonly property bool ready: userInfo && ! userInfo.loading hideHeaderUnderHeight: avatarPreferredSize - headerLabel.text: qsTr("Account settings for %1") - .arg(Utils.coloredNameHtml(userInfo.displayName, userId)) + headerLabel.text: + qsTr("Account settings for %1").arg( + Utils.coloredNameHtml(userInfo ? userInfo.displayName : "", userId) + ) HRectangle { - color: theme.box.background + color: ready ? theme.box.background : "transparent" + Behavior on color { HColorAnimation {} } Layout.alignment: Qt.AlignCenter @@ -31,18 +35,9 @@ HPage { Layout.preferredHeight: childrenRect.height - Profile { width: parent.width } + Loader { + width: parent.width + source: ready ? "Profile.qml" : "../../Base/HBusyIndicator.qml" + } } - - // HRectangle { - // color: theme.box.background - // radius: theme.box.radius - // ClientSettings { width: parent.width } - // } - - // HRectangle { - // color: theme.box.background - // radius: theme.box.radius - // Devices { width: parent.width } - // } } diff --git a/src/qml/Python.qml b/src/qml/Python.qml index 0d6d53c1..e5f4de37 100644 --- a/src/qml/Python.qml +++ b/src/qml/Python.qml @@ -33,9 +33,9 @@ Python { call("APP.call_client_coro", [accountId, name, uuid, args]) } - function saveSettings(callback=null) { + function saveConfig(backend_attribute, data, callback=null) { if (! py.ready) { return } // config not loaded yet - callCoro("ui_settings.write", [window.settings], callback) + callCoro(backend_attribute + ".write", [data], callback) } Component.onCompleted: { @@ -51,8 +51,9 @@ Python { call("APP.is_debug_on", [Qt.application.arguments], on => { window.debug = on - callCoro("ui_settings.read", [], settings => { + callCoro("load_settings", [], ([settings, uiState]) => { window.settings = settings + window.uiState = uiState callCoro("saved_accounts.any_saved", [], any => { py.ready = true diff --git a/src/qml/SidePane/AccountDelegate.qml b/src/qml/SidePane/AccountDelegate.qml index 99818452..6edb13e9 100644 --- a/src/qml/SidePane/AccountDelegate.qml +++ b/src/qml/SidePane/AccountDelegate.qml @@ -53,7 +53,7 @@ Column { visible: false // TODO id: statusEdit - text: userInfo.statusMessage + // text: userInfo.statusMessage placeholderText: qsTr("Set status message") font.pixelSize: theme.fontSize.small background: null diff --git a/src/qml/UI.qml b/src/qml/UI.qml index f29cb9f0..ff80e7e1 100644 --- a/src/qml/UI.qml +++ b/src/qml/UI.qml @@ -14,8 +14,8 @@ Item { Connections { target: py onWillLoadAccounts: will => { - pageStack.showPage(will ? "Default": "SignIn") - // if (will) { initialRoomTimer.start() } + if (! will) { pageStack.showPage("SignIn") } + pageStack.show(window.uiState.page) } } @@ -60,33 +60,17 @@ Item { id: pageStack property bool isWide: width > theme.contentIsWideAbove + function show(componentUrl, properties={}) { + pageStack.replace(componentUrl, properties) + } + function showPage(name, properties={}) { - pageStack.replace("Pages/" + name + ".qml", properties) + show("Pages/" + name + ".qml", properties) } function showRoom(userId, category, roomId) { let info = rooms.getWhere({userId, roomId, category}, 1)[0] - pageStack.replace("Chat/Chat.qml", {"roomInfo": info}) - } - - Timer { - // TODO: remove this, debug - id: initialRoomTimer - interval: 4000 - repeat: false - // onTriggered: pageStack.showRoom( - // "@test_mary:matrix.org", - // "Rooms", - // "!TSXGsbBbdwsdylIOJZ:matrix.org" // st - // "!VDSsFIzQnXARSCVNxS:matrix.org" // hs - // "!XhxUcnVhVhUHkBZEIL:matrix.org" // nc - // "Invites", - // "!xjqvLOGhMVutPXpAqi:matrix.org" - // ) - onTriggered: pageStack.showPage( - "EditAccount/EditAccount", - {"userId": "@test_mary:matrix.org"} - ) + show("Chat/Chat.qml", {"roomInfo": info}) } onCurrentItemChanged: if (currentItem) { diff --git a/src/qml/Window.qml b/src/qml/Window.qml index 3faf07ed..5c89fa84 100644 --- a/src/qml/Window.qml +++ b/src/qml/Window.qml @@ -27,9 +27,12 @@ ApplicationWindow { property bool debug: false property bool ready: false - // Note: window.settingsChanged() must be called manually + // Note: settingsChanged(), uiStateChanged(), etc must be called manually property var settings: ({}) - onSettingsChanged: py.saveSettings() + onSettingsChanged: py.saveConfig("ui_settings", settings) + + property var uiState: ({}) + onUiStateChanged: py.saveConfig("ui_state", uiState) Theme { id: theme } Shortcuts { id: shortcuts}