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<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);
|
Future<Uint8List?> getFile(Uri mxcUri);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -308,26 +308,48 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
return Event.fromJson(convertToJson(raw), room);
|
return Event.fromJson(convertToJson(raw), room);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
/// Loads a whole list of events at once from the store for a specific room
|
||||||
Future<List<Event>> getEventList(int clientId, Room room) async {
|
Future<List<Event>> _getEventsByIds(List<String> eventIds, Room room) =>
|
||||||
final List timelineEventIds =
|
Future.wait(eventIds
|
||||||
(await _timelineFragmentsBox.get(MultiKey(room.id, '').toString()) ??
|
.map(
|
||||||
[]);
|
(eventId) async => Event.fromJson(
|
||||||
final List sendingEventIds = (await _timelineFragmentsBox
|
convertToJson(
|
||||||
.get(MultiKey(room.id, 'SENDING').toString()) ??
|
await _eventsBox.get(MultiKey(room.id, eventId).toString()),
|
||||||
[]);
|
),
|
||||||
final eventIds = sendingEventIds + timelineEventIds;
|
room,
|
||||||
final events = await Future.wait(eventIds
|
|
||||||
.map(
|
|
||||||
(eventId) async => Event.fromJson(
|
|
||||||
convertToJson(
|
|
||||||
await _eventsBox.get(MultiKey(room.id, eventId).toString()),
|
|
||||||
),
|
),
|
||||||
room,
|
)
|
||||||
),
|
.toList());
|
||||||
)
|
|
||||||
.toList());
|
@override
|
||||||
return events;
|
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
|
@override
|
||||||
|
|
|
||||||
|
|
@ -387,7 +387,7 @@ class Room {
|
||||||
|
|
||||||
/// The default count of how much events should be requested when requesting the
|
/// The default count of how much events should be requested when requesting the
|
||||||
/// history of this room.
|
/// 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
|
/// Calculates the displayname. First checks if there is a name, then checks for a canonical alias and
|
||||||
/// then generates a name from the heroes.
|
/// 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**
|
/// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before**
|
||||||
/// the historical events will be published in the onEvent stream.
|
/// the historical events will be published in the onEvent stream.
|
||||||
Future<void> requestHistory(
|
Future<void> requestHistory(
|
||||||
{int historyCount = defaultHistoryCount, onHistoryReceived}) async {
|
{int historyCount = defaultHistoryCount,
|
||||||
|
void Function() onHistoryReceived}) async {
|
||||||
if (prev_batch == null) {
|
if (prev_batch == null) {
|
||||||
throw 'Tried to request history without a prev_batch token';
|
throw 'Tried to request history without a prev_batch token';
|
||||||
}
|
}
|
||||||
|
|
@ -1069,7 +1070,11 @@ class Room {
|
||||||
await postLoad();
|
await postLoad();
|
||||||
var events;
|
var events;
|
||||||
if (client.database != null) {
|
if (client.database != null) {
|
||||||
events = await client.database.getEventList(client.id, this);
|
events = await client.database.getEventList(
|
||||||
|
client.id,
|
||||||
|
this,
|
||||||
|
limit: defaultHistoryCount,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
events = <Event>[];
|
events = <Event>[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ class Timeline {
|
||||||
// This ensures that the entire history fetching only triggers `onUpdate` only *once*,
|
// This ensures that the entire history fetching only triggers `onUpdate` only *once*,
|
||||||
// even if /sync's complete while history is being proccessed.
|
// even if /sync's complete while history is being proccessed.
|
||||||
bool _collectHistoryUpdates = false;
|
bool _collectHistoryUpdates = false;
|
||||||
final Set<EventUpdate> _historyUpdates = <EventUpdate>{};
|
|
||||||
|
|
||||||
bool get canRequestHistory {
|
bool get canRequestHistory {
|
||||||
if (events.isEmpty) return true;
|
if (events.isEmpty) return true;
|
||||||
|
|
@ -73,32 +72,36 @@ class Timeline {
|
||||||
|
|
||||||
Future<void> requestHistory(
|
Future<void> requestHistory(
|
||||||
{int historyCount = Room.defaultHistoryCount}) async {
|
{int historyCount = Room.defaultHistoryCount}) async {
|
||||||
if (!isRequestingHistory) {
|
if (isRequestingHistory) {
|
||||||
isRequestingHistory = true;
|
return;
|
||||||
await room.requestHistory(
|
|
||||||
historyCount: historyCount,
|
|
||||||
onHistoryReceived: () {
|
|
||||||
_collectHistoryUpdates = true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
_proccessHistoryUpdates();
|
|
||||||
} finally {
|
|
||||||
_collectHistoryUpdates = false;
|
|
||||||
isRequestingHistory = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
isRequestingHistory = true;
|
||||||
|
onUpdate?.call();
|
||||||
|
|
||||||
void _proccessHistoryUpdates() async {
|
try {
|
||||||
_collectHistoryUpdates = false;
|
// Look up for events in hive first
|
||||||
for (final update in _historyUpdates) {
|
final eventsFromStore = await room.client.database?.getEventList(
|
||||||
_handleEventUpdate(await update.decrypt(room, store: true),
|
room.client.id,
|
||||||
update: false);
|
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;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_collectHistoryUpdates = false;
|
||||||
|
isRequestingHistory = false;
|
||||||
|
onUpdate?.call();
|
||||||
}
|
}
|
||||||
_historyUpdates.clear();
|
|
||||||
if (onUpdate != null) onUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timeline({this.room, List<Event> events, this.onUpdate, this.onInsert})
|
Timeline({this.room, List<Event> events, this.onUpdate, this.onInsert})
|
||||||
|
|
@ -247,12 +250,6 @@ class Timeline {
|
||||||
try {
|
try {
|
||||||
if (eventUpdate.roomID != room.id) return;
|
if (eventUpdate.roomID != room.id) return;
|
||||||
|
|
||||||
if (eventUpdate.type == EventUpdateType.history &&
|
|
||||||
_collectHistoryUpdates) {
|
|
||||||
_historyUpdates.add(eventUpdate);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventUpdate.type != EventUpdateType.timeline &&
|
if (eventUpdate.type != EventUpdateType.timeline &&
|
||||||
eventUpdate.type != EventUpdateType.history) {
|
eventUpdate.type != EventUpdateType.history) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -318,7 +315,7 @@ class Timeline {
|
||||||
if (onInsert != null) onInsert(0);
|
if (onInsert != null) onInsert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (update) {
|
if (update && !_collectHistoryUpdates) {
|
||||||
if (onUpdate != null) onUpdate();
|
if (onUpdate != null) onUpdate();
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
|
|
||||||
|
|
@ -1337,7 +1337,7 @@ class FakeMatrixApi extends MockClient {
|
||||||
(var req) => messagesResponse,
|
(var req) => messagesResponse,
|
||||||
'/client/r0/rooms/!localpart%3Aserver.abc/messages?from=&dir=b&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D':
|
'/client/r0/rooms/!localpart%3Aserver.abc/messages?from=&dir=b&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D':
|
||||||
(var req) => messagesResponse,
|
(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,
|
(var req) => messagesResponse,
|
||||||
'/client/versions': (var req) => {
|
'/client/versions': (var req) => {
|
||||||
'versions': [
|
'versions': [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue