feat(web): persist scroll position on navigation back to album (#11388)
Co-authored-by: Calum Dingwall <caburum@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import { scrollMemory } from '$lib/actions/scroll-memory';
|
||||
import { AlbumFilter, albumViewSettings } from '$lib/stores/preferences.store';
|
||||
import { createAlbumAndRedirect } from '$lib/utils/album-utils';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
@@ -8,6 +9,7 @@
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import GroupTab from '$lib/components/elements/group-tab.svelte';
|
||||
import SearchBar from '$lib/components/elements/search-bar.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface Props {
|
||||
@@ -20,7 +22,7 @@
|
||||
let albumGroups: string[] = $state([]);
|
||||
</script>
|
||||
|
||||
<UserPageLayout title={data.meta.title}>
|
||||
<UserPageLayout title={data.meta.title} use={[[scrollMemory, { routeStartsWith: AppRoute.ALBUMS }]]}>
|
||||
{#snippet buttons()}
|
||||
<div class="flex place-items-center gap-2">
|
||||
<AlbumsControls {albumGroups} bind:searchQuery />
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate, goto, onNavigate } from '$app/navigation';
|
||||
import { scrollMemoryClearer } from '$lib/actions/scroll-memory';
|
||||
import AlbumDescription from '$lib/components/album-page/album-description.svelte';
|
||||
import AlbumOptions from '$lib/components/album-page/album-options.svelte';
|
||||
import AlbumSummary from '$lib/components/album-page/album-summary.svelte';
|
||||
@@ -430,7 +431,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex overflow-hidden">
|
||||
<div class="flex overflow-hidden" use:scrollMemoryClearer={{ routeStartsWith: AppRoute.ALBUMS }}>
|
||||
<div class="relative w-full shrink">
|
||||
{#if $isMultiSelectState}
|
||||
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { focusTrap } from '$lib/actions/focus-trap';
|
||||
import { scrollMemory } from '$lib/actions/scroll-memory';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
@@ -17,7 +18,7 @@
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { ActionQueryParameterValue, AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { ActionQueryParameterValue, AppRoute, QueryParameter, SessionStorageKey } from '$lib/constants';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { websocketEvents } from '$lib/stores/websocket';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
@@ -50,6 +51,7 @@
|
||||
let showSetBirthDateModal = $state(false);
|
||||
let showMergeModal = $state(false);
|
||||
let personName = $state('');
|
||||
let currentPage = $state(1);
|
||||
let nextPage = $state(data.people.hasNextPage ? 2 : null);
|
||||
let personMerge1 = $state<PersonResponseDto>();
|
||||
let personMerge2 = $state<PersonResponseDto>();
|
||||
@@ -68,6 +70,7 @@
|
||||
handlePromiseError(searchPeopleElement.searchPeople(true, searchName));
|
||||
}
|
||||
}
|
||||
|
||||
return websocketEvents.on('on_person_thumbnail', (personId: string) => {
|
||||
for (const person of people) {
|
||||
if (person.id === personId) {
|
||||
@@ -77,6 +80,36 @@
|
||||
});
|
||||
});
|
||||
|
||||
const loadInitialScroll = () =>
|
||||
new Promise<void>((resolve) => {
|
||||
// Load up to previously loaded page when returning.
|
||||
let newNextPage = sessionStorage.getItem(SessionStorageKey.INFINITE_SCROLL_PAGE);
|
||||
if (newNextPage && nextPage) {
|
||||
let startingPage = nextPage,
|
||||
pagesToLoad = Number.parseInt(newNextPage) - nextPage;
|
||||
|
||||
if (pagesToLoad) {
|
||||
handlePromiseError(
|
||||
Promise.all(
|
||||
Array.from({ length: pagesToLoad }).map((_, i) => {
|
||||
return getAllPeople({ withHidden: true, page: startingPage + i });
|
||||
}),
|
||||
).then((pages) => {
|
||||
for (const page of pages) {
|
||||
people = people.concat(page.people);
|
||||
}
|
||||
currentPage = startingPage + pagesToLoad - 1;
|
||||
nextPage = pages.at(-1)?.hasNextPage ? startingPage + pagesToLoad : null;
|
||||
resolve(); // wait until extra pages are loaded
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
sessionStorage.removeItem(SessionStorageKey.INFINITE_SCROLL_PAGE);
|
||||
}
|
||||
});
|
||||
|
||||
const loadNextPage = async () => {
|
||||
if (!nextPage) {
|
||||
return;
|
||||
@@ -85,6 +118,9 @@
|
||||
try {
|
||||
const { people: newPeople, hasNextPage } = await getAllPeople({ withHidden: true, page: nextPage });
|
||||
people = people.concat(newPeople);
|
||||
if (nextPage !== null) {
|
||||
currentPage = nextPage;
|
||||
}
|
||||
nextPage = hasNextPage ? nextPage + 1 : null;
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.failed_to_load_people'));
|
||||
@@ -323,6 +359,23 @@
|
||||
<UserPageLayout
|
||||
title={$t('people')}
|
||||
description={countVisiblePeople === 0 && !searchName ? undefined : `(${countVisiblePeople.toLocaleString($locale)})`}
|
||||
use={[
|
||||
[
|
||||
scrollMemory,
|
||||
{
|
||||
routeStartsWith: AppRoute.PEOPLE,
|
||||
beforeSave: () => {
|
||||
if (currentPage) {
|
||||
sessionStorage.setItem(SessionStorageKey.INFINITE_SCROLL_PAGE, currentPage.toString());
|
||||
}
|
||||
},
|
||||
beforeClear: () => {
|
||||
sessionStorage.removeItem(SessionStorageKey.INFINITE_SCROLL_PAGE);
|
||||
},
|
||||
beforeLoad: loadInitialScroll,
|
||||
},
|
||||
],
|
||||
]}
|
||||
>
|
||||
{#snippet buttons()}
|
||||
{#if people.length > 0}
|
||||
|
||||
+12
-3
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate, goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { scrollMemoryClearer } from '$lib/actions/scroll-memory';
|
||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||
import EditNameInput from '$lib/components/faces-page/edit-name-input.svelte';
|
||||
import MergeFaceSelector from '$lib/components/faces-page/merge-face-selector.svelte';
|
||||
@@ -25,7 +26,7 @@
|
||||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { AppRoute, PersonPageViewMode, QueryParameter } from '$lib/constants';
|
||||
import { AppRoute, PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
||||
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { AssetStore } from '$lib/stores/assets.store';
|
||||
@@ -164,7 +165,7 @@
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
|
||||
await goto(previousRoute, { replaceState: true });
|
||||
await goto(previousRoute);
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_hide_person'));
|
||||
}
|
||||
@@ -431,7 +432,15 @@
|
||||
{/if}
|
||||
</header>
|
||||
|
||||
<main class="relative h-screen overflow-hidden bg-immich-bg tall:ml-4 pt-[var(--navbar-height)] dark:bg-immich-dark-bg">
|
||||
<main
|
||||
class="relative h-screen overflow-hidden bg-immich-bg tall:ml-4 pt-[var(--navbar-height)] dark:bg-immich-dark-bg"
|
||||
use:scrollMemoryClearer={{
|
||||
routeStartsWith: AppRoute.PEOPLE,
|
||||
beforeClear: () => {
|
||||
sessionStorage.removeItem(SessionStorageKey.INFINITE_SCROLL_PAGE);
|
||||
},
|
||||
}}
|
||||
>
|
||||
{#key person.id}
|
||||
<AssetGrid
|
||||
enableRouting={true}
|
||||
|
||||
Reference in New Issue
Block a user