fix(mobile): search results

This commit is contained in:
Thomas Way
2026-02-24 21:32:10 +00:00
parent dd97395f3a
commit 532567ab2a
5 changed files with 91 additions and 24 deletions
@@ -1,6 +1,11 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/search_result.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/services/search.service.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:immich_mobile/providers/infrastructure/search.provider.dart';
@@ -58,3 +63,49 @@ class PaginatedSearchNotifier extends StateNotifier<SearchResult> {
state = const SearchResult(assets: [], nextPage: 1, scrollOffset: 0.0);
}
}
/// A [TimelineService] for search results that stays alive across pagination.
/// Instead of recreating the service on each page load, it uses a long-lived
/// bucket stream that emits new buckets whenever the search assets change.
final paginatedSearchTimelineProvider = Provider.autoDispose<TimelineService>((ref) {
final controller = StreamController<List<Bucket>>.broadcast();
List<Bucket> generateBuckets(int count) {
if (count == 0) return [];
final buckets = List.filled(
(count / kTimelineNoneSegmentSize).ceil(),
const Bucket(assetCount: kTimelineNoneSegmentSize),
);
if (count % kTimelineNoneSegmentSize != 0) {
buckets[buckets.length - 1] = Bucket(assetCount: count % kTimelineNoneSegmentSize);
}
return buckets;
}
// Emit new buckets whenever asset count changes
ref.listen(paginatedSearchProvider.select((s) => s.assets.length), (_, count) {
controller.add(generateBuckets(count));
});
// Each subscriber gets the current bucket state then follows updates
Stream<List<Bucket>> bucketSource() async* {
yield generateBuckets(ref.read(paginatedSearchProvider).assets.length);
yield* controller.stream;
}
final service = TimelineService((
bucketSource: bucketSource,
assetSource: (offset, count) {
final assets = ref.read(paginatedSearchProvider).assets;
return Future.value(assets.skip(offset).take(count).toList(growable: false));
},
origin: TimelineOrigin.search,
));
ref.onDispose(() {
controller.close();
service.dispose();
});
return service;
});