fix: make sure to keep track of aggregated events after requesting history or future

This commit is contained in:
td 2025-04-30 17:43:55 +02:00
parent 94f23272c3
commit 7e41b01176
No known key found for this signature in database
GPG Key ID: 62A30523D4D6CE28
5 changed files with 116 additions and 11 deletions

View File

@ -1521,6 +1521,7 @@ class Room {
void Function()? onNewEvent, void Function()? onNewEvent,
void Function()? onUpdate, void Function()? onUpdate,
String? eventContextId, String? eventContextId,
int? limit = Room.defaultHistoryCount,
}) async { }) async {
await postLoad(); await postLoad();
@ -1529,7 +1530,7 @@ class Room {
if (!isArchived) { if (!isArchived) {
events = await client.database?.getEventList( events = await client.database?.getEventList(
this, this,
limit: defaultHistoryCount, limit: limit,
) ?? ) ??
<Event>[]; <Event>[];
} else { } else {

View File

@ -152,6 +152,9 @@ class Timeline {
); );
if (eventsFromStore != null && eventsFromStore.isNotEmpty) { if (eventsFromStore != null && eventsFromStore.isNotEmpty) {
for (final e in eventsFromStore) {
addAggregatedEvent(e);
}
// Fetch all users from database we have got here. // Fetch all users from database we have got here.
for (final event in events) { for (final event in events) {
if (room.getState(EventTypes.RoomMember, event.senderId) != null) { if (room.getState(EventTypes.RoomMember, event.senderId) != null) {
@ -500,12 +503,12 @@ class Timeline {
if (relationshipType == null || relationshipEventId == null) { if (relationshipType == null || relationshipEventId == null) {
return; // nothing to do return; // nothing to do
} }
final events = (aggregatedEvents[relationshipEventId] ??= final e = (aggregatedEvents[relationshipEventId] ??=
<String, Set<Event>>{})[relationshipType] ??= <Event>{}; <String, Set<Event>>{})[relationshipType] ??= <Event>{};
// remove a potential old event // remove a potential old event
_removeEventFromSet(events, event); _removeEventFromSet(e, event);
// add the new one // add the new one
events.add(event); e.add(event);
if (onChange != null) { if (onChange != null) {
final index = _findEvent(event_id: relationshipEventId); final index = _findEvent(event_id: relationshipEventId);
onChange?.call(index); onChange?.call(index);
@ -518,8 +521,8 @@ class Timeline {
aggregatedEvents.remove(event.transactionId); aggregatedEvents.remove(event.transactionId);
} }
for (final types in aggregatedEvents.values) { for (final types in aggregatedEvents.values) {
for (final events in types.values) { for (final e in types.values) {
_removeEventFromSet(events, event); _removeEventFromSet(e, event);
} }
} }
} }

View File

@ -32,7 +32,7 @@ dependencies:
sqflite_common: ^2.4.5 sqflite_common: ^2.4.5
sqlite3: ^2.1.0 sqlite3: ^2.1.0
typed_data: ^1.3.2 typed_data: ^1.3.2
webrtc_interface: ^1.2.2+hotfix.1 webrtc_interface: ^1.2.0
dev_dependencies: dev_dependencies:
build_runner: ^2.4.15 build_runner: ^2.4.15

View File

@ -1098,6 +1098,111 @@ void main() {
expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events[0].status, EventStatus.synced);
expect(timeline.events.length, 1); expect(timeline.events.length, 1);
}); });
test('make sure aggregated events are updated on requestHistory', () async {
timeline.events.clear();
await client.handleSync(
SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(
join: {
timeline.room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(
events: [
MatrixEvent.fromJson({
'type': 'm.room.message',
'content': {'msgtype': 'm.text', 'body': 'Testcase'},
'event_id': '11',
'sender': '@alice:example.com',
'origin_server_ts': DateTime.now().millisecondsSinceEpoch,
}),
MatrixEvent.fromJson({
'type': 'm.room.message',
'content': {'msgtype': 'm.text', 'body': 'Testcase'},
'event_id': '22',
'sender': '@alice:example.com',
'origin_server_ts': DateTime.now().millisecondsSinceEpoch,
}),
MatrixEvent.fromJson({
'type': 'm.room.message',
'content': {
'msgtype': 'm.text',
'body': '* edit 11',
'm.new_content': {
'msgtype': 'm.text',
'body': 'edit 11',
'm.mentions': {},
},
'm.mentions': {},
'm.relates_to': {
'rel_type': 'm.replace',
'event_id': '11',
},
},
'event_id': '33',
'sender': '@alice:example.com',
'origin_server_ts': DateTime.now().millisecondsSinceEpoch,
}),
MatrixEvent.fromJson({
'type': 'm.room.message',
'content': {
'msgtype': 'm.text',
'body': '* edit 22',
'm.new_content': {
'msgtype': 'm.text',
'body': 'edit 22',
'm.mentions': {},
},
'm.mentions': {},
'm.relates_to': {
'rel_type': 'm.replace',
'event_id': '22',
},
},
'event_id': '44',
'sender': '@alice:example.com',
'origin_server_ts': DateTime.now().millisecondsSinceEpoch,
}),
],
),
),
},
),
),
);
final t = await room.getTimeline(limit: 1);
expect(t.events.length, 1);
expect(
t.events.single.getDisplayEvent(t).body,
'* edit 22',
);
await t.requestHistory();
expect(
t.events.reversed
.where(
(element) => element.relationshipType != RelationshipTypes.edit,
)
.last
.getDisplayEvent(t)
.body,
'edit 22',
);
expect(
t.events.reversed
.where(
(element) => element.relationshipType != RelationshipTypes.edit,
)
.first
.getDisplayEvent(t)
.body,
'edit 11',
);
});
test('logout', () async { test('logout', () async {
await client.logout(); await client.logout();
}); });

View File

@ -849,8 +849,4 @@ class MockVideoRenderer implements VideoRenderer {
// Mock implementation for disposing VideoRenderer // Mock implementation for disposing VideoRenderer
Logs().i('Mock: Disposing VideoRenderer'); Logs().i('Mock: Disposing VideoRenderer');
} }
@override
// TODO: implement videoValue
RTCVideoValue get videoValue => RTCVideoValue.empty;
} }