2022-06-03 11:04:30 -05:00
|
|
|
<script lang="ts">
|
2023-10-25 09:48:25 -04:00
|
|
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
2024-02-14 08:09:49 -05:00
|
|
|
import { user } from '$lib/stores/user.store';
|
2023-08-25 18:20:45 -05:00
|
|
|
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
2024-02-14 08:09:49 -05:00
|
|
|
import { getAssetJobName } from '$lib/utils';
|
2023-07-01 00:50:47 -04:00
|
|
|
import { clickOutside } from '$lib/utils/click-outside';
|
2023-10-25 09:48:25 -04:00
|
|
|
import { getContextMenuPosition } from '$lib/utils/context-menu';
|
2024-03-21 19:39:33 +01:00
|
|
|
import { AssetJobName, AssetTypeEnum, type AssetResponseDto, type AlbumResponseDto } from '@immich/sdk';
|
2023-10-25 09:48:25 -04:00
|
|
|
import {
|
2024-03-21 19:39:33 +01:00
|
|
|
mdiAccountCircleOutline,
|
2023-10-25 09:48:25 -04:00
|
|
|
mdiAlertOutline,
|
2024-03-21 19:39:33 +01:00
|
|
|
mdiArchiveArrowDownOutline,
|
|
|
|
mdiArchiveArrowUpOutline,
|
2023-10-25 09:48:25 -04:00
|
|
|
mdiArrowLeft,
|
2024-03-21 19:39:33 +01:00
|
|
|
mdiCogRefreshOutline,
|
2023-10-25 09:48:25 -04:00
|
|
|
mdiContentCopy,
|
2024-03-21 19:39:33 +01:00
|
|
|
mdiDatabaseRefreshOutline,
|
2023-10-25 09:48:25 -04:00
|
|
|
mdiDeleteOutline,
|
|
|
|
mdiDotsVertical,
|
2024-03-21 19:39:33 +01:00
|
|
|
mdiFolderDownloadOutline,
|
2023-10-25 09:48:25 -04:00
|
|
|
mdiHeart,
|
|
|
|
mdiHeartOutline,
|
2024-03-21 19:39:33 +01:00
|
|
|
mdiImageAlbum,
|
|
|
|
mdiImageMinusOutline,
|
|
|
|
mdiImageOutline,
|
|
|
|
mdiImageRefreshOutline,
|
2023-10-25 09:48:25 -04:00
|
|
|
mdiInformationOutline,
|
|
|
|
mdiMagnifyMinusOutline,
|
|
|
|
mdiMagnifyPlusOutline,
|
|
|
|
mdiMotionPauseOutline,
|
2023-10-27 23:59:44 -05:00
|
|
|
mdiPlaySpeed,
|
2024-03-21 19:39:33 +01:00
|
|
|
mdiPresentationPlay,
|
2024-03-03 16:15:35 -05:00
|
|
|
mdiShareVariantOutline,
|
2023-10-25 09:48:25 -04:00
|
|
|
} from '@mdi/js';
|
2023-07-01 00:50:47 -04:00
|
|
|
import { createEventDispatcher } from 'svelte';
|
|
|
|
import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
|
|
|
|
import MenuOption from '../shared-components/context-menu/menu-option.svelte';
|
2022-11-08 11:20:36 -05:00
|
|
|
|
2023-07-01 00:50:47 -04:00
|
|
|
export let asset: AssetResponseDto;
|
2024-03-21 19:39:33 +01:00
|
|
|
export let album: AlbumResponseDto | null = null;
|
2023-07-01 00:50:47 -04:00
|
|
|
export let showCopyButton: boolean;
|
|
|
|
export let showZoomButton: boolean;
|
|
|
|
export let showMotionPlayButton: boolean;
|
|
|
|
export let isMotionPhotoPlaying = false;
|
|
|
|
export let showDownloadButton: boolean;
|
2023-10-14 03:46:30 +02:00
|
|
|
export let showDetailButton: boolean;
|
2024-03-03 16:15:35 -05:00
|
|
|
export let showShareButton: boolean;
|
2023-08-25 18:20:45 -05:00
|
|
|
export let showSlideshow = false;
|
2023-11-03 17:01:48 +03:00
|
|
|
export let hasStackChildren = false;
|
2022-11-08 11:20:36 -05:00
|
|
|
|
2023-12-12 17:35:28 +01:00
|
|
|
$: isOwner = asset.ownerId === $user?.id;
|
2022-11-04 10:32:09 -04:00
|
|
|
|
2024-03-03 16:15:35 -05:00
|
|
|
type MenuItemEvent =
|
|
|
|
| 'addToAlbum'
|
|
|
|
| 'addToSharedAlbum'
|
|
|
|
| 'asProfileImage'
|
2024-03-21 19:39:33 +01:00
|
|
|
| 'setAsAlbumCover'
|
2024-03-03 16:15:35 -05:00
|
|
|
| 'download'
|
|
|
|
| 'playSlideShow'
|
|
|
|
| 'runJob'
|
|
|
|
| 'unstack';
|
2023-08-18 10:31:48 -04:00
|
|
|
|
|
|
|
const dispatch = createEventDispatcher<{
|
2023-12-15 03:54:21 +01:00
|
|
|
back: void;
|
2023-08-18 10:31:48 -04:00
|
|
|
stopMotionPhoto: void;
|
|
|
|
playMotionPhoto: void;
|
|
|
|
download: void;
|
|
|
|
showDetail: void;
|
|
|
|
favorite: void;
|
|
|
|
delete: void;
|
|
|
|
toggleArchive: void;
|
|
|
|
addToAlbum: void;
|
|
|
|
addToSharedAlbum: void;
|
|
|
|
asProfileImage: void;
|
2024-03-21 19:39:33 +01:00
|
|
|
setAsAlbumCover: void;
|
2023-08-18 10:31:48 -04:00
|
|
|
runJob: AssetJobName;
|
2023-08-25 18:20:45 -05:00
|
|
|
playSlideShow: void;
|
2023-10-27 15:34:01 -05:00
|
|
|
unstack: void;
|
2024-03-03 16:15:35 -05:00
|
|
|
showShareModal: void;
|
2023-08-18 10:31:48 -04:00
|
|
|
}>();
|
2022-11-04 10:32:09 -04:00
|
|
|
|
2023-07-01 00:50:47 -04:00
|
|
|
let contextMenuPosition = { x: 0, y: 0 };
|
|
|
|
let isShowAssetOptions = false;
|
2022-11-04 10:32:09 -04:00
|
|
|
|
2023-09-29 13:41:58 -04:00
|
|
|
const showOptionsMenu = (event: MouseEvent) => {
|
|
|
|
contextMenuPosition = getContextMenuPosition(event, 'top-right');
|
2023-07-01 00:50:47 -04:00
|
|
|
isShowAssetOptions = !isShowAssetOptions;
|
|
|
|
};
|
2022-11-04 10:32:09 -04:00
|
|
|
|
2023-08-18 10:31:48 -04:00
|
|
|
const onJobClick = (name: AssetJobName) => {
|
|
|
|
isShowAssetOptions = false;
|
|
|
|
dispatch('runJob', name);
|
|
|
|
};
|
|
|
|
|
|
|
|
const onMenuClick = (eventName: MenuItemEvent) => {
|
2023-07-01 00:50:47 -04:00
|
|
|
isShowAssetOptions = false;
|
|
|
|
dispatch(eventName);
|
|
|
|
};
|
2022-06-03 11:04:30 -05:00
|
|
|
</script>
|
|
|
|
|
2022-07-16 23:52:00 -05:00
|
|
|
<div
|
2023-07-18 13:19:39 -05:00
|
|
|
class="z-[1001] flex h-16 place-items-center justify-between bg-gradient-to-b from-black/40 px-3 transition-transform duration-200"
|
2022-07-16 23:52:00 -05:00
|
|
|
>
|
2023-07-01 00:50:47 -04:00
|
|
|
<div class="text-white">
|
2023-12-15 03:54:21 +01:00
|
|
|
<CircleIconButton isOpacity={true} icon={mdiArrowLeft} on:click={() => dispatch('back')} />
|
2023-07-01 00:50:47 -04:00
|
|
|
</div>
|
2023-07-18 13:19:39 -05:00
|
|
|
<div class="flex w-[calc(100%-3rem)] justify-end gap-2 overflow-hidden text-white">
|
2024-03-03 16:15:35 -05:00
|
|
|
{#if showShareButton}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
|
|
|
icon={mdiShareVariantOutline}
|
|
|
|
on:click={() => dispatch('showShareModal')}
|
|
|
|
title="Share"
|
|
|
|
/>
|
|
|
|
{/if}
|
2023-09-20 13:16:33 +02:00
|
|
|
{#if asset.isOffline}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
2023-10-25 09:48:25 -04:00
|
|
|
icon={mdiAlertOutline}
|
2023-09-20 13:16:33 +02:00
|
|
|
on:click={() => dispatch('showDetail')}
|
|
|
|
title="Asset Offline"
|
|
|
|
/>
|
|
|
|
{/if}
|
2023-07-01 00:50:47 -04:00
|
|
|
{#if showMotionPlayButton}
|
|
|
|
{#if isMotionPhotoPlaying}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
2023-10-25 09:48:25 -04:00
|
|
|
icon={mdiMotionPauseOutline}
|
2023-07-01 00:50:47 -04:00
|
|
|
title="Stop Motion Photo"
|
|
|
|
on:click={() => dispatch('stopMotionPhoto')}
|
|
|
|
/>
|
|
|
|
{:else}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
2023-10-27 23:59:44 -05:00
|
|
|
icon={mdiPlaySpeed}
|
2023-07-01 00:50:47 -04:00
|
|
|
title="Play Motion Photo"
|
|
|
|
on:click={() => dispatch('playMotionPhoto')}
|
|
|
|
/>
|
|
|
|
{/if}
|
|
|
|
{/if}
|
|
|
|
{#if showZoomButton}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
|
|
|
hideMobile={true}
|
2023-10-25 09:48:25 -04:00
|
|
|
icon={$photoZoomState && $photoZoomState.currentZoom > 1 ? mdiMagnifyMinusOutline : mdiMagnifyPlusOutline}
|
2023-07-01 00:50:47 -04:00
|
|
|
title="Zoom Image"
|
|
|
|
on:click={() => {
|
|
|
|
const zoomImage = new CustomEvent('zoomImage');
|
|
|
|
window.dispatchEvent(zoomImage);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
{/if}
|
|
|
|
{#if showCopyButton}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
2023-10-25 09:48:25 -04:00
|
|
|
icon={mdiContentCopy}
|
2023-07-01 00:50:47 -04:00
|
|
|
title="Copy Image"
|
|
|
|
on:click={() => {
|
|
|
|
const copyEvent = new CustomEvent('copyImage');
|
|
|
|
window.dispatchEvent(copyEvent);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
{/if}
|
2023-10-14 03:46:30 +02:00
|
|
|
{#if showDetailButton}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
2023-10-25 09:48:25 -04:00
|
|
|
icon={mdiInformationOutline}
|
2023-10-14 03:46:30 +02:00
|
|
|
on:click={() => dispatch('showDetail')}
|
|
|
|
title="Info"
|
|
|
|
/>
|
|
|
|
{/if}
|
2023-07-01 00:50:47 -04:00
|
|
|
{#if isOwner}
|
|
|
|
<CircleIconButton
|
|
|
|
isOpacity={true}
|
2023-10-25 09:48:25 -04:00
|
|
|
icon={asset.isFavorite ? mdiHeart : mdiHeartOutline}
|
2023-07-01 00:50:47 -04:00
|
|
|
on:click={() => dispatch('favorite')}
|
2023-12-19 22:56:55 +01:00
|
|
|
title={asset.isFavorite ? 'Unfavorite' : 'Favorite'}
|
2023-07-01 00:50:47 -04:00
|
|
|
/>
|
|
|
|
{/if}
|
2023-01-09 14:16:08 -06:00
|
|
|
|
2023-07-01 00:50:47 -04:00
|
|
|
{#if isOwner}
|
2023-09-26 01:04:30 +02:00
|
|
|
{#if !asset.isReadOnly || !asset.isExternal}
|
2023-10-25 09:48:25 -04:00
|
|
|
<CircleIconButton isOpacity={true} icon={mdiDeleteOutline} on:click={() => dispatch('delete')} title="Delete" />
|
2023-09-20 13:16:33 +02:00
|
|
|
{/if}
|
2023-07-01 00:50:47 -04:00
|
|
|
<div use:clickOutside on:outclick={() => (isShowAssetOptions = false)}>
|
2023-10-25 09:48:25 -04:00
|
|
|
<CircleIconButton isOpacity={true} icon={mdiDotsVertical} on:click={showOptionsMenu} title="More" />
|
2023-08-18 10:31:48 -04:00
|
|
|
{#if isShowAssetOptions}
|
|
|
|
<ContextMenu {...contextMenuPosition} direction="left">
|
2023-08-25 18:20:45 -05:00
|
|
|
{#if showSlideshow}
|
2024-03-21 19:39:33 +01:00
|
|
|
<MenuOption icon={mdiPresentationPlay} on:click={() => onMenuClick('playSlideShow')} text="Slideshow" />
|
2023-08-25 18:20:45 -05:00
|
|
|
{/if}
|
2024-03-03 16:15:35 -05:00
|
|
|
{#if showDownloadButton}
|
2024-03-21 19:39:33 +01:00
|
|
|
<MenuOption icon={mdiFolderDownloadOutline} on:click={() => onMenuClick('download')} text="Download" />
|
2024-03-03 16:15:35 -05:00
|
|
|
{/if}
|
2024-03-21 19:39:33 +01:00
|
|
|
<MenuOption icon={mdiImageAlbum} on:click={() => onMenuClick('addToAlbum')} text="Add to album" />
|
|
|
|
<MenuOption
|
|
|
|
icon={mdiShareVariantOutline}
|
|
|
|
on:click={() => onMenuClick('addToSharedAlbum')}
|
|
|
|
text="Add to shared album"
|
|
|
|
/>
|
2023-05-26 09:11:10 -04:00
|
|
|
|
2023-08-18 10:31:48 -04:00
|
|
|
{#if isOwner}
|
2024-03-21 19:39:33 +01:00
|
|
|
{#if hasStackChildren}
|
|
|
|
<MenuOption icon={mdiImageMinusOutline} on:click={() => onMenuClick('unstack')} text="Un-stack" />
|
|
|
|
{/if}
|
|
|
|
{#if album}
|
|
|
|
<MenuOption
|
|
|
|
text="Set as album cover"
|
|
|
|
icon={mdiImageOutline}
|
|
|
|
on:click={() => onMenuClick('setAsAlbumCover')}
|
|
|
|
/>
|
|
|
|
{/if}
|
|
|
|
{#if asset.type === AssetTypeEnum.Image}
|
|
|
|
<MenuOption
|
|
|
|
icon={mdiAccountCircleOutline}
|
|
|
|
on:click={() => onMenuClick('asProfileImage')}
|
|
|
|
text="Set as profile picture"
|
|
|
|
/>
|
|
|
|
{/if}
|
2023-08-18 10:31:48 -04:00
|
|
|
<MenuOption
|
|
|
|
on:click={() => dispatch('toggleArchive')}
|
2024-03-21 19:39:33 +01:00
|
|
|
icon={asset.isArchived ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline}
|
2023-08-18 10:31:48 -04:00
|
|
|
text={asset.isArchived ? 'Unarchive' : 'Archive'}
|
|
|
|
/>
|
2024-03-21 19:39:33 +01:00
|
|
|
<hr />
|
2023-08-18 10:31:48 -04:00
|
|
|
<MenuOption
|
2024-03-21 19:39:33 +01:00
|
|
|
icon={mdiDatabaseRefreshOutline}
|
2023-08-18 10:31:48 -04:00
|
|
|
on:click={() => onJobClick(AssetJobName.RefreshMetadata)}
|
2024-02-14 08:09:49 -05:00
|
|
|
text={getAssetJobName(AssetJobName.RefreshMetadata)}
|
2023-08-18 10:31:48 -04:00
|
|
|
/>
|
|
|
|
<MenuOption
|
2024-03-21 19:39:33 +01:00
|
|
|
icon={mdiImageRefreshOutline}
|
2023-08-18 10:31:48 -04:00
|
|
|
on:click={() => onJobClick(AssetJobName.RegenerateThumbnail)}
|
2024-02-14 08:09:49 -05:00
|
|
|
text={getAssetJobName(AssetJobName.RegenerateThumbnail)}
|
2023-08-18 10:31:48 -04:00
|
|
|
/>
|
|
|
|
{#if asset.type === AssetTypeEnum.Video}
|
2023-07-01 00:50:47 -04:00
|
|
|
<MenuOption
|
2024-03-21 19:39:33 +01:00
|
|
|
icon={mdiCogRefreshOutline}
|
2023-08-18 10:31:48 -04:00
|
|
|
on:click={() => onJobClick(AssetJobName.TranscodeVideo)}
|
2024-02-14 08:09:49 -05:00
|
|
|
text={getAssetJobName(AssetJobName.TranscodeVideo)}
|
2023-07-01 00:50:47 -04:00
|
|
|
/>
|
|
|
|
{/if}
|
2023-08-18 10:31:48 -04:00
|
|
|
{/if}
|
|
|
|
</ContextMenu>
|
|
|
|
{/if}
|
2023-07-01 00:50:47 -04:00
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
</div>
|
2022-06-03 11:04:30 -05:00
|
|
|
</div>
|