This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / profile / pagination.svelte.ts
1.7 kB 59 lines
1export interface CursorPage<T, C> { 2 items: T[]; 3 cursor?: C; 4} 5 6type CursorPagerState = 7 | { kind: "at-page"; page: number } 8 | { kind: "loading"; page: number } 9 | { kind: "failed"; page: number }; 10 11export const pageCount = (total: number, limit: number): number => 12 Math.max(1, Math.ceil(Math.max(total, 0) / limit)); 13 14// search uses this since there isn't a known limit 15export const discoveredPageCount = (page: number, hasNext: boolean): number => 16 Math.max(1, page + (hasNext ? 1 : 0)); 17 18export const createCursorPager = <T, C>( 19 initial: CursorPage<T, C>, 20 load: (cursor: C) => Promise<CursorPage<T, C>>, 21 hasNext: (cursor: C | undefined) => boolean = (cursor) => cursor !== undefined 22) => { 23 let state = $state<CursorPagerState>({ kind: "at-page", page: 1 }); 24 let pages = $state<Record<number, CursorPage<T, C>>>({ 1: initial }); 25 26 const select = async (target: number) => { 27 if (state.kind === "loading" || target < 1 || target === state.page) return; 28 29 const currentPage = state.page; 30 state = { kind: "loading", page: currentPage }; 31 try { 32 const loaded = { ...pages }; 33 for (let next = 2; next <= target; next++) { 34 if (loaded[next]) continue; 35 const cursor = loaded[next - 1]?.cursor; 36 if (!hasNext(cursor)) break; 37 loaded[next] = await load(cursor as C); 38 } 39 40 pages = loaded; 41 state = { kind: "at-page", page: loaded[target] ? target : currentPage }; 42 } catch { 43 state = { kind: "failed", page: currentPage }; 44 } 45 }; 46 47 return { 48 get state() { 49 return state; 50 }, 51 get items() { 52 return pages[state.page]?.items ?? []; 53 }, 54 get hasNext() { 55 return hasNext(pages[state.page]?.cursor); 56 }, 57 select 58 }; 59};