feat: Do not load all timeline events from store at once

This commit is contained in:
Christian Pauly 2021-09-01 09:34:42 +02:00
parent 9c1f79359e
commit 524b09c572
5 changed files with 84 additions and 55 deletions

View File

@ -78,7 +78,12 @@ abstract class DatabaseApi {
Future<List<User>> getUsers(int clientId, Room room);
Future<List<Event>> getEventList(int clientId, Room room);
Future<List<Event>> getEventList(
int clientId,
Room room, {
int start = 0,
int limit,
});
Future<Uint8List?> getFile(Uri mxcUri);

View File

@ -308,16 +308,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
return Event.fromJson(convertToJson(raw), room);
}
@override
Future<List<Event>> getEventList(int clientId, Room room) async {
final List timelineEventIds =
(await _timelineFragmentsBox.get(MultiKey(room.id, '').toString()) ??
[]);
final List sendingEventIds = (await _timelineFragmentsBox
.get(MultiKey(room.id, 'SENDING').toString()) ??
[]);
final eventIds = sendingEventIds + timelineEventIds;
final events = await Future.wait(eventIds
/// Loads a whole list of events at once from the store for a specific room
Future<List<Event>> _getEventsByIds(List<String> eventIds, Room room) =>
Future.wait(eventIds
.map(
(eventId) async => Event.fromJson(
convertToJson(
@ -327,7 +320,36 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
),
)
.toList());
return events;
@override
Future<List<Event>> getEventList(
int clientId,
Room room, {
int start = 0,
int? limit,
}) async {
// Get the synced event IDs from the store
final timelineKey = MultiKey(room.id, '').toString();
final timelineEventIds =
(await _timelineFragmentsBox.get(timelineKey) as List? ?? []);
// Get the local stored SENDING events from the store
late final List sendingEventIds;
if (start != 0) {
sendingEventIds = [];
} else {
final sendingTimelineKey = MultiKey(room.id, 'SENDING').toString();
sendingEventIds =
(await _timelineFragmentsBox.get(sendingTimelineKey) as List? ?? []);
}
// Combine those two lists while respecting the start and limit parameters.
final end = min(
timelineEventIds.length, start + (limit ?? timelineEventIds.length));
final eventIds =
sendingEventIds + timelineEventIds.getRange(start, end).toList();
return await _getEventsByIds(eventIds.cast<String>(), room);
}
@override

View File

@ -387,7 +387,7 @@ class Room {
/// The default count of how much events should be requested when requesting the
/// history of this room.
static const int defaultHistoryCount = 100;
static const int defaultHistoryCount = 30;
/// Calculates the displayname. First checks if there is a name, then checks for a canonical alias and
/// then generates a name from the heroes.
@ -931,7 +931,8 @@ class Room {
/// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before**
/// the historical events will be published in the onEvent stream.
Future<void> requestHistory(
{int historyCount = defaultHistoryCount, onHistoryReceived}) async {
{int historyCount = defaultHistoryCount,
void Function() onHistoryReceived}) async {
if (prev_batch == null) {
throw 'Tried to request history without a prev_batch token';
}
@ -1069,7 +1070,11 @@ class Room {
await postLoad();
var events;
if (client.database != null) {
events = await client.database.getEventList(client.id, this);
events = await client.database.getEventList(
client.id,
this,
limit: defaultHistoryCount,
);
} else {
events = <Event>[];
}

View File

@ -64,7 +64,6 @@ class Timeline {
// This ensures that the entire history fetching only triggers `onUpdate` only *once*,
// even if /sync's complete while history is being proccessed.
bool _collectHistoryUpdates = false;
final Set<EventUpdate> _historyUpdates = <EventUpdate>{};
bool get canRequestHistory {
if (events.isEmpty) return true;
@ -73,33 +72,37 @@ class Timeline {
Future<void> requestHistory(
{int historyCount = Room.defaultHistoryCount}) async {
if (!isRequestingHistory) {
if (isRequestingHistory) {
return;
}
isRequestingHistory = true;
onUpdate?.call();
try {
// Look up for events in hive first
final eventsFromStore = await room.client.database?.getEventList(
room.client.id,
room,
start: events.length,
limit: Room.defaultHistoryCount,
);
if (eventsFromStore.isNotEmpty) {
events.addAll(eventsFromStore);
} else {
Logs().v('No more events found in the store. Request from server...');
await room.requestHistory(
historyCount: historyCount,
onHistoryReceived: () {
_collectHistoryUpdates = true;
},
);
try {
await Future.delayed(const Duration(seconds: 2));
_proccessHistoryUpdates();
}
} finally {
_collectHistoryUpdates = false;
isRequestingHistory = false;
onUpdate?.call();
}
}
}
void _proccessHistoryUpdates() async {
_collectHistoryUpdates = false;
for (final update in _historyUpdates) {
_handleEventUpdate(await update.decrypt(room, store: true),
update: false);
}
_historyUpdates.clear();
if (onUpdate != null) onUpdate();
}
Timeline({this.room, List<Event> events, this.onUpdate, this.onInsert})
: events = events ?? [] {
@ -247,12 +250,6 @@ class Timeline {
try {
if (eventUpdate.roomID != room.id) return;
if (eventUpdate.type == EventUpdateType.history &&
_collectHistoryUpdates) {
_historyUpdates.add(eventUpdate);
return;
}
if (eventUpdate.type != EventUpdateType.timeline &&
eventUpdate.type != EventUpdateType.history) {
return;
@ -318,7 +315,7 @@ class Timeline {
if (onInsert != null) onInsert(0);
}
}
if (update) {
if (update && !_collectHistoryUpdates) {
if (onUpdate != null) onUpdate();
}
} catch (e, s) {

View File

@ -1337,7 +1337,7 @@ class FakeMatrixApi extends MockClient {
(var req) => messagesResponse,
'/client/r0/rooms/!localpart%3Aserver.abc/messages?from=&dir=b&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D':
(var req) => messagesResponse,
'/client/r0/rooms/!1234%3Aexample.com/messages?from=1234&dir=b&limit=100&filter=%7B%22lazy_load_members%22%3Atrue%7D':
'/client/r0/rooms/!1234%3Aexample.com/messages?from=1234&dir=b&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D':
(var req) => messagesResponse,
'/client/versions': (var req) => {
'versions': [