mirror of
https://github.com/immich-app/immich.git
synced 2025-01-04 02:46:47 +01:00
267 lines
9.1 KiB
Dart
267 lines
9.1 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:auto_route/auto_route.dart';
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
|
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
|
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
|
import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';
|
|
import 'package:immich_mobile/widgets/album/album_viewer_editable_title.dart';
|
|
import 'package:immich_mobile/providers/multiselect.provider.dart';
|
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
|
import 'package:immich_mobile/widgets/album/album_viewer_appbar.dart';
|
|
import 'package:immich_mobile/routing/router.dart';
|
|
import 'package:immich_mobile/entities/album.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/common/immich_toast.dart';
|
|
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
|
|
|
|
@RoutePage()
|
|
class AlbumViewerPage extends HookConsumerWidget {
|
|
final int albumId;
|
|
|
|
const AlbumViewerPage({super.key, required this.albumId});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
FocusNode titleFocusNode = useFocusNode();
|
|
final album = ref.watch(albumWatcher(albumId));
|
|
// Listen provider to prevent autoDispose when navigating to other routes from within the viewer page
|
|
ref.listen(currentAlbumProvider, (_, __) {});
|
|
album.whenData(
|
|
(value) => Future.microtask(
|
|
() => ref.read(currentAlbumProvider.notifier).set(value),
|
|
),
|
|
);
|
|
final userId = ref.watch(authProvider).userId;
|
|
final isProcessing = useProcessingOverlay();
|
|
|
|
Future<bool> onRemoveFromAlbumPressed(Iterable<Asset> assets) async {
|
|
final a = album.valueOrNull;
|
|
final bool isSuccess = a != null &&
|
|
await ref.read(albumProvider.notifier).removeAsset(a, assets);
|
|
|
|
if (!isSuccess) {
|
|
ImmichToast.show(
|
|
context: context,
|
|
msg: "album_viewer_appbar_share_err_remove".tr(),
|
|
toastType: ToastType.error,
|
|
gravity: ToastGravity.BOTTOM,
|
|
);
|
|
}
|
|
return isSuccess;
|
|
}
|
|
|
|
/// Find out if the assets in album exist on the device
|
|
/// If they exist, add to selected asset state to show they are already selected.
|
|
void onAddPhotosPressed(Album albumInfo) async {
|
|
AssetSelectionPageResult? returnPayload =
|
|
await context.pushRoute<AssetSelectionPageResult?>(
|
|
AlbumAssetSelectionRoute(
|
|
existingAssets: albumInfo.assets,
|
|
canDeselect: false,
|
|
query: getRemoteAssetQuery(ref),
|
|
),
|
|
);
|
|
|
|
if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) {
|
|
// Check if there is new assets add
|
|
isProcessing.value = true;
|
|
|
|
await ref.watch(albumProvider.notifier).addAssets(
|
|
albumInfo,
|
|
returnPayload.selectedAssets,
|
|
);
|
|
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
void onAddUsersPressed(Album album) async {
|
|
List<String>? sharedUserIds = await context.pushRoute<List<String>?>(
|
|
AlbumAdditionalSharedUserSelectionRoute(album: album),
|
|
);
|
|
|
|
if (sharedUserIds != null) {
|
|
isProcessing.value = true;
|
|
|
|
await ref.watch(albumProvider.notifier).addUsers(album, sharedUserIds);
|
|
|
|
isProcessing.value = false;
|
|
}
|
|
}
|
|
|
|
Widget buildControlButton(Album album) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 16),
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: ListView(
|
|
scrollDirection: Axis.horizontal,
|
|
children: [
|
|
AlbumActionFilledButton(
|
|
iconData: Icons.add_photo_alternate_outlined,
|
|
onPressed: () => onAddPhotosPressed(album),
|
|
labelText: "share_add_photos".tr(),
|
|
),
|
|
if (userId == album.ownerId)
|
|
AlbumActionFilledButton(
|
|
iconData: Icons.person_add_alt_rounded,
|
|
onPressed: () => onAddUsersPressed(album),
|
|
labelText: "album_viewer_page_share_add_users".tr(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildTitle(Album album) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: 8, right: 8),
|
|
child: userId == album.ownerId && album.isRemote
|
|
? AlbumViewerEditableTitle(
|
|
album: album,
|
|
titleFocusNode: titleFocusNode,
|
|
)
|
|
: Padding(
|
|
padding: const EdgeInsets.only(left: 8.0),
|
|
child: Text(
|
|
album.name,
|
|
style: context.textTheme.headlineMedium,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildAlbumDateRange(Album album) {
|
|
final DateTime? startDate = album.startDate;
|
|
final DateTime? endDate = album.endDate;
|
|
|
|
if (startDate == null || endDate == null) {
|
|
return const SizedBox();
|
|
}
|
|
|
|
final String dateRangeText;
|
|
if (startDate.day == endDate.day &&
|
|
startDate.month == endDate.month &&
|
|
startDate.year == endDate.year) {
|
|
dateRangeText = DateFormat.yMMMd().format(startDate);
|
|
} else {
|
|
final String startDateText = (startDate.year == endDate.year
|
|
? DateFormat.MMMd()
|
|
: DateFormat.yMMMd())
|
|
.format(startDate);
|
|
final String endDateText = DateFormat.yMMMd().format(endDate);
|
|
dateRangeText = "$startDateText - $endDateText";
|
|
}
|
|
|
|
return Padding(
|
|
padding: EdgeInsets.only(
|
|
left: 16.0,
|
|
bottom: album.shared ? 0.0 : 8.0,
|
|
),
|
|
child: Text(
|
|
dateRangeText,
|
|
style: context.textTheme.labelLarge,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildSharedUserIconsRow(Album album) {
|
|
return album.sharedUsers.isNotEmpty
|
|
? GestureDetector(
|
|
onTap: () => context.pushRoute(AlbumOptionsRoute(album: album)),
|
|
child: SizedBox(
|
|
height: 50,
|
|
child: ListView.builder(
|
|
padding: const EdgeInsets.only(left: 16),
|
|
scrollDirection: Axis.horizontal,
|
|
itemBuilder: ((context, index) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 8.0),
|
|
child: UserCircleAvatar(
|
|
user: album.sharedUsers.toList()[index],
|
|
radius: 18,
|
|
size: 36,
|
|
),
|
|
);
|
|
}),
|
|
itemCount: album.sharedUsers.length,
|
|
),
|
|
),
|
|
)
|
|
: const SizedBox.shrink();
|
|
}
|
|
|
|
Widget buildHeader(Album album) {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildTitle(album),
|
|
if (album.assets.isNotEmpty == true) buildAlbumDateRange(album),
|
|
buildSharedUserIconsRow(album),
|
|
],
|
|
);
|
|
}
|
|
|
|
onActivitiesPressed(Album album) {
|
|
if (album.remoteId != null) {
|
|
context.pushRoute(
|
|
const ActivitiesRoute(),
|
|
);
|
|
}
|
|
}
|
|
|
|
return Scaffold(
|
|
body: Stack(
|
|
children: [
|
|
album.widgetWhen(
|
|
onData: (albumInfo) => MultiselectGrid(
|
|
renderListProvider: albumRenderlistProvider(albumId),
|
|
topWidget: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildHeader(albumInfo),
|
|
if (albumInfo.isRemote) buildControlButton(albumInfo),
|
|
],
|
|
),
|
|
onRemoveFromAlbum: onRemoveFromAlbumPressed,
|
|
editEnabled: albumInfo.ownerId == userId,
|
|
),
|
|
),
|
|
AnimatedPositioned(
|
|
duration: const Duration(milliseconds: 300),
|
|
top: ref.watch(multiselectProvider)
|
|
? -(kToolbarHeight + context.padding.top)
|
|
: 0,
|
|
left: 0,
|
|
right: 0,
|
|
child: album.when(
|
|
data: (data) => AlbumViewerAppbar(
|
|
titleFocusNode: titleFocusNode,
|
|
album: data,
|
|
userId: userId,
|
|
onAddPhotos: onAddPhotosPressed,
|
|
onAddUsers: onAddUsersPressed,
|
|
onActivities: onActivitiesPressed,
|
|
),
|
|
error: (error, stackTrace) => AppBar(title: const Text("Error")),
|
|
loading: () => AppBar(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|