storeEventUpdate method
Stores an EventUpdate object in the database. Must be called inside of transaction.
Implementation
@override
Future<void> 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);
// In case of this is a redaction event
if (eventUpdate.content['type'] == EventTypes.Redaction) {
final eventId = eventUpdate.content.tryGet<String>('redacts');
final event =
eventId != null ? await getEventById(eventId, tmpRoom) : null;
if (event != null) {
event.setRedactionEvent(Event.fromJson(eventUpdate.content, tmpRoom));
await _eventsBox.put(
TupleKey(eventUpdate.roomID, event.eventId).toString(),
event.toJson());
if (tmpRoom.lastEvent?.eventId == event.eventId) {
if (client.importantStateEvents.contains(event.type)) {
await _preloadRoomStateBox.put(
TupleKey(eventUpdate.roomID, event.type).toString(),
{'': event.toJson()},
);
} else {
await _nonPreloadRoomStateBox.put(
TupleKey(eventUpdate.roomID, event.type).toString(),
{'': event.toJson()},
);
}
}
}
}
// Store a common message event
if ({EventUpdateType.timeline, EventUpdateType.history}
.contains(eventUpdate.type)) {
final eventId = eventUpdate.content['event_id'];
// Is this ID already in the store?
final prevEvent = await _eventsBox
.get(TupleKey(eventUpdate.roomID, eventId).toString());
final prevStatus = prevEvent == null
? null
: () {
final json = copyMap(prevEvent);
final statusInt = json.tryGet<int>('status') ??
json
.tryGetMap<String, dynamic>('unsigned')
?.tryGet<int>(messageSendingStatusKey);
return statusInt == null ? null : eventStatusFromInt(statusInt);
}();
// calculate the status
final newStatus = eventStatusFromInt(
eventUpdate.content.tryGet<int>('status') ??
eventUpdate.content
.tryGetMap<String, dynamic>('unsigned')
?.tryGet<int>(messageSendingStatusKey) ??
EventStatus.synced.intValue,
);
// Is this the response to a sending event which is already synced? Then
// there is nothing to do here.
if (!newStatus.isSynced && prevStatus != null && prevStatus.isSynced) {
return;
}
final status = newStatus.isError || prevStatus == null
? newStatus
: latestEventStatus(
prevStatus,
newStatus,
);
// Add the status and the sort order to the content so it get stored
eventUpdate.content['unsigned'] ??= <String, dynamic>{};
eventUpdate.content['unsigned'][messageSendingStatusKey] =
eventUpdate.content['status'] = status.intValue;
// In case this event has sent from this account we have a transaction ID
final transactionId = eventUpdate.content
.tryGetMap<String, dynamic>('unsigned')
?.tryGet<String>('transaction_id');
await _eventsBox.put(TupleKey(eventUpdate.roomID, eventId).toString(),
eventUpdate.content);
// Update timeline fragments
final key = TupleKey(eventUpdate.roomID, status.isSent ? '' : 'SENDING')
.toString();
final eventIds =
List<String>.from(await _timelineFragmentsBox.get(key) ?? []);
if (!eventIds.contains(eventId)) {
if (eventUpdate.type == EventUpdateType.history) {
eventIds.add(eventId);
} else {
eventIds.insert(0, eventId);
}
await _timelineFragmentsBox.put(key, eventIds);
} else if (status.isSynced &&
prevStatus != null &&
prevStatus.isSent &&
eventUpdate.type != EventUpdateType.history) {
// Status changes from 1 -> 2? Make sure event is correctly sorted.
eventIds.remove(eventId);
eventIds.insert(0, eventId);
}
// If event comes from server timeline, remove sending events with this ID
if (status.isSent) {
final key = TupleKey(eventUpdate.roomID, 'SENDING').toString();
final eventIds =
List<String>.from(await _timelineFragmentsBox.get(key) ?? []);
final i = eventIds.indexWhere((id) => id == eventId);
if (i != -1) {
await _timelineFragmentsBox.put(key, eventIds..removeAt(i));
}
}
// Is there a transaction id? Then delete the event with this id.
if (!status.isError && !status.isSending && transactionId != null) {
await removeEvent(transactionId, eventUpdate.roomID);
}
}
final stateKey = eventUpdate.content['state_key'];
// Store a common state event
if (stateKey != null &&
// Don't store events as state updates when paginating backwards.
(eventUpdate.type == EventUpdateType.timeline ||
eventUpdate.type == EventUpdateType.state ||
eventUpdate.type == EventUpdateType.inviteState)) {
if (eventUpdate.content['type'] == EventTypes.RoomMember) {
await _roomMembersBox.put(
TupleKey(
eventUpdate.roomID,
eventUpdate.content['state_key'],
).toString(),
eventUpdate.content);
} else {
final type = eventUpdate.content['type'] as String;
final roomStateBox = client.importantStateEvents.contains(type)
? _preloadRoomStateBox
: _nonPreloadRoomStateBox;
final key = TupleKey(
eventUpdate.roomID,
type,
).toString();
final stateMap = copyMap(await roomStateBox.get(key) ?? {});
stateMap[stateKey] = eventUpdate.content;
await roomStateBox.put(key, stateMap);
}
}
// Store a room account data event
if (eventUpdate.type == EventUpdateType.accountData) {
await _roomAccountDataBox.put(
TupleKey(
eventUpdate.roomID,
eventUpdate.content['type'],
).toString(),
eventUpdate.content,
);
}
}