Skip to content

Commit

Permalink
fix(web): disable metadata edit if user is not owner (immich-app#5415)
Browse files Browse the repository at this point in the history
* fix(web): disable metadata edit if user is not owner

* pr feedback

* pr feedback

* get data from page data

* fix: better representation

* feat: warn user if there's issues with the selected assets

---------

Co-authored-by: Alex Tran <[email protected]>
  • Loading branch information
martabal and alextran1502 authored Dec 1, 2023
1 parent 5a50d32 commit ec92608
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 53 deletions.
2 changes: 1 addition & 1 deletion web/src/lib/components/album-page/album-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
<main
class="relative h-screen overflow-hidden bg-immich-bg px-6 pt-[var(--navbar-height)] dark:bg-immich-dark-bg sm:px-12 md:px-24 lg:px-40"
>
<AssetGrid {album} {user} {assetStore} {assetInteractionStore}>
<AssetGrid {album} {assetStore} {assetInteractionStore}>
<section class="pt-24">
<!-- ALBUM TITLE -->
<p
Expand Down
11 changes: 5 additions & 6 deletions web/src/lib/components/asset-viewer/asset-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
AssetTypeEnum,
ReactionType,
SharedLinkResponseDto,
UserResponseDto,
} from '@api';
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import { fly } from 'svelte/transition';
Expand Down Expand Up @@ -42,6 +41,7 @@
import { updateNumberOfComments } from '$lib/stores/activity.store';
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
import SlideshowBar from './slideshow-bar.svelte';
import { user } from '$lib/stores/user.store';
export let assetStore: AssetStore | null = null;
export let asset: AssetResponseDto;
Expand All @@ -51,7 +51,6 @@
export let force = false;
export let withStacked = false;
export let isShared = false;
export let user: UserResponseDto | null = null;
export let album: AlbumResponseDto | null = null;
let reactions: ActivityResponseDto[] = [];
Expand Down Expand Up @@ -143,10 +142,10 @@
};
const getFavorite = async () => {
if (album && user) {
if (album && $user) {
try {
const { data } = await api.activityApi.getActivities({
userId: user.id,
userId: $user.id,
assetId: asset.id,
albumId: album.id,
type: ReactionType.Like,
Expand Down Expand Up @@ -743,15 +742,15 @@
</div>
{/if}

{#if isShared && album && isShowActivity && user}
{#if isShared && album && isShowActivity && $user}
<div
transition:fly={{ duration: 150 }}
id="activity-panel"
class="z-[1002] row-start-1 row-span-5 w-[360px] md:w-[460px] overflow-y-auto bg-immich-bg transition-all dark:border-l dark:border-l-immich-dark-gray dark:bg-immich-dark-bg pl-4"
translate="yes"
>
<ActivityViewer
{user}
user={$user}
disabled={!album.isActivityEnabled}
assetType={asset.type}
albumOwnerId={album.ownerId}
Expand Down
42 changes: 26 additions & 16 deletions web/src/lib/components/asset-viewer/detail-panel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import { AppRoute } from '$lib/constants';
import ChangeLocation from '../shared-components/change-location.svelte';
import { handleError } from '../../utils/handle-error';
import { user } from '$lib/stores/user.store';
export let asset: AssetResponseDto;
export let albums: AlbumResponseDto[] = [];
Expand Down Expand Up @@ -238,12 +239,14 @@
zone: asset.exifInfo.timeZone ?? undefined,
})}
<div
class="flex justify-between place-items-start gap-4 py-4 hover:dark:text-immich-dark-primary hover:text-immich-primary cursor-pointer"
on:click={() => (isShowChangeDate = true)}
on:keydown={(event) => event.key === 'Enter' && (isShowChangeDate = true)}
class="flex justify-between place-items-start gap-4 py-4"
tabindex="0"
role="button"
title="Edit date"
on:click={() => (isOwner ? (isShowChangeDate = true) : null)}
on:keydown={(event) => (isOwner ? event.key === 'Enter' && (isShowChangeDate = true) : null)}
title={isOwner ? 'Edit date' : ''}
class:hover:dark:text-immich-dark-primary={isOwner}
class:hover:text-immich-primary={isOwner}
>
<div class="flex gap-4">
<div>
Expand Down Expand Up @@ -276,11 +279,14 @@
</div>
</div>
</div>
<button class="focus:outline-none">
<Icon path={mdiPencil} size="20" />
</button>

{#if isOwner}
<button class="focus:outline-none">
<Icon path={mdiPencil} size="20" />
</button>
{/if}
</div>
{:else if !asset.exifInfo?.dateTimeOriginal && !asset.isReadOnly}
{:else if !asset.exifInfo?.dateTimeOriginal && !asset.isReadOnly && $user && asset.ownerId === $user.id}
<div class="flex justify-between place-items-start gap-4 py-4">
<div class="flex gap-4">
<div>
Expand Down Expand Up @@ -410,12 +416,14 @@

{#if asset.exifInfo?.city && !asset.isReadOnly}
<div
class="flex justify-between place-items-start gap-4 py-4 hover:dark:text-immich-dark-primary hover:text-immich-primary cursor-pointer"
on:click={() => (isShowChangeLocation = true)}
on:keydown={(event) => event.key === 'Enter' && (isShowChangeLocation = true)}
class="flex justify-between place-items-start gap-4 py-4"
on:click={() => (isOwner ? (isShowChangeLocation = true) : null)}
on:keydown={(event) => (isOwner ? event.key === 'Enter' && (isShowChangeLocation = true) : null)}
tabindex="0"
title={isOwner ? 'Edit location' : ''}
role="button"
title="Edit location"
class:hover:dark:text-immich-dark-primary={isOwner}
class:hover:text-immich-primary={isOwner}
>
<div class="flex gap-4">
<div><Icon path={mdiMapMarkerOutline} size="24" /></div>
Expand All @@ -435,11 +443,13 @@
</div>
</div>

<div>
<Icon path={mdiPencil} size="20" />
</div>
{#if isOwner}
<div>
<Icon path={mdiPencil} size="20" />
</div>
{/if}
</div>
{:else if !asset.exifInfo?.city && !asset.isReadOnly}
{:else if !asset.exifInfo?.city && !asset.isReadOnly && $user && asset.ownerId === $user.id}
<div
class="flex justify-between place-items-start gap-4 py-4 rounded-lg pr-2 hover:dark:text-immich-dark-primary hover:text-immich-primary"
on:click={() => (isShowChangeLocation = true)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
import { DateTime } from 'luxon';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import { user } from '$lib/stores/user.store';
import { getSelectedAssets } from '$lib/utils/asset-utils';
export let menuItem = false;
const { clearSelect, getOwnedAssets } = getAssetControlContext();
let isShowChangeDate = false;
const handleConfirm = async (dateTimeOriginal: string) => {
isShowChangeDate = false;
const ids = Array.from(getOwnedAssets())
.filter((a) => !a.isExternal)
.map((a) => a.id);
const ids = getSelectedAssets(getOwnedAssets(), $user);
try {
await api.assetApi.updateAssets({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import ChangeLocation from '$lib/components/shared-components/change-location.svelte';
import { handleError } from '../../../utils/handle-error';
import { user } from '$lib/stores/user.store';
import { getSelectedAssets } from '$lib/utils/asset-utils';
export let menuItem = false;
const { clearSelect, getOwnedAssets } = getAssetControlContext();
Expand All @@ -12,9 +14,7 @@
async function handleConfirm(point: { lng: number; lat: number }) {
isShowChangeLocation = false;
const ids = Array.from(getOwnedAssets())
.filter((a) => !a.isExternal)
.map((a) => a.id);
const ids = getSelectedAssets(getOwnedAssets(), $user);
try {
await api.assetApi.updateAssets({
Expand Down
4 changes: 1 addition & 3 deletions web/src/lib/components/photos-page/asset-grid.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { locale } from '$lib/stores/preferences.store';
import { isSearchEnabled } from '$lib/stores/search.store';
import { formatGroupTitle, splitBucketIntoDateGroups } from '$lib/utils/timeline-util';
import type { AlbumResponseDto, AssetResponseDto, UserResponseDto } from '@api';
import type { AlbumResponseDto, AssetResponseDto } from '@api';
import { DateTime } from 'luxon';
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import AssetViewer from '../asset-viewer/asset-viewer.svelte';
Expand All @@ -27,7 +27,6 @@
export let removeAction: AssetAction | null = null;
export let withStacked = false;
export let isShared = false;
export let user: UserResponseDto | null = null;
export let album: AlbumResponseDto | null = null;
$: isTrashEnabled = $featureFlags.loaded && $featureFlags.trash;
Expand Down Expand Up @@ -394,7 +393,6 @@
<Portal target="body">
{#if $showAssetViewer}
<AssetViewer
{user}
{withStacked}
{assetStore}
asset={$viewingAsset}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { goto } from '$app/navigation';
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
import { downloadArchive } from '$lib/utils/asset-utils';
import { api, AssetResponseDto, SharedLinkResponseDto, UserResponseDto } from '@api';
import { api, AssetResponseDto, SharedLinkResponseDto } from '@api';
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import DownloadAction from '../photos-page/actions/download-action.svelte';
Expand All @@ -17,7 +17,6 @@
export let sharedLink: SharedLinkResponseDto;
export let isOwned: boolean;
export let user: UserResponseDto | undefined = undefined;
let selectedAssets: Set<AssetResponseDto> = new Set();
Expand Down Expand Up @@ -103,6 +102,6 @@
</ControlAppBar>
{/if}
<section class="my-[160px] flex flex-col px-6 sm:px-12 md:px-24 lg:px-40">
<GalleryViewer {user} {assets} bind:selectedAssets />
<GalleryViewer {assets} bind:selectedAssets />
</section>
</section>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { page } from '$app/stores';
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
import { handleError } from '$lib/utils/handle-error';
import { AssetResponseDto, ThumbnailFormat, UserResponseDto } from '@api';
import { AssetResponseDto, ThumbnailFormat } from '@api';
import AssetViewer from '../../asset-viewer/asset-viewer.svelte';
import { flip } from 'svelte/animate';
import { getThumbnailSize } from '$lib/utils/thumbnail-util';
Expand All @@ -13,7 +13,6 @@
export let selectedAssets: Set<AssetResponseDto> = new Set();
export let disableAssetSelect = false;
export let showArchiveIcon = false;
export let user: UserResponseDto | undefined = undefined;
let { isViewing: showAssetViewer } = assetViewingStore;
Expand Down Expand Up @@ -109,7 +108,6 @@
<!-- Overlay Asset Viewer -->
{#if $showAssetViewer}
<AssetViewer
{user}
asset={selectedAsset}
on:previous={navigateAssetBackward}
on:next={navigateAssetForward}
Expand Down
4 changes: 4 additions & 0 deletions web/src/lib/stores/user.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { writable } from 'svelte/store';
import type { UserResponseDto } from '@api';

export const user = writable<UserResponseDto | null>(null);
24 changes: 23 additions & 1 deletion web/src/lib/utils/asset-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
import { downloadManager } from '$lib/stores/download';
import { api, BulkIdResponseDto, AssetResponseDto, DownloadResponseDto, DownloadInfoDto, AssetTypeEnum } from '@api';
import {
api,
BulkIdResponseDto,
AssetResponseDto,
DownloadResponseDto,
DownloadInfoDto,
AssetTypeEnum,
UserResponseDto,
} from '@api';
import { handleError } from './handle-error';

export const addAssetsToAlbum = async (albumId: string, assetIds: Array<string>): Promise<BulkIdResponseDto[]> =>
Expand Down Expand Up @@ -203,3 +211,17 @@ export const getAssetType = (type: AssetTypeEnum) => {
return 'Asset';
}
};

export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserResponseDto | null): string[] => {
const ids = Array.from(assets)
.filter((a) => !a.isExternal && user && a.ownerId !== user.id)
.map((a) => a.id);
const numberOfIssues = Array.from(assets).filter((a) => a.isExternal || (user && a.ownerId === user.id)).length;
if (numberOfIssues > 0) {
notificationController.show({
message: `Can't change metadata of ${numberOfIssues} asset${numberOfIssues > 1 ? 's' : ''}`,
type: NotificationType.Warning,
});
}
return ids;
};
25 changes: 11 additions & 14 deletions web/src/routes/(user)/albums/[albumId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@
import { numberOfComments, setNumberOfComments, updateNumberOfComments } from '$lib/stores/activity.store';
import AlbumOptions from '$lib/components/album-page/album-options.svelte';
import UpdatePanel from '$lib/components/shared-components/update-panel.svelte';
import { user } from '$lib/stores/user.store';
export let data: PageData;
let { isViewing: showAssetViewer, setAssetId } = assetViewingStore;
let { slideshowState, slideshowShuffle } = slideshowStore;
let album = data.album;
$user = data.user;
$: album = data.album;
$: {
Expand Down Expand Up @@ -96,7 +100,6 @@
let isShowActivity = false;
let isLiked: ActivityResponseDto | null = null;
let reactions: ActivityResponseDto[] = [];
let user = data.user;
let globalWidth: number;
let assetGridWidth: number;
Expand Down Expand Up @@ -179,10 +182,10 @@
};
const getFavorite = async () => {
if (user) {
if ($user) {
try {
const { data } = await api.activityApi.getActivities({
userId: user.id,
userId: $user.id,
albumId: album.id,
type: ReactionType.Like,
level: ReactionLevel.Album,
Expand Down Expand Up @@ -549,16 +552,10 @@
style={`width:${assetGridWidth}px`}
>
{#if viewMode === ViewMode.SELECT_ASSETS}
<AssetGrid
user={data.user}
assetStore={timelineStore}
assetInteractionStore={timelineInteractionStore}
isSelectionMode={true}
/>
<AssetGrid assetStore={timelineStore} assetInteractionStore={timelineInteractionStore} isSelectionMode={true} />
{:else}
<AssetGrid
{album}
user={data.user}
{assetStore}
{assetInteractionStore}
isShared={album.sharedUsers.length > 0}
Expand Down Expand Up @@ -679,7 +676,7 @@
{/if}
</main>
</div>
{#if album.sharedUsers.length > 0 && album && isShowActivity && user && !$showAssetViewer}
{#if album.sharedUsers.length > 0 && album && isShowActivity && $user && !$showAssetViewer}
<div class="flex">
<div
transition:fly={{ duration: 150 }}
Expand All @@ -688,7 +685,7 @@
translate="yes"
>
<ActivityViewer
{user}
user={$user}
disabled={!album.isActivityEnabled}
albumOwnerId={album.ownerId}
albumId={album.id}
Expand Down Expand Up @@ -738,10 +735,10 @@
</ConfirmDialogue>
{/if}

{#if viewMode === ViewMode.OPTIONS}
{#if viewMode === ViewMode.OPTIONS && $user}
<AlbumOptions
{album}
{user}
user={$user}
on:close={() => (viewMode = ViewMode.VIEW)}
on:toggleEnableActivity={handleToggleEnableActivity}
on:showSelectSharedUser={() => (viewMode = ViewMode.SELECT_USERS)}
Expand Down
Loading

0 comments on commit ec92608

Please sign in to comment.