diff options
Diffstat (limited to 'packages/app/src/context/sync.tsx')
| -rw-r--r-- | packages/app/src/context/sync.tsx | 45 |
1 files changed, 36 insertions, 9 deletions
diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx index 3e3696924..9dc6623a7 100644 --- a/packages/app/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -32,6 +32,12 @@ const keyFor = (directory: string, id: string) => `${directory}\n${id}` const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0) +function merge<T extends { id: string }>(a: readonly T[], b: readonly T[]) { + const map = new Map(a.map((item) => [item.id, item] as const)) + for (const item of b) map.set(item.id, item) + return [...map.values()].sort((x, y) => cmp(x.id, y.id)) +} + type OptimisticStore = { message: Record<string, Message[] | undefined> part: Record<string, Part[] | undefined> @@ -119,6 +125,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const seen = new Map<string, Set<string>>() const [meta, setMeta] = createStore({ limit: {} as Record<string, number>, + cursor: {} as Record<string, string | undefined>, complete: {} as Record<string, boolean>, loading: {} as Record<string, boolean>, }) @@ -157,6 +164,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ for (const sessionID of sessionIDs) { const key = keyFor(directory, sessionID) delete draft.limit[key] + delete draft.cursor[key] delete draft.complete[key] delete draft.loading[key] } @@ -187,17 +195,24 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ evict(directory, setStore, stale) } - const fetchMessages = async (input: { client: typeof sdk.client; sessionID: string; limit: number }) => { + const fetchMessages = async (input: { + client: typeof sdk.client + sessionID: string + limit: number + before?: string + }) => { const messages = await retry(() => - input.client.session.messages({ sessionID: input.sessionID, limit: input.limit }), + input.client.session.messages({ sessionID: input.sessionID, limit: input.limit, before: input.before }), ) const items = (messages.data ?? []).filter((x) => !!x?.info?.id) const session = items.map((x) => x.info).sort((a, b) => cmp(a.id, b.id)) const part = items.map((message) => ({ id: message.info.id, part: sortParts(message.parts) })) + const cursor = messages.response.headers.get("x-next-cursor") ?? undefined return { session, part, - complete: session.length < input.limit, + cursor, + complete: !cursor, } } @@ -209,6 +224,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ setStore: Setter sessionID: string limit: number + before?: string + mode?: "replace" | "prepend" }) => { const key = keyFor(input.directory, input.sessionID) if (meta.loading[key]) return @@ -217,17 +234,22 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ await fetchMessages(input) .then((next) => { if (!tracked(input.directory, input.sessionID)) return + const [store] = globalSync.child(input.directory, { bootstrap: false }) + const cached = input.mode === "prepend" ? (store.message[input.sessionID] ?? []) : [] + const message = input.mode === "prepend" ? merge(cached, next.session) : next.session batch(() => { - input.setStore("message", input.sessionID, reconcile(next.session, { key: "id" })) + input.setStore("message", input.sessionID, reconcile(message, { key: "id" })) for (const p of next.part) { input.setStore("part", p.id, p.part) } - setMeta("limit", key, input.limit) + setMeta("limit", key, message.length) + setMeta("cursor", key, next.cursor) setMeta("complete", key, next.complete) setSessionPrefetch({ directory: input.directory, sessionID: input.sessionID, - limit: input.limit, + limit: message.length, + cursor: next.cursor, complete: next.complete, }) }) @@ -312,6 +334,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ if (seeded && store.message[sessionID] !== undefined && meta.limit[key] === undefined) { batch(() => { setMeta("limit", key, seeded.limit) + setMeta("cursor", key, seeded.cursor) setMeta("complete", key, seeded.complete) setMeta("loading", key, false) }) @@ -325,6 +348,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ if (seeded && store.message[sessionID] !== undefined && meta.limit[key] === undefined) { batch(() => { setMeta("limit", key, seeded.limit) + setMeta("cursor", key, seeded.cursor) setMeta("complete", key, seeded.complete) setMeta("loading", key, false) }) @@ -420,7 +444,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ if (store.message[sessionID] === undefined) return false if (meta.limit[key] === undefined) return false if (meta.complete[key]) return false - return true + return !!meta.cursor[key] }, loading(sessionID: string) { const key = keyFor(sdk.directory, sessionID) @@ -435,14 +459,17 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const step = count ?? messagePageSize if (meta.loading[key]) return if (meta.complete[key]) return + const before = meta.cursor[key] + if (!before) return - const currentLimit = meta.limit[key] ?? messagePageSize await loadMessages({ directory, client, setStore, sessionID, - limit: currentLimit + step, + limit: step, + before, + mode: "prepend", }) }, }, |
