startSearch method

Stream<(List<Event>, String?)> startSearch({
  1. String? searchTerm,
  2. int requestHistoryCount = 100,
  3. int maxHistoryRequests = 10,
  4. String? prevBatch,
  5. @Deprecated('Use [prevBatch] instead.') String? sinceEventId,
  6. int? limit,
  7. 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. Returns the List of Events and the next prevBatch at the end of the search.

Implementation

Stream<(List<Event>, String?)> startSearch({
  String? searchTerm,
  int requestHistoryCount = 100,
  int maxHistoryRequests = 10,
  String? prevBatch,
  @Deprecated('Use [prevBatch] instead.') 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), null);
      }
    }

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

  // Search on the server
  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 matrixEvent in resp.chunk) {
        var event = Event.fromMatrixEvent(matrixEvent, 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), resp.end);
          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 MatrixException catch (e) {
      // We have no permission anymore to request the history
      if (e.error == MatrixError.M_FORBIDDEN) {
        break;
      }
      rethrow;
    }
  }
  return;
}