Presence fixes and improvements
- Allow using invisible mode on servers not supporting presence, to make use of the prevention of sending typing notifications and read marker updates - For servers not supporting presence, display the account's presence orb in the left pane with half opacity - Indicate in the presence orb tooltip when the presence we set hasn't yet been noticed by the server/the server doesn't support presence - When reconnecting after being offline, if the server doesn't indicate we have a status message [set from another client], restore any previous message we had set in the current client - Show our status message striked out when we're invisible or offline to indicate that it isn't being broadcasted - Some code style cleanups - Try to handle cases where we set a presence, but receive a new presence event for our account before the server takes notice of that new presence we want, which probably resulted in the "account keeps switching between online and unavailable every few sec" glitch
This commit is contained in:
parent
909756cff7
commit
18f742966e
|
@ -384,15 +384,14 @@ class Backend:
|
|||
account = self.models["accounts"][client.user_id]
|
||||
|
||||
if room:
|
||||
room.set_fields(
|
||||
unreads = 0,
|
||||
highlights = 0,
|
||||
local_unreads = False,
|
||||
)
|
||||
room.set_fields(unreads=0, highlights=0, local_unreads=False)
|
||||
await client.update_account_unread_counts()
|
||||
|
||||
# Only update server markers if the account is not invisible
|
||||
if account.presence != Presence.State.invisible:
|
||||
if account.presence not in [
|
||||
Presence.State.echo_invisible,
|
||||
Presence.State.invisible,
|
||||
Presence.State.offline,
|
||||
]:
|
||||
await client.update_receipt_marker(room_id, event_id)
|
||||
|
||||
await asyncio.gather(*[update(c) for c in self.clients.values()])
|
||||
|
|
|
@ -306,7 +306,7 @@ class MatrixClient(nio.AsyncClient):
|
|||
)
|
||||
|
||||
# TODO: be able to set presence before logging in
|
||||
item.set_fields(presence=Presence.State.online, connecting=True)
|
||||
item.set_fields(presence=Presence.State.echo_online, connecting=True)
|
||||
self._presence = "online"
|
||||
self.start_task = asyncio.ensure_future(self._start())
|
||||
|
||||
|
@ -326,6 +326,9 @@ class MatrixClient(nio.AsyncClient):
|
|||
account = self.models["accounts"][user_id]
|
||||
self._presence = "offline" if state == "invisible" else state
|
||||
|
||||
if state != "offline":
|
||||
state = f"echo_{state}"
|
||||
|
||||
account.set_fields(
|
||||
presence=Presence.State(state), status_msg=status_msg,
|
||||
)
|
||||
|
@ -385,7 +388,6 @@ class MatrixClient(nio.AsyncClient):
|
|||
|
||||
account = self.models["accounts"][self.user_id]
|
||||
|
||||
# Get or create presence for account
|
||||
presence = self.backend.presences.setdefault(self.user_id, Presence())
|
||||
presence.account = account
|
||||
presence.presence = Presence.State(self._presence)
|
||||
|
@ -518,10 +520,10 @@ class MatrixClient(nio.AsyncClient):
|
|||
|
||||
async def pause_while_offline(self) -> None:
|
||||
"""Block until our account is online."""
|
||||
while (
|
||||
self.models["accounts"][self.user_id].presence ==
|
||||
Presence.State.offline
|
||||
):
|
||||
|
||||
account = self.models["accounts"][self.user_id]
|
||||
|
||||
while account.presence == Presence.State.offline:
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
|
||||
|
@ -1382,9 +1384,11 @@ class MatrixClient(nio.AsyncClient):
|
|||
):
|
||||
return
|
||||
|
||||
presence = self.models["accounts"][self.user_id].presence
|
||||
|
||||
if presence not in [Presence.State.invisible, Presence.State.offline]:
|
||||
if self.models["accounts"][self.user_id].presence not in [
|
||||
Presence.State.echo_invisible,
|
||||
Presence.State.invisible,
|
||||
Presence.State.offline,
|
||||
]:
|
||||
await super().room_typing(room_id, typing_state, timeout)
|
||||
|
||||
|
||||
|
@ -1589,60 +1593,61 @@ class MatrixClient(nio.AsyncClient):
|
|||
) -> None:
|
||||
"""Set presence state for this account."""
|
||||
|
||||
account = self.models["accounts"][self.user_id]
|
||||
status_msg = status_msg if status_msg is not None else (
|
||||
self.models["accounts"][self.user_id].status_msg
|
||||
)
|
||||
set_status_msg = True
|
||||
account = self.models["accounts"][self.user_id]
|
||||
call_presence_api = True
|
||||
new_presence = presence
|
||||
for_server = "offline" if presence == "invisible" else presence
|
||||
|
||||
if presence == "offline":
|
||||
# Do not do anything if account is offline and setting to offline
|
||||
if status_msg is None:
|
||||
status_msg = account.status_msg
|
||||
|
||||
# Starting/stopping client if current/new presence is offline
|
||||
|
||||
if new_presence == "offline":
|
||||
if account.presence == Presence.State.offline:
|
||||
return
|
||||
|
||||
await self._stop()
|
||||
|
||||
# Update manually since we may not receive the presence event back
|
||||
# in time
|
||||
# We stop syncing, so update the account manually
|
||||
account.set_fields(
|
||||
presence = Presence.State.offline,
|
||||
status_msg = "",
|
||||
currently_active = False,
|
||||
)
|
||||
elif (
|
||||
account.presence == Presence.State.offline and
|
||||
presence != "offline"
|
||||
):
|
||||
# In this case we will not run super().set_presence()
|
||||
set_status_msg = False
|
||||
elif account.presence == Presence.State.offline:
|
||||
# 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())
|
||||
|
||||
self._presence = "offline" if presence == "invisible" else presence
|
||||
# Update our account model item's presence
|
||||
|
||||
if (
|
||||
Presence.State(presence) != account.presence and
|
||||
presence != "offline"
|
||||
new_presence != "offline"
|
||||
):
|
||||
account.presence = Presence.State("echo_" + presence)
|
||||
account.presence = Presence.State("echo_" + new_presence)
|
||||
|
||||
if not account.presence_support:
|
||||
account.presence = Presence.State(presence)
|
||||
# Saving new details in accounts.json
|
||||
|
||||
if save:
|
||||
account.save_presence = True
|
||||
|
||||
await self.backend.saved_accounts.set(
|
||||
self.user_id, presence=presence, status_msg=status_msg,
|
||||
self.user_id, presence=new_presence, status_msg=status_msg,
|
||||
)
|
||||
else:
|
||||
account.save_presence = False
|
||||
|
||||
if set_status_msg:
|
||||
account.status_msg = status_msg
|
||||
# Update our presence/status on the server
|
||||
|
||||
await super().set_presence(
|
||||
"offline" if presence == "invisible" else presence,
|
||||
status_msg,
|
||||
)
|
||||
if call_presence_api:
|
||||
account.status_msg = status_msg
|
||||
await super().set_presence(for_server, status_msg)
|
||||
|
||||
|
||||
async def import_keys(self, infile: str, passphrase: str) -> None:
|
||||
|
|
|
@ -869,63 +869,60 @@ class NioCallbacks:
|
|||
# Presence event callbacks
|
||||
|
||||
async def onPresenceEvent(self, ev: nio.PresenceEvent) -> 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())
|
||||
|
||||
presence.currently_active = ev.currently_active or False
|
||||
presence.status_msg = ev.status_msg or ""
|
||||
presence.last_active_at = (
|
||||
datetime.now() - timedelta(milliseconds=ev.last_active_ago)
|
||||
) if ev.last_active_ago else datetime.fromtimestamp(0)
|
||||
|
||||
presence.presence = \
|
||||
Presence.State(ev.presence) if ev.presence else \
|
||||
Presence.State.offline
|
||||
if ev.last_active_ago:
|
||||
presence.last_active_at = datetime.now() - timedelta(
|
||||
milliseconds=ev.last_active_ago,
|
||||
)
|
||||
else:
|
||||
presence.last_active_at = datetime.fromtimestamp(0)
|
||||
|
||||
if ev.presence:
|
||||
presence.presence = Presence.State(ev.presence)
|
||||
else:
|
||||
presence.presence = Presence.State.offline
|
||||
|
||||
# Add all existing members related to this presence
|
||||
for room_id in self.models[self.user_id, "rooms"]:
|
||||
member = self.models[self.user_id, room_id, "members"].get(
|
||||
ev.user_id,
|
||||
)
|
||||
members = self.models[self.user_id, room_id, "members"]
|
||||
|
||||
if member:
|
||||
presence.members[room_id] = member
|
||||
if ev.user_id in members:
|
||||
presence.members[room_id] = members[ev.user_id]
|
||||
|
||||
# Update members and accounts
|
||||
presence.update_members()
|
||||
|
||||
# Check if presence event is ours
|
||||
if (
|
||||
ev.user_id in self.models["accounts"] and
|
||||
self.models["accounts"][ev.user_id].presence !=
|
||||
Presence.State.offline and
|
||||
not (
|
||||
presence.presence == Presence.State.offline and
|
||||
self.models["accounts"][ev.user_id].presence !=
|
||||
Presence.State.echo_invisible
|
||||
)
|
||||
):
|
||||
account = self.models["accounts"][ev.user_id]
|
||||
|
||||
# Set status_msg if none is set on the server and we have one
|
||||
# If presence event represents a change for one of our account
|
||||
if account and account.presence != Presence.State.offline:
|
||||
# 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 (
|
||||
not presence.status_msg and
|
||||
account.status_msg and
|
||||
ev.user_id in self.client.backend.clients and
|
||||
account.presence != Presence.State.echo_invisible and
|
||||
presence.presence == Presence.State.offline
|
||||
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
|
||||
):
|
||||
asyncio.ensure_future(
|
||||
self.client.backend.clients[ev.user_id].set_presence(
|
||||
presence.presence.value,
|
||||
account.status_msg,
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
# Do not fight back presence from other clients
|
||||
self.client.backend.clients[ev.user_id]._presence = ev.presence
|
||||
|
||||
# Servers that send presence events support presence
|
||||
account.presence_support = True
|
||||
# Restore status msg lost from server due to e.g. getting offline
|
||||
if not ev.status_msg and account.status_msg:
|
||||
await self.client.backend.clients[ev.user_id].set_presence(
|
||||
ev.presence, account.status_msg,
|
||||
)
|
||||
|
||||
# Save the presence for the next resume
|
||||
if account.save_presence:
|
||||
|
@ -943,5 +940,5 @@ class NioCallbacks:
|
|||
)
|
||||
|
||||
presence.update_account()
|
||||
|
||||
self.client.backend.presences[ev.user_id] = presence
|
||||
else:
|
||||
self.client.backend.presences[ev.user_id] = presence
|
||||
|
|
|
@ -77,7 +77,7 @@ class Presence:
|
|||
|
||||
|
||||
def update_members(self) -> None:
|
||||
"""Update presence fields of every `M̀ember` in `members`.
|
||||
"""Update presence fields of every `Member` in `members`.
|
||||
|
||||
Currently it is only called when receiving a `PresenceEvent` and when
|
||||
registering room members.
|
||||
|
|
|
@ -35,10 +35,11 @@ Rectangle {
|
|||
|
||||
HToolTip {
|
||||
visible: presenceHover.hovered
|
||||
text:
|
||||
text: qsTr("%1 (%2)").arg(
|
||||
presence.includes("online") ? qsTr("Online") :
|
||||
presence.includes("unavailable") ? qsTr("Unavailable") :
|
||||
presence.includes("invisible") ? qsTr("Invisible") :
|
||||
qsTr("Offline")
|
||||
).arg("unknown to server")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,6 @@ HMenu {
|
|||
}
|
||||
|
||||
HMenuItem {
|
||||
enabled: presence
|
||||
icon.name: "presence-invisible"
|
||||
icon.color: theme.controls.presence.offline
|
||||
text: qsTr("Invisible")
|
||||
|
|
|
@ -120,10 +120,14 @@ HTile {
|
|||
}
|
||||
|
||||
SubtitleLabel {
|
||||
id: statusMsg
|
||||
tile: account
|
||||
text: utils.escapeHtml(model.status_msg.trim())
|
||||
id: statusMsg
|
||||
tile: account
|
||||
text: utils.escapeHtml(model.status_msg.trim())
|
||||
visible: model.status_msg.trim()
|
||||
font.strikeout:
|
||||
! model.presence_support ||
|
||||
model.presence.includes("offline") ||
|
||||
model.presence.includes("invisible")
|
||||
|
||||
Layout.leftMargin: theme.spacing
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user