More leaderboard types

This commit is contained in:
2025-08-29 12:49:35 -07:00
parent 064c8a3336
commit d7d5d281ae
9 changed files with 239 additions and 9 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,11 +1,11 @@
@import "tailwindcss"; @import "tailwindcss";
.leaderboard-container { .leaderboard-container {
@apply flex justify-center; @apply flex items-end justify-center gap-3;
} }
.leaderboard-scroll { .leaderboard-scroll {
@apply h-[510px] bg-[#161616] border border-[#242424] rounded-lg overflow-y-auto w-[475px]; @apply h-[510px] bg-[#161616] border border-[#242424] rounded-lg overflow-y-auto w-[475px] relative;
} }
.leaderboard-entry { .leaderboard-entry {
@@ -15,3 +15,39 @@
.leaderboard-entry p.score { .leaderboard-entry p.score {
@apply font-mono text-blue-500 text-lg; @apply font-mono text-blue-500 text-lg;
} }
.side-dropdown {
@apply flex items-end min-w-[52px];
}
.dropdown-root {
@apply relative w-max;
}
.dropdown-btn {
@apply px-3 py-2 rounded-md bg-[#242424] disabled:bg-[#161616] border border-[#484848] disabled:border-[#383838] text-gray-200 hover:bg-[#323232] hover:border-[#565656] transition-colors cursor-pointer;
}
.dropdown-menu {
@apply absolute bottom-full mb-2 w-45 bg-[#242424] border border-[#484848] rounded-md shadow-lg hidden z-50;
}
.dropdown-left .dropdown-menu {
@apply left-0;
}
.dropdown-right .dropdown-menu {
@apply right-0 w-40;
}
.dropdown-menu.open {
@apply block;
}
.dropdown-item {
@apply block w-full text-left px-4 py-2 hover:bg-[#323232] text-gray-200 cursor-pointer;
}
.dropdown-item.selected {
@apply bg-[#323232] hover:bg-[#484848];
}

View File

@@ -1,28 +1,57 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
import './Leaderboards.css' import './Leaderboards.css'
import axios from 'axios' import axios from 'axios'
import { app } from '@tauri-apps/api' import { app } from '@tauri-apps/api'
import { platform } from '@tauri-apps/plugin-os' import { platform } from '@tauri-apps/plugin-os'
import { decrypt } from '../util/Encryption' import { decrypt, encrypt } from '../util/Encryption'
import { invoke } from '@tauri-apps/api/core' import { invoke } from '@tauri-apps/api/core'
import Image from 'next/image' import Image from 'next/image'
import { LeaderboardResponse } from '../types/LeaderboardResponse' import { LeaderboardResponse } from '../types/LeaderboardResponse'
import { faChevronDown } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { getKey } from '../util/KeysHelper'
import Berry from '../assets/berries/Berry.png'
import PoisonBerry from '../assets/berries/PoisonBerry.png'
import SlowBerry from '../assets/berries/SlowBerry.png'
import UltraBerry from '../assets/berries/UltraBerry.png'
import SpeedyBerry from '../assets/berries/SpeedyBerry.png'
import CoinBerry from '../assets/berries/CoinBerry.png'
export default function Leaderboards () { export default function Leaderboards () {
const [leaderboardData, setLeaderboardData] = const [leaderboardData, setLeaderboardData] =
useState<LeaderboardResponse | null>(null) useState<LeaderboardResponse | null>(null)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [leftOpen, setLeftOpen] = useState(false)
const [rightOpen, setRightOpen] = useState(false)
const leftRef = useRef<HTMLDivElement | null>(null)
const rightRef = useRef<HTMLDivElement | null>(null)
const formatter = new Intl.NumberFormat('en-US') const formatter = new Intl.NumberFormat('en-US')
const [leaderboardType, setLeaderboardType] = useState<number>(0)
const [berryType, setBerryType] = useState<number>(0)
async function refresh () { const refresh = useCallback(async () => {
setLoading(true) setLoading(true)
setLeaderboardData(null) setLeaderboardData(null)
try { try {
const launcherVersion = await app.getVersion() const launcherVersion = await app.getVersion()
const response = await axios.get( const sendKey = await getKey(1)
const formData = new URLSearchParams()
formData.append(
await encrypt('type', sendKey),
await encrypt(leaderboardType.toString(), sendKey)
)
if (leaderboardType == 1) {
formData.append(
await encrypt('showType', sendKey),
await encrypt(berryType.toString(), sendKey)
)
}
const response = await axios.post(
'https://berrydash.lncvrt.xyz/database/getTopPlayers.php', 'https://berrydash.lncvrt.xyz/database/getTopPlayers.php',
formData,
{ {
headers: { headers: {
Requester: 'BerryDashLauncher', Requester: 'BerryDashLauncher',
@@ -38,7 +67,7 @@ export default function Leaderboards () {
} finally { } finally {
setLoading(false) setLoading(false)
} }
} }, [leaderboardType, berryType])
function downloadLeaderboard () { function downloadLeaderboard () {
let content = '"Username","Score","ScoreFormatted"\n' let content = '"Username","Score","ScoreFormatted"\n'
@@ -55,6 +84,16 @@ export default function Leaderboards () {
useEffect(() => { useEffect(() => {
refresh() refresh()
}, [refresh])
useEffect(() => {
function onDocClick (e: MouseEvent) {
const t = e.target as Node
if (leftRef.current && !leftRef.current.contains(t)) setLeftOpen(false)
if (rightRef.current && !rightRef.current.contains(t)) setRightOpen(false)
}
document.addEventListener('mousedown', onDocClick)
return () => document.removeEventListener('mousedown', onDocClick)
}, []) }, [])
return ( return (
@@ -65,7 +104,7 @@ export default function Leaderboards () {
<button <button
className='button text-3xl' className='button text-3xl'
onClick={downloadLeaderboard} onClick={downloadLeaderboard}
disabled={loading || leaderboardData?.entries.length === 0} disabled={loading || leaderboardData?.entries?.length === 0}
> >
Download Leaderboards Download Leaderboards
</button> </button>
@@ -78,9 +117,73 @@ export default function Leaderboards () {
</button> </button>
</div> </div>
</div> </div>
<div className='leaderboard-container'> <div className='leaderboard-container'>
<div className='side-dropdown'>
<div ref={leftRef} className='dropdown-root dropdown-left'>
<button
className='dropdown-btn'
onClick={() => setLeftOpen(v => !v)}
aria-expanded={leftOpen}
disabled={loading}
>
Type{' '}
<FontAwesomeIcon
icon={faChevronDown}
className={leftOpen ? 'rotate-180' : ''}
/>
</button>
<div className={`dropdown-menu ${leftOpen ? 'open' : ''}`}>
<button
className={`dropdown-item ${
leaderboardType == 0 ? 'selected' : ''
}`}
onClick={() => {
setLeftOpen(false)
setLeaderboardType(0)
}}
>
Score Leaderboard
</button>
<button
className={`dropdown-item ${
leaderboardType == 1 ? 'selected' : ''
}`}
onClick={() => {
setLeftOpen(false)
setLeaderboardType(1)
}}
>
Berry Leaderboard
</button>
<button
className={`dropdown-item ${
leaderboardType == 2 ? 'selected' : ''
}`}
onClick={() => {
setLeftOpen(false)
setLeaderboardType(2)
}}
>
Coins Leaderboard
</button>
<button
className={`dropdown-item ${
leaderboardType == 3 ? 'selected' : ''
}`}
onClick={() => {
setLeftOpen(false)
setLeaderboardType(3)
}}
>
Legacy Leaderboard
</button>
</div>
</div>
</div>
<div className='leaderboard-scroll'> <div className='leaderboard-scroll'>
{leaderboardData?.entries.length ? ( {leaderboardData?.entries?.length ? (
leaderboardData.entries.map((entry, i) => ( leaderboardData.entries.map((entry, i) => (
<div key={i} className='leaderboard-entry justify-between'> <div key={i} className='leaderboard-entry justify-between'>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
@@ -124,6 +227,97 @@ export default function Leaderboards () {
</div> </div>
)} )}
</div> </div>
<div className='side-dropdown'>
<div ref={rightRef} className='dropdown-root dropdown-right'>
<button
className='dropdown-btn'
onClick={() => setRightOpen(v => !v)}
aria-expanded={rightOpen}
disabled={loading || leaderboardType != 1}
>
Berry Type{' '}
<FontAwesomeIcon
icon={faChevronDown}
className={rightOpen ? 'rotate-180' : ''}
/>
</button>
<div className={`dropdown-menu ${rightOpen ? 'open' : ''}`}>
<button
className={`dropdown-item ${berryType == 0 ? 'selected' : ''}`}
onClick={() => {
setRightOpen(false)
setBerryType(0)
}}
>
<span className='flex items-center gap-2'>
<Image src={Berry} width={24} height={24} alt='' />
Normal Berry
</span>
</button>
<button
className={`dropdown-item ${berryType == 1 ? 'selected' : ''}`}
onClick={() => {
setRightOpen(false)
setBerryType(1)
}}
>
<span className='flex items-center gap-2'>
<Image src={PoisonBerry} width={24} height={24} alt='' />
Poison Berry
</span>
</button>
<button
className={`dropdown-item ${berryType == 2 ? 'selected' : ''}`}
onClick={() => {
setRightOpen(false)
setBerryType(2)
}}
>
<span className='flex items-center gap-2'>
<Image src={SlowBerry} width={24} height={24} alt='' />
Slow Berry
</span>
</button>
<button
className={`dropdown-item ${berryType == 3 ? 'selected' : ''}`}
onClick={() => {
setRightOpen(false)
setBerryType(3)
}}
>
<span className='flex items-center gap-2'>
<Image src={UltraBerry} width={24} height={24} alt='' />
Ultra Berry
</span>
</button>
<button
className={`dropdown-item ${berryType == 4 ? 'selected' : ''}`}
onClick={() => {
setRightOpen(false)
setBerryType(4)
}}
>
<span className='flex items-center gap-2'>
<Image src={SpeedyBerry} width={24} height={24} alt='' />
Speedy Berry
</span>
</button>
<button
className={`dropdown-item ${berryType == 5 ? 'selected' : ''}`}
onClick={() => {
setRightOpen(false)
setBerryType(5)
}}
>
<span className='flex items-center gap-2'>
<Image src={CoinBerry} width={24} height={24} alt='' />
Coin Berry
</span>
</button>
</div>
</div>
</div>
</div> </div>
</div> </div>
) )