fix: Timeline history requests causing "scrolling" and sometimes ordering things wrong

This commit is contained in:
Sorunome 2020-10-29 18:07:26 +01:00
parent 793d398d72
commit 33b1e36efd
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
2 changed files with 91 additions and 59 deletions

View File

@ -902,6 +902,7 @@ class Room {
'$id': (JoinedRoomUpdate()
..state = resp.state
..timeline = (TimelineUpdate()
..limited = false
..events = resp.chunk
..prevBatch = resp.end)),
}),

View File

@ -33,10 +33,10 @@ typedef onTimelineInsertCallback = void Function(int insertID);
/// event list will be retreived when created by the [room.getTimeline] method.
class Timeline {
final Room room;
List<Event> events = [];
final List<Event> events;
/// Map of event ID to map of type to set of aggregated events
Map<String, Map<String, Set<Event>>> aggregatedEvents = {};
final Map<String, Map<String, Set<Event>>> aggregatedEvents = {};
final onTimelineUpdateCallback onUpdate;
final onTimelineInsertCallback onInsert;
@ -62,6 +62,13 @@ class Timeline {
return _eventCache[id];
}
// When fetching history, we will collect them into the `_historyUpdates` set
// first, and then only process all events at once, once we have the full history.
// 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>{};
Future<void> requestHistory(
{int historyCount = Room.DefaultHistoryCount}) async {
if (!_requestingHistoryLock) {
@ -69,18 +76,32 @@ class Timeline {
await room.requestHistory(
historyCount: historyCount,
onHistoryReceived: () {
if (room.prev_batch.isEmpty || room.prev_batch == null) {
events.clear();
aggregatedEvents.clear();
}
_collectHistoryUpdates = true;
},
);
try {
await Future.delayed(const Duration(seconds: 2));
_proccessHistoryUpdates();
} finally {
_collectHistoryUpdates = false;
_requestingHistoryLock = false;
}
}
}
Timeline({this.room, this.events, this.onUpdate, this.onInsert}) {
void _proccessHistoryUpdates() async {
_collectHistoryUpdates = false;
for (final update in _historyUpdates) {
_handleEventUpdate(await update.decrypt(room, store: true),
sortAndUpdate: false);
}
_historyUpdates.clear();
_sort();
if (onUpdate != null) onUpdate();
}
Timeline({this.room, List<Event> events, this.onUpdate, this.onInsert})
: events = events ?? [] {
sub ??= room.client.onEvent.stream.listen(_handleEventUpdate);
// if the timeline is limited we want to clear our events cache
// as r.limitedTimeline can be "null" sometimes, we need to check for == true
@ -222,12 +243,21 @@ class Timeline {
}
}
void _handleEventUpdate(EventUpdate eventUpdate) async {
void _handleEventUpdate(EventUpdate eventUpdate,
{bool sortAndUpdate = true}) {
try {
if (eventUpdate.roomID != room.id) return;
if (eventUpdate.type == EventUpdateType.timeline ||
eventUpdate.type == EventUpdateType.history) {
if (eventUpdate.type == EventUpdateType.history &&
_collectHistoryUpdates) {
_historyUpdates.add(eventUpdate);
return;
}
if (eventUpdate.type != EventUpdateType.timeline &&
eventUpdate.type != EventUpdateType.history) {
return;
}
var status = eventUpdate.content['status'] ??
(eventUpdate.content['unsigned'] is Map<String, dynamic>
? eventUpdate.content['unsigned'][MessageSendingStatusKey]
@ -238,8 +268,8 @@ class Timeline {
final eventId = _findEvent(event_id: eventUpdate.content['redacts']);
if (eventId < events.length) {
removeAggregatedEvent(events[eventId]);
events[eventId].setRedactionEvent(Event.fromJson(
eventUpdate.content, room, eventUpdate.sortOrder));
events[eventId].setRedactionEvent(
Event.fromJson(eventUpdate.content, room, eventUpdate.sortOrder));
}
} else if (status == -2) {
var i = _findEvent(event_id: eventUpdate.content['event_id']);
@ -257,16 +287,16 @@ class Timeline {
if (i < events.length) {
// if the old status is larger than the new one, we also want to preserve the old status
final oldStatus = events[i].status;
events[i] = Event.fromJson(
eventUpdate.content, room, eventUpdate.sortOrder);
events[i] =
Event.fromJson(eventUpdate.content, room, eventUpdate.sortOrder);
// do we preserve the status? we should allow 0 -> -1 updates and status increases
if (status < oldStatus && !(status == -1 && oldStatus == 0)) {
events[i].status = oldStatus;
}
addAggregatedEvent(events[i]);
} else {
var newEvent = Event.fromJson(
eventUpdate.content, room, eventUpdate.sortOrder);
var newEvent =
Event.fromJson(eventUpdate.content, room, eventUpdate.sortOrder);
if (eventUpdate.type == EventUpdateType.history &&
events.indexWhere(
@ -278,9 +308,10 @@ class Timeline {
if (onInsert != null) onInsert(0);
}
}
}
if (sortAndUpdate) {
_sort();
if (onUpdate != null) onUpdate();
}
} catch (e, s) {
Logs.warning('Handle event update failed: ${e.toString()}', s);
}