diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 8ae9edef0d..52d0d11162 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -65,7 +65,7 @@ custom_lint: allowed: # required / wanted - lib/entities/*.entity.dart - - lib/repositories/{album,asset,backup,database,etag,exif_info,user}.repository.dart + - lib/repositories/{album,asset,backup,database,etag,exif_info,user,timeline}.repository.dart - lib/infrastructure/entities/*.entity.dart - lib/infrastructure/repositories/{store,db}.repository.dart - lib/providers/infrastructure/db.provider.dart @@ -79,7 +79,7 @@ custom_lint: - lib/widgets/asset_grid/asset_grid_data_structure.dart - test/**.dart # refactor the remaining providers - - lib/providers/{archive,asset,authentication,db,favorite,partner,user}.provider.dart + - lib/providers/{asset,authentication,db,partner,user}.provider.dart - lib/providers/{asset_viewer/render_list,backup/backup,search/all_motion_photos,search/recently_added_asset}.provider.dart - import_rule_openapi: diff --git a/mobile/lib/interfaces/album.interface.dart b/mobile/lib/interfaces/album.interface.dart index 2d8c460b67..3a83a8feb7 100644 --- a/mobile/lib/interfaces/album.interface.dart +++ b/mobile/lib/interfaces/album.interface.dart @@ -3,7 +3,6 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/interfaces/database.interface.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; abstract interface class IAlbumRepository implements IDatabaseRepository { Future create(Album album); @@ -50,8 +49,6 @@ abstract interface class IAlbumRepository implements IDatabaseRepository { Stream watchAlbum(int id); - Stream getRenderListStream(Album album); - Future clearTable(); } diff --git a/mobile/lib/interfaces/asset.interface.dart b/mobile/lib/interfaces/asset.interface.dart index b56c99a711..0f1336c1db 100644 --- a/mobile/lib/interfaces/asset.interface.dart +++ b/mobile/lib/interfaces/asset.interface.dart @@ -2,7 +2,6 @@ import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/device_asset.entity.dart'; import 'package:immich_mobile/interfaces/database.interface.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; abstract interface class IAssetRepository implements IDatabaseRepository { Future getByRemoteId(String id); @@ -66,8 +65,6 @@ abstract interface class IAssetRepository implements IDatabaseRepository { Stream watchAsset(int id, {bool fireImmediately = false}); Future> getTrashAssets(int userId); - - Stream getTrashRenderListStream(int userId); } enum AssetSort { checksum, ownerIdChecksum } diff --git a/mobile/lib/interfaces/timeline.interface.dart b/mobile/lib/interfaces/timeline.interface.dart new file mode 100644 index 0000000000..2d46dbf688 --- /dev/null +++ b/mobile/lib/interfaces/timeline.interface.dart @@ -0,0 +1,16 @@ +import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; + +abstract class ITimelineRepository { + Stream watchArchiveTimeline(int userId); + Stream watchFavoriteTimeline(int userId); + Stream watchTrashTimeline(int userId); + Stream watchAlbumTimeline(Album album); + Stream watchAllVideosTimeline(); + + Stream watchHomeTimeline(int userId, GroupAssetsBy groupAssetsBy); + Stream watchMultiUsersTimeline( + List userIds, + GroupAssetsBy groupAssetsBy, + ); +} diff --git a/mobile/lib/pages/album/album_viewer.dart b/mobile/lib/pages/album/album_viewer.dart index 19782c4e30..a15c21659c 100644 --- a/mobile/lib/pages/album/album_viewer.dart +++ b/mobile/lib/pages/album/album_viewer.dart @@ -14,6 +14,7 @@ import 'package:immich_mobile/pages/album/album_shared_user_icons.dart'; import 'package:immich_mobile/pages/album/album_title.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'; import 'package:immich_mobile/utils/immich_loading_overlay.dart'; import 'package:immich_mobile/providers/multiselect.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; @@ -104,7 +105,7 @@ class AlbumViewer extends HookConsumerWidget { children: [ MultiselectGrid( key: const ValueKey("albumViewerMultiselectGrid"), - renderListProvider: albumRenderlistProvider(album.id), + renderListProvider: albumTimelineProvider(album.id), topWidget: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/mobile/lib/pages/library/archive.page.dart b/mobile/lib/pages/library/archive.page.dart index 0082142113..a13adc21f2 100644 --- a/mobile/lib/pages/library/archive.page.dart +++ b/mobile/lib/pages/library/archive.page.dart @@ -2,8 +2,8 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/providers/archive.provider.dart'; import 'package:immich_mobile/providers/multiselect.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; @RoutePage() @@ -13,8 +13,8 @@ class ArchivePage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { AppBar buildAppBar() { - final archivedAssets = ref.watch(archiveProvider); - final count = archivedAssets.value?.totalAssets.toString() ?? "?"; + final archiveRenderList = ref.watch(archiveTimelineProvider); + final count = archiveRenderList.value?.totalAssets.toString() ?? "?"; return AppBar( leading: IconButton( onPressed: () => context.maybePop(), @@ -31,7 +31,7 @@ class ArchivePage extends HookConsumerWidget { return Scaffold( appBar: ref.watch(multiselectProvider) ? null : buildAppBar(), body: MultiselectGrid( - renderListProvider: archiveProvider, + renderListProvider: archiveTimelineProvider, unarchive: true, archiveEnabled: true, deleteEnabled: true, diff --git a/mobile/lib/pages/library/favorite.page.dart b/mobile/lib/pages/library/favorite.page.dart index cc422f88c7..55e3937166 100644 --- a/mobile/lib/pages/library/favorite.page.dart +++ b/mobile/lib/pages/library/favorite.page.dart @@ -2,8 +2,8 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/providers/favorite.provider.dart'; import 'package:immich_mobile/providers/multiselect.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; @RoutePage() @@ -29,7 +29,7 @@ class FavoritesPage extends HookConsumerWidget { return Scaffold( appBar: ref.watch(multiselectProvider) ? null : buildAppBar(), body: MultiselectGrid( - renderListProvider: favoriteAssetsProvider, + renderListProvider: favoriteTimelineProvider, favoriteEnabled: true, editEnabled: true, unfavorite: true, diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart index 0874aacfa7..f018726fe2 100644 --- a/mobile/lib/pages/library/partner/partner_detail.page.dart +++ b/mobile/lib/pages/library/partner/partner_detail.page.dart @@ -7,6 +7,7 @@ import 'package:immich_mobile/providers/multiselect.provider.dart'; import 'package:immich_mobile/providers/partner.provider.dart'; import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -110,7 +111,7 @@ class PartnerDetailPage extends HookConsumerWidget { ), ), ), - renderListProvider: assetsProvider(partner.isarId), + renderListProvider: singleUserTimelineProvider(partner.isarId), onRefresh: () => ref.read(assetProvider.notifier).getAllAsset(), deleteEnabled: false, favoriteEnabled: false, diff --git a/mobile/lib/pages/library/trash.page.dart b/mobile/lib/pages/library/trash.page.dart index 7322b00579..8a969c8e9a 100644 --- a/mobile/lib/pages/library/trash.page.dart +++ b/mobile/lib/pages/library/trash.page.dart @@ -7,6 +7,7 @@ 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/providers/asset.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; import 'package:immich_mobile/providers/trash.provider.dart'; @@ -22,7 +23,7 @@ class TrashPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final trashedAssets = ref.watch(trashedAssetsProvider); + final trashRenderList = ref.watch(trashTimelineProvider); final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); final selectionEnabledHook = useState(false); @@ -234,11 +235,11 @@ class TrashPage extends HookConsumerWidget { } return Scaffold( - appBar: trashedAssets.maybeWhen( + appBar: trashRenderList.maybeWhen( orElse: () => buildAppBar("?"), data: (data) => buildAppBar(data.totalAssets.toString()), ), - body: trashedAssets.widgetWhen( + body: trashRenderList.widgetWhen( onData: (data) => data.isEmpty ? Center( child: Text('trash_page_no_assets'.tr()), diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index 845de40ee7..7910d45e13 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/multiselect.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/memories/memory_lane.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; @@ -108,8 +109,8 @@ class PhotosPage extends HookConsumerWidget { ? const MemoryLane() : const SizedBox(), renderListProvider: timelineUsers.length > 1 - ? multiUserAssetsProvider(timelineUsers) - : assetsProvider(currentUser?.isarId), + ? multiUsersTimelineProvider(timelineUsers) + : singleUserTimelineProvider(currentUser!.isarId), buildLoadingIndicator: buildLoadingIndicator, onRefresh: refreshAssets, stackEnabled: true, diff --git a/mobile/lib/pages/search/all_videos.page.dart b/mobile/lib/pages/search/all_videos.page.dart index e96e060255..b7997313f3 100644 --- a/mobile/lib/pages/search/all_videos.page.dart +++ b/mobile/lib/pages/search/all_videos.page.dart @@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/providers/search/all_video_assets.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; @RoutePage() @@ -19,7 +19,7 @@ class AllVideosPage extends HookConsumerWidget { icon: const Icon(Icons.arrow_back_ios_rounded), ), ), - body: MultiselectGrid(renderListProvider: allVideoAssetsProvider), + body: MultiselectGrid(renderListProvider: allVideosTimelineProvider), ); } } diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart index d9dd5bcc96..a2d7db68ec 100644 --- a/mobile/lib/providers/album/album.provider.dart +++ b/mobile/lib/providers/album/album.provider.dart @@ -5,7 +5,6 @@ import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/services/album.service.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -152,17 +151,6 @@ final albumWatcher = } }); -final albumRenderlistProvider = - StreamProvider.autoDispose.family((ref, id) { - final album = ref.watch(albumWatcher(id)).value; - - if (album != null) { - return ref.watch(albumServiceProvider).getRenderListGenerator(album); - } - - return const Stream.empty(); -}); - class LocalAlbumsNotifier extends StateNotifier> { LocalAlbumsNotifier(this.albumService) : super([]) { albumService.getAllLocalAlbums().then((value) { diff --git a/mobile/lib/providers/archive.provider.dart b/mobile/lib/providers/archive.provider.dart deleted file mode 100644 index ba4937bd82..0000000000 --- a/mobile/lib/providers/archive.provider.dart +++ /dev/null @@ -1,22 +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/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; -import 'package:immich_mobile/utils/renderlist_generator.dart'; -import 'package:isar/isar.dart'; - -final archiveProvider = StreamProvider((ref) { - final user = ref.watch(currentUserProvider); - if (user == null) return const Stream.empty(); - final query = ref - .watch(dbProvider) - .assets - .where() - .ownerIdEqualToAnyChecksum(user.isarId) - .filter() - .isArchivedEqualTo(true) - .isTrashedEqualTo(false) - .sortByFileCreatedAtDesc(); - return renderListGenerator(query, ref); -}); diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index 1c12eda6b7..43020e7687 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -4,7 +4,6 @@ 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/locale_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'; @@ -13,8 +12,6 @@ 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:immich_mobile/utils/renderlist_generator.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; @@ -188,47 +185,6 @@ final assetWatcher = return assetService.watchAsset(asset.id, fireImmediately: true); }); -final assetsProvider = StreamProvider.family( - (ref, userId) { - if (userId == null) return const Stream.empty(); - ref.watch(localeProvider); - - final query = ref - .watch(dbProvider) - .assets - .where() - .ownerIdEqualToAnyChecksum(userId) - .filter() - .isArchivedEqualTo(false) - .isTrashedEqualTo(false) - .stackPrimaryAssetIdIsNull() - .sortByFileCreatedAtDesc(); - - return renderListGenerator(query, ref); - }, - dependencies: [localeProvider], -); - -final multiUserAssetsProvider = StreamProvider.family>( - (ref, userIds) { - if (userIds.isEmpty) return const Stream.empty(); - ref.watch(localeProvider); - final query = ref - .watch(dbProvider) - .assets - .where() - .anyOf(userIds, (q, u) => q.ownerIdEqualToAnyChecksum(u)) - .filter() - .isArchivedEqualTo(false) - .isTrashedEqualTo(false) - .stackPrimaryAssetIdIsNull() - .sortByFileCreatedAtDesc(); - - return renderListGenerator(query, ref); - }, - dependencies: [localeProvider], -); - QueryBuilder? getRemoteAssetQuery(WidgetRef ref) { final userId = ref.watch(currentUserProvider)?.isarId; if (userId == null) { diff --git a/mobile/lib/providers/favorite.provider.dart b/mobile/lib/providers/favorite.provider.dart deleted file mode 100644 index 340fd01080..0000000000 --- a/mobile/lib/providers/favorite.provider.dart +++ /dev/null @@ -1,22 +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/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; -import 'package:immich_mobile/utils/renderlist_generator.dart'; -import 'package:isar/isar.dart'; - -final favoriteAssetsProvider = StreamProvider((ref) { - final user = ref.watch(currentUserProvider); - if (user == null) return const Stream.empty(); - final query = ref - .watch(dbProvider) - .assets - .where() - .ownerIdEqualToAnyChecksum(user.isarId) - .filter() - .isFavoriteEqualTo(true) - .isTrashedEqualTo(false) - .sortByFileCreatedAtDesc(); - return renderListGenerator(query, ref); -}); diff --git a/mobile/lib/providers/search/all_video_assets.provider.dart b/mobile/lib/providers/search/all_video_assets.provider.dart deleted file mode 100644 index b0daf6b984..0000000000 --- a/mobile/lib/providers/search/all_video_assets.provider.dart +++ /dev/null @@ -1,17 +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/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/db.provider.dart'; -import 'package:immich_mobile/utils/renderlist_generator.dart'; - -final allVideoAssetsProvider = StreamProvider((ref) { - final query = ref - .watch(dbProvider) - .assets - .filter() - .isArchivedEqualTo(false) - .isTrashedEqualTo(false) - .typeEqualTo(AssetType.video) - .sortByFileCreatedAtDesc(); - return renderListGenerator(query, ref); -}); diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart new file mode 100644 index 0000000000..8937ec0da8 --- /dev/null +++ b/mobile/lib/providers/timeline.provider.dart @@ -0,0 +1,55 @@ +import 'package:hooks_riverpod/hooks_riverpod.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'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; + +final singleUserTimelineProvider = StreamProvider.family( + (ref, userId) { + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchHomeTimeline(userId); + }, + dependencies: [localeProvider], +); + +final multiUsersTimelineProvider = StreamProvider.family>( + (ref, userIds) { + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchMultiUsersTimeline(userIds); + }, + dependencies: [localeProvider], +); + +final albumTimelineProvider = + StreamProvider.autoDispose.family((ref, id) { + final album = ref.watch(albumWatcher(id)).value; + final timelineService = ref.watch(timelineServiceProvider); + + if (album != null) { + return timelineService.watchAlbumTimeline(album); + } + + return const Stream.empty(); +}); + +final archiveTimelineProvider = StreamProvider((ref) { + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchArchiveTimeline(); +}); + +final favoriteTimelineProvider = StreamProvider((ref) { + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchFavoriteTimeline(); +}); + +final trashTimelineProvider = StreamProvider((ref) { + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchTrashTimeline(); +}); + +final allVideosTimelineProvider = StreamProvider((ref) { + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchAllVideosTimeline(); +}); diff --git a/mobile/lib/providers/trash.provider.dart b/mobile/lib/providers/trash.provider.dart index 0f7ae780a0..c78cccff8a 100644 --- a/mobile/lib/providers/trash.provider.dart +++ b/mobile/lib/providers/trash.provider.dart @@ -1,8 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/services/trash.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; import 'package:logging/logging.dart'; class TrashNotifier extends StateNotifier { @@ -49,12 +47,3 @@ final trashProvider = StateNotifierProvider((ref) { ref.watch(trashServiceProvider), ); }); - -final trashedAssetsProvider = StreamProvider((ref) { - final user = ref.read(currentUserProvider); - if (user == null) { - return const Stream.empty(); - } - - return ref.watch(trashServiceProvider).getRenderListGenerator(user.isarId); -}); diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart index 0f1cf64865..1d2df89579 100644 --- a/mobile/lib/repositories/album.repository.dart +++ b/mobile/lib/repositories/album.repository.dart @@ -1,5 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -9,7 +8,6 @@ import 'package:immich_mobile/interfaces/album.interface.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:isar/isar.dart'; final albumRepositoryProvider = @@ -177,22 +175,4 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository { Stream watchAlbum(int id) { return db.albums.watchObject(id, fireImmediately: true); } - - @override - Stream getRenderListStream(Album album) async* { - final query = album.assets.filter().isTrashedEqualTo(false); - final withSortedOption = switch (album.sortOrder) { - SortOrder.asc => query.sortByFileCreatedAt(), - SortOrder.desc => query.sortByFileCreatedAtDesc(), - }; - - yield await RenderList.fromQuery( - withSortedOption, - GroupAssetsBy.none, - ); - - await for (final _ in query.watchLazy()) { - yield await RenderList.fromQuery(withSortedOption, GroupAssetsBy.none); - } - } } diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index 55add8cf96..1f419fcb76 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -11,7 +11,6 @@ import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:isar/isar.dart'; final assetRepositoryProvider = @@ -234,20 +233,6 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { .isTrashedEqualTo(true) .findAll(); } - - @override - Stream getTrashRenderListStream(int userId) async* { - final query = db.assets - .filter() - .ownerIdEqualTo(userId) - .isTrashedEqualTo(true) - .sortByFileCreatedAtDesc(); - - yield await RenderList.fromQuery(query, GroupAssetsBy.none); - await for (final _ in query.watchLazy()) { - yield await RenderList.fromQuery(query, GroupAssetsBy.none); - } - } } Future> _getMatchesImpl( diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart new file mode 100644 index 0000000000..8203e5c6e9 --- /dev/null +++ b/mobile/lib/repositories/timeline.repository.dart @@ -0,0 +1,120 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.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/providers/db.provider.dart'; +import 'package:immich_mobile/repositories/database.repository.dart'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; +import 'package:isar/isar.dart'; + +final timelineRepositoryProvider = + Provider((ref) => TimelineRepository(ref.watch(dbProvider))); + +class TimelineRepository extends DatabaseRepository + implements ITimelineRepository { + TimelineRepository(super.db); + + @override + Stream watchArchiveTimeline(int userId) { + final query = db.assets + .where() + .ownerIdEqualToAnyChecksum(userId) + .filter() + .isArchivedEqualTo(true) + .isTrashedEqualTo(false) + .sortByFileCreatedAtDesc(); + + return _watchRenderList(query, GroupAssetsBy.none); + } + + @override + Stream watchFavoriteTimeline(int userId) { + final query = db.assets + .where() + .ownerIdEqualToAnyChecksum(userId) + .filter() + .isFavoriteEqualTo(true) + .isTrashedEqualTo(false) + .sortByFileCreatedAtDesc(); + + return _watchRenderList(query, GroupAssetsBy.none); + } + + @override + Stream watchAlbumTimeline(Album album) { + final query = album.assets.filter().isTrashedEqualTo(false); + final withSortedOption = switch (album.sortOrder) { + SortOrder.asc => query.sortByFileCreatedAt(), + SortOrder.desc => query.sortByFileCreatedAtDesc(), + }; + + return _watchRenderList(withSortedOption, GroupAssetsBy.none); + } + + @override + Stream watchTrashTimeline(int userId) { + final query = db.assets + .filter() + .ownerIdEqualTo(userId) + .isTrashedEqualTo(true) + .sortByFileCreatedAtDesc(); + + return _watchRenderList(query, GroupAssetsBy.none); + } + + @override + Stream watchAllVideosTimeline() { + final query = db.assets + .filter() + .isArchivedEqualTo(false) + .isTrashedEqualTo(false) + .typeEqualTo(AssetType.video) + .sortByFileCreatedAtDesc(); + + return _watchRenderList(query, GroupAssetsBy.none); + } + + @override + Stream watchHomeTimeline( + int userId, + GroupAssetsBy groupAssetByOption, + ) { + final query = db.assets + .where() + .ownerIdEqualToAnyChecksum(userId) + .filter() + .isArchivedEqualTo(false) + .isTrashedEqualTo(false) + .stackPrimaryAssetIdIsNull() + .sortByFileCreatedAtDesc(); + + return _watchRenderList(query, groupAssetByOption); + } + + @override + Stream watchMultiUsersTimeline( + List userIds, + GroupAssetsBy groupAssetByOption, + ) { + final query = db.assets + .where() + .anyOf(userIds, (qb, userId) => qb.ownerIdEqualToAnyChecksum(userId)) + .filter() + .isArchivedEqualTo(false) + .isTrashedEqualTo(false) + .stackPrimaryAssetIdIsNull() + .sortByFileCreatedAtDesc(); + return _watchRenderList(query, groupAssetByOption); + } + + Stream _watchRenderList( + QueryBuilder query, + GroupAssetsBy groupAssetsBy, + ) async* { + yield await RenderList.fromQuery(query, groupAssetsBy); + await for (final _ in query.watchLazy()) { + yield await RenderList.fromQuery(query, groupAssetsBy); + } + } +} diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index e189dbe245..142ac48193 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -27,7 +27,6 @@ import 'package:immich_mobile/repositories/backup.repository.dart'; import 'package:immich_mobile/services/entity.service.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:immich_mobile/services/user.service.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:logging/logging.dart'; final albumServiceProvider = Provider( @@ -469,10 +468,6 @@ class AlbumService { return _albumRepository.watchAlbum(id); } - Stream getRenderListGenerator(Album album) { - return _albumRepository.getRenderListStream(album); - } - Future> search( String searchTerm, QuickFilterMode filterMode, diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart new file mode 100644 index 0000000000..e435ba72c9 --- /dev/null +++ b/mobile/lib/services/timeline.service.dart @@ -0,0 +1,70 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/album.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'; +import 'package:immich_mobile/repositories/timeline.repository.dart'; +import 'package:immich_mobile/repositories/user.repository.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; + +final timelineServiceProvider = Provider((ref) { + return TimelineService( + ref.watch(timelineRepositoryProvider), + ref.watch(userRepositoryProvider), + ref.watch(appSettingsServiceProvider), + ); +}); + +class TimelineService { + final ITimelineRepository _timelineRepository; + final IUserRepository _userRepository; + final AppSettingsService _appSettingsService; + TimelineService( + this._timelineRepository, + this._userRepository, + this._appSettingsService, + ); + + Stream watchHomeTimeline(int userId) { + return _timelineRepository.watchHomeTimeline(userId, _getGroupByOption()); + } + + Stream watchMultiUsersTimeline(List userIds) { + return _timelineRepository.watchMultiUsersTimeline( + userIds, + _getGroupByOption(), + ); + } + + Stream watchArchiveTimeline() async* { + final user = await _userRepository.me(); + + yield* _timelineRepository.watchArchiveTimeline(user.isarId); + } + + Stream watchFavoriteTimeline() async* { + final user = await _userRepository.me(); + + yield* _timelineRepository.watchFavoriteTimeline(user.isarId); + } + + Stream watchAlbumTimeline(Album album) async* { + yield* _timelineRepository.watchAlbumTimeline(album); + } + + Stream watchTrashTimeline() async* { + final user = await _userRepository.me(); + + yield* _timelineRepository.watchTrashTimeline(user.isarId); + } + + Stream watchAllVideosTimeline() { + return _timelineRepository.watchAllVideosTimeline(); + } + + GroupAssetsBy _getGroupByOption() { + return GroupAssetsBy + .values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)]; + } +} diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart index 690031d934..8d6cdd8bab 100644 --- a/mobile/lib/services/trash.service.dart +++ b/mobile/lib/services/trash.service.dart @@ -2,11 +2,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/interfaces/user.interface.dart'; + import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/repositories/user.repository.dart'; + import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:openapi/api.dart'; final trashServiceProvider = Provider((ref) { @@ -82,8 +83,4 @@ class TrashService { await _assetRepository.updateAll(updatedAssets); } - - Stream getRenderListGenerator(int userId) { - return _assetRepository.getTrashRenderListStream(userId); - } }