This repository has no description
1<script lang="ts">
2 import Star from "$icon/star";
3 import { getAuth } from "$lib/auth.svelte";
4 import { createStar, deleteStar } from "$lib/api/graph";
5 import Button from "$lib/components/ui/Button.svelte";
6 import ButtonGroup from "$lib/components/ui/ButtonGroup.svelte";
7 import { getProfileCounts } from "$lib/components/profile/counts.svelte";
8 import { createOptimisticRelation, createOptimisticCount } from "$lib/optimistic.svelte";
9
10 interface Props {
11 repoDid: string;
12 repoOwnerHandle: string;
13 repoName: string;
14 initialCount?: number;
15 initialRkey?: string | null;
16 insetShadow?: boolean;
17 }
18
19 let {
20 repoDid,
21 repoOwnerHandle,
22 repoName,
23 initialCount,
24 initialRkey,
25 insetShadow = true
26 }: Props = $props();
27
28 const auth = getAuth();
29 const profileCounts = getProfileCounts();
30 const signedIn = $derived(Boolean(auth.currentDid));
31 const relation = createOptimisticRelation({
32 key: () => `${auth.currentDid ?? ""}:${repoDid}`,
33 loadedRkey: () => initialRkey
34 });
35 const starCount = createOptimisticCount({
36 key: () => repoDid,
37 loaded: () => initialCount
38 });
39
40 const starred = $derived(relation.active);
41 const failed = $derived(relation.failed || starCount.failed);
42
43 const toggle = async () => {
44 const agent = auth.agent;
45 if (!agent || !relation.known || relation.loading || !repoDid) return;
46 relation.begin();
47 starCount.resetFailure();
48 try {
49 if (relation.active && relation.rkey) {
50 await deleteStar(agent, relation.rkey);
51 relation.deleted();
52 starCount.adjust(-1);
53 profileCounts?.adjust(agent.sub, "stars", -1);
54 } else {
55 const rkey = await createStar(agent, repoDid);
56 relation.created(rkey);
57 starCount.adjust(1);
58 profileCounts?.adjust(agent.sub, "stars", 1);
59 }
60 } catch {
61 relation.fail();
62 starCount.fail();
63 }
64 };
65</script>
66
67{#if signedIn && repoDid}
68 <div class="flex flex-col items-end">
69 <ButtonGroup>
70 <Button
71 variant="default"
72 size="sm"
73 {insetShadow}
74 class="flex-1"
75 loading={relation.loading}
76 disabled={!relation.known}
77 onclick={toggle}
78 >
79 <Star
80 class={`size-4 shrink-0 ${starred ? "[&_path]:fill-[currentColor]" : ""}`}
81 aria-hidden="true"
82 />
83 <span>{starred ? "Unstar" : "Star"}</span>
84 </Button>
85 <Button
86 href={`/${repoOwnerHandle}/${repoName}/stars`}
87 variant="ghost"
88 size="sm"
89 title="Starred by"
90 >
91 {starCount.value}
92 </Button>
93 </ButtonGroup>
94 {#if failed}
95 <p class="mt-1 typography-paragraph-small text-foreground-danger">
96 Something went wrong. Try again.
97 </p>
98 {/if}
99 </div>
100{/if}