2023-06-14 20:47:18 -05:00
|
|
|
<script lang="ts">
|
2023-07-01 00:50:47 -04:00
|
|
|
import { goto } from '$app/navigation';
|
|
|
|
import { page } from '$app/stores';
|
2024-05-31 13:44:04 -04:00
|
|
|
import { shortcuts } from '$lib/actions/shortcut';
|
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
|
|
|
|
2024-02-14 08:09:49 -05:00
|
|
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
2024-04-28 13:39:57 -05:00
|
|
|
import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte';
|
|
|
|
import ArchiveAction from '$lib/components/photos-page/actions/archive-action.svelte';
|
2024-05-31 13:44:04 -04:00
|
|
|
import ChangeDate from '$lib/components/photos-page/actions/change-date-action.svelte';
|
|
|
|
import ChangeLocation from '$lib/components/photos-page/actions/change-location-action.svelte';
|
2024-04-28 13:39:57 -05:00
|
|
|
import CreateSharedLink from '$lib/components/photos-page/actions/create-shared-link.svelte';
|
|
|
|
import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte';
|
|
|
|
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
|
|
|
|
import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte';
|
2024-06-18 03:52:38 +00:00
|
|
|
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
2024-04-28 13:39:57 -05:00
|
|
|
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
|
2024-02-14 08:09:49 -05:00
|
|
|
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
|
|
|
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
|
|
|
|
import { AppRoute, QueryParameter } from '$lib/constants';
|
2024-04-28 13:39:57 -05:00
|
|
|
import { type Viewport } from '$lib/stores/assets.store';
|
2024-02-14 08:09:49 -05:00
|
|
|
import { memoryStore } from '$lib/stores/memory.store';
|
2024-03-27 16:14:29 -04:00
|
|
|
import { getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
|
2024-02-14 08:09:49 -05:00
|
|
|
import { fromLocalDateTime } from '$lib/utils/timeline-util';
|
2024-05-31 13:44:04 -04:00
|
|
|
import { AssetMediaSize, getMemoryLane, type AssetResponseDto } from '@immich/sdk';
|
2024-04-28 13:39:57 -05:00
|
|
|
import {
|
|
|
|
mdiChevronDown,
|
|
|
|
mdiChevronLeft,
|
|
|
|
mdiChevronRight,
|
|
|
|
mdiChevronUp,
|
|
|
|
mdiDotsVertical,
|
|
|
|
mdiPause,
|
|
|
|
mdiPlay,
|
|
|
|
mdiPlus,
|
|
|
|
mdiSelectAll,
|
|
|
|
} from '@mdi/js';
|
2024-02-14 08:09:49 -05:00
|
|
|
import { DateTime } from 'luxon';
|
|
|
|
import { onMount } from 'svelte';
|
|
|
|
import { tweened } from 'svelte/motion';
|
|
|
|
import { fade } from 'svelte/transition';
|
2024-06-04 21:53:00 +02:00
|
|
|
import { t } from 'svelte-i18n';
|
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
|
|
|
import { intersectionObserver } from '$lib/actions/intersection-observer';
|
|
|
|
import { resizeObserver } from '$lib/actions/resize-observer';
|
2024-07-29 16:38:27 +02:00
|
|
|
import { locale } from '$lib/stores/preferences.store';
|
2023-07-01 00:50:47 -04:00
|
|
|
|
2024-02-02 04:18:00 +01:00
|
|
|
const parseIndex = (s: string | null, max: number | null) =>
|
|
|
|
Math.max(Math.min(Number.parseInt(s ?? '') || 0, max ?? 0), 0);
|
2023-07-01 00:50:47 -04:00
|
|
|
|
2024-01-28 01:54:31 +01:00
|
|
|
$: memoryIndex = parseIndex($page.url.searchParams.get(QueryParameter.MEMORY_INDEX), $memoryStore?.length - 1);
|
|
|
|
$: assetIndex = parseIndex($page.url.searchParams.get(QueryParameter.ASSET_INDEX), currentMemory?.assets.length - 1);
|
2023-07-01 00:50:47 -04:00
|
|
|
|
|
|
|
$: previousMemory = $memoryStore?.[memoryIndex - 1];
|
|
|
|
$: currentMemory = $memoryStore?.[memoryIndex];
|
|
|
|
$: nextMemory = $memoryStore?.[memoryIndex + 1];
|
|
|
|
|
|
|
|
$: previousAsset = currentMemory?.assets[assetIndex - 1];
|
|
|
|
$: currentAsset = currentMemory?.assets[assetIndex];
|
|
|
|
$: nextAsset = currentMemory?.assets[assetIndex + 1];
|
|
|
|
|
|
|
|
$: canGoForward = !!(nextMemory || nextAsset);
|
|
|
|
$: canGoBack = !!(previousMemory || previousAsset);
|
|
|
|
|
2024-02-17 11:00:55 -06:00
|
|
|
const viewport: Viewport = { width: 0, height: 0 };
|
2024-01-28 01:54:31 +01:00
|
|
|
const toNextMemory = () => goto(`?${QueryParameter.MEMORY_INDEX}=${memoryIndex + 1}`);
|
|
|
|
const toPreviousMemory = () => goto(`?${QueryParameter.MEMORY_INDEX}=${memoryIndex - 1}`);
|
2023-07-01 00:50:47 -04:00
|
|
|
|
2024-01-28 01:54:31 +01:00
|
|
|
const toNextAsset = () =>
|
|
|
|
goto(`?${QueryParameter.MEMORY_INDEX}=${memoryIndex}&${QueryParameter.ASSET_INDEX}=${assetIndex + 1}`);
|
|
|
|
const toPreviousAsset = () =>
|
|
|
|
goto(`?${QueryParameter.MEMORY_INDEX}=${memoryIndex}&${QueryParameter.ASSET_INDEX}=${assetIndex - 1}`);
|
2023-07-01 00:50:47 -04:00
|
|
|
|
|
|
|
const toNext = () => (nextAsset ? toNextAsset() : toNextMemory());
|
|
|
|
const toPrevious = () => (previousAsset ? toPreviousAsset() : toPreviousMemory());
|
|
|
|
|
|
|
|
const progress = tweened<number>(0, {
|
|
|
|
duration: (from: number, to: number) => (to ? 5000 * (to - from) : 0),
|
|
|
|
});
|
|
|
|
|
|
|
|
const play = () => progress.set(1);
|
|
|
|
const pause = () => progress.set($progress);
|
|
|
|
|
|
|
|
let resetPromise = Promise.resolve();
|
|
|
|
const reset = () => (resetPromise = progress.set(0));
|
|
|
|
|
|
|
|
let paused = false;
|
|
|
|
|
|
|
|
// Play or pause progress when the paused state changes.
|
2024-08-05 19:13:00 +00:00
|
|
|
$: {
|
|
|
|
if (paused) {
|
|
|
|
handlePromiseError(pause());
|
|
|
|
} else {
|
|
|
|
handlePromiseError(play());
|
|
|
|
}
|
|
|
|
}
|
2023-07-01 00:50:47 -04:00
|
|
|
|
|
|
|
// Progress should be paused when it's no longer possible to advance.
|
|
|
|
$: paused ||= !canGoForward || galleryInView;
|
|
|
|
|
|
|
|
// Advance to the next asset or memory when progress is complete.
|
2024-08-05 19:13:00 +00:00
|
|
|
$: {
|
|
|
|
if ($progress === 1) {
|
|
|
|
handlePromiseError(toNext());
|
|
|
|
}
|
|
|
|
}
|
2023-07-01 00:50:47 -04:00
|
|
|
|
|
|
|
// Progress should be resumed when reset and not paused.
|
2024-08-05 19:13:00 +00:00
|
|
|
$: {
|
|
|
|
if (!$progress && !paused) {
|
|
|
|
handlePromiseError(play());
|
|
|
|
}
|
|
|
|
}
|
2023-07-01 00:50:47 -04:00
|
|
|
|
|
|
|
// Progress should be reset when the current memory or asset changes.
|
2024-08-05 19:13:00 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
2024-02-27 08:37:37 -08:00
|
|
|
$: memoryIndex, assetIndex, handlePromiseError(reset());
|
2023-07-01 00:50:47 -04:00
|
|
|
|
2024-04-28 13:39:57 -05:00
|
|
|
let selectedAssets: Set<AssetResponseDto> = new Set();
|
|
|
|
$: isMultiSelectionMode = selectedAssets.size > 0;
|
|
|
|
|
|
|
|
let memoryGallery: HTMLElement;
|
|
|
|
let memoryWrapper: HTMLElement;
|
|
|
|
let galleryInView = false;
|
|
|
|
|
|
|
|
$: isAllArchived = [...selectedAssets].every((asset) => asset.isArchived);
|
|
|
|
$: isAllFavorite = [...selectedAssets].every((asset) => asset.isFavorite);
|
|
|
|
$: {
|
|
|
|
if (!galleryInView) {
|
|
|
|
selectedAssets = new Set();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-01 00:50:47 -04:00
|
|
|
onMount(async () => {
|
|
|
|
if (!$memoryStore) {
|
2023-10-04 18:11:11 -04:00
|
|
|
const localTime = new Date();
|
2024-02-14 08:09:49 -05:00
|
|
|
$memoryStore = await getMemoryLane({
|
2023-10-04 18:11:11 -04:00
|
|
|
month: localTime.getMonth() + 1,
|
|
|
|
day: localTime.getDate(),
|
2023-07-01 00:50:47 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-04-28 13:39:57 -05:00
|
|
|
const triggerAssetUpdate = () => (currentMemory.assets = currentMemory.assets);
|
|
|
|
|
|
|
|
const onAssetDelete = (assetIds: string[]) => {
|
|
|
|
const assetIdSet = new Set(assetIds);
|
|
|
|
currentMemory.assets = currentMemory.assets.filter((a: AssetResponseDto) => !assetIdSet.has(a.id));
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleSelectAll = () => {
|
|
|
|
selectedAssets = new Set(currentMemory.assets);
|
|
|
|
};
|
2023-06-14 20:47:18 -05:00
|
|
|
</script>
|
|
|
|
|
2024-03-15 00:16:55 +01:00
|
|
|
<svelte:window
|
|
|
|
use:shortcuts={[
|
|
|
|
{ shortcut: { key: 'ArrowRight' }, onShortcut: () => canGoForward && toNext() },
|
|
|
|
{ shortcut: { key: 'ArrowLeft' }, onShortcut: () => canGoBack && toPrevious() },
|
|
|
|
{ shortcut: { key: 'Escape' }, onShortcut: () => goto(AppRoute.PHOTOS) },
|
|
|
|
]}
|
|
|
|
/>
|
2023-06-21 23:28:58 +03:00
|
|
|
|
2024-04-28 13:39:57 -05:00
|
|
|
{#if isMultiSelectionMode}
|
|
|
|
<div class="sticky top-0 z-[90]">
|
|
|
|
<AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}>
|
|
|
|
<CreateSharedLink />
|
2024-06-04 21:53:00 +02:00
|
|
|
<CircleIconButton title={$t('select_all')} icon={mdiSelectAll} on:click={handleSelectAll} />
|
2024-04-28 13:39:57 -05:00
|
|
|
|
2024-06-18 03:52:38 +00:00
|
|
|
<ButtonContextMenu icon={mdiPlus} title={$t('add_to')}>
|
2024-04-28 13:39:57 -05:00
|
|
|
<AddToAlbum />
|
|
|
|
<AddToAlbum shared />
|
2024-06-18 03:52:38 +00:00
|
|
|
</ButtonContextMenu>
|
2024-04-28 13:39:57 -05:00
|
|
|
|
|
|
|
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={triggerAssetUpdate} />
|
|
|
|
|
2024-06-18 03:52:38 +00:00
|
|
|
<ButtonContextMenu icon={mdiDotsVertical} title={$t('add')}>
|
2024-04-28 13:39:57 -05:00
|
|
|
<DownloadAction menuItem />
|
|
|
|
<ChangeDate menuItem />
|
|
|
|
<ChangeLocation menuItem />
|
|
|
|
<ArchiveAction menuItem unarchive={isAllArchived} onArchive={triggerAssetUpdate} />
|
|
|
|
<DeleteAssets menuItem {onAssetDelete} />
|
2024-06-18 03:52:38 +00:00
|
|
|
</ButtonContextMenu>
|
2024-04-28 13:39:57 -05:00
|
|
|
</AssetSelectControlBar>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
|
2023-06-14 20:47:18 -05:00
|
|
|
<section id="memory-viewer" class="w-full bg-immich-dark-gray" bind:this={memoryWrapper}>
|
2023-07-01 00:50:47 -04:00
|
|
|
{#if currentMemory}
|
2023-12-15 03:54:21 +01:00
|
|
|
<ControlAppBar on:close={() => goto(AppRoute.PHOTOS)} forceDark>
|
2023-07-01 00:50:47 -04:00
|
|
|
<svelte:fragment slot="leading">
|
|
|
|
<p class="text-lg">
|
2024-07-05 15:06:35 +02:00
|
|
|
{$memoryLaneTitle(currentMemory.yearsAgo)}
|
2023-07-01 00:50:47 -04:00
|
|
|
</p>
|
|
|
|
</svelte:fragment>
|
|
|
|
|
2024-03-12 03:36:34 +00:00
|
|
|
{#if canGoForward}
|
2023-07-18 13:19:39 -05:00
|
|
|
<div class="flex place-content-center place-items-center gap-2 overflow-hidden">
|
2024-03-29 12:48:07 +00:00
|
|
|
<CircleIconButton
|
2024-06-04 21:53:00 +02:00
|
|
|
title={paused ? $t('play_memories') : $t('pause_memories')}
|
2024-03-29 12:48:07 +00:00
|
|
|
icon={paused ? mdiPlay : mdiPause}
|
|
|
|
on:click={() => (paused = !paused)}
|
2024-04-27 22:29:43 +00:00
|
|
|
class="hover:text-black"
|
2024-03-29 12:48:07 +00:00
|
|
|
/>
|
2023-07-01 00:50:47 -04:00
|
|
|
|
2024-02-02 04:18:00 +01:00
|
|
|
{#each currentMemory.assets as _, index}
|
2024-05-27 09:06:15 +02:00
|
|
|
<a
|
2024-01-28 01:54:31 +01:00
|
|
|
class="relative w-full py-2"
|
2024-05-27 09:06:15 +02:00
|
|
|
href="?{QueryParameter.MEMORY_INDEX}={memoryIndex}&{QueryParameter.ASSET_INDEX}={index}"
|
2024-01-28 01:54:31 +01:00
|
|
|
>
|
2023-07-18 13:19:39 -05:00
|
|
|
<span class="absolute left-0 h-[2px] w-full bg-gray-500" />
|
2023-07-01 00:50:47 -04:00
|
|
|
{#await resetPromise}
|
2024-02-02 04:18:00 +01:00
|
|
|
<span class="absolute left-0 h-[2px] bg-white" style:width={`${index < assetIndex ? 100 : 0}%`} />
|
2023-07-01 00:50:47 -04:00
|
|
|
{:then}
|
|
|
|
<span
|
|
|
|
class="absolute left-0 h-[2px] bg-white"
|
2024-02-02 04:18:00 +01:00
|
|
|
style:width={`${index < assetIndex ? 100 : index > assetIndex ? 0 : $progress * 100}%`}
|
2023-07-01 00:50:47 -04:00
|
|
|
/>
|
|
|
|
{/await}
|
2024-05-27 09:06:15 +02:00
|
|
|
</a>
|
2023-07-01 00:50:47 -04:00
|
|
|
{/each}
|
|
|
|
|
|
|
|
<div>
|
|
|
|
<p class="text-small">
|
2024-07-29 16:38:27 +02:00
|
|
|
{(assetIndex + 1).toLocaleString($locale)}/{currentMemory.assets.length.toLocaleString($locale)}
|
2023-07-01 00:50:47 -04:00
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
</ControlAppBar>
|
|
|
|
|
|
|
|
{#if galleryInView}
|
|
|
|
<div
|
2024-07-02 12:59:11 +02:00
|
|
|
class="fixed top-20 z-30 left-1/2 -translate-x-1/2 transition-opacity"
|
2023-07-01 00:50:47 -04:00
|
|
|
class:opacity-0={!galleryInView}
|
|
|
|
class:opacity-100={galleryInView}
|
|
|
|
>
|
2024-05-27 09:06:15 +02:00
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
on:click={() => memoryWrapper.scrollIntoView({ behavior: 'smooth' })}
|
|
|
|
disabled={!galleryInView}
|
|
|
|
>
|
2024-06-04 21:53:00 +02:00
|
|
|
<CircleIconButton title={$t('hide_gallery')} icon={mdiChevronUp} color="light" />
|
2023-07-01 00:50:47 -04:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
<!-- Viewer -->
|
2023-07-18 13:19:39 -05:00
|
|
|
<section class="overflow-hidden pt-20">
|
2023-07-01 00:50:47 -04:00
|
|
|
<div
|
2023-07-18 13:19:39 -05:00
|
|
|
class="ml-[-100%] box-border flex h-[calc(100vh_-_180px)] w-[300%] items-center justify-center gap-10 overflow-hidden"
|
2023-07-01 00:50:47 -04:00
|
|
|
>
|
|
|
|
<!-- PREVIOUS MEMORY -->
|
|
|
|
<div
|
2023-07-18 13:19:39 -05:00
|
|
|
class="h-1/2 w-[20vw] rounded-2xl"
|
2023-07-01 00:50:47 -04:00
|
|
|
class:opacity-25={previousMemory}
|
|
|
|
class:opacity-0={!previousMemory}
|
|
|
|
class:hover:opacity-70={previousMemory}
|
|
|
|
>
|
2024-05-27 09:06:15 +02:00
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
class="relative h-full w-full rounded-2xl"
|
|
|
|
disabled={!previousMemory}
|
|
|
|
on:click={toPreviousMemory}
|
|
|
|
>
|
2024-02-18 20:18:40 +01:00
|
|
|
{#if previousMemory}
|
|
|
|
<img
|
|
|
|
class="h-full w-full rounded-2xl object-cover"
|
2024-05-31 13:44:04 -04:00
|
|
|
src={getAssetThumbnailUrl({ id: previousMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
2024-06-04 21:53:00 +02:00
|
|
|
alt={$t('previous_memory')}
|
2024-02-18 20:18:40 +01:00
|
|
|
draggable="false"
|
|
|
|
/>
|
|
|
|
{:else}
|
|
|
|
<enhanced:img
|
|
|
|
class="h-full w-full rounded-2xl object-cover"
|
2024-02-19 19:42:22 +01:00
|
|
|
src="$lib/assets/no-thumbnail.png"
|
2024-02-18 20:18:40 +01:00
|
|
|
sizes="min(271px,186px)"
|
2024-06-04 21:53:00 +02:00
|
|
|
alt={$t('previous_memory')}
|
2024-02-18 20:18:40 +01:00
|
|
|
draggable="false"
|
|
|
|
/>
|
|
|
|
{/if}
|
2023-07-01 00:50:47 -04:00
|
|
|
|
|
|
|
{#if previousMemory}
|
2023-07-18 13:19:39 -05:00
|
|
|
<div class="absolute bottom-4 right-4 text-left text-white">
|
2024-06-04 21:53:00 +02:00
|
|
|
<p class="text-xs font-semibold text-gray-200">{$t('previous').toUpperCase()}</p>
|
2024-07-05 15:06:35 +02:00
|
|
|
<p class="text-xl">{$memoryLaneTitle(previousMemory.yearsAgo)}</p>
|
2023-07-01 00:50:47 -04:00
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- CURRENT MEMORY -->
|
|
|
|
<div
|
2023-07-18 13:19:39 -05:00
|
|
|
class="main-view relative flex h-full w-[70vw] place-content-center place-items-center rounded-2xl bg-black"
|
2023-07-01 00:50:47 -04:00
|
|
|
>
|
2023-12-19 18:01:22 +01:00
|
|
|
<div class="relative h-full w-full rounded-2xl bg-black">
|
2023-07-01 00:50:47 -04:00
|
|
|
{#key currentAsset.id}
|
|
|
|
<img
|
2023-07-15 20:13:04 -05:00
|
|
|
transition:fade
|
2023-07-18 13:19:39 -05:00
|
|
|
class="h-full w-full rounded-2xl object-contain transition-all"
|
2024-05-31 13:44:04 -04:00
|
|
|
src={getAssetThumbnailUrl({ id: currentAsset.id, size: AssetMediaSize.Preview })}
|
2024-03-03 16:42:17 -05:00
|
|
|
alt={currentAsset.exifInfo?.description}
|
2023-07-01 00:50:47 -04:00
|
|
|
draggable="false"
|
|
|
|
/>
|
|
|
|
{/key}
|
2023-12-19 18:01:22 +01:00
|
|
|
<!-- CONTROL BUTTONS -->
|
|
|
|
{#if canGoBack}
|
|
|
|
<div class="absolute top-1/2 left-0 ml-4">
|
2024-06-04 21:53:00 +02:00
|
|
|
<CircleIconButton
|
|
|
|
title={$t('previous_memory')}
|
|
|
|
icon={mdiChevronLeft}
|
|
|
|
color="dark"
|
|
|
|
on:click={toPrevious}
|
|
|
|
/>
|
2023-12-19 18:01:22 +01:00
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
{#if canGoForward}
|
|
|
|
<div class="absolute top-1/2 right-0 mr-4">
|
2024-06-04 21:53:00 +02:00
|
|
|
<CircleIconButton title={$t('next_memory')} icon={mdiChevronRight} color="dark" on:click={toNext} />
|
2023-12-19 18:01:22 +01:00
|
|
|
</div>
|
|
|
|
{/if}
|
2023-07-01 00:50:47 -04:00
|
|
|
|
2023-07-18 13:19:39 -05:00
|
|
|
<div class="absolute left-8 top-4 text-sm font-medium text-white">
|
2023-07-01 00:50:47 -04:00
|
|
|
<p>
|
2023-10-06 08:12:09 -04:00
|
|
|
{fromLocalDateTime(currentMemory.assets[0].localDateTime).toLocaleString(DateTime.DATE_FULL)}
|
2023-07-01 00:50:47 -04:00
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
{currentAsset.exifInfo?.city || ''}
|
|
|
|
{currentAsset.exifInfo?.country || ''}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- NEXT MEMORY -->
|
|
|
|
<div
|
2023-07-18 13:19:39 -05:00
|
|
|
class="h-1/2 w-[20vw] rounded-xl"
|
2023-07-01 00:50:47 -04:00
|
|
|
class:opacity-25={nextMemory}
|
|
|
|
class:opacity-0={!nextMemory}
|
|
|
|
class:hover:opacity-70={nextMemory}
|
|
|
|
>
|
2024-05-27 09:06:15 +02:00
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
class="relative h-full w-full rounded-2xl"
|
|
|
|
on:click={toNextMemory}
|
|
|
|
disabled={!nextMemory}
|
|
|
|
>
|
2024-02-18 20:18:40 +01:00
|
|
|
{#if nextMemory}
|
|
|
|
<img
|
|
|
|
class="h-full w-full rounded-2xl object-cover"
|
2024-05-31 13:44:04 -04:00
|
|
|
src={getAssetThumbnailUrl({ id: nextMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
2024-06-04 21:53:00 +02:00
|
|
|
alt={$t('next_memory')}
|
2024-02-18 20:18:40 +01:00
|
|
|
draggable="false"
|
|
|
|
/>
|
|
|
|
{:else}
|
|
|
|
<enhanced:img
|
|
|
|
class="h-full w-full rounded-2xl object-cover"
|
2024-02-19 19:42:22 +01:00
|
|
|
src="$lib/assets/no-thumbnail.png"
|
2024-02-18 20:18:40 +01:00
|
|
|
sizes="min(271px,186px)"
|
2024-06-04 21:53:00 +02:00
|
|
|
alt={$t('next_memory')}
|
2024-02-18 20:18:40 +01:00
|
|
|
draggable="false"
|
|
|
|
/>
|
|
|
|
{/if}
|
2023-07-01 00:50:47 -04:00
|
|
|
|
|
|
|
{#if nextMemory}
|
2023-07-18 13:19:39 -05:00
|
|
|
<div class="absolute bottom-4 left-4 text-left text-white">
|
2024-06-04 21:53:00 +02:00
|
|
|
<p class="text-xs font-semibold text-gray-200">{$t('up_next').toUpperCase()}</p>
|
2024-07-05 15:06:35 +02:00
|
|
|
<p class="text-xl">{$memoryLaneTitle(nextMemory.yearsAgo)}</p>
|
2023-07-01 00:50:47 -04:00
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
|
2024-03-12 03:36:34 +00:00
|
|
|
<!-- GALLERY VIEWER -->
|
2024-07-02 12:59:11 +02:00
|
|
|
<section class="bg-immich-dark-gray p-4">
|
2023-07-01 00:50:47 -04:00
|
|
|
<div
|
2024-07-02 12:59:11 +02:00
|
|
|
class="sticky mb-10 flex place-content-center place-items-center transition-all"
|
2023-07-01 00:50:47 -04:00
|
|
|
class:opacity-0={galleryInView}
|
|
|
|
class:opacity-100={!galleryInView}
|
|
|
|
>
|
2024-05-04 18:29:50 +00:00
|
|
|
<CircleIconButton
|
2024-06-04 21:53:00 +02:00
|
|
|
title={$t('show_gallery')}
|
2024-05-04 18:29:50 +00:00
|
|
|
icon={mdiChevronDown}
|
|
|
|
color="light"
|
|
|
|
on:click={() => memoryGallery.scrollIntoView({ behavior: 'smooth' })}
|
|
|
|
/>
|
2023-07-01 00:50:47 -04:00
|
|
|
</div>
|
|
|
|
|
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
|
|
|
<div
|
|
|
|
id="gallery-memory"
|
|
|
|
use:intersectionObserver={{
|
|
|
|
onIntersect: () => (galleryInView = true),
|
|
|
|
onSeparate: () => (galleryInView = false),
|
|
|
|
bottom: '-200px',
|
|
|
|
}}
|
|
|
|
use:resizeObserver={({ height, width }) => ((viewport.height = height), (viewport.width = width))}
|
|
|
|
bind:this={memoryGallery}
|
2023-07-01 00:50:47 -04:00
|
|
|
>
|
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
|
|
|
<GalleryViewer assets={currentMemory.assets} {viewport} bind:selectedAssets />
|
|
|
|
</div>
|
2023-07-01 00:50:47 -04:00
|
|
|
</section>
|
|
|
|
{/if}
|
2023-06-14 20:47:18 -05:00
|
|
|
</section>
|
|
|
|
|
|
|
|
<style>
|
2023-07-01 00:50:47 -04:00
|
|
|
.main-view {
|
2023-11-27 11:42:04 -05:00
|
|
|
box-shadow:
|
|
|
|
0 4px 4px 0 rgba(0, 0, 0, 0.3),
|
|
|
|
0 8px 12px 6px rgba(0, 0, 0, 0.15);
|
2023-07-01 00:50:47 -04:00
|
|
|
}
|
2023-06-14 20:47:18 -05:00
|
|
|
</style>
|