Merge branch 'krille/timeline-auto-update-list' into 'main'

feat: Add onInsert, onRemove and onUpdate cb to timeline

See merge request famedly/company/frontend/famedlysdk!880
This commit is contained in:
Nicolas Werner 2021-11-09 13:08:42 +00:00
commit 7f9a75a43d
2 changed files with 53 additions and 21 deletions

View File

@ -35,7 +35,9 @@ class Timeline {
final Map<String, Map<String, Set<Event>>> aggregatedEvents = {}; final Map<String, Map<String, Set<Event>>> aggregatedEvents = {};
final void Function()? onUpdate; final void Function()? onUpdate;
final void Function(int insertID)? onInsert; final void Function(int index)? onChange;
final void Function(int index)? onInsert;
final void Function(int index)? onRemove;
StreamSubscription<EventUpdate>? sub; StreamSubscription<EventUpdate>? sub;
StreamSubscription<SyncUpdate>? roomSub; StreamSubscription<SyncUpdate>? roomSub;
@ -86,6 +88,11 @@ class Timeline {
); );
if (eventsFromStore != null && eventsFromStore.isNotEmpty) { if (eventsFromStore != null && eventsFromStore.isNotEmpty) {
events.addAll(eventsFromStore); events.addAll(eventsFromStore);
final startIndex = events.length - eventsFromStore.length;
final endIndex = events.length;
for (var i = startIndex; i < endIndex; i++) {
onInsert?.call(i);
}
} else { } else {
Logs().v('No more events found in the store. Request from server...'); Logs().v('No more events found in the store. Request from server...');
await room.requestHistory( await room.requestHistory(
@ -102,9 +109,14 @@ class Timeline {
} }
} }
Timeline( Timeline({
{required this.room, List<Event>? events, this.onUpdate, this.onInsert}) required this.room,
: events = events ?? [] { List<Event>? events,
this.onUpdate,
this.onChange,
this.onInsert,
this.onRemove,
}) : events = events ?? [] {
sub = room.client.onEvent.stream.listen(_handleEventUpdate); sub = room.client.onEvent.stream.listen(_handleEventUpdate);
// If the timeline is limited we want to clear our events cache // If the timeline is limited we want to clear our events cache
roomSub = room.client.onSync.stream roomSub = room.client.onSync.stream
@ -142,6 +154,7 @@ class Timeline {
events[i].content['session_id'] == sessionId) { events[i].content['session_id'] == sessionId) {
events[i] = await encryption.decryptRoomEvent(room.id, events[i], events[i] = await encryption.decryptRoomEvent(room.id, events[i],
store: true); store: true);
onChange?.call(i);
if (events[i].type != EventTypes.Encrypted) { if (events[i].type != EventTypes.Encrypted) {
decryptAtLeastOneEvent = true; decryptAtLeastOneEvent = true;
} }
@ -249,19 +262,21 @@ class Timeline {
EventStatus.synced.intValue); EventStatus.synced.intValue);
// Redaction events are handled as modification for existing events. // Redaction events are handled as modification for existing events.
if (eventUpdate.content['type'] == EventTypes.Redaction) { if (eventUpdate.content['type'] == EventTypes.Redaction) {
final eventId = _findEvent(event_id: eventUpdate.content['redacts']); final index = _findEvent(event_id: eventUpdate.content['redacts']);
if (eventId < events.length) { if (index < events.length) {
removeAggregatedEvent(events[eventId]); removeAggregatedEvent(events[index]);
events[eventId].setRedactionEvent(Event.fromJson( events[index].setRedactionEvent(Event.fromJson(
eventUpdate.content, eventUpdate.content,
room, room,
)); ));
onChange?.call(index);
} }
} else if (status.isRemoved) { } else if (status.isRemoved) {
final i = _findEvent(event_id: eventUpdate.content['event_id']); final i = _findEvent(event_id: eventUpdate.content['event_id']);
if (i < events.length) { if (i < events.length) {
removeAggregatedEvent(events[i]); removeAggregatedEvent(events[i]);
events.removeAt(i); events.removeAt(i);
onRemove?.call(i);
} }
} else { } else {
final i = _findEvent( final i = _findEvent(
@ -283,6 +298,7 @@ class Timeline {
events[i].status = oldStatus; events[i].status = oldStatus;
} }
addAggregatedEvent(events[i]); addAggregatedEvent(events[i]);
onChange?.call(i);
} else { } else {
final newEvent = Event.fromJson( final newEvent = Event.fromJson(
eventUpdate.content, eventUpdate.content,
@ -293,14 +309,16 @@ class Timeline {
events.indexWhere( events.indexWhere(
(e) => e.eventId == eventUpdate.content['event_id']) != (e) => e.eventId == eventUpdate.content['event_id']) !=
-1) return; -1) return;
var index = events.length;
if (eventUpdate.type == EventUpdateType.history) { if (eventUpdate.type == EventUpdateType.history) {
events.add(newEvent); events.add(newEvent);
} else { } else {
events.insert(events.firstIndexWhereNotError, newEvent); index = events.firstIndexWhereNotError;
events.insert(index, newEvent);
} }
addAggregatedEvent(newEvent); addAggregatedEvent(newEvent);
onInsert?.call(0); onInsert?.call(index);
} }
} }
if (update && !_collectHistoryUpdates) { if (update && !_collectHistoryUpdates) {

View File

@ -34,6 +34,8 @@ void main() {
final testTimeStamp = DateTime.now().millisecondsSinceEpoch; final testTimeStamp = DateTime.now().millisecondsSinceEpoch;
var updateCount = 0; var updateCount = 0;
final insertList = <int>[]; final insertList = <int>[];
final changeList = <int>[];
final removeList = <int>[];
var olmEnabled = true; var olmEnabled = true;
late Client client; late Client client;
@ -54,14 +56,15 @@ void main() {
room = Room( room = Room(
id: roomID, client: client, prev_batch: '1234', roomAccountData: {}); id: roomID, client: client, prev_batch: '1234', roomAccountData: {});
timeline = Timeline( timeline = Timeline(
room: room, room: room,
events: [], events: [],
onUpdate: () { onUpdate: () {
updateCount++; updateCount++;
}, },
onInsert: (int insertID) { onInsert: insertList.add,
insertList.add(insertID); onChange: changeList.add,
}); onRemove: removeList.add,
);
}); });
test('Create', () async { test('Create', () async {
@ -100,6 +103,8 @@ void main() {
expect(updateCount, 2); expect(updateCount, 2);
expect(insertList, [0, 0]); expect(insertList, [0, 0]);
expect(insertList.length, timeline.events.length); expect(insertList.length, timeline.events.length);
expect(changeList, []);
expect(removeList, []);
expect(timeline.events.length, 2); expect(timeline.events.length, 2);
expect(timeline.events[0].eventId, '1'); expect(timeline.events[0].eventId, '1');
expect(timeline.events[0].sender.id, '@alice:example.com'); expect(timeline.events[0].sender.id, '@alice:example.com');
@ -146,6 +151,8 @@ void main() {
expect(updateCount, 3); expect(updateCount, 3);
expect(insertList, [0, 0]); expect(insertList, [0, 0]);
expect(insertList.length, timeline.events.length); expect(insertList.length, timeline.events.length);
expect(changeList, [1]);
expect(removeList, []);
expect(timeline.events.length, 2); expect(timeline.events.length, 2);
expect(timeline.events[1].redacted, true); expect(timeline.events[1].redacted, true);
}); });
@ -211,8 +218,10 @@ void main() {
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect(updateCount, 13); expect(updateCount, 13);
expect(insertList, [0, 0, 0, 0, 0, 0, 0]); expect(insertList, [0, 0, 0, 0, 0, 1, 2]);
expect(insertList.length, timeline.events.length); expect(insertList.length, timeline.events.length);
expect(changeList, [1, 0, 0, 0, 1, 2]);
expect(removeList, []);
expect(timeline.events[0].status, EventStatus.error); expect(timeline.events[0].status, EventStatus.error);
expect(timeline.events[1].status, EventStatus.error); expect(timeline.events[1].status, EventStatus.error);
expect(timeline.events[2].status, EventStatus.error); expect(timeline.events[2].status, EventStatus.error);
@ -225,7 +234,9 @@ void main() {
expect(updateCount, 14); expect(updateCount, 14);
expect(insertList, [0, 0, 0, 0, 0, 0, 0]); expect(insertList, [0, 0, 0, 0, 0, 1, 2]);
expect(changeList, [1, 0, 0, 0, 1, 2]);
expect(removeList, [0]);
expect(timeline.events.length, 6); expect(timeline.events.length, 6);
expect(timeline.events[0].status, EventStatus.error); expect(timeline.events[0].status, EventStatus.error);
}); });
@ -270,7 +281,9 @@ void main() {
expect(updateCount, 17); expect(updateCount, 17);
expect(insertList, [0, 0, 0, 0, 0, 0, 0, 0]); expect(insertList, [0, 0, 0, 0, 0, 1, 2, 0]);
expect(changeList, [1, 0, 0, 0, 1, 2, 0, 0]);
expect(removeList, [0]);
expect(timeline.events.length, 1); expect(timeline.events.length, 1);
expect(timeline.events[0].status, EventStatus.sent); expect(timeline.events[0].status, EventStatus.sent);
}); });
@ -283,6 +296,7 @@ void main() {
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect(updateCount, 20); expect(updateCount, 20);
expect(insertList, [0, 0, 0, 0, 0, 1, 2, 0, 0, 1, 2]);
expect(timeline.events.length, 3); expect(timeline.events.length, 3);
expect(timeline.events[0].eventId, '3143273582443PhrSn:example.org'); expect(timeline.events[0].eventId, '3143273582443PhrSn:example.org');
expect(timeline.events[1].eventId, '2143273582443PhrSn:example.org'); expect(timeline.events[1].eventId, '2143273582443PhrSn:example.org');