mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	dev/add detail viewer to album (#358)
* Rename asset viewer folder * Refactor AssetViewer to be able to user with different component * Refactor AssetViewer to be able to user with different component * Added viewer for album and sharing
This commit is contained in:
		| @@ -164,6 +164,7 @@ export class AlbumRepository implements IAlbumRepository { | |||||||
|       .leftJoinAndSelect('sharedUser.userInfo', 'userInfo') |       .leftJoinAndSelect('sharedUser.userInfo', 'userInfo') | ||||||
|       .leftJoinAndSelect('album.assets', 'assets') |       .leftJoinAndSelect('album.assets', 'assets') | ||||||
|       .leftJoinAndSelect('assets.assetInfo', 'assetInfo') |       .leftJoinAndSelect('assets.assetInfo', 'assetInfo') | ||||||
|  |       .leftJoinAndSelect('assetInfo.exifInfo', 'exifInfo') | ||||||
|       .orderBy('"assetInfo"."createdAt"::timestamptz', 'ASC') |       .orderBy('"assetInfo"."createdAt"::timestamptz', 'ASC') | ||||||
|       .getOne(); |       .getOne(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,15 +1,21 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import { afterNavigate } from '$app/navigation'; | 	import { afterNavigate } from '$app/navigation'; | ||||||
|  | 	import { page } from '$app/stores'; | ||||||
| 	import { AlbumResponseDto, ThumbnailFormat } from '@api'; | 	import { AlbumResponseDto, AssetResponseDto, ThumbnailFormat } from '@api'; | ||||||
| 	import { createEventDispatcher, onMount } from 'svelte'; | 	import { createEventDispatcher, onMount } from 'svelte'; | ||||||
| 	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte'; | 	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte'; | ||||||
| 	import FileImagePlusOutline from 'svelte-material-icons/FileImagePlusOutline.svelte'; | 	import FileImagePlusOutline from 'svelte-material-icons/FileImagePlusOutline.svelte'; | ||||||
|  | 	import AssetViewer from '../asset-viewer/asset-viewer.svelte'; | ||||||
| 	import CircleAvatar from '../shared-components/circle-avatar.svelte'; | 	import CircleAvatar from '../shared-components/circle-avatar.svelte'; | ||||||
| 	import ImmichThumbnail from '../shared-components/immich-thumbnail.svelte'; | 	import ImmichThumbnail from '../shared-components/immich-thumbnail.svelte'; | ||||||
|  |  | ||||||
| 	const dispatch = createEventDispatcher(); | 	const dispatch = createEventDispatcher(); | ||||||
| 	export let album: AlbumResponseDto; | 	export let album: AlbumResponseDto; | ||||||
|  |  | ||||||
|  | 	let isShowAssetViewer = false; | ||||||
|  | 	let selectedAsset: AssetResponseDto; | ||||||
|  | 	let currentViewAssetIndex = 0; | ||||||
|  |  | ||||||
| 	let viewWidth: number; | 	let viewWidth: number; | ||||||
| 	let thumbnailSize: number = 300; | 	let thumbnailSize: number = 300; | ||||||
| 	let border = ''; | 	let border = ''; | ||||||
| @@ -52,6 +58,50 @@ | |||||||
| 			} | 			} | ||||||
| 		}; | 		}; | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
|  | 	const viewAsset = (event: CustomEvent) => { | ||||||
|  | 		const { assetId, deviceId }: { assetId: string; deviceId: string } = event.detail; | ||||||
|  |  | ||||||
|  | 		currentViewAssetIndex = album.assets.findIndex((a) => a.id == assetId); | ||||||
|  | 		selectedAsset = album.assets[currentViewAssetIndex]; | ||||||
|  | 		isShowAssetViewer = true; | ||||||
|  | 		pushState(selectedAsset.id); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	const navigateAssetForward = () => { | ||||||
|  | 		try { | ||||||
|  | 			if (currentViewAssetIndex < album.assets.length - 1) { | ||||||
|  | 				currentViewAssetIndex++; | ||||||
|  | 				selectedAsset = album.assets[currentViewAssetIndex]; | ||||||
|  | 				pushState(selectedAsset.id); | ||||||
|  | 			} | ||||||
|  | 		} catch (e) { | ||||||
|  | 			console.error(e); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	const navigateAssetBackward = () => { | ||||||
|  | 		try { | ||||||
|  | 			if (currentViewAssetIndex > 0) { | ||||||
|  | 				currentViewAssetIndex--; | ||||||
|  | 				selectedAsset = album.assets[currentViewAssetIndex]; | ||||||
|  | 				pushState(selectedAsset.id); | ||||||
|  | 			} | ||||||
|  | 		} catch (e) { | ||||||
|  | 			console.error(e); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	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}`); | ||||||
|  | 	}; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <section class="w-screen h-screen bg-immich-bg"> | <section class="w-screen h-screen bg-immich-bg"> | ||||||
| @@ -97,11 +147,26 @@ | |||||||
| 		<div class="flex flex-wrap gap-1 w-full" bind:clientWidth={viewWidth}> | 		<div class="flex flex-wrap gap-1 w-full" bind:clientWidth={viewWidth}> | ||||||
| 			{#each album.assets as asset} | 			{#each album.assets as asset} | ||||||
| 				{#if album.assets.length < 7} | 				{#if album.assets.length < 7} | ||||||
| 					<ImmichThumbnail {asset} {thumbnailSize} format={ThumbnailFormat.Jpeg} /> | 					<ImmichThumbnail | ||||||
|  | 						{asset} | ||||||
|  | 						{thumbnailSize} | ||||||
|  | 						format={ThumbnailFormat.Jpeg} | ||||||
|  | 						on:viewAsset={viewAsset} | ||||||
|  | 					/> | ||||||
| 				{:else} | 				{:else} | ||||||
| 					<ImmichThumbnail {asset} {thumbnailSize} /> | 					<ImmichThumbnail {asset} {thumbnailSize} on:viewAsset={viewAsset} /> | ||||||
| 				{/if} | 				{/if} | ||||||
| 			{/each} | 			{/each} | ||||||
| 		</div> | 		</div> | ||||||
| 	</section> | 	</section> | ||||||
| </section> | </section> | ||||||
|  |  | ||||||
|  | <!-- Overlay Asset Viewer --> | ||||||
|  | {#if isShowAssetViewer} | ||||||
|  | 	<AssetViewer | ||||||
|  | 		asset={selectedAsset} | ||||||
|  | 		on:navigate-backward={navigateAssetBackward} | ||||||
|  | 		on:navigate-forward={navigateAssetForward} | ||||||
|  | 		on:close={closeViewer} | ||||||
|  | 	/> | ||||||
|  | {/if} | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ | |||||||
| 	import { createEventDispatcher, onDestroy, onMount } from 'svelte'; | 	import { createEventDispatcher, onDestroy, onMount } from 'svelte'; | ||||||
| 	import { fly } from 'svelte/transition'; | 	import { fly } from 'svelte/transition'; | ||||||
| 	import AsserViewerNavBar from './asser-viewer-nav-bar.svelte'; | 	import AsserViewerNavBar from './asser-viewer-nav-bar.svelte'; | ||||||
| 	import { flattenAssetGroupByDate } from '$lib/stores/assets'; |  | ||||||
| 	import ChevronRight from 'svelte-material-icons/ChevronRight.svelte'; | 	import ChevronRight from 'svelte-material-icons/ChevronRight.svelte'; | ||||||
| 	import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte'; | 	import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte'; | ||||||
| 	import PhotoViewer from './photo-viewer.svelte'; | 	import PhotoViewer from './photo-viewer.svelte'; | ||||||
| @@ -14,31 +13,16 @@ | |||||||
| 
 | 
 | ||||||
| 	const dispatch = createEventDispatcher(); | 	const dispatch = createEventDispatcher(); | ||||||
| 
 | 
 | ||||||
| 	export let selectedAsset: AssetResponseDto; | 	export let asset: AssetResponseDto; | ||||||
| 
 |  | ||||||
| 	export let selectedIndex: number; |  | ||||||
| 
 |  | ||||||
| 	let viewDeviceId: string; |  | ||||||
| 	let viewAssetId: string; |  | ||||||
| 
 | 
 | ||||||
| 	let halfLeftHover = false; | 	let halfLeftHover = false; | ||||||
| 	let halfRightHover = false; | 	let halfRightHover = false; | ||||||
| 	let isShowDetail = false; | 	let isShowDetail = false; | ||||||
| 
 | 
 | ||||||
| 	onMount(() => { | 	onMount(() => { | ||||||
| 		viewAssetId = selectedAsset.id; |  | ||||||
| 		viewDeviceId = selectedAsset.deviceId; |  | ||||||
| 		pushState(viewAssetId); |  | ||||||
| 
 |  | ||||||
| 		document.addEventListener('keydown', (keyInfo) => handleKeyboardPress(keyInfo.key)); | 		document.addEventListener('keydown', (keyInfo) => handleKeyboardPress(keyInfo.key)); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	onDestroy(() => { |  | ||||||
| 		document.removeEventListener('keydown', (b) => { |  | ||||||
| 			console.log('destroyed', b); |  | ||||||
| 		}); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	const handleKeyboardPress = (key: string) => { | 	const handleKeyboardPress = (key: string) => { | ||||||
| 		switch (key) { | 		switch (key) { | ||||||
| 			case 'Escape': | 			case 'Escape': | ||||||
| @@ -57,38 +41,17 @@ | |||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const closeViewer = () => { | 	const closeViewer = () => { | ||||||
| 		history.pushState(null, '', `/photos`); |  | ||||||
| 		dispatch('close'); | 		dispatch('close'); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const navigateAssetForward = (e?: Event) => { | 	const navigateAssetForward = (e?: Event) => { | ||||||
| 		e?.stopPropagation(); | 		e?.stopPropagation(); | ||||||
| 
 | 		dispatch('navigate-forward'); | ||||||
| 		const nextAsset = $flattenAssetGroupByDate[selectedIndex + 1]; |  | ||||||
| 		viewDeviceId = nextAsset.deviceId; |  | ||||||
| 		viewAssetId = nextAsset.id; |  | ||||||
| 
 |  | ||||||
| 		selectedIndex = selectedIndex + 1; |  | ||||||
| 		selectedAsset = $flattenAssetGroupByDate[selectedIndex]; |  | ||||||
| 		pushState(viewAssetId); |  | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const navigateAssetBackward = (e?: Event) => { | 	const navigateAssetBackward = (e?: Event) => { | ||||||
| 		e?.stopPropagation(); | 		e?.stopPropagation(); | ||||||
| 
 | 		dispatch('navigate-backward'); | ||||||
| 		const lastAsset = $flattenAssetGroupByDate[selectedIndex - 1]; |  | ||||||
| 		viewDeviceId = lastAsset.deviceId; |  | ||||||
| 		viewAssetId = lastAsset.id; |  | ||||||
| 
 |  | ||||||
| 		selectedIndex = selectedIndex - 1; |  | ||||||
| 		selectedAsset = $flattenAssetGroupByDate[selectedIndex]; |  | ||||||
| 		pushState(viewAssetId); |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	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, '', `/photos/${assetId}`); |  | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const showDetailInfoHandler = () => { | 	const showDetailInfoHandler = () => { | ||||||
| @@ -98,19 +61,20 @@ | |||||||
| 	const downloadFile = async () => { | 	const downloadFile = async () => { | ||||||
| 		if ($session.user) { | 		if ($session.user) { | ||||||
| 			try { | 			try { | ||||||
| 				const imageName = selectedAsset.exifInfo?.imageName ? selectedAsset.exifInfo?.imageName : selectedAsset.id; | 				const imageName = asset.exifInfo?.imageName ? asset.exifInfo?.imageName : asset.id; | ||||||
| 				const imageExtension = selectedAsset.originalPath.split('.')[1]; | 				const imageExtension = asset.originalPath.split('.')[1]; | ||||||
| 				const imageFileName = imageName + '.' + imageExtension; | 				const imageFileName = imageName + '.' + imageExtension; | ||||||
| 
 | 
 | ||||||
| 				// If assets is already download -> return; | 				// If assets is already download -> return; | ||||||
| 				if ($downloadAssets[imageFileName]) { | 				if ($downloadAssets[imageFileName]) { | ||||||
| 					return; | 					return; | ||||||
| 				} | 				} | ||||||
|  | 
 | ||||||
| 				$downloadAssets[imageFileName] = 0; | 				$downloadAssets[imageFileName] = 0; | ||||||
| 
 | 
 | ||||||
| 				const { data, status } = await api.assetApi.downloadFile( | 				const { data, status } = await api.assetApi.downloadFile( | ||||||
| 					selectedAsset.deviceAssetId, | 					asset.deviceAssetId, | ||||||
| 					selectedAsset.deviceId, | 					asset.deviceId, | ||||||
| 					false, | 					false, | ||||||
| 					false, | 					false, | ||||||
| 					{ | 					{ | ||||||
| @@ -123,8 +87,8 @@ | |||||||
| 
 | 
 | ||||||
| 								$downloadAssets[imageFileName] = percentCompleted; | 								$downloadAssets[imageFileName] = percentCompleted; | ||||||
| 							} | 							} | ||||||
| 						}, | 						} | ||||||
| 					}, | 					} | ||||||
| 				); | 				); | ||||||
| 
 | 
 | ||||||
| 				if (!(data instanceof Blob)) { | 				if (!(data instanceof Blob)) { | ||||||
| @@ -162,12 +126,16 @@ | |||||||
| 	class="absolute h-screen w-screen top-0 overflow-y-hidden bg-black z-[999] grid grid-rows-[64px_1fr] grid-cols-4  " | 	class="absolute h-screen w-screen top-0 overflow-y-hidden bg-black z-[999] grid grid-rows-[64px_1fr] grid-cols-4  " | ||||||
| > | > | ||||||
| 	<div class="col-start-1 col-span-4 row-start-1 row-span-1 z-[1000] transition-transform"> | 	<div class="col-start-1 col-span-4 row-start-1 row-span-1 z-[1000] transition-transform"> | ||||||
| 		<AsserViewerNavBar on:goBack={closeViewer} on:showDetail={showDetailInfoHandler} on:download={downloadFile} /> | 		<AsserViewerNavBar | ||||||
|  | 			on:goBack={closeViewer} | ||||||
|  | 			on:showDetail={showDetailInfoHandler} | ||||||
|  | 			on:download={downloadFile} | ||||||
|  | 		/> | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<div | 	<div | ||||||
| 		class={`row-start-2 row-span-end col-start-1 col-span-2 flex place-items-center hover:cursor-pointer w-3/4 ${ | 		class={`row-start-2 row-span-end col-start-1 col-span-2 flex place-items-center hover:cursor-pointer w-3/4 ${ | ||||||
| 			selectedAsset.type == 'VIDEO' ? '' : 'z-[999]' | 			asset.type == AssetTypeEnum.Video ? '' : 'z-[999]' | ||||||
| 		}`} | 		}`} | ||||||
| 		on:mouseenter={() => { | 		on:mouseenter={() => { | ||||||
| 			halfLeftHover = true; | 			halfLeftHover = true; | ||||||
| @@ -188,20 +156,18 @@ | |||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<div class="row-start-1 row-span-full col-start-1 col-span-4"> | 	<div class="row-start-1 row-span-full col-start-1 col-span-4"> | ||||||
| 		{#key selectedIndex} | 		{#key asset.id} | ||||||
| 			{#if viewAssetId && viewDeviceId} | 			{#if asset.type == AssetTypeEnum.Image} | ||||||
| 				{#if selectedAsset.type == AssetTypeEnum.Image} | 				<PhotoViewer assetId={asset.id} deviceId={asset.deviceId} on:close={closeViewer} /> | ||||||
| 					<PhotoViewer assetId={viewAssetId} deviceId={viewDeviceId} on:close={closeViewer} /> | 			{:else} | ||||||
| 				{:else} | 				<VideoViewer assetId={asset.id} on:close={closeViewer} /> | ||||||
| 					<VideoViewer assetId={viewAssetId} on:close={closeViewer} /> |  | ||||||
| 				{/if} |  | ||||||
| 			{/if} | 			{/if} | ||||||
| 		{/key} | 		{/key} | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<div | 	<div | ||||||
| 		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 ${ | 		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 ${ | ||||||
| 			selectedAsset.type == 'VIDEO' ? '' : 'z-[500]' | 			asset.type == AssetTypeEnum.Video ? '' : 'z-[500]' | ||||||
| 		}`} | 		}`} | ||||||
| 		on:click={navigateAssetForward} | 		on:click={navigateAssetForward} | ||||||
| 		on:mouseenter={() => { | 		on:mouseenter={() => { | ||||||
| @@ -228,7 +194,7 @@ | |||||||
| 			class="bg-immich-bg w-[360px] row-span-full transition-all " | 			class="bg-immich-bg w-[360px] row-span-full transition-all " | ||||||
| 			translate="yes" | 			translate="yes" | ||||||
| 		> | 		> | ||||||
| 			<DetailPanel asset={selectedAsset} on:close={() => (isShowDetail = false)} /> | 			<DetailPanel {asset} on:close={() => (isShowDetail = false)} /> | ||||||
| 		</div> | 		</div> | ||||||
| 	{/if} | 	{/if} | ||||||
| </section> | </section> | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| 	import { session } from '$app/stores'; | 	import { session } from '$app/stores'; | ||||||
| 	import { createEventDispatcher, onDestroy } from 'svelte'; | 	import { createEventDispatcher, onDestroy } from 'svelte'; | ||||||
| 	import { fade, fly } from 'svelte/transition'; | 	import { fade, fly } from 'svelte/transition'; | ||||||
| 	import IntersectionObserver from '$lib/components/asset-viewer-page/intersection-observer.svelte'; | 	import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte'; | ||||||
| 	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte'; | 	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte'; | ||||||
| 	import PlayCircleOutline from 'svelte-material-icons/PlayCircleOutline.svelte'; | 	import PlayCircleOutline from 'svelte-material-icons/PlayCircleOutline.svelte'; | ||||||
| 	import PauseCircleOutline from 'svelte-material-icons/PauseCircleOutline.svelte'; | 	import PauseCircleOutline from 'svelte-material-icons/PauseCircleOutline.svelte'; | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ | |||||||
| 			{#if user.email == albumOwner.email} | 			{#if user.email == albumOwner.email} | ||||||
| 				<p class="text-xs text-gray-600">Owned</p> | 				<p class="text-xs text-gray-600">Owned</p> | ||||||
| 			{:else} | 			{:else} | ||||||
| 				<p class="text-xs text-gray-600">Shared by {albumOwner.email}</p> | 				<p class="text-xs text-gray-600">Shared by {albumOwner.firstName} {albumOwner.lastName}</p> | ||||||
| 			{/if} | 			{/if} | ||||||
| 		{/await} | 		{/await} | ||||||
| 	</div> | 	</div> | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|  |  | ||||||
| 	import { blur, fade, slide } from 'svelte/transition'; | 	import { blur, fade, slide } from 'svelte/transition'; | ||||||
|  |  | ||||||
| 	import DownloadPanel from '$lib/components/asset-viewer-page/download-panel.svelte'; | 	import DownloadPanel from '$lib/components/asset-viewer/download-panel.svelte'; | ||||||
| 	import AnnouncementBox from '$lib/components/shared-components/announcement-box.svelte'; | 	import AnnouncementBox from '$lib/components/shared-components/announcement-box.svelte'; | ||||||
| 	import UploadPanel from '$lib/components/shared-components/upload-panel.svelte'; | 	import UploadPanel from '$lib/components/shared-components/upload-panel.svelte'; | ||||||
| 	import { onMount } from 'svelte'; | 	import { onMount } from 'svelte'; | ||||||
|   | |||||||
| @@ -35,8 +35,6 @@ | |||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import { goto } from '$app/navigation'; |  | ||||||
| 
 |  | ||||||
| 	import AlbumViewer from '$lib/components/album-page/album-viewer.svelte'; | 	import AlbumViewer from '$lib/components/album-page/album-viewer.svelte'; | ||||||
| 
 | 
 | ||||||
| 	export let album: AlbumResponseDto; | 	export let album: AlbumResponseDto; | ||||||
							
								
								
									
										27
									
								
								web/src/routes/albums/[albumId]/photos/[assetId].svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								web/src/routes/albums/[albumId]/photos/[assetId].svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | <script context="module" lang="ts"> | ||||||
|  | 	export const prerender = false; | ||||||
|  |  | ||||||
|  | 	import type { Load } from '@sveltejs/kit'; | ||||||
|  |  | ||||||
|  | 	export const load: Load = async ({ session, params }) => { | ||||||
|  | 		if (!session.user) { | ||||||
|  | 			return { | ||||||
|  | 				status: 302, | ||||||
|  | 				redirect: '/auth/login' | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 		const albumId = params['albumId']; | ||||||
|  |  | ||||||
|  | 		if (albumId) { | ||||||
|  | 			return { | ||||||
|  | 				status: 302, | ||||||
|  | 				redirect: `/albums/${albumId}` | ||||||
|  | 			}; | ||||||
|  | 		} else { | ||||||
|  | 			return { | ||||||
|  | 				status: 302, | ||||||
|  | 				redirect: `/photos` | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | </script> | ||||||
| @@ -33,7 +33,7 @@ | |||||||
| 	import { assetsGroupByDate, flattenAssetGroupByDate } from '$lib/stores/assets'; | 	import { assetsGroupByDate, flattenAssetGroupByDate } from '$lib/stores/assets'; | ||||||
| 	import ImmichThumbnail from '$lib/components/shared-components/immich-thumbnail.svelte'; | 	import ImmichThumbnail from '$lib/components/shared-components/immich-thumbnail.svelte'; | ||||||
| 	import moment from 'moment'; | 	import moment from 'moment'; | ||||||
| 	import AssetViewer from '$lib/components/asset-viewer-page/asset-viewer.svelte'; | 	import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte'; | ||||||
| 	import { fileUploader } from '$lib/utils/file-uploader'; | 	import { fileUploader } from '$lib/utils/file-uploader'; | ||||||
| 	import { AssetResponseDto } from '@api'; | 	import { AssetResponseDto } from '@api'; | ||||||
| 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; | 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; | ||||||
| @@ -42,13 +42,14 @@ | |||||||
|  |  | ||||||
| 	let selectedGroupThumbnail: number | null; | 	let selectedGroupThumbnail: number | null; | ||||||
| 	let isMouseOverGroup: boolean; | 	let isMouseOverGroup: boolean; | ||||||
|  |  | ||||||
| 	$: if (isMouseOverGroup == false) { | 	$: if (isMouseOverGroup == false) { | ||||||
| 		selectedGroupThumbnail = null; | 		selectedGroupThumbnail = null; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	let isShowAsset = false; | 	let isShowAssetViewer = false; | ||||||
| 	let currentViewAssetIndex = 0; | 	let currentViewAssetIndex = 0; | ||||||
| 	let currentSelectedAsset: AssetResponseDto; | 	let selectedAsset: AssetResponseDto; | ||||||
|  |  | ||||||
| 	const thumbnailMouseEventHandler = (event: CustomEvent) => { | 	const thumbnailMouseEventHandler = (event: CustomEvent) => { | ||||||
| 		const { selectedGroupIndex }: { selectedGroupIndex: number } = event.detail; | 		const { selectedGroupIndex }: { selectedGroupIndex: number } = event.detail; | ||||||
| @@ -60,8 +61,9 @@ | |||||||
| 		const { assetId, deviceId }: { assetId: string; deviceId: string } = event.detail; | 		const { assetId, deviceId }: { assetId: string; deviceId: string } = event.detail; | ||||||
|  |  | ||||||
| 		currentViewAssetIndex = $flattenAssetGroupByDate.findIndex((a) => a.id == assetId); | 		currentViewAssetIndex = $flattenAssetGroupByDate.findIndex((a) => a.id == assetId); | ||||||
| 		currentSelectedAsset = $flattenAssetGroupByDate[currentViewAssetIndex]; | 		selectedAsset = $flattenAssetGroupByDate[currentViewAssetIndex]; | ||||||
| 		isShowAsset = true; | 		isShowAssetViewer = true; | ||||||
|  | 		pushState(selectedAsset.id); | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	const uploadClickedHandler = async () => { | 	const uploadClickedHandler = async () => { | ||||||
| @@ -91,6 +93,41 @@ | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  | 	const navigateAssetForward = () => { | ||||||
|  | 		try { | ||||||
|  | 			if (currentViewAssetIndex < $flattenAssetGroupByDate.length - 1) { | ||||||
|  | 				currentViewAssetIndex++; | ||||||
|  | 				selectedAsset = $flattenAssetGroupByDate[currentViewAssetIndex]; | ||||||
|  | 				pushState(selectedAsset.id); | ||||||
|  | 			} | ||||||
|  | 		} catch (e) { | ||||||
|  | 			console.log('Error navigating asset forward', e); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	const navigateAssetBackward = () => { | ||||||
|  | 		try { | ||||||
|  | 			if (currentViewAssetIndex > 0) { | ||||||
|  | 				currentViewAssetIndex--; | ||||||
|  | 				selectedAsset = $flattenAssetGroupByDate[currentViewAssetIndex]; | ||||||
|  | 				pushState(selectedAsset.id); | ||||||
|  | 			} | ||||||
|  | 		} catch (e) { | ||||||
|  | 			console.log('Error navigating asset backward', e); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	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, '', `/photos/${assetId}`); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	const closeViewer = () => { | ||||||
|  | 		isShowAssetViewer = false; | ||||||
|  | 		history.pushState(null, '', `/photos`); | ||||||
|  | 	}; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <svelte:head> | <svelte:head> | ||||||
| @@ -149,10 +186,11 @@ | |||||||
| </section> | </section> | ||||||
|  |  | ||||||
| <!-- Overlay Asset Viewer --> | <!-- Overlay Asset Viewer --> | ||||||
| {#if isShowAsset} | {#if isShowAssetViewer} | ||||||
| 	<AssetViewer | 	<AssetViewer | ||||||
| 		selectedAsset={currentSelectedAsset} | 		asset={selectedAsset} | ||||||
| 		selectedIndex={currentViewAssetIndex} | 		on:navigate-backward={navigateAssetBackward} | ||||||
| 		on:close={() => (isShowAsset = false)} | 		on:navigate-forward={navigateAssetForward} | ||||||
|  | 		on:close={closeViewer} | ||||||
| 	/> | 	/> | ||||||
| {/if} | {/if} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user