From 6c4ee17e4046a8c5c2ef7e256a4561814b87d3b0 Mon Sep 17 00:00:00 2001 From: miruka Date: Fri, 17 Jul 2020 00:46:46 -0400 Subject: [PATCH] Retrieve last seen time for offline room members --- TODO.md | 1 - src/backend/backend.py | 2 ++ src/backend/matrix_client.py | 27 +++++++++++++++++++ .../RoomPane/MemberView/MemberDelegate.qml | 11 ++++++++ .../RoomPane/MemberView/MemberProfile.qml | 18 +++++++++++-- 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index cf625848..d58774f0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,6 @@ # TODO - global presence control -- retrieve last seen time for offline members on hover/in profile/automatically - fix HLabeledItem disabled opacity (visible for the topic area in room settings) diff --git a/src/backend/backend.py b/src/backend/backend.py index 3d903340..22bcf2a9 100644 --- a/src/backend/backend.py +++ b/src/backend/backend.py @@ -114,6 +114,8 @@ class Backend: self.presences: Dict[str, Presence] = {} + self.concurrent_get_presence_limit = asyncio.BoundedSemaphore(8) + def __repr__(self) -> str: return f"{type(self).__name__}(clients={self.clients!r})" diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index a87a5c5c..8c423717 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -1353,6 +1353,33 @@ class MatrixClient(nio.AsyncClient): await self.set_avatar(mxc) + async def get_offline_presence(self, user_id: str) -> None: + """Get a offline room member's presence and set it on model item. + + This is called by QML when a member list delegate or profile that + is offline is displayed. + Since we don't get last seen times for offline in users in syncs, + we have to fetch those manually. + """ + + if self.backend.presences.get(user_id): + return + + if not self.models["accounts"][self.user_id].presence_support: + return + + async with self.backend.concurrent_get_presence_limit: + resp = await self.get_presence(user_id) + + await self.nio_callbacks.onPresenceEvent(nio.PresenceEvent( + user_id = resp.user_id, + presence = resp.presence, + last_active_ago = resp.last_active_ago, + currently_active = resp.currently_active, + status_msg = resp.status_msg, + )) + + async def set_presence( self, presence: str, diff --git a/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml b/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml index 9cd9f6d2..7d5f558d 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml @@ -6,10 +6,13 @@ import "../../../.." import "../../../../Base" import "../../../../Base/HTile" import "../../../../Popups" +import "../../../../PythonBridge" HTile { id: member + property Future getPresenceFuture: null + backgroundColor: theme.chat.roomPane.listView.member.background contentOpacity: model.invited ? theme.chat.roomPane.listView.member.invitedOpacity : 1 @@ -145,6 +148,14 @@ HTile { } } + Component.onCompleted: + if (model.presence === "offline" && model.last_active_at < new Date(1)) + getPresenceFuture = py.callClientCoro( + chat.userId, "get_offline_presence", [model.id], + ) + + Component.onDestruction: if (getPresenceFuture) getPresenceFuture.cancel() + Behavior on contentOpacity { HNumberAnimation {} } Behavior on spacing { HNumberAnimation {} } diff --git a/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml b/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml index a50c2c7f..b38311cf 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml @@ -20,6 +20,7 @@ HListView { property bool powerLevelFieldFocused: false property Future setPowerFuture: null + property Future getPresenceFuture: null function loadDevices() { py.callClientCoro(userId, "member_devices", [member.id], devices => { @@ -269,8 +270,21 @@ HListView { } } - Component.onCompleted: loadDevices() - Component.onDestruction: if (setPowerFuture) setPowerFuture.cancel() + Component.onCompleted: { + loadDevices() + + if (member.presence === "offline" && + member.last_active_at < new Date(1)) + { + getPresenceFuture = + py.callClientCoro(userId, "get_offline_presence", [member.id]) + } + } + + Component.onDestruction: { + if (setPowerFuture) setPowerFuture.cancel() + if (getPresenceFuture) getPresenceFuture.cancel() + } Keys.onEnterPressed: Keys.onReturnPressed(event) Keys.onReturnPressed: if (! root.powerLevelFieldFocused && currentItem) {