1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-28 11:15:54 +02:00

fix(web): detail panel asset description (#9765)

This commit is contained in:
Michel Heusschen 2024-05-26 14:10:01 +02:00 committed by GitHub
parent 459fee9ee4
commit 99f0aa868a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 57 deletions

View File

@ -43,4 +43,18 @@ test.describe('Detail Panel', () => {
await page.keyboard.press('i'); await page.keyboard.press('i');
await expect(page.locator('#detail-panel')).toHaveCount(0); await expect(page.locator('#detail-panel')).toHaveCount(0);
}); });
test('description is visible for owner on shared links', async ({ context, page }) => {
const sharedLink = await utils.createSharedLink(admin.accessToken, {
type: SharedLinkType.Individual,
assetIds: [asset.id],
});
await utils.setAuthCookies(context, admin.accessToken);
await page.goto(`/share/${sharedLink.key}/photos/${asset.id}`);
const textarea = page.getByRole('textbox', { name: 'Add a description' });
await page.getByRole('button', { name: 'Info' }).click();
await expect(textarea).toBeVisible();
await expect(textarea).not.toBeDisabled();
});
}); });

View File

@ -0,0 +1,62 @@
<script lang="ts">
import { autoGrowHeight } from '$lib/actions/autogrow';
import { clickOutside } from '$lib/actions/click-outside';
import { shortcut } from '$lib/actions/shortcut';
import {
NotificationType,
notificationController,
} from '$lib/components/shared-components/notification/notification';
import { handleError } from '$lib/utils/handle-error';
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
import { tick } from 'svelte';
export let asset: AssetResponseDto;
export let isOwner: boolean;
let textarea: HTMLTextAreaElement;
$: description = asset.exifInfo?.description || '';
$: newDescription = description;
$: if (textarea) {
newDescription;
void tick().then(() => autoGrowHeight(textarea));
}
const handleFocusOut = async () => {
if (description === newDescription) {
return;
}
try {
await updateAsset({ id: asset.id, updateAssetDto: { description: newDescription } });
notificationController.show({
type: NotificationType.Info,
message: 'Asset description has been updated',
});
} catch (error) {
handleError(error, 'Cannot update the description');
}
};
</script>
{#if isOwner}
<section class="px-4 mt-10">
<textarea
bind:this={textarea}
class="max-h-[500px] w-full resize-none border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary immich-scrollbar"
placeholder="Add a description"
on:focusout={handleFocusOut}
on:input={(e) => (newDescription = e.currentTarget.value)}
value={description}
use:clickOutside={{ onOutclick: void handleFocusOut }}
use:shortcut={{
shortcut: { key: 'Enter', ctrl: true },
onShortcut: (e) => e.currentTarget.blur(),
}}
/>
</section>
{:else if description}
<section class="px-4 mt-6">
<p class="break-words whitespace-pre-line w-full text-black dark:text-white text-base">{description}</p>
</section>
{/if}

View File

@ -8,10 +8,8 @@
import { featureFlags } from '$lib/stores/server-config.store'; import { featureFlags } from '$lib/stores/server-config.store';
import { user } from '$lib/stores/user.store'; import { user } from '$lib/stores/user.store';
import { websocketEvents } from '$lib/stores/websocket'; import { websocketEvents } from '$lib/stores/websocket';
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink, handlePromiseError } from '$lib/utils'; import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError, isSharedLink } from '$lib/utils';
import { delay, isFlipped } from '$lib/utils/asset-utils'; import { delay, isFlipped } from '$lib/utils/asset-utils';
import { autoGrowHeight } from '$lib/actions/autogrow';
import { clickOutside } from '$lib/actions/click-outside';
import { import {
ThumbnailFormat, ThumbnailFormat,
getAssetInfo, getAssetInfo,
@ -40,9 +38,8 @@
import PersonSidePanel from '../faces-page/person-side-panel.svelte'; import PersonSidePanel from '../faces-page/person-side-panel.svelte';
import UserAvatar from '../shared-components/user-avatar.svelte'; import UserAvatar from '../shared-components/user-avatar.svelte';
import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import { NotificationType, notificationController } from '../shared-components/notification/notification';
import { shortcut } from '$lib/actions/shortcut';
import AlbumListItemDetails from './album-list-item-details.svelte'; import AlbumListItemDetails from './album-list-item-details.svelte';
import DetailPanelDescription from '$lib/components/asset-viewer/detail-panel-description.svelte';
export let asset: AssetResponseDto; export let asset: AssetResponseDto;
export let albums: AlbumResponseDto[] = []; export let albums: AlbumResponseDto[] = [];
@ -58,9 +55,6 @@
}; };
let showAssetPath = false; let showAssetPath = false;
let textArea: HTMLTextAreaElement;
let description: string;
let originalDescription: string;
let showEditFaces = false; let showEditFaces = false;
let previousId: string; let previousId: string;
@ -77,16 +71,11 @@
$: isOwner = $user?.id === asset.ownerId; $: isOwner = $user?.id === asset.ownerId;
const handleNewAsset = async (newAsset: AssetResponseDto) => { const handleNewAsset = async (newAsset: AssetResponseDto) => {
description = newAsset?.exifInfo?.description || ''; // TODO: check if reloading asset data is necessary
// Get latest description from server
if (newAsset.id && !isSharedLink()) { if (newAsset.id && !isSharedLink()) {
const data = await getAssetInfo({ id: asset.id }); const data = await getAssetInfo({ id: asset.id });
people = data?.people || []; people = data?.people || [];
description = data.exifInfo?.description || '';
} }
originalDescription = description;
}; };
$: handlePromiseError(handleNewAsset(asset)); $: handlePromiseError(handleNewAsset(asset));
@ -129,28 +118,10 @@
const handleRefreshPeople = async () => { const handleRefreshPeople = async () => {
await getAssetInfo({ id: asset.id }).then((data) => { await getAssetInfo({ id: asset.id }).then((data) => {
people = data?.people || []; people = data?.people || [];
textArea.value = data?.exifInfo?.description || '';
}); });
showEditFaces = false; showEditFaces = false;
}; };
const handleFocusOut = async () => {
textArea.blur();
if (description === originalDescription) {
return;
}
originalDescription = description;
try {
await updateAsset({ id: asset.id, updateAssetDto: { description } });
notificationController.show({
type: NotificationType.Info,
message: 'Asset description has been updated',
});
} catch (error) {
handleError(error, 'Cannot update the description');
}
};
const toggleAssetPath = () => (showAssetPath = !showAssetPath); const toggleAssetPath = () => (showAssetPath = !showAssetPath);
let isShowChangeDate = false; let isShowChangeDate = false;
@ -185,31 +156,7 @@
</section> </section>
{/if} {/if}
{#if isOwner} <DetailPanelDescription {asset} {isOwner} />
<section class="px-4 mt-10">
{#key asset.id}
<textarea
disabled={!isOwner || isSharedLink()}
bind:this={textArea}
class="max-h-[500px]
w-full resize-none border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary immich-scrollbar"
placeholder={isOwner ? 'Add a description' : ''}
on:focusout={handleFocusOut}
on:input={() => autoGrowHeight(textArea)}
bind:value={description}
use:autoGrowHeight
use:clickOutside
on:outclick={handleFocusOut}
use:shortcut={{
shortcut: { key: 'Enter', ctrl: true },
onShortcut: () => handlePromiseError(handleFocusOut()),
}}
/>
{/key}
</section>
{:else if description}
<p class="px-4 break-words whitespace-pre-line w-full text-black dark:text-white text-base">{description}</p>
{/if}
{#if !isSharedLink() && people.length > 0} {#if !isSharedLink() && people.length > 0}
<section class="px-4 py-4 text-sm"> <section class="px-4 py-4 text-sm">