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 / profile / FollowButton.svelte
2.8 kB 101 lines
1<script lang="ts"> 2 import { resolve } from "$app/paths"; 3 import Button from "$lib/components/ui/Button.svelte"; 4 import UserRoundPlus from "$icon/user-round-plus"; 5 import UserRoundMinus from "$icon/user-round-minus"; 6 import { getAuth } from "$lib/auth.svelte"; 7 import { createFollow, deleteFollow } from "$lib/api/graph"; 8 import { getProfileCounts } from "./counts.svelte"; 9 import type { FollowChange } from "./types"; 10 import { createOptimisticRelation } from "$lib/optimistic.svelte"; 11 12 interface Props { 13 profileDid: string; 14 initialRkey?: string | null; 15 size?: "sm" | "md"; 16 onCommit?: (change: FollowChange) => void; 17 } 18 19 let { profileDid, initialRkey, size = "sm", onCommit }: Props = $props(); 20 21 const auth = getAuth(); 22 const profileCounts = getProfileCounts(); 23 const signedIn = $derived(Boolean(auth.currentDid)); 24 const isSelf = $derived(auth.currentDid === profileDid); 25 const relation = createOptimisticRelation({ 26 key: () => `${auth.currentDid ?? ""}:${profileDid}`, 27 loadedRkey: () => initialRkey 28 }); 29 30 let busy = $state(false); 31 const following = $derived(relation.active); 32 33 const commit = (change: FollowChange) => { 34 profileCounts?.adjust(change.subjectDid, "followers", change.delta); 35 profileCounts?.adjust(change.viewerDid, "following", change.delta); 36 onCommit?.(change); 37 }; 38 39 const toggle = async () => { 40 const agent = auth.agent; 41 if (!agent || busy || !relation.known) return; 42 busy = true; 43 relation.resetFailure(); 44 try { 45 if (relation.active && relation.rkey) { 46 await deleteFollow(agent, relation.rkey); 47 relation.deleted(); 48 commit({ 49 viewerDid: agent.sub, 50 subjectDid: profileDid, 51 following: false, 52 rkey: null, 53 delta: -1 54 }); 55 } else { 56 const rkey = await createFollow(agent, profileDid); 57 relation.created(rkey); 58 commit({ 59 viewerDid: agent.sub, 60 subjectDid: profileDid, 61 following: true, 62 rkey, 63 delta: 1 64 }); 65 } 66 } catch { 67 relation.fail(); 68 } finally { 69 busy = false; 70 } 71 }; 72</script> 73 74{#if !isSelf} 75 {#if !signedIn} 76 <Button href={resolve("/login")} variant="default" {size} class="w-full gap-2"> 77 <UserRoundPlus class="size-4" aria-hidden="true" /><span>Follow</span> 78 </Button> 79 {:else} 80 <div class="w-full"> 81 <Button 82 variant="default" 83 {size} 84 class="w-full gap-2" 85 loading={busy} 86 disabled={!relation.known} 87 onclick={toggle} 88 > 89 {#if following} 90 <UserRoundMinus class="size-4" aria-hidden="true" /> 91 {:else} 92 <UserRoundPlus class="size-4" aria-hidden="true" /> 93 {/if} 94 <span>{following ? "Unfollow" : "Follow"}</span> 95 </Button> 96 {#if relation.failed} 97 <p class="mt-1 text-xs text-foreground-danger">Something went wrong. Try again.</p> 98 {/if} 99 </div> 100 {/if} 101{/if}