From 94bf41dc3ed1a5c742602f141d521cb615e338d9 Mon Sep 17 00:00:00 2001 From: miruka Date: Mon, 26 Jul 2021 00:01:02 -0400 Subject: [PATCH] Fix presence loop with unavailable/offline Yes, again. Also removes echo presence states to simplify things. --- src/backend/backend.py | 4 +- src/backend/matrix_client.py | 20 +++----- src/backend/nio_callbacks.py | 94 ++++++++++++++---------------------- src/backend/presence.py | 13 ++--- src/gui/Base/PresenceOrb.qml | 3 +- 5 files changed, 49 insertions(+), 85 deletions(-) diff --git a/src/backend/backend.py b/src/backend/backend.py index 51db10e8..89a96a24 100644 --- a/src/backend/backend.py +++ b/src/backend/backend.py @@ -388,9 +388,7 @@ class Backend: await client.update_account_unread_counts() if account.presence not in [ - Presence.State.echo_invisible, - Presence.State.invisible, - Presence.State.offline, + Presence.State.invisible, Presence.State.offline, ]: await client.update_receipt_marker(room_id, event_id) diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 7fd1c3c9..0541656f 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -307,7 +307,7 @@ class MatrixClient(nio.AsyncClient): ) # TODO: be able to set presence before logging in - item.set_fields(presence=Presence.State.echo_online, connecting=True) + item.set_fields(presence=Presence.State.online, connecting=True) self._presence = "online" self.start_task = asyncio.ensure_future(self._start()) @@ -327,9 +327,6 @@ class MatrixClient(nio.AsyncClient): account = self.models["accounts"][user_id] self._presence = "offline" if state == "invisible" else state - if state in ("online", "unavailable"): - state = f"echo_{state}" - account.set_fields( presence=Presence.State(state), status_msg=status_msg, ) @@ -1386,7 +1383,6 @@ class MatrixClient(nio.AsyncClient): return if self.models["accounts"][self.user_id].presence not in [ - Presence.State.echo_invisible, Presence.State.invisible, Presence.State.offline, ]: @@ -1596,15 +1592,14 @@ class MatrixClient(nio.AsyncClient): account = self.models["accounts"][self.user_id] call_presence_api = True - new_presence = presence - for_server = "offline" if presence == "invisible" else presence + self._presence = "offline" if presence == "invisible" else presence if status_msg is None: status_msg = account.status_msg # Starting/stopping client if current/new presence is offline - if new_presence == "offline": + if presence == "offline": if account.presence == Presence.State.offline: return @@ -1620,7 +1615,6 @@ class MatrixClient(nio.AsyncClient): # We might receive a recent status_msg set from another client on # startup, so don't try to set a new one immediatly. # Presence though will be sent on first sync. - self._presence = for_server call_presence_api = False account.connecting = True self.start_task = asyncio.ensure_future(self._start()) @@ -1629,9 +1623,9 @@ class MatrixClient(nio.AsyncClient): if ( Presence.State(presence) != account.presence and - new_presence != "offline" + presence != "offline" ): - account.presence = Presence.State("echo_" + new_presence) + account.presence = Presence.State(presence) # Saving new details in accounts.json @@ -1639,7 +1633,7 @@ class MatrixClient(nio.AsyncClient): account.save_presence = True await self.backend.saved_accounts.set( - self.user_id, presence=new_presence, status_msg=status_msg, + self.user_id, presence=presence, status_msg=status_msg, ) else: account.save_presence = False @@ -1648,7 +1642,7 @@ class MatrixClient(nio.AsyncClient): if call_presence_api: account.status_msg = status_msg - await super().set_presence(for_server, status_msg) + await super().set_presence(self._presence, status_msg) async def import_keys(self, infile: str, passphrase: str) -> None: diff --git a/src/backend/nio_callbacks.py b/src/backend/nio_callbacks.py index 0c79f933..8913a359 100644 --- a/src/backend/nio_callbacks.py +++ b/src/backend/nio_callbacks.py @@ -870,20 +870,29 @@ class NioCallbacks: async def onPresenceEvent( self, ev: Union[nio.PresenceEvent, nio.PresenceGetResponse], ) -> None: + # Servers that send presence events support presence self.models["accounts"][self.client.user_id].presence_support = True account = self.models["accounts"].get(ev.user_id) presence = self.client.backend.presences.get(ev.user_id, Presence()) + invisible = False if account: - client = self.client.backend.clients[ev.user_id] + invisible = account.presence == Presence.State.invisible + client = self.client.backend.clients[ev.user_id] + # Synapse is stupid enough to return an older presence state on # sync, which then causes a never-ending loop of presence cycling. # Let's hope they didn't screw up the get_presence API too: ev = await client.get_presence(ev.user_id) - invisible = account and "invisible" in account.presence.value + if ev.presence == "offline" and not invisible: + to_set = account.presence.value + await client.set_presence(to_set, account.status_msg) + return + elif not (invisible and ev.presence != "offline"): + client._presence = ev.presence if invisible and ev.presence == "offline": presence.presence = Presence.State.invisible @@ -891,7 +900,15 @@ class NioCallbacks: presence.presence = Presence.State(ev.presence) presence.currently_active = ev.currently_active or False - presence.status_msg = ev.status_msg or "" + + # Restore status msg lost from server due to e.g. getting offline + if account and account.status_msg and not ev.status_msg: + if invisible: + presence.status_msg = account.status_msg + else: + await client.set_presence(ev.presence, account.status_msg) + else: + presence.status_msg = ev.status_msg or "" if ev.last_active_ago: presence.last_active_at = datetime.now() - timedelta( @@ -909,58 +926,21 @@ class NioCallbacks: presence.update_members() - # If presence event represents a change for one of our account - if account and account.presence != Presence.State.offline: - client = self.client.backend.clients[ev.user_id] - - # Ignore cases where we send a new presence to the server, but it - # returns an older state that doesn't match due to lag: - if ( - account.presence == Presence.State.echo_invisible and - ev.presence != Presence.State.offline.value - ) or ( - account.presence == Presence.State.echo_unavailable and - ev.presence != Presence.State.unavailable.value - ) or ( - account.presence == Presence.State.echo_online and - ev.presence != Presence.State.online.value - ): - return - - # Do not fight back presence from other clients, unless server says - # we're offline, which happens if another client disconnected or we - # had a long connection issue. Note that this makes invisibility - # only possible if we're the only client using the account, or the - # other clients are invisible/offline themselves. - if ev.presence != Presence.State.offline.value: - client._presence = ev.presence - - # Restore status msg lost from server due to e.g. getting offline - if not ev.status_msg and account.status_msg: - if invisible: - presence.status_msg = account.status_msg - else: - await client.set_presence(ev.presence, account.status_msg) - - # Save the presence to be restored next time we restart application - if account.save_presence: - status_msg = presence.status_msg - state = presence.presence - - if account.presence == Presence.State.echo_invisible: - status_msg = account.status_msg - state = Presence.State.invisible - elif state == Presence.State.echo_online: - state = Presence.State.online - elif state == Presence.State.echo_unavailable: - state = Presence.State.unavailable - - await self.client.backend.saved_accounts.set( - user_id = ev.user_id, - status_msg = status_msg, - presence = state.value, - ) - - presence.update_account() - else: + if not account: self.client.backend.presences[ev.user_id] = presence + return + + client = self.client.backend.clients[ev.user_id] + + # Save the presence to be restored next time we restart application + if account.save_presence: + status_msg = presence.status_msg + state = presence.presence + + await self.client.backend.saved_accounts.set( + user_id = ev.user_id, + status_msg = status_msg, + presence = state.value, + ) + + presence.update_account() diff --git a/src/backend/presence.py b/src/backend/presence.py index 3350bb7d..98171dcd 100644 --- a/src/backend/presence.py +++ b/src/backend/presence.py @@ -12,12 +12,9 @@ if TYPE_CHECKING: ORDER: Dict[str, int] = { "online": 0, - "echo_online": 1, - "unavailable": 2, - "echo_unavailable": 3, - "invisible": 4, - "echo_invisible": 5, - "offline": 6, + "unavailable": 1, + "invisible": 2, + "offline": 3, } @@ -60,10 +57,6 @@ class Presence: online = auto() invisible = auto() - echo_unavailable = auto() - echo_online = auto() - echo_invisible = auto() - def __lt__(self, other: "Presence.State") -> bool: return ORDER[self.value] < ORDER[other.value] diff --git a/src/gui/Base/PresenceOrb.qml b/src/gui/Base/PresenceOrb.qml index 9562b0d3..e964e070 100644 --- a/src/gui/Base/PresenceOrb.qml +++ b/src/gui/Base/PresenceOrb.qml @@ -13,8 +13,7 @@ Rectangle { implicitHeight: width radius: width / 2 - opacity: - theme.controls.presence.opacity * (presence.includes("echo") ? 0.5 : 1) + opacity: theme.controls.presence.opacity color: presence.includes("online") ?