Improvements & add leaderboards

This commit is contained in:
2026-02-02 00:57:53 -07:00
parent 902100f7ca
commit 569fb276a9
6 changed files with 200 additions and 8 deletions

View File

@@ -2,13 +2,29 @@ import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import Link from 'next/link'
export function BackButton ({ href }: { href: string }) {
export function BackButton ({
href,
onClick
}: {
href?: string
onClick?: () => void
}) {
return (
<>
<div className='relative'>
{href && (
<Link href={href} className='top-button absolute -top-4 -left-4'>
<FontAwesomeIcon icon={faArrowLeft} />
</Link>
)}
{onClick && (
<button
onClick={onClick}
className='top-button absolute -top-4 -left-4'
>
<FontAwesomeIcon icon={faArrowLeft} />
</button>
)}
</div>
</>
)

View File

@@ -18,7 +18,7 @@ interface WSMessage {
data: any
}
type BirdColor = [number, number, number]
export type BirdColor = [number, number, number]
interface Message {
username: string
@@ -35,7 +35,7 @@ interface Message {
editing: boolean
}
interface CustomIconEntry {
export interface CustomIconEntry {
data: string
id: string
}

View File

@@ -64,7 +64,7 @@ export default function BerryDashIconMarketplace () {
/>
</div>
<p>Bird Name: {icon.name}</p>
<p>Price: {icon.price}</p>
<p>Price: {icon.price.toLocaleString('en-US')}</p>
<p>Designer Name: {icon.username}</p>
</div>
))

View File

@@ -176,7 +176,7 @@ export default function BerryDashSubmitIcon () {
? 'N/A'
: name}
</p>
<p>Price: {price}</p>
<p>Price: {price.toLocaleString('en-US')}</p>
<p>Designer Name: {getCookie('accountUsername', '-1')}</p>
</div>
</div>

View File

@@ -0,0 +1,173 @@
'use client'
import { BackButton } from '@/app/components/BackButton'
import { DiscordButton } from '@/app/components/DiscordButton'
import { useEffect, useState } from 'react'
import { BirdColor, CustomIconEntry } from '../chatroom/page'
import axios from 'axios'
import { GetIconForUser } from '@/util/bd'
interface LeaderboardEntry {
id: number
username: string
value: number
icon: number
overlay: number
birdColor: BirdColor
overlayColor: BirdColor
customIcon: string | null
}
export default function BerryDashLeaderboards () {
const [selected, setSelected] = useState<number>(-1)
const [selectedBerryOption, setSelectedBerryOption] = useState<number>(0)
const [gettingEntries, setGettingEntries] = useState<boolean>(false)
const [entries, setEntries] = useState<LeaderboardEntry[]>([])
const [customIconCache, setCustomIconCache] = useState<CustomIconEntry[]>([])
const Refresh = async () => {
setGettingEntries(true)
try {
const result = await axios.get(
'/api/berrydash/leaderboard/' +
(selected == 0
? 'score'
: selected == 1
? 'berry?berry=' + selectedBerryOption
: selected == 2
? 'coin'
: selected == 3
? 'legacy'
: 'total')
)
if (result.data.success) {
setEntries(result.data.data)
}
} catch {
setEntries([])
}
setGettingEntries(false)
}
useEffect(() => {
document.title = 'Lncvrt Games - Berry Dash Leaderboards'
}, [])
useEffect(() => {
if (selected != -1) Refresh()
}, [selected])
useEffect(() => {
const all = customIconCache.map(icon => icon.id)
const allFromMessages = Array.from(
new Set(
entries
.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
}))
])
}
})()
}
}, [entries])
return (
<div className='box'>
{selected == -1 ? (
<BackButton href='/game/berry-dash' />
) : (
<BackButton
onClick={() => {
if (gettingEntries) return
setEntries([])
setSelectedBerryOption(0)
setSelected(-1)
}}
/>
)}
<DiscordButton />
<p className='px-20 mb-2 -mt-2 text-center text-2xl'>
Berry Dash Leaderboards
</p>
{selected == -1 ? (
<>
<p className='text-center mt-4 text-xl'>Select a Leaderboard</p>
<div className='flex flex-col gap-2 mt-4 items-center justify-center'>
<button onClick={() => setSelected(0)}>Score Leaderboard</button>
<button onClick={() => setSelected(1)}>Berry Leaderboard</button>
<button onClick={() => setSelected(2)}>Coins Leaderboard</button>
<button onClick={() => setSelected(3)}>Legacy Leaderboard</button>
<button onClick={() => setSelected(4)}>
Total Berries Leaderboard
</button>
</div>
</>
) : (
<>
<div className='flex flex-col gap-2'>
{entries.map((item, index) => {
return (
<div
key={item.id}
className='flex justify-between items-center'
>
<div className='flex items-center gap-1'>
<img
src={
!item.customIcon
? `https://games-r2.lncvrt.xyz/game-assets/berrydash/icons/bird_${
item.icon == 1
? GetIconForUser(item.id)
: 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'
}
width={48}
height={48}
/>
<p>
{item.username} (#{index + 1})
</p>
</div>
<p>
{selected != 2 ? 'Berries' : 'Coins'}:{' '}
{item.value.toLocaleString('en-US')}
</p>
</div>
)
})}
</div>
</>
)}
</div>
)
}

View File

@@ -113,6 +113,9 @@ export default function BerryDashGameInfo () {
<Link href='/game/berry-dash/chatroom' draggable={false}>
View & interact with the Chatroom!
</Link>
<Link href='/game/berry-dash/leaderboards' draggable={false}>
View the Leaderboards
</Link>
</div>
<p className='text-2xl my-1'>Downloads</p>
<div className='downloads'>