Bug fix and minor improvements
Improvements: - Add instant feedback upon setting a different presence for account (local echo) - Sort room members by power level and then presence - Periodically update members' `last_acitve_at` field on the room pane - Move status message field up on account context menu, and put invisible before offline again Bug fix: - Do not try to override presence set from another client, accept it
This commit is contained in:
parent
a3c9ac20c6
commit
43df8fd60b
|
@ -400,7 +400,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
presence = self.backend.presences.get(self.user_id, None)
|
presence = self.backend.presences.get(self.user_id, None)
|
||||||
|
|
||||||
if presence:
|
if presence:
|
||||||
presence.members.pop(("account", self.user_id), None)
|
presence.account = None
|
||||||
|
|
||||||
|
|
||||||
async def update_own_profile(self) -> None:
|
async def update_own_profile(self) -> None:
|
||||||
|
@ -1315,10 +1315,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
account.connecting = True
|
account.connecting = True
|
||||||
self.start_task = asyncio.ensure_future(self._start())
|
self.start_task = asyncio.ensure_future(self._start())
|
||||||
|
|
||||||
# Assign invisible on model in here, because server will tell us we are
|
if (
|
||||||
# offline
|
presence != "offline" and
|
||||||
if presence == "invisible":
|
Presence.State(presence) != account.presence
|
||||||
account.presence = Presence.State.invisible
|
):
|
||||||
|
account.presence = Presence.State("echo_" + presence)
|
||||||
|
|
||||||
if not account.presence_support:
|
if not account.presence_support:
|
||||||
account.presence = Presence.State(presence)
|
account.presence = Presence.State(presence)
|
||||||
|
|
|
@ -10,7 +10,6 @@ from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import lxml # nosec
|
import lxml # nosec
|
||||||
|
|
||||||
import nio
|
import nio
|
||||||
|
|
||||||
from ..utils import AutoStrEnum, auto
|
from ..utils import AutoStrEnum, auto
|
||||||
|
@ -20,7 +19,6 @@ ZeroDate = datetime.fromtimestamp(0)
|
||||||
OptionalExceptionType = Union[Type[None], Type[Exception]]
|
OptionalExceptionType = Union[Type[None], Type[Exception]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TypeSpecifier(AutoStrEnum):
|
class TypeSpecifier(AutoStrEnum):
|
||||||
"""Enum providing clarification of purpose for some matrix events."""
|
"""Enum providing clarification of purpose for some matrix events."""
|
||||||
|
|
||||||
|
@ -37,6 +35,10 @@ class Presence():
|
||||||
online = auto()
|
online = auto()
|
||||||
invisible = auto()
|
invisible = auto()
|
||||||
|
|
||||||
|
echo_unavailable = auto()
|
||||||
|
echo_online = auto()
|
||||||
|
echo_invisible = auto()
|
||||||
|
|
||||||
def __lt__(self, other: "Presence.State") -> bool:
|
def __lt__(self, other: "Presence.State") -> bool:
|
||||||
order = [
|
order = [
|
||||||
self.online,
|
self.online,
|
||||||
|
@ -53,7 +55,7 @@ class Presence():
|
||||||
|
|
||||||
presence: State = State.offline
|
presence: State = State.offline
|
||||||
currently_active: bool = False
|
currently_active: bool = False
|
||||||
last_active_ago: int = -1
|
last_active_at: datetime = ZeroDate
|
||||||
status_msg: str = ""
|
status_msg: str = ""
|
||||||
|
|
||||||
members: Dict[Tuple[str, str], "Member"] = field(default_factory=dict)
|
members: Dict[Tuple[str, str], "Member"] = field(default_factory=dict)
|
||||||
|
@ -63,7 +65,7 @@ class Presence():
|
||||||
for member in self.members.values():
|
for member in self.members.values():
|
||||||
member.presence = self.presence
|
member.presence = self.presence
|
||||||
member.status_msg = self.status_msg
|
member.status_msg = self.status_msg
|
||||||
member.last_active_ago = self.last_active_ago
|
member.last_active_at = self.last_active_at
|
||||||
member.currently_active = self.currently_active
|
member.currently_active = self.currently_active
|
||||||
|
|
||||||
def update_account(self) -> None:
|
def update_account(self) -> None:
|
||||||
|
@ -73,14 +75,16 @@ class Presence():
|
||||||
# account presence to offline.
|
# account presence to offline.
|
||||||
if (
|
if (
|
||||||
not self.account or
|
not self.account or
|
||||||
self.account.presence == self.State.invisible and
|
self.presence == self.State.offline and
|
||||||
self.presence == self.State.offline
|
self.account.presence != self.State.echo_invisible
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.account.presence = self.presence
|
self.account.presence = self.presence if (
|
||||||
|
self.account.presence != self.State.echo_invisible
|
||||||
|
) else self.State.invisible
|
||||||
self.account.status_msg = self.status_msg
|
self.account.status_msg = self.status_msg
|
||||||
self.account.last_active_ago = self.last_active_ago
|
self.account.last_active_at = self.last_active_at
|
||||||
self.account.currently_active = self.currently_active
|
self.account.currently_active = self.currently_active
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,7 +109,7 @@ class Account(ModelItem):
|
||||||
presence_support: bool = False
|
presence_support: bool = False
|
||||||
presence: Presence.State = Presence.State.offline
|
presence: Presence.State = Presence.State.offline
|
||||||
currently_active: bool = False
|
currently_active: bool = False
|
||||||
last_active_ago: int = -1
|
last_active_at: datetime = ZeroDate
|
||||||
status_msg: str = ""
|
status_msg: str = ""
|
||||||
|
|
||||||
def __lt__(self, other: "Account") -> bool:
|
def __lt__(self, other: "Account") -> bool:
|
||||||
|
@ -242,7 +246,7 @@ class Member(ModelItem):
|
||||||
|
|
||||||
presence: Presence.State = Presence.State.offline
|
presence: Presence.State = Presence.State.offline
|
||||||
currently_active: bool = False
|
currently_active: bool = False
|
||||||
last_active_ago: int = -1
|
last_active_at: datetime = ZeroDate
|
||||||
status_msg: str = ""
|
status_msg: str = ""
|
||||||
|
|
||||||
def __lt__(self, other: "Member") -> bool:
|
def __lt__(self, other: "Member") -> bool:
|
||||||
|
@ -252,14 +256,14 @@ class Member(ModelItem):
|
||||||
other_name = other.display_name or other.id[1:]
|
other_name = other.display_name or other.id[1:]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
self.presence,
|
|
||||||
self.invited,
|
self.invited,
|
||||||
other.power_level,
|
other.power_level,
|
||||||
|
self.presence,
|
||||||
name.lower(),
|
name.lower(),
|
||||||
) < (
|
) < (
|
||||||
other.presence,
|
|
||||||
other.invited,
|
other.invited,
|
||||||
self.power_level,
|
self.power_level,
|
||||||
|
other.presence,
|
||||||
other_name.lower(),
|
other_name.lower(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import json
|
import json
|
||||||
import logging as log
|
import logging as log
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from html import escape
|
from html import escape
|
||||||
from typing import TYPE_CHECKING, Optional, Tuple
|
from typing import TYPE_CHECKING, Optional, Tuple
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
@ -596,7 +596,9 @@ class NioCallbacks:
|
||||||
presence.status_msg = ev.status_msg or ""
|
presence.status_msg = ev.status_msg or ""
|
||||||
presence.presence = Presence.State(ev.presence) if ev.presence\
|
presence.presence = Presence.State(ev.presence) if ev.presence\
|
||||||
else Presence.State.offline
|
else Presence.State.offline
|
||||||
presence.last_active_ago = ev.last_active_ago or -1
|
presence.last_active_at = (
|
||||||
|
datetime.now() - timedelta(milliseconds=ev.last_active_ago)
|
||||||
|
) if ev.last_active_ago else datetime.fromtimestamp(0)
|
||||||
presence.currently_active = ev.currently_active or False
|
presence.currently_active = ev.currently_active or False
|
||||||
|
|
||||||
# Add all existing members related to this presence
|
# Add all existing members related to this presence
|
||||||
|
@ -614,17 +616,26 @@ class NioCallbacks:
|
||||||
# Check if presence event is ours
|
# Check if presence event is ours
|
||||||
if (
|
if (
|
||||||
ev.user_id in self.models["accounts"] and
|
ev.user_id in self.models["accounts"] and
|
||||||
presence.presence != Presence.State.offline
|
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]
|
account = self.models["accounts"][ev.user_id]
|
||||||
|
|
||||||
|
# Do not fight back presence
|
||||||
|
self.client.backend.clients[ev.user_id]._presence = ev.presence
|
||||||
|
|
||||||
# Servers that send presence events support presence
|
# Servers that send presence events support presence
|
||||||
account.presence_support = True
|
account.presence_support = True
|
||||||
|
|
||||||
# Save the presence for the next resume
|
# Save the presence for the next resume
|
||||||
await self.client.backend.saved_accounts.update(
|
await self.client.backend.saved_accounts.update(
|
||||||
user_id = ev.user_id,
|
user_id = ev.user_id,
|
||||||
presence = presence.presence.value,
|
presence = presence.presence.value if (
|
||||||
|
account.presence != Presence.State.echo_invisible
|
||||||
|
) else "invisible",
|
||||||
)
|
)
|
||||||
|
|
||||||
presence.update_account()
|
presence.update_account()
|
||||||
|
|
|
@ -71,12 +71,13 @@ HAvatar {
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
radius: diameter / 2
|
radius: diameter / 2
|
||||||
|
opacity: presence.includes("echo") ? 0.4 : 1
|
||||||
|
|
||||||
color:
|
color:
|
||||||
presence === "online" ?
|
presence.includes("online") ?
|
||||||
theme.controls.presence.online :
|
theme.controls.presence.online :
|
||||||
|
|
||||||
presence === "unavailable" ?
|
presence.includes("unavailable") ?
|
||||||
theme.controls.presence.unavailable :
|
theme.controls.presence.unavailable :
|
||||||
|
|
||||||
theme.controls.presence.offline
|
theme.controls.presence.offline
|
||||||
|
@ -84,6 +85,9 @@ HAvatar {
|
||||||
border.color: theme.controls.presence.border
|
border.color: theme.controls.presence.border
|
||||||
border.width: diameter / 10
|
border.width: diameter / 10
|
||||||
|
|
||||||
|
Behavior on color { HColorAnimation {} }
|
||||||
|
Behavior on opacity { HNumberAnimation {} }
|
||||||
|
|
||||||
HoverHandler { id: presenceHover }
|
HoverHandler { id: presenceHover }
|
||||||
|
|
||||||
HToolTip {
|
HToolTip {
|
||||||
|
|
|
@ -21,38 +21,6 @@ HMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HMenuItem {
|
|
||||||
icon.name: "presence"
|
|
||||||
icon.color: theme.controls.presence.online
|
|
||||||
text: qsTr("Online")
|
|
||||||
onTriggered: setPresence("online")
|
|
||||||
}
|
|
||||||
|
|
||||||
HMenuItem {
|
|
||||||
enabled: presence
|
|
||||||
icon.name: "presence-busy"
|
|
||||||
icon.color: theme.controls.presence.unavailable
|
|
||||||
text: qsTr("Unavailable")
|
|
||||||
onTriggered: setPresence("unavailable")
|
|
||||||
}
|
|
||||||
|
|
||||||
HMenuItem {
|
|
||||||
icon.name: "presence-offline"
|
|
||||||
icon.color: theme.controls.presence.offline
|
|
||||||
text: qsTr("Offline")
|
|
||||||
onTriggered: setPresence("offline")
|
|
||||||
}
|
|
||||||
|
|
||||||
HMenuItem {
|
|
||||||
enabled: presence
|
|
||||||
icon.name: "presence-invisible"
|
|
||||||
icon.color: theme.controls.presence.offline
|
|
||||||
text: qsTr("Invisible")
|
|
||||||
onTriggered: setPresence("invisible")
|
|
||||||
}
|
|
||||||
|
|
||||||
HMenuSeparator { }
|
|
||||||
|
|
||||||
HLabeledItem {
|
HLabeledItem {
|
||||||
id: statusMsgLabel
|
id: statusMsgLabel
|
||||||
enabled: presence && presence !== "offline"
|
enabled: presence && presence !== "offline"
|
||||||
|
@ -95,6 +63,38 @@ HMenu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HMenuSeparator { }
|
||||||
|
|
||||||
|
HMenuItem {
|
||||||
|
icon.name: "presence"
|
||||||
|
icon.color: theme.controls.presence.online
|
||||||
|
text: qsTr("Online")
|
||||||
|
onTriggered: setPresence("online")
|
||||||
|
}
|
||||||
|
|
||||||
|
HMenuItem {
|
||||||
|
enabled: presence
|
||||||
|
icon.name: "presence-busy"
|
||||||
|
icon.color: theme.controls.presence.unavailable
|
||||||
|
text: qsTr("Unavailable")
|
||||||
|
onTriggered: setPresence("unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
HMenuItem {
|
||||||
|
enabled: presence
|
||||||
|
icon.name: "presence-invisible"
|
||||||
|
icon.color: theme.controls.presence.offline
|
||||||
|
text: qsTr("Invisible")
|
||||||
|
onTriggered: setPresence("invisible")
|
||||||
|
}
|
||||||
|
|
||||||
|
HMenuItem {
|
||||||
|
icon.name: "presence-offline"
|
||||||
|
icon.color: theme.controls.presence.offline
|
||||||
|
text: qsTr("Offline")
|
||||||
|
onTriggered: setPresence("offline")
|
||||||
|
}
|
||||||
|
|
||||||
HMenuSeparator {
|
HMenuSeparator {
|
||||||
visible: statusMsgLabel.visible
|
visible: statusMsgLabel.visible
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
|
|
|
@ -44,11 +44,25 @@ HTile {
|
||||||
|
|
||||||
TitleRightInfoLabel {
|
TitleRightInfoLabel {
|
||||||
tile: member
|
tile: member
|
||||||
text:
|
visible: presenceTimer.running
|
||||||
model.presence !== "online" &&
|
|
||||||
model.last_active_ago !== -1 ?
|
Timer {
|
||||||
utils.formatRelativeTime(model.last_active_ago) :
|
id: presenceTimer
|
||||||
""
|
running:
|
||||||
|
! model.currently_active &&
|
||||||
|
model.last_active_at > new Date(1)
|
||||||
|
repeat: true
|
||||||
|
interval:
|
||||||
|
new Date() - model.last_active_at < 60000 ?
|
||||||
|
10000 :
|
||||||
|
60000
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: parent.text = Qt.binding(() =>
|
||||||
|
utils.formatRelativeTime(
|
||||||
|
new Date() - model.last_active_at
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user