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 { fetchPeoplePage } from "../pages";
7 import FollowCard from "../FollowCard.svelte";
8 import Section from "$lib/components/ui/Section.svelte";
9 import LoadMore from "$lib/components/ui/LoadMore.svelte";
10 import type { PersonData } from "../types";
11
12 interface Props {
13 initial: PersonData[];
14 cursor?: string;
15 did: string;
16 direction: "followers" | "following";
17 title: string;
18 emptyMessage: string;
19 }
20
21 let { initial, cursor: initialCursor, did, direction, title, emptyMessage }: Props = $props();
22
23 const auth = getAuth();
24
25 let extra = $state<PersonData[]>([]);
26 let cursor = $state(untrack(() => initialCursor));
27 let loading = $state(false);
28 let failed = $state(false);
29 // shared across pages so repeat dids only resolve once
30 let identityCache: IdentityCache | undefined;
31
32 const people = $derived([...initial, ...extra]);
33
34 const loadMore = async () => {
35 if (loading || !cursor) return;
36 loading = true;
37 failed = false;
38 try {
39 const ctx = createBobbinClient({ serviceUrl: auth.bobbinUrl });
40 identityCache ??= new IdentityCache(ctx);
41 const next = await fetchPeoplePage(ctx, {
42 did,
43 viewerDid: auth.currentDid ?? undefined,
44 direction,
45 cursor,
46 cache: identityCache
47 });
48 extra = [...extra, ...next.items];
49 cursor = next.cursor;
50 } catch {
51 failed = true;
52 } finally {
53 loading = false;
54 }
55 };
56</script>
57
58<Section {title} empty={people.length === 0} {emptyMessage} listClass="flex flex-col gap-8">
59 {#each people as person (person.did)}
60 <FollowCard {person} />
61 {/each}
62 {#if cursor}
63 <LoadMore {loading} {failed} onclick={loadMore} />
64 {/if}
65</Section>