Replace legacy hardcoded border-kodo-steel (RGB 59,69,84, theme-unaware) with semantic border-border token across 86 user-facing components. Covers UI primitives (checkbox, badge, modal, table, textarea, alert, radio-group, avatar), all modals, settings views, social features, playlist views, inventory, chat, commerce, and cloud file browser. Only story/test files retain the legacy token. Co-authored-by: Cursor <cursoragent@cursor.com>
231 lines
7.9 KiB
TypeScript
231 lines
7.9 KiB
TypeScript
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">
|
|
<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>
|
|
);
|
|
};
|