mirror of
https://github.com/immich-app/immich.git
synced 2025-06-03 05:34:32 -04:00
refactor(mobile): album provider (#16099)
This commit is contained in:
parent
47203d2760
commit
4f912de018
@ -77,7 +77,7 @@ custom_lint:
|
|||||||
- test/**.dart
|
- test/**.dart
|
||||||
# refactor the remaining providers
|
# refactor the remaining providers
|
||||||
- lib/providers/{archive,asset,authentication,db,favorite,partner,trash,user}.provider.dart
|
- lib/providers/{archive,asset,authentication,db,favorite,partner,trash,user}.provider.dart
|
||||||
- lib/providers/{album/album,album/shared_album,asset_viewer/render_list,backup/backup,search/all_motion_photos,search/recently_added_asset}.provider.dart
|
- lib/providers/{asset_viewer/render_list,backup/backup,search/all_motion_photos,search/recently_added_asset}.provider.dart
|
||||||
|
|
||||||
- import_rule_openapi:
|
- import_rule_openapi:
|
||||||
message: openapi must only be used through ApiRepositories
|
message: openapi must only be used through ApiRepositories
|
||||||
|
@ -5,7 +5,7 @@ environment:
|
|||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
analyzer: ^7.0.0
|
analyzer: ^6.0.0
|
||||||
analyzer_plugin: ^0.11.3
|
analyzer_plugin: ^0.11.3
|
||||||
custom_lint_builder: ^0.6.4
|
custom_lint_builder: ^0.6.4
|
||||||
glob: ^2.1.2
|
glob: ^2.1.2
|
||||||
|
@ -3,6 +3,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
import 'package:immich_mobile/interfaces/database.interface.dart';
|
||||||
import 'package:immich_mobile/models/albums/album_search.model.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 {
|
abstract interface class IAlbumRepository implements IDatabaseRepository {
|
||||||
Future<Album> create(Album album);
|
Future<Album> create(Album album);
|
||||||
@ -42,6 +43,14 @@ abstract interface class IAlbumRepository implements IDatabaseRepository {
|
|||||||
Future<Album> recalculateMetadata(Album album);
|
Future<Album> recalculateMetadata(Album album);
|
||||||
|
|
||||||
Future<List<Album>> search(String searchTerm, QuickFilterMode filterMode);
|
Future<List<Album>> search(String searchTerm, QuickFilterMode filterMode);
|
||||||
|
|
||||||
|
Stream<List<Album>> watchRemoteAlbums();
|
||||||
|
|
||||||
|
Stream<List<Album>> watchLocalAlbums();
|
||||||
|
|
||||||
|
Stream<Album?> watchAlbum(int id);
|
||||||
|
|
||||||
|
Stream<RenderList> getRenderListStream(Album album);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AlbumSort { remoteId, localId }
|
enum AlbumSort { remoteId, localId }
|
||||||
|
@ -8,43 +8,40 @@ import 'package:immich_mobile/services/album.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/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
|
||||||
import 'package:immich_mobile/utils/renderlist_generator.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
|
|
||||||
final isRefreshingRemoteAlbumProvider = StateProvider<bool>((ref) => false);
|
final isRefreshingRemoteAlbumProvider = StateProvider<bool>((ref) => false);
|
||||||
|
|
||||||
class AlbumNotifier extends StateNotifier<List<Album>> {
|
class AlbumNotifier extends StateNotifier<List<Album>> {
|
||||||
AlbumNotifier(this._albumService, this.db, this.ref) : super([]) {
|
AlbumNotifier(this.albumService, this.ref) : super([]) {
|
||||||
final query = db.albums.filter().remoteIdIsNotNull();
|
albumService.getAllRemoteAlbums().then((value) {
|
||||||
query.findAll().then((value) {
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
state = value;
|
state = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_streamSub = query.watch().listen((data) => state = data);
|
|
||||||
|
_streamSub =
|
||||||
|
albumService.watchRemoteAlbums().listen((data) => state = data);
|
||||||
}
|
}
|
||||||
|
|
||||||
final AlbumService _albumService;
|
final AlbumService albumService;
|
||||||
final Isar db;
|
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
late final StreamSubscription<List<Album>> _streamSub;
|
late final StreamSubscription<List<Album>> _streamSub;
|
||||||
|
|
||||||
Future<void> refreshRemoteAlbums() async {
|
Future<void> refreshRemoteAlbums() async {
|
||||||
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = true;
|
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = true;
|
||||||
await _albumService.refreshRemoteAlbums();
|
await albumService.refreshRemoteAlbums();
|
||||||
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = false;
|
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> refreshDeviceAlbums() => _albumService.refreshDeviceAlbums();
|
Future<void> refreshDeviceAlbums() => albumService.refreshDeviceAlbums();
|
||||||
|
|
||||||
Future<bool> deleteAlbum(Album album) => _albumService.deleteAlbum(album);
|
Future<bool> deleteAlbum(Album album) => albumService.deleteAlbum(album);
|
||||||
|
|
||||||
Future<Album?> createAlbum(
|
Future<Album?> createAlbum(
|
||||||
String albumTitle,
|
String albumTitle,
|
||||||
Set<Asset> assets,
|
Set<Asset> assets,
|
||||||
) =>
|
) =>
|
||||||
_albumService.createAlbum(albumTitle, assets, []);
|
albumService.createAlbum(albumTitle, assets, []);
|
||||||
|
|
||||||
Future<Album?> getAlbumByName(
|
Future<Album?> getAlbumByName(
|
||||||
String albumName, {
|
String albumName, {
|
||||||
@ -52,7 +49,7 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
bool? shared,
|
bool? shared,
|
||||||
bool? owner,
|
bool? owner,
|
||||||
}) =>
|
}) =>
|
||||||
_albumService.getAlbumByName(
|
albumService.getAlbumByName(
|
||||||
albumName,
|
albumName,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
shared: shared,
|
shared: shared,
|
||||||
@ -74,7 +71,7 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> leaveAlbum(Album album) async {
|
Future<bool> leaveAlbum(Album album) async {
|
||||||
var res = await _albumService.leaveAlbum(album);
|
var res = await albumService.leaveAlbum(album);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
await deleteAlbum(album);
|
await deleteAlbum(album);
|
||||||
@ -85,15 +82,15 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void searchAlbums(String searchTerm, QuickFilterMode filterMode) async {
|
void searchAlbums(String searchTerm, QuickFilterMode filterMode) async {
|
||||||
state = await _albumService.search(searchTerm, filterMode);
|
state = await albumService.search(searchTerm, filterMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addUsers(Album album, List<String> userIds) async {
|
Future<void> addUsers(Album album, List<String> userIds) async {
|
||||||
await _albumService.addUsers(album, userIds);
|
await albumService.addUsers(album, userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> removeUser(Album album, User user) async {
|
Future<bool> removeUser(Album album, User user) async {
|
||||||
final isRemoved = await _albumService.removeUser(album, user);
|
final isRemoved = await albumService.removeUser(album, user);
|
||||||
|
|
||||||
if (isRemoved && album.sharedUsers.isEmpty) {
|
if (isRemoved && album.sharedUsers.isEmpty) {
|
||||||
state = state.where((element) => element.id != album.id).toList();
|
state = state.where((element) => element.id != album.id).toList();
|
||||||
@ -103,25 +100,25 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addAssets(Album album, Iterable<Asset> assets) async {
|
Future<void> addAssets(Album album, Iterable<Asset> assets) async {
|
||||||
await _albumService.addAssets(album, assets);
|
await albumService.addAssets(album, assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> removeAsset(Album album, Iterable<Asset> assets) async {
|
Future<bool> removeAsset(Album album, Iterable<Asset> assets) async {
|
||||||
return await _albumService.removeAsset(album, assets);
|
return await albumService.removeAsset(album, assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> setActivitystatus(
|
Future<bool> setActivitystatus(
|
||||||
Album album,
|
Album album,
|
||||||
bool enabled,
|
bool enabled,
|
||||||
) {
|
) {
|
||||||
return _albumService.setActivityStatus(album, enabled);
|
return albumService.setActivityStatus(album, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Album?> toggleSortOrder(Album album) {
|
Future<Album?> toggleSortOrder(Album album) {
|
||||||
final order =
|
final order =
|
||||||
album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
|
album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
|
||||||
|
|
||||||
return _albumService.updateSortOrder(album, order);
|
return albumService.updateSortOrder(album, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -135,57 +132,49 @@ final albumProvider =
|
|||||||
StateNotifierProvider.autoDispose<AlbumNotifier, List<Album>>((ref) {
|
StateNotifierProvider.autoDispose<AlbumNotifier, List<Album>>((ref) {
|
||||||
return AlbumNotifier(
|
return AlbumNotifier(
|
||||||
ref.watch(albumServiceProvider),
|
ref.watch(albumServiceProvider),
|
||||||
ref.watch(dbProvider),
|
|
||||||
ref,
|
ref,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
final albumWatcher =
|
final albumWatcher =
|
||||||
StreamProvider.autoDispose.family<Album, int>((ref, albumId) async* {
|
StreamProvider.autoDispose.family<Album, int>((ref, id) async* {
|
||||||
final db = ref.watch(dbProvider);
|
final albumService = ref.watch(albumServiceProvider);
|
||||||
final a = await db.albums.get(albumId);
|
|
||||||
if (a != null) yield a;
|
final album = await albumService.getAlbumById(id);
|
||||||
await for (final a in db.albums.watchObject(albumId, fireImmediately: true)) {
|
if (album != null) {
|
||||||
if (a != null) yield a;
|
yield album;
|
||||||
|
}
|
||||||
|
|
||||||
|
await for (final album in albumService.watchAlbum(id)) {
|
||||||
|
if (album != null) {
|
||||||
|
yield album;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final albumRenderlistProvider =
|
final albumRenderlistProvider =
|
||||||
StreamProvider.autoDispose.family<RenderList, int>((ref, albumId) {
|
StreamProvider.autoDispose.family<RenderList, int>((ref, id) {
|
||||||
final album = ref.watch(albumWatcher(albumId)).value;
|
final album = ref.watch(albumWatcher(id)).value;
|
||||||
|
|
||||||
if (album != null) {
|
if (album != null) {
|
||||||
final query = album.assets.filter().isTrashedEqualTo(false);
|
return ref.watch(albumServiceProvider).getRenderListGenerator(album);
|
||||||
if (album.sortOrder == SortOrder.asc) {
|
|
||||||
return renderListGeneratorWithGroupBy(
|
|
||||||
query.sortByFileCreatedAt(),
|
|
||||||
GroupAssetsBy.none,
|
|
||||||
);
|
|
||||||
} else if (album.sortOrder == SortOrder.desc) {
|
|
||||||
return renderListGeneratorWithGroupBy(
|
|
||||||
query.sortByFileCreatedAtDesc(),
|
|
||||||
GroupAssetsBy.none,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return const Stream.empty();
|
return const Stream.empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
class LocalAlbumsNotifier extends StateNotifier<List<Album>> {
|
class LocalAlbumsNotifier extends StateNotifier<List<Album>> {
|
||||||
LocalAlbumsNotifier(this.db) : super([]) {
|
LocalAlbumsNotifier(this.albumService) : super([]) {
|
||||||
final query = db.albums.where().remoteIdIsNull();
|
albumService.getAllLocalAlbums().then((value) {
|
||||||
|
|
||||||
query.findAll().then((value) {
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
state = value;
|
state = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_streamSub = query.watch().listen((data) => state = data);
|
_streamSub = albumService.watchLocalAlbums().listen((data) => state = data);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Isar db;
|
final AlbumService albumService;
|
||||||
late final StreamSubscription<List<Album>> _streamSub;
|
late final StreamSubscription<List<Album>> _streamSub;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -197,5 +186,5 @@ class LocalAlbumsNotifier extends StateNotifier<List<Album>> {
|
|||||||
|
|
||||||
final localAlbumsProvider =
|
final localAlbumsProvider =
|
||||||
StateNotifierProvider.autoDispose<LocalAlbumsNotifier, List<Album>>((ref) {
|
StateNotifierProvider.autoDispose<LocalAlbumsNotifier, List<Album>>((ref) {
|
||||||
return LocalAlbumsNotifier(ref.watch(dbProvider));
|
return LocalAlbumsNotifier(ref.watch(albumServiceProvider));
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/album.entity.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';
|
||||||
@ -7,6 +8,7 @@ import 'package:immich_mobile/interfaces/album.interface.dart';
|
|||||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/database.repository.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';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
final albumRepositoryProvider =
|
final albumRepositoryProvider =
|
||||||
@ -152,4 +154,37 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
|
|||||||
|
|
||||||
return await query.findAll();
|
return await query.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<Album>> watchRemoteAlbums() {
|
||||||
|
return db.albums.where().remoteIdIsNotNull().watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<Album>> watchLocalAlbums() {
|
||||||
|
return db.albums.where().localIdIsNotNull().watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Album?> watchAlbum(int id) {
|
||||||
|
return db.albums.watchObject(id, fireImmediately: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<RenderList> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import 'package:immich_mobile/repositories/album_media.repository.dart';
|
|||||||
import 'package:immich_mobile/services/entity.service.dart';
|
import 'package:immich_mobile/services/entity.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:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
final albumServiceProvider = Provider(
|
final albumServiceProvider = Provider(
|
||||||
@ -442,10 +443,35 @@ class AlbumService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Album>> getAll() async {
|
Future<List<Album>> getAllRemoteAlbums() async {
|
||||||
return _albumRepository.getAll(remote: true);
|
return _albumRepository.getAll(remote: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Album>> getAllLocalAlbums() async {
|
||||||
|
return _albumRepository.getAll(remote: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Album>> watchRemoteAlbums() {
|
||||||
|
return _albumRepository.watchRemoteAlbums();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Album>> watchLocalAlbums() {
|
||||||
|
return _albumRepository.watchLocalAlbums();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get album by Isar ID
|
||||||
|
Future<Album?> getAlbumById(int id) {
|
||||||
|
return _albumRepository.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<Album?> watchAlbum(int id) {
|
||||||
|
return _albumRepository.watchAlbum(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<RenderList> getRenderListGenerator(Album album) {
|
||||||
|
return _albumRepository.getRenderListStream(album);
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<Album>> search(
|
Future<List<Album>> search(
|
||||||
String searchTerm,
|
String searchTerm,
|
||||||
QuickFilterMode filterMode,
|
QuickFilterMode filterMode,
|
||||||
|
3
mobile/openapi/devtools_options.yaml
Normal file
3
mobile/openapi/devtools_options.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
description: This file stores settings for Dart & Flutter DevTools.
|
||||||
|
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||||
|
extensions:
|
Loading…
x
Reference in New Issue
Block a user