From a1f8120c59fcb961e62287a181142f464d10adb1 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Tue, 30 Jun 2020 12:17:56 +0200 Subject: [PATCH] Greatly imporve initial loading performance --- lib/src/client.dart | 21 +++++++++++++++++++++ lib/src/database/database.dart | 2 +- lib/src/database/database.g.dart | 15 +++++++++++++++ lib/src/database/database.moor | 2 ++ lib/src/room.dart | 23 +++++++++++++++++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/lib/src/client.dart b/lib/src/client.dart index 6208b288..7448adec 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -533,6 +533,10 @@ class Client { final StreamController onKeyVerificationRequest = StreamController.broadcast(); + /// When a new update entered, be it a sync or an avatar post-loaded + /// payload will always be true + final StreamController onUpdate = StreamController.broadcast(); + /// Matrix synchronisation is done with https long polling. This needs a /// timeout which is usually 30 seconds. int syncTimeoutSec = 30; @@ -642,6 +646,9 @@ class Client { } _userDeviceKeys = await database.getUserDeviceKeys(this); _rooms = await database.getRoomList(this, onlyLeft: false); + for (final r in rooms) { + r.onUpdate.stream.listen((v) => _addUpdate()); + } _sortRooms(); accountData = await database.getAccountData(id); presences = await database.getPresences(id); @@ -769,6 +776,7 @@ class Client { encryption.handleDeviceOneTimeKeysCount(sync.deviceOneTimeKeysCount); } onSync.add(sync); + _addUpdate(); } Future _handleDeviceListsEvents(DeviceListsUpdate deviceLists) async { @@ -1009,6 +1017,7 @@ class Client { roomAccountData: {}, client: this, ); + newRoom.onUpdate.stream.listen((v) => _addUpdate()); rooms.insert(position, newRoom); } // If the membership is "leave" then remove the item and stop here @@ -1419,6 +1428,18 @@ class Client { } } + Timer _updateTimer; + + void _addUpdate() { + // we only want max. one update per 50ms + if (_updateTimer == null) { + _updateTimer = Timer(Duration(milliseconds: 50), () { + onUpdate.add(true); + _updateTimer = null; + }); + } + } + bool _disposed = false; /// Stops the synchronization and closes the database. After this diff --git a/lib/src/database/database.dart b/lib/src/database/database.dart index 39a37839..7240a35f 100644 --- a/lib/src/database/database.dart +++ b/lib/src/database/database.dart @@ -157,7 +157,7 @@ class Database extends _$Database { ? t.membership.equals('leave') : t.membership.equals('leave').not())) .get(); - final resStates = await getAllRoomStates(client.id).get(); + final resStates = await getImportantRoomStates(client.id).get(); final resAccountData = await getAllRoomAccountData(client.id).get(); final roomList = []; for (final r in res) { diff --git a/lib/src/database/database.g.dart b/lib/src/database/database.g.dart index d51eec59..89714530 100644 --- a/lib/src/database/database.g.dart +++ b/lib/src/database/database.g.dart @@ -6077,6 +6077,13 @@ abstract class _$Database extends GeneratedDatabase { ); } + Selectable getImportantRoomStates(int client_id) { + return customSelect( + 'SELECT * FROM room_states WHERE client_id = :client_id AND type IN (\'m.room.name\', \'m.room.avatar\', \'m.room.message\', \'m.room.encrypted\', \'m.room.encryption\')', + variables: [Variable.withInt(client_id)], + readsFrom: {roomStates}).map(_rowToDbRoomState); + } + Selectable getAllRoomStates(int client_id) { return customSelect( 'SELECT * FROM room_states WHERE client_id = :client_id', @@ -6084,6 +6091,14 @@ abstract class _$Database extends GeneratedDatabase { readsFrom: {roomStates}).map(_rowToDbRoomState); } + Selectable getAllRoomStatesForRoom( + int client_id, String room_id) { + return customSelect( + 'SELECT * FROM room_states WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + readsFrom: {roomStates}).map(_rowToDbRoomState); + } + Future storeEvent( int client_id, String event_id, diff --git a/lib/src/database/database.moor b/lib/src/database/database.moor index c2e5ff0c..b5459bf3 100644 --- a/lib/src/database/database.moor +++ b/lib/src/database/database.moor @@ -210,7 +210,9 @@ getAllPresences: SELECT * FROM presences WHERE client_id = :client_id; storePresence: INSERT OR REPLACE INTO presences (client_id, type, sender, content) VALUES (:client_id, :type, :sender, :content); updateEvent: UPDATE events SET unsigned = :unsigned, content = :content, prev_content = :prev_content WHERE client_id = :client_id AND event_id = :event_id AND room_id = :room_id; updateEventStatus: UPDATE events SET status = :status, event_id = :new_event_id WHERE client_id = :client_id AND event_id = :old_event_id AND room_id = :room_id; +getImportantRoomStates: SELECT * FROM room_states WHERE client_id = :client_id AND type IN ('m.room.name', 'm.room.avatar', 'm.room.message', 'm.room.encrypted', 'm.room.encryption'); getAllRoomStates: SELECT * FROM room_states WHERE client_id = :client_id; +getAllRoomStatesForRoom: SELECT * FROM room_states WHERE client_id = :client_id AND room_id = :room_id; storeEvent: INSERT OR REPLACE INTO events (client_id, event_id, room_id, sort_order, origin_server_ts, sender, type, unsigned, content, prev_content, state_key, status) VALUES (:client_id, :event_id, :room_id, :sort_order, :origin_server_ts, :sender, :type, :unsigned, :content, :prev_content, :state_key, :status); storeRoomState: INSERT OR REPLACE INTO room_states (client_id, event_id, room_id, sort_order, origin_server_ts, sender, type, unsigned, content, prev_content, state_key) VALUES (:client_id, :event_id, :room_id, :sort_order, :origin_server_ts, :sender, :type, :unsigned, :content, :prev_content, :state_key); getAllRoomAccountData: SELECT * FROM room_account_data WHERE client_id = :client_id; diff --git a/lib/src/room.dart b/lib/src/room.dart index 6f93ea34..2f92adeb 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -101,6 +101,19 @@ class Room { _oldestSortOrder, _newestSortOrder, client.id, id); } + bool partial = true; + Future postLoad() async { + if (!partial || client.database == null) { + return; + } + final allStates = await client.database.getAllRoomStatesForRoom(client.id, id).get(); + for (final state in allStates) { + final newState = Event.fromDb(state, this); + setState(newState); + } + partial = false; + } + /// Returns the [Event] for the given [typeKey] and optional [stateKey]. /// If no [stateKey] is provided, it defaults to an empty string. Event getState(String typeKey, [String stateKey = '']) => @@ -934,6 +947,7 @@ class Room { Future getTimeline( {onTimelineUpdateCallback onUpdate, onTimelineInsertCallback onInsert}) async { + await postLoad(); var events; if (client.database != null) { events = await client.database.getEventList(client.id, this); @@ -1033,6 +1047,15 @@ class Room { if (getState(EventTypes.RoomMember, mxID) != null) { return getState(EventTypes.RoomMember, mxID).asUser; } + if (client.database != null) { + // it may be in the database + final user = await client.database.getUser(client.id, mxID, this); + if (user != null) { + setState(user); + if (onUpdate != null) onUpdate.add(id); + return user; + } + } if (mxID == null || !_requestingMatrixIds.add(mxID)) return null; Map resp; try {