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:
parent
71f78feec6
commit
853bb350b4
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
from typing import Dict, Optional, Set, Tuple
|
from typing import Any, Dict, Optional, Set, Tuple
|
||||||
|
|
||||||
from .app import App
|
from .app import App
|
||||||
from .events import users
|
from .events import users
|
||||||
|
@ -18,6 +18,7 @@ class Backend:
|
||||||
from . import config_files
|
from . import config_files
|
||||||
self.saved_accounts = config_files.Accounts(self)
|
self.saved_accounts = config_files.Accounts(self)
|
||||||
self.ui_settings = config_files.UISettings(self)
|
self.ui_settings = config_files.UISettings(self)
|
||||||
|
self.ui_state = config_files.UIState(self)
|
||||||
|
|
||||||
self.clients: Dict[str, MatrixClient] = {}
|
self.clients: Dict[str, MatrixClient] = {}
|
||||||
|
|
||||||
|
@ -92,7 +93,14 @@ class Backend:
|
||||||
|
|
||||||
# General functions
|
# 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:
|
async def request_user_update_event(self, user_id: str) -> None:
|
||||||
|
if not self.clients:
|
||||||
|
return
|
||||||
|
|
||||||
client = self.clients.get(
|
client = self.clients.get(
|
||||||
user_id,
|
user_id,
|
||||||
random.choice(tuple(self.clients.values()))
|
random.choice(tuple(self.clients.values()))
|
||||||
|
|
|
@ -18,7 +18,7 @@ WRITE_LOCK = asyncio.Lock()
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ConfigFile:
|
class ConfigFile:
|
||||||
backend: Backend = field()
|
backend: Backend = field(repr=False)
|
||||||
filename: str = field()
|
filename: str = field()
|
||||||
use_data_dir: bool = False
|
use_data_dir: bool = False
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,9 @@ class AccountDeleted(Event):
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UserUpdated(Event):
|
class UserUpdated(Event):
|
||||||
user_id: str = field()
|
user_id: str = field()
|
||||||
display_name: str = ""
|
display_name: str = ""
|
||||||
avatar_url: str = ""
|
avatar_url: str = ""
|
||||||
status_message: str = ""
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_nio(cls, user: MatrixUser) -> "UserUpdated":
|
def from_nio(cls, user: MatrixUser) -> "UserUpdated":
|
||||||
|
|
|
@ -131,7 +131,6 @@ class MatrixClient(nio.AsyncClient):
|
||||||
async def request_user_update_event(self, user_id: str) -> None:
|
async def request_user_update_event(self, user_id: str) -> None:
|
||||||
if user_id in self.backend.pending_profile_requests:
|
if user_id in self.backend.pending_profile_requests:
|
||||||
return
|
return
|
||||||
print("Requesting profile for", user_id)
|
|
||||||
self.backend.pending_profile_requests.add(user_id)
|
self.backend.pending_profile_requests.add(user_id)
|
||||||
|
|
||||||
response = await self.get_profile(user_id)
|
response = await self.get_profile(user_id)
|
||||||
|
@ -143,7 +142,6 @@ class MatrixClient(nio.AsyncClient):
|
||||||
user_id = user_id,
|
user_id = user_id,
|
||||||
display_name = getattr(response, "displayname", "") or "",
|
display_name = getattr(response, "displayname", "") or "",
|
||||||
avatar_url = getattr(response, "avatar_url", "") or "",
|
avatar_url = getattr(response, "avatar_url", "") or "",
|
||||||
status_message = "", # TODO
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.backend.pending_profile_requests.discard(user_id)
|
self.backend.pending_profile_requests.discard(user_id)
|
||||||
|
|
4
src/qml/Base/HBusyIndicator.qml
Normal file
4
src/qml/Base/HBusyIndicator.qml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
|
||||||
|
BusyIndicator {}
|
|
@ -11,8 +11,8 @@ function onAccountDeleted(userId) {
|
||||||
accounts.popWhere({userId}, 1)
|
accounts.popWhere({userId}, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUserUpdated(userId, displayName, avatarUrl, statusMessage) {
|
function onUserUpdated(userId, displayName, avatarUrl) {
|
||||||
users.upsert({userId}, {userId, displayName, avatarUrl, statusMessage})
|
users.upsert({userId}, {userId, displayName, avatarUrl, loading: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDeviceUpdated(userId, deviceId, ed25519Key, trust, displayName,
|
function onDeviceUpdated(userId, deviceId, ed25519Key, trust, displayName,
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
// This file is part of harmonyqml, licensed under LGPLv3.
|
// This file is part of harmonyqml, licensed under LGPLv3.
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import "Base"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "lightgray"
|
color: "lightgray"
|
||||||
|
|
||||||
BusyIndicator {
|
HBusyIndicator {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ HListModel {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userId,
|
userId,
|
||||||
displayName: "",
|
displayName: "",
|
||||||
avatarUrl: "",
|
avatarUrl: "",
|
||||||
statusMessage: "",
|
loading: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,17 @@ HPage {
|
||||||
|
|
||||||
property string userId: ""
|
property string userId: ""
|
||||||
readonly property var userInfo: users.find(userId)
|
readonly property var userInfo: users.find(userId)
|
||||||
|
readonly property bool ready: userInfo && ! userInfo.loading
|
||||||
|
|
||||||
hideHeaderUnderHeight: avatarPreferredSize
|
hideHeaderUnderHeight: avatarPreferredSize
|
||||||
headerLabel.text: qsTr("Account settings for %1")
|
headerLabel.text:
|
||||||
.arg(Utils.coloredNameHtml(userInfo.displayName, userId))
|
qsTr("Account settings for %1").arg(
|
||||||
|
Utils.coloredNameHtml(userInfo ? userInfo.displayName : "", userId)
|
||||||
|
)
|
||||||
|
|
||||||
HRectangle {
|
HRectangle {
|
||||||
color: theme.box.background
|
color: ready ? theme.box.background : "transparent"
|
||||||
|
Behavior on color { HColorAnimation {} }
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
|
@ -31,18 +35,9 @@ HPage {
|
||||||
|
|
||||||
Layout.preferredHeight: childrenRect.height
|
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 }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,9 @@ Python {
|
||||||
call("APP.call_client_coro", [accountId, name, uuid, args])
|
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
|
if (! py.ready) { return } // config not loaded yet
|
||||||
callCoro("ui_settings.write", [window.settings], callback)
|
callCoro(backend_attribute + ".write", [data], callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
@ -51,8 +51,9 @@ Python {
|
||||||
call("APP.is_debug_on", [Qt.application.arguments], on => {
|
call("APP.is_debug_on", [Qt.application.arguments], on => {
|
||||||
window.debug = on
|
window.debug = on
|
||||||
|
|
||||||
callCoro("ui_settings.read", [], settings => {
|
callCoro("load_settings", [], ([settings, uiState]) => {
|
||||||
window.settings = settings
|
window.settings = settings
|
||||||
|
window.uiState = uiState
|
||||||
|
|
||||||
callCoro("saved_accounts.any_saved", [], any => {
|
callCoro("saved_accounts.any_saved", [], any => {
|
||||||
py.ready = true
|
py.ready = true
|
||||||
|
|
|
@ -53,7 +53,7 @@ Column {
|
||||||
visible: false // TODO
|
visible: false // TODO
|
||||||
|
|
||||||
id: statusEdit
|
id: statusEdit
|
||||||
text: userInfo.statusMessage
|
// text: userInfo.statusMessage
|
||||||
placeholderText: qsTr("Set status message")
|
placeholderText: qsTr("Set status message")
|
||||||
font.pixelSize: theme.fontSize.small
|
font.pixelSize: theme.fontSize.small
|
||||||
background: null
|
background: null
|
||||||
|
|
|
@ -14,8 +14,8 @@ Item {
|
||||||
Connections {
|
Connections {
|
||||||
target: py
|
target: py
|
||||||
onWillLoadAccounts: will => {
|
onWillLoadAccounts: will => {
|
||||||
pageStack.showPage(will ? "Default": "SignIn")
|
if (! will) { pageStack.showPage("SignIn") }
|
||||||
// if (will) { initialRoomTimer.start() }
|
pageStack.show(window.uiState.page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,33 +60,17 @@ Item {
|
||||||
id: pageStack
|
id: pageStack
|
||||||
property bool isWide: width > theme.contentIsWideAbove
|
property bool isWide: width > theme.contentIsWideAbove
|
||||||
|
|
||||||
|
function show(componentUrl, properties={}) {
|
||||||
|
pageStack.replace(componentUrl, properties)
|
||||||
|
}
|
||||||
|
|
||||||
function showPage(name, properties={}) {
|
function showPage(name, properties={}) {
|
||||||
pageStack.replace("Pages/" + name + ".qml", properties)
|
show("Pages/" + name + ".qml", properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
function showRoom(userId, category, roomId) {
|
function showRoom(userId, category, roomId) {
|
||||||
let info = rooms.getWhere({userId, roomId, category}, 1)[0]
|
let info = rooms.getWhere({userId, roomId, category}, 1)[0]
|
||||||
pageStack.replace("Chat/Chat.qml", {"roomInfo": info})
|
show("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"}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentItemChanged: if (currentItem) {
|
onCurrentItemChanged: if (currentItem) {
|
||||||
|
|
|
@ -27,9 +27,12 @@ ApplicationWindow {
|
||||||
property bool debug: false
|
property bool debug: false
|
||||||
property bool ready: false
|
property bool ready: false
|
||||||
|
|
||||||
// Note: window.settingsChanged() must be called manually
|
// Note: settingsChanged(), uiStateChanged(), etc must be called manually
|
||||||
property var settings: ({})
|
property var settings: ({})
|
||||||
onSettingsChanged: py.saveSettings()
|
onSettingsChanged: py.saveConfig("ui_settings", settings)
|
||||||
|
|
||||||
|
property var uiState: ({})
|
||||||
|
onUiStateChanged: py.saveConfig("ui_state", uiState)
|
||||||
|
|
||||||
Theme { id: theme }
|
Theme { id: theme }
|
||||||
Shortcuts { id: shortcuts}
|
Shortcuts { id: shortcuts}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user