wetalk/src/pages/Profile.tsx
ordinarthur 503e658f03
Some checks failed
Build & Deploy / build-and-deploy (push) Failing after 16s
feat: We Talk — podcast communautaire PWA
2026-04-12 11:45:29 +02:00

148 lines
5.2 KiB
TypeScript

import { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { Users, Headphones } from 'lucide-react'
import { supabase } from '@/lib/supabase'
import { useAuthStore } from '@/stores/auth'
import type { Profile as ProfileType, Podcast } from '@/types'
import { Avatar } from '@/components/ui/Avatar'
import { Button } from '@/components/ui/Button'
import { PodcastCard } from '@/components/podcast/PodcastCard'
export function Profile() {
const { username } = useParams<{ username: string }>()
const { user } = useAuthStore()
const [profile, setProfile] = useState<ProfileType | null>(null)
const [podcasts, setPodcasts] = useState<Podcast[]>([])
const [isFollowing, setIsFollowing] = useState(false)
const [followersCount, setFollowersCount] = useState(0)
const [followingCount, setFollowingCount] = useState(0)
const [loading, setLoading] = useState(true)
const isOwn = user && profile && user.id === profile.id
useEffect(() => {
if (!username) return
async function load() {
const { data: profileData } = await supabase
.from('profiles')
.select('*')
.eq('username', username)
.single()
if (!profileData) { setLoading(false); return }
setProfile(profileData)
const [podcastsRes, followersRes, followingRes] = await Promise.all([
supabase
.from('podcasts')
.select('*, creator:profiles(*), tags:podcast_tags(tag:tags(*))')
.eq('creator_id', profileData.id)
.order('created_at', { ascending: false }),
supabase.from('follows').select('*', { count: 'exact', head: true }).eq('following_id', profileData.id),
supabase.from('follows').select('*', { count: 'exact', head: true }).eq('follower_id', profileData.id),
])
if (podcastsRes.data) {
setPodcasts(podcastsRes.data.map((p: any) => ({
...p,
tags: p.tags?.map((t: any) => t.tag).filter(Boolean) || [],
})))
}
setFollowersCount(followersRes.count || 0)
setFollowingCount(followingRes.count || 0)
if (user) {
const { data: follow } = await supabase
.from('follows')
.select('*')
.eq('follower_id', user.id)
.eq('following_id', profileData.id)
.maybeSingle()
setIsFollowing(!!follow)
}
setLoading(false)
}
load()
}, [username, user])
async function handleFollow() {
if (!user || !profile) return
if (isFollowing) {
await supabase.from('follows').delete().eq('follower_id', user.id).eq('following_id', profile.id)
setIsFollowing(false)
setFollowersCount((c) => c - 1)
} else {
await supabase.from('follows').insert({ follower_id: user.id, following_id: profile.id })
setIsFollowing(true)
setFollowersCount((c) => c + 1)
}
}
if (loading) {
return (
<div className="max-w-3xl mx-auto space-y-6">
<div className="flex items-center gap-6">
<div className="w-20 h-20 rounded-full bg-border-light animate-pulse" />
<div className="space-y-2 flex-1">
<div className="h-6 w-40 bg-border-light rounded animate-pulse" />
<div className="h-4 w-60 bg-border-light rounded animate-pulse" />
</div>
</div>
</div>
)
}
if (!profile) {
return <div className="text-center py-16 text-text-secondary">Utilisateur introuvable.</div>
}
return (
<div className="max-w-3xl mx-auto space-y-8">
<div className="flex flex-col sm:flex-row items-start gap-6">
<Avatar src={profile.avatar_url} name={profile.username} size="lg" />
<div className="flex-1">
<div className="flex items-center gap-3 flex-wrap">
<h1 className="text-2xl font-heading font-bold">{profile.username}</h1>
{profile.is_premium && (
<span className="bg-primary/10 text-primary text-xs font-semibold px-2.5 py-0.5 rounded-full">PRO</span>
)}
</div>
{profile.bio && <p className="text-text-secondary text-sm mt-1">{profile.bio}</p>}
<div className="flex items-center gap-5 mt-3 text-sm text-text-secondary">
<span className="flex items-center gap-1"><Headphones size={14} />{podcasts.length} podcasts</span>
<span className="flex items-center gap-1"><Users size={14} />{followersCount} abonnés</span>
<span>{followingCount} abonnements</span>
</div>
{!isOwn && user && (
<div className="mt-4">
<Button
variant={isFollowing ? 'secondary' : 'primary'}
size="sm"
onClick={handleFollow}
>
{isFollowing ? 'Abonné' : 'S\'abonner'}
</Button>
</div>
)}
</div>
</div>
<div>
<h2 className="text-lg font-heading font-bold mb-4">Podcasts</h2>
{podcasts.length === 0 ? (
<p className="text-text-secondary text-sm text-center py-8">Aucun podcast publié.</p>
) : (
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
{podcasts.map((p) => (
<PodcastCard key={p.id} podcast={p} />
))}
</div>
)}
</div>
</div>
)
}