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.6 kB 104 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 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 let busy = $state(false); 41 const starred = $derived(relation.active); 42 const failed = $derived(relation.failed || starCount.failed); 43 44 const toggle = async () => { 45 const agent = auth.agent; 46 if (!agent || busy || !relation.known || !repoDid) return; 47 busy = true; 48 relation.resetFailure(); 49 starCount.resetFailure(); 50 try { 51 if (relation.active && relation.rkey) { 52 await deleteStar(agent, relation.rkey); 53 relation.deleted(); 54 starCount.adjust(-1); 55 profileCounts?.adjust(agent.sub, "stars", -1); 56 } else { 57 const rkey = await createStar(agent, repoDid); 58 relation.created(rkey); 59 starCount.adjust(1); 60 profileCounts?.adjust(agent.sub, "stars", 1); 61 } 62 } catch { 63 relation.fail(); 64 starCount.fail(); 65 } finally { 66 busy = false; 67 } 68 }; 69</script> 70 71{#if signedIn && repoDid} 72 <div class="flex flex-col items-end"> 73 <ButtonGroup> 74 <Button 75 variant="default" 76 size="sm" 77 {insetShadow} 78 class="flex-1" 79 loading={busy} 80 disabled={!relation.known} 81 onclick={toggle} 82 > 83 <Star 84 class={`size-4 shrink-0 ${starred ? "[&_path]:fill-[currentColor]" : ""}`} 85 aria-hidden="true" 86 /> 87 <span>{starred ? "Unstar" : "Star"}</span> 88 </Button> 89 <Button 90 href={`/${repoOwnerHandle}/${repoName}/stars`} 91 variant="ghost" 92 size="sm" 93 title="Starred by" 94 > 95 {starCount.value} 96 </Button> 97 </ButtonGroup> 98 {#if failed} 99 <p class="mt-1 typography-paragraph-small text-foreground-danger"> 100 Something went wrong. Try again. 101 </p> 102 {/if} 103 </div> 104{/if}