Make Chat show spinner until ready

Like EditAccount, instead of crashing if the room isn't loaded yet.
This commit is contained in:
miruka 2019-07-21 08:38:49 -04:00
parent 5b421f02d8
commit 246058e647
8 changed files with 178 additions and 115 deletions

View File

@ -91,6 +91,17 @@ class Backend:
)) ))
async def wait_until_client_exists(self, user_id: str = "") -> None:
while True:
if user_id and user_id in self.clients:
return
if not user_id and self.clients:
return
await asyncio.sleep(0.1)
# General functions # General functions
async def load_settings(self) -> Tuple[Dict[str, Any], ...]: async def load_settings(self) -> Tuple[Dict[str, Any], ...]:
@ -99,7 +110,7 @@ class Backend:
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: if not self.clients:
return await self.wait_until_client_exists()
client = self.clients.get( client = self.clients.get(
user_id, user_id,

View File

@ -4,13 +4,11 @@
import QtQuick 2.12 import QtQuick 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import "../Base" import "../Base"
import "Banners"
import "Timeline"
import "RoomSidePane"
HPage { HPage {
id: chatPage id: chatPage
onFocusChanged: sendBox.setFocus()
property bool ready: roomInfo && ! roomInfo.loading
property var roomInfo: null property var roomInfo: null
onRoomInfoChanged: if (! roomInfo) { pageStack.showPage("Default") } onRoomInfoChanged: if (! roomInfo) { pageStack.showPage("Default") }
@ -27,120 +25,34 @@ HPage {
header: RoomHeader { header: RoomHeader {
id: roomHeader id: roomHeader
width: parent.width
displayName: roomInfo.displayName displayName: roomInfo.displayName
topic: roomInfo.topic topic: roomInfo.topic
clip: height < implicitHeight
width: parent.width
height: ready ? implicitHeight : 0
Behavior on height { HNumberAnimation {} }
} }
page.leftPadding: 0 page.leftPadding: 0
page.rightPadding: 0 page.rightPadding: 0
HSplitView {
id: chatSplitView
Layout.fillWidth: true
Layout.fillHeight: true
HColumnLayout { Loader {
Layout.fillWidth: true Timer {
interval: 200
EventList { repeat: true
Layout.fillWidth: true running: ! ready
Layout.fillHeight: true onTriggered: {
} let info = rooms.find(userId, category, roomId)
if (! info.loading) { roomInfo = Qt.binding(() => info) }
TypingMembersBar {
Layout.fillWidth: true
}
InviteBanner {
visible: category == "Invites"
inviterId: roomInfo.inviterId
Layout.fillWidth: true
}
//UnknownDevicesBanner {
//visible: category == "Rooms" && hasUnknownDevices
//
//Layout.fillWidth: true
//}
SendBox {
id: sendBox
visible: category == "Rooms" && ! hasUnknownDevices
}
LeftBanner {
visible: category == "Left"
userId: chatPage.userId
Layout.fillWidth: true
} }
} }
RoomSidePane { source: ready ? "ChatSplitView.qml" : "../Base/HBusyIndicator.qml"
id: roomSidePane
activeView: roomHeader.activeButton Layout.fillWidth: ready
property int oldWidth: width Layout.fillHeight: ready
onActiveViewChanged: Layout.alignment: Qt.AlignCenter
activeView ? restoreAnimation.start() : hideAnimation.start()
HNumberAnimation {
id: hideAnimation
target: roomSidePane
properties: "width"
from: target.width
to: 0
onStarted: {
target.oldWidth = target.width
target.Layout.minimumWidth = 0
}
}
HNumberAnimation {
id: restoreAnimation
target: roomSidePane
properties: "width"
from: 0
to: target.oldWidth
onStopped: target.Layout.minimumWidth = Qt.binding(
() => theme.avatar.size
)
}
collapsed: width < theme.avatar.size + theme.spacing
property bool wasSnapped: false
property int referenceWidth: roomHeader.buttonsWidth
onReferenceWidthChanged: {
if (! chatSplitView.manuallyResized || wasSnapped) {
if (wasSnapped) { chatSplitView.manuallyResized = false }
width = referenceWidth
}
}
property int currentWidth: width
onCurrentWidthChanged: {
if (referenceWidth != width &&
referenceWidth - 15 < width &&
width < referenceWidth + 15)
{
currentWidth = referenceWidth
width = referenceWidth
wasSnapped = true
currentWidth = Qt.binding(() => roomSidePane.width)
} else {
wasSnapped = false
}
}
width: referenceWidth // Initial width
Layout.minimumWidth: theme.avatar.size
Layout.maximumWidth:
parent.width - theme.minimumSupportedWidthPlusSpacing
}
} }
} }

View File

@ -0,0 +1,117 @@
// Copyright 2019 miruka
// This file is part of harmonyqml, licensed under LGPLv3.
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
import "Banners"
import "Timeline"
import "RoomSidePane"
HSplitView {
id: chatSplitView
Component.onCompleted: sendBox.setFocus()
HColumnLayout {
Layout.fillWidth: true
EventList {
Layout.fillWidth: true
Layout.fillHeight: true
}
TypingMembersBar {
Layout.fillWidth: true
}
InviteBanner {
visible: category == "Invites"
inviterId: roomInfo.inviterId
Layout.fillWidth: true
}
//UnknownDevicesBanner {
//visible: category == "Rooms" && hasUnknownDevices
//
//Layout.fillWidth: true
//}
SendBox {
id: sendBox
visible: category == "Rooms" && ! hasUnknownDevices
}
LeftBanner {
visible: category == "Left"
userId: chatPage.userId
Layout.fillWidth: true
}
}
RoomSidePane {
id: roomSidePane
activeView: roomHeader.activeButton
property int oldWidth: width
onActiveViewChanged:
activeView ? restoreAnimation.start() : hideAnimation.start()
HNumberAnimation {
id: hideAnimation
target: roomSidePane
properties: "width"
from: target.width
to: 0
onStarted: {
target.oldWidth = target.width
target.Layout.minimumWidth = 0
}
}
HNumberAnimation {
id: restoreAnimation
target: roomSidePane
properties: "width"
from: 0
to: target.oldWidth
onStopped: target.Layout.minimumWidth = Qt.binding(
() => theme.avatar.size
)
}
collapsed: width < theme.avatar.size + theme.spacing
property bool wasSnapped: false
property int referenceWidth: roomHeader.buttonsWidth
onReferenceWidthChanged: {
if (! chatSplitView.manuallyResized || wasSnapped) {
if (wasSnapped) { chatSplitView.manuallyResized = false }
width = referenceWidth
}
}
property int currentWidth: width
onCurrentWidthChanged: {
if (referenceWidth != width &&
referenceWidth - 15 < width &&
width < referenceWidth + 15)
{
currentWidth = referenceWidth
width = referenceWidth
wasSnapped = true
currentWidth = Qt.binding(() => roomSidePane.width)
} else {
wasSnapped = false
}
}
width: referenceWidth // Initial width
Layout.minimumWidth: theme.avatar.size
Layout.maximumWidth:
parent.width - theme.minimumSupportedWidthPlusSpacing
}
}

View File

@ -56,6 +56,7 @@ function onRoomUpdated(
else if (category == "Left") { replace = find("Invites") || find("Rooms")} else if (category == "Left") { replace = find("Invites") || find("Rooms")}
let item = { let item = {
loading: false,
typingText: typingTextFor(typingMembers, userId), typingText: typingTextFor(typingMembers, userId),
userId, category, roomId, displayName, avatarUrl, topic, members, userId, category, roomId, displayName, avatarUrl, topic, members,

View File

@ -9,4 +9,24 @@ HListModel {
sorters: StringSorter { sorters: StringSorter {
roleName: "displayName" roleName: "displayName"
} }
readonly property ListModel _emptyModel: ListModel {}
function find(userId, category, roomId) {
if (! userId) { return }
let found = rooms.getWhere({userId, roomId, category}, 1)[0]
if (found) { return found }
return {
userId, category, roomId,
displayName: "",
avatarUrl: "",
topic: "",
members: _emptyModel,
typingText: "",
inviterId: "",
loading: true,
}
}
} }

View File

@ -11,8 +11,8 @@ HListModel {
// the expression with invalid data to establish property bindings // the expression with invalid data to establish property bindings
if (! userId) { return } if (! userId) { return }
let found = getWhere({userId}, 1) let found = getWhere({userId}, 1)[0]
if (found.length > 0) { return found[0] } if (found) { return found }
py.callCoro("request_user_update_event", [userId]) py.callCoro("request_user_update_event", [userId])

View File

@ -27,10 +27,12 @@ Python {
} }
function callClientCoro(accountId, name, args=[], callback=null) { function callClientCoro(accountId, name, args=[], callback=null) {
callCoro("wait_until_client_exists", [accountId], () => {
let uuid = Math.random() + "." + name let uuid = Math.random() + "." + name
pendingCoroutines[uuid] = callback || function() {} pendingCoroutines[uuid] = callback || function() {}
call("APP.call_client_coro", [accountId, name, uuid, args]) call("APP.call_client_coro", [accountId, name, uuid, args])
})
} }
function saveConfig(backend_attribute, data, callback=null) { function saveConfig(backend_attribute, data, callback=null) {

View File

@ -69,8 +69,8 @@ Item {
} }
function showRoom(userId, category, roomId) { function showRoom(userId, category, roomId) {
let info = rooms.getWhere({userId, roomId, category}, 1)[0] let roomInfo = rooms.find(userId, category, roomId)
show("Chat/Chat.qml", {"roomInfo": info}) show("Chat/Chat.qml", {roomInfo})
} }
onCurrentItemChanged: if (currentItem) { onCurrentItemChanged: if (currentItem) {