searchEvent method

Stream<List<Event>> searchEvent({
  1. String? searchTerm,
  2. int requestHistoryCount = 100,
  3. int maxHistoryRequests = 10,
  4. String? sinceEventId,
  5. int? limit,
  6. bool searchFunc(
    1. Event
    )?,
})

Searches searchTerm in this timeline. It first searches in the cache, then in the database and then on the server. The search can take a while, which is why this returns a stream so the already found events can already be displayed. Override the searchFunc if you need another search. This will then ignore searchTerm.

Implementation

Stream<List<Event>> searchEvent({
  String? searchTerm,
  int requestHistoryCount = 100,
  int maxHistoryRequests = 10,
  String? sinceEventId,
  int? limit,
  bool Function(Event)? searchFunc,
}) async* {
  assert(searchTerm != null || searchFunc != null);
  searchFunc ??= (event) =>
      event.body.toLowerCase().contains(searchTerm?.toLowerCase() ?? '');
  final found = <Event>[];

  if (sinceEventId == null) {
    // Search locally
    for (final event in events) {
      if (searchFunc(event)) {
        yield found..add(event);
      }
    }

    // Search in database
    var start = events.length;
    while (true) {
      final eventsFromStore = await room.client.database?.getEventList(
            room,
            start: start,
            limit: requestHistoryCount,
          ) ??
          [];
      if (eventsFromStore.isEmpty) break;
      start += eventsFromStore.length;
      for (final event in eventsFromStore) {
        if (searchFunc(event)) {
          yield found..add(event);
        }
      }
    }
  }

  // Search on the server
  var prevBatch = room.prev_batch;
  if (sinceEventId != null) {
    prevBatch =
        (await room.client.getEventContext(room.id, sinceEventId)).end;
  }
  final encryption = room.client.encryption;
  for (var i = 0; i < maxHistoryRequests; i++) {
    if (prevBatch == null) break;
    if (limit != null && found.length >= limit) break;
    try {
      final resp = await room.client.getRoomEvents(
        room.id,
        Direction.b,
        from: prevBatch,
        limit: requestHistoryCount,
        filter: jsonEncode(StateFilter(lazyLoadMembers: true).toJson()),
      );
      for (final sdnEvent in resp.chunk) {
        var event = Event.fromSDNEvent(sdnEvent, room);
        if (event.type == EventTypes.Encrypted && encryption != null) {
          event = await encryption.decryptRoomEvent(room.id, event);
          if (event.type == EventTypes.Encrypted &&
              event.messageType == MessageTypes.BadEncrypted &&
              event.content['can_request_session'] == true) {
            // Await requestKey() here to ensure decrypted message bodies
            await event.requestKey();
          }
        }
        if (searchFunc(event)) {
          yield found..add(event);
          if (limit != null && found.length >= limit) break;
        }
      }
      prevBatch = resp.end;
      // We are at the beginning of the room
      if (resp.chunk.length < requestHistoryCount) break;
    } on SDNException catch (e) {
      // We have no permission anymore to request the history
      if (e.error == SDNError.M_FORBIDDEN) {
        break;
      }
      rethrow;
    }
  }
  return;
}