From 0f179b9914f3321faea6c0fa6b878bcb94ebf99d Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Sep 2026 15:20:54 -0400 Subject: [PATCH] fix(web): album inconsistencies (#31857) --- .../lib/components/album-page/AlbumsList.svelte | 9 +++------ web/src/lib/services/album.service.ts | 10 ++++++++-- .../[[photos=photos]]/[[assetId=id]]/+page.svelte | 14 +++++++++----- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/web/src/lib/components/album-page/AlbumsList.svelte b/web/src/lib/components/album-page/AlbumsList.svelte index a4c6af76ba..0cbdaa62b5 100644 --- a/web/src/lib/components/album-page/AlbumsList.svelte +++ b/web/src/lib/components/album-page/AlbumsList.svelte @@ -3,7 +3,7 @@ import AlbumsTable from '$lib/components/album-page/AlbumsTable.svelte'; import OnEvents from '$lib/components/OnEvents.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; - import { handleDeleteAlbum } from '$lib/services/album.service'; + import { handleDeleteAlbum, isAlbumOwner } from '$lib/services/album.service'; import { AlbumFilter, AlbumGroupBy, @@ -15,7 +15,7 @@ } from '$lib/stores/preferences.store'; import { getSelectedAlbumGroupOption, sortAlbums, stringToSortOrder, type AlbumGroup } from '$lib/utils/album-utils'; import { normalizeSearchString } from '$lib/utils/string-utils'; - import { AlbumUserRole, type AlbumResponseDto, type SharedLinkResponseDto } from '@immich/sdk'; + import { type AlbumResponseDto, type SharedLinkResponseDto } from '@immich/sdk'; import { groupBy } from 'lodash-es'; import { onMount, type Snippet } from 'svelte'; import { t } from 'svelte-i18n'; @@ -123,10 +123,7 @@ return sharedAlbums; } default: { - const nonOwnedAlbums = sharedAlbums.filter( - (album) => - album.albumUsers.find(({ user: { id } }) => id === authManager.user.id)?.role !== AlbumUserRole.Owner, - ); + const nonOwnedAlbums = sharedAlbums.filter((album) => !isAlbumOwner(album)); return nonOwnedAlbums.length > 0 ? ownedAlbums.concat(nonOwnedAlbums) : ownedAlbums; } } diff --git a/web/src/lib/services/album.service.ts b/web/src/lib/services/album.service.ts index c021b02771..dfda3c2786 100644 --- a/web/src/lib/services/album.service.ts +++ b/web/src/lib/services/album.service.ts @@ -46,6 +46,11 @@ import { openFileUploadDialog } from '$lib/utils/file-uploader'; import { handleError } from '$lib/utils/handle-error'; import { getFormatter } from '$lib/utils/i18n'; +export const isAlbumOwner = (album: AlbumResponseDto) => album.albumUsers[0].user.id === authManager.user.id; +export const isAlbumEditor = (album: AlbumResponseDto) => + isAlbumOwner(album) || + album.albumUsers.find(({ user: { id } }) => id === authManager.user.id)?.role === AlbumUserRole.Editor; + export const getAlbumsActions = ($t: MessageFormatter) => { const Create: ActionItem = { title: $t('create_album'), @@ -57,7 +62,8 @@ export const getAlbumsActions = ($t: MessageFormatter) => { }; export const getAlbumActions = ($t: MessageFormatter, album: AlbumResponseDto) => { - const isOwned = album.albumUsers[0].user.id === authManager.user.id; + const isOwned = isAlbumOwner(album); + const isEditor = isAlbumEditor(album); const AddUsers: ActionItem = { title: $t('invite_people'), @@ -90,7 +96,7 @@ export const getAlbumActions = ($t: MessageFormatter, album: AlbumResponseDto) = const Edit: ActionItem = { title: $t('edit_album'), icon: mdiRenameOutline, - $if: () => isOwned, + $if: () => isEditor, onAction: () => modalManager.show(AlbumEditModal, { album }), }; diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index fce64dbdbf..be03c3761f 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -35,7 +35,13 @@ import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; import { Route } from '$lib/route'; - import { getAlbumActions, getAlbumAssetsActions, handleDeleteAlbum } from '$lib/services/album.service'; + import { + getAlbumActions, + getAlbumAssetsActions, + handleDeleteAlbum, + isAlbumEditor, + isAlbumOwner, + } from '$lib/services/album.service'; import { getGlobalActions } from '$lib/services/app.service'; import { getAssetBulkActions } from '$lib/services/asset.service'; import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store'; @@ -235,16 +241,14 @@ onDestroy(() => activityManager.reset()); - const isOwned = $derived(album.albumUsers[0].user.id === authManager.user.id); + const isOwned = $derived(isAlbumOwner(album)); let showActivityStatus = $derived( album.albumUsers.length > 1 && !assetViewerManager.isViewing && (album.isActivityEnabled || activityManager.commentCount > 0), ); - const isEditor = $derived( - album.albumUsers.find(({ user: { id } }) => id === authManager.user.id)?.role === AlbumUserRole.Editor || isOwned, - ); + const isEditor = $derived(isAlbumEditor(album)); const isSelectionMode = $derived( viewMode === AlbumPageViewMode.SELECT_ASSETS ? true : viewMode === AlbumPageViewMode.SELECT_THUMBNAIL,