mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
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
This commit is contained in:
parent
7c851893b4
commit
d0381fddec
@ -75,12 +75,12 @@ custom_lint:
|
|||||||
- lib/pages/album/album_asset_selection.page.dart
|
- lib/pages/album/album_asset_selection.page.dart
|
||||||
- lib/routing/router.dart
|
- lib/routing/router.dart
|
||||||
- lib/services/immich_logger.service.dart # not really a service... more a util
|
- 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
|
- lib/widgets/asset_grid/asset_grid_data_structure.dart
|
||||||
- test/**.dart
|
- test/**.dart
|
||||||
# refactor the remaining providers
|
# refactor the remaining providers
|
||||||
- lib/providers/{asset,authentication,db,user}.provider.dart
|
- lib/providers/{db,user}.provider.dart
|
||||||
- lib/providers/{asset_viewer/render_list,backup/backup,search/all_motion_photos,search/recently_added_asset}.provider.dart
|
- lib/providers/backup/backup.provider.dart
|
||||||
|
|
||||||
- import_rule_openapi:
|
- import_rule_openapi:
|
||||||
message: openapi must only be used through ApiRepositories
|
message: openapi must only be used through ApiRepositories
|
||||||
|
@ -65,6 +65,9 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
|
|||||||
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false});
|
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false});
|
||||||
|
|
||||||
Future<List<Asset>> getTrashAssets(int userId);
|
Future<List<Asset>> getTrashAssets(int userId);
|
||||||
|
|
||||||
|
Future<List<Asset>> getRecentlyAddedAssets(int userId);
|
||||||
|
Future<List<Asset>> getMotionAssets(int userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AssetSort { checksum, ownerIdChecksum }
|
enum AssetSort { checksum, ownerIdChecksum }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
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';
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
|
|
||||||
abstract class ITimelineRepository {
|
abstract class ITimelineRepository {
|
||||||
@ -16,4 +17,11 @@ abstract class ITimelineRepository {
|
|||||||
List<int> userIds,
|
List<int> userIds,
|
||||||
GroupAssetsBy groupAssetsBy,
|
GroupAssetsBy groupAssetsBy,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<RenderList> getTimelineFromAssets(
|
||||||
|
List<Asset> assets,
|
||||||
|
GroupAssetsBy getGroupByOption,
|
||||||
|
);
|
||||||
|
|
||||||
|
Stream<RenderList> watchAssetSelectionTimeline(int userId);
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_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/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/asset_grid_data_structure.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
|
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class AlbumAssetSelectionPage extends HookConsumerWidget {
|
class AlbumAssetSelectionPage extends HookConsumerWidget {
|
||||||
@ -18,16 +17,14 @@ class AlbumAssetSelectionPage extends HookConsumerWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.existingAssets,
|
required this.existingAssets,
|
||||||
this.canDeselect = false,
|
this.canDeselect = false,
|
||||||
required this.query,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Set<Asset> existingAssets;
|
final Set<Asset> existingAssets;
|
||||||
final QueryBuilder<Asset, Asset, QAfterSortBy>? query;
|
|
||||||
final bool canDeselect;
|
final bool canDeselect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final renderList = ref.watch(renderListQueryProvider(query));
|
final assetSelectionRenderList = ref.watch(assetSelectionTimelineProvider);
|
||||||
final selected = useState<Set<Asset>>(existingAssets);
|
final selected = useState<Set<Asset>>(existingAssets);
|
||||||
final selectionEnabledHook = useState(true);
|
final selectionEnabledHook = useState(true);
|
||||||
|
|
||||||
@ -83,7 +80,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: renderList.widgetWhen(
|
body: assetSelectionRenderList.widgetWhen(
|
||||||
onData: (data) => buildBody(data),
|
onData: (data) => buildBody(data),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -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/widgets/album/album_viewer_appbar.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.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/asset_grid/multiselect_grid.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
|
||||||
@ -63,7 +62,6 @@ class AlbumViewer extends HookConsumerWidget {
|
|||||||
AlbumAssetSelectionRoute(
|
AlbumAssetSelectionRoute(
|
||||||
existingAssets: album.assets,
|
existingAssets: album.assets,
|
||||||
canDeselect: false,
|
canDeselect: false,
|
||||||
query: getRemoteAssetQuery(ref),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/pages/album/album_viewer.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/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/timeline.provider.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class AlbumViewerPage extends HookConsumerWidget {
|
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
|
// Listen provider to prevent autoDispose when navigating to other routes from within the viewer page
|
||||||
ref.listen(currentAlbumProvider, (_, __) {});
|
ref.listen(currentAlbumProvider, (_, __) {});
|
||||||
|
|
||||||
|
// This call helps rendering the asset selection instantly
|
||||||
|
ref.listen(assetSelectionTimelineProvider, (_, __) {});
|
||||||
|
|
||||||
ref.listen(albumWatcher(albumId), (_, albumFuture) {
|
ref.listen(albumWatcher(albumId), (_, albumFuture) {
|
||||||
albumFuture.whenData(
|
albumFuture.whenData(
|
||||||
(value) => ref.read(currentAlbumProvider.notifier).set(value),
|
(value) => ref.read(currentAlbumProvider.notifier).set(value),
|
||||||
|
@ -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/models/albums/asset_selection_page_result.model.dart';
|
||||||
import 'package:immich_mobile/providers/album/album.provider.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/album/album_title.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
|
||||||
import 'package:immich_mobile/routing/router.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_action_filled_button.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_title_text_field.dart';
|
import 'package:immich_mobile/widgets/album/album_title_text_field.dart';
|
||||||
@ -54,7 +53,6 @@ class CreateAlbumPage extends HookConsumerWidget {
|
|||||||
AlbumAssetSelectionRoute(
|
AlbumAssetSelectionRoute(
|
||||||
existingAssets: selectedAssets.value,
|
existingAssets: selectedAssets.value,
|
||||||
canDeselect: true,
|
canDeselect: true,
|
||||||
query: getRemoteAssetQuery(ref),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (selectedAsset == null) {
|
if (selectedAsset == null) {
|
||||||
|
@ -3,16 +3,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.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/memory.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
|
||||||
import 'package:immich_mobile/services/album.service.dart';
|
import 'package:immich_mobile/services/album.service.dart';
|
||||||
import 'package:immich_mobile/services/asset.service.dart';
|
import 'package:immich_mobile/services/asset.service.dart';
|
||||||
import 'package:immich_mobile/services/etag.service.dart';
|
import 'package:immich_mobile/services/etag.service.dart';
|
||||||
import 'package:immich_mobile/services/exif.service.dart';
|
import 'package:immich_mobile/services/exif.service.dart';
|
||||||
import 'package:immich_mobile/services/sync.service.dart';
|
import 'package:immich_mobile/services/sync.service.dart';
|
||||||
import 'package:immich_mobile/services/user.service.dart';
|
import 'package:immich_mobile/services/user.service.dart';
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
final assetProvider = StateNotifierProvider<AssetNotifier, bool>((ref) {
|
final assetProvider = StateNotifierProvider<AssetNotifier, bool>((ref) {
|
||||||
@ -184,20 +181,3 @@ final assetWatcher =
|
|||||||
final assetService = ref.watch(assetServiceProvider);
|
final assetService = ref.watch(assetServiceProvider);
|
||||||
return assetService.watchAsset(asset.id, fireImmediately: true);
|
return assetService.watchAsset(asset.id, fireImmediately: true);
|
||||||
});
|
});
|
||||||
|
|
||||||
QueryBuilder<Asset, Asset, QAfterSortBy>? 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();
|
|
||||||
}
|
|
||||||
|
@ -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<RenderList, List<Asset>>((ref, assets) {
|
|
||||||
final settings = ref.watch(appSettingsServiceProvider);
|
|
||||||
|
|
||||||
return RenderList.fromAssets(
|
|
||||||
assets,
|
|
||||||
GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
final renderListProviderWithGrouping =
|
|
||||||
FutureProvider.family<RenderList, (List<Asset>, 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<RenderList,
|
|
||||||
QueryBuilder<Asset, Asset, QAfterSortBy>?>(
|
|
||||||
(ref, query) =>
|
|
||||||
query == null ? const Stream.empty() : renderListGenerator(query, ref),
|
|
||||||
);
|
|
@ -6,7 +6,7 @@ part of 'db.provider.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$isarHash() => r'69d3a06aa7e69a4381478e03f7956eb07d7f7feb';
|
String _$isarHash() => r'f0e886fa20e56dd1dc0082fcc723016289bd03cb';
|
||||||
|
|
||||||
/// See also [isar].
|
/// See also [isar].
|
||||||
@ProviderFor(isar)
|
@ProviderFor(isar)
|
||||||
|
@ -6,7 +6,7 @@ part of 'store.provider.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$storeRepositoryHash() => r'9f378b96e552151fa14a8c8ce2c30a5f38f436ed';
|
String _$storeRepositoryHash() => r'2f1c3e2e2db5082a40eb30a183a6c770f5b09d76';
|
||||||
|
|
||||||
/// See also [storeRepository].
|
/// See also [storeRepository].
|
||||||
@ProviderFor(storeRepository)
|
@ProviderFor(storeRepository)
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/services/asset.service.dart';
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
|
|
||||||
final allMotionPhotosProvider = FutureProvider<List<Asset>>((ref) async {
|
final allMotionPhotosProvider = FutureProvider<List<Asset>>((ref) async {
|
||||||
return ref
|
return ref.watch(assetServiceProvider).getMotionAssets();
|
||||||
.watch(dbProvider)
|
|
||||||
.assets
|
|
||||||
.filter()
|
|
||||||
.livePhotoVideoIdIsNotNull()
|
|
||||||
.findAll();
|
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/models/search/search_result.model.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/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||||
import 'package:immich_mobile/services/search.service.dart';
|
import 'package:immich_mobile/services/search.service.dart';
|
||||||
@ -44,14 +44,13 @@ class PaginatedSearchNotifier extends StateNotifier<SearchResult> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
AsyncValue<RenderList> paginatedSearchRenderList(
|
Future<RenderList> paginatedSearchRenderList(
|
||||||
PaginatedSearchRenderListRef ref,
|
PaginatedSearchRenderListRef ref,
|
||||||
) {
|
) {
|
||||||
final result = ref.watch(paginatedSearchProvider);
|
final result = ref.watch(paginatedSearchProvider);
|
||||||
|
final timelineService = ref.watch(timelineServiceProvider);
|
||||||
return ref.watch(
|
return timelineService.getTimelineFromAssets(
|
||||||
renderListProviderWithGrouping(
|
result.assets,
|
||||||
(result.assets, GroupAssetsBy.none),
|
GroupAssetsBy.none,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@ part of 'paginated_search.provider.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$paginatedSearchRenderListHash() =>
|
String _$paginatedSearchRenderListHash() =>
|
||||||
r'4585c832106b16b6d294055f47bbbe83e0802846';
|
r'9efb98fd73d4e57e1ccd97a902cd459e3f18f749';
|
||||||
|
|
||||||
/// See also [paginatedSearchRenderList].
|
/// See also [paginatedSearchRenderList].
|
||||||
@ProviderFor(paginatedSearchRenderList)
|
@ProviderFor(paginatedSearchRenderList)
|
||||||
final paginatedSearchRenderListProvider =
|
final paginatedSearchRenderListProvider =
|
||||||
AutoDisposeProvider<AsyncValue<RenderList>>.internal(
|
AutoDisposeFutureProvider<RenderList>.internal(
|
||||||
paginatedSearchRenderList,
|
paginatedSearchRenderList,
|
||||||
name: r'paginatedSearchRenderListProvider',
|
name: r'paginatedSearchRenderListProvider',
|
||||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
@ -22,7 +22,6 @@ final paginatedSearchRenderListProvider =
|
|||||||
allTransitiveDependencies: null,
|
allTransitiveDependencies: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef PaginatedSearchRenderListRef
|
typedef PaginatedSearchRenderListRef = AutoDisposeFutureProviderRef<RenderList>;
|
||||||
= AutoDisposeProviderRef<AsyncValue<RenderList>>;
|
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/services/asset.service.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
|
|
||||||
final recentlyAddedAssetProvider = FutureProvider<List<Asset>>((ref) async {
|
final recentlyAddedAssetProvider = FutureProvider<List<Asset>>((ref) async {
|
||||||
final user = ref.read(currentUserProvider);
|
final assetService = ref.read(assetServiceProvider);
|
||||||
if (user == null) return [];
|
|
||||||
|
|
||||||
return ref
|
return assetService.getRecentlyAddedAssets();
|
||||||
.watch(dbProvider)
|
|
||||||
.assets
|
|
||||||
.where()
|
|
||||||
.ownerIdEqualToAnyChecksum(user.isarId)
|
|
||||||
.sortByFileCreatedAtDesc()
|
|
||||||
.findAll();
|
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/locale_provider.dart';
|
import 'package:immich_mobile/providers/locale_provider.dart';
|
||||||
import 'package:immich_mobile/services/timeline.service.dart';
|
import 'package:immich_mobile/services/timeline.service.dart';
|
||||||
@ -53,3 +54,17 @@ final allVideosTimelineProvider = StreamProvider<RenderList>((ref) {
|
|||||||
final timelineService = ref.watch(timelineServiceProvider);
|
final timelineService = ref.watch(timelineServiceProvider);
|
||||||
return timelineService.watchAllVideosTimeline();
|
return timelineService.watchAllVideosTimeline();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final assetSelectionTimelineProvider = StreamProvider<RenderList>((ref) {
|
||||||
|
final timelineService = ref.watch(timelineServiceProvider);
|
||||||
|
return timelineService.watchAssetSelectionTimeline();
|
||||||
|
});
|
||||||
|
|
||||||
|
final assetsTimelineProvider =
|
||||||
|
FutureProvider.family<RenderList, List<Asset>>((ref, assets) {
|
||||||
|
final timelineService = ref.watch(timelineServiceProvider);
|
||||||
|
return timelineService.getTimelineFromAssets(
|
||||||
|
assets,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
@ -233,6 +233,25 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
|
|||||||
.isTrashedEqualTo(true)
|
.isTrashedEqualTo(true)
|
||||||
.findAll();
|
.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Asset>> getRecentlyAddedAssets(int userId) {
|
||||||
|
return db.assets
|
||||||
|
.where()
|
||||||
|
.ownerIdEqualToAnyChecksum(userId)
|
||||||
|
.sortByFileCreatedAtDesc()
|
||||||
|
.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Asset>> getMotionAssets(int userId) {
|
||||||
|
return db.assets
|
||||||
|
.where()
|
||||||
|
.ownerIdEqualToAnyChecksum(userId)
|
||||||
|
.filter()
|
||||||
|
.livePhotoVideoIdIsNotNull()
|
||||||
|
.findAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Asset>> _getMatchesImpl(
|
Future<List<Asset>> _getMatchesImpl(
|
||||||
|
@ -111,6 +111,28 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, groupAssetByOption);
|
return _watchRenderList(query, groupAssetByOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<RenderList> getTimelineFromAssets(
|
||||||
|
List<Asset> assets,
|
||||||
|
GroupAssetsBy getGroupByOption,
|
||||||
|
) {
|
||||||
|
return RenderList.fromAssets(assets, getGroupByOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<RenderList> watchAssetSelectionTimeline(int userId) {
|
||||||
|
final query = db.assets
|
||||||
|
.where()
|
||||||
|
.remoteIdIsNotNull()
|
||||||
|
.filter()
|
||||||
|
.ownerIdEqualTo(userId)
|
||||||
|
.isTrashedEqualTo(false)
|
||||||
|
.stackPrimaryAssetIdIsNull()
|
||||||
|
.sortByFileCreatedAtDesc();
|
||||||
|
|
||||||
|
return _watchRenderList(query, GroupAssetsBy.none);
|
||||||
|
}
|
||||||
|
|
||||||
Stream<RenderList> _watchRenderList(
|
Stream<RenderList> _watchRenderList(
|
||||||
QueryBuilder<Asset, Asset, QAfterSortBy> query,
|
QueryBuilder<Asset, Asset, QAfterSortBy> query,
|
||||||
GroupAssetsBy groupAssetsBy,
|
GroupAssetsBy groupAssetsBy,
|
||||||
|
@ -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/routing/duplicate_guard.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.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';
|
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||||
|
|
||||||
part 'router.gr.dart';
|
part 'router.gr.dart';
|
||||||
|
@ -83,7 +83,6 @@ class AlbumAssetSelectionRoute
|
|||||||
Key? key,
|
Key? key,
|
||||||
required Set<Asset> existingAssets,
|
required Set<Asset> existingAssets,
|
||||||
bool canDeselect = false,
|
bool canDeselect = false,
|
||||||
required QueryBuilder<Asset, Asset, QAfterSortBy>? query,
|
|
||||||
List<PageRouteInfo>? children,
|
List<PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
AlbumAssetSelectionRoute.name,
|
AlbumAssetSelectionRoute.name,
|
||||||
@ -91,7 +90,6 @@ class AlbumAssetSelectionRoute
|
|||||||
key: key,
|
key: key,
|
||||||
existingAssets: existingAssets,
|
existingAssets: existingAssets,
|
||||||
canDeselect: canDeselect,
|
canDeselect: canDeselect,
|
||||||
query: query,
|
|
||||||
),
|
),
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
);
|
);
|
||||||
@ -106,7 +104,6 @@ class AlbumAssetSelectionRoute
|
|||||||
key: args.key,
|
key: args.key,
|
||||||
existingAssets: args.existingAssets,
|
existingAssets: args.existingAssets,
|
||||||
canDeselect: args.canDeselect,
|
canDeselect: args.canDeselect,
|
||||||
query: args.query,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -117,7 +114,6 @@ class AlbumAssetSelectionRouteArgs {
|
|||||||
this.key,
|
this.key,
|
||||||
required this.existingAssets,
|
required this.existingAssets,
|
||||||
this.canDeselect = false,
|
this.canDeselect = false,
|
||||||
required this.query,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Key? key;
|
final Key? key;
|
||||||
@ -126,11 +122,9 @@ class AlbumAssetSelectionRouteArgs {
|
|||||||
|
|
||||||
final bool canDeselect;
|
final bool canDeselect;
|
||||||
|
|
||||||
final QueryBuilder<Asset, Asset, QAfterSortBy>? query;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AlbumAssetSelectionRouteArgs{key: $key, existingAssets: $existingAssets, canDeselect: $canDeselect, query: $query}';
|
return 'AlbumAssetSelectionRouteArgs{key: $key, existingAssets: $existingAssets, canDeselect: $canDeselect}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,4 +514,14 @@ class AssetService {
|
|||||||
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false}) {
|
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false}) {
|
||||||
return _assetRepository.watchAsset(id, fireImmediately: fireImmediately);
|
return _assetRepository.watchAsset(id, fireImmediately: fireImmediately);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Asset>> getRecentlyAddedAssets() async {
|
||||||
|
final me = await _userRepository.me();
|
||||||
|
return _assetRepository.getRecentlyAddedAssets(me.isarId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Asset>> getMotionAssets() async {
|
||||||
|
final me = await _userRepository.me();
|
||||||
|
return _assetRepository.getMotionAssets(me.isarId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.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/timeline.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
@ -66,6 +67,29 @@ class TimelineService {
|
|||||||
return _timelineRepository.watchAllVideosTimeline();
|
return _timelineRepository.watchAllVideosTimeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<RenderList> getTimelineFromAssets(
|
||||||
|
List<Asset> assets,
|
||||||
|
GroupAssetsBy? groupBy,
|
||||||
|
) {
|
||||||
|
GroupAssetsBy groupOption = GroupAssetsBy.none;
|
||||||
|
if (groupBy != null) {
|
||||||
|
groupOption = groupBy;
|
||||||
|
} else {
|
||||||
|
groupOption = _getGroupByOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _timelineRepository.getTimelineFromAssets(
|
||||||
|
assets,
|
||||||
|
groupOption,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<RenderList> watchAssetSelectionTimeline() async* {
|
||||||
|
final user = await _userRepository.me();
|
||||||
|
|
||||||
|
yield* _timelineRepository.watchAssetSelectionTimeline(user.isarId);
|
||||||
|
}
|
||||||
|
|
||||||
GroupAssetsBy _getGroupByOption() {
|
GroupAssetsBy _getGroupByOption() {
|
||||||
return GroupAssetsBy
|
return GroupAssetsBy
|
||||||
.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)];
|
.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)];
|
||||||
|
@ -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<RenderList> renderListGenerator(
|
|
||||||
QueryBuilder<Asset, Asset, QAfterSortBy> query,
|
|
||||||
StreamProviderRef<RenderList> ref,
|
|
||||||
) {
|
|
||||||
final settings = ref.watch(appSettingsServiceProvider);
|
|
||||||
final groupBy =
|
|
||||||
GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)];
|
|
||||||
return renderListGeneratorWithGroupBy(query, groupBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<RenderList> renderListGeneratorWithGroupBy(
|
|
||||||
QueryBuilder<Asset, Asset, QAfterSortBy> query,
|
|
||||||
GroupAssetsBy groupBy,
|
|
||||||
) async* {
|
|
||||||
yield await RenderList.fromQuery(query, groupBy);
|
|
||||||
await for (final _ in query.watchLazy()) {
|
|
||||||
yield await RenderList.fromQuery(query, groupBy);
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.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/asset_grid_data_structure.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid_view.dart';
|
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid_view.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
@ -125,7 +125,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
|
|||||||
|
|
||||||
if (renderList != null) return buildAssetGridView(renderList!);
|
if (renderList != null) return buildAssetGridView(renderList!);
|
||||||
|
|
||||||
final renderListFuture = ref.watch(renderListProvider(assets!));
|
final renderListFuture = ref.watch(assetsTimelineProvider(assets!));
|
||||||
return renderListFuture.widgetWhen(
|
return renderListFuture.widgetWhen(
|
||||||
onData: (renderList) => buildAssetGridView(renderList),
|
onData: (renderList) => buildAssetGridView(renderList),
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/collection_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/asset_grid_data_structure.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
|
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
|
||||||
import 'package:immich_mobile/models/map/map_event.model.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
|
// Place it just below the drag handle
|
||||||
heightFactor: 0.80,
|
heightFactor: 0.80,
|
||||||
child: assetsInBounds.value.isNotEmpty
|
child: assetsInBounds.value.isNotEmpty
|
||||||
? ref.watch(renderListProvider(assetsInBounds.value)).when(
|
? ref
|
||||||
|
.watch(assetsTimelineProvider(assetsInBounds.value))
|
||||||
|
.when(
|
||||||
data: (renderList) {
|
data: (renderList) {
|
||||||
// Cache render list here to use it back during visibleItemsListener
|
// Cache render list here to use it back during visibleItemsListener
|
||||||
cachedRenderList.value = renderList;
|
cachedRenderList.value = renderList;
|
||||||
|
@ -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/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/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:isar/isar.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
@ -38,8 +36,6 @@ void main() {
|
|||||||
registerFallbackValue(MockSmartSearchDto());
|
registerFallbackValue(MockSmartSearchDto());
|
||||||
registerFallbackValue(MockMetadataSearchDto());
|
registerFallbackValue(MockMetadataSearchDto());
|
||||||
overrides = [
|
overrides = [
|
||||||
paginatedSearchRenderListProvider
|
|
||||||
.overrideWithValue(AsyncValue.data(RenderList.empty())),
|
|
||||||
dbProvider.overrideWithValue(db),
|
dbProvider.overrideWithValue(db),
|
||||||
isarProvider.overrideWithValue(db),
|
isarProvider.overrideWithValue(db),
|
||||||
apiServiceProvider.overrideWithValue(mockApiService),
|
apiServiceProvider.overrideWithValue(mockApiService),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user