diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 96f2d45a..ac400b17 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -39,7 +39,9 @@ from .errors import ( ) from .html_markdown import HTML_PROCESSOR as HTML from .media_cache import Media, Thumbnail -from .models.items import Event, Member, Room, Upload, UploadStatus, ZeroDate +from .models.items import ( + Event, Member, Presence, Room, Upload, UploadStatus, ZeroDate, +) from .models.model_store import ModelStore from .nio_callbacks import NioCallbacks from .pyotherside_events import AlertRequested, LoopException @@ -1573,6 +1575,13 @@ class MatrixClient(nio.AsyncClient): typing = user_id in room.typing_users, power_level = member.power_level, invited = member.invited, + + last_active_ago = -1 if member.last_active_ago is None else + member.last_active_ago, + + currently_active = member.currently_active or False, + presence = member.presence or Presence.Offline, + status_message = member.status_msg or "", ) if member.display_name: diff --git a/src/backend/models/items.py b/src/backend/models/items.py index d06d47a6..46135897 100644 --- a/src/backend/models/items.py +++ b/src/backend/models/items.py @@ -10,6 +10,7 @@ from typing import Any, Dict, List, Optional, Tuple, Type, Union from uuid import UUID import lxml # nosec + import nio from ..utils import AutoStrEnum, auto @@ -28,6 +29,12 @@ class TypeSpecifier(AutoStrEnum): MembershipChange = auto() +class Presence(AutoStrEnum): + Offline = auto() # can mean offline, invisible or unknwon + Unavailable = auto() + Online = auto() + + @dataclass class Account(ModelItem): """A logged in matrix account.""" @@ -172,9 +179,16 @@ class Member(ModelItem): power_level: int = 0 invited: bool = False profile_updated: datetime = ZeroDate + last_read_event: str = "" last_read_at: datetime = ZeroDate + last_active_ago: int = -1 + currently_active: bool = False + presence: Presence = Presence.Offline + status_message: str = "" + + def __lt__(self, other: "Member") -> bool: """Sort by power level, then by display name/user ID.""" diff --git a/src/backend/nio_callbacks.py b/src/backend/nio_callbacks.py index 084b3169..2b90d84f 100644 --- a/src/backend/nio_callbacks.py +++ b/src/backend/nio_callbacks.py @@ -11,7 +11,7 @@ from urllib.parse import quote import nio from .html_markdown import HTML_PROCESSOR -from .models.items import TypeSpecifier +from .models.items import Presence, TypeSpecifier from .pyotherside_events import DevicesUpdated from .utils import classes_defined_in, plain2html @@ -60,6 +60,8 @@ class NioCallbacks: self.client.add_to_device_callback(method, event_class) elif issubclass(event_class, nio.AccountDataEvent): self.client.add_room_account_data_callback(method, event_class) + elif issubclass(event_class, nio.PresenceEvent): + self.client.add_presence_callback(method, event_class) else: self.client.add_event_callback(method, event_class) @@ -585,3 +587,19 @@ class NioCallbacks: last_read_event = receipt.event_id, last_read_at = datetime.fromtimestamp(timestamp), ) + + + # Presence event callbacks + + async def onPresenceEvent(self, ev: nio.PresenceEvent) -> None: + for room_id in self.models[self.user_id, "rooms"]: + member = \ + self.models[self.user_id, room_id, "members"].get(ev.user_id) + + if member: + member.last_active_ago = \ + -1 if ev.last_active_ago is None else ev.last_active_ago + + member.currently_active = ev.currently_active or False + member.presence = ev.presence or Presence.Offline + member.status_message = ev.status_msg or "" diff --git a/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml b/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml index 362623f3..a5716ae3 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml @@ -41,7 +41,9 @@ HTile { SubtitleLabel { tile: member - text: model.display_name ? model.id : "" + // text: model.display_name ? model.id : "" + text: [model.last_active_ago, model.currently_active, + model.presence, model.status_message].join(" | ") color: theme.chat.roomPane.listView.member.subtitle } }