moment/harmonyqml/backend/signal_manager.py

186 lines
6.0 KiB
Python
Raw Normal View History

# Copyright 2019 miruka
# This file is part of harmonyqml, licensed under GPLv3.
from threading import Lock
2019-04-15 06:12:07 +10:00
from typing import Any, Deque, Dict, List, Optional
2019-04-15 02:56:30 +10:00
from PyQt5.QtCore import QDateTime, QObject, pyqtBoundSignal
import nio
from .backend import Backend
from .client import Client
2019-04-15 02:56:30 +10:00
from .model.items import Room, RoomEvent, User
class SignalManager(QObject):
_event_handling_lock: Lock = Lock()
def __init__(self, backend: Backend) -> None:
2019-04-15 02:56:30 +10:00
super().__init__(parent=backend)
self.backend = backend
self.last_room_events: Deque[str] = Deque(maxlen=1000)
self._events_in_transfer: int = 0
cm = self.backend.clientManager
cm.clientAdded.connect(self.onClientAdded)
cm.clientDeleted.connect(self.onClientDeleted)
def onClientAdded(self, client: Client) -> None:
self.connectClient(client)
self.backend.models.accounts.append(User(
user_id = client.userID,
display_name = self.backend.getUserDisplayName(client.userID),
))
def onClientDeleted(self, user_id: str) -> None:
accs = self.backend.models.accounts
del accs[accs.indexWhere("user_id", user_id)]
def connectClient(self, client: Client) -> None:
2019-04-15 02:56:30 +10:00
for name in dir(client):
attr = getattr(client, name)
if isinstance(attr, pyqtBoundSignal):
def onSignal(*args, name=name) -> None:
func = getattr(self, f"on{name[0].upper()}{name[1:]}")
func(client, *args)
attr.connect(onSignal)
def onRoomInvited(self, client: Client, room_id: str) -> None:
pass # TODO
def onRoomJoined(self, client: Client, room_id: str) -> None:
model = self.backend.models.rooms[client.userID]
room = client.nio.rooms[room_id]
def group_name() -> Optional[str]:
name = room.group_name()
return None if name == "Empty room?" else name
item = Room(
room_id = room_id,
display_name = room.name or room.canonical_alias or group_name(),
description = room.topic,
)
try:
index = model.indexWhere("room_id", room_id)
except ValueError:
model.append(item)
else:
model[index] = item
def onRoomLeft(self, client: Client, room_id: str) -> None:
rooms = self.backend.models.rooms[client.userID]
del rooms[rooms.indexWhere("room_id", room_id)]
2019-04-15 02:56:30 +10:00
def onRoomSyncPrevBatchTokenReceived(
self, _: Client, room_id: str, token: str
) -> None:
if room_id not in self.backend.past_tokens:
self.backend.past_tokens[room_id] = token
def onRoomPastPrevBatchTokenReceived(
self, _: Client, room_id: str, token: str
) -> None:
if self.backend.past_tokens[room_id] == token:
self.backend.fully_loaded_rooms.add(room_id)
self.backend.past_tokens[room_id] = token
2019-04-15 02:56:30 +10:00
def onRoomEventReceived(
self, _: Client, room_id: str, etype: str, edict: Dict[str, Any]
) -> None:
with self._event_handling_lock:
# Prevent duplicate events in models due to multiple accounts
if edict["event_id"] in self.last_room_events:
return
self.last_room_events.appendleft(edict["event_id"])
model = self.backend.models.roomEvents[room_id]
date_time = QDateTime\
.fromMSecsSinceEpoch(edict["server_timestamp"])
new_event = RoomEvent(type=etype, date_time=date_time, dict=edict)
if self._events_in_transfer:
local_echoes_met: int = 0
replace_at: Optional[int] = None
# Find if any locally echoed event corresponds to new_event
for i, event in enumerate(model):
if not event.is_local_echo:
continue
sb = (event.dict["sender"], event.dict["body"])
new_sb = (new_event.dict["sender"], new_event.dict["body"])
if sb == new_sb:
# The oldest matching local echo shall be replaced
replace_at = max(replace_at or 0, i)
local_echoes_met += 1
if local_echoes_met >= self._events_in_transfer:
break
2019-04-15 02:56:30 +10:00
if replace_at is not None:
model[replace_at] = new_event
self._events_in_transfer -= 1
return
for i, event in enumerate(model):
if event.is_local_echo:
continue
# Model is sorted from newest to oldest message
if new_event.date_time > event.date_time:
model.insert(i, new_event)
return
2019-04-15 02:56:30 +10:00
model.append(new_event)
2019-04-15 06:12:07 +10:00
def onRoomTypingUsersUpdated(
self, client: Client, room_id: str, users: List[str]
) -> None:
rooms = self.backend.models.rooms[client.userID]
rooms[rooms.indexWhere("room_id", room_id)].typing_users = users
def onMessageAboutToBeSent(
self, client: Client, room_id: str, content: Dict[str, str]
) -> None:
with self._event_handling_lock:
timestamp = QDateTime.currentMSecsSinceEpoch()
model = self.backend.models.roomEvents[room_id]
nio_event = nio.events.RoomMessage.parse_event({
"event_id": "",
"sender": client.userID,
"origin_server_ts": timestamp,
"content": content,
})
event = RoomEvent(
type = type(nio_event).__name__,
date_time = QDateTime.fromMSecsSinceEpoch(timestamp),
dict = nio_event.__dict__,
is_local_echo = True,
)
model.insert(0, event)
self._events_in_transfer += 1