veza/apps/web/src/components/live/LiveStreamDetailView.tsx

232 lines
7.9 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { Button } from '../ui/button';
import { LiveStream } from '../../types';
import {
Users,
Heart,
Share2,
DollarSign,
Send,
Radio,
Settings,
ArrowLeft,
} from 'lucide-react';
import { useToast } from '../../components/feedback/ToastProvider';
import { TipStreamerModal } from './modals/TipStreamerModal';
interface LiveStreamDetailViewProps {
streamId: string;
onBack: () => void;
}
// Mock Stream Data
const MOCK_STREAM: LiveStream = {
id: 's1',
title: 'Late Night DnB Production 🎧 | Feedback Session',
streamer: 'Neuro_Glitch',
viewers: 1240,
thumbnailUrl: 'https://picsum.photos/id/140/1200/800',
tags: ['Production', 'Ableton', 'DnB'],
isLive: true,
category: 'Production',
};
export const LiveStreamDetailView: React.FC<LiveStreamDetailViewProps> = ({
streamId: _streamId,
onBack,
}) => {
const { addToast } = useToast();
const [chatInput, setChatInput] = useState('');
const [showTipModal, setShowTipModal] = useState(false);
const [messages, setMessages] = useState([
{
id: 1,
user: 'BassHead99',
text: 'That Reese bass is filthy! 🤮🔥',
color: 'text-kodo-cyan',
},
{
id: 2,
user: 'Studio_Rat',
text: 'What VST is that?',
color: 'text-muted-foreground',
},
{
id: 3,
user: 'Neuro_Glitch',
text: "It's Phase Plant, just initializing now.",
color: 'text-kodo-gold font-bold',
},
]);
const handleSendChat = () => {
if (!chatInput.trim()) return;
setMessages([
...messages,
{ id: Date.now(), user: 'You', text: chatInput, color: 'text-white' },
]);
setChatInput('');
};
const handleTip = (amount: number, message: string) => {
addToast(`Sent $${amount} to ${MOCK_STREAM.streamer}`, 'success');
setMessages([
...messages,
{
id: Date.now(),
user: 'System',
text: `You tipped $${amount}: ${message}`,
color: 'text-kodo-lime font-bold italic',
},
]);
};
return (
<div className="flex flex-col h-[calc(100vh-6rem)] -m-6 md:-m-12 bg-black animate-fadeIn overflow-hidden">
{/* Header Overlay (Fade in/out logic usually here) */}
<div className="absolute top-0 left-0 right-0 p-4 z-20 flex justify-between items-start pointer-events-none">
<Button
variant="ghost"
size="sm"
className="bg-black/50 backdrop-blur pointer-events-auto text-white hover:bg-black/70"
onClick={onBack}
>
<ArrowLeft className="w-4 h-4 mr-2" /> Back
</Button>
</div>
<div className="flex flex-1 overflow-hidden">
{/* Video Player Area */}
<div className="flex-1 bg-black relative flex items-center justify-center">
{/* Simulated Video */}
<div className="absolute inset-0">
<img
src={MOCK_STREAM.thumbnailUrl}
className="w-full h-full object-cover opacity-80"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black via-transparent to-black/30"></div>
</div>
{/* Stream Info Overlay */}
<div className="absolute bottom-0 left-0 right-0 p-6 bg-gradient-to-t from-black to-transparent pt-24">
<div className="flex items-end justify-between">
<div className="flex items-center gap-4">
<div className="w-14 h-14 rounded-full border-2 border-kodo-red p-0.5">
<img
src="https://picsum.photos/id/100/100"
className="w-full h-full rounded-full object-cover"
/>
</div>
<div>
<h1 className="text-3xl font-bold text-white mb-1">
{MOCK_STREAM.title}
</h1>
<div className="flex items-center gap-4 text-sm">
aesthetic-improvements: reduce decorative cyan across multiple component categories (80/20 rule, batch 11) - Social: FeedView, ConnectionsView, GroupsView, ExploreView, GroupDetailView loading spinners and decorative text, CreatePostModal decorative select text and hashtag links, PostCard decorative tag links and waveform bars and view comments link, CreateGroupModal decorative icon (9 instances) - Settings: DataExportModal decorative icon, LoginHistory decorative IP text, AppearanceSettingsView decorative icon and selected theme checkmark, BackupsView decorative icon, CloudIntegrationView decorative icon, AccessibilitySettingsView decorative icon, SecuritySettings decorative icon, PasskeyModal decorative icon and loading spinner (8 instances) - Studio: ProjectsManager loading spinner and progress percentage text, CloudFileBrowser decorative music icons, AIToolsView decorative music icon, ConnectivityView decorative icon, CreateProjectModal decorative icon, CloudSettingsView decorative icon (6 instances) - Admin: AdminDashboardView loading spinner and decorative chart bars and icon, AdminSettingsView decorative icon, AdminModerationView loading spinner, AdminUsersView loading spinner (5 instances) - Inventory: InventoryView loading spinner, EquipmentCard decorative price icon, EquipmentDetailView loading spinner and decorative icons and price text, AddEquipmentView decorative icon (5 instances) - Seller: CreateProductView decorative icon, SellerDashboardView loading spinner and decorative icon and sales text (3 instances) - Live: LiveStreamDetailView decorative streamer name text (1 instance) - Developer: DeveloperDashboardView loading spinner, WebhooksView decorative icon (2 instances) - Upload: BulkUploadModal decorative icon, FilePreviewCard decorative audio file icon, MetadataForm decorative button text, CoverArtUploadModal decorative icon, LyricsEditorModal decorative icon (5 instances) - Notifications: NotificationItem decorative follow icon and mark as read button, NotificationBell decorative mark all read link (3 instances) - Total: ~46 files, ~46 instances replaced - Preserved: Active/selected states (CloudFileBrowser selected files checkmarks, CreatePostModal post type active state, GroupCard/GroupDetailView public/private badges - semantic indicators, DataExportModal checkbox accents - focus/interaction, AppearanceSettingsView selected theme - active state, PasskeyModal checkbox accent - focus/interaction, LyricsEditorModal checkbox accent - focus/interaction, FileUploadZone drag active state - active state, EquipmentDetailView support link - functional link, FlashSaleModal link - functional link, EquipmentDetailView image indicator dots - active state), primary actions, design system variants - Action 11.3.1.3 in progress (eleventh batch: social, settings, studio, admin, inventory, seller, live, developer, upload, notifications components)
2026-01-16 10:26:33 +00:00
<span className="text-kodo-steel font-bold">
{MOCK_STREAM.streamer}
</span>
<span className="flex items-center gap-1 text-kodo-red font-bold animate-pulse">
<Radio className="w-3 h-3" /> LIVE
</span>
<span className="flex items-center gap-1 text-white">
<Users className="w-3 h-3" />{' '}
{MOCK_STREAM.viewers.toLocaleString()}
</span>
</div>
</div>
</div>
<div className="flex gap-4">
<Button
variant="secondary"
className="bg-black/50 backdrop-blur border-white/20 text-white"
icon={<Heart className="w-4 h-4" />}
>
Follow
</Button>
<Button
variant="primary"
className=""
icon={<DollarSign className="w-4 h-4" />}
onClick={() => setShowTipModal(true)}
>
Tip
</Button>
<Button
variant="ghost"
size="icon"
className="bg-black/50 backdrop-blur text-white"
>
<Share2 className="w-4 h-4" />
</Button>
</div>
</div>
</div>
</div>
{/* Chat Sidebar */}
<div className="w-80 md:w-96 bg-kodo-graphite border-l border-border flex flex-col z-20 shadow-2xl">
<div className="p-4 border-b border-border bg-kodo-ink flex justify-between items-center">
<h3 className="font-bold text-white text-sm">LIVE CHAT</h3>
<Settings className="w-4 h-4 text-muted-foreground cursor-pointer hover:text-white" />
</div>
<div className="flex-1 overflow-y-auto p-4 space-y-2 font-mono text-sm custom-scrollbar">
{messages.map((msg) => (
<div key={msg.id} className="break-words animate-slideInRight">
<span
className={`font-bold ${msg.color} mr-2 cursor-pointer hover:underline opacity-90`}
>
{msg.user}:
</span>
<span className="text-kodo-text-main">{msg.text}</span>
</div>
))}
</div>
<div className="p-4 border-t border-border bg-kodo-ink">
<div className="relative">
<input
className="w-full bg-kodo-void border border-border rounded-full py-2.5 pl-4 pr-10 text-white text-sm focus:border-border outline-none"
placeholder="Send a message..."
value={chatInput}
onChange={(e) => setChatInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSendChat()}
/>
<Button
variant="default"
size="icon"
className="absolute right-1.5 top-1.5 rounded-full hover:bg-white"
onClick={handleSendChat}
>
<Send className="w-3 h-3 fill-current" />
</Button>
</div>
<div className="flex justify-between items-center mt-2 px-2">
<span className="text-xs text-muted-foreground">Slow Mode: Off</span>
<div className="flex gap-2">
<Button
variant="ghost"
size="icon"
className="text-kodo-gold hover:text-white"
title="Send Tip"
onClick={() => setShowTipModal(true)}
>
<DollarSign className="w-4 h-4" />
</Button>
</div>
</div>
</div>
</div>
</div>
{showTipModal && (
<TipStreamerModal
streamerName={MOCK_STREAM.streamer}
onClose={() => setShowTipModal(false)}
onSend={handleTip}
/>
)}
</div>
);
};