feat: Do not load all timeline events from store at once
This commit is contained in:
parent
9c1f79359e
commit
524b09c572
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
Loading…
Reference in New Issue