storeEventUpdate method

  1. @override
Future<void> storeEventUpdate(
  1. EventUpdate eventUpdate,
  2. Client client
)
override

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;

  // In case of this is a redaction event
  if (eventUpdate.content['type'] == EventTypes.Redaction) {
    final tmpRoom = client.getRoomById(eventUpdate.roomID) ??
        Room(id: eventUpdate.roomID, client: client);
    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(
          MultiKey(eventUpdate.roomID, event.eventId).toString(),
          event.toJson());

      if (tmpRoom.lastEvent?.eventId == event.eventId) {
        await _roomStateBox.put(
          MultiKey(eventUpdate.roomID, event.type).toString(),
          {'': event.toJson()},
        );
      }
    }
  }

  // Store a common message event
  if ({
    EventUpdateType.timeline,
    EventUpdateType.history,
    EventUpdateType.decryptedTimelineQueue
  }.contains(eventUpdate.type)) {
    final eventId = eventUpdate.content['event_id'];
    // Is this ID already in the store?
    final Map? prevEvent = await _eventsBox
        .get(MultiKey(eventUpdate.roomID, eventId).toString());
    final prevStatus = prevEvent == null
        ? null
        : () {
            final json = convertToJson(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(MultiKey(eventUpdate.roomID, eventId).toString(),
        eventUpdate.content);

    // Update timeline fragments
    final key = MultiKey(eventUpdate.roomID, status.isSent ? '' : 'SENDING')
        .toString();

    final List eventIds = (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 = MultiKey(eventUpdate.roomID, 'SENDING').toString();
      final List eventIds = (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) {
    if (eventUpdate.content['type'] == EventTypes.RoomMember) {
      await _roomMembersBox.put(
          MultiKey(
            eventUpdate.roomID,
            eventUpdate.content['state_key'],
          ).toString(),
          eventUpdate.content);
    } else {
      final key = MultiKey(
        eventUpdate.roomID,
        eventUpdate.content['type'],
      ).toString();
      final Map stateMap = 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(
      MultiKey(
        eventUpdate.roomID,
        eventUpdate.content['type'],
      ).toString(),
      eventUpdate.content,
    );
  }
}