Add read-only chatroom
This commit is contained in:
174
src/app/game/berry-dash/chatroom/page.tsx
Normal file
174
src/app/game/berry-dash/chatroom/page.tsx
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { BackButton } from '@/app/components/BackButton'
|
||||||
|
import { DiscordButton } from '@/app/components/DiscordButton'
|
||||||
|
import { GetIconForUser } from '@/util/bd'
|
||||||
|
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 [messages, setMessages] = useState<Message[]>([])
|
||||||
|
const [customIconCache, setCustomIconCache] = useState<CustomIconEntry[]>([])
|
||||||
|
const [ws, setWs] = useState<WebSocket | null>(null)
|
||||||
|
const messagesDiv = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = 'Lncvrt Games - Chatroom'
|
||||||
|
|
||||||
|
const socket = new WebSocket('wss://games.lncvrt.xyz/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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(',')
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
const result = await axios.get(
|
||||||
|
`https://games.lncvrt.xyz/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='max-w-240 truncate select-text'>
|
||||||
|
{atob(item.content)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!connected && (
|
||||||
|
<p className='text-center text-red-500'>
|
||||||
|
Not connected to the chatroom
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
7
src/util/bd.ts
Normal file
7
src/util/bd.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export const GetIconForUser = (user: number) => {
|
||||||
|
if (user == 1) return -1
|
||||||
|
else if (user == 2) return -2
|
||||||
|
else if (user == 4) return -3
|
||||||
|
else if (user == 3) return -4
|
||||||
|
else return 1
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user