233 lines
6.7 KiB
TypeScript
233 lines
6.7 KiB
TypeScript
'use client'
|
|
|
|
import { BackButton } from '@/app/components/BackButton'
|
|
import { DiscordButton } from '@/app/components/DiscordButton'
|
|
import { GetIconForUser } from '@/util/bd'
|
|
import { getCookie } from '@/util/cookie'
|
|
import axios from 'axios'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
|
|
interface WSMessage {
|
|
success: boolean
|
|
message: string | null
|
|
for: string
|
|
data: any
|
|
}
|
|
|
|
type BirdColor = [number, number, number]
|
|
|
|
interface Message {
|
|
username: string
|
|
userId: number
|
|
content: string
|
|
id: number
|
|
icon: number
|
|
overlay: number
|
|
birdColor: BirdColor
|
|
overlayColor: BirdColor
|
|
customIcon: string | null
|
|
}
|
|
|
|
interface CustomIconEntry {
|
|
data: string
|
|
id: string
|
|
}
|
|
|
|
export default function BerryDashChatroom () {
|
|
const [connected, setConnected] = useState<boolean>(false)
|
|
const [loggedIn, setLoggedIn] = useState<boolean>(false)
|
|
const [token, setToken] = useState<string | null>(null)
|
|
const [messages, setMessages] = useState<Message[]>([])
|
|
const [customIconCache, setCustomIconCache] = useState<CustomIconEntry[]>([])
|
|
const [ws, setWs] = useState<WebSocket | null>(null)
|
|
|
|
const messagesDiv = useRef<HTMLDivElement>(null)
|
|
const [input, setInput] = useState<string>('')
|
|
|
|
useEffect(() => {
|
|
document.title = 'Lncvrt Games - Chatroom'
|
|
|
|
const token = getCookie('accountToken', '-1')
|
|
if (token !== '-1') {
|
|
setLoggedIn(true)
|
|
setToken(token)
|
|
}
|
|
|
|
const socket = new WebSocket('/api/ws')
|
|
|
|
socket.onopen = () => {
|
|
setConnected(true)
|
|
socket.send(
|
|
JSON.stringify({
|
|
type: 'info_request',
|
|
kind: 'chatroom_messages',
|
|
timestamp: Date.now()
|
|
})
|
|
)
|
|
}
|
|
|
|
socket.onmessage = event => {
|
|
const message = JSON.parse(event.data) as WSMessage
|
|
if (message.for == 'info_request:chatroom_messages') {
|
|
const messages = message.data as Message[]
|
|
setMessages(messages)
|
|
} else if (message.for == 'upload:chatroom_message') {
|
|
const msg = message.data as Message
|
|
setMessages(prev => [...prev.slice(1), msg])
|
|
} else if (message.for == 'delete:chatroom_message') {
|
|
const msg = message.data.fillerMessage as Message
|
|
setMessages(prev => [msg, ...prev.slice(0, -1)])
|
|
} else if (message.for == 'edit:chatroom_message') {
|
|
setMessages(prev =>
|
|
prev.map(msg =>
|
|
msg.id === message.data.id
|
|
? { ...msg, content: btoa(message.data.newContent) }
|
|
: msg
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
socket.onclose = () => {
|
|
setConnected(false)
|
|
setMessages([])
|
|
}
|
|
|
|
setWs(socket)
|
|
|
|
return () => {
|
|
socket.close()
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (messagesDiv.current) {
|
|
messagesDiv.current.scrollTop = messagesDiv.current.scrollHeight
|
|
}
|
|
const all = customIconCache.map(icon => icon.id)
|
|
const allFromMessages = Array.from(
|
|
new Set(
|
|
messages
|
|
.filter(icon => icon.customIcon != null)
|
|
.map(icon => icon.customIcon as string)
|
|
)
|
|
)
|
|
const notInAllIds = allFromMessages
|
|
.filter(id => !all.includes(id))
|
|
.map(id => `"${id}"`)
|
|
.join(',')
|
|
|
|
if (notInAllIds.length != 0) {
|
|
;(async () => {
|
|
const result = await axios.get(
|
|
`/api/berrydash/icon-marketplace/icon?ids=[${notInAllIds}]`
|
|
)
|
|
if (result.data.success) {
|
|
const add: CustomIconEntry[] = []
|
|
for (const item of result.data.data) {
|
|
add.push({
|
|
data: item.data,
|
|
id: item.id
|
|
})
|
|
}
|
|
setCustomIconCache(prev => [
|
|
...prev,
|
|
...result.data.data.map((item: CustomIconEntry) => ({
|
|
data: item.data,
|
|
id: item.id
|
|
}))
|
|
])
|
|
}
|
|
})()
|
|
}
|
|
}, [messages])
|
|
|
|
return (
|
|
<div className='box'>
|
|
<BackButton href='/game/berry-dash' />
|
|
<DiscordButton />
|
|
<p className='px-8 mb-4 -mt-2 text-center text-2xl'>
|
|
Berry Dash Chatroom
|
|
</p>
|
|
{connected && (
|
|
<>
|
|
<div
|
|
className='sub-box -m-4 mt-0 flex flex-col gap-2 max-h-[calc(100vh-204px)] overflow-y-auto'
|
|
ref={messagesDiv}
|
|
>
|
|
{messages.map(item => {
|
|
return (
|
|
<div key={item.id} className='sub-box2 flex flex-row gap-1'>
|
|
<img
|
|
src={
|
|
!item.customIcon
|
|
? `https://games-r2.lncvrt.xyz/game-assets/berrydash/icons/bird_${
|
|
item.icon == 1
|
|
? GetIconForUser(item.userId)
|
|
: item.icon
|
|
}.png`
|
|
: customIconCache.find(i => i.id === item.customIcon)
|
|
? 'data:image/png;base64,' +
|
|
customIconCache.find(i => i.id === item.customIcon)
|
|
?.data
|
|
: 'https://games-r2.lncvrt.xyz/game-assets/berrydash/other/loading.png'
|
|
}
|
|
className='pointer-events-none'
|
|
width={48}
|
|
height={48}
|
|
></img>
|
|
<div>
|
|
<p>{item.username}</p>
|
|
<p className='w-240 max-w-[calc(100vw-136px)] truncate select-text'>
|
|
{atob(item.content)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
{loggedIn ? (
|
|
<form
|
|
onSubmit={e => {
|
|
e.preventDefault()
|
|
setInput('')
|
|
ws?.send(
|
|
JSON.stringify({
|
|
type: 'upload',
|
|
kind: 'chatroom_message',
|
|
data: {
|
|
content: input,
|
|
auth: token
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
)
|
|
}}
|
|
className='flex flex-row gap-2 -mb-4 mt-6'
|
|
>
|
|
<input
|
|
type='text'
|
|
className='w-full -ml-4 text-center'
|
|
placeholder='Enter a message to send...'
|
|
maxLength={60}
|
|
value={input}
|
|
onChange={e => setInput(e.target.value)}
|
|
></input>
|
|
<button className='-mr-4'>Send</button>
|
|
</form>
|
|
) : (
|
|
<p className='text-center -mb-4 mt-6'>
|
|
You must be logged in to send messages
|
|
</p>
|
|
)}
|
|
</>
|
|
)}
|
|
{!connected && (
|
|
<p className='text-center text-red-500'>
|
|
Not connected to the chatroom
|
|
</p>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|