2023-04-20 09:09:27 -05:00
|
|
|
<script lang="ts" context="module">
|
|
|
|
export type ViewFrom =
|
|
|
|
| 'archive-page'
|
|
|
|
| 'album-page'
|
|
|
|
| 'favorites-page'
|
|
|
|
| 'search-page'
|
|
|
|
| 'shared-link-page';
|
|
|
|
</script>
|
|
|
|
|
2023-01-14 23:49:47 -06:00
|
|
|
<script lang="ts">
|
|
|
|
import { page } from '$app/stores';
|
2023-03-27 05:53:35 +02:00
|
|
|
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
2023-01-14 23:49:47 -06:00
|
|
|
import { handleError } from '$lib/utils/handle-error';
|
2023-01-21 22:15:16 -06:00
|
|
|
import { AssetResponseDto, SharedLinkResponseDto, ThumbnailFormat } from '@api';
|
2023-01-14 23:49:47 -06:00
|
|
|
import AssetViewer from '../../asset-viewer/asset-viewer.svelte';
|
2023-04-08 20:40:37 -05:00
|
|
|
import justifiedLayout from 'justified-layout';
|
|
|
|
import { flip } from 'svelte/animate';
|
2023-04-20 09:09:27 -05:00
|
|
|
import { archivedAsset } from '$lib/stores/archived-asset.store';
|
2023-01-14 23:49:47 -06:00
|
|
|
|
|
|
|
export let assets: AssetResponseDto[];
|
2023-01-21 22:15:16 -06:00
|
|
|
export let sharedLink: SharedLinkResponseDto | undefined = undefined;
|
2023-01-14 23:49:47 -06:00
|
|
|
export let selectedAssets: Set<AssetResponseDto> = new Set();
|
2023-04-06 19:50:44 +02:00
|
|
|
export let disableAssetSelect = false;
|
2023-04-20 09:09:27 -05:00
|
|
|
export let viewFrom: ViewFrom;
|
2023-04-21 10:10:08 -05:00
|
|
|
export let showArchiveIcon = false;
|
2023-01-14 23:49:47 -06:00
|
|
|
|
|
|
|
let isShowAssetViewer = false;
|
|
|
|
|
|
|
|
let selectedAsset: AssetResponseDto;
|
|
|
|
let currentViewAssetIndex = 0;
|
|
|
|
|
|
|
|
let viewWidth: number;
|
|
|
|
|
|
|
|
$: isMultiSelectionMode = selectedAssets.size > 0;
|
2023-04-21 17:24:25 -04:00
|
|
|
$: geometry = justifiedLayout(assets.map(getAssetRatio), {
|
|
|
|
boxSpacing: 5,
|
|
|
|
containerWidth: Math.floor(viewWidth),
|
|
|
|
targetRowHeight: 235
|
|
|
|
});
|
2023-01-14 23:49:47 -06:00
|
|
|
|
2023-04-21 17:24:25 -04:00
|
|
|
function getAssetRatio(asset: AssetResponseDto) {
|
|
|
|
let height = asset.exifInfo?.exifImageHeight || 235;
|
|
|
|
let width = asset.exifInfo?.exifImageWidth || 235;
|
2023-04-08 20:40:37 -05:00
|
|
|
|
2023-04-21 17:24:25 -04:00
|
|
|
const orientation = Number(asset.exifInfo?.orientation);
|
|
|
|
if (orientation) {
|
|
|
|
if (orientation == 6 || orientation == -90) {
|
|
|
|
[width, height] = [height, width];
|
2023-04-08 20:40:37 -05:00
|
|
|
}
|
2023-01-14 23:49:47 -06:00
|
|
|
}
|
2023-04-08 20:40:37 -05:00
|
|
|
|
2023-04-21 17:24:25 -04:00
|
|
|
return { width, height };
|
2023-01-14 23:49:47 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
const viewAssetHandler = (event: CustomEvent) => {
|
|
|
|
const { asset }: { asset: AssetResponseDto } = event.detail;
|
|
|
|
|
|
|
|
currentViewAssetIndex = assets.findIndex((a) => a.id == asset.id);
|
|
|
|
selectedAsset = assets[currentViewAssetIndex];
|
|
|
|
isShowAssetViewer = true;
|
|
|
|
pushState(selectedAsset.id);
|
|
|
|
};
|
|
|
|
|
|
|
|
const selectAssetHandler = (event: CustomEvent) => {
|
|
|
|
const { asset }: { asset: AssetResponseDto } = event.detail;
|
|
|
|
let temp = new Set(selectedAssets);
|
|
|
|
|
|
|
|
if (selectedAssets.has(asset)) {
|
|
|
|
temp.delete(asset);
|
|
|
|
} else {
|
|
|
|
temp.add(asset);
|
|
|
|
}
|
|
|
|
|
|
|
|
selectedAssets = temp;
|
|
|
|
};
|
|
|
|
|
|
|
|
const navigateAssetForward = () => {
|
|
|
|
try {
|
|
|
|
if (currentViewAssetIndex < assets.length - 1) {
|
|
|
|
currentViewAssetIndex++;
|
|
|
|
selectedAsset = assets[currentViewAssetIndex];
|
|
|
|
pushState(selectedAsset.id);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
handleError(e, 'Cannot navigate to the next asset');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const navigateAssetBackward = () => {
|
|
|
|
try {
|
|
|
|
if (currentViewAssetIndex > 0) {
|
|
|
|
currentViewAssetIndex--;
|
|
|
|
selectedAsset = assets[currentViewAssetIndex];
|
|
|
|
pushState(selectedAsset.id);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
handleError(e, 'Cannot navigate to previous asset');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const pushState = (assetId: string) => {
|
|
|
|
// add a URL to the browser's history
|
|
|
|
// changes the current URL in the address bar but doesn't perform any SvelteKit navigation
|
|
|
|
history.pushState(null, '', `${$page.url.pathname}/photos/${assetId}`);
|
|
|
|
};
|
|
|
|
|
|
|
|
const closeViewer = () => {
|
|
|
|
isShowAssetViewer = false;
|
|
|
|
history.pushState(null, '', `${$page.url.pathname}`);
|
|
|
|
};
|
2023-04-20 09:09:27 -05:00
|
|
|
|
|
|
|
const handleUnarchivedSuccess = (event: CustomEvent) => {
|
|
|
|
const asset = event.detail as AssetResponseDto;
|
|
|
|
switch (viewFrom) {
|
|
|
|
case 'archive-page':
|
|
|
|
$archivedAsset = $archivedAsset.filter((a) => a.id != asset.id);
|
|
|
|
navigateAssetForward();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
2023-01-14 23:49:47 -06:00
|
|
|
</script>
|
|
|
|
|
|
|
|
{#if assets.length > 0}
|
2023-04-21 17:24:25 -04:00
|
|
|
<div
|
|
|
|
class="relative w-full mb-20"
|
|
|
|
bind:clientWidth={viewWidth}
|
|
|
|
style="height: {geometry.containerHeight}px"
|
|
|
|
>
|
2023-04-08 20:40:37 -05:00
|
|
|
{#if viewWidth}
|
|
|
|
{#each assets as asset, index (asset.id)}
|
2023-04-21 17:24:25 -04:00
|
|
|
{@const box = geometry.boxes[index]}
|
|
|
|
<div
|
|
|
|
class="absolute"
|
|
|
|
style="width: {box.width}px; height: {box.height}px; top: {box.top}px; left: {box.left}px"
|
|
|
|
animate:flip={{ duration: 500 }}
|
|
|
|
>
|
2023-04-08 20:40:37 -05:00
|
|
|
<Thumbnail
|
|
|
|
{asset}
|
2023-04-21 17:24:25 -04:00
|
|
|
thumbnailWidth={box.width}
|
|
|
|
thumbnailHeight={box.height}
|
2023-04-08 20:40:37 -05:00
|
|
|
readonly={disableAssetSelect}
|
|
|
|
publicSharedKey={sharedLink?.key}
|
|
|
|
format={assets.length < 7 ? ThumbnailFormat.Jpeg : ThumbnailFormat.Webp}
|
|
|
|
on:click={(e) => (isMultiSelectionMode ? selectAssetHandler(e) : viewAssetHandler(e))}
|
|
|
|
on:select={selectAssetHandler}
|
|
|
|
selected={selectedAssets.has(asset)}
|
2023-04-21 10:10:08 -05:00
|
|
|
{showArchiveIcon}
|
2023-04-08 20:40:37 -05:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
{/each}
|
|
|
|
{/if}
|
2023-01-14 23:49:47 -06:00
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
<!-- Overlay Asset Viewer -->
|
|
|
|
{#if isShowAssetViewer}
|
|
|
|
<AssetViewer
|
|
|
|
asset={selectedAsset}
|
2023-01-21 22:15:16 -06:00
|
|
|
publicSharedKey={sharedLink?.key}
|
|
|
|
{sharedLink}
|
2023-01-14 23:49:47 -06:00
|
|
|
on:navigate-previous={navigateAssetBackward}
|
|
|
|
on:navigate-next={navigateAssetForward}
|
|
|
|
on:close={closeViewer}
|
2023-04-20 09:09:27 -05:00
|
|
|
on:unarchived={handleUnarchivedSuccess}
|
2023-01-14 23:49:47 -06:00
|
|
|
/>
|
|
|
|
{/if}
|