mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
* cropping, panel * fix presets * types * prettier * fix lint * fix aspect ratio, performance optimization * improved tool selection, removed placeholder * fix the mouse's exit from canvas * fix error * the "save" button and change tracking * lint, format * the mini functionality of the save button * fix aspect ratio * hide editor button on mobiles * strict equality Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * Use the dollar sign syntax for stores inside components * unobtrusive grid lines, circles at the corners * more correct image load, handleError * more strict equality * fix styles. unused and tailwind Co-Authored-By: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * dont store isShowEditor * if showEditor - hide navbar & shortcuts * crop-canvas decomposition (danger) I could have accidentally broken something.. but I checked the work and it seems ok. * fix lint * fix ts * callback function as props * correctly disabling shortcuts * convenient canvas borders • you can use the mouse to go beyond the boundaries and freely change the crop. • the circles on the corners of the canvas are not cut off. * -the editor button for video files, -save button * hide editor btn if panoramic || gif || live * corners instead of circles (preview), fix lint&format * confirm close editor without save * vertical aspect ratios * recovery after merge. editor's closing shortcut * fix format * move from canvas to html elements * fix changes detections * rotation * hide detail panel if showing editor * fix aspect ratios near min size * fix crop area when changing image size when rotate * fix of fix * better layout - grouping https://github.com/user-attachments/assets/48f15172-9666-4588-acb6-3cb5eda873a8 * hide the button * fix i18n, format * hide button * hide button v2 --------- Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
176 lines
6.9 KiB
Svelte
176 lines
6.9 KiB
Svelte
<script lang="ts">
|
|
import type { OnAction } from '$lib/components/asset-viewer/actions/action';
|
|
import AddToAlbumAction from '$lib/components/asset-viewer/actions/add-to-album-action.svelte';
|
|
import ArchiveAction from '$lib/components/asset-viewer/actions/archive-action.svelte';
|
|
import CloseAction from '$lib/components/asset-viewer/actions/close-action.svelte';
|
|
import DeleteAction from '$lib/components/asset-viewer/actions/delete-action.svelte';
|
|
import DownloadAction from '$lib/components/asset-viewer/actions/download-action.svelte';
|
|
import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte';
|
|
import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte';
|
|
import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte';
|
|
import SetProfilePictureAction from '$lib/components/asset-viewer/actions/set-profile-picture-action.svelte';
|
|
import ShareAction from '$lib/components/asset-viewer/actions/share-action.svelte';
|
|
import ShowDetailAction from '$lib/components/asset-viewer/actions/show-detail-action.svelte';
|
|
import UnstackAction from '$lib/components/asset-viewer/actions/unstack-action.svelte';
|
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
|
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
|
import { user } from '$lib/stores/user.store';
|
|
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
|
import { getAssetJobName, getSharedLink } from '$lib/utils';
|
|
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
|
import { AssetJobName, AssetTypeEnum, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk';
|
|
import {
|
|
mdiAlertOutline,
|
|
mdiCogRefreshOutline,
|
|
mdiContentCopy,
|
|
mdiDatabaseRefreshOutline,
|
|
mdiDotsVertical,
|
|
mdiImageRefreshOutline,
|
|
mdiMagnifyMinusOutline,
|
|
mdiMagnifyPlusOutline,
|
|
mdiPresentationPlay,
|
|
mdiUpload,
|
|
} from '@mdi/js';
|
|
import { canCopyImagesToClipboard } from 'copy-image-clipboard';
|
|
import { t } from 'svelte-i18n';
|
|
|
|
export let asset: AssetResponseDto;
|
|
export let album: AlbumResponseDto | null = null;
|
|
export let stackedAssets: AssetResponseDto[];
|
|
export let showDetailButton: boolean;
|
|
export let showSlideshow = false;
|
|
export let hasStackChildren = false;
|
|
export let onZoomImage: () => void;
|
|
export let onCopyImage: () => void;
|
|
export let onAction: OnAction;
|
|
export let onRunJob: (name: AssetJobName) => void;
|
|
export let onPlaySlideshow: () => void;
|
|
export let onShowDetail: () => void;
|
|
// export let showEditorHandler: () => void;
|
|
export let onClose: () => void;
|
|
|
|
const sharedLink = getSharedLink();
|
|
|
|
$: isOwner = $user && asset.ownerId === $user?.id;
|
|
$: showDownloadButton = sharedLink ? sharedLink.allowDownload : !asset.isOffline;
|
|
// $: showEditorButton =
|
|
// isOwner &&
|
|
// asset.type === AssetTypeEnum.Image &&
|
|
// !(
|
|
// asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR ||
|
|
// (asset.originalPath && asset.originalPath.toLowerCase().endsWith('.insp'))
|
|
// ) &&
|
|
// !(asset.originalPath && asset.originalPath.toLowerCase().endsWith('.gif')) &&
|
|
// !asset.livePhotoVideoId;
|
|
</script>
|
|
|
|
<div
|
|
class="z-[1001] flex h-16 place-items-center justify-between bg-gradient-to-b from-black/40 px-3 transition-transform duration-200"
|
|
>
|
|
<div class="text-white">
|
|
<CloseAction {onClose} />
|
|
</div>
|
|
<div
|
|
class="flex w-[calc(100%-3rem)] justify-end gap-2 overflow-hidden text-white"
|
|
data-testid="asset-viewer-navbar-actions"
|
|
>
|
|
{#if !asset.isTrashed && $user}
|
|
<ShareAction {asset} />
|
|
{/if}
|
|
{#if asset.isOffline}
|
|
<CircleIconButton color="opaque" icon={mdiAlertOutline} on:click={onShowDetail} title={$t('asset_offline')} />
|
|
{/if}
|
|
{#if asset.livePhotoVideoId}
|
|
<slot name="motion-photo" />
|
|
{/if}
|
|
{#if asset.type === AssetTypeEnum.Image}
|
|
<CircleIconButton
|
|
color="opaque"
|
|
hideMobile={true}
|
|
icon={$photoZoomState && $photoZoomState.currentZoom > 1 ? mdiMagnifyMinusOutline : mdiMagnifyPlusOutline}
|
|
title={$t('zoom_image')}
|
|
on:click={onZoomImage}
|
|
/>
|
|
{/if}
|
|
{#if canCopyImagesToClipboard() && asset.type === AssetTypeEnum.Image}
|
|
<CircleIconButton color="opaque" icon={mdiContentCopy} title={$t('copy_image')} on:click={onCopyImage} />
|
|
{/if}
|
|
|
|
{#if !isOwner && showDownloadButton}
|
|
<DownloadAction {asset} />
|
|
{/if}
|
|
|
|
{#if showDetailButton}
|
|
<ShowDetailAction {onShowDetail} />
|
|
{/if}
|
|
|
|
{#if isOwner}
|
|
<FavoriteAction {asset} {onAction} />
|
|
{/if}
|
|
<!-- {#if showEditorButton}
|
|
<CircleIconButton
|
|
color="opaque"
|
|
hideMobile={true}
|
|
icon={mdiImageEditOutline}
|
|
on:click={showEditorHandler}
|
|
title={$t('editor')}
|
|
/>
|
|
{/if} -->
|
|
|
|
{#if isOwner}
|
|
<DeleteAction {asset} {onAction} />
|
|
|
|
<ButtonContextMenu direction="left" align="top-right" color="opaque" title={$t('more')} icon={mdiDotsVertical}>
|
|
{#if showSlideshow}
|
|
<MenuOption icon={mdiPresentationPlay} text={$t('slideshow')} onClick={onPlaySlideshow} />
|
|
{/if}
|
|
{#if showDownloadButton}
|
|
<DownloadAction {asset} menuItem />
|
|
{/if}
|
|
{#if asset.isTrashed}
|
|
<RestoreAction {asset} {onAction} />
|
|
{:else}
|
|
<AddToAlbumAction {asset} {onAction} />
|
|
<AddToAlbumAction {asset} {onAction} shared />
|
|
{/if}
|
|
|
|
{#if isOwner}
|
|
{#if hasStackChildren}
|
|
<UnstackAction {stackedAssets} {onAction} />
|
|
{/if}
|
|
{#if album}
|
|
<SetAlbumCoverAction {asset} {album} />
|
|
{/if}
|
|
{#if asset.type === AssetTypeEnum.Image}
|
|
<SetProfilePictureAction {asset} />
|
|
{/if}
|
|
<ArchiveAction {asset} {onAction} />
|
|
<MenuOption
|
|
icon={mdiUpload}
|
|
onClick={() => openFileUploadDialog({ multiple: false, assetId: asset.id })}
|
|
text={$t('replace_with_upload')}
|
|
/>
|
|
<hr />
|
|
<MenuOption
|
|
icon={mdiDatabaseRefreshOutline}
|
|
onClick={() => onRunJob(AssetJobName.RefreshMetadata)}
|
|
text={$getAssetJobName(AssetJobName.RefreshMetadata)}
|
|
/>
|
|
<MenuOption
|
|
icon={mdiImageRefreshOutline}
|
|
onClick={() => onRunJob(AssetJobName.RegenerateThumbnail)}
|
|
text={$getAssetJobName(AssetJobName.RegenerateThumbnail)}
|
|
/>
|
|
{#if asset.type === AssetTypeEnum.Video}
|
|
<MenuOption
|
|
icon={mdiCogRefreshOutline}
|
|
onClick={() => onRunJob(AssetJobName.TranscodeVideo)}
|
|
text={$getAssetJobName(AssetJobName.TranscodeVideo)}
|
|
/>
|
|
{/if}
|
|
{/if}
|
|
</ButtonContextMenu>
|
|
{/if}
|
|
</div>
|
|
</div>
|