Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit c0c2472

Browse files
authored
preserver infinite scroll positions (#615)
1 parent 87dd224 commit c0c2472

3 files changed

Lines changed: 71 additions & 4 deletions

File tree

modules/desktop/src/components/packages/packages.svelte

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
import Package from "./package.svelte";
1111
import NoInstalls from "./no-installs.svelte";
1212
import NoUpdates from "./no-updates.svelte";
13-
import { packagesStore } from "$libs/stores";
13+
import { packagesStore, scrollStore } from "$libs/stores";
14+
import { afterUpdate, beforeUpdate } from "svelte";
1415
1516
const { packageList: allPackages } = packagesStore;
1617
export let packageFilter: SideMenuOptions = SideMenuOptions.all;
@@ -20,6 +21,11 @@
2021
let loadMore = 9;
2122
let limit = loadMore + 9;
2223
24+
const updateLimit = (value: number) => {
25+
limit += value;
26+
scrollStore.setLimit(packageFilter, limit);
27+
};
28+
2329
// TODO: figure out a better type strategy here so that this breaks if SideMenuOptions is updated
2430
const pkgFilters: { [key: string]: (pkg: GUIPackage) => boolean } = {
2531
[SideMenuOptions.all]: (_pkg: GUIPackage) => true,
@@ -46,6 +52,7 @@
4652
const onScroll = (e: Event) => {
4753
const target = e.target as HTMLInputElement;
4854
scrollY = target.scrollTop || 0;
55+
scrollStore.setScrollPosition(packageFilter, scrollY);
4956
};
5057
5158
$: packages = $allPackages.filter(pkgFilters[packageFilter] || pkgFilters.all);
@@ -56,13 +63,37 @@
5663
const minCardRows = Math.floor(node.scrollHeight / assumedCardHeight);
5764
if (cardRows < minCardRows) {
5865
const addLimit = 3 * (minCardRows - cardRows);
59-
limit += addLimit;
66+
updateLimit(addLimit);
6067
}
6168
};
69+
70+
let scrollElement: any = null;
71+
let prevFilter: SideMenuOptions | null;
72+
73+
// Restore the limit before the update...
74+
beforeUpdate(() => {
75+
if (prevFilter !== packageFilter) {
76+
limit = scrollStore.getLimit(packageFilter);
77+
}
78+
});
79+
80+
// ...and scroll position after the update
81+
afterUpdate(() => {
82+
if (prevFilter !== packageFilter && scrollElement) {
83+
prevFilter = packageFilter;
84+
const scrollPosition = scrollStore.getScrollPosition(packageFilter);
85+
scrollElement.scrollTop = scrollPosition;
86+
}
87+
});
6288
</script>
6389

6490
<div class="relative h-full w-full">
65-
<ul class="flex flex-wrap content-start bg-black" use:watchResize={onResize} on:scroll={onScroll}>
91+
<ul
92+
bind:this={scrollElement}
93+
class="flex flex-wrap content-start bg-black"
94+
use:watchResize={onResize}
95+
on:scroll={onScroll}
96+
>
6697
{#if packages.length > 0}
6798
{#each packages as pkg, index}
6899
{#if index < limit}
@@ -84,7 +115,7 @@
84115
</section>
85116
{/each}
86117
{/if}
87-
<InfiniteScroll threshold={100} on:loadMore={() => (limit += loadMore)} />
118+
<InfiniteScroll threshold={100} on:loadMore={() => updateLimit(loadMore)} />
88119
</ul>
89120
</div>
90121

modules/desktop/src/libs/stores.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import pkgStore from "./stores/pkgs";
1111
import initNotificationStore from "./stores/notifications";
1212
import initAppUpdateStore from "./stores/update";
1313
import { trackSearch } from "./analytics";
14+
import initScrollStore from "./stores/scroll";
1415

1516
export const featuredPackages = writable<Package[]>([]);
1617

@@ -140,3 +141,5 @@ export const navStore = initNavStore();
140141
export const notificationStore = initNotificationStore();
141142

142143
export const appUpdateStore = initAppUpdateStore();
144+
145+
export const scrollStore = initScrollStore();
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { SideMenuOptions } from "$libs/types";
2+
import { get, writable } from "svelte/store";
3+
4+
type ScrollValue = Record<SideMenuOptions, number>;
5+
6+
const defaultLimit = 18;
7+
8+
// keep track of the scroll position of each side menu filter so it's not lost after navigation
9+
export default function initScrollStore() {
10+
const scrollPositions = writable<ScrollValue>({} as ScrollValue);
11+
const limits = writable<ScrollValue>({} as ScrollValue);
12+
13+
return {
14+
setScrollPosition: (filter: SideMenuOptions, pos: number) => {
15+
scrollPositions.update((prev) => {
16+
prev[filter] = pos;
17+
return prev;
18+
});
19+
},
20+
getScrollPosition: (filter: SideMenuOptions) => {
21+
return get(scrollPositions)[filter] || 0;
22+
},
23+
setLimit: (filter: SideMenuOptions, limit: number) => {
24+
limits.update((prev) => {
25+
prev[filter] = limit;
26+
return prev;
27+
});
28+
},
29+
getLimit: (filter: SideMenuOptions) => {
30+
return get(limits)[filter] || defaultLimit;
31+
}
32+
};
33+
}

0 commit comments

Comments
 (0)