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;
  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,
    );
  }
}