import 'package:matrix/matrix.dart'; /// Lokale Nachrichtensuche über die entschlüsselten Events in der Client-DB. /// /// Die server-seitige `/search`-API sieht bei E2EE nur Chiffretext und liefert /// für verschlüsselte Räume nichts – deshalb wird (wie bei Element) die lokale /// Datenbank durchsucht. Aus `search_modal.dart` herausgelöst, damit Tests und /// `benchmark/` genau diesen Codepfad prüfen/messen können. /// /// Bei [mediaOnly] werden nur Medien-Nachrichten geliefert, deren /// Dateiname/Beschriftung passt. Ergebnis: neueste zuerst, höchstens /// [searchTotalCap] Treffer; pro Raum werden höchstens /// [searchPerRoomScanCap] Events gescannt. Future> localMessageSearch( Client client, String query, { bool mediaOnly = false, }) async { final db = client.database; final lower = query.toLowerCase(); const mediaTypes = {'m.image', 'm.video', 'm.file', 'm.audio'}; final results = []; for (final room in client.rooms.where((r) => !r.isSpace)) { var start = 0; var scanned = 0; try { while (scanned < searchPerRoomScanCap) { final events = await db.getEventList( room, start: start, limit: searchScanChunk, ); if (events.isEmpty) break; for (final e in events) { if (e.type != EventTypes.Message || e.redacted) continue; final msgtype = e.content.tryGet('msgtype') ?? ''; if (mediaOnly && !mediaTypes.contains(msgtype)) continue; if (!e.body.toLowerCase().contains(lower)) continue; results.add(e); } start += searchScanChunk; scanned += events.length; final roomExhausted = events.length < searchScanChunk; if (roomExhausted || results.length >= searchTotalCap) break; } } catch (_) {} if (results.length >= searchTotalCap) break; } results.sort((a, b) => b.originServerTs.compareTo(a.originServerTs)); return results.take(searchTotalCap).toList(); } /// Höchstens so viele Events werden pro Raum gescannt. const searchPerRoomScanCap = 1500; /// Höchstens so viele Treffer liefert die Suche insgesamt. const searchTotalCap = 60; /// So viele Events werden pro DB-Abfrage geladen. const searchScanChunk = 500;