mirror of
https://github.com/immich-app/immich.git
synced 2025-11-12 01:26:50 -05:00
feat: show "appears in" albums on asset viewer bottom sheet (#21925)
* feat: show "appears in" albums on asset viewer bottom sheet fix: multiple RemoteAlbumPages in navigation stack this also allows us to not have to set the current album before navigating to RemoteAlbumPage chore: clarification comments handle nested album pages fix: hide "appears in" when an asset is not in any albums fix: way more bottom padding for some reason we can't query the safe area here :/ * fix: bottom sheet now is usable when navigating to another asset viewer * fix: rebase conflict * fix: restore ancestors album to currentRemoteAlbumProvider when popping * fix: view flashing when dismissing a album viewer * chore: code review changes * fix: styling and padding * chore: rework currentRemoteAlbumProvider to be scoped by the Remote album page * fix: override remote album provider on required pages * chore: convert query to all SQL calls instead of matching in Dart * fix: album query * fix: unawaited future * Update deep_link.service.dart --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
74f2c10a5a
commit
e0c2cdddd4
@ -164,6 +164,10 @@ class RemoteAlbumService {
|
|||||||
return _repository.getCount();
|
return _repository.getCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<RemoteAlbum>> getAlbumsContainingAsset(String assetId) {
|
||||||
|
return _repository.getAlbumsContainingAsset(assetId);
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<RemoteAlbum>> _sortByNewestAsset(List<RemoteAlbum> albums) async {
|
Future<List<RemoteAlbum>> _sortByNewestAsset(List<RemoteAlbum> albums) async {
|
||||||
// map album IDs to their newest asset dates
|
// map album IDs to their newest asset dates
|
||||||
final Map<String, Future<DateTime?>> assetTimestampFutures = {};
|
final Map<String, Future<DateTime?>> assetTimestampFutures = {};
|
||||||
|
|||||||
@ -382,6 +382,61 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
|
|
||||||
return query.map((row) => row.read(_db.remoteAssetEntity.id)!).get();
|
return query.map((row) => row.read(_db.remoteAssetEntity.id)!).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<RemoteAlbum>> getAlbumsContainingAsset(String assetId) async {
|
||||||
|
// Note: this needs to be 2 queries as the where clause filtering causes the assetCount to always be 1
|
||||||
|
final albumIdsQuery = _db.remoteAlbumAssetEntity.selectOnly()
|
||||||
|
..addColumns([_db.remoteAlbumAssetEntity.albumId])
|
||||||
|
..where(_db.remoteAlbumAssetEntity.assetId.equals(assetId));
|
||||||
|
|
||||||
|
final albumIds = await albumIdsQuery.map((row) => row.read(_db.remoteAlbumAssetEntity.albumId)!).get();
|
||||||
|
|
||||||
|
if (albumIds.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true);
|
||||||
|
final query =
|
||||||
|
_db.remoteAlbumEntity.select().join([
|
||||||
|
leftOuterJoin(
|
||||||
|
_db.remoteAlbumAssetEntity,
|
||||||
|
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
leftOuterJoin(
|
||||||
|
_db.remoteAssetEntity,
|
||||||
|
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
leftOuterJoin(
|
||||||
|
_db.userEntity,
|
||||||
|
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
leftOuterJoin(
|
||||||
|
_db.remoteAlbumUserEntity,
|
||||||
|
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
..where(_db.remoteAlbumEntity.id.isIn(albumIds) & _db.remoteAssetEntity.deletedAt.isNull())
|
||||||
|
..addColumns([assetCount])
|
||||||
|
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
|
||||||
|
..addColumns([_db.userEntity.name])
|
||||||
|
..groupBy([_db.remoteAlbumEntity.id]);
|
||||||
|
|
||||||
|
return query
|
||||||
|
.map(
|
||||||
|
(row) => row
|
||||||
|
.readTable(_db.remoteAlbumEntity)
|
||||||
|
.toDto(
|
||||||
|
ownerName: row.read(_db.userEntity.name) ?? '',
|
||||||
|
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||||
|
assetCount: row.read(assetCount) ?? 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on RemoteAlbumEntityData {
|
extension on RemoteAlbumEntityData {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.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';
|
||||||
@ -18,12 +19,13 @@ import 'package:immich_mobile/widgets/activities/dismissible_activity.dart';
|
|||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class DriftActivitiesPage extends HookConsumerWidget {
|
class DriftActivitiesPage extends HookConsumerWidget {
|
||||||
const DriftActivitiesPage({super.key});
|
final RemoteAlbum album;
|
||||||
|
|
||||||
|
const DriftActivitiesPage({super.key, required this.album});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final album = ref.watch(currentRemoteAlbumProvider)!;
|
final asset = ref.watch(currentAssetNotifier) as RemoteAsset?;
|
||||||
final asset = ref.read(currentAssetNotifier) as RemoteAsset?;
|
|
||||||
final user = ref.watch(currentUserProvider);
|
final user = ref.watch(currentUserProvider);
|
||||||
|
|
||||||
final activityNotifier = ref.read(albumActivityProvider(album.id, asset?.id).notifier);
|
final activityNotifier = ref.read(albumActivityProvider(album.id, asset?.id).notifier);
|
||||||
@ -43,7 +45,9 @@ class DriftActivitiesPage extends HookConsumerWidget {
|
|||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return ProviderScope(
|
||||||
|
overrides: [currentRemoteAlbumScopedProvider.overrideWithValue(album)],
|
||||||
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: asset == null ? Text(album.name) : null,
|
title: asset == null ? Text(album.name) : null,
|
||||||
actions: [const LikeActivityActionButton(menuItem: true)],
|
actions: [const LikeActivityActionButton(menuItem: true)],
|
||||||
@ -99,6 +103,7 @@ class DriftActivitiesPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
resizeToAvoidBottomInset: true,
|
resizeToAvoidBottomInset: true,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
|
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
|
||||||
|
|
||||||
@ -43,7 +42,6 @@ class _DriftAlbumsPageState extends ConsumerState<DriftAlbumsPage> {
|
|||||||
),
|
),
|
||||||
AlbumSelector(
|
AlbumSelector(
|
||||||
onAlbumSelected: (album) {
|
onAlbumSelected: (album) {
|
||||||
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album);
|
|
||||||
context.router.push(RemoteAlbumRoute(album: album));
|
context.router.push(RemoteAlbumRoute(album: album));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
@ -22,15 +23,11 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
|
|||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class DriftAlbumOptionsPage extends HookConsumerWidget {
|
class DriftAlbumOptionsPage extends HookConsumerWidget {
|
||||||
const DriftAlbumOptionsPage({super.key});
|
final RemoteAlbum album;
|
||||||
|
const DriftAlbumOptionsPage({super.key, required this.album});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final album = ref.watch(currentRemoteAlbumProvider);
|
|
||||||
if (album == null) {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
final sharedUsersAsync = ref.watch(remoteAlbumSharedUsersProvider(album.id));
|
final sharedUsersAsync = ref.watch(remoteAlbumSharedUsersProvider(album.id));
|
||||||
final userId = ref.watch(authProvider).userId;
|
final userId = ref.watch(authProvider).userId;
|
||||||
final activityEnabled = useState(album.isActivityEnabled);
|
final activityEnabled = useState(album.isActivityEnabled);
|
||||||
@ -191,7 +188,9 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return ProviderScope(
|
||||||
|
overrides: [currentRemoteAlbumScopedProvider.overrideWithValue(album)],
|
||||||
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back_ios_new_rounded),
|
icon: const Icon(Icons.arrow_back_ios_new_rounded),
|
||||||
@ -234,6 +233,7 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
|
|||||||
buildSharedUsersList(),
|
buildSharedUsersList(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.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';
|
||||||
|
|
||||||
@ -180,7 +179,6 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (album != null) {
|
if (album != null) {
|
||||||
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album);
|
|
||||||
unawaited(context.replaceRoute(RemoteAlbumRoute(album: album)));
|
unawaited(context.replaceRoute(RemoteAlbumRoute(album: album)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -168,7 +168,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showActivity(BuildContext context) async {
|
Future<void> showActivity(BuildContext context) async {
|
||||||
unawaited(context.pushRoute(const DriftActivitiesRoute()));
|
unawaited(context.pushRoute(DriftActivitiesRoute(album: _album)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showOptionSheet(BuildContext context) async {
|
Future<void> showOptionSheet(BuildContext context) async {
|
||||||
@ -224,7 +224,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
|||||||
: null,
|
: null,
|
||||||
onShowOptions: () {
|
onShowOptions: () {
|
||||||
context.pop();
|
context.pop();
|
||||||
context.pushRoute(const DriftAlbumOptionsRoute());
|
context.pushRoute(DriftAlbumOptionsRoute(album: _album));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -237,24 +237,14 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
|||||||
final user = ref.watch(currentUserProvider);
|
final user = ref.watch(currentUserProvider);
|
||||||
final isOwner = user != null ? user.id == _album.ownerId : false;
|
final isOwner = user != null ? user.id == _album.ownerId : false;
|
||||||
|
|
||||||
return PopScope(
|
return ProviderScope(
|
||||||
onPopInvokedWithResult: (didPop, _) {
|
|
||||||
if (didPop) {
|
|
||||||
Future.microtask(() {
|
|
||||||
if (mounted) {
|
|
||||||
ref.read(currentRemoteAlbumProvider.notifier).dispose();
|
|
||||||
ref.read(remoteAlbumProvider.notifier).refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: ProviderScope(
|
|
||||||
overrides: [
|
overrides: [
|
||||||
timelineServiceProvider.overrideWith((ref) {
|
timelineServiceProvider.overrideWith((ref) {
|
||||||
final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id);
|
final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id);
|
||||||
ref.onDispose(timelineService.dispose);
|
ref.onDispose(timelineService.dispose);
|
||||||
return timelineService;
|
return timelineService;
|
||||||
}),
|
}),
|
||||||
|
currentRemoteAlbumScopedProvider.overrideWithValue(_album),
|
||||||
],
|
],
|
||||||
child: Timeline(
|
child: Timeline(
|
||||||
appBar: RemoteAlbumSliverAppBar(
|
appBar: RemoteAlbumSliverAppBar(
|
||||||
@ -266,7 +256,6 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
|
|||||||
),
|
),
|
||||||
bottomSheet: RemoteAlbumBottomSheet(album: _album),
|
bottomSheet: RemoteAlbumBottomSheet(album: _album),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,10 +12,9 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.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/pages/common/large_leading_tile.dart';
|
import 'package:immich_mobile/presentation/widgets/album/album_tile.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
@ -516,38 +515,6 @@ class _AlbumList extends ConsumerWidget {
|
|||||||
sliver: SliverList.builder(
|
sliver: SliverList.builder(
|
||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
final album = albums[index];
|
final album = albums[index];
|
||||||
final albumTile = LargeLeadingTile(
|
|
||||||
title: Text(
|
|
||||||
album.name,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
'${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}',
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
|
||||||
),
|
|
||||||
onTap: () => onAlbumSelected(album),
|
|
||||||
leadingPadding: const EdgeInsets.only(right: 16),
|
|
||||||
leading: album.thumbnailAssetId != null
|
|
||||||
? ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
|
||||||
child: SizedBox(width: 80, height: 80, child: Thumbnail.remote(remoteId: album.thumbnailAssetId!)),
|
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
width: 80,
|
|
||||||
height: 80,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: context.colorScheme.surfaceContainer,
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
|
||||||
border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1),
|
|
||||||
),
|
|
||||||
child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
final isOwner = album.ownerId == userId;
|
final isOwner = album.ownerId == userId;
|
||||||
|
|
||||||
if (isOwner) {
|
if (isOwner) {
|
||||||
@ -576,11 +543,14 @@ class _AlbumList extends ConsumerWidget {
|
|||||||
onDismissed: (direction) async {
|
onDismissed: (direction) async {
|
||||||
await ref.read(remoteAlbumProvider.notifier).deleteAlbum(album.id);
|
await ref.read(remoteAlbumProvider.notifier).deleteAlbum(album.id);
|
||||||
},
|
},
|
||||||
child: albumTile,
|
child: AlbumTile(album: album, isOwner: isOwner, onAlbumSelected: onAlbumSelected),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Padding(padding: const EdgeInsets.only(bottom: 8.0), child: albumTile);
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: AlbumTile(album: album, isOwner: isOwner, onAlbumSelected: onAlbumSelected),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemCount: albums.length,
|
itemCount: albums.length,
|
||||||
@ -709,7 +679,6 @@ class AddToAlbumHeader extends ConsumerWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum);
|
|
||||||
ref.read(multiSelectProvider.notifier).reset();
|
ref.read(multiSelectProvider.notifier).reset();
|
||||||
unawaited(context.pushRoute(RemoteAlbumRoute(album: newAlbum)));
|
unawaited(context.pushRoute(RemoteAlbumRoute(album: newAlbum)));
|
||||||
}
|
}
|
||||||
|
|||||||
51
mobile/lib/presentation/widgets/album/album_tile.dart
Normal file
51
mobile/lib/presentation/widgets/album/album_tile.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
|
|
||||||
|
class AlbumTile extends StatelessWidget {
|
||||||
|
const AlbumTile({super.key, required this.album, required this.isOwner, this.onAlbumSelected});
|
||||||
|
|
||||||
|
final RemoteAlbum album;
|
||||||
|
final bool isOwner;
|
||||||
|
final Function(RemoteAlbum)? onAlbumSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LargeLeadingTile(
|
||||||
|
title: Text(
|
||||||
|
album.name,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${isOwner ? 'owned'.t(context: context) : 'shared_by_user'.t(context: context, args: {'user': album.ownerName})}',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
||||||
|
),
|
||||||
|
onTap: () => onAlbumSelected?.call(album),
|
||||||
|
leadingPadding: const EdgeInsets.only(right: 16),
|
||||||
|
leading: album.thumbnailAssetId != null
|
||||||
|
? ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||||
|
child: SizedBox(width: 80, height: 80, child: Thumbnail.remote(remoteId: album.thumbnailAssetId!)),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.colorScheme.surfaceContainer,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
|
border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1),
|
||||||
|
),
|
||||||
|
child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||||
@ -13,6 +14,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/scroll_extensions.dart';
|
import 'package:immich_mobile/extensions/scroll_extensions.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/download_status_floating_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/download_status_floating_button.widget.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/activities_bottom_sheet.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
@ -20,7 +22,6 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_bar.widge
|
|||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/top_app_bar.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/top_app_bar.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/activities_bottom_sheet.widget.dart';
|
|
||||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
@ -28,6 +29,7 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provi
|
|||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
|
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
|
||||||
@ -39,15 +41,25 @@ class AssetViewerPage extends StatelessWidget {
|
|||||||
final int initialIndex;
|
final int initialIndex;
|
||||||
final TimelineService timelineService;
|
final TimelineService timelineService;
|
||||||
final int? heroOffset;
|
final int? heroOffset;
|
||||||
|
final RemoteAlbum? currentAlbum;
|
||||||
|
|
||||||
const AssetViewerPage({super.key, required this.initialIndex, required this.timelineService, this.heroOffset});
|
const AssetViewerPage({
|
||||||
|
super.key,
|
||||||
|
required this.initialIndex,
|
||||||
|
required this.timelineService,
|
||||||
|
this.heroOffset,
|
||||||
|
this.currentAlbum,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// This is necessary to ensure that the timeline service is available
|
// This is necessary to ensure that the timeline service is available
|
||||||
// since the Timeline and AssetViewer are on different routes / Widget subtrees.
|
// since the Timeline and AssetViewer are on different routes / Widget subtrees.
|
||||||
return ProviderScope(
|
return ProviderScope(
|
||||||
overrides: [timelineServiceProvider.overrideWithValue(timelineService)],
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWithValue(timelineService),
|
||||||
|
currentRemoteAlbumScopedProvider.overrideWithValue(currentAlbum),
|
||||||
|
],
|
||||||
child: AssetViewer(initialIndex: initialIndex, heroOffset: heroOffset),
|
child: AssetViewer(initialIndex: initialIndex, heroOffset: heroOffset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -8,11 +12,14 @@ import 'package:immich_mobile/domain/models/exif.model.dart';
|
|||||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/album/album_tile.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
||||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
||||||
@ -20,6 +27,7 @@ import 'package:immich_mobile/providers/routes.provider.dart';
|
|||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/utils/action_button.utils.dart';
|
import 'package:immich_mobile/utils/action_button.utils.dart';
|
||||||
import 'package:immich_mobile/utils/bytes_units.dart';
|
import 'package:immich_mobile/utils/bytes_units.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
@ -132,6 +140,60 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
|
|||||||
await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context);
|
await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildAppearsInList(WidgetRef ref, BuildContext context) {
|
||||||
|
final isRemote = ref.watch(currentAssetNotifier)?.hasRemote ?? false;
|
||||||
|
if (!isRemote) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final remoteAsset = ref.watch(currentAssetNotifier) as RemoteAsset;
|
||||||
|
final userId = ref.watch(currentUserProvider)?.id;
|
||||||
|
final assetAlbums = ref.watch(albumsContainingAssetProvider(remoteAsset.id));
|
||||||
|
|
||||||
|
return assetAlbums.when(
|
||||||
|
data: (albums) {
|
||||||
|
if (albums.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
albums.sortBy((a) => a.name);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
spacing: 12,
|
||||||
|
children: [
|
||||||
|
if (albums.isNotEmpty)
|
||||||
|
_SheetTile(
|
||||||
|
title: 'appears_in'.t(context: context).toUpperCase(),
|
||||||
|
titleStyle: context.textTheme.labelMedium?.copyWith(
|
||||||
|
color: context.textTheme.labelMedium?.color?.withAlpha(200),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 24),
|
||||||
|
child: Column(
|
||||||
|
spacing: 12,
|
||||||
|
children: albums.map((album) {
|
||||||
|
final isOwner = album.ownerId == userId;
|
||||||
|
return AlbumTile(
|
||||||
|
album: album,
|
||||||
|
isOwner: isOwner,
|
||||||
|
onAlbumSelected: (album) async {
|
||||||
|
ref.invalidate(assetViewerProvider);
|
||||||
|
unawaited(context.router.popAndPush(RemoteAlbumRoute(album: album)));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const SizedBox.shrink(),
|
||||||
|
error: (_, __) => const SizedBox.shrink(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final asset = ref.watch(currentAssetNotifier);
|
final asset = ref.watch(currentAssetNotifier);
|
||||||
@ -217,7 +279,10 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
|
|||||||
color: context.textTheme.bodyMedium?.color?.withAlpha(155),
|
color: context.textTheme.bodyMedium?.color?.withAlpha(155),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 64),
|
// Appears in (Albums)
|
||||||
|
_buildAppearsInList(ref, context),
|
||||||
|
// padding at the bottom to avoid cut-off
|
||||||
|
const SizedBox(height: 100),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'
|
|||||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
@ -163,6 +164,7 @@ class _AssetTileWidget extends ConsumerWidget {
|
|||||||
initialIndex: assetIndex,
|
initialIndex: assetIndex,
|
||||||
timelineService: ref.read(timelineServiceProvider),
|
timelineService: ref.read(timelineServiceProvider),
|
||||||
heroOffset: heroOffset,
|
heroOffset: heroOffset,
|
||||||
|
currentAlbum: ref.read(currentRemoteAlbumProvider),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/local_album.service.dart';
|
import 'package:immich_mobile/domain/services/local_album.service.dart';
|
||||||
@ -40,3 +41,7 @@ final remoteAlbumProvider = NotifierProvider<RemoteAlbumNotifier, RemoteAlbumSta
|
|||||||
RemoteAlbumNotifier.new,
|
RemoteAlbumNotifier.new,
|
||||||
dependencies: [remoteAlbumServiceProvider],
|
dependencies: [remoteAlbumServiceProvider],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final albumsContainingAssetProvider = FutureProvider.family<List<RemoteAlbum>, String>(
|
||||||
|
(ref, assetId) => ref.watch(remoteAlbumServiceProvider).getAlbumsContainingAsset(assetId),
|
||||||
|
);
|
||||||
|
|||||||
@ -1,36 +1,30 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
|
|
||||||
final currentRemoteAlbumProvider = AutoDisposeNotifierProvider<CurrentAlbumNotifier, RemoteAlbum?>(
|
final currentRemoteAlbumScopedProvider = Provider<RemoteAlbum?>((ref) => null);
|
||||||
|
final currentRemoteAlbumProvider = NotifierProvider<CurrentAlbumNotifier, RemoteAlbum?>(
|
||||||
CurrentAlbumNotifier.new,
|
CurrentAlbumNotifier.new,
|
||||||
|
dependencies: [currentRemoteAlbumScopedProvider, remoteAlbumServiceProvider],
|
||||||
);
|
);
|
||||||
|
|
||||||
class CurrentAlbumNotifier extends AutoDisposeNotifier<RemoteAlbum?> {
|
class CurrentAlbumNotifier extends Notifier<RemoteAlbum?> {
|
||||||
KeepAliveLink? _keepAliveLink;
|
|
||||||
StreamSubscription<RemoteAlbum?>? _assetSubscription;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RemoteAlbum? build() => null;
|
RemoteAlbum? build() {
|
||||||
|
final album = ref.watch(currentRemoteAlbumScopedProvider);
|
||||||
|
|
||||||
void setAlbum(RemoteAlbum album) {
|
if (album == null) {
|
||||||
_keepAliveLink?.close();
|
return null;
|
||||||
_assetSubscription?.cancel();
|
}
|
||||||
state = album;
|
|
||||||
|
|
||||||
_assetSubscription = ref.watch(remoteAlbumServiceProvider).watchAlbum(album.id).listen((updatedAlbum) {
|
final watcher = ref.watch(remoteAlbumServiceProvider).watchAlbum(album.id).listen((updatedAlbum) {
|
||||||
if (updatedAlbum != null) {
|
if (updatedAlbum != null) {
|
||||||
state = updatedAlbum;
|
state = updatedAlbum;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_keepAliveLink = ref.keepAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispose() {
|
ref.onDispose(watcher.cancel);
|
||||||
_keepAliveLink?.close();
|
|
||||||
_assetSubscription?.cancel();
|
return album;
|
||||||
state = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -303,7 +303,7 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: DriftBackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DriftBackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: RemoteAlbumRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: RemoteAlbumRoute.page, guards: [_authGuard]),
|
||||||
AutoRoute(
|
AutoRoute(
|
||||||
page: AssetViewerRoute.page,
|
page: AssetViewerRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
|||||||
@ -448,6 +448,7 @@ class AssetViewerRoute extends PageRouteInfo<AssetViewerRouteArgs> {
|
|||||||
required int initialIndex,
|
required int initialIndex,
|
||||||
required TimelineService timelineService,
|
required TimelineService timelineService,
|
||||||
int? heroOffset,
|
int? heroOffset,
|
||||||
|
RemoteAlbum? currentAlbum,
|
||||||
List<PageRouteInfo>? children,
|
List<PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
AssetViewerRoute.name,
|
AssetViewerRoute.name,
|
||||||
@ -456,6 +457,7 @@ class AssetViewerRoute extends PageRouteInfo<AssetViewerRouteArgs> {
|
|||||||
initialIndex: initialIndex,
|
initialIndex: initialIndex,
|
||||||
timelineService: timelineService,
|
timelineService: timelineService,
|
||||||
heroOffset: heroOffset,
|
heroOffset: heroOffset,
|
||||||
|
currentAlbum: currentAlbum,
|
||||||
),
|
),
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
);
|
);
|
||||||
@ -471,6 +473,7 @@ class AssetViewerRoute extends PageRouteInfo<AssetViewerRouteArgs> {
|
|||||||
initialIndex: args.initialIndex,
|
initialIndex: args.initialIndex,
|
||||||
timelineService: args.timelineService,
|
timelineService: args.timelineService,
|
||||||
heroOffset: args.heroOffset,
|
heroOffset: args.heroOffset,
|
||||||
|
currentAlbum: args.currentAlbum,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -482,6 +485,7 @@ class AssetViewerRouteArgs {
|
|||||||
required this.initialIndex,
|
required this.initialIndex,
|
||||||
required this.timelineService,
|
required this.timelineService,
|
||||||
this.heroOffset,
|
this.heroOffset,
|
||||||
|
this.currentAlbum,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Key? key;
|
final Key? key;
|
||||||
@ -492,9 +496,11 @@ class AssetViewerRouteArgs {
|
|||||||
|
|
||||||
final int? heroOffset;
|
final int? heroOffset;
|
||||||
|
|
||||||
|
final RemoteAlbum? currentAlbum;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AssetViewerRouteArgs{key: $key, initialIndex: $initialIndex, timelineService: $timelineService, heroOffset: $heroOffset}';
|
return 'AssetViewerRouteArgs{key: $key, initialIndex: $initialIndex, timelineService: $timelineService, heroOffset: $heroOffset, currentAlbum: $currentAlbum}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -706,36 +712,78 @@ class DownloadInfoRoute extends PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [DriftActivitiesPage]
|
/// [DriftActivitiesPage]
|
||||||
class DriftActivitiesRoute extends PageRouteInfo<void> {
|
class DriftActivitiesRoute extends PageRouteInfo<DriftActivitiesRouteArgs> {
|
||||||
const DriftActivitiesRoute({List<PageRouteInfo>? children})
|
DriftActivitiesRoute({
|
||||||
: super(DriftActivitiesRoute.name, initialChildren: children);
|
Key? key,
|
||||||
|
required RemoteAlbum album,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
DriftActivitiesRoute.name,
|
||||||
|
args: DriftActivitiesRouteArgs(key: key, album: album),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
static const String name = 'DriftActivitiesRoute';
|
static const String name = 'DriftActivitiesRoute';
|
||||||
|
|
||||||
static PageInfo page = PageInfo(
|
static PageInfo page = PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const DriftActivitiesPage();
|
final args = data.argsAs<DriftActivitiesRouteArgs>();
|
||||||
|
return DriftActivitiesPage(key: args.key, album: args.album);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DriftActivitiesRouteArgs {
|
||||||
|
const DriftActivitiesRouteArgs({this.key, required this.album});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final RemoteAlbum album;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DriftActivitiesRouteArgs{key: $key, album: $album}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [DriftAlbumOptionsPage]
|
/// [DriftAlbumOptionsPage]
|
||||||
class DriftAlbumOptionsRoute extends PageRouteInfo<void> {
|
class DriftAlbumOptionsRoute extends PageRouteInfo<DriftAlbumOptionsRouteArgs> {
|
||||||
const DriftAlbumOptionsRoute({List<PageRouteInfo>? children})
|
DriftAlbumOptionsRoute({
|
||||||
: super(DriftAlbumOptionsRoute.name, initialChildren: children);
|
Key? key,
|
||||||
|
required RemoteAlbum album,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
DriftAlbumOptionsRoute.name,
|
||||||
|
args: DriftAlbumOptionsRouteArgs(key: key, album: album),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
static const String name = 'DriftAlbumOptionsRoute';
|
static const String name = 'DriftAlbumOptionsRoute';
|
||||||
|
|
||||||
static PageInfo page = PageInfo(
|
static PageInfo page = PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const DriftAlbumOptionsPage();
|
final args = data.argsAs<DriftAlbumOptionsRouteArgs>();
|
||||||
|
return DriftAlbumOptionsPage(key: args.key, album: args.album);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DriftAlbumOptionsRouteArgs {
|
||||||
|
const DriftAlbumOptionsRouteArgs({this.key, required this.album});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final RemoteAlbum album;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DriftAlbumOptionsRouteArgs{key: $key, album: $album}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [DriftAlbumsPage]
|
/// [DriftAlbumsPage]
|
||||||
class DriftAlbumsRoute extends PageRouteInfo<void> {
|
class DriftAlbumsRoute extends PageRouteInfo<void> {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
|||||||
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider;
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider;
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
@ -29,7 +28,6 @@ final deepLinkServiceProvider = Provider(
|
|||||||
// Below is used for beta timeline
|
// Below is used for beta timeline
|
||||||
ref.watch(timelineFactoryProvider),
|
ref.watch(timelineFactoryProvider),
|
||||||
ref.watch(beta_asset_provider.assetServiceProvider),
|
ref.watch(beta_asset_provider.assetServiceProvider),
|
||||||
ref.watch(currentRemoteAlbumProvider.notifier),
|
|
||||||
ref.watch(remoteAlbumServiceProvider),
|
ref.watch(remoteAlbumServiceProvider),
|
||||||
ref.watch(driftMemoryServiceProvider),
|
ref.watch(driftMemoryServiceProvider),
|
||||||
),
|
),
|
||||||
@ -46,7 +44,6 @@ class DeepLinkService {
|
|||||||
/// Used for beta timeline
|
/// Used for beta timeline
|
||||||
final TimelineFactory _betaTimelineFactory;
|
final TimelineFactory _betaTimelineFactory;
|
||||||
final beta_asset_service.AssetService _betaAssetService;
|
final beta_asset_service.AssetService _betaAssetService;
|
||||||
final CurrentAlbumNotifier _betaCurrentAlbumNotifier;
|
|
||||||
final RemoteAlbumService _betaRemoteAlbumService;
|
final RemoteAlbumService _betaRemoteAlbumService;
|
||||||
final DriftMemoryService _betaMemoryServiceProvider;
|
final DriftMemoryService _betaMemoryServiceProvider;
|
||||||
|
|
||||||
@ -58,7 +55,6 @@ class DeepLinkService {
|
|||||||
this._currentAlbum,
|
this._currentAlbum,
|
||||||
this._betaTimelineFactory,
|
this._betaTimelineFactory,
|
||||||
this._betaAssetService,
|
this._betaAssetService,
|
||||||
this._betaCurrentAlbumNotifier,
|
|
||||||
this._betaRemoteAlbumService,
|
this._betaRemoteAlbumService,
|
||||||
this._betaMemoryServiceProvider,
|
this._betaMemoryServiceProvider,
|
||||||
);
|
);
|
||||||
@ -176,7 +172,6 @@ class DeepLinkService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_betaCurrentAlbumNotifier.setAlbum(album);
|
|
||||||
return RemoteAlbumRoute(album: album);
|
return RemoteAlbumRoute(album: album);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Remove this when beta is default
|
// TODO: Remove this when beta is default
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => context.pushRoute(const DriftAlbumOptionsRoute()),
|
onTap: () => context.pushRoute(DriftAlbumOptionsRoute(album: currentAlbum)),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 50,
|
height: 50,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import 'package:immich_mobile/providers/infrastructure/current_album.provider.da
|
|||||||
import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
|
||||||
import 'package:immich_mobile/widgets/album/remote_album_shared_user_icons.dart';
|
import 'package:immich_mobile/widgets/album/remote_album_shared_user_icons.dart';
|
||||||
|
|
||||||
class RemoteAlbumSliverAppBar extends ConsumerStatefulWidget {
|
class RemoteAlbumSliverAppBar extends ConsumerStatefulWidget {
|
||||||
@ -89,7 +88,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
|
|||||||
color: actionIconColor,
|
color: actionIconColor,
|
||||||
shadows: actionIconShadows,
|
shadows: actionIconShadows,
|
||||||
),
|
),
|
||||||
onPressed: () => context.navigateTo(const TabShellRoute(children: [DriftAlbumsRoute()])),
|
onPressed: () => context.maybePop(),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
if (widget.onToggleAlbumOrder != null)
|
if (widget.onToggleAlbumOrder != null)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user