Past events loading (with two bugs)

This commit is contained in:
miruka 2019-07-05 02:45:30 -04:00
parent 83694d387b
commit ecba7c47ec
5 changed files with 83 additions and 29 deletions

View File

@ -1,6 +1,7 @@
- license headers - license headers
- replace "property var" by "property <object>" where applicable - replace "property var" by "property <object>" where applicable
- [debug mode](https://docs.python.org/3/library/asyncio-dev.html) - [debug mode](https://docs.python.org/3/library/asyncio-dev.html)
- `pyotherside.atexit()`
ideas ideas
(^/v) messages unread + messages still sending (^/v) messages unread + messages still sending
@ -17,7 +18,9 @@ OLD
- Don't bake in size properties for components - Don't bake in size properties for components
- Bug fixes - Bug fixes
- 100% CPU usage when hitting top edge to trigger messages loading - Past events loading (limit 100) freezes the GUI - need to move upsert func
to a WorkerScript
- Past events loading: text binding loop on name request
- `MessageDelegate.qml:63: TypeError: 'reloadPreviousItem' not a function` - `MessageDelegate.qml:63: TypeError: 'reloadPreviousItem' not a function`
- UI - UI

View File

@ -2,7 +2,7 @@ import asyncio
import json import json
import random import random
from pathlib import Path from pathlib import Path
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Set, Tuple
from atomicfile import AtomicFile from atomicfile import AtomicFile
@ -18,8 +18,12 @@ CONFIG_LOCK = asyncio.Lock()
class Backend: class Backend:
def __init__(self, app: App) -> None: def __init__(self, app: App) -> None:
self.app = app self.app = app
self.clients: Dict[str, MatrixClient] = {} self.clients: Dict[str, MatrixClient] = {}
self.past_tokens: Dict[str, str] = {} # {room_id: token}
self.fully_loaded_rooms: Set[str] = set() # {room_id}
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{type(self).__name__}(clients={self.clients!r})" return f"{type(self).__name__}(clients={self.clients!r})"

View File

@ -114,7 +114,7 @@ class MatrixClient(nio.AsyncClient):
response = await self.get_profile(user_id) response = await self.get_profile(user_id)
if isinstance(response, nio.ProfileGetError): if isinstance(response, nio.ProfileGetError):
log.warning("Error getting profile for %r: %s", user_id, response) log.warning("%s: %s", user_id, response)
users.UserUpdated( users.UserUpdated(
user_id = user_id, user_id = user_id,
@ -124,6 +124,11 @@ class MatrixClient(nio.AsyncClient):
) )
@property
def all_rooms(self) -> Dict[str, MatrixRoom]:
return {**self.invited_rooms, **self.rooms}
async def send_markdown(self, room_id: str, text: str) -> None: async def send_markdown(self, room_id: str, text: str) -> None:
content = { content = {
"body": text, "body": text,
@ -149,6 +154,35 @@ class MatrixClient(nio.AsyncClient):
log.error("Failed to send message: %s", response) log.error("Failed to send message: %s", response)
async def load_past_events(self, room_id: str, limit: int = 100) -> bool:
if room_id in self.backend.fully_loaded_rooms:
return False
response = await self.room_messages(
room_id = room_id,
start = self.backend.past_tokens[room_id],
limit = limit,
)
more_to_load = True
print(len(response.chunk))
if self.backend.past_tokens[room_id] == response.end:
self.backend.fully_loaded_rooms.add(room_id)
more_to_load = False
self.backend.past_tokens[room_id] = response.end
for event in response.chunk:
for cb in self.event_callbacks:
if (cb.filter is None or isinstance(event, cb.filter)):
await cb.func(
self.all_rooms[room_id], event, from_past=True
)
return more_to_load
# Callbacks for nio responses # Callbacks for nio responses
@staticmethod @staticmethod
@ -176,9 +210,12 @@ class MatrixClient(nio.AsyncClient):
inviter = room.inviter or "", inviter = room.inviter or "",
) )
for room_id, _ in resp.rooms.join.items(): for room_id, info in resp.rooms.join.items():
room = self.rooms[room_id] room = self.rooms[room_id]
if room_id not in self.backend.past_tokens:
self.backend.past_tokens[room_id] = info.timeline.prev_batch
rooms.RoomUpdated( rooms.RoomUpdated(
user_id = self.user_id, user_id = self.user_id,
category = "Rooms", category = "Rooms",
@ -208,7 +245,9 @@ class MatrixClient(nio.AsyncClient):
# %S = sender's displayname # %S = sender's displayname
# %T = target (ev.state_key)'s displayname # %T = target (ev.state_key)'s displayname
async def onRoomMessageText(self, room, ev) -> None: # pylint: disable=unused-argument
async def onRoomMessageText(self, room, ev, from_past=False) -> None:
co = HTML_FILTER.filter( co = HTML_FILTER.filter(
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)
@ -217,26 +256,27 @@ class MatrixClient(nio.AsyncClient):
TimelineMessageReceived.from_nio(room, ev, content=co) TimelineMessageReceived.from_nio(room, ev, content=co)
async def onRoomCreateEvent(self, room, ev) -> None: async def onRoomCreateEvent(self, room, ev, from_past=False) -> None:
co = "%S allowed users on other matrix servers to join this room." \ co = "%S allowed users on other matrix servers to join this room." \
if ev.federate else \ if ev.federate else \
"%S blocked users on other matrix servers from joining this room." "%S blocked users on other matrix servers from joining this room."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onRoomGuestAccessEvent(self, room, ev) -> None: async def onRoomGuestAccessEvent(self, room, ev, from_past=False) -> None:
allowed = "allowed" if ev.guest_access else "forbad" allowed = "allowed" if ev.guest_access else "forbad"
co = f"%S {allowed} guests to join the room." co = f"%S {allowed} guests to join the room."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onRoomJoinRulesEvent(self, room, ev) -> None: async def onRoomJoinRulesEvent(self, room, ev, from_past=False) -> None:
access = "public" if ev.join_rule == "public" else "invite-only" access = "public" if ev.join_rule == "public" else "invite-only"
co = f"%S made the room {access}." co = f"%S made the room {access}."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onRoomHistoryVisibilityEvent(self, room, ev) -> None: async def onRoomHistoryVisibilityEvent(self, room, ev, from_past=False
) -> None:
if ev.history_visibility == "shared": if ev.history_visibility == "shared":
to = "all room members" to = "all room members"
elif ev.history_visibility == "world_readable": elif ev.history_visibility == "world_readable":
@ -254,7 +294,7 @@ class MatrixClient(nio.AsyncClient):
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onPowerLevelsEvent(self, room, ev) -> None: async def onPowerLevelsEvent(self, room, ev, from_past=False) -> None:
co = "%S changed the room's permissions." # TODO: improve co = "%S changed the room's permissions." # TODO: improve
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
@ -322,9 +362,8 @@ class MatrixClient(nio.AsyncClient):
return None return None
async def onRoomMemberEvent(self, room, ev) -> None: async def onRoomMemberEvent(self, room, ev, from_past=False) -> None:
# TODO: ignore for past events if not from_past and ev.content["membership"] != "leave":
if ev.content["membership"] != "leave":
users.UserUpdated( users.UserUpdated(
user_id = ev.state_key, user_id = ev.state_key,
display_name = ev.content["displayname"] or "", display_name = ev.content["displayname"] or "",
@ -338,40 +377,40 @@ class MatrixClient(nio.AsyncClient):
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onRoomAliasEvent(self, room, ev) -> None: async def onRoomAliasEvent(self, room, ev, from_past=False) -> None:
co = f"%S set the room's main address to {ev.canonical_alias}." co = f"%S set the room's main address to {ev.canonical_alias}."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onRoomNameEvent(self, room, ev) -> None: async def onRoomNameEvent(self, room, ev, from_past=False) -> None:
co = f"%S changed the room's name to \"{ev.name}\"." co = f"%S changed the room's name to \"{ev.name}\"."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onRoomTopicEvent(self, room, ev) -> None: async def onRoomTopicEvent(self, room, ev, from_past=False) -> None:
co = f"%S changed the room's topic to \"{ev.topic}\"." co = f"%S changed the room's topic to \"{ev.topic}\"."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onRoomEncryptionEvent(self, room, ev) -> None: async def onRoomEncryptionEvent(self, room, ev, from_past=False) -> None:
co = f"%S turned on encryption for this room." co = f"%S turned on encryption for this room."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onOlmEvent(self, room, ev) -> None: async def onOlmEvent(self, room, ev, from_past=False) -> None:
co = f"%S hasn't sent your device the keys to decrypt this message." co = f"%S hasn't sent your device the keys to decrypt this message."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onMegolmEvent(self, room, ev) -> None: async def onMegolmEvent(self, room, ev, from_past=False) -> None:
await self.onOlmEvent(room, ev) await self.onOlmEvent(room, ev, from_past=False)
async def onBadEvent(self, room, ev) -> None: async def onBadEvent(self, room, ev, from_past=False) -> None:
co = f"%S sent a malformed event." co = f"%S sent a malformed event."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)
async def onUnknownBadEvent(self, room, ev) -> None: async def onUnknownBadEvent(self, room, ev, from_past=False) -> None:
co = f"%S sent an event this client doesn't understand." co = f"%S sent an event this client doesn't understand."
TimelineEventReceived.from_nio(room, ev, content=co) TimelineEventReceived.from_nio(room, ev, content=co)

View File

@ -34,15 +34,23 @@ HRectangle {
// reloaded from network. // reloaded from network.
cacheBuffer: height * 6 cacheBuffer: height * 6
// Declaring this "alias" provides the on... signal // Declaring this as "alias" provides the on... signal
property real yPos: visibleArea.yPosition property real yPos: visibleArea.yPosition
property bool canLoad: true
property int zz: 0 property int zz: 0
onYPosChanged: { onYPosChanged: {
if (chatPage.category != "Invites" && yPos <= 0.1) { if (chatPage.category != "Invites" && canLoad && yPos <= 0.1) {
zz += 1 zz += 1
print(zz) print(canLoad, zz)
//Backend.loadPastEvents(chatPage.roomId) canLoad = false
py.callClientCoro(
chatPage.userId,
"load_past_events",
[chatPage.roomId],
{},
function(more_to_load) { canLoad = more_to_load }
)
} }
} }
} }

View File

@ -86,13 +86,13 @@ function onTimelineEventReceived(
"senderId": sender_id, "senderId": sender_id,
"content": content, "content": content,
"isLocalEcho": true "isLocalEcho": true
}, 1, 500) }, 1, 250)
if (found.length > 0) { if (found.length > 0) {
timelines.set(found[0], item) timelines.set(found[0], item)
} else { } else {
// Multiple clients will emit duplicate events with the same eventId // Multiple clients will emit duplicate events with the same eventId
timelines.upsert({"eventId": event_id}, item, true, 500) timelines.upsert({"eventId": event_id}, item, true, 250)
} }
} }