Monorepo for Tangled tangled.org
1

Configure Feed

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

core / web / src / lib / components / repo / StarButton.svelte
2.7 kB 92 lines
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 { getProfileCounts } from "$lib/components/profile/counts.svelte"; 7 import { createOptimisticRelation, createOptimisticCount } from "$lib/optimistic.svelte"; 8 9 interface Props { 10 repoDid: string; 11 repoOwnerHandle: string; 12 repoName: string; 13 initialCount?: number; 14 initialRkey?: string | null; 15 } 16 17 let { repoDid, repoOwnerHandle, repoName, initialCount, initialRkey }: Props = $props(); 18 19 const auth = getAuth(); 20 const profileCounts = getProfileCounts(); 21 const signedIn = $derived(Boolean(auth.currentDid)); 22 const relation = createOptimisticRelation({ 23 key: () => `${auth.currentDid ?? ""}:${repoDid}`, 24 loadedRkey: () => initialRkey 25 }); 26 const starCount = createOptimisticCount({ 27 key: () => repoDid, 28 loaded: () => initialCount 29 }); 30 31 let busy = $state(false); 32 const starred = $derived(relation.active); 33 const failed = $derived(relation.failed || starCount.failed); 34 35 const toggle = async () => { 36 const agent = auth.agent; 37 if (!agent || busy || !relation.known || !repoDid) return; 38 busy = true; 39 relation.resetFailure(); 40 starCount.resetFailure(); 41 try { 42 if (relation.active && relation.rkey) { 43 await deleteStar(agent, relation.rkey); 44 relation.deleted(); 45 starCount.adjust(-1); 46 profileCounts?.adjust(agent.sub, "stars", -1); 47 } else { 48 const rkey = await createStar(agent, repoDid); 49 relation.created(rkey); 50 starCount.adjust(1); 51 profileCounts?.adjust(agent.sub, "stars", 1); 52 } 53 } catch { 54 relation.fail(); 55 starCount.fail(); 56 } finally { 57 busy = false; 58 } 59 }; 60</script> 61 62{#if signedIn && repoDid} 63 <div class="flex flex-col items-end"> 64 <div 65 class="inline-flex max-h-8 items-stretch divide-x divide-border-default overflow-clip rounded border border-border-default" 66 > 67 <Button 68 variant="default" 69 size="sm" 70 class="min-h-[30px] flex-1 rounded-none border-0 before:rounded-none before:rounded-l-sm" 71 loading={busy} 72 disabled={!relation.known} 73 onclick={toggle} 74 > 75 <Star class={`size-4 shrink-0 ${starred ? "fill-current" : ""}`} aria-hidden="true" /> 76 <span>{starred ? "Unstar" : "Star"}</span> 77 </Button> 78 <Button 79 href={`/${repoOwnerHandle}/${repoName}/stars`} 80 variant="flat" 81 size="sm" 82 class="min-h-[30px] rounded-none" 83 title="Starred by" 84 > 85 {starCount.value} 86 </Button> 87 </div> 88 {#if failed} 89 <p class="mt-1 text-xs text-foreground-danger">Something went wrong. Try again.</p> 90 {/if} 91 </div> 92{/if}