2022-06-03 11:04:30 -05:00
< script lang = "ts" >
2022-08-24 22:18:28 -07:00
import { createEventDispatcher , onMount , onDestroy } from 'svelte';
import { fly } from 'svelte/transition';
2022-10-30 17:08:22 +01:00
import AsserViewerNavBar from './asset-viewer-nav-bar.svelte';
2022-08-24 22:18:28 -07:00
import ChevronRight from 'svelte-material-icons/ChevronRight.svelte';
import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte';
import PhotoViewer from './photo-viewer.svelte';
import DetailPanel from './detail-panel.svelte';
2022-11-04 10:32:09 -04:00
import { goto } from '$app/navigation';
2022-08-24 22:18:28 -07:00
import { downloadAssets } from '$lib/stores/download';
import VideoViewer from './video-viewer.svelte';
2022-11-04 23:29:48 -04:00
import AlbumSelectionModal from '../shared-components/album-selection-modal.svelte';
2022-11-04 10:32:09 -04:00
import {
api,
AddAssetsResponseDto,
AssetResponseDto,
AssetTypeEnum,
AlbumResponseDto
} from '@api';
2022-08-26 10:36:41 -07:00
import {
notificationController,
NotificationType
} from '../shared-components/notification/notification';
2022-10-30 17:08:22 +01:00
import { assetStore } from '$lib/stores/assets.store';
2022-08-24 22:18:28 -07:00
export let asset: AssetResponseDto;
2022-09-05 15:50:20 +02:00
$: {
appearsInAlbums = [];
2022-09-08 12:53:09 +02:00
api.albumApi.getAllAlbums(undefined, asset.id).then((result) => {
2022-09-05 15:50:20 +02:00
appearsInAlbums = result.data;
});
}
2022-08-24 22:18:28 -07:00
const dispatch = createEventDispatcher();
let halfLeftHover = false;
let halfRightHover = false;
let isShowDetail = false;
2022-09-05 15:50:20 +02:00
let appearsInAlbums: AlbumResponseDto[] = [];
2022-11-04 10:32:09 -04:00
let isShowAlbumPicker = false;
let addToSharedAlbum = true;
2022-08-24 22:18:28 -07:00
2022-09-08 17:30:49 +02:00
const onKeyboardPress = (keyInfo: KeyboardEvent) => handleKeyboardPress(keyInfo.key);
2022-08-24 22:18:28 -07:00
onMount(() => {
2022-09-08 17:30:49 +02:00
document.addEventListener('keydown', onKeyboardPress);
2022-08-24 22:18:28 -07:00
});
onDestroy(() => {
2022-09-08 17:30:49 +02:00
document.removeEventListener('keydown', onKeyboardPress);
2022-08-24 22:18:28 -07:00
});
const handleKeyboardPress = (key: string) => {
switch (key) {
case 'Escape':
closeViewer();
return;
2022-10-30 17:08:22 +01:00
case 'Delete':
deleteAsset();
return;
2022-08-24 22:18:28 -07:00
case 'i':
isShowDetail = !isShowDetail;
return;
case 'ArrowLeft':
navigateAssetBackward();
return;
case 'ArrowRight':
navigateAssetForward();
return;
}
};
const closeViewer = () => {
dispatch('close');
};
const navigateAssetForward = (e?: Event) => {
e?.stopPropagation();
2022-09-04 08:34:39 -05:00
dispatch('navigate-next');
2022-08-24 22:18:28 -07:00
};
const navigateAssetBackward = (e?: Event) => {
e?.stopPropagation();
2022-09-04 08:34:39 -05:00
dispatch('navigate-previous');
2022-08-24 22:18:28 -07:00
};
const showDetailInfoHandler = () => {
isShowDetail = !isShowDetail;
};
const downloadFile = async () => {
try {
const imageName = asset.exifInfo?.imageName ? asset.exifInfo?.imageName : asset.id;
const imageExtension = asset.originalPath.split('.')[1];
const imageFileName = imageName + '.' + imageExtension;
// If assets is already download -> return;
if ($downloadAssets[imageFileName]) {
return;
}
$downloadAssets[imageFileName] = 0;
const { data , status } = await api.assetApi.downloadFile(
asset.deviceAssetId,
asset.deviceId,
false,
false,
{
responseType: 'blob',
onDownloadProgress: (progressEvent) => {
if (progressEvent.lengthComputable) {
const total = progressEvent.total;
const current = progressEvent.loaded;
$downloadAssets[imageFileName] = Math.floor((current / total) * 100);
}
}
}
);
if (!(data instanceof Blob)) {
return;
}
if (status === 200) {
const fileUrl = URL.createObjectURL(data);
const anchor = document.createElement('a');
anchor.href = fileUrl;
anchor.download = imageFileName;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
URL.revokeObjectURL(fileUrl);
// Remove item from download list
setTimeout(() => {
const copy = $downloadAssets;
delete copy[imageFileName];
$downloadAssets = copy;
}, 2000);
}
} catch (e) {
2022-08-26 10:36:41 -07:00
console.error('Error downloading file ', e);
notificationController.show({
type: NotificationType.Error,
message: 'Error downloading file, check console for more details.'
});
2022-08-24 22:18:28 -07:00
}
};
2022-10-30 17:08:22 +01:00
const deleteAsset = async () => {
try {
if (
window.confirm(
`Caution! Are you sure you want to delete this asset? This step also deletes this asset in the album(s) to which it belongs. You can not undo this action!`
)
) {
const { data : deletedAssets } = await api.assetApi.deleteAsset({
ids: [asset.id]
});
navigateAssetForward();
for (const asset of deletedAssets) {
if (asset.status == 'SUCCESS') {
assetStore.removeAsset(asset.id);
}
}
}
} catch (e) {
notificationController.show({
type: NotificationType.Error,
message: 'Error deleting this asset, check console for more details'
});
console.error('Error deleteSelectedAssetHandler', e);
}
};
2022-11-04 10:32:09 -04:00
2022-11-08 11:20:36 -05:00
const toggleFavorite = async () => {
const { data } = await api.assetApi.updateAssetById(asset.id, {
isFavorite: !asset.isFavorite
});
asset.isFavorite = data.isFavorite;
};
2022-11-04 10:32:09 -04:00
const openAlbumPicker = (shared: boolean) => {
isShowAlbumPicker = true;
addToSharedAlbum = shared;
};
const showAddNotification = (dto: AddAssetsResponseDto) => {
notificationController.show({
message: `Added ${ dto . successfullyAdded } to ${ dto . album ? . albumName } `,
type: NotificationType.Info
});
if (dto.successfullyAdded === 1 && dto.album) {
appearsInAlbums = [...appearsInAlbums, dto.album];
}
};
const handleAddToNewAlbum = () => {
isShowAlbumPicker = false;
api.albumApi.createAlbum({ albumName : 'Untitled' , assetIds : [ asset . id ] } ).then((response) => {
const album = response.data;
goto('/albums/' + album.id);
});
};
const handleAddToAlbum = async (event: CustomEvent< { album : AlbumResponseDto } >) => {
isShowAlbumPicker = false;
const album = event.detail.album;
api.albumApi
.addAssetsToAlbum(album.id, { assetIds : [ asset . id ] } )
.then((response) => showAddNotification(response.data));
};
2022-06-03 11:04:30 -05:00
< / script >
< section
2022-08-24 22:18:28 -07:00
id="immich-asset-viewer"
2022-09-04 08:34:39 -05:00
class="fixed h-screen w-screen top-0 overflow-y-hidden bg-black z-[999] grid grid-rows-[64px_1fr] grid-cols-4"
2022-06-03 11:04:30 -05:00
>
2022-08-24 22:18:28 -07:00
< div class = "col-start-1 col-span-4 row-start-1 row-span-1 z-[1000] transition-transform" >
< AsserViewerNavBar
2022-11-08 11:20:36 -05:00
{ asset }
2022-08-24 22:18:28 -07:00
on:goBack={ closeViewer }
on:showDetail={ showDetailInfoHandler }
on:download={ downloadFile }
2022-10-30 17:08:22 +01:00
on:delete={ deleteAsset }
2022-11-08 11:20:36 -05:00
on:favorite={ toggleFavorite }
2022-11-04 10:32:09 -04:00
on:addToAlbum={() => openAlbumPicker ( false )}
on:addToSharedAlbum={() => openAlbumPicker ( true )}
2022-08-24 22:18:28 -07:00
/>
< / div >
< div
2022-09-23 18:22:06 -05:00
class={ `row-start-2 row-span-end col-start-1 col-span-2 flex place-items-center hover:cursor-pointer w-3/4 mb-[60px] $ {
2022-08-12 21:19:54 -05:00
asset.type === AssetTypeEnum.Video ? '' : 'z-[999]'
2022-06-04 18:34:11 -05:00
}`}
2022-08-24 22:18:28 -07:00
on:mouseenter={() => {
2022-06-03 11:04:30 -05:00
halfLeftHover = true;
halfRightHover = false;
}}
2022-08-24 22:18:28 -07:00
on:mouseleave={() => {
2022-06-03 11:04:30 -05:00
halfLeftHover = false;
}}
2022-08-24 22:18:28 -07:00
on:click={ navigateAssetBackward }
>
< button
class="rounded-full p-3 hover:bg-gray-500 hover:text-gray-700 z-[1000] text-gray-500 mx-4"
class:navigation-button-hover={ halfLeftHover }
on:click={ navigateAssetBackward }
>
< ChevronLeft size = "36" / >
< / button >
< / div >
< div class = "row-start-1 row-span-full col-start-1 col-span-4" >
{ #key asset . id }
{ #if asset . type === AssetTypeEnum . Image }
< PhotoViewer assetId = { asset . id } deviceId= { asset . deviceId } on:close = { closeViewer } / >
{ : else }
< VideoViewer assetId = { asset . id } on:close= { closeViewer } />
{ /if }
{ /key }
< / div >
< div
2022-09-23 18:22:06 -05:00
class={ `row-start-2 row-span-full col-start-3 col-span-2 flex justify-end place-items-center hover:cursor-pointer w-3/4 justify-self-end mb-[60px] $ {
2022-08-12 21:19:54 -05:00
asset.type === AssetTypeEnum.Video ? '' : 'z-[500]'
2022-06-04 18:34:11 -05:00
}`}
2022-08-24 22:18:28 -07:00
on:click={ navigateAssetForward }
on:mouseenter={() => {
2022-06-03 11:04:30 -05:00
halfLeftHover = false;
halfRightHover = true;
}}
2022-08-24 22:18:28 -07:00
on:mouseleave={() => {
2022-06-03 11:04:30 -05:00
halfRightHover = false;
}}
2022-08-24 22:18:28 -07:00
>
< button
2022-09-23 18:22:06 -05:00
class="rounded-full p-3 hover:bg-gray-500 hover:text-gray-700 text-gray-500 mx-4"
2022-08-24 22:18:28 -07:00
class:navigation-button-hover={ halfRightHover }
on:click={ navigateAssetForward }
>
< ChevronRight size = "36" / >
< / button >
< / div >
{ #if isShowDetail }
< div
transition:fly={{ duration : 150 }}
id="detail-panel"
2022-10-26 11:10:48 -05:00
class="bg-immich-bg w-[360px] row-span-full transition-all overflow-y-auto dark:bg-immich-dark-bg dark:border-l dark:border-l-immich-dark-gray"
2022-08-24 22:18:28 -07:00
translate="yes"
>
2022-09-05 15:50:20 +02:00
< DetailPanel { asset } albums = { appearsInAlbums } on:close= {() => ( isShowDetail = false )} />
2022-08-24 22:18:28 -07:00
< / div >
{ /if }
2022-11-04 10:32:09 -04:00
{ #if isShowAlbumPicker }
< AlbumSelectionModal
shared={ addToSharedAlbum }
on:newAlbum={ handleAddToNewAlbum }
on:newSharedAlbum={ handleAddToNewAlbum }
on:album={ handleAddToAlbum }
on:close={() => ( isShowAlbumPicker = false )}
/>
{ /if }
2022-06-03 11:04:30 -05:00
< / section >
< style >
2022-09-04 08:34:39 -05:00
#immich-asset-viewer {
contain: layout;
}
2022-08-24 22:18:28 -07:00
.navigation-button-hover {
background-color: rgb(107 114 128 / var(--tw-bg-opacity));
color: rgb(55 65 81 / var(--tw-text-opacity));
transition: all 150ms;
}
2022-06-03 11:04:30 -05:00
< / style >