9 changed files with 185 additions and 44 deletions
@ -0,0 +1,113 @@ |
|||||
|
import Navbar from "../components/Navbar.jsx"; |
||||
|
import {useEffect, useState} from "react"; |
||||
|
import {useParams} from "react-router-dom"; |
||||
|
import {fetchChannelDetails, subscribe} from "../services/channel.service.js"; |
||||
|
import TabLayout from "../components/TabLayout.jsx"; |
||||
|
import ChannelLastVideos from "../components/ChannelLastVideos.jsx"; |
||||
|
import { isSubscribed } from "../services/user.service.js"; |
||||
|
|
||||
|
|
||||
|
export default function Channel() { |
||||
|
|
||||
|
const id = useParams().id; |
||||
|
|
||||
|
const [alerts, setAlerts] = useState([]); |
||||
|
const [channel, setChannel] = useState(null); |
||||
|
const [isSubscribedToChannel, setIsSubscribedToChannel] = useState(false); |
||||
|
const tabs = [ |
||||
|
{ id: 'last', label: 'Dernières vidéos', element: () => <ChannelLastVideos videos={channel && channel.videos.slice(0, 10)} /> }, |
||||
|
{ id: 'all', label: 'Toutes les vidéos', element: () => <ChannelLastVideos videos={channel && channel.videos} /> }, |
||||
|
]; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
async function fetchData() { |
||||
|
const chan = await fetchChannelDetails(id, addAlert); |
||||
|
setChannel(chan); |
||||
|
// If not authenticated, isSubscribed may be undefined -> default to false |
||||
|
const subscribed = await isSubscribed(id, addAlert); |
||||
|
setIsSubscribedToChannel(Boolean(subscribed)); |
||||
|
} |
||||
|
fetchData(); |
||||
|
}, [id]) |
||||
|
|
||||
|
const addAlert = (type, message) => { |
||||
|
const newAlert = { type, message, id: Date.now() }; // Add unique ID |
||||
|
setAlerts(prev => [...prev, newAlert]); |
||||
|
}; |
||||
|
const onCloseAlert = (alertToRemove) => { |
||||
|
setAlerts(alerts.filter(alert => alert !== alertToRemove)); |
||||
|
} |
||||
|
|
||||
|
const handleSubscribe = async () => { |
||||
|
try { |
||||
|
const result = await subscribe(id, addAlert); |
||||
|
// Update local counter from API response |
||||
|
const newCount = Number(result?.subscriptions ?? (channel?.subscriptions ?? 0)); |
||||
|
setChannel(prev => (prev ? { ...prev, subscriptions: newCount } : prev)); |
||||
|
|
||||
|
// Toggle local subscription state and notify |
||||
|
const next = !isSubscribedToChannel; |
||||
|
setIsSubscribedToChannel(next); |
||||
|
} catch (e) { |
||||
|
// Error alert already handled in service |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<div className="min-w-screen min-h-screen bg-linear-to-br from-left-gradient to-right-gradient"> |
||||
|
<Navbar isSearchPage={false} alerts={alerts} onCloseAlert={onCloseAlert} /> |
||||
|
|
||||
|
<main className="pt-[118px] px-36" > |
||||
|
|
||||
|
{/* Channel Header */} |
||||
|
<div className="glassmorphism p-4" > |
||||
|
<div className="flex items-center gap-4" > |
||||
|
<img |
||||
|
src={channel && channel.picture} |
||||
|
alt={channel && channel.name} |
||||
|
className="w-[192px] aspect-square object-cover rounded-full border-2 border-white" |
||||
|
/> |
||||
|
<div> |
||||
|
<h1 className="font-montserrat font-bold text-3xl text-white" >{channel && channel.name}</h1> |
||||
|
<p className="font-montserrat font-medium text-xl text-white" >{channel && channel.subscriptions} abonné(es)</p> |
||||
|
</div> |
||||
|
{ |
||||
|
isSubscribedToChannel ? ( |
||||
|
<button |
||||
|
className="ml-5 bg-primary text-white font-montserrat font-bold px-4 py-2 rounded-md cursor-pointer" |
||||
|
onClick={handleSubscribe} |
||||
|
> |
||||
|
se désabonner |
||||
|
</button> |
||||
|
) : ( |
||||
|
<button |
||||
|
className="ml-5 bg-primary text-white font-montserrat font-bold px-4 py-2 rounded-md cursor-pointer" |
||||
|
onClick={handleSubscribe} |
||||
|
> |
||||
|
s'abonner |
||||
|
</button> |
||||
|
) |
||||
|
} |
||||
|
</div> |
||||
|
|
||||
|
<h2 className="font-bold font-montserrat text-white text-xl mt-4" >Description</h2> |
||||
|
<p className="text-white text-lg font-medium font-montserrat pl-[24px]"> |
||||
|
{ channel && channel.description } |
||||
|
</p> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
{/* Tab selector */} |
||||
|
|
||||
|
<TabLayout tabs={tabs}/> |
||||
|
|
||||
|
{/* 10 Last videos */} |
||||
|
|
||||
|
|
||||
|
|
||||
|
</main> |
||||
|
|
||||
|
</div> |
||||
|
) |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue