الملفات
curated-learning-video-plat…/src/ChannelView.tsx
2026-06-17 00:21:29 +00:00

194 أسطر
10 KiB
TypeScript
خام اللوم التاريخ

هذا الملف يحتوي على أحرف Unicode غامضة

هذا الملف يحتوي على أحرف Unicode قد تُخلط مع أحرف أخرى. إذا كنت تعتقد أن هذا مقصود، يمكنك تجاهل هذا التحذير بأمان. استخدم زر الهروب للكشف عنها.

import { useApp } from '../store';
import { getChannel, getChannelPlaylists, getPlaylistVideos, standaloneVideos, formatNumber, getCategoryColor } from '../data';
import { ArrowRight, Play, List, CheckCircle, Users, Video } from 'lucide-react';
export default function ChannelView({ channelId }: { channelId: string }) {
const { setView, progress, followedChannelIds, followChannel, unfollowChannel } = useApp();
const channel = getChannel(channelId);
if (!channel) {
return <div className="p-8 text-center text-gray-400">القناة غير موجودة</div>;
}
const isFollowed = followedChannelIds.includes(channelId);
const channelPlaylists = getChannelPlaylists(channelId);
const channelStandalone = standaloneVideos.filter(v => v.channelId === channelId);
const totalVideos = channelPlaylists.reduce((sum, pl) => sum + (getPlaylistVideos(pl.id)?.length || 0), 0) + channelStandalone.length;
const watchedCount = [
...channelStandalone,
...channelPlaylists.flatMap(pl => getPlaylistVideos(pl.id) || [])
].filter(v => progress.watchedVideos.includes(v.id)).length;
return (
<div className="h-screen overflow-y-auto custom-scrollbar animate-slide-up">
{/* Channel Header */}
<div className={`bg-gradient-to-l ${channel.gradient} p-8 relative overflow-hidden`}>
<div className="absolute inset-0 bg-black/20" />
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIzMCIgY3k9IjMwIiByPSIxLjUiIGZpbGw9InJnYmEoMjU1LDI1NSwyNTUsMC4wOCkiLz48L3N2Zz4=')] " />
<div className="relative">
<button
onClick={() => setView({ type: 'dashboard' })}
className="flex items-center gap-1 text-white/80 hover:text-white text-sm mb-4 transition-colors"
>
<ArrowRight className="w-4 h-4" />
العودة للرئيسية
</button>
<div className="flex items-start gap-4">
<div className="w-20 h-20 rounded-2xl bg-white/20 backdrop-blur-sm flex items-center justify-center text-5xl shadow-2xl">
{channel.emoji}
</div>
<div className="flex-1">
<h1 className="text-2xl font-black text-white mb-1">{channel.name}</h1>
<p className="text-white/70 text-sm mb-3 max-w-lg">{channel.description}</p>
<div className="flex items-center gap-4 text-white/60 text-xs">
<span className="flex items-center gap-1"><Users className="w-3.5 h-3.5" /> {formatNumber(channel.subscriberCount)} مشترك</span>
<span className="flex items-center gap-1"><Video className="w-3.5 h-3.5" /> {channel.videoCount} فيديو</span>
<span className="flex items-center gap-1"><List className="w-3.5 h-3.5" /> {channelPlaylists.length} قائمة تشغيل</span>
</div>
</div>
<button
onClick={() => isFollowed ? unfollowChannel(channelId) : followChannel(channelId)}
className={`px-6 py-2.5 rounded-xl font-bold text-sm transition-all ${
isFollowed
? 'bg-white/20 text-white hover:bg-white/30 backdrop-blur-sm'
: 'bg-white text-gray-800 hover:bg-gray-100 shadow-lg'
}`}
>
{isFollowed ? '✓ متابَع' : '+ متابعة'}
</button>
</div>
{/* Categories */}
<div className="flex gap-2 mt-4 flex-wrap">
{channel.categories.map(cat => (
<span key={cat} className={`text-[10px] px-2.5 py-1 rounded-full ${getCategoryColor(cat)}`}>
{cat}
</span>
))}
</div>
</div>
</div>
{/* Stats */}
<div className="px-6 py-4 flex gap-3">
<div className="bg-white rounded-xl px-4 py-2.5 flex items-center gap-2 shadow-sm border border-gray-100">
<div className="text-sm font-black text-orange-500">{watchedCount}/{totalVideos}</div>
<div className="text-[10px] text-gray-400">فيديو مُشاهد</div>
</div>
<div className="bg-white rounded-xl px-4 py-2.5 flex items-center gap-2 shadow-sm border border-gray-100">
<div className="text-sm font-black text-emerald-500">
{channelPlaylists.filter(pl => progress.completedPlaylists.includes(pl.id)).length}/{channelPlaylists.length}
</div>
<div className="text-[10px] text-gray-400">قوائم مكتملة</div>
</div>
{totalVideos > 0 && (
<div className="flex-1 bg-white rounded-xl px-4 py-2.5 flex items-center gap-3 shadow-sm border border-gray-100">
<div className="text-[10px] text-gray-400">التقدم الكلي</div>
<div className="flex-1 h-2 bg-gray-100 rounded-full overflow-hidden">
<div className="h-full bg-gradient-to-l from-orange-400 to-emerald-400 rounded-full" style={{ width: `${(watchedCount / totalVideos) * 100}%` }} />
</div>
<div className="text-xs font-bold text-gray-600">{Math.round((watchedCount / totalVideos) * 100)}%</div>
</div>
)}
</div>
{/* Playlists */}
<div className="px-6 pb-6">
<h3 className="text-lg font-bold text-gray-800 flex items-center gap-2 mb-4">
<List className="w-5 h-5 text-purple-500" />
قوائم التشغيل ({channelPlaylists.length})
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
{channelPlaylists.map(pl => {
const plVideos = getPlaylistVideos(pl.id);
const completedCount = plVideos.filter(v => progress.watchedVideos.includes(v.id)).length;
const percentage = plVideos.length > 0 ? Math.round((completedCount / plVideos.length) * 100) : 0;
const isComplete = progress.completedPlaylists.includes(pl.id);
return (
<button
key={pl.id}
onClick={() => setView({ type: 'playlist', playlistId: pl.id })}
className="group bg-white rounded-2xl shadow-sm hover:shadow-xl transition-all duration-300 overflow-hidden text-right border border-gray-100 hover:border-purple-200"
>
<div className="relative">
<div className={`h-28 bg-gradient-to-br ${pl.gradient} flex items-center justify-center`}>
<span className="text-5xl opacity-40 group-hover:opacity-70 transition-opacity">{pl.emoji}</span>
</div>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
<div className="absolute bottom-2 right-3 left-3 flex justify-between items-end">
<div className="flex items-center gap-1 bg-black/50 rounded-lg px-2 py-1 text-white text-[10px]">
<Play className="w-3 h-3" />
{plVideos.length} فيديو
</div>
{isComplete && (
<div className="bg-emerald-500 rounded-lg px-2 py-1 text-white text-[10px] font-bold flex items-center gap-1">
<CheckCircle className="w-3 h-3" />
مكتملة
</div>
)}
</div>
</div>
<div className="p-4">
<h4 className="font-bold text-gray-800 text-sm mb-1 group-hover:text-purple-600 transition-colors">{pl.title}</h4>
<p className="text-[11px] text-gray-400 mb-3 line-clamp-2">{pl.description}</p>
<div className="h-1.5 bg-gray-100 rounded-full overflow-hidden">
<div
className={`h-full rounded-full transition-all duration-700 ${percentage === 100 ? 'bg-emerald-400' : 'bg-gradient-to-l from-purple-400 to-pink-400'}`}
style={{ width: `${percentage}%` }}
/>
</div>
<div className="flex justify-between mt-1.5 text-[10px]">
<span className="text-gray-400">{completedCount}/{plVideos.length}</span>
<span className={`font-bold ${percentage === 100 ? 'text-emerald-500' : 'text-purple-500'}`}>{percentage}%</span>
</div>
</div>
</button>
);
})}
</div>
{/* Standalone Videos */}
{channelStandalone.length > 0 && (
<>
<h3 className="text-lg font-bold text-gray-800 flex items-center gap-2 mb-4">
<Video className="w-5 h-5 text-orange-500" />
فيديوهات حديثة
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{channelStandalone.map(video => {
const isWatched = progress.watchedVideos.includes(video.id);
return (
<button
key={video.id}
onClick={() => {
setView({ type: 'video', videoId: video.id });
}}
className="group bg-white rounded-2xl shadow-sm hover:shadow-xl transition-all duration-300 overflow-hidden text-right border border-gray-100 hover:border-orange-200 flex items-center gap-3 p-3"
>
<div className={`w-24 h-16 rounded-xl bg-gradient-to-br ${channel.gradient} flex items-center justify-center shrink-0 relative`}>
<Play className="w-6 h-6 text-white/70 group-hover:text-white transition-colors" />
<span className="absolute bottom-1 left-1 bg-black/60 text-white text-[9px] px-1 rounded">
{video.duration}
</span>
{isWatched && <CheckCircle className="w-3.5 h-3.5 text-emerald-400 absolute top-1 right-1" />}
</div>
<div className="flex-1 min-w-0">
<h4 className="text-xs font-bold text-gray-800 line-clamp-2 group-hover:text-orange-600 transition-colors">{video.title}</h4>
<div className="text-[10px] text-gray-400 mt-1">{formatNumber(video.viewCount)} مشاهدة</div>
</div>
</button>
);
})}
</div>
</>
)}
</div>
</div>
);
}