mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
fix(web): disable metadata edit if user is not owner (#5415)
* 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 <alex.tran1502@gmail.com>
This commit is contained in:
parent
5a50d32748
commit
ec92608024
12 changed files with 83 additions and 53 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
AssetTypeEnum,
|
||||
ReactionType,
|
||||
SharedLinkResponseDto,
|
||||
UserResponseDto,
|
||||
} from '@api';
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
|
|
@ -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;
|
||||
|
|
@ -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[] = [];
|
||||
|
|
@ -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,
|
||||
|
|
@ -743,7 +742,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if isShared && album && isShowActivity && user}
|
||||
{#if isShared && album && isShowActivity && $user}
|
||||
<div
|
||||
transition:fly={{ duration: 150 }}
|
||||
id="activity-panel"
|
||||
|
|
@ -751,7 +750,7 @@
|
|||
translate="yes"
|
||||
>
|
||||
<ActivityViewer
|
||||
{user}
|
||||
user={$user}
|
||||
disabled={!album.isActivityEnabled}
|
||||
assetType={asset.type}
|
||||
albumOwnerId={album.ownerId}
|
||||
|
|
|
|||
|
|
@ -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[] = [];
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
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();
|
||||
|
||||
|
|
@ -12,9 +14,7 @@
|
|||
|
||||
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({
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -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;
|
||||
|
|
@ -394,7 +393,6 @@
|
|||
<Portal target="body">
|
||||
{#if $showAssetViewer}
|
||||
<AssetViewer
|
||||
{user}
|
||||
{withStacked}
|
||||
{assetStore}
|
||||
asset={$viewingAsset}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
export let sharedLink: SharedLinkResponseDto;
|
||||
export let isOwned: boolean;
|
||||
export let user: UserResponseDto | undefined = undefined;
|
||||
|
||||
let selectedAssets: Set<AssetResponseDto> = new Set();
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -109,7 +108,6 @@
|
|||
<!-- Overlay Asset Viewer -->
|
||||
{#if $showAssetViewer}
|
||||
<AssetViewer
|
||||
{user}
|
||||
asset={selectedAsset}
|
||||
on:previous={navigateAssetBackward}
|
||||
on:next={navigateAssetForward}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue