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.7 kB 98 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 onCommit?: (change: FollowChange) => void; 16 } 17 18 let { profileDid, initialRkey, onCommit }: Props = $props(); 19 20 const auth = getAuth(); 21 const profileCounts = getProfileCounts(); 22 const signedIn = $derived(Boolean(auth.currentDid)); 23 const isSelf = $derived(auth.currentDid === profileDid); 24 const relation = createOptimisticRelation({ 25 key: () => `${auth.currentDid ?? ""}:${profileDid}`, 26 loadedRkey: () => initialRkey 27 }); 28 29 const following = $derived(relation.active); 30 31 const commit = (change: FollowChange) => { 32 profileCounts?.adjust(change.subjectDid, "followers", change.delta); 33 profileCounts?.adjust(change.viewerDid, "following", change.delta); 34 onCommit?.(change); 35 }; 36 37 const toggle = async () => { 38 const agent = auth.agent; 39 if (!agent || !relation.known || relation.loading) return; 40 relation.begin(); 41 try { 42 if (relation.active && relation.rkey) { 43 await deleteFollow(agent, relation.rkey); 44 relation.deleted(); 45 commit({ 46 viewerDid: agent.sub, 47 subjectDid: profileDid, 48 following: false, 49 rkey: null, 50 delta: -1 51 }); 52 } else { 53 const rkey = await createFollow(agent, profileDid); 54 relation.created(rkey); 55 commit({ 56 viewerDid: agent.sub, 57 subjectDid: profileDid, 58 following: true, 59 rkey, 60 delta: 1 61 }); 62 } 63 } catch { 64 relation.fail(); 65 } 66 }; 67</script> 68 69{#if !isSelf} 70 {#if !signedIn} 71 <Button href={resolve("/login")} variant="default" class="w-full gap-2" insetShadow={true}> 72 <UserRoundPlus class="size-4" aria-hidden="true" /><span>Follow</span> 73 </Button> 74 {:else} 75 <div class="w-full"> 76 <Button 77 variant="default" 78 class="w-full gap-2" 79 insetShadow={true} 80 loading={relation.loading} 81 disabled={!relation.known} 82 onclick={toggle} 83 > 84 {#if following} 85 <UserRoundMinus class="size-4" aria-hidden="true" /> 86 {:else} 87 <UserRoundPlus class="size-4" aria-hidden="true" /> 88 {/if} 89 <span>{following ? "Unfollow" : "Follow"}</span> 90 </Button> 91 {#if relation.failed} 92 <p class="mt-1 typography-paragraph-small text-foreground-danger"> 93 Something went wrong. Try again. 94 </p> 95 {/if} 96 </div> 97 {/if} 98{/if}