FollowButton.svelte
1 <script lang="ts"> 2 import type { MastodonAccount } from '$lib/mastodon'; 3 import { followAccount, unfollowAccount } from '$lib/mastodon'; 4 import { friendkit } from '$lib/store.svelte'; 5 import { Button, Loader } from 'svelte-akui'; 6 import PermissionModal from './PermissionModal.svelte'; 7 8 let { account }: { account: MastodonAccount } = $props(); 9 10 let isProcessing = $state(false); 11 let showPermissionModal = $state(false); 12 13 const isFollowingUser = $derived.by(() => friendkit.following.some((a) => a.id === account.id)); 14 15 async function handleToggleFollow() { 16 if (isProcessing) return; 17 if (!friendkit.accessToken) return; 18 19 if (!friendkit.hasWriteAccess) { 20 showPermissionModal = true; 21 return; 22 } 23 24 const accountId = account.id; 25 const accountData = { ...account }; // Capture a snapshot 26 27 isProcessing = true; 28 try { 29 if (isFollowingUser) { 30 await unfollowAccount(accountId); 31 friendkit.following = friendkit.following.filter((a) => a.id !== accountId); 32 } else { 33 await followAccount(accountId); 34 friendkit.following = [...friendkit.following, accountData]; 35 } 36 } catch (e: unknown) { 37 alert(e instanceof Error ? e.message : String(e)); 38 } finally { 39 isProcessing = false; 40 } 41 } 42 </script> 43 44 <div class="fk-follow-button-wrapper"> 45 <Button 46 variant="regular" 47 size="small" 48 onclick={handleToggleFollow} 49 disabled={isProcessing} 50 class="fk-follow-button" 51 > 52 {#if isProcessing} 53 <Loader size="small" /> 54 {:else} 55 {isFollowingUser ? 'Unfollow' : 'Follow'} 56 {/if} 57 </Button> 58 </div> 59 60 {#if showPermissionModal} 61 <PermissionModal 62 {account} 63 actionName={isFollowingUser ? 'unfollowing accounts' : 'following accounts'} 64 onClose={() => (showPermissionModal = false)} 65 /> 66 {/if} 67 68 <style> 69 .fk-follow-button-wrapper :global(.fk-follow-button) { 70 width: 6em; 71 justify-content: center; 72 } 73 </style>