Improve room past events loading
- Trigger when room is shown if there's not enough messages to fill the list height - Trigger whenever user is scrolling before a certain point, instead of when dragging is released/scrolling stopped and the top edge is hit - Prevent multiple load requests at same time - Keep a set of fully loaded rooms, don't request anymore history if a room is fully loaded
This commit is contained in:
		
							
								
								
									
										4
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								TODO.md
									
									
									
									
									
								
							| @@ -30,3 +30,7 @@ | |||||||
| - Push instead of replacing in stack view | - Push instead of replacing in stack view | ||||||
|  |  | ||||||
| - QQuickImageProvider, matrix preview API | - QQuickImageProvider, matrix preview API | ||||||
|  |  | ||||||
|  | - Spinner when loading past room events or images | ||||||
|  |  | ||||||
|  | - nio: org.matrix.room.preview\_urls, m.room.aliases | ||||||
|   | |||||||
| @@ -2,20 +2,21 @@ | |||||||
| # This file is part of harmonyqml, licensed under GPLv3. | # This file is part of harmonyqml, licensed under GPLv3. | ||||||
|  |  | ||||||
| import hashlib | import hashlib | ||||||
| from typing import Dict | from typing import Dict, Set | ||||||
|  |  | ||||||
| from PyQt5.QtCore import QObject, pyqtProperty, pyqtSlot | from PyQt5.QtCore import QObject, pyqtProperty, pyqtSlot | ||||||
|  |  | ||||||
| from .client_manager import ClientManager | from .client_manager import ClientManager | ||||||
|  | from .html_filter import HtmlFilter | ||||||
| from .model.items import User | from .model.items import User | ||||||
| from .model.qml_models import QMLModels | from .model.qml_models import QMLModels | ||||||
| from .html_filter import HtmlFilter |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Backend(QObject): | class Backend(QObject): | ||||||
|     def __init__(self) -> None: |     def __init__(self) -> None: | ||||||
|         super().__init__() |         super().__init__() | ||||||
|         self.past_tokens: Dict[str, str] = {} |         self.past_tokens:        Dict[str, str] = {} | ||||||
|  |         self.fully_loaded_rooms: Set[str]       = set() | ||||||
|  |  | ||||||
|         self._client_manager: ClientManager = ClientManager() |         self._client_manager: ClientManager = ClientManager() | ||||||
|         self._models:         QMLModels     = QMLModels() |         self._models:         QMLModels     = QMLModels() | ||||||
| @@ -61,13 +62,19 @@ class Backend(QObject): | |||||||
|  |  | ||||||
|  |  | ||||||
|     @pyqtSlot(str) |     @pyqtSlot(str) | ||||||
|     def loadPastEvents(self, room_id: str) -> None: |     @pyqtSlot(str, int) | ||||||
|  |     def loadPastEvents(self, room_id: str, limit: int = 100) -> None: | ||||||
|         if not room_id in self.past_tokens: |         if not room_id in self.past_tokens: | ||||||
|             return  # Initial sync not done yet |             return  # Initial sync not done yet | ||||||
|  |  | ||||||
|  |         if room_id in self.fully_loaded_rooms: | ||||||
|  |             return | ||||||
|  |  | ||||||
|         for client in self.clientManager.clients.values(): |         for client in self.clientManager.clients.values(): | ||||||
|             if room_id in client.nio.rooms: |             if room_id in client.nio.rooms: | ||||||
|                 client.loadPastEvents(room_id, self.past_tokens[room_id]) |                 client.loadPastEvents( | ||||||
|  |                     room_id, self.past_tokens[room_id], limit | ||||||
|  |                 ) | ||||||
|                 break |                 break | ||||||
|         else: |         else: | ||||||
|             raise ValueError(f"Room not found in any client: {room_id}") |             raise ValueError(f"Room not found in any client: {room_id}") | ||||||
|   | |||||||
| @@ -69,6 +69,8 @@ class Client(QObject): | |||||||
|         self.net      = NetworkManager(self.host, self.port, self.nio) |         self.net      = NetworkManager(self.host, self.port, self.nio) | ||||||
|         self.net_sync = NetworkManager(self.host, self.port, self.nio_sync) |         self.net_sync = NetworkManager(self.host, self.port, self.nio_sync) | ||||||
|  |  | ||||||
|  |         self._loading: bool = False | ||||||
|  |  | ||||||
|         self._stop_sync: Event = Event() |         self._stop_sync: Event = Event() | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -157,14 +159,22 @@ class Client(QObject): | |||||||
|  |  | ||||||
|  |  | ||||||
|     @futurize |     @futurize | ||||||
|     def loadPastEvents(self, room_id: str, start_token: str) -> None: |     def loadPastEvents(self, room_id: str, start_token: str, limit: int = 100 | ||||||
|  |                       ) -> None: | ||||||
|         # From QML, use Backend.loastPastEvents instead |         # From QML, use Backend.loastPastEvents instead | ||||||
|  |  | ||||||
|  |         if self._loading: | ||||||
|  |             return | ||||||
|  |         self._loading = True | ||||||
|  |  | ||||||
|  |         print("load", limit) | ||||||
|         self._on_past_events( |         self._on_past_events( | ||||||
|             room_id, |             room_id, | ||||||
|             self.net.talk( |             self.net.talk( | ||||||
|                 self.nio.room_messages, room_id, start=start_token, limit=100 |                 self.nio.room_messages, room_id, start=start_token, limit=limit | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|  |         self._loading = False | ||||||
|  |  | ||||||
|  |  | ||||||
|     def _on_past_events(self, room_id: str, response: nr.RoomMessagesResponse |     def _on_past_events(self, room_id: str, response: nr.RoomMessagesResponse | ||||||
|   | |||||||
| @@ -93,6 +93,9 @@ class SignalManager(QObject): | |||||||
|             self, _: Client, room_id: str, token: str |             self, _: Client, room_id: str, token: str | ||||||
|         ) -> None: |         ) -> None: | ||||||
|  |  | ||||||
|  |         if self.backend.past_tokens[room_id] == token: | ||||||
|  |             self.backend.fully_loaded_rooms.add(room_id) | ||||||
|  |  | ||||||
|         self.backend.past_tokens[room_id] = token |         self.backend.past_tokens[room_id] = token | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,8 +26,14 @@ Rectangle { | |||||||
|         // reloaded from network. |         // reloaded from network. | ||||||
|         cacheBuffer: height * 6 |         cacheBuffer: height * 6 | ||||||
|  |  | ||||||
|         onMovementEnded: if (atYBeginning) { |         // Declaring this "alias" provides the on... signal | ||||||
|             Backend.loadPastEvents(chatPage.room.room_id) |         property real yPos: visibleArea.yPosition | ||||||
|  |  | ||||||
|  |         onYPosChanged: { | ||||||
|  |             console.log(yPos) | ||||||
|  |             if (yPos <= 0.1) { | ||||||
|  |                 Backend.loadPastEvents(chatPage.room.room_id) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	