From d61569faf67cd28ddf9e8171a2bbe62edd909889 Mon Sep 17 00:00:00 2001 From: bwees Date: Fri, 26 Sep 2025 22:14:57 -0500 Subject: [PATCH] fix: shared album control permissions --- .../pages/drift_remote_album.page.dart | 20 ++++++----- .../asset_viewer/bottom_bar.widget.dart | 2 +- .../asset_viewer/bottom_sheet.widget.dart | 7 ++-- .../asset_viewer/top_app_bar.widget.dart | 3 +- .../remote_album_bottom_sheet.widget.dart | 34 +++++++++++-------- 5 files changed, 39 insertions(+), 27 deletions(-) diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index e0fe5ee62b..e6f1bf108e 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -201,14 +201,18 @@ class _RemoteAlbumPageState extends ConsumerState { await toggleAlbumOrder(); context.pop(); }, - onEditAlbum: () async { - context.pop(); - await showEditTitleAndDescription(context); - }, - onCreateSharedLink: () async { - context.pop(); - context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); - }, + onEditAlbum: isOwner + ? () async { + context.pop(); + await showEditTitleAndDescription(context); + } + : null, + onCreateSharedLink: isOwner + ? () async { + context.pop(); + context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); + } + : null, onShowOptions: () { context.pop(); context.pushRoute(const DriftAlbumOptionsRoute()); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 3111512823..44660e440c 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -43,7 +43,7 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), - if (asset.type == AssetType.image) const EditImageActionButton(), + if (asset.type == AssetType.image && isOwner) const EditImageActionButton(), if (isOwner) ...[ if (asset.hasRemote && isOwner && isArchived) const UnArchiveActionButton(source: ActionSource.viewer) diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 2586789beb..3dd8f994bd 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -140,6 +140,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull; final cameraTitle = _getCameraInfoTitle(exifInfo); + final isOwner = ref.watch(currentUserProvider)?.id == (asset is RemoteAsset ? asset.ownerId : null); return SliverList.list( children: [ @@ -147,10 +148,10 @@ class _AssetDetailBottomSheet extends ConsumerWidget { _SheetTile( title: _getDateTime(context, asset), titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), - trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null, - onTap: asset.hasRemote ? () async => await _editDateTime(context, ref) : null, + trailing: asset.hasRemote && isOwner ? const Icon(Icons.edit, size: 18) : null, + onTap: asset.hasRemote && isOwner ? () async => await _editDateTime(context, ref) : null, ), - if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), + if (exifInfo != null && isOwner) _SheetAssetDescription(exif: exifInfo), const SheetPeopleDetails(), const SheetLocationDetails(), // Details header diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index 10c9821eb0..3d0361b536 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -44,7 +44,8 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final showViewInTimelineButton = (previousRouteName != TabShellRoute.name || tabRoute == TabEnum.search) && previousRouteName != AssetViewerRoute.name && - previousRouteName != null; + previousRouteName != null && + isOwner; final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index 0ab419a56b..638f860fde 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -24,6 +24,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_shee import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class RemoteAlbumBottomSheet extends ConsumerStatefulWidget { @@ -53,6 +54,7 @@ class _RemoteAlbumBottomSheetState extends ConsumerState Widget build(BuildContext context) { final multiselect = ref.watch(multiSelectProvider); final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + final ownsAlbum = ref.watch(currentUserProvider)?.id == widget.album.ownerId; Future addAssetsToAlbum(RemoteAlbum album) async { final selectedAssets = multiselect.selectedAssets; @@ -93,28 +95,32 @@ class _RemoteAlbumBottomSheetState extends ConsumerState const ShareActionButton(source: ActionSource.timeline), if (multiselect.hasRemote) ...[ const ShareLinkActionButton(source: ActionSource.timeline), - const ArchiveActionButton(source: ActionSource.timeline), - const FavoriteActionButton(source: ActionSource.timeline), + if (ownsAlbum) const ArchiveActionButton(source: ActionSource.timeline), + if (ownsAlbum) const FavoriteActionButton(source: ActionSource.timeline), const DownloadActionButton(source: ActionSource.timeline), - isTrashEnable - ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(source: ActionSource.timeline), - const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton(source: ActionSource.timeline), - if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (ownsAlbum) + isTrashEnable + ? const TrashActionButton(source: ActionSource.timeline) + : const DeletePermanentActionButton(source: ActionSource.timeline), + if (ownsAlbum) const EditDateTimeActionButton(source: ActionSource.timeline), + if (ownsAlbum) const EditLocationActionButton(source: ActionSource.timeline), + if (ownsAlbum) const MoveToLockFolderActionButton(source: ActionSource.timeline), + if (multiselect.selectedAssets.length > 1 && ownsAlbum) + const StackActionButton(source: ActionSource.timeline), if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), const UploadActionButton(source: ActionSource.timeline), ], - RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: widget.album.id), - ], - slivers: [ - const AddToAlbumHeader(), - AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), + if (ownsAlbum) RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: widget.album.id), ], + slivers: ownsAlbum + ? [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), + ] + : null, ); } }