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