chat search now lists local and server-side hits like the file search, and jumping to old messages pages in history with a progress banner and keeps the hit marked

This commit is contained in:
MineTec committed 2026-10-28 22:40:21 +01:00
1 parent fcfd21bb4d
commit b9cb6a8af1
13 files changed
+926 -322

No files matched your search

@@ -180,6 +180,48 @@ class ChatBloc
}
}
/// Pages in older history until [messageId] is held. Returns false when the
/// history ends (or a page brings no progress) before reaching it, or when
/// [isCancelled] or a chat switch aborts the walk. [onProgress] reports the
/// send time of the oldest held message after every page.
Future<bool> loadOlderUntil(
int messageId, {
required bool Function() isCancelled,
ValueChanged<DateTime>? onProgress,
}) async {
final token = innerState?.currentToken ?? '';
while (true) {
final state = innerState;
final response = state?.chatResponse;
if (isClosed || isCancelled() || state == null || response == null) {
return false;
}
if (state.currentToken != token) return false;
if (response.data.any((m) => m.id == messageId)) return true;
final oldestId = _minMessageId(response);
if (oldestId > 0 && oldestId < messageId) return false;
if (!state.hasMoreOld) return false;
await loadOlder();
// A load started by the scroll listener makes loadOlder() a no-op, and
// its result is merged by a queued event — wait until it has landed.
if (innerState?.isLoadingOlder ?? false) {
try {
await stream.firstWhere((s) => !(s.data?.isLoadingOlder ?? false));
} on StateError {
return false; // bloc closed meanwhile
}
}
final after = innerState?.chatResponse;
if (after == null || _minMessageId(after) == oldestId) {
if (innerState?.hasMoreOld ?? false) return false;
} else {
final oldest = after.data.where((m) => m.id == _minMessageId(after));
if (oldest.isNotEmpty) onProgress?.call(oldest.first.sentAt);
}
}
}
Future<void> sendServerReadMarker(String token, int messageId) async {
try {
await SetReadMarker(