1
0
mirror of https://github.com/immich-app/immich.git synced 2025-08-10 23:22:22 +02:00

feat(album): album view sort order (#14648)

* feat(mobile): album view sort order

* feat: add error message

* refactor(mobile): album page (#14659)

* refactor album page

* update lint rule

* const record

* fix: updating sort order when pull to refresh

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>

* Move sort toggle button to bottom sheet menu

* chore: revert multiselectgrid loading status

* chore: revert multiselectgrid loading status

---------

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
This commit is contained in:
Alex
2024-12-16 10:11:48 -06:00
committed by GitHub
parent 364b717fde
commit 9503bf479b
32 changed files with 711 additions and 336 deletions

View File

@@ -1,22 +1,21 @@
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/build_context_extensions.dart';
import 'package:immich_mobile/providers/activity_statistics.provider.dart';
import 'package:immich_mobile/providers/album/album.provider.dart';
import 'package:immich_mobile/providers/album/album_viewer.provider.dart';
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
import 'package:immich_mobile/providers/album/current_album.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
class AlbumViewerAppbar extends HookConsumerWidget
implements PreferredSizeWidget {
const AlbumViewerAppbar({
super.key,
required this.album,
required this.userId,
required this.titleFocusNode,
this.onAddPhotos,
@@ -24,34 +23,48 @@ class AlbumViewerAppbar extends HookConsumerWidget
required this.onActivities,
});
final Album album;
final String userId;
final FocusNode titleFocusNode;
final Function(Album album)? onAddPhotos;
final Function(Album album)? onAddUsers;
final Function(Album album) onActivities;
final void Function()? onAddPhotos;
final void Function()? onAddUsers;
final void Function() onActivities;
@override
Widget build(BuildContext context, WidgetRef ref) {
final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText;
final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum;
final isProcessing = useProcessingOverlay();
final albumState = useState(ref.read(currentAlbumProvider));
final album = albumState.value;
ref.listen(currentAlbumProvider, (_, newAlbum) {
final oldAlbum = albumState.value;
if (oldAlbum != null && newAlbum != null && oldAlbum.id == newAlbum.id) {
return;
}
albumState.value = newAlbum;
});
if (album == null) {
return const SizedBox();
}
final albumViewer = ref.watch(albumViewerProvider);
final newAlbumTitle = albumViewer.editTitleText;
final isEditAlbum = albumViewer.isEditAlbum;
final comments = album.shared
? ref.watch(activityStatisticsProvider(album.remoteId!))
: 0;
deleteAlbum() async {
isProcessing.value = true;
final bool success =
await ref.watch(albumProvider.notifier).deleteAlbum(album);
final bool success;
if (album.shared) {
success = await ref.watch(albumProvider.notifier).deleteAlbum(album);
context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()]));
} else {
success = await ref.watch(albumProvider.notifier).deleteAlbum(album);
context
.navigateTo(const TabControllerRoute(children: [LibraryRoute()]));
}
if (!success) {
ImmichToast.show(
context: context,
@@ -60,11 +73,9 @@ class AlbumViewerAppbar extends HookConsumerWidget
gravity: ToastGravity.BOTTOM,
);
}
isProcessing.value = false;
}
Future<void> showConfirmationDialog() async {
Future<void> onDeleteAlbumPressed() {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
@@ -102,13 +113,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
);
}
void onDeleteAlbumPressed() async {
showConfirmationDialog();
}
void onLeaveAlbumPressed() async {
isProcessing.value = true;
bool isSuccess =
await ref.watch(albumProvider.notifier).leaveAlbum(album);
@@ -123,8 +128,6 @@ class AlbumViewerAppbar extends HookConsumerWidget
gravity: ToastGravity.BOTTOM,
);
}
isProcessing.value = false;
}
buildBottomSheetActions() {
@@ -136,7 +139,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
'album_viewer_appbar_share_delete',
style: TextStyle(fontWeight: FontWeight.w500),
).tr(),
onTap: () => onDeleteAlbumPressed(),
onTap: onDeleteAlbumPressed,
)
: ListTile(
leading: const Icon(Icons.person_remove_rounded),
@@ -144,25 +147,52 @@ class AlbumViewerAppbar extends HookConsumerWidget
'album_viewer_appbar_share_leave',
style: TextStyle(fontWeight: FontWeight.w500),
).tr(),
onTap: () => onLeaveAlbumPressed(),
onTap: onLeaveAlbumPressed,
),
];
// }
}
void onSortOrderToggled() async {
final updatedAlbum =
await ref.read(albumProvider.notifier).toggleSortOrder(album);
if (updatedAlbum == null) {
ImmichToast.show(
context: context,
msg: "error_change_sort_album".tr(),
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
}
context.pop();
}
void buildBottomSheet() {
final ownerActions = [
ListTile(
leading: const Icon(Icons.person_add_alt_rounded),
onTap: () {
context.pop();
onAddUsers!(album);
final onAddUsers = this.onAddUsers;
if (onAddUsers != null) {
onAddUsers();
}
},
title: const Text(
"album_viewer_page_share_add_users",
style: TextStyle(fontWeight: FontWeight.w500),
).tr(),
),
ListTile(
leading: const Icon(Icons.swap_vert_rounded),
onTap: onSortOrderToggled,
title: const Text(
"change_display_order",
style: TextStyle(fontWeight: FontWeight.w500),
).tr(),
),
ListTile(
leading: const Icon(Icons.share_rounded),
onTap: () {
@@ -176,7 +206,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
),
ListTile(
leading: const Icon(Icons.settings_rounded),
onTap: () => context.navigateTo(AlbumOptionsRoute(album: album)),
onTap: () => context.navigateTo(AlbumOptionsRoute()),
title: const Text(
"translated_text_options",
style: TextStyle(fontWeight: FontWeight.w500),
@@ -189,7 +219,10 @@ class AlbumViewerAppbar extends HookConsumerWidget
leading: const Icon(Icons.add_photo_alternate_outlined),
onTap: () {
context.pop();
onAddPhotos!(album);
final onAddPhotos = this.onAddPhotos;
if (onAddPhotos != null) {
onAddPhotos();
}
},
title: const Text(
"share_add_photos",
@@ -222,9 +255,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
Widget buildActivitiesButton() {
return IconButton(
onPressed: () {
onActivities(album);
},
onPressed: onActivities,
icon: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@@ -271,7 +302,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
);
} else {
return IconButton(
onPressed: () async => await context.maybePop(),
onPressed: context.maybePop,
icon: const Icon(Icons.arrow_back_ios_rounded),
splashRadius: 25,
);
@@ -285,12 +316,13 @@ class AlbumViewerAppbar extends HookConsumerWidget
actions: [
if (album.shared && (album.activityEnabled || comments != 0))
buildActivitiesButton(),
if (album.isRemote)
if (album.isRemote) ...[
IconButton(
splashRadius: 25,
onPressed: buildBottomSheet,
icon: const Icon(Icons.more_horiz_rounded),
),
],
],
);
}

View File

@@ -4,20 +4,19 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/album/album_viewer.provider.dart';
import 'package:immich_mobile/entities/album.entity.dart';
class AlbumViewerEditableTitle extends HookConsumerWidget {
final Album album;
final String albumName;
final FocusNode titleFocusNode;
const AlbumViewerEditableTitle({
super.key,
required this.album,
required this.albumName,
required this.titleFocusNode,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final titleTextEditController = useTextEditingController(text: album.name);
final titleTextEditController = useTextEditingController(text: albumName);
void onFocusModeChange() {
if (!titleFocusNode.hasFocus && titleTextEditController.text.isEmpty) {
@@ -51,7 +50,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
onTap: () {
context.focusScope.requestFocus(titleFocusNode);
ref.watch(albumViewerProvider.notifier).setEditTitleText(album.name);
ref.watch(albumViewerProvider.notifier).setEditTitleText(albumName);
ref.watch(albumViewerProvider.notifier).enableEditAlbum();
if (titleTextEditController.text == 'Untitled') {