Add local echoing of messages

As per
https://matrix.org/docs/spec/client_server/latest.html#local-echo
This commit is contained in:
miruka 2019-04-18 13:46:39 -04:00
parent e9b3628fcc
commit 11d900965a
5 changed files with 85 additions and 22 deletions

View File

@ -36,3 +36,6 @@
- nio: org.matrix.room.preview\_urls, m.room.aliases - nio: org.matrix.room.preview\_urls, m.room.aliases
- Markdown: don't turn #things into title (space), disable __ syntax - Markdown: don't turn #things into title (space), disable __ syntax
- ![A picture](https://picsum.photos/256/256) not clickable?
- On sync, check messages API, if a limited sync timeline was received

View File

@ -46,6 +46,7 @@ class Client(QObject):
roomPastPrevBatchTokenReceived = pyqtSignal(str, str) roomPastPrevBatchTokenReceived = pyqtSignal(str, str)
roomEventReceived = pyqtSignal(str, str, dict) roomEventReceived = pyqtSignal(str, str, dict)
roomTypingUsersUpdated = pyqtSignal(str, list) roomTypingUsersUpdated = pyqtSignal(str, list)
messageAboutToBeSent = pyqtSignal(str, dict)
def __init__(self, def __init__(self,
@ -171,7 +172,6 @@ class Client(QObject):
return return
self._loading = True self._loading = True
print("load", limit)
self._on_past_events( self._on_past_events(
room_id, room_id,
self.net.talk( self.net.talk(
@ -201,4 +201,11 @@ class Client(QObject):
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
"msgtype": "m.text", "msgtype": "m.text",
} }
self.net.talk(self.nio.room_send, room_id, "m.room.message", content) self.messageAboutToBeSent.emit(room_id, content)
self.net.talk(
self.nio.room_send,
room_id = room_id,
message_type = "m.room.message",
content = content,
)

View File

@ -24,3 +24,4 @@ class RoomEvent(NamedTuple):
type: str type: str
date_time: QDateTime date_time: QDateTime
dict: Dict[str, str] dict: Dict[str, str]
is_local_echo: bool = False

View File

@ -6,19 +6,22 @@ from typing import Any, Deque, Dict, List, Optional
from PyQt5.QtCore import QDateTime, QObject, pyqtBoundSignal from PyQt5.QtCore import QDateTime, QObject, pyqtBoundSignal
import nio
from .backend import Backend from .backend import Backend
from .client import Client from .client import Client
from .model.items import Room, RoomEvent, User from .model.items import Room, RoomEvent, User
class SignalManager(QObject): class SignalManager(QObject):
_duplicate_check_lock: Lock = Lock() _event_handling_lock: Lock = Lock()
def __init__(self, backend: Backend) -> None: def __init__(self, backend: Backend) -> None:
super().__init__(parent=backend) super().__init__(parent=backend)
self.backend = backend self.backend = backend
self.last_room_events: Deque[str] = Deque(maxlen=1000) self.last_room_events: Deque[str] = Deque(maxlen=1000)
self._events_in_transfer: int = 0
cm = self.backend.clientManager cm = self.backend.clientManager
cm.clientAdded.connect(self.onClientAdded) cm.clientAdded.connect(self.onClientAdded)
@ -103,28 +106,53 @@ class SignalManager(QObject):
self, _: Client, room_id: str, etype: str, edict: Dict[str, Any] self, _: Client, room_id: str, etype: str, edict: Dict[str, Any]
) -> None: ) -> None:
with self._event_handling_lock:
# Prevent duplicate events in models due to multiple accounts # Prevent duplicate events in models due to multiple accounts
with self._duplicate_check_lock:
if edict["event_id"] in self.last_room_events: if edict["event_id"] in self.last_room_events:
return return
self.last_room_events.appendleft(edict["event_id"]) self.last_room_events.appendleft(edict["event_id"])
model = self.backend.models.roomEvents[room_id] model = self.backend.models.roomEvents[room_id]
date_time = QDateTime.fromMSecsSinceEpoch(edict["server_timestamp"]) date_time = QDateTime\
.fromMSecsSinceEpoch(edict["server_timestamp"])
new_event = RoomEvent(type=etype, date_time=date_time, dict=edict) new_event = RoomEvent(type=etype, date_time=date_time, dict=edict)
# Model is sorted from newest to oldest message if self._events_in_transfer:
insert_at = None 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): for i, event in enumerate(model):
if new_event.date_time > event.date_time: if not event.is_local_echo:
insert_at = i 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 break
if insert_at is None: 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
model.append(new_event) model.append(new_event)
else:
model.insert(insert_at, new_event)
def onRoomTypingUsersUpdated( def onRoomTypingUsersUpdated(
@ -133,3 +161,25 @@ class SignalManager(QObject):
rooms = self.backend.models.rooms[client.userID] rooms = self.backend.models.rooms[client.userID]
rooms[rooms.indexWhere("room_id", room_id)].typing_users = users 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

View File

@ -37,13 +37,15 @@ Row {
//Qt.formatDateTime(date_time, "hh:mm:ss") + //Qt.formatDateTime(date_time, "hh:mm:ss") +
//"</font>" + //"</font>" +
// (isOwn ? "&nbsp;&nbsp;" + content : "") // (isOwn ? "&nbsp;&nbsp;" + content : "")
//
text: (dict.formatted_body ? text: (dict.formatted_body ?
Backend.htmlFilter.filter(dict.formatted_body) : Backend.htmlFilter.filter(dict.formatted_body) :
dict.body) + dict.body) +
"&nbsp;&nbsp;<font size=" + smallSize + "px color=gray>" + "&nbsp;&nbsp;<font size=" + smallSize + "px color=gray>" +
Qt.formatDateTime(date_time, "hh:mm:ss") + Qt.formatDateTime(date_time, "hh:mm:ss") +
"</font>" "</font>" +
(is_local_echo ?
"&nbsp;<font size=" + smallSize + "px>⏳</font>" : "")
textFormat: Text.RichText textFormat: Text.RichText
background: Rectangle {color: "#DDD"} background: Rectangle {color: "#DDD"}
wrapMode: Text.Wrap wrapMode: Text.Wrap