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.
This commit is contained in:
miruka 2019-07-21 07:14:16 -04:00
parent 71f78feec6
commit 853bb350b4
13 changed files with 53 additions and 62 deletions

View File

@ -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()))

View File

@ -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

View File

@ -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":

View File

@ -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)

View File

@ -0,0 +1,4 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
BusyIndicator {}

View File

@ -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,

View File

@ -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
}
}

View File

@ -18,9 +18,9 @@ HListModel {
return {
userId,
displayName: "",
avatarUrl: "",
statusMessage: "",
displayName: "",
avatarUrl: "",
loading: true,
}
}
}

View File

@ -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 }
// }
}

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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}