|
|
|
|
@ -10,6 +10,7 @@
|
|
|
|
|
import { AppRoute, AssetAction, ProjectionType } from '$lib/constants';
|
|
|
|
|
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
|
|
|
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
|
|
|
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
|
|
|
|
import { preloadManager } from '$lib/managers/PreloadManager.svelte';
|
|
|
|
|
import { closeEditorCofirm } from '$lib/stores/asset-editor.store';
|
|
|
|
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
|
|
|
|
@ -51,8 +52,6 @@
|
|
|
|
|
import SlideshowBar from './slideshow-bar.svelte';
|
|
|
|
|
import VideoViewer from './video-wrapper-viewer.svelte';
|
|
|
|
|
|
|
|
|
|
type HasAsset = boolean;
|
|
|
|
|
|
|
|
|
|
export type AssetCursor = {
|
|
|
|
|
current: AssetResponseDto;
|
|
|
|
|
nextAsset: AssetResponseDto | undefined | null;
|
|
|
|
|
@ -69,10 +68,9 @@
|
|
|
|
|
preAction?: PreAction | undefined;
|
|
|
|
|
onAction?: OnAction | undefined;
|
|
|
|
|
showCloseButton?: boolean;
|
|
|
|
|
onClose: (asset: AssetResponseDto) => void;
|
|
|
|
|
onNext: () => Promise<HasAsset>;
|
|
|
|
|
onPrevious: () => Promise<HasAsset>;
|
|
|
|
|
onRandom: () => Promise<{ id: string } | undefined>;
|
|
|
|
|
onClose?: (asset: AssetResponseDto) => void;
|
|
|
|
|
onNavigateToAsset?: (asset: AssetResponseDto | undefined | null) => Promise<boolean>;
|
|
|
|
|
onRandom?: () => Promise<{ id: string } | undefined>;
|
|
|
|
|
copyImage?: () => Promise<void>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -87,8 +85,7 @@
|
|
|
|
|
onAction = undefined,
|
|
|
|
|
showCloseButton,
|
|
|
|
|
onClose,
|
|
|
|
|
onNext,
|
|
|
|
|
onPrevious,
|
|
|
|
|
onNavigateToAsset,
|
|
|
|
|
onRandom,
|
|
|
|
|
copyImage = $bindable(),
|
|
|
|
|
}: Props = $props();
|
|
|
|
|
@ -105,6 +102,8 @@
|
|
|
|
|
const stackSelectedThumbnailSize = 65;
|
|
|
|
|
|
|
|
|
|
let asset = $derived(cursor.current);
|
|
|
|
|
let nextAsset = $derived(cursor.nextAsset);
|
|
|
|
|
let previousAsset = $derived(cursor.previousAsset);
|
|
|
|
|
let appearsInAlbums: AlbumResponseDto[] = $state([]);
|
|
|
|
|
let shouldPlayMotionPhoto = $state(false);
|
|
|
|
|
let sharedLink = getSharedLink();
|
|
|
|
|
@ -220,7 +219,7 @@
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const closeViewer = () => {
|
|
|
|
|
onClose(asset);
|
|
|
|
|
onClose?.(asset);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const closeEditor = () => {
|
|
|
|
|
@ -252,14 +251,19 @@
|
|
|
|
|
if ($slideshowState === SlideshowState.PlaySlideshow && $slideshowNavigation === SlideshowNavigation.Shuffle) {
|
|
|
|
|
hasNext = order === 'previous' ? slideshowHistory.previous() : slideshowHistory.next();
|
|
|
|
|
if (!hasNext) {
|
|
|
|
|
const asset = await onRandom();
|
|
|
|
|
const asset = await onRandom?.();
|
|
|
|
|
if (asset) {
|
|
|
|
|
slideshowHistory.queue(asset);
|
|
|
|
|
hasNext = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (onNavigateToAsset) {
|
|
|
|
|
hasNext =
|
|
|
|
|
order === 'previous'
|
|
|
|
|
? await onNavigateToAsset(cursor.previousAsset)
|
|
|
|
|
: await onNavigateToAsset(cursor.nextAsset);
|
|
|
|
|
} else {
|
|
|
|
|
hasNext = order === 'previous' ? await onPrevious() : await onNext();
|
|
|
|
|
hasNext = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($slideshowState === SlideshowState.PlaySlideshow) {
|
|
|
|
|
@ -396,7 +400,6 @@
|
|
|
|
|
await ocrManager.getAssetOcr(asset.id);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
|
|
|
asset;
|
|
|
|
|
@ -404,6 +407,34 @@
|
|
|
|
|
preloadManager.preload(cursor.nextAsset);
|
|
|
|
|
preloadManager.preload(cursor.previousAsset);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
|
|
|
asset.id;
|
|
|
|
|
if (viewerKind !== 'PhotoViewer' && viewerKind !== 'ImagePanaramaViewer') {
|
|
|
|
|
eventManager.emit('AssetViewerFree');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const viewerKind = $derived.by(() => {
|
|
|
|
|
if (previewStackedAsset) {
|
|
|
|
|
return asset.type === AssetTypeEnum.Image ? 'StackPhotoViewer' : 'StackVideoViewer';
|
|
|
|
|
}
|
|
|
|
|
if (asset.type === AssetTypeEnum.Image) {
|
|
|
|
|
if (shouldPlayMotionPhoto && asset.livePhotoVideoId) {
|
|
|
|
|
return 'LiveVideoViewer';
|
|
|
|
|
} else if (
|
|
|
|
|
asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR ||
|
|
|
|
|
(asset.originalPath && asset.originalPath.toLowerCase().endsWith('.insp'))
|
|
|
|
|
) {
|
|
|
|
|
return 'ImagePanaramaViewer';
|
|
|
|
|
} else if (isShowEditor && selectedEditType === 'crop') {
|
|
|
|
|
return 'CropArea';
|
|
|
|
|
}
|
|
|
|
|
return 'PhotoViewer';
|
|
|
|
|
}
|
|
|
|
|
return 'VideoViewer';
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<OnEvents onAssetReplace={handleAssetReplace} />
|
|
|
|
|
@ -449,7 +480,7 @@
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if $slideshowState != SlideshowState.None}
|
|
|
|
|
<div class="absolute w-full flex">
|
|
|
|
|
<div class="absolute w-full flex justify-center">
|
|
|
|
|
<SlideshowBar
|
|
|
|
|
{isFullScreen}
|
|
|
|
|
onSetToFullScreen={() => assetViewerHtmlElement?.requestFullscreen?.()}
|
|
|
|
|
@ -460,110 +491,99 @@
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && showNavigation && !isShowEditor}
|
|
|
|
|
<div class="my-auto column-span-1 col-start-1 row-span-full row-start-1 justify-self-start">
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && showNavigation && !isShowEditor && previousAsset}
|
|
|
|
|
<div class="my-auto col-span-1 col-start-1 row-span-full row-start-1 justify-self-start">
|
|
|
|
|
<PreviousAssetAction onPreviousAsset={() => navigateAsset('previous')} />
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<!-- Asset Viewer -->
|
|
|
|
|
<div class="z-[-1] relative col-start-1 col-span-4 row-start-1 row-span-full">
|
|
|
|
|
{#if previewStackedAsset}
|
|
|
|
|
{#key previewStackedAsset.id}
|
|
|
|
|
{#if previewStackedAsset.type === AssetTypeEnum.Image}
|
|
|
|
|
<PhotoViewer
|
|
|
|
|
bind:zoomToggle
|
|
|
|
|
bind:copyImage
|
|
|
|
|
cursor={{ ...cursor, current: previewStackedAsset }}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
haveFadeTransition={false}
|
|
|
|
|
{sharedLink}
|
|
|
|
|
/>
|
|
|
|
|
{:else}
|
|
|
|
|
<VideoViewer
|
|
|
|
|
assetId={previewStackedAsset.id}
|
|
|
|
|
cacheKey={previewStackedAsset.thumbhash}
|
|
|
|
|
projectionType={previewStackedAsset.exifInfo?.projectionType}
|
|
|
|
|
loopVideo={true}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
onClose={closeViewer}
|
|
|
|
|
onVideoEnded={() => navigateAsset()}
|
|
|
|
|
onVideoStarted={handleVideoStarted}
|
|
|
|
|
{playOriginalVideo}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
{/key}
|
|
|
|
|
{:else}
|
|
|
|
|
{#key asset.id}
|
|
|
|
|
{#if asset.type === AssetTypeEnum.Image}
|
|
|
|
|
{#if shouldPlayMotionPhoto && asset.livePhotoVideoId}
|
|
|
|
|
<VideoViewer
|
|
|
|
|
assetId={asset.livePhotoVideoId}
|
|
|
|
|
cacheKey={asset.thumbhash}
|
|
|
|
|
projectionType={asset.exifInfo?.projectionType}
|
|
|
|
|
loopVideo={$slideshowState !== SlideshowState.PlaySlideshow}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
onVideoEnded={() => (shouldPlayMotionPhoto = false)}
|
|
|
|
|
{playOriginalVideo}
|
|
|
|
|
/>
|
|
|
|
|
{:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || (asset.originalPath && asset.originalPath
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.endsWith('.insp'))}
|
|
|
|
|
<ImagePanoramaViewer bind:zoomToggle {asset} />
|
|
|
|
|
{:else if isShowEditor && selectedEditType === 'crop'}
|
|
|
|
|
<CropArea {asset} />
|
|
|
|
|
{:else}
|
|
|
|
|
<PhotoViewer
|
|
|
|
|
bind:zoomToggle
|
|
|
|
|
bind:copyImage
|
|
|
|
|
{cursor}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
{sharedLink}
|
|
|
|
|
haveFadeTransition={$slideshowState !== SlideshowState.None && $slideshowTransition}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
{:else}
|
|
|
|
|
<VideoViewer
|
|
|
|
|
assetId={asset.id}
|
|
|
|
|
cacheKey={asset.thumbhash}
|
|
|
|
|
projectionType={asset.exifInfo?.projectionType}
|
|
|
|
|
loopVideo={$slideshowState !== SlideshowState.PlaySlideshow}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
onClose={closeViewer}
|
|
|
|
|
onVideoEnded={() => navigateAsset()}
|
|
|
|
|
onVideoStarted={handleVideoStarted}
|
|
|
|
|
{playOriginalVideo}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && isShared && ((album && album.isActivityEnabled) || activityManager.commentCount > 0) && !activityManager.isLoading}
|
|
|
|
|
<div class="absolute bottom-0 end-0 mb-20 me-8">
|
|
|
|
|
<ActivityStatus
|
|
|
|
|
disabled={!album?.isActivityEnabled}
|
|
|
|
|
isLiked={activityManager.isLiked}
|
|
|
|
|
numberOfComments={activityManager.commentCount}
|
|
|
|
|
numberOfLikes={activityManager.likeCount}
|
|
|
|
|
onFavorite={handleFavorite}
|
|
|
|
|
onOpenActivityTab={handleOpenActivity}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
{#if viewerKind === 'StackPhotoViewer'}
|
|
|
|
|
<PhotoViewer
|
|
|
|
|
bind:zoomToggle
|
|
|
|
|
bind:copyImage
|
|
|
|
|
cursor={{ ...cursor, current: previewStackedAsset! }}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
haveFadeTransition={false}
|
|
|
|
|
{sharedLink}
|
|
|
|
|
/>
|
|
|
|
|
{:else if viewerKind === 'StackVideoViewer'}
|
|
|
|
|
<VideoViewer
|
|
|
|
|
assetId={previewStackedAsset!.id}
|
|
|
|
|
cacheKey={previewStackedAsset!.thumbhash}
|
|
|
|
|
projectionType={previewStackedAsset!.exifInfo?.projectionType}
|
|
|
|
|
loopVideo={true}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
onClose={closeViewer}
|
|
|
|
|
onVideoEnded={() => navigateAsset()}
|
|
|
|
|
onVideoStarted={handleVideoStarted}
|
|
|
|
|
{playOriginalVideo}
|
|
|
|
|
/>
|
|
|
|
|
{:else if viewerKind === 'LiveVideoViewer'}
|
|
|
|
|
<VideoViewer
|
|
|
|
|
assetId={asset.livePhotoVideoId!}
|
|
|
|
|
cacheKey={asset.thumbhash}
|
|
|
|
|
projectionType={asset.exifInfo?.projectionType}
|
|
|
|
|
loopVideo={$slideshowState !== SlideshowState.PlaySlideshow}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
onVideoEnded={() => (shouldPlayMotionPhoto = false)}
|
|
|
|
|
{playOriginalVideo}
|
|
|
|
|
/>
|
|
|
|
|
{:else if viewerKind === 'ImagePanaramaViewer'}
|
|
|
|
|
<ImagePanoramaViewer bind:zoomToggle {asset} />
|
|
|
|
|
{:else if viewerKind === 'CropArea'}
|
|
|
|
|
<CropArea {asset} />
|
|
|
|
|
{:else if viewerKind === 'PhotoViewer'}
|
|
|
|
|
<PhotoViewer
|
|
|
|
|
bind:zoomToggle
|
|
|
|
|
bind:copyImage
|
|
|
|
|
{cursor}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
{sharedLink}
|
|
|
|
|
haveFadeTransition={$slideshowState !== SlideshowState.None && $slideshowTransition}
|
|
|
|
|
onFree={() => eventManager.emit('AssetViewerFree')}
|
|
|
|
|
/>
|
|
|
|
|
{:else if viewerKind === 'VideoViewer'}
|
|
|
|
|
<VideoViewer
|
|
|
|
|
assetId={asset.id}
|
|
|
|
|
cacheKey={asset.thumbhash}
|
|
|
|
|
projectionType={asset.exifInfo?.projectionType}
|
|
|
|
|
loopVideo={$slideshowState !== SlideshowState.PlaySlideshow}
|
|
|
|
|
onPreviousAsset={() => navigateAsset('previous')}
|
|
|
|
|
onNextAsset={() => navigateAsset('next')}
|
|
|
|
|
onClose={closeViewer}
|
|
|
|
|
onVideoEnded={() => navigateAsset()}
|
|
|
|
|
onVideoStarted={handleVideoStarted}
|
|
|
|
|
{playOriginalVideo}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && asset.type === AssetTypeEnum.Image && !isShowEditor && ocrManager.hasOcrData}
|
|
|
|
|
<div class="absolute bottom-0 end-0 mb-6 me-6">
|
|
|
|
|
<OcrButton />
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
{/key}
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && isShared && ((album && album.isActivityEnabled) || activityManager.commentCount > 0) && !activityManager.isLoading}
|
|
|
|
|
<div class="absolute bottom-0 end-0 mb-20 me-8">
|
|
|
|
|
<ActivityStatus
|
|
|
|
|
disabled={!album?.isActivityEnabled}
|
|
|
|
|
isLiked={activityManager.isLiked}
|
|
|
|
|
numberOfComments={activityManager.commentCount}
|
|
|
|
|
numberOfLikes={activityManager.likeCount}
|
|
|
|
|
onFavorite={handleFavorite}
|
|
|
|
|
onOpenActivityTab={handleOpenActivity}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && asset.type === AssetTypeEnum.Image && !isShowEditor && ocrManager.hasOcrData}
|
|
|
|
|
<div class="absolute bottom-0 end-0 mb-6 me-6">
|
|
|
|
|
<OcrButton />
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && showNavigation && !isShowEditor}
|
|
|
|
|
{#if $slideshowState === SlideshowState.None && showNavigation && !isShowEditor && nextAsset}
|
|
|
|
|
<div class="my-auto col-span-1 col-start-4 row-span-full row-start-1 justify-self-end">
|
|
|
|
|
<NextAssetAction onNextAsset={() => navigateAsset('next')} />
|
|
|
|
|
</div>
|
|
|
|
|
|