diff --git a/lib/src/database/hive_collections_database.dart b/lib/src/database/hive_collections_database.dart index cab11485..94a896e4 100644 --- a/lib/src/database/hive_collections_database.dart +++ b/lib/src/database/hive_collections_database.dart @@ -990,6 +990,7 @@ class HiveCollectionsDatabase extends DatabaseApi { Future storeEventUpdate(EventUpdate eventUpdate, Client client) async { // Ephemerals should not be stored if (eventUpdate.type == EventUpdateType.ephemeral) return; + final tmpRoom = client.getRoomById(eventUpdate.roomID) ?? Room(id: eventUpdate.roomID, client: client); @@ -1107,13 +1108,17 @@ class HiveCollectionsDatabase extends DatabaseApi { await removeEvent(transactionId, eventUpdate.roomID); } } - + final stateKey = + client.roomPreviewLastEvents.contains(eventUpdate.content['type']) + ? '' + : eventUpdate.content['state_key']; // Store a common state event if ({ - EventUpdateType.timeline, - EventUpdateType.state, - EventUpdateType.inviteState - }.contains(eventUpdate.type)) { + EventUpdateType.timeline, + EventUpdateType.state, + EventUpdateType.inviteState + }.contains(eventUpdate.type) && + stateKey != null) { if (eventUpdate.content['type'] == EventTypes.RoomMember) { await _roomMembersBox.put( TupleKey( @@ -1133,29 +1138,36 @@ class HiveCollectionsDatabase extends DatabaseApi { .tryGetMap('content') ?.tryGetMap('m.relates_to') == null) { - stateMap[eventUpdate.content['state_key'] ?? ''] = - eventUpdate.content; + stateMap[stateKey] = eventUpdate.content; await _roomStateBox.put(key, stateMap); } else { final editedEventRelationshipEventId = eventUpdate.content .tryGetMap('content') ?.tryGetMap('m.relates_to') ?.tryGet('event_id'); - final state = stateMap[''] == null - ? null - : Event.fromJson(stateMap[''] as Map, tmpRoom); - if (eventUpdate.content['type'] != EventTypes.Message || - eventUpdate.content - .tryGetMap('content') - ?.tryGetMap('m.relates_to') - ?.tryGet('rel_type') != - RelationshipTypes.edit || - editedEventRelationshipEventId == state?.eventId || - ((state?.relationshipType == RelationshipTypes.edit && + + final tmpRoom = client.getRoomById(eventUpdate.roomID) ?? + Room(id: eventUpdate.roomID, client: client); + + if (eventUpdate.content['type'] != + EventTypes + .Message || // send anything other than a message + eventUpdate.content + .tryGetMap('content') + ?.tryGetMap('m.relates_to') + ?.tryGet('rel_type') != + RelationshipTypes + .edit || // replies are always latest anyway editedEventRelationshipEventId == - state?.relationshipEventId))) { - stateMap[eventUpdate.content['state_key'] ?? ''] = - eventUpdate.content; + tmpRoom.lastEvent + ?.eventId || // edit of latest (original event) event + (tmpRoom.lastEvent?.relationshipType == + RelationshipTypes.edit && + editedEventRelationshipEventId == + tmpRoom.lastEvent + ?.relationshipEventId) // edit of latest (edited event) event + ) { + stateMap[stateKey] = eventUpdate.content; await _roomStateBox.put(key, stateMap); } } diff --git a/lib/src/database/hive_database.dart b/lib/src/database/hive_database.dart index 5f6da7e6..114b4232 100644 --- a/lib/src/database/hive_database.dart +++ b/lib/src/database/hive_database.dart @@ -1049,12 +1049,17 @@ class FamedlySdkHiveDatabase extends DatabaseApi { } } + final stateKey = + client.roomPreviewLastEvents.contains(eventUpdate.content['type']) + ? '' + : eventUpdate.content['state_key']; // Store a common state event if ({ - EventUpdateType.timeline, - EventUpdateType.state, - EventUpdateType.inviteState - }.contains(eventUpdate.type)) { + EventUpdateType.timeline, + EventUpdateType.state, + EventUpdateType.inviteState + }.contains(eventUpdate.type) && + stateKey != null) { if (eventUpdate.content['type'] == EventTypes.RoomMember) { await _roomMembersBox.put( MultiKey( @@ -1068,30 +1073,43 @@ class FamedlySdkHiveDatabase extends DatabaseApi { eventUpdate.content['type'], ).toString(); final Map stateMap = await _roomStateBox.get(key) ?? {}; + // store state events and new messages, that either are not an edit or an edit of the lastest message // An edit is an event, that has an edit relation to the latest event. In some cases for the second edit, we need to compare if both have an edit relation to the same event instead. if (eventUpdate.content .tryGetMap('content') ?.tryGetMap('m.relates_to') == null) { - stateMap[eventUpdate.content['state_key']] = eventUpdate.content; + stateMap[stateKey] = eventUpdate.content; await _roomStateBox.put(key, stateMap); } else { final editedEventRelationshipEventId = eventUpdate.content .tryGetMap('content') ?.tryGetMap('m.relates_to') ?.tryGet('event_id'); - if (eventUpdate.content['type'] != EventTypes.Message || - eventUpdate.content - .tryGetMap('content') - ?.tryGetMap('m.relates_to') - ?.tryGet('rel_type') != - RelationshipTypes.edit || - editedEventRelationshipEventId == stateMap['']?.eventId || - ((stateMap['']?.relationshipType == RelationshipTypes.edit && + + final tmpRoom = client.getRoomById(eventUpdate.roomID) ?? + Room(id: eventUpdate.roomID, client: client); + + if (eventUpdate.content['type'] != + EventTypes + .Message || // send anything other than a message + eventUpdate.content + .tryGetMap('content') + ?.tryGetMap('m.relates_to') + ?.tryGet('rel_type') != + RelationshipTypes + .edit || // replies are always latest anyway editedEventRelationshipEventId == - stateMap['']?.relationshipEventId))) { - stateMap[eventUpdate.content['state_key']] = eventUpdate.content; + tmpRoom.lastEvent + ?.eventId || // edit of latest (original event) event + (tmpRoom.lastEvent?.relationshipType == + RelationshipTypes.edit && + editedEventRelationshipEventId == + tmpRoom.lastEvent + ?.relationshipEventId) // edit of latest (edited event) event + ) { + stateMap[stateKey] = eventUpdate.content; await _roomStateBox.put(key, stateMap); } } diff --git a/pubspec.yaml b/pubspec.yaml index 2527a988..8bec74e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,5 +37,4 @@ dev_dependencies: import_sorter: ^4.6.0 lints: ^2.0.0 test: ^1.15.7 - test_api: 0.4.16 # pinned because of https://github.com/dart-lang/test/issues/1977 #flutter_test: {sdk: flutter} diff --git a/test/room_test.dart b/test/room_test.dart index dc407bf3..04ef1813 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -332,6 +332,99 @@ void main() { ); expect(room.lastEvent?.body, 'B'); }); + + test('lastEvent with deleted message', () async { + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.encrypted', + room: room, + eventId: '8', + originServerTs: DateTime.now(), + content: {'msgtype': 'm.text', 'body': 'AA'}, + stateKey: '', + ), + ); + expect(room.lastEvent?.body, 'AA'); + + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.encrypted', + room: room, + eventId: '9', + originServerTs: DateTime.now(), + content: { + 'msgtype': 'm.text', + 'body': 'B', + 'm.relates_to': {'rel_type': 'm.in_reply_to', 'event_id': '8'} + }, + stateKey: '', + ), + ); + expect(room.lastEvent?.body, 'B'); + + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.encrypted', + room: room, + eventId: '10', + originServerTs: DateTime.now(), + content: { + 'type': 'm.room.redaction', + 'content': {'reason': 'test'}, + 'sender': '@test:example.com', + 'redacts': '9', + 'event_id': '10', + 'origin_server_ts': DateTime.now(), + }, + stateKey: '', + ), + ); + expect(room.lastEvent?.eventId, '10'); + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.encrypted', + room: room, + eventId: '11', + originServerTs: DateTime.now(), + content: {'msgtype': 'm.text', 'body': 'BB'}, + stateKey: '', + ), + ); + expect(room.lastEvent?.body, 'BB'); + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.encrypted', + room: room, + eventId: '12', + originServerTs: DateTime.now(), + content: { + 'm.new_content': {'msgtype': 'm.text', 'body': 'BBB'}, + 'm.relates_to': {'rel_type': 'm.replace', 'event_id': '11'}, + 'msgtype': 'm.text', + 'body': '* BBB', + }, + stateKey: '', + ), + ); + expect(room.lastEvent?.body, '* BBB'); + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.name', + room: room, + eventId: '12', + originServerTs: DateTime.now(), + content: {'body': 'brainfarts'}, + ), + ); + expect(room.lastEvent?.body, '* BBB'); + }); + test('sendReadMarker', () async { await room.setReadMarker('ยง1234:fakeServer.notExisting'); });