+24
src/utils/route-cache.ts
+24
src/utils/route-cache.ts
···
1
+
import { createStore } from "solid-js/store";
2
+
3
+
export interface CollectionCacheEntry {
4
+
records: unknown[];
5
+
cursor: string | undefined;
6
+
scrollY: number;
7
+
reverse: boolean;
8
+
}
9
+
10
+
type RouteCache = Record<string, CollectionCacheEntry>;
11
+
12
+
const [routeCache, setRouteCache] = createStore<RouteCache>({});
13
+
14
+
export const getCollectionCache = (key: string): CollectionCacheEntry | undefined => {
15
+
return routeCache[key];
16
+
};
17
+
18
+
export const setCollectionCache = (key: string, entry: CollectionCacheEntry): void => {
19
+
setRouteCache(key, entry);
20
+
};
21
+
22
+
export const clearCollectionCache = (key: string): void => {
23
+
setRouteCache(key, undefined!);
24
+
};
+55
-2
src/views/collection.tsx
+55
-2
src/views/collection.tsx
···
2
2
import { Client, simpleFetchHandler } from "@atcute/client";
3
3
import { $type, ActorIdentifier, InferXRPCBodyOutput } from "@atcute/lexicons";
4
4
import * as TID from "@atcute/tid";
5
-
import { A, useParams } from "@solidjs/router";
6
-
import { createEffect, createMemo, createResource, createSignal, For, Show } from "solid-js";
5
+
import { A, useBeforeLeave, useParams } from "@solidjs/router";
6
+
import {
7
+
createEffect,
8
+
createMemo,
9
+
createResource,
10
+
createSignal,
11
+
For,
12
+
onMount,
13
+
Show,
14
+
} from "solid-js";
7
15
import { createStore } from "solid-js/store";
8
16
import { hasUserScope } from "../auth/scope-utils";
9
17
import { agent } from "../auth/state";
···
17
25
import { isTouchDevice } from "../layout.jsx";
18
26
import { resolvePDS } from "../utils/api.js";
19
27
import { localDateFromTimestamp } from "../utils/date.js";
28
+
import {
29
+
clearCollectionCache,
30
+
getCollectionCache,
31
+
setCollectionCache,
32
+
} from "../utils/route-cache.js";
20
33
21
34
interface AtprotoRecord {
22
35
rkey: string;
···
85
98
const [reverse, setReverse] = createSignal(false);
86
99
const [recreate, setRecreate] = createSignal(false);
87
100
const [openDelete, setOpenDelete] = createSignal(false);
101
+
const [restoredFromCache, setRestoredFromCache] = createSignal(false);
88
102
const did = params.repo;
89
103
let pds: string;
90
104
let rpc: Client;
91
105
106
+
const cacheKey = () => `${params.pds}/${params.repo}/${params.collection}`;
107
+
108
+
onMount(() => {
109
+
const cached = getCollectionCache(cacheKey());
110
+
if (cached) {
111
+
setRecords(cached.records as AtprotoRecord[]);
112
+
setCursor(cached.cursor);
113
+
setReverse(cached.reverse);
114
+
setRestoredFromCache(true);
115
+
requestAnimationFrame(() => {
116
+
window.scrollTo(0, cached.scrollY);
117
+
});
118
+
}
119
+
});
120
+
121
+
useBeforeLeave((e) => {
122
+
const recordPathPrefix = `/at://${did}/${params.collection}/`;
123
+
const isNavigatingToRecord = typeof e.to === "string" && e.to.startsWith(recordPathPrefix);
124
+
125
+
if (isNavigatingToRecord && records.length > 0) {
126
+
setCollectionCache(cacheKey(), {
127
+
records: [...records],
128
+
cursor: cursor(),
129
+
scrollY: window.scrollY,
130
+
reverse: reverse(),
131
+
});
132
+
} else {
133
+
clearCollectionCache(cacheKey());
134
+
}
135
+
});
136
+
92
137
const fetchRecords = async () => {
138
+
if (restoredFromCache() && records.length > 0 && !cursor()) {
139
+
setRestoredFromCache(false);
140
+
return records;
141
+
}
142
+
if (restoredFromCache()) setRestoredFromCache(false);
143
+
93
144
if (!pds) pds = await resolvePDS(did!);
94
145
if (!rpc) rpc = new Client({ handler: simpleFetchHandler({ service: pds }) });
95
146
const res = await rpc.get("com.atproto.repo.listRecords", {
···
168
219
setCursor(undefined);
169
220
setOpenDelete(false);
170
221
setRecreate(false);
222
+
clearCollectionCache(cacheKey());
171
223
refetch();
172
224
};
173
225
···
304
356
setReverse(!reverse());
305
357
setRecords([]);
306
358
setCursor(undefined);
359
+
clearCollectionCache(cacheKey());
307
360
refetch();
308
361
}}
309
362
>