From d0381fddec7a267a88eef30e315c5b06cc220e16 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 25 Feb 2025 11:33:48 -0600 Subject: [PATCH] refactor(mobile): render list (#16303) * refactor(mobile): render list 2 * wip * wip: asset selection page * remove render_list provider * remove dead code * yaml format * remove unused file * woop woop more clean up * woop woop more clean up 2 * fix: album selection doesn't load instantly --- mobile/analysis_options.yaml | 6 ++-- mobile/lib/interfaces/asset.interface.dart | 3 ++ mobile/lib/interfaces/timeline.interface.dart | 8 +++++ .../album/album_asset_selection.page.dart | 9 ++---- mobile/lib/pages/album/album_viewer.dart | 2 -- mobile/lib/pages/album/album_viewer.page.dart | 4 +++ .../lib/pages/common/create_album.page.dart | 2 -- mobile/lib/providers/asset.provider.dart | 20 ------------ .../asset_viewer/render_list.provider.dart | 32 ------------------- .../infrastructure/db.provider.g.dart | 2 +- .../infrastructure/store.provider.g.dart | 2 +- .../search/all_motion_photos.provider.dart | 10 ++---- .../search/paginated_search.provider.dart | 13 ++++---- .../search/paginated_search.provider.g.dart | 7 ++-- .../search/recently_added_asset.provider.dart | 15 ++------- mobile/lib/providers/timeline.provider.dart | 15 +++++++++ mobile/lib/repositories/asset.repository.dart | 19 +++++++++++ .../lib/repositories/timeline.repository.dart | 22 +++++++++++++ mobile/lib/routing/router.dart | 1 - mobile/lib/routing/router.gr.dart | 8 +---- mobile/lib/services/asset.service.dart | 10 ++++++ mobile/lib/services/timeline.service.dart | 24 ++++++++++++++ mobile/lib/utils/renderlist_generator.dart | 26 --------------- .../widgets/asset_grid/immich_asset_grid.dart | 4 +-- mobile/lib/widgets/map/map_asset_grid.dart | 6 ++-- .../test/pages/search/search.page_test.dart | 4 --- 26 files changed, 134 insertions(+), 140 deletions(-) delete mode 100644 mobile/lib/providers/asset_viewer/render_list.provider.dart delete mode 100644 mobile/lib/utils/renderlist_generator.dart diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 5f8b899469..6794f39b81 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -75,12 +75,12 @@ custom_lint: - lib/pages/album/album_asset_selection.page.dart - lib/routing/router.dart - lib/services/immich_logger.service.dart # not really a service... more a util - - lib/utils/{db,migration,renderlist_generator}.dart + - lib/utils/{db,migration}.dart - lib/widgets/asset_grid/asset_grid_data_structure.dart - test/**.dart # refactor the remaining providers - - lib/providers/{asset,authentication,db,user}.provider.dart - - lib/providers/{asset_viewer/render_list,backup/backup,search/all_motion_photos,search/recently_added_asset}.provider.dart + - lib/providers/{db,user}.provider.dart + - lib/providers/backup/backup.provider.dart - import_rule_openapi: message: openapi must only be used through ApiRepositories diff --git a/mobile/lib/interfaces/asset.interface.dart b/mobile/lib/interfaces/asset.interface.dart index 0f1336c1db..83a020f843 100644 --- a/mobile/lib/interfaces/asset.interface.dart +++ b/mobile/lib/interfaces/asset.interface.dart @@ -65,6 +65,9 @@ abstract interface class IAssetRepository implements IDatabaseRepository { Stream watchAsset(int id, {bool fireImmediately = false}); Future> getTrashAssets(int userId); + + Future> getRecentlyAddedAssets(int userId); + Future> getMotionAssets(int userId); } enum AssetSort { checksum, ownerIdChecksum } diff --git a/mobile/lib/interfaces/timeline.interface.dart b/mobile/lib/interfaces/timeline.interface.dart index 178cd4a39c..78b1a22111 100644 --- a/mobile/lib/interfaces/timeline.interface.dart +++ b/mobile/lib/interfaces/timeline.interface.dart @@ -1,4 +1,5 @@ import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; abstract class ITimelineRepository { @@ -16,4 +17,11 @@ abstract class ITimelineRepository { List userIds, GroupAssetsBy groupAssetsBy, ); + + Future getTimelineFromAssets( + List assets, + GroupAssetsBy getGroupByOption, + ); + + Stream watchAssetSelectionTimeline(int userId); } diff --git a/mobile/lib/pages/album/album_asset_selection.page.dart b/mobile/lib/pages/album/album_asset_selection.page.dart index 18ceb3e144..ac7f3dbedc 100644 --- a/mobile/lib/pages/album/album_asset_selection.page.dart +++ b/mobile/lib/pages/album/album_asset_selection.page.dart @@ -6,11 +6,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart'; -import 'package:immich_mobile/providers/asset_viewer/render_list.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:isar/isar.dart'; @RoutePage() class AlbumAssetSelectionPage extends HookConsumerWidget { @@ -18,16 +17,14 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { super.key, required this.existingAssets, this.canDeselect = false, - required this.query, }); final Set existingAssets; - final QueryBuilder? query; final bool canDeselect; @override Widget build(BuildContext context, WidgetRef ref) { - final renderList = ref.watch(renderListQueryProvider(query)); + final assetSelectionRenderList = ref.watch(assetSelectionTimelineProvider); final selected = useState>(existingAssets); final selectionEnabledHook = useState(true); @@ -83,7 +80,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { ), ], ), - body: renderList.widgetWhen( + body: assetSelectionRenderList.widgetWhen( onData: (data) => buildBody(data), ), ); diff --git a/mobile/lib/pages/album/album_viewer.dart b/mobile/lib/pages/album/album_viewer.dart index a15c21659c..75b2c09af3 100644 --- a/mobile/lib/pages/album/album_viewer.dart +++ b/mobile/lib/pages/album/album_viewer.dart @@ -21,7 +21,6 @@ import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/widgets/album/album_viewer_appbar.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -63,7 +62,6 @@ class AlbumViewer extends HookConsumerWidget { AlbumAssetSelectionRoute( existingAssets: album.assets, canDeselect: false, - query: getRemoteAssetQuery(ref), ), ); diff --git a/mobile/lib/pages/album/album_viewer.page.dart b/mobile/lib/pages/album/album_viewer.page.dart index 491bd3bb8d..146a93a0a6 100644 --- a/mobile/lib/pages/album/album_viewer.page.dart +++ b/mobile/lib/pages/album/album_viewer.page.dart @@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/pages/album/album_viewer.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; @RoutePage() class AlbumViewerPage extends HookConsumerWidget { @@ -16,6 +17,9 @@ class AlbumViewerPage extends HookConsumerWidget { // Listen provider to prevent autoDispose when navigating to other routes from within the viewer page ref.listen(currentAlbumProvider, (_, __) {}); + // This call helps rendering the asset selection instantly + ref.listen(assetSelectionTimelineProvider, (_, __) {}); + ref.listen(albumWatcher(albumId), (_, albumFuture) { albumFuture.whenData( (value) => ref.read(currentAlbumProvider.notifier).set(value), diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index 55261f6d55..86bf47c1a7 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -8,7 +8,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/album_title.provider.dart'; -import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/album/album_action_filled_button.dart'; import 'package:immich_mobile/widgets/album/album_title_text_field.dart'; @@ -54,7 +53,6 @@ class CreateAlbumPage extends HookConsumerWidget { AlbumAssetSelectionRoute( existingAssets: selectedAssets.value, canDeselect: true, - query: getRemoteAssetQuery(ref), ), ); if (selectedAsset == null) { diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index 43020e7687..8d5209ccb7 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -3,16 +3,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/memory.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/asset.service.dart'; import 'package:immich_mobile/services/etag.service.dart'; import 'package:immich_mobile/services/exif.service.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:immich_mobile/services/user.service.dart'; -import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; final assetProvider = StateNotifierProvider((ref) { @@ -184,20 +181,3 @@ final assetWatcher = final assetService = ref.watch(assetServiceProvider); return assetService.watchAsset(asset.id, fireImmediately: true); }); - -QueryBuilder? getRemoteAssetQuery(WidgetRef ref) { - final userId = ref.watch(currentUserProvider)?.isarId; - if (userId == null) { - return null; - } - return ref - .watch(dbProvider) - .assets - .where() - .remoteIdIsNotNull() - .filter() - .ownerIdEqualTo(userId) - .isTrashedEqualTo(false) - .stackPrimaryAssetIdIsNull() - .sortByFileCreatedAtDesc(); -} diff --git a/mobile/lib/providers/asset_viewer/render_list.provider.dart b/mobile/lib/providers/asset_viewer/render_list.provider.dart deleted file mode 100644 index a0b3bba210..0000000000 --- a/mobile/lib/providers/asset_viewer/render_list.provider.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/utils/renderlist_generator.dart'; -import 'package:isar/isar.dart'; - -final renderListProvider = - FutureProvider.family>((ref, assets) { - final settings = ref.watch(appSettingsServiceProvider); - - return RenderList.fromAssets( - assets, - GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)], - ); -}); - -final renderListProviderWithGrouping = - FutureProvider.family, GroupAssetsBy?)>( - (ref, args) { - final settings = ref.watch(appSettingsServiceProvider); - final grouping = args.$2 ?? - GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; - return RenderList.fromAssets(args.$1, grouping); -}); - -final renderListQueryProvider = StreamProvider.family?>( - (ref, query) => - query == null ? const Stream.empty() : renderListGenerator(query, ref), -); diff --git a/mobile/lib/providers/infrastructure/db.provider.g.dart b/mobile/lib/providers/infrastructure/db.provider.g.dart index a6122394ea..1bfe7b0ad5 100644 --- a/mobile/lib/providers/infrastructure/db.provider.g.dart +++ b/mobile/lib/providers/infrastructure/db.provider.g.dart @@ -6,7 +6,7 @@ part of 'db.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$isarHash() => r'69d3a06aa7e69a4381478e03f7956eb07d7f7feb'; +String _$isarHash() => r'f0e886fa20e56dd1dc0082fcc723016289bd03cb'; /// See also [isar]. @ProviderFor(isar) diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart index 69523af272..f53d677384 100644 --- a/mobile/lib/providers/infrastructure/store.provider.g.dart +++ b/mobile/lib/providers/infrastructure/store.provider.g.dart @@ -6,7 +6,7 @@ part of 'store.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$storeRepositoryHash() => r'9f378b96e552151fa14a8c8ce2c30a5f38f436ed'; +String _$storeRepositoryHash() => r'2f1c3e2e2db5082a40eb30a183a6c770f5b09d76'; /// See also [storeRepository]. @ProviderFor(storeRepository) diff --git a/mobile/lib/providers/search/all_motion_photos.provider.dart b/mobile/lib/providers/search/all_motion_photos.provider.dart index 1740613e58..48bc1bb80c 100644 --- a/mobile/lib/providers/search/all_motion_photos.provider.dart +++ b/mobile/lib/providers/search/all_motion_photos.provider.dart @@ -1,13 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; -import 'package:isar/isar.dart'; +import 'package:immich_mobile/services/asset.service.dart'; final allMotionPhotosProvider = FutureProvider>((ref) async { - return ref - .watch(dbProvider) - .assets - .filter() - .livePhotoVideoIdIsNotNull() - .findAll(); + return ref.watch(assetServiceProvider).getMotionAssets(); }); diff --git a/mobile/lib/providers/search/paginated_search.provider.dart b/mobile/lib/providers/search/paginated_search.provider.dart index 60264947b2..990bd3f74a 100644 --- a/mobile/lib/providers/search/paginated_search.provider.dart +++ b/mobile/lib/providers/search/paginated_search.provider.dart @@ -1,6 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/search/search_result.model.dart'; -import 'package:immich_mobile/providers/asset_viewer/render_list.provider.dart'; +import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/services/search.service.dart'; @@ -44,14 +44,13 @@ class PaginatedSearchNotifier extends StateNotifier { } @riverpod -AsyncValue paginatedSearchRenderList( +Future paginatedSearchRenderList( PaginatedSearchRenderListRef ref, ) { final result = ref.watch(paginatedSearchProvider); - - return ref.watch( - renderListProviderWithGrouping( - (result.assets, GroupAssetsBy.none), - ), + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.getTimelineFromAssets( + result.assets, + GroupAssetsBy.none, ); } diff --git a/mobile/lib/providers/search/paginated_search.provider.g.dart b/mobile/lib/providers/search/paginated_search.provider.g.dart index cdf8cdd741..5682795ea1 100644 --- a/mobile/lib/providers/search/paginated_search.provider.g.dart +++ b/mobile/lib/providers/search/paginated_search.provider.g.dart @@ -7,12 +7,12 @@ part of 'paginated_search.provider.dart'; // ************************************************************************** String _$paginatedSearchRenderListHash() => - r'4585c832106b16b6d294055f47bbbe83e0802846'; + r'9efb98fd73d4e57e1ccd97a902cd459e3f18f749'; /// See also [paginatedSearchRenderList]. @ProviderFor(paginatedSearchRenderList) final paginatedSearchRenderListProvider = - AutoDisposeProvider>.internal( + AutoDisposeFutureProvider.internal( paginatedSearchRenderList, name: r'paginatedSearchRenderListProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,7 +22,6 @@ final paginatedSearchRenderListProvider = allTransitiveDependencies: null, ); -typedef PaginatedSearchRenderListRef - = AutoDisposeProviderRef>; +typedef PaginatedSearchRenderListRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/mobile/lib/providers/search/recently_added_asset.provider.dart b/mobile/lib/providers/search/recently_added_asset.provider.dart index bf728ba095..c4819d9d44 100644 --- a/mobile/lib/providers/search/recently_added_asset.provider.dart +++ b/mobile/lib/providers/search/recently_added_asset.provider.dart @@ -1,18 +1,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; -import 'package:isar/isar.dart'; +import 'package:immich_mobile/services/asset.service.dart'; final recentlyAddedAssetProvider = FutureProvider>((ref) async { - final user = ref.read(currentUserProvider); - if (user == null) return []; + final assetService = ref.read(assetServiceProvider); - return ref - .watch(dbProvider) - .assets - .where() - .ownerIdEqualToAnyChecksum(user.isarId) - .sortByFileCreatedAtDesc() - .findAll(); + return assetService.getRecentlyAddedAssets(); }); diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart index 8937ec0da8..b0e9482b81 100644 --- a/mobile/lib/providers/timeline.provider.dart +++ b/mobile/lib/providers/timeline.provider.dart @@ -1,4 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/services/timeline.service.dart'; @@ -53,3 +54,17 @@ final allVideosTimelineProvider = StreamProvider((ref) { final timelineService = ref.watch(timelineServiceProvider); return timelineService.watchAllVideosTimeline(); }); + +final assetSelectionTimelineProvider = StreamProvider((ref) { + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchAssetSelectionTimeline(); +}); + +final assetsTimelineProvider = + FutureProvider.family>((ref, assets) { + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.getTimelineFromAssets( + assets, + null, + ); +}); diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index 1f419fcb76..ec7fe77dea 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -233,6 +233,25 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { .isTrashedEqualTo(true) .findAll(); } + + @override + Future> getRecentlyAddedAssets(int userId) { + return db.assets + .where() + .ownerIdEqualToAnyChecksum(userId) + .sortByFileCreatedAtDesc() + .findAll(); + } + + @override + Future> getMotionAssets(int userId) { + return db.assets + .where() + .ownerIdEqualToAnyChecksum(userId) + .filter() + .livePhotoVideoIdIsNotNull() + .findAll(); + } } Future> _getMatchesImpl( diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index 7b3f169750..d1abaaf3a5 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -111,6 +111,28 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, groupAssetByOption); } + @override + Future getTimelineFromAssets( + List assets, + GroupAssetsBy getGroupByOption, + ) { + return RenderList.fromAssets(assets, getGroupByOption); + } + + @override + Stream watchAssetSelectionTimeline(int userId) { + final query = db.assets + .where() + .remoteIdIsNotNull() + .filter() + .ownerIdEqualTo(userId) + .isTrashedEqualTo(false) + .stackPrimaryAssetIdIsNull() + .sortByFileCreatedAtDesc(); + + return _watchRenderList(query, GroupAssetsBy.none); + } + Stream _watchRenderList( QueryBuilder query, GroupAssetsBy groupAssetsBy, diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 3078a0dc1a..66a65f559e 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -67,7 +67,6 @@ import 'package:immich_mobile/routing/custom_transition_builders.dart'; import 'package:immich_mobile/routing/duplicate_guard.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -import 'package:isar/isar.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; part 'router.gr.dart'; diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 1940ca26db..e4f1190510 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -83,7 +83,6 @@ class AlbumAssetSelectionRoute Key? key, required Set existingAssets, bool canDeselect = false, - required QueryBuilder? query, List? children, }) : super( AlbumAssetSelectionRoute.name, @@ -91,7 +90,6 @@ class AlbumAssetSelectionRoute key: key, existingAssets: existingAssets, canDeselect: canDeselect, - query: query, ), initialChildren: children, ); @@ -106,7 +104,6 @@ class AlbumAssetSelectionRoute key: args.key, existingAssets: args.existingAssets, canDeselect: args.canDeselect, - query: args.query, ); }, ); @@ -117,7 +114,6 @@ class AlbumAssetSelectionRouteArgs { this.key, required this.existingAssets, this.canDeselect = false, - required this.query, }); final Key? key; @@ -126,11 +122,9 @@ class AlbumAssetSelectionRouteArgs { final bool canDeselect; - final QueryBuilder? query; - @override String toString() { - return 'AlbumAssetSelectionRouteArgs{key: $key, existingAssets: $existingAssets, canDeselect: $canDeselect, query: $query}'; + return 'AlbumAssetSelectionRouteArgs{key: $key, existingAssets: $existingAssets, canDeselect: $canDeselect}'; } } diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index 12552effcb..b4a2c097b7 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -514,4 +514,14 @@ class AssetService { Stream watchAsset(int id, {bool fireImmediately = false}) { return _assetRepository.watchAsset(id, fireImmediately: fireImmediately); } + + Future> getRecentlyAddedAssets() async { + final me = await _userRepository.me(); + return _assetRepository.getRecentlyAddedAssets(me.isarId); + } + + Future> getMotionAssets() async { + final me = await _userRepository.me(); + return _assetRepository.getMotionAssets(me.isarId); + } } diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index 513b1b1ab2..a89377548f 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -1,5 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/interfaces/timeline.interface.dart'; import 'package:immich_mobile/interfaces/user.interface.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; @@ -66,6 +67,29 @@ class TimelineService { return _timelineRepository.watchAllVideosTimeline(); } + Future getTimelineFromAssets( + List assets, + GroupAssetsBy? groupBy, + ) { + GroupAssetsBy groupOption = GroupAssetsBy.none; + if (groupBy != null) { + groupOption = groupBy; + } else { + groupOption = _getGroupByOption(); + } + + return _timelineRepository.getTimelineFromAssets( + assets, + groupOption, + ); + } + + Stream watchAssetSelectionTimeline() async* { + final user = await _userRepository.me(); + + yield* _timelineRepository.watchAssetSelectionTimeline(user.isarId); + } + GroupAssetsBy _getGroupByOption() { return GroupAssetsBy .values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)]; diff --git a/mobile/lib/utils/renderlist_generator.dart b/mobile/lib/utils/renderlist_generator.dart deleted file mode 100644 index a601ef068d..0000000000 --- a/mobile/lib/utils/renderlist_generator.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:isar/isar.dart'; - -Stream renderListGenerator( - QueryBuilder query, - StreamProviderRef ref, -) { - final settings = ref.watch(appSettingsServiceProvider); - final groupBy = - GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; - return renderListGeneratorWithGroupBy(query, groupBy); -} - -Stream renderListGeneratorWithGroupBy( - QueryBuilder query, - GroupAssetsBy groupBy, -) async* { - yield await RenderList.fromQuery(query, groupBy); - await for (final _ in query.watchLazy()) { - yield await RenderList.fromQuery(query, groupBy); - } -} diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart index 17872852e5..5c1aa6ef69 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; -import 'package:immich_mobile/providers/asset_viewer/render_list.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid_view.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; @@ -125,7 +125,7 @@ class ImmichAssetGrid extends HookConsumerWidget { if (renderList != null) return buildAssetGridView(renderList!); - final renderListFuture = ref.watch(renderListProvider(assets!)); + final renderListFuture = ref.watch(assetsTimelineProvider(assets!)); return renderListFuture.widgetWhen( onData: (renderList) => buildAssetGridView(renderList), ); diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index 7205feefa4..18003cf293 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -6,7 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; -import 'package:immich_mobile/providers/asset_viewer/render_list.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/models/map/map_event.model.dart'; @@ -126,7 +126,9 @@ class MapAssetGrid extends HookConsumerWidget { // Place it just below the drag handle heightFactor: 0.80, child: assetsInBounds.value.isNotEmpty - ? ref.watch(renderListProvider(assetsInBounds.value)).when( + ? ref + .watch(assetsTimelineProvider(assetsInBounds.value)) + .when( data: (renderList) { // Cache render list here to use it back during visibleItemsListener cachedRenderList.value = renderList; diff --git a/mobile/test/pages/search/search.page_test.dart b/mobile/test/pages/search/search.page_test.dart index de19acc5a3..fa7f037da5 100644 --- a/mobile/test/pages/search/search.page_test.dart +++ b/mobile/test/pages/search/search.page_test.dart @@ -11,8 +11,6 @@ import 'package:immich_mobile/pages/search/search.page.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -import 'package:immich_mobile/providers/search/paginated_search.provider.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:isar/isar.dart'; import 'package:mocktail/mocktail.dart'; import 'package:openapi/api.dart'; @@ -38,8 +36,6 @@ void main() { registerFallbackValue(MockSmartSearchDto()); registerFallbackValue(MockMetadataSearchDto()); overrides = [ - paginatedSearchRenderListProvider - .overrideWithValue(AsyncValue.data(RenderList.empty())), dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db), apiServiceProvider.overrideWithValue(mockApiService),