diff --git a/app/lib/ui/flow/albums/albums_screen.dart b/app/lib/ui/flow/albums/albums_screen.dart index 2f969dd..8c65864 100644 --- a/app/lib/ui/flow/albums/albums_screen.dart +++ b/app/lib/ui/flow/albums/albums_screen.dart @@ -107,6 +107,7 @@ class _AlbumsScreenState extends ConsumerState { .map( (album) => AlbumItem( album: album, + media: state.medias[album.id], onTap: () async { await AlbumMediaListRoute( $extra: album, @@ -161,12 +162,14 @@ class _AlbumsScreenState extends ConsumerState { class AlbumItem extends StatelessWidget { final Album album; + final AppMedia? media; final void Function() onTap; final void Function() onLongTap; const AlbumItem({ super.key, required this.album, + required this.media, required this.onTap, required this.onLongTap, }); @@ -180,7 +183,7 @@ class AlbumItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: album.medias.isEmpty + child: media == null ? Container( width: double.infinity, decoration: BoxDecoration( @@ -197,12 +200,7 @@ class AlbumItem extends StatelessWidget { ), ) : AppMediaImage( - media: AppMedia( - id: album.medias.first, - path: '', - type: AppMediaType.image, - sources: [album.source], - ), + media: media!, size: Size(300, 300), ), ), diff --git a/app/lib/ui/flow/albums/albums_view_notifier.dart b/app/lib/ui/flow/albums/albums_view_notifier.dart index 0f0e9f1..da6a206 100644 --- a/app/lib/ui/flow/albums/albums_view_notifier.dart +++ b/app/lib/ui/flow/albums/albums_view_notifier.dart @@ -87,11 +87,28 @@ class AlbumStateNotifier extends StateNotifier { } } + /// Lookups for the first media in the album that is available + Future<({String id, AppMedia media})?> _getThumbnailMedia({ + required Album album, + required Future Function(String id) fetchMedia, + }) async { + if (album.medias.isEmpty) return null; + + for (final id in album.medias) { + final media = await fetchMedia.call(id); + if (media != null) { + return (id: album.id, media: media); + } + } + return null; + } + Future loadAlbums() async { if (state.loading) return; state = state.copyWith(loading: true, error: null); try { + _backupFolderId ??= await _googleDriveService.getBackUpFolderId(); final res = await Future.wait([ _localMediaService.getAlbums(), (state.googleAccount != null && _backupFolderId != null) @@ -102,8 +119,32 @@ class AlbumStateNotifier extends StateNotifier { : Future.value([]), ]); + final medias = await Future.wait([ + for (Album album in res[0]) + _getThumbnailMedia( + album: album, + fetchMedia: (id) => _localMediaService.getMedia(id: id), + ), + for (final album in res[1]) + _getThumbnailMedia( + album: album, + fetchMedia: (id) => _googleDriveService.getMedia(id: id), + ), + for (final album in res[2]) + _getThumbnailMedia( + album: album, + fetchMedia: (id) => _dropboxService.getMedia(id: id), + ), + ]).then( + (value) => { + for (final item in value) + if (item != null) item.id: item.media, + }, + ); + state = state.copyWith( albums: [...res[0], ...res[1], ...res[2]], + medias: medias, loading: false, ); } catch (e, s) { @@ -153,6 +194,7 @@ class AlbumsState with _$AlbumsState { const factory AlbumsState({ @Default(false) bool loading, @Default([]) List albums, + @Default({}) Map medias, GoogleSignInAccount? googleAccount, DropboxAccount? dropboxAccount, Object? error, diff --git a/app/lib/ui/flow/albums/albums_view_notifier.freezed.dart b/app/lib/ui/flow/albums/albums_view_notifier.freezed.dart index 215ac6f..9fa76e8 100644 --- a/app/lib/ui/flow/albums/albums_view_notifier.freezed.dart +++ b/app/lib/ui/flow/albums/albums_view_notifier.freezed.dart @@ -18,6 +18,7 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$AlbumsState { bool get loading => throw _privateConstructorUsedError; List get albums => throw _privateConstructorUsedError; + Map get medias => throw _privateConstructorUsedError; GoogleSignInAccount? get googleAccount => throw _privateConstructorUsedError; DropboxAccount? get dropboxAccount => throw _privateConstructorUsedError; Object? get error => throw _privateConstructorUsedError; @@ -39,6 +40,7 @@ abstract class $AlbumsStateCopyWith<$Res> { $Res call( {bool loading, List albums, + Map medias, GoogleSignInAccount? googleAccount, DropboxAccount? dropboxAccount, Object? error, @@ -64,6 +66,7 @@ class _$AlbumsStateCopyWithImpl<$Res, $Val extends AlbumsState> $Res call({ Object? loading = null, Object? albums = null, + Object? medias = null, Object? googleAccount = freezed, Object? dropboxAccount = freezed, Object? error = freezed, @@ -78,6 +81,10 @@ class _$AlbumsStateCopyWithImpl<$Res, $Val extends AlbumsState> ? _value.albums : albums // ignore: cast_nullable_to_non_nullable as List, + medias: null == medias + ? _value.medias + : medias // ignore: cast_nullable_to_non_nullable + as Map, googleAccount: freezed == googleAccount ? _value.googleAccount : googleAccount // ignore: cast_nullable_to_non_nullable @@ -117,6 +124,7 @@ abstract class _$$AlbumsStateImplCopyWith<$Res> $Res call( {bool loading, List albums, + Map medias, GoogleSignInAccount? googleAccount, DropboxAccount? dropboxAccount, Object? error, @@ -141,6 +149,7 @@ class __$$AlbumsStateImplCopyWithImpl<$Res> $Res call({ Object? loading = null, Object? albums = null, + Object? medias = null, Object? googleAccount = freezed, Object? dropboxAccount = freezed, Object? error = freezed, @@ -155,6 +164,10 @@ class __$$AlbumsStateImplCopyWithImpl<$Res> ? _value._albums : albums // ignore: cast_nullable_to_non_nullable as List, + medias: null == medias + ? _value._medias + : medias // ignore: cast_nullable_to_non_nullable + as Map, googleAccount: freezed == googleAccount ? _value.googleAccount : googleAccount // ignore: cast_nullable_to_non_nullable @@ -175,11 +188,13 @@ class _$AlbumsStateImpl implements _AlbumsState { const _$AlbumsStateImpl( {this.loading = false, final List albums = const [], + final Map medias = const {}, this.googleAccount, this.dropboxAccount, this.error, this.actionError}) - : _albums = albums; + : _albums = albums, + _medias = medias; @override @JsonKey() @@ -193,6 +208,15 @@ class _$AlbumsStateImpl implements _AlbumsState { return EqualUnmodifiableListView(_albums); } + final Map _medias; + @override + @JsonKey() + Map get medias { + if (_medias is EqualUnmodifiableMapView) return _medias; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_medias); + } + @override final GoogleSignInAccount? googleAccount; @override @@ -204,7 +228,7 @@ class _$AlbumsStateImpl implements _AlbumsState { @override String toString() { - return 'AlbumsState(loading: $loading, albums: $albums, googleAccount: $googleAccount, dropboxAccount: $dropboxAccount, error: $error, actionError: $actionError)'; + return 'AlbumsState(loading: $loading, albums: $albums, medias: $medias, googleAccount: $googleAccount, dropboxAccount: $dropboxAccount, error: $error, actionError: $actionError)'; } @override @@ -214,6 +238,7 @@ class _$AlbumsStateImpl implements _AlbumsState { other is _$AlbumsStateImpl && (identical(other.loading, loading) || other.loading == loading) && const DeepCollectionEquality().equals(other._albums, _albums) && + const DeepCollectionEquality().equals(other._medias, _medias) && (identical(other.googleAccount, googleAccount) || other.googleAccount == googleAccount) && (identical(other.dropboxAccount, dropboxAccount) || @@ -228,6 +253,7 @@ class _$AlbumsStateImpl implements _AlbumsState { runtimeType, loading, const DeepCollectionEquality().hash(_albums), + const DeepCollectionEquality().hash(_medias), googleAccount, dropboxAccount, const DeepCollectionEquality().hash(error), @@ -246,6 +272,7 @@ abstract class _AlbumsState implements AlbumsState { const factory _AlbumsState( {final bool loading, final List albums, + final Map medias, final GoogleSignInAccount? googleAccount, final DropboxAccount? dropboxAccount, final Object? error, @@ -256,6 +283,8 @@ abstract class _AlbumsState implements AlbumsState { @override List get albums; @override + Map get medias; + @override GoogleSignInAccount? get googleAccount; @override DropboxAccount? get dropboxAccount;