Something went wrong. Try again.
A Bsky-like frontend using the atprotocol natively.
Something went wrong. Try again.
1.7 kB · 62 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263import { useState, useCallback } from 'react';import { atprotoClient } from '../api/client';import type { AtUri } from '../api/types';import './FollowButton.css';
interface FollowButtonProps { subjectDid: string; followingUri: AtUri | string | null | undefined; onFollowChange?: (following: boolean) => void; className?: string;}
export default function FollowButton({ subjectDid, followingUri: initialUri, onFollowChange, className,}: FollowButtonProps) { const [following, setFollowing] = useState(!!initialUri); const [followUri, setFollowUri] = useState<string | null>(initialUri ?? null); const [loading, setLoading] = useState(false);
const handleClick = useCallback(async (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (loading) return;
setLoading(true); try { if (following && followUri) { await atprotoClient.unfollow(followUri); setFollowing(false); setFollowUri(null); onFollowChange?.(false); } else { const result = await atprotoClient.follow(subjectDid); setFollowing(true); setFollowUri(result.uri); onFollowChange?.(true); } } catch (err) { console.error('Follow/unfollow failed:', err); } finally { setLoading(false); } }, [loading, following, followUri, subjectDid, onFollowChange]);
return ( <button className={`follow-btn${following ? ' following' : ''}${loading ? ' loading' : ''}${className ? ` ${className}` : ''}`} onClick={handleClick} disabled={loading} type="button" > {loading ? '…' : following ? 'Following' : 'Follow'} </button> );}