Add local echoing of messages
As per https://matrix.org/docs/spec/client_server/latest.html#local-echo
This commit is contained in:
parent
e9b3628fcc
commit
11d900965a
3
TODO.md
3
TODO.md
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
|
@ -21,6 +21,7 @@ class Room(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class RoomEvent(NamedTuple):
|
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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
# Prevent duplicate events in models due to multiple accounts
|
with self._event_handling_lock:
|
||||||
with self._duplicate_check_lock:
|
# Prevent duplicate events in models due to multiple accounts
|
||||||
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\
|
||||||
new_event = RoomEvent(type=etype, date_time=date_time, dict=edict)
|
.fromMSecsSinceEpoch(edict["server_timestamp"])
|
||||||
|
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
|
||||||
for i, event in enumerate(model):
|
replace_at: Optional[int] = None
|
||||||
if new_event.date_time > event.date_time:
|
|
||||||
insert_at = i
|
# Find if any locally echoed event corresponds to new_event
|
||||||
break
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
if insert_at is None:
|
|
||||||
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
|
||||||
|
|
|
@ -37,13 +37,15 @@ Row {
|
||||||
//Qt.formatDateTime(date_time, "hh:mm:ss") +
|
//Qt.formatDateTime(date_time, "hh:mm:ss") +
|
||||||
//"</font>" +
|
//"</font>" +
|
||||||
// (isOwn ? " " + content : "")
|
// (isOwn ? " " + 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) +
|
||||||
" <font size=" + smallSize + "px color=gray>" +
|
" <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 ?
|
||||||
|
" <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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user