Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Comments & Engagement System

System komentarzy i zaangażowania jak na YouTube.

Comments Component 💬

1interface Comment {
2  id: number;
3  author: string;
4  content: string;
5  likes: number;
6  replies: Comment[];
7  createdAt: string;
8}
9
10function CommentsList({ videoId }: { videoId: number }) {
11  const [comments, setComments] = useState<Comment[]>([]);
12  const [newComment, setNewComment] = useState("");
13
14  const handleSubmit = async (e: React.FormEvent) => {
15    e.preventDefault();
16    const res = await fetch(`/api/videos/${videoId}/comments`, {
17      method: 'POST',
18      body: JSON.stringify({ content: newComment }),
19      headers: { 'Content-Type': 'application/json' }
20    });
21    const data = await res.json();
22    setComments([data.comment, ...comments]);
23    setNewComment("");
24  };
25
26  return (
27    <div>
28      <form onSubmit={handleSubmit} className="mb-6">
29        <textarea
30          value={newComment}
31          onChange={(e) => setNewComment(e.target.value)}
32          className="w-full border rounded p-3"
33        />
34        <button 
35          type="submit"
36          className="bg-blue-600 text-white px-4 py-2 rounded mt-2"
37        >
38          Comment
39        </button>
40      </form>
41
42      <div className="space-y-4">
43        {comments.map(comment => (
44          <CommentCard key={comment.id} comment={comment} />
45        ))}
46      </div>
47    </div>
48  );
49}
50
51function CommentCard({ comment }: { comment: Comment }) {
52  return (
53    <div className="flex gap-3">
54      <div className="w-10 h-10 bg-gray-300 rounded-full" />
55      <div className="flex-1">
56        <div className="flex items-center gap-2 mb-1">
57          <span className="font-bold">{comment.author}</span>
58          <span className="text-sm text-gray-500">{comment.createdAt}</span>
59        </div>
60        <p className="text-gray-800 mb-2">{comment.content}</p>
61        <div className="flex gap-4 text-sm">
62          <button className="hover:text-blue-600">
63            👍 {comment.likes}
64          </button>
65          <button className="hover:text-blue-600">Reply</button>
66        </div>
67      </div>
68    </div>
69  );
70}

Like/Subscribe System 👍

1function EngagementButtons({ videoId }: { videoId: number }) {
2  const [liked, setLiked] = useState(false);
3  const [subscribed, setSubscribed] = useState(false);
4  const [likes, setLikes] = useState(0);
5
6  const handleLike = async () => {
7    await fetch(`/api/videos/${videoId}/like`, { method: 'POST' });
8    setLiked(!liked);
9    setLikes(liked ? likes - 1 : likes + 1);
10  };
11
12  const handleSubscribe = async () => {
13    await fetch(`/api/channels/subscribe`, { method: 'POST' });
14    setSubscribed(!subscribed);
15  };
16
17  return (
18    <div className="flex gap-4">
19      <button 
20        onClick={handleLike}
21        className={`px-4 py-2 rounded ${liked ? 'bg-blue-600 text-white' : 'bg-gray-200'}`}
22      >
23        👍 {likes}
24      </button>
25      <button 
26        onClick={handleSubscribe}
27        className={`px-6 py-2 rounded ${subscribed ? 'bg-gray-800 text-white' : 'bg-red-600 text-white'}`}
28      >
29        {subscribed ? 'Subscribed' : 'Subscribe'}
30      </button>
31    </div>
32  );
33}

Notifications System 🔔

1function NotificationBell() {
2  const [notifications, setNotifications] = useState<Notification[]>([]);
3  const [open, setOpen] = useState(false);
4
5  useEffect(() => {
6    // Poll for new notifications
7    const interval = setInterval(async () => {
8      const res = await fetch('/api/notifications');
9      const data = await res.json();
10      setNotifications(data.notifications);
11    }, 30000);  // Every 30s
12
13    return () => clearInterval(interval);
14  }, []);
15
16  return (
17    <div className="relative">
18      <button onClick={() => setOpen(!open)} className="relative">
19        🔔
20        {notifications.length > 0 && (
21          <span className="absolute -top-1 -right-1 bg-red-600 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
22            {notifications.length}
23          </span>
24        )}
25      </button>
26
27      {open && (
28        <div className="absolute top-full right-0 w-80 bg-white border rounded shadow-lg mt-2 max-h-96 overflow-auto">
29          {notifications.map(notif => (
30            <div key={notif.id} className="p-3 border-b hover:bg-gray-50">
31              <p className="text-sm">{notif.message}</p>
32              <span className="text-xs text-gray-500">{notif.createdAt}</span>
33            </div>
34          ))}
35        </div>
36      )}
37    </div>
38  );
39}

Podsumowanie 🎓

Comments - nested replies, threading ✅ Like/Dislike - engagement buttons ✅ Subscribe - channel subscriptions ✅ Notifications - real-time updates

Do zobaczenia! 🚀

Przejdź do CodeWorlds