120 lines
3.5 KiB
TypeScript
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>
|
|
);
|
|
}
|