diff --git a/resources/js/pages/channels/show.tsx b/resources/js/pages/channels/show.tsx index 3720ae2..2840392 100644 --- a/resources/js/pages/channels/show.tsx +++ b/resources/js/pages/channels/show.tsx @@ -1,15 +1,17 @@ import { Head, Link, router, useForm, usePage } from '@inertiajs/react'; import { + Crown, Heart, LogIn, MessageSquare, Radio, Send, + SmilePlus, Users, Video, } from 'lucide-react'; import type { FormEvent } from 'react'; -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Avatar as ChatAvatar, AvatarFallback, @@ -45,6 +47,25 @@ type Props = { isFollowing: boolean; }; +const chatEmojis = [ + '😀', + '😂', + '😍', + '😎', + '🔥', + '👏', + '🙌', + '❤️', + '💯', + '🎉', + '👍', + '👀', + '🤯', + '😭', + '🙏', + '✨', +]; + export default function ChannelShow({ channel, currentBroadcast, @@ -155,6 +176,9 @@ export default function ChannelShow({ >; onSubmit: (event: FormEvent) => void; compact?: boolean; }) { const getInitials = useInitials(); + const inputRef = useRef(null); + const messagesEndRef = useRef(null); + const [emojiPickerOpen, setEmojiPickerOpen] = useState(false); + const messageLength = form.data.body.length; + const canSend = + channel.is_live && form.data.body.trim().length > 0 && !form.processing; + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ block: 'end' }); + }, [messages.length]); + + function submitChatForm(event: FormEvent) { + setEmojiPickerOpen(false); + onSubmit(event); + } + + function insertEmoji(emoji: string) { + const input = inputRef.current; + const cursorStart = input?.selectionStart ?? form.data.body.length; + const cursorEnd = input?.selectionEnd ?? form.data.body.length; + const nextBody = ( + form.data.body.slice(0, cursorStart) + + emoji + + form.data.body.slice(cursorEnd) + ).slice(0, 500); + const nextCursor = Math.min( + cursorStart + emoji.length, + nextBody.length, + ); + + form.setData('body', nextBody); + + window.requestAnimationFrame(() => { + inputRef.current?.focus(); + inputRef.current?.setSelectionRange(nextCursor, nextCursor); + }); + } return (
@@ -296,66 +360,198 @@ function ChatPanel({ >
-

Live chat

+
+

Live chat

+

+ {formatMessageCount(messages.length)} +

+
-
- {messages.map((message) => ( -
- - - - {getInitials(message.user.name)} - - -
-
- {message.user.name} -
-
- {message.body} +
+
+ {messages.map((message) => { + const isOwnMessage = currentUserId === message.user.id; + const isChannelOwner = + channel.owner.id === message.user.id; + + return ( +
+ + + + {getInitials(message.user.name)} + + +
+
+ + {isOwnMessage + ? 'You' + : message.user.name} + + {isChannelOwner && ( + + + Owner + + )} + {message.created_at && ( + + )} +
+
+ {message.body} +
+
+ ); + })} + {messages.length === 0 && ( +
+ + + {channel.is_live + ? 'No messages yet.' + : 'Chat appears when the channel is live.'} +
-
- ))} - {messages.length === 0 && ( -
- {channel.is_live - ? 'No messages yet.' - : 'Chat appears when the channel is live.'} -
- )} + )} +
+
{authUser ? ( -
- - form.setData('body', event.target.value) - } - placeholder={ - channel.is_live - ? 'Send a message' - : 'Channel is offline' - } - disabled={!channel.is_live} - /> - + +
+ + form.setData('body', event.target.value) + } + placeholder={ + channel.is_live + ? 'Send a message' + : 'Channel is offline' + } + disabled={!channel.is_live || form.processing} + maxLength={500} + /> +
+ + {emojiPickerOpen && channel.is_live && ( +
+ {chatEmojis.map((emoji) => ( + + ))} +
+ )} +
+ +
+
+ + {form.errors.body ?? + (!channel.is_live + ? 'Channel is offline' + : '')} + + 450 && 'text-primary', + )} + > + {messageLength}/500 + +
) : (