diff --git a/apps/web/src/components/views/LiveView.stories.tsx b/apps/web/src/components/views/LiveView.stories.tsx index 48a6d6cdb..85d53415e 100644 --- a/apps/web/src/components/views/LiveView.stories.tsx +++ b/apps/web/src/components/views/LiveView.stories.tsx @@ -1,21 +1,26 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { LiveView } from './LiveView'; +import { LiveView, LiveViewSkeleton } from './live-view'; const meta: Meta = { - title: 'Components/Features/Views/LiveView', - component: LiveView, - parameters: { layout: 'fullscreen' }, - tags: ['autodocs'], - decorators: [ - (Story) => ( -
- -
- ), - ], + title: 'Components/Features/Views/LiveView', + component: LiveView, + parameters: { layout: 'fullscreen' }, + tags: ['autodocs'], + decorators: [ + (Story) => ( +
+ +
+ ), + ], }; export default meta; type Story = StoryObj; +export const Loading: Story = { + name: 'Loading', + render: () => , +}; + export const Default: Story = { name: 'Par défaut' }; diff --git a/apps/web/src/components/views/LiveView.tsx b/apps/web/src/components/views/LiveView.tsx index ab1760ee0..0f51fcfb0 100644 --- a/apps/web/src/components/views/LiveView.tsx +++ b/apps/web/src/components/views/LiveView.tsx @@ -1,270 +1 @@ -import React, { useState } from 'react'; -import { Card } from '../ui/card'; -import { Button } from '../ui/button'; -import { Badge } from '../ui/badge'; -import { - Users, - Heart, - Share2, - DollarSign, - MessageSquare, - Send, - Radio, - Settings, - Maximize2, -} from 'lucide-react'; -import { LiveStream } from '../../types'; -import { useToast } from '../../components/feedback/ToastProvider'; - -const featuredStream: LiveStream = { - id: '1', - title: 'Late Night DnB Production 🎧 | Feedback Session', - streamer: 'Neuro_Glitch', - viewers: 1240, - thumbnailUrl: 'https://picsum.photos/id/140/800/450', - tags: ['Production', 'Ableton', 'DnB'], - isLive: true, - category: 'Production', -}; - -const chatMessages = [ - { - user: 'BassHead99', - text: 'That Reese bass is filthy! 🤮🔥', - color: 'text-kodo-steel', - }, - { user: 'Studio_Rat', text: 'What VST is that?', color: 'text-kodo-content-dim' }, - { - user: 'Neuro_Glitch', - text: "It's Phase Plant, just initializing now.", - color: 'text-kodo-gold font-bold', - }, - { - user: 'VocalChops', - text: 'Sent a $5 dono! Check my track?', - color: 'text-kodo-lime', - }, -]; - -export const LiveView: React.FC = () => { - const { addToast } = useToast(); - const [msgInput, setMsgInput] = useState(''); - - const handleSend = () => { - if (!msgInput) return; - addToast('Message sent to chat', 'success'); - setMsgInput(''); - }; - - return ( -
- {/* Main Stream Area */} -
-
- {/* Mock Video Feed */} - -
- - {/* Live Indicator */} -
- - LIVE - - - {featuredStream.viewers} - -
- - {/* Stream Controls Overlay */} -
-
- - -
-
- -
-
-
- - {/* Stream Info */} -
-
-
- -
-
-

- {featuredStream.title} -

-

addToast('Opening Streamer Profile')} - > - {featuredStream.streamer} -

-
- {featuredStream.tags.map((tag) => ( - - ))} -
-
-
-
- - - -
-
- - {/* Suggested Streams */} -
-

- Recommended Channels -

-
- {[1, 2, 3].map((i) => ( - addToast('Switching stream...')} - > -
- -
- DJ Set -
-
-
-
-
-
- Techno Bunker 24/7 -
-
- Underground_Radio -
-
-
-
- ))} -
-
-
- - {/* Live Chat */} - -
- - STREAM CHAT - -
-
- -
- {chatMessages.map((msg, i) => ( -
- - {msg.user}: - - {msg.text} -
- ))} -
- - Welcome to the chat room! - -
-
- -
-
-
- setMsgInput(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && handleSend()} - className="w-full bg-kodo-ink border border-kodo-steel rounded px-4 py-2 text-sm text-white focus:border-kodo-steel outline-none" - placeholder="Say something..." - /> - -
- -
-
- - Balance: 420 $VEZA - - addToast('Opening Wallet...')} - > - Get Coins - -
-
-
-
- ); -}; +export { LiveView } from './live-view'; diff --git a/apps/web/src/components/views/live-view/LiveView.tsx b/apps/web/src/components/views/live-view/LiveView.tsx new file mode 100644 index 000000000..aacd20b4c --- /dev/null +++ b/apps/web/src/components/views/live-view/LiveView.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { useLiveView } from './useLiveView'; +import { LiveViewPlayer } from './LiveViewPlayer'; +import { LiveViewStreamInfo } from './LiveViewStreamInfo'; +import { LiveViewRecommended } from './LiveViewRecommended'; +import { LiveViewChat } from './LiveViewChat'; +import type { LiveViewProps } from './types'; + +export function LiveView({ stream: streamOverride, chatMessages: chatOverride }: LiveViewProps = {}) { + const { + stream, + chatMessages, + msgInput, + setMsgInput, + handleSend, + addToast, + } = useLiveView({ + stream: streamOverride ?? undefined, + chatMessages: chatOverride, + }); + + return ( +
+
+ addToast('Chat hidden')} + onSettings={() => addToast('Stream Settings')} + onFullscreen={() => addToast('Entering Fullscreen')} + /> + addToast('Opening Streamer Profile')} + onFollow={() => addToast('Followed Streamer', 'success')} + onDonate={() => addToast('Donation modal opening...', 'info')} + onShare={() => addToast('Stream link copied!')} + /> + addToast('Switching stream...')} /> +
+ + addToast('Opening Wallet...')} + /> +
+ ); +} diff --git a/apps/web/src/components/views/live-view/LiveViewChat.tsx b/apps/web/src/components/views/live-view/LiveViewChat.tsx new file mode 100644 index 000000000..7e3b4b859 --- /dev/null +++ b/apps/web/src/components/views/live-view/LiveViewChat.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { Card } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Send, DollarSign } from 'lucide-react'; +import type { LiveViewChatMessage } from './types'; + +interface LiveViewChatProps { + messages: LiveViewChatMessage[]; + msgInput: string; + onMsgInputChange: (value: string) => void; + onSend: () => void; + onWalletClick?: () => void; +} + +export function LiveViewChat({ + messages, + msgInput, + onMsgInputChange, + onSend, + onWalletClick, +}: LiveViewChatProps) { + return ( + +
+ + STREAM CHAT + +
+
+ +
+ {messages.map((msg, i) => ( +
+ + {msg.user}: + + {msg.text} +
+ ))} +
+ + Welcome to the chat room! + +
+
+ +
+
+
+ onMsgInputChange(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && onSend()} + className="w-full bg-kodo-ink border border-kodo-steel rounded px-4 py-2 text-sm text-white focus:border-kodo-steel outline-none" + placeholder="Say something..." + /> + +
+ +
+
+ + Balance: 420 $VEZA + + + Get Coins + +
+
+ + ); +} diff --git a/apps/web/src/components/views/live-view/LiveViewPlayer.tsx b/apps/web/src/components/views/live-view/LiveViewPlayer.tsx new file mode 100644 index 000000000..9ef6a88e8 --- /dev/null +++ b/apps/web/src/components/views/live-view/LiveViewPlayer.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Users, Radio, MessageSquare, Settings, Maximize2 } from 'lucide-react'; +import type { LiveStream } from '@/types'; + +interface LiveViewPlayerProps { + stream: LiveStream; + onToggleChat?: () => void; + onSettings?: () => void; + onFullscreen?: () => void; +} + +export function LiveViewPlayer({ + stream, + onToggleChat, + onSettings, + onFullscreen, +}: LiveViewPlayerProps) { + return ( +
+ +
+ +
+ + LIVE + + + {stream.viewers} + +
+ +
+
+ + +
+
+ +
+
+
+ ); +} diff --git a/apps/web/src/components/views/live-view/LiveViewRecommended.tsx b/apps/web/src/components/views/live-view/LiveViewRecommended.tsx new file mode 100644 index 000000000..e329ad3c3 --- /dev/null +++ b/apps/web/src/components/views/live-view/LiveViewRecommended.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Card } from '@/components/ui/card'; + +interface LiveViewRecommendedProps { + onChannelClick?: (index: number) => void; +} + +export function LiveViewRecommended({ onChannelClick }: LiveViewRecommendedProps) { + return ( +
+

+ Recommended Channels +

+
+ {[1, 2, 3].map((i) => ( + onChannelClick?.(i)} + > +
+ +
+ DJ Set +
+
+
+
+
+
+ Techno Bunker 24/7 +
+
+ Underground_Radio +
+
+
+ + ))} +
+
+ ); +} diff --git a/apps/web/src/components/views/live-view/LiveViewSkeleton.tsx b/apps/web/src/components/views/live-view/LiveViewSkeleton.tsx new file mode 100644 index 000000000..621338144 --- /dev/null +++ b/apps/web/src/components/views/live-view/LiveViewSkeleton.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Skeleton } from '@/components/ui/skeleton'; + +export function LiveViewSkeleton() { + return ( +
+
+ +
+
+ +
+ + +
+ + + +
+
+
+
+ + + +
+
+
+ +
+ {[1, 2, 3].map((i) => ( + + ))} +
+
+
+
+ +
+ {[1, 2, 3, 4, 5].map((i) => ( + + ))} +
+ +
+
+ ); +} diff --git a/apps/web/src/components/views/live-view/LiveViewStreamInfo.tsx b/apps/web/src/components/views/live-view/LiveViewStreamInfo.tsx new file mode 100644 index 000000000..3238b3fe7 --- /dev/null +++ b/apps/web/src/components/views/live-view/LiveViewStreamInfo.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { Heart, Share2, DollarSign } from 'lucide-react'; +import type { LiveStream } from '@/types'; + +interface LiveViewStreamInfoProps { + stream: LiveStream; + onStreamerClick?: () => void; + onFollow?: () => void; + onDonate?: () => void; + onShare?: () => void; +} + +export function LiveViewStreamInfo({ + stream, + onStreamerClick, + onFollow, + onDonate, + onShare, +}: LiveViewStreamInfoProps) { + return ( +
+
+
+ +
+
+

{stream.title}

+

+ {stream.streamer} +

+
+ {stream.tags.map((tag) => ( + + ))} +
+
+
+
+ + + +
+
+ ); +} diff --git a/apps/web/src/components/views/live-view/index.ts b/apps/web/src/components/views/live-view/index.ts new file mode 100644 index 000000000..9c89676aa --- /dev/null +++ b/apps/web/src/components/views/live-view/index.ts @@ -0,0 +1,6 @@ +export type { LiveViewProps, LiveViewChatMessage } from './types'; +export type { LiveStream } from '@/types'; +export { LiveView } from './LiveView'; +export { LiveViewSkeleton } from './LiveViewSkeleton'; +export { useLiveView } from './useLiveView'; +export { FEATURED_STREAM, CHAT_MESSAGES } from './mockData'; diff --git a/apps/web/src/components/views/live-view/mockData.ts b/apps/web/src/components/views/live-view/mockData.ts new file mode 100644 index 000000000..72d238dd8 --- /dev/null +++ b/apps/web/src/components/views/live-view/mockData.ts @@ -0,0 +1,36 @@ +import type { LiveStream } from '@/types'; +import type { LiveViewChatMessage } from './types'; + +export const FEATURED_STREAM: LiveStream = { + id: '1', + title: 'Late Night DnB Production 🎧 | Feedback Session', + streamer: 'Neuro_Glitch', + viewers: 1240, + thumbnailUrl: 'https://picsum.photos/id/140/800/450', + tags: ['Production', 'Ableton', 'DnB'], + isLive: true, + category: 'Production', +}; + +export const CHAT_MESSAGES: LiveViewChatMessage[] = [ + { + user: 'BassHead99', + text: 'That Reese bass is filthy! 🤮🔥', + color: 'text-kodo-steel', + }, + { + user: 'Studio_Rat', + text: 'What VST is that?', + color: 'text-kodo-content-dim', + }, + { + user: 'Neuro_Glitch', + text: "It's Phase Plant, just initializing now.", + color: 'text-kodo-gold font-bold', + }, + { + user: 'VocalChops', + text: 'Sent a $5 dono! Check my track?', + color: 'text-kodo-lime', + }, +]; diff --git a/apps/web/src/components/views/live-view/types.ts b/apps/web/src/components/views/live-view/types.ts new file mode 100644 index 000000000..321f82541 --- /dev/null +++ b/apps/web/src/components/views/live-view/types.ts @@ -0,0 +1,16 @@ +import type { LiveStream } from '@/types'; + +export type { LiveStream }; + +export interface LiveViewChatMessage { + user: string; + text: string; + color: string; +} + +export interface LiveViewProps { + /** Optional stream override for stories */ + stream?: LiveStream | null; + /** Optional chat messages override */ + chatMessages?: LiveViewChatMessage[]; +} diff --git a/apps/web/src/components/views/live-view/useLiveView.ts b/apps/web/src/components/views/live-view/useLiveView.ts new file mode 100644 index 000000000..f6dfe8577 --- /dev/null +++ b/apps/web/src/components/views/live-view/useLiveView.ts @@ -0,0 +1,37 @@ +import { useState, useCallback } from 'react'; +import { useToast } from '@/components/feedback/ToastProvider'; +import { FEATURED_STREAM, CHAT_MESSAGES } from './mockData'; +import type { LiveStream } from '@/types'; +import type { LiveViewChatMessage } from './types'; + +export interface UseLiveViewOptions { + stream?: LiveStream | null; + chatMessages?: LiveViewChatMessage[]; + onSendMessage?: (text: string) => void; +} + +export function useLiveView(options: UseLiveViewOptions = {}) { + const { addToast } = useToast(); + const stream = options.stream ?? FEATURED_STREAM; + const chatMessages = options.chatMessages ?? CHAT_MESSAGES; + const [msgInput, setMsgInput] = useState(''); + + const handleSend = useCallback(() => { + if (!msgInput.trim()) return; + if (options.onSendMessage) { + options.onSendMessage(msgInput); + } else { + addToast('Message sent to chat', 'success'); + } + setMsgInput(''); + }, [msgInput, options.onSendMessage, addToast]); + + return { + stream, + chatMessages, + msgInput, + setMsgInput, + handleSend, + addToast, + }; +}