Sending messages and local echo

This commit is contained in:
miruka 2019-07-03 21:20:49 -04:00
parent 1f73f634e8
commit 8ac731149d
6 changed files with 84 additions and 30 deletions

View File

@ -1,4 +1,5 @@
from enum import Enum from enum import Enum
from typing import Any
from dataclasses import dataclass from dataclasses import dataclass
@ -17,7 +18,15 @@ class Event:
# CPython >= 3.6 or any Python >= 3.7 needed for correct dict order # CPython >= 3.6 or any Python >= 3.7 needed for correct dict order
args = [ args = [
# pylint: disable=no-member # pylint: disable=no-member
getattr(self, field) self._process_field(getattr(self, field))
for field in self.__dataclass_fields__ # type: ignore for field in self.__dataclass_fields__ # type: ignore
] ]
pyotherside.send(type(self).__name__, *args) pyotherside.send(type(self).__name__, *args)
@staticmethod
def _process_field(value: Any) -> Any:
if hasattr(value, "__class__") and issubclass(value.__class__, Enum):
return value.value
return value

View File

@ -54,6 +54,8 @@ class ContentType(AutoStrEnum):
location = auto() location = auto()
@dataclass @dataclass
class TimelineEventReceived(Event): class TimelineEventReceived(Event):
event_type: Type[nio.Event] = field() event_type: Type[nio.Event] = field()
@ -62,7 +64,6 @@ class TimelineEventReceived(Event):
sender_id: str = field() sender_id: str = field()
date: datetime = field() date: datetime = field()
content: str = field() content: str = field()
content_type: ContentType = ContentType.html content_type: ContentType = ContentType.html
is_local_echo: bool = False is_local_echo: bool = False
@ -71,10 +72,8 @@ class TimelineEventReceived(Event):
target_user_id: Optional[str] = None target_user_id: Optional[str] = None
@classmethod @classmethod
def from_nio(cls, room: nio.rooms.MatrixRoom, ev: nio.Event, **fields def from_nio(cls, room, ev, **fields) -> "TimelineEventReceived":
) -> "TimelineEventReceived":
return cls( return cls(
event_type = type(ev), event_type = type(ev),
room_id = room.room_id, room_id = room.room_id,

View File

@ -5,6 +5,7 @@ import json
import logging as log import logging as log
import platform import platform
from contextlib import suppress from contextlib import suppress
from datetime import datetime
from types import ModuleType from types import ModuleType
from typing import Dict, Optional, Type from typing import Dict, Optional, Type
@ -22,9 +23,10 @@ class MatrixClient(nio.AsyncClient):
user: str, user: str,
homeserver: str = "https://matrix.org", homeserver: str = "https://matrix.org",
device_id: Optional[str] = None) -> None: device_id: Optional[str] = None) -> None:
# TODO: ensure homeserver starts with a scheme:// # TODO: ensure homeserver starts with a scheme://
self.sync_task: Optional[asyncio.Future] = None self.sync_task: Optional[asyncio.Future] = None
super().__init__(homeserver=homeserver, user=user, device_id=device_id) super().__init__(homeserver=homeserver, user=user, device_id=device_id)
self.connect_callbacks() self.connect_callbacks()
@ -113,6 +115,27 @@ class MatrixClient(nio.AsyncClient):
) )
async def send_markdown(self, room_id: str, text: str) -> None:
content = {
"body": text,
"formatted_body": HTML_FILTER.from_markdown(text),
"format": "org.matrix.custom.html",
"msgtype": "m.text",
}
TimelineMessageReceived(
event_type = nio.RoomMessageText,
room_id = room_id,
event_id = "local_echo",
sender_id = self.user_id,
date = datetime.now(),
content = content["formatted_body"],
is_local_echo = True,
)
await self.room_send(room_id, "m.room.message", content)
# Callbacks for nio responses # Callbacks for nio responses
@staticmethod @staticmethod
@ -172,6 +195,7 @@ class MatrixClient(nio.AsyncClient):
ev.formatted_body ev.formatted_body
if ev.format == "org.matrix.custom.html" else html.escape(ev.body) if ev.format == "org.matrix.custom.html" else html.escape(ev.body)
) )
TimelineMessageReceived.from_nio(room, ev, content=co) TimelineMessageReceived.from_nio(room, ev, content=co)

View File

@ -34,6 +34,7 @@ HRectangle {
property bool textChangedSinceLostFocus: false property bool textChangedSinceLostFocus: false
function setTyping(typing) { function setTyping(typing) {
return
Backend.clients.get(chatPage.userId) Backend.clients.get(chatPage.userId)
.setTypingState(chatPage.roomId, typing) .setTypingState(chatPage.roomId, typing)
} }
@ -60,8 +61,9 @@ HRectangle {
} }
if (textArea.text === "") { return } if (textArea.text === "") { return }
Backend.clients.get(chatPage.userId)
.sendMarkdown(chatPage.roomId, textArea.text) var args = [chatPage.roomId, textArea.text]
py.callClientCoro(chatPage.userId, "send_markdown", args)
area.clear() area.clear()
} }

View File

@ -12,8 +12,8 @@ Column {
function getPreviousItem(nth) { function getPreviousItem(nth) {
// Remember, index 0 = newest bottomest message // Remember, index 0 = newest bottomest message
nth = nth || 1 nth = nth || 1
return model.index + nth - 1 < roomEventListView.model.count - 1 ? return roomEventListView.model.count - 1 > model.index + nth ?
roomEventListView.model.get(index + nth) : null roomEventListView.model.get(model.index + nth) : null
} }
function isMessage(item) { function isMessage(item) {
@ -32,23 +32,28 @@ Column {
readonly property bool isFirstEvent: model.eventType == "RoomCreateEvent" readonly property bool isFirstEvent: model.eventType == "RoomCreateEvent"
readonly property bool combine: // Item roles may not be loaded yet, reason for all these checks
previousItem && readonly property bool combine: Boolean(
model.date &&
previousItem && previousItem.eventType && previousItem.date &&
isMessage(previousItem) == isMessage(model) && isMessage(previousItem) == isMessage(model) &&
! talkBreak && ! talkBreak &&
! dayBreak && ! dayBreak &&
previousItem.senderId === model.senderId && previousItem.senderId === model.senderId &&
minsBetween(previousItem.date, model.date) <= 5 minsBetween(previousItem.date, model.date) <= 5
)
readonly property bool dayBreak: readonly property bool dayBreak: Boolean(
isFirstEvent || isFirstEvent ||
previousItem && model.date && previousItem && previousItem.date &&
model.date.getDate() != previousItem.date.getDate() model.date.getDate() != previousItem.date.getDate()
)
readonly property bool talkBreak: readonly property bool talkBreak: Boolean(
previousItem && model.date && previousItem && previousItem.date &&
! dayBreak && ! dayBreak &&
minsBetween(previousItem.date, model.date) >= 20 minsBetween(previousItem.date, model.date) >= 20
)
property int standardSpacing: 16 property int standardSpacing: 16

View File

@ -43,7 +43,6 @@ function onRoomUpdated(user_id, category, room_id, display_name, avatar_url,
"inviter": inviter, "inviter": inviter,
"leftEvent": left_event "leftEvent": left_event
}) })
//print("room up", rooms.toJson())
} }
@ -65,19 +64,35 @@ function onTimelineEventReceived(
event_type, room_id, event_id, sender_id, date, content, event_type, room_id, event_id, sender_id, date, content,
content_type, is_local_echo, show_name_line, translatable, target_user_id content_type, is_local_echo, show_name_line, translatable, target_user_id
) { ) {
models.timelines.upsert({"eventId": event_id}, { var item = {
"eventType": py.getattr(event_type, "__name__"), "eventType": py.getattr(event_type, "__name__"),
"roomId": room_id, "roomId": room_id,
"eventId": event_id, "eventId": event_id,
"senderId": sender_id, "senderId": sender_id,
"date": date, "date": date,
"content": content, "content": content,
"contentType": content, "contentType": content_type,
"isLocalEcho": is_local_echo,
"showNameLine": show_name_line, "showNameLine": show_name_line,
"translatable": translatable, "translatable": translatable,
"targetUserId": target_user_id "targetUserId": target_user_id,
}, true, 1000) "isLocalEcho": is_local_echo,
}
// Replace any matching local echo
var found = models.timelines.getIndices({
"roomId": room_id,
"senderId": sender_id,
"content": content,
"isLocalEcho": true
}, 1, 500)
if (found.length > 0) {
models.timelines.set(found[0], item)
return
}
// Multiple clients will emit duplicate events with the same eventId
models.timelines.upsert({"eventId": event_id}, item, true, 500)
} }
var onTimelineMessageReceived = onTimelineEventReceived var onTimelineMessageReceived = onTimelineEventReceived