preserver infinite scroll positions (#615)

This commit is contained in:
ABevier 2023-05-16 20:05:19 -04:00 committed by GitHub
parent 87dd224f9a
commit c0c2472b12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 4 deletions

View file

@ -10,7 +10,8 @@
import Package from "./package.svelte";
import NoInstalls from "./no-installs.svelte";
import NoUpdates from "./no-updates.svelte";
import { packagesStore } from "$libs/stores";
import { packagesStore, scrollStore } from "$libs/stores";
import { afterUpdate, beforeUpdate } from "svelte";
const { packageList: allPackages } = packagesStore;
export let packageFilter: SideMenuOptions = SideMenuOptions.all;
@ -20,6 +21,11 @@
let loadMore = 9;
let limit = loadMore + 9;
const updateLimit = (value: number) => {
limit += value;
scrollStore.setLimit(packageFilter, limit);
};
// TODO: figure out a better type strategy here so that this breaks if SideMenuOptions is updated
const pkgFilters: { [key: string]: (pkg: GUIPackage) => boolean } = {
[SideMenuOptions.all]: (_pkg: GUIPackage) => true,
@ -46,6 +52,7 @@
const onScroll = (e: Event) => {
const target = e.target as HTMLInputElement;
scrollY = target.scrollTop || 0;
scrollStore.setScrollPosition(packageFilter, scrollY);
};
$: packages = $allPackages.filter(pkgFilters[packageFilter] || pkgFilters.all);
@ -56,13 +63,37 @@
const minCardRows = Math.floor(node.scrollHeight / assumedCardHeight);
if (cardRows < minCardRows) {
const addLimit = 3 * (minCardRows - cardRows);
limit += addLimit;
updateLimit(addLimit);
}
};
let scrollElement: any = null;
let prevFilter: SideMenuOptions | null;
// Restore the limit before the update...
beforeUpdate(() => {
if (prevFilter !== packageFilter) {
limit = scrollStore.getLimit(packageFilter);
}
});
// ...and scroll position after the update
afterUpdate(() => {
if (prevFilter !== packageFilter && scrollElement) {
prevFilter = packageFilter;
const scrollPosition = scrollStore.getScrollPosition(packageFilter);
scrollElement.scrollTop = scrollPosition;
}
});
</script>
<div class="relative h-full w-full">
<ul class="flex flex-wrap content-start bg-black" use:watchResize={onResize} on:scroll={onScroll}>
<ul
bind:this={scrollElement}
class="flex flex-wrap content-start bg-black"
use:watchResize={onResize}
on:scroll={onScroll}
>
{#if packages.length > 0}
{#each packages as pkg, index}
{#if index < limit}
@ -84,7 +115,7 @@
</section>
{/each}
{/if}
<InfiniteScroll threshold={100} on:loadMore={() => (limit += loadMore)} />
<InfiniteScroll threshold={100} on:loadMore={() => updateLimit(loadMore)} />
</ul>
</div>

View file

@ -11,6 +11,7 @@ import pkgStore from "./stores/pkgs";
import initNotificationStore from "./stores/notifications";
import initAppUpdateStore from "./stores/update";
import { trackSearch } from "./analytics";
import initScrollStore from "./stores/scroll";
export const featuredPackages = writable<Package[]>([]);
@ -140,3 +141,5 @@ export const navStore = initNavStore();
export const notificationStore = initNotificationStore();
export const appUpdateStore = initAppUpdateStore();
export const scrollStore = initScrollStore();

View file

@ -0,0 +1,33 @@
import type { SideMenuOptions } from "$libs/types";
import { get, writable } from "svelte/store";
type ScrollValue = Record<SideMenuOptions, number>;
const defaultLimit = 18;
// keep track of the scroll position of each side menu filter so it's not lost after navigation
export default function initScrollStore() {
const scrollPositions = writable<ScrollValue>({} as ScrollValue);
const limits = writable<ScrollValue>({} as ScrollValue);
return {
setScrollPosition: (filter: SideMenuOptions, pos: number) => {
scrollPositions.update((prev) => {
prev[filter] = pos;
return prev;
});
},
getScrollPosition: (filter: SideMenuOptions) => {
return get(scrollPositions)[filter] || 0;
},
setLimit: (filter: SideMenuOptions, limit: number) => {
limits.update((prev) => {
prev[filter] = limit;
return prev;
});
},
getLimit: (filter: SideMenuOptions) => {
return get(limits)[filter] || defaultLimit;
}
};
}