veza/apps/web/src/features/profile/components/FollowButton.tsx
2026-01-07 19:39:21 +01:00

120 lines
3.5 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { Button } from '@/components/ui/button';
import { UserPlus, UserCheck, Loader2 } from 'lucide-react';
import { followUser, unfollowUser, getProfile, type UserProfile } from '../services/profileService';
import { useToast } from '@/hooks/useToast';
import { useAuthStore } from '@/features/auth/store/authStore';
import { parseApiError } from '@/utils/apiErrorHandler';
/**
* FE-COMP-015: Follow/Unfollow button component for user profiles
*/
interface FollowButtonProps {
userId: string;
initialFollowing?: boolean;
onFollowChange?: (isFollowing: boolean) => void;
className?: string;
size?: 'default' | 'sm' | 'lg' | 'icon';
variant?: 'default' | 'outline' | 'ghost';
}
export function FollowButton({
userId,
initialFollowing = false,
onFollowChange,
className,
size = 'default',
variant,
}: FollowButtonProps) {
const { user } = useAuthStore();
const { success: showSuccess, error: showError } = useToast();
const queryClient = useQueryClient();
const [following, setFollowing] = useState(initialFollowing);
const [isUpdating, setIsUpdating] = useState(false);
// Fetch profile to get current follow status
const { data: profile } = useQuery<UserProfile>({
queryKey: ['userProfile', userId],
queryFn: () => getProfile(userId),
enabled: !!userId && userId !== user?.id,
staleTime: 30000, // 30 seconds
});
// Update following state from profile if available
useEffect(() => {
if (profile && (profile as any).is_following !== undefined) {
setFollowing((profile as any).is_following);
} else if (initialFollowing !== undefined) {
setFollowing(initialFollowing);
}
}, [profile, initialFollowing]);
// Don't show follow button if viewing own profile
if (user?.id === userId) {
return null;
}
const handleClick = async () => {
if (isUpdating || !user) return;
setIsUpdating(true);
const newFollowing = !following;
try {
if (newFollowing) {
await followUser(userId);
showSuccess('Vous suivez maintenant cet utilisateur');
} else {
await unfollowUser(userId);
showSuccess('Vous ne suivez plus cet utilisateur');
}
setFollowing(newFollowing);
onFollowChange?.(newFollowing);
// Invalidate profile queries to refresh data
queryClient.invalidateQueries({ queryKey: ['userProfile', userId] });
queryClient.invalidateQueries({ queryKey: ['userProfile'] });
} catch (error: unknown) {
const apiError = parseApiError(error);
const errorMessage = apiError.message;
showError(errorMessage);
} finally {
setIsUpdating(false);
}
};
// Don't show follow button if viewing own profile or not logged in
if (user?.id === userId || !user) {
return null;
}
const buttonVariant = variant || (following ? 'outline' : 'default');
return (
<Button
onClick={handleClick}
disabled={isUpdating}
variant={buttonVariant}
size={size}
className={className || 'min-w-[100px]'}
>
{isUpdating ? (
<>
<Loader2 className="h-4 w-4 animate-spin mr-2" />
{following ? 'Désabonnement...' : 'Abonnement...'}
</>
) : following ? (
<>
<UserCheck className="h-4 w-4 mr-2" />
Abonné
</>
) : (
<>
<UserPlus className="h-4 w-4 mr-2" />
Suivre
</>
)}
</Button>
);
}