Merge branch 'td/fixParsing' into 'main'

chore: fix edited last events breaking db

See merge request famedly/company/frontend/famedlysdk!1309
This commit is contained in:
Nicolas Werner 2023-06-01 19:23:39 +00:00
commit bd4ae6d747
4 changed files with 159 additions and 37 deletions

View File

@ -990,6 +990,7 @@ class HiveCollectionsDatabase extends DatabaseApi {
Future<void> storeEventUpdate(EventUpdate eventUpdate, Client client) async { Future<void> storeEventUpdate(EventUpdate eventUpdate, Client client) async {
// Ephemerals should not be stored // Ephemerals should not be stored
if (eventUpdate.type == EventUpdateType.ephemeral) return; if (eventUpdate.type == EventUpdateType.ephemeral) return;
final tmpRoom = client.getRoomById(eventUpdate.roomID) ?? final tmpRoom = client.getRoomById(eventUpdate.roomID) ??
Room(id: eventUpdate.roomID, client: client); Room(id: eventUpdate.roomID, client: client);
@ -1107,13 +1108,17 @@ class HiveCollectionsDatabase extends DatabaseApi {
await removeEvent(transactionId, eventUpdate.roomID); await removeEvent(transactionId, eventUpdate.roomID);
} }
} }
final stateKey =
client.roomPreviewLastEvents.contains(eventUpdate.content['type'])
? ''
: eventUpdate.content['state_key'];
// Store a common state event // Store a common state event
if ({ if ({
EventUpdateType.timeline, EventUpdateType.timeline,
EventUpdateType.state, EventUpdateType.state,
EventUpdateType.inviteState EventUpdateType.inviteState
}.contains(eventUpdate.type)) { }.contains(eventUpdate.type) &&
stateKey != null) {
if (eventUpdate.content['type'] == EventTypes.RoomMember) { if (eventUpdate.content['type'] == EventTypes.RoomMember) {
await _roomMembersBox.put( await _roomMembersBox.put(
TupleKey( TupleKey(
@ -1133,29 +1138,36 @@ class HiveCollectionsDatabase extends DatabaseApi {
.tryGetMap<String, dynamic>('content') .tryGetMap<String, dynamic>('content')
?.tryGetMap<String, dynamic>('m.relates_to') == ?.tryGetMap<String, dynamic>('m.relates_to') ==
null) { null) {
stateMap[eventUpdate.content['state_key'] ?? ''] = stateMap[stateKey] = eventUpdate.content;
eventUpdate.content;
await _roomStateBox.put(key, stateMap); await _roomStateBox.put(key, stateMap);
} else { } else {
final editedEventRelationshipEventId = eventUpdate.content final editedEventRelationshipEventId = eventUpdate.content
.tryGetMap<String, dynamic>('content') .tryGetMap<String, dynamic>('content')
?.tryGetMap<String, dynamic>('m.relates_to') ?.tryGetMap<String, dynamic>('m.relates_to')
?.tryGet<String>('event_id'); ?.tryGet<String>('event_id');
final state = stateMap[''] == null
? null final tmpRoom = client.getRoomById(eventUpdate.roomID) ??
: Event.fromJson(stateMap[''] as Map<String, dynamic>, tmpRoom); Room(id: eventUpdate.roomID, client: client);
if (eventUpdate.content['type'] != EventTypes.Message ||
eventUpdate.content if (eventUpdate.content['type'] !=
.tryGetMap<String, dynamic>('content') EventTypes
?.tryGetMap<String, dynamic>('m.relates_to') .Message || // send anything other than a message
?.tryGet<String>('rel_type') != eventUpdate.content
RelationshipTypes.edit || .tryGetMap<String, dynamic>('content')
editedEventRelationshipEventId == state?.eventId || ?.tryGetMap<String, dynamic>('m.relates_to')
((state?.relationshipType == RelationshipTypes.edit && ?.tryGet<String>('rel_type') !=
RelationshipTypes
.edit || // replies are always latest anyway
editedEventRelationshipEventId == editedEventRelationshipEventId ==
state?.relationshipEventId))) { tmpRoom.lastEvent
stateMap[eventUpdate.content['state_key'] ?? ''] = ?.eventId || // edit of latest (original event) event
eventUpdate.content; (tmpRoom.lastEvent?.relationshipType ==
RelationshipTypes.edit &&
editedEventRelationshipEventId ==
tmpRoom.lastEvent
?.relationshipEventId) // edit of latest (edited event) event
) {
stateMap[stateKey] = eventUpdate.content;
await _roomStateBox.put(key, stateMap); await _roomStateBox.put(key, stateMap);
} }
} }

View File

@ -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 // Store a common state event
if ({ if ({
EventUpdateType.timeline, EventUpdateType.timeline,
EventUpdateType.state, EventUpdateType.state,
EventUpdateType.inviteState EventUpdateType.inviteState
}.contains(eventUpdate.type)) { }.contains(eventUpdate.type) &&
stateKey != null) {
if (eventUpdate.content['type'] == EventTypes.RoomMember) { if (eventUpdate.content['type'] == EventTypes.RoomMember) {
await _roomMembersBox.put( await _roomMembersBox.put(
MultiKey( MultiKey(
@ -1068,30 +1073,43 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
eventUpdate.content['type'], eventUpdate.content['type'],
).toString(); ).toString();
final Map stateMap = await _roomStateBox.get(key) ?? {}; 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 // 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. // 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 if (eventUpdate.content
.tryGetMap<String, dynamic>('content') .tryGetMap<String, dynamic>('content')
?.tryGetMap<String, dynamic>('m.relates_to') == ?.tryGetMap<String, dynamic>('m.relates_to') ==
null) { null) {
stateMap[eventUpdate.content['state_key']] = eventUpdate.content; stateMap[stateKey] = eventUpdate.content;
await _roomStateBox.put(key, stateMap); await _roomStateBox.put(key, stateMap);
} else { } else {
final editedEventRelationshipEventId = eventUpdate.content final editedEventRelationshipEventId = eventUpdate.content
.tryGetMap<String, dynamic>('content') .tryGetMap<String, dynamic>('content')
?.tryGetMap<String, dynamic>('m.relates_to') ?.tryGetMap<String, dynamic>('m.relates_to')
?.tryGet<String>('event_id'); ?.tryGet<String>('event_id');
if (eventUpdate.content['type'] != EventTypes.Message ||
eventUpdate.content final tmpRoom = client.getRoomById(eventUpdate.roomID) ??
.tryGetMap<String, dynamic>('content') Room(id: eventUpdate.roomID, client: client);
?.tryGetMap<String, dynamic>('m.relates_to')
?.tryGet<String>('rel_type') != if (eventUpdate.content['type'] !=
RelationshipTypes.edit || EventTypes
editedEventRelationshipEventId == stateMap['']?.eventId || .Message || // send anything other than a message
((stateMap['']?.relationshipType == RelationshipTypes.edit && eventUpdate.content
.tryGetMap<String, dynamic>('content')
?.tryGetMap<String, dynamic>('m.relates_to')
?.tryGet<String>('rel_type') !=
RelationshipTypes
.edit || // replies are always latest anyway
editedEventRelationshipEventId == editedEventRelationshipEventId ==
stateMap['']?.relationshipEventId))) { tmpRoom.lastEvent
stateMap[eventUpdate.content['state_key']] = eventUpdate.content; ?.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); await _roomStateBox.put(key, stateMap);
} }
} }

View File

@ -37,5 +37,4 @@ dev_dependencies:
import_sorter: ^4.6.0 import_sorter: ^4.6.0
lints: ^2.0.0 lints: ^2.0.0
test: ^1.15.7 test: ^1.15.7
test_api: 0.4.16 # pinned because of https://github.com/dart-lang/test/issues/1977
#flutter_test: {sdk: flutter} #flutter_test: {sdk: flutter}

View File

@ -332,6 +332,99 @@ void main() {
); );
expect(room.lastEvent?.body, 'B'); 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 { test('sendReadMarker', () async {
await room.setReadMarker('§1234:fakeServer.notExisting'); await room.setReadMarker('§1234:fakeServer.notExisting');
}); });