Twitch is about monetization - subscriptions, bits, followers.
1interface Subscription {
2 userId: string;
3 channelId: string;
4 tier: 'tier1' | 'tier2' | 'tier3'; // $4.99, $9.99, $24.99
5 startDate: Date;
6 renewDate: Date;
7 isGift: boolean;
8 giftedBy?: string;
9 streak: number; // consecutive months
10 cumulativeMonths: number; // total months
11}
12
13interface SubBenefits {
14 tier1: {
15 price: 4.99;
16 emotes: string[]; // unlock emotes
17 badge: string;
18 adFree: true;
19 chatPerks: {
20 slowModeExempt: true;
21 subsOnlyChat: true;
22 };
23 };
24 tier2: {
25 price: 9.99;
26 emotes: string[];
27 badge: string;
28 adFree: true;
29 chatPerks: {
30 slowModeExempt: true;
31 subsOnlyChat: true;
32 };
33 };
34 tier3: {
35 price: 24.99;
36 emotes: string[];
37 badge: string;
38 adFree: true;
39 chatPerks: {
40 slowModeExempt: true;
41 subsOnlyChat: true;
42 };
43 };
44}
45
46interface Bits {
47 userId: string;
48 channelId: string;
49 amount: number; // 100 bits = $1
50 message?: string;
51 timestamp: Date;
52}1'use client';
2
3interface ChannelPageProps {
4 channel: Channel;
5 isFollowing: boolean;
6 isSubscribed: boolean;
7 subscriptionTier?: 'tier1' | 'tier2' | 'tier3';
8}
9
10export default function ChannelPage({
11 channel,
12 isFollowing,
13 isSubscribed,
14 subscriptionTier
15}: ChannelPageProps) {
16 const [activeTab, setActiveTab] = useState<'home' | 'videos' | 'schedule' | 'about'>('home');
17
18 return (
19 <div className="min-h-screen bg-gray-950 text-white">
20 {/* Banner */}
21 {channel.banner && (
22 <div
23 className="h-80 bg-cover bg-center"
24 style={{ backgroundImage: `url(${channel.banner})` }}
25 />
26 )}
27
28 {/* Channel Header */}
29 <div className="max-w-7xl mx-auto px-6 -mt-16 relative z-10">
30 <div className="flex items-end gap-6 mb-6">
31 {/* Avatar */}
32 <img
33 src={channel.avatar}
34 alt={channel.displayName}
35 className="w-32 h-32 rounded-full border-4 border-gray-950"
36 />
37
38 {/* Info */}
39 <div className="flex-1 pb-4">
40 <h1 className="text-4xl font-bold mb-2">{channel.displayName}</h1>
41 <p className="text-gray-400">{channel.description}</p>
42 </div>
43
44 {/* Actions */}
45 <div className="flex items-center gap-3 pb-4">
46 <button
47 className={`
48 px-6 py-2 rounded font-semibold
49 ${isFollowing
50 ? 'bg-gray-700 hover:bg-gray-600'
51 : 'bg-purple-600 hover:bg-purple-700'
52 }
53 `}
54 >
55 {isFollowing ? '❤️ Following' : '🤍 Follow'}
56 </button>
57
58 {!isSubscribed ? (
59 <button className="px-6 py-2 bg-purple-600 rounded font-semibold hover:bg-purple-700">
60 ⭐ Subscribe
61 </button>
62 ) : (
63 <button className="px-6 py-2 bg-purple-900 border border-purple-600 rounded font-semibold hover:bg-purple-800">
64 ⭐ Subscribed ({subscriptionTier})
65 </button>
66 )}
67
68 <button className="p-2 hover:bg-gray-800 rounded">
69 ⚙️
70 </button>
71 </div>
72 </div>
73
74 {/* Stats */}
75 <div className="flex gap-6 mb-6 text-sm text-gray-400">
76 <div>
77 <span className="font-semibold text-white">{channel.followers.toLocaleString()}</span> followers
78 </div>
79 <div>
80 <span className="font-semibold text-white">{channel.totalViews.toLocaleString()}</span> total views
81 </div>
82 {channel.isPartner && (
83 <div className="flex items-center gap-1">
84 <span className="text-purple-500">✓</span>
85 <span>Partner</span>
86 </div>
87 )}
88 </div>
89
90 {/* Tabs */}
91 <div className="border-b border-gray-800">
92 <div className="flex gap-8">
93 {[
94 { key: 'home', label: 'Home' },
95 { key: 'videos', label: 'Videos' },
96 { key: 'schedule', label: 'Schedule' },
97 { key: 'about', label: 'About' }
98 ].map(tab => (
99 <button
100 key={tab.key}
101 onClick={() => setActiveTab(tab.key as any)}
102 className={`
103 pb-3 font-semibold
104 ${activeTab === tab.key
105 ? 'border-b-2 border-purple-500 text-white'
106 : 'text-gray-400 hover:text-white'
107 }
108 `}
109 >
110 {tab.label}
111 </button>
112 ))}
113 </div>
114 </div>
115 </div>
116
117 {/* Content */}
118 <div className="max-w-7xl mx-auto px-6 py-8">
119 {activeTab === 'home' && (
120 <div>
121 {/* Live Stream or Offline */}
122 {channel.isLive && channel.currentStream ? (
123 <div className="mb-8">
124 <h2 className="text-xl font-bold mb-4">Live Now</h2>
125 <StreamCard stream={channel.currentStream} />
126 </div>
127 ) : (
128 <div className="text-center py-12 text-gray-500">
129 <div className="text-4xl mb-4">📺</div>
130 <p className="text-lg">Channel is offline</p>
131 <p>Check back later or follow to get notified</p>
132 </div>
133 )}
134 </div>
135 )}
136
137 {activeTab === 'videos' && (
138 <div>
139 <h2 className="text-xl font-bold mb-4">Recent Videos</h2>
140 <div className="grid grid-cols-3 gap-4">
141 {/* VODs grid */}
142 </div>
143 </div>
144 )}
145
146 {activeTab === 'about' && (
147 <div className="max-w-2xl">
148 <h2 className="text-xl font-bold mb-4">About</h2>
149 <p className="text-gray-300 leading-relaxed">
150 {channel.description}
151 </p>
152 </div>
153 )}
154 </div>
155 </div>
156 );
157}
158
159function StreamCard({ stream }: { stream: Stream }) {
160 return (
161 <div className="bg-gray-900 rounded overflow-hidden hover:scale-105 transition cursor-pointer">
162 <div className="relative">
163 <img
164 src={stream.thumbnailUrl}
165 alt={stream.title}
166 className="w-full aspect-video object-cover"
167 />
168 <div className="absolute top-2 left-2 px-2 py-1 bg-red-600 text-white text-xs font-bold rounded">
169 LIVE
170 </div>
171 <div className="absolute bottom-2 left-2 px-2 py-1 bg-black bg-opacity-70 text-white text-xs rounded">
172 {stream.viewerCount.toLocaleString()} viewers
173 </div>
174 </div>
175
176 <div className="p-3">
177 <h3 className="font-semibold mb-1 line-clamp-1">{stream.title}</h3>
178 <p className="text-sm text-gray-400">{stream.category.name}</p>
179 </div>
180 </div>
181 );
182}✅ Channel page - banner, avatar, description, tabs ✅ Follow button - follow/unfollow channel ✅ Subscribe button - subscription tiers ✅ Channel stats - followers, total views ✅ Partner badge - verified partner status ✅ Live stream card - thumbnail, viewer count, category ✅ Offline state - message when not streaming ✅ Subscription tiers - tier1, tier2, tier3 with benefits
In the next exercise we'll add browse categories and clips!
See you soon! 🚀