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 / tabs / VouchTab.svelte
2.4 kB 74 lines
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>