This repository has no description
1<script lang="ts">
2 import { untrack } from "svelte";
3 import { getAuth } from "$lib/auth.svelte";
4 import { createBobbinClient } from "$lib/api/client";
5 import { IdentityCache } from "$lib/api/identity";
6 import VouchCard from "../VouchCard.svelte";
7 import Section from "$lib/components/ui/Section.svelte";
8 import Pagination from "$lib/components/ui/Pagination.svelte";
9 import Error from "$lib/components/ui/Error.svelte";
10 import { createCursorPager, pageCount } from "../pagination.svelte";
11 import { PROFILE_PAGE_LIMIT, fetchVouchesPage, type VouchCursors } from "../pages";
12 import type { VouchData } from "../types";
13
14 interface Props {
15 initial: VouchData[];
16 cursors: VouchCursors;
17 total: number;
18 did: string;
19 isSelf: boolean;
20 profileHandle: string;
21 }
22
23 let { initial, cursors: initialCursors, total, did, isSelf, profileHandle }: Props = $props();
24
25 const auth = getAuth();
26
27 // shared across pages so repeat dids only resolve once
28 let identityCache: IdentityCache | undefined;
29
30 const profileLabel = $derived(isSelf ? "you" : profileHandle);
31 const pager = createCursorPager<VouchData, VouchCursors>(
32 { items: untrack(() => initial), cursor: untrack(() => initialCursors) },
33 (cursors) => {
34 const ctx = createBobbinClient({ serviceUrl: auth.bobbinUrl });
35 identityCache ??= new IdentityCache(ctx);
36 return fetchVouchesPage(ctx, { did, cursors, cache: identityCache }).then((next) => ({
37 items: next.items,
38 cursor: next.cursors
39 }));
40 },
41 (cursors) => Boolean(cursors?.incoming ?? cursors?.outgoing)
42 );
43 const vouches = $derived(pager.items);
44 const pages = $derived(pageCount(total, PROFILE_PAGE_LIMIT));
45</script>
46
47<Section
48 title="Vouches"
49 empty={vouches.length === 0}
50 emptyMessage="No vouches yet."
51 listClass="relative ml-5 flex flex-col gap-6 border border-transparent px-4"
52>
53 <div class="absolute inset-y-8 z-0 w-0.5 bg-border-default"></div>
54 {#each vouches as vouch (vouch.uri)}
55 <VouchCard {vouch} {profileLabel} />
56 {/each}
57 {#snippet footer()}
58 {#if pages > 1}
59 <div class="mt-4 flex justify-center">
60 <Pagination
61 page={pager.state.page}
62 total={pages}
63 labels
64 disabled={pager.state.kind === "loading"}
65 onchange={pager.select}
66 class="gap-5"
67 />
68 </div>
69 {/if}
70 {#if pager.state.kind === "failed"}
71 <Error label="couldn't load that page. try again." class="mt-2" />
72 {/if}
73 {/snippet}
74</Section>