import { useEffect, useMemo, useRef, useState } from 'react'
import Tree from '../Tree/Tree'
import FallingLeaves from '../Tree/FallingLeaves'
const numberFormat = new Intl.NumberFormat('en-GB')
const dateFormat = new Intl.DateTimeFormat('en-GB', {
dateStyle: 'medium',
timeStyle: 'short',
})
const apiEndpoints = {
health: '/health',
uptime: '/api/uptime',
viewers: '/api/viewers',
viewerEvent: '/api/viewers/event',
viewerDelete: '/api/viewers/delete',
teams: '/api/tss/leaderboard/teams?limit=100',
teamsHealth: '/api/tss/leaderboard/teams?limit=1',
resolve: (name) => `/api/tss/teams/resolve?name=${encodeURIComponent(name)}`,
detail: (name) => `/api/tss/teams/${encodeURIComponent(name)}`,
history: (name) => `/api/tss/teams/${encodeURIComponent(name)}/history`,
games: (name) => `/api/tss/teams/${encodeURIComponent(name)}/games`,
}
const navItems = [
{ path: '/', label: 'Home' },
{ path: '/teams', label: 'Team leaderboard' },
{ path: '/battle-logs', label: 'Battle Logs' },
{ path: '/viewers', label: 'Viewers' },
]
const analyticsConsentKey = 'tssbot.analyticsConsent'
const analyticsPreferencesKey = 'tssbot.analyticsPreferences'
const analyticsPreferencesCookie = 'tssbot_analytics_preferences'
const analyticsVisitorKey = 'tssbot.analyticsVisitor'
const analyticsConsentVersion = 2
const defaultAnalyticsPreferences = {
chosen: false,
analytics: false,
device: false,
display: false,
locale: false,
referrer: false,
version: analyticsConsentVersion,
}
async function fetchJson(path, signal) {
const response = await fetch(path, {
signal,
headers: { Accept: 'application/json' },
})
const body = await response.json().catch(() => null)
if (!response.ok) {
throw new Error(body?.error || `Request failed with ${response.status}`)
}
return body
}
function parseRoute(pathname = window.location.pathname) {
if (pathname === '/') return { page: 'home', teamName: '' }
if (pathname === '/teams') return { page: 'teams', teamName: '' }
if (pathname === '/uptime') return { page: 'uptime', teamName: '' }
if (pathname === '/viewers') return { page: 'viewers', teamName: '' }
if (pathname === '/privacy') return { page: 'privacy', teamName: '' }
if (pathname.startsWith('/teams/')) {
const teamName = decodeURIComponent(pathname.slice('/teams/'.length))
return { page: 'team', teamName }
}
if (pathname === '/battle-logs' || pathname === '/live') return { page: 'battle-logs', teamName: '' }
return { page: 'home', teamName: '' }
}
function teamPath(name) {
return `/teams/${encodeURIComponent(name)}`
}
function formatNumber(value) {
return numberFormat.format(Number(value || 0))
}
function formatDate(timestamp) {
if (!timestamp) return 'Unknown time'
return dateFormat.format(new Date(Number(timestamp) * 1000))
}
function bestTeamName(team) {
return team?.tag_name || team?.short_name || team?.long_name || ''
}
function normalizeAnalyticsPreferences(value) {
if (!value || typeof value !== 'object') return { ...defaultAnalyticsPreferences }
return {
chosen: Boolean(value.chosen) && value.version === analyticsConsentVersion,
analytics: Boolean(value.analytics),
device: Boolean(value.device),
display: Boolean(value.display),
locale: Boolean(value.locale),
referrer: Boolean(value.referrer),
version: analyticsConsentVersion,
}
}
function browserPrivacySignalEnabled() {
return Boolean(
navigator.globalPrivacyControl ||
navigator.doNotTrack === '1' ||
window.doNotTrack === '1',
)
}
function readCookie(name) {
try {
const match = document.cookie
.split('; ')
.find((item) => item.startsWith(`${encodeURIComponent(name)}=`))
return match ? decodeURIComponent(match.split('=').slice(1).join('=')) : ''
} catch {
return ''
}
}
function writeCookie(name, value) {
try {
const sixMonths = 60 * 60 * 24 * 180
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; Max-Age=${sixMonths}; Path=/; SameSite=Lax`
} catch {
// Some browsers block cookies; local storage and in-memory state still work.
}
}
function storedAnalyticsPreferences() {
const cookieValue = readCookie(analyticsPreferencesCookie)
if (cookieValue) {
try {
return normalizeAnalyticsPreferences(JSON.parse(cookieValue))
} catch {
// Fall back through the older storage formats below.
}
}
try {
const stored = window.localStorage.getItem(analyticsPreferencesKey)
if (stored) return normalizeAnalyticsPreferences(JSON.parse(stored))
} catch {
// Fall back through the older consent flag below.
}
try {
const legacyConsent = window.localStorage.getItem(analyticsConsentKey) || ''
if (legacyConsent === 'analytics') {
return {
...defaultAnalyticsPreferences,
chosen: true,
analytics: true,
device: true,
display: true,
locale: true,
referrer: true,
}
}
if (legacyConsent === 'declined') {
return { ...defaultAnalyticsPreferences, chosen: true, analytics: false }
}
} catch {
// Use the default prompt state.
}
return { ...defaultAnalyticsPreferences }
}
function persistAnalyticsPreferences(preferences) {
const normalized = normalizeAnalyticsPreferences(preferences)
const serialized = JSON.stringify(normalized)
writeCookie(analyticsPreferencesCookie, serialized)
try {
window.localStorage.setItem(analyticsPreferencesKey, serialized)
window.localStorage.setItem(
analyticsConsentKey,
normalized.analytics ? 'analytics' : 'declined',
)
} catch {
// Local storage can be blocked; the cookie and in-memory choice still apply.
}
return normalized
}
function stableId(storageKey) {
try {
const existing = window.localStorage.getItem(storageKey)
if (existing) return existing
const id =
window.crypto?.randomUUID?.() ||
`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
window.localStorage.setItem(storageKey, id)
return id
} catch {
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
}
}
function storedVisitorId(storageKey) {
try {
return window.localStorage.getItem(storageKey) || ''
} catch {
return ''
}
}
function forgetVisitorId(storageKey) {
try {
window.localStorage.removeItem(storageKey)
} catch {
// Storage may be unavailable; nothing else to clear locally.
}
}
const analyticsSessionId =
window.crypto?.randomUUID?.() ||
`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
function browserName() {
const ua = navigator.userAgent
if (ua.includes('Edg/')) return 'Microsoft Edge'
if (ua.includes('OPR/')) return 'Opera'
if (ua.includes('Firefox/')) return 'Firefox'
if (ua.includes('Chrome/') && !ua.includes('Chromium/')) return 'Chrome'
if (ua.includes('Safari/') && ua.includes('Version/')) return 'Safari'
return 'Unknown'
}
function operatingSystem() {
const ua = navigator.userAgent
if (ua.includes('Windows NT')) return 'Windows'
if (ua.includes('Android')) return 'Android'
if (/iPhone|iPad|iPod/.test(ua)) return 'iOS'
if (ua.includes('Mac OS X')) return 'macOS'
if (ua.includes('Linux')) return 'Linux'
return 'Unknown'
}
function deviceType() {
const ua = navigator.userAgent
if (/Mobi|Android|iPhone|iPod/.test(ua)) return 'Mobile'
if (/iPad|Tablet/.test(ua)) return 'Tablet'
return 'Desktop'
}
function routeLabel(route) {
if (route.page === 'team' && route.teamName) return `Team: ${route.teamName}`
if (route.page === 'teams') return 'Team leaderboard'
if (route.page === 'battle-logs') return 'Battle Logs'
if (route.page === 'uptime') return 'Uptime'
if (route.page === 'viewers') return 'viewers'
if (route.page === 'privacy') return 'Privacy notice'
return 'Home'
}
async function fetchRecentTssGames(teams, signal) {
const teamNames = teams.map(bestTeamName).filter(Boolean).slice(0, 12)
if (!teamNames.length) {
return { matches: [] }
}
const responses = await Promise.allSettled(
teamNames.map((name) => fetchJson(apiEndpoints.games(name), signal).then((data) => ({ name, data }))),
)
const bySession = new Map()
responses.forEach((result) => {
if (result.status !== 'fulfilled') return
const { name, data } = result.value
;(data.games || []).forEach((game) => {
if (!game.session_id) return
const existing = bySession.get(game.session_id)
const currentTimestamp = Number(game.timestamp || 0)
if (existing && Number(existing.timestamp || 0) >= currentTimestamp) return
bySession.set(game.session_id, {
...game,
team_name: data.tag_name || name,
long_name: data.long_name || '',
})
})
})
return {
matches: Array.from(bySession.values())
.sort((a, b) => Number(b.timestamp || 0) - Number(a.timestamp || 0))
.slice(0, 50),
}
}
function Stat({ label, value }) {
return (
)
}
function App() {
const [route, setRoute] = useState(() => parseRoute())
const [leaderboard, setLeaderboard] = useState({ status: 'idle', data: null, error: null })
const [live, setLive] = useState({ status: 'idle', data: null, error: null })
const [uptime, setUptime] = useState({ status: 'idle', checks: [], history: [], updatedAt: null })
const [viewers, setViewers] = useState({ status: 'idle', data: null, error: null, updatedAt: null })
const [analyticsPreferences, setAnalyticsPreferences] = useState(() => storedAnalyticsPreferences())
const [teamQuery, setTeamQuery] = useState('')
const [searchHint, setSearchHint] = useState({ status: 'idle', name: '' })
const [profile, setProfile] = useState({
teamName: '',
detail: { status: 'idle', data: null, error: null },
history: { status: 'idle', data: null, error: null },
games: { status: 'idle', data: null, error: null },
})
const teams = useMemo(
() => leaderboard.data?.teams || leaderboard.data?.squadrons || [],
[leaderboard.data],
)
const matches = live.data?.matches || []
function navigate(path) {
window.history.pushState({}, '', path)
setRoute(parseRoute(path))
}
useEffect(() => {
const onPopState = () => setRoute(parseRoute())
window.addEventListener('popstate', onPopState)
return () => window.removeEventListener('popstate', onPopState)
}, [])
useEffect(() => {
const title =
route.page === 'team' && route.teamName
? `${route.teamName} | Toothless' TSS Bot`
: route.page === 'teams'
? "Team leaderboard | Toothless' TSS Bot"
: route.page === 'battle-logs'
? "Battle Logs | Toothless' TSS Bot"
: route.page === 'uptime'
? "Uptime | Toothless' TSS Bot"
: route.page === 'viewers'
? "viewers | Toothless' TSS Bot"
: route.page === 'privacy'
? "Privacy notice | Toothless' TSS Bot"
: "Toothless' TSS Bot"
document.title = title
}, [route.page, route.teamName])
useEffect(() => {
if (!analyticsPreferences.analytics) return
const visitorId = stableId(analyticsVisitorKey)
let stopped = false
const deviceDetails = analyticsPreferences.device
const displayDetails = analyticsPreferences.display
const localeDetails = analyticsPreferences.locale
const referrerDetails = analyticsPreferences.referrer
function sendViewerEvent(eventType) {
if (stopped) return
const body = {
consent: 'analytics',
event_type: eventType,
visitor_id: visitorId,
session_id: analyticsSessionId,
page_path: window.location.pathname,
page_title: routeLabel(route),
referrer: referrerDetails ? document.referrer : '',
user_agent: deviceDetails ? navigator.userAgent : 'Not shared',
browser: deviceDetails ? browserName() : 'Not shared',
os: deviceDetails ? operatingSystem() : 'Not shared',
device: deviceDetails ? deviceType() : 'Not shared',
screen: displayDetails ? `${window.screen.width}x${window.screen.height}` : '',
language: localeDetails ? navigator.language : '',
timezone: localeDetails ? Intl.DateTimeFormat().resolvedOptions().timeZone : '',
metadata: displayDetails
? {
color_depth: window.screen.colorDepth,
viewport: `${window.innerWidth}x${window.innerHeight}`,
}
: {},
}
fetch(apiEndpoints.viewerEvent, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(body),
keepalive: true,
}).catch(() => {})
}
sendViewerEvent('page_view')
const timer = window.setInterval(() => sendViewerEvent('heartbeat'), 30000)
const onVisibilityChange = () => {
if (document.visibilityState === 'visible') sendViewerEvent('heartbeat')
}
document.addEventListener('visibilitychange', onVisibilityChange)
return () => {
stopped = true
window.clearInterval(timer)
document.removeEventListener('visibilitychange', onVisibilityChange)
}
}, [analyticsPreferences, route])
useEffect(() => {
const onKeyDown = (event) => {
if (event.key === 'Escape') {
navigate('/')
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
}, [])
useEffect(() => {
const query = teamQuery.trim()
if (query.length < 2) {
setSearchHint({ status: 'idle', name: '' })
return
}
const controller = new AbortController()
const timer = window.setTimeout(() => {
setSearchHint({ status: 'loading', name: '' })
fetchJson(apiEndpoints.resolve(query), controller.signal)
.then((data) => {
const name = data.tag_name || data.short_name || data.long_name || query
setSearchHint({ status: 'ready', name })
})
.catch(() => {
if (!controller.signal.aborted) {
setSearchHint({ status: 'error', name: '' })
}
})
}, 350)
return () => {
window.clearTimeout(timer)
controller.abort()
}
}, [teamQuery])
useEffect(() => {
if (!['home', 'teams', 'team', 'battle-logs'].includes(route.page)) return
if (leaderboard.status === 'ready' || leaderboard.status === 'loading') return
const controller = new AbortController()
setLeaderboard({ status: 'loading', data: null, error: null })
fetchJson(apiEndpoints.teams, controller.signal)
.then((data) => setLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLeaderboard({ status: 'error', data: null, error: error.message })
}
})
return () => controller.abort()
}, [leaderboard.status, route.page])
useEffect(() => {
if (!['home', 'teams', 'team', 'battle-logs'].includes(route.page)) return
const controller = new AbortController()
const timer = window.setInterval(() => {
fetchJson(apiEndpoints.teams, controller.signal)
.then((data) => setLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLeaderboard((current) => ({ ...current, error: error.message }))
}
})
}, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (!['home', 'battle-logs'].includes(route.page)) return
if (!teams.length) return
const controller = new AbortController()
setLive((current) =>
current.status === 'ready' ? current : { status: 'loading', data: null, error: null },
)
fetchRecentTssGames(teams, controller.signal)
.then((data) => setLive({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLive({ status: 'error', data: null, error: error.message })
}
})
return () => controller.abort()
}, [route.page, teams])
useEffect(() => {
if (!['home', 'battle-logs'].includes(route.page)) return
if (!teams.length) return
const controller = new AbortController()
const timer = window.setInterval(() => {
fetchRecentTssGames(teams, controller.signal)
.then((data) => setLive({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLive((current) => ({ ...current, error: error.message }))
}
})
}, 15000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page, teams])
useEffect(() => {
if (route.page !== 'team' || !route.teamName) return
const controller = new AbortController()
setProfile({
teamName: route.teamName,
detail: { status: 'loading', data: null, error: null },
history: { status: 'loading', data: null, error: null },
games: { status: 'loading', data: null, error: null },
})
Promise.allSettled([
fetchJson(apiEndpoints.detail(route.teamName), controller.signal),
fetchJson(apiEndpoints.history(route.teamName), controller.signal),
fetchJson(apiEndpoints.games(route.teamName), controller.signal),
]).then(([detailResult, historyResult, gamesResult]) => {
if (controller.signal.aborted) return
setProfile({
teamName: route.teamName,
detail:
detailResult.status === 'fulfilled'
? { status: 'ready', data: detailResult.value, error: null }
: { status: 'error', data: null, error: detailResult.reason.message },
history:
historyResult.status === 'fulfilled'
? { status: 'ready', data: historyResult.value, error: null }
: { status: 'error', data: null, error: historyResult.reason.message },
games:
gamesResult.status === 'fulfilled'
? { status: 'ready', data: gamesResult.value, error: null }
: { status: 'error', data: null, error: gamesResult.reason.message },
})
})
return () => controller.abort()
}, [route.page, route.teamName])
useEffect(() => {
if (route.page !== 'uptime') return
const controller = new AbortController()
async function loadUptime() {
setUptime((current) => ({
status: current.status === 'ready' ? 'refreshing' : 'loading',
checks: current.checks,
history: current.history,
updatedAt: current.updatedAt,
}))
fetchJson(apiEndpoints.uptime, controller.signal)
.then((data) => {
const latest = data.latest
const checks = latest
? [
{
name: 'Website',
detail: 'App shell and static assets',
ok: latest.website_ok,
label: latest.details?.website?.label || (latest.website_ok ? 'Online' : 'Issue'),
latency: latest.latency_ms,
},
{
name: 'Health endpoint',
detail: apiEndpoints.health,
ok: latest.health_ok,
label: latest.details?.health?.label || (latest.health_ok ? 'Operational' : 'Issue'),
latency: latest.latency_ms,
},
{
name: 'TSS data proxy',
detail: apiEndpoints.teamsHealth,
ok: latest.tss_ok,
label: latest.details?.tss?.label || (latest.tss_ok ? 'Operational' : 'Issue'),
latency: latest.details?.tss?.latency_ms || latest.latency_ms,
},
]
: []
setUptime({
status: 'ready',
checks,
history: (data.history || []).map((sample) => ({
timestamp: new Date(sample.checked_at).getTime(),
onlineChecks: [sample.website_ok, sample.health_ok, sample.tss_ok].filter(Boolean).length,
totalChecks: 3,
ok: sample.ok,
})),
updatedAt: latest ? new Date(latest.checked_at).getTime() : null,
configured: data.configured,
})
})
.catch((error) => {
if (controller.signal.aborted) return
setUptime({
status: 'error',
updatedAt: null,
configured: false,
history: [],
checks: [
{
name: 'Website',
detail: 'App shell and static assets',
ok: true,
label: 'Online',
latency: 0,
},
{
name: 'Health endpoint',
detail: apiEndpoints.health,
ok: false,
label: error.message,
latency: 0,
},
{
name: 'TSS data proxy',
detail: apiEndpoints.teamsHealth,
ok: false,
label: 'Uptime history unavailable',
latency: 0,
},
],
})
})
}
loadUptime()
const timer = window.setInterval(loadUptime, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (route.page !== 'viewers') return
const controller = new AbortController()
function loadViewers() {
setViewers((current) => ({
status: current.status === 'ready' ? 'refreshing' : 'loading',
data: current.data,
error: null,
updatedAt: current.updatedAt,
}))
fetchJson(apiEndpoints.viewers, controller.signal)
.then((data) => {
setViewers({
status: 'ready',
data,
error: null,
updatedAt: Date.now(),
})
})
.catch((error) => {
if (!controller.signal.aborted) {
setViewers((current) => ({ ...current, status: 'error', error: error.message }))
}
})
}
loadViewers()
const timer = window.setInterval(loadViewers, 5000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (route.page !== 'team' || !route.teamName) return
const controller = new AbortController()
const timer = window.setInterval(() => {
Promise.allSettled([
fetchJson(apiEndpoints.detail(route.teamName), controller.signal),
fetchJson(apiEndpoints.history(route.teamName), controller.signal),
fetchJson(apiEndpoints.games(route.teamName), controller.signal),
]).then(([detailResult, historyResult, gamesResult]) => {
if (controller.signal.aborted) return
setProfile((current) => ({
teamName: route.teamName,
detail:
detailResult.status === 'fulfilled'
? { status: 'ready', data: detailResult.value, error: null }
: current.detail,
history:
historyResult.status === 'fulfilled'
? { status: 'ready', data: historyResult.value, error: null }
: current.history,
games:
gamesResult.status === 'fulfilled'
? { status: 'ready', data: gamesResult.value, error: null }
: current.games,
}))
})
}, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page, route.teamName])
const topTeamName = bestTeamName(teams[0])
const searchPlaceholder =
searchHint.status === 'ready' ? `Found ${searchHint.name}` : topTeamName || 'Search teams'
async function handleTeamSearch(event) {
event.preventDefault()
const name = teamQuery.trim()
if (!name) return
try {
const resolved = await fetchJson(apiEndpoints.resolve(name))
navigate(teamPath(resolved.tag_name || resolved.short_name || resolved.long_name || name))
} catch {
navigate(teamPath(name))
}
}
function chooseAnalyticsConsent(preferences) {
const previousVisitorId = storedVisitorId(analyticsVisitorKey)
const nextPreferences = persistAnalyticsPreferences({ ...preferences, chosen: true })
if (!nextPreferences.analytics) {
forgetVisitorId(analyticsVisitorKey)
if (previousVisitorId) {
fetch(apiEndpoints.viewerDelete, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
visitor_id: previousVisitorId,
session_id: analyticsSessionId,
}),
keepalive: true,
}).catch(() => {})
}
}
setAnalyticsPreferences(nextPreferences)
}
const activeNavPath =
route.page === 'team'
? '/teams'
: route.page === 'battle-logs'
? '/battle-logs'
: route.page === 'viewers'
? '/viewers'
: window.location.pathname
return (
navigate('/')} type="button">
Toothless' TSS Bot
{navItems.map((item) => (
navigate(item.path)}
type="button"
>
{item.label}
))}
SRE
{route.page === 'home' ? (
) : null}
{route.page === 'teams' ? (
) : null}
{route.page === 'team' ? (
) : null}
{route.page === 'battle-logs' ? : null}
{route.page === 'uptime' ? : null}
{route.page === 'viewers' ? : null}
{route.page === 'privacy' ? : null}
)
}
function Footer({ navigate }) {
return (
)
}
function ConsentBanner({ preferences, onChoose }) {
const [isOpen, setIsOpen] = useState(() => !preferences.chosen)
const [isConfiguring, setIsConfiguring] = useState(() => Boolean(preferences.chosen))
const [draft, setDraft] = useState(() => normalizeAnalyticsPreferences(preferences))
const privacySignalEnabled = browserPrivacySignalEnabled()
useEffect(() => {
setDraft(normalizeAnalyticsPreferences(preferences))
if (!preferences.chosen) {
setIsOpen(true)
setIsConfiguring(false)
}
}, [preferences])
useEffect(() => {
if (!isOpen) return undefined
const previousOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = previousOverflow
}
}, [isOpen])
function updateDraft(key, value) {
setDraft((current) => ({ ...current, [key]: value }))
}
function savePreferences(nextPreferences) {
onChoose(normalizeAnalyticsPreferences(nextPreferences))
setIsOpen(false)
}
if (!isOpen) {
return (
{
setIsConfiguring(true)
setIsOpen(true)
}}
type="button"
>
Cookie settings
)
}
return (
Cookie and analytics settings
We use a necessary cookie to remember these choices. You can also allow
analytics for the public viewers page and choose which details are included.
{privacySignalEnabled ? (
Your browser is signalling a privacy preference, so optional analytics stay
off unless you explicitly allow them.
) : null}
{isConfiguring ? (
<>
updateDraft('analytics', value)}
/>
updateDraft('device', value)}
/>
updateDraft('display', value)}
/>
updateDraft('locale', value)}
/>
updateDraft('referrer', value)}
/>
savePreferences({ ...defaultAnalyticsPreferences, chosen: true })}
type="button"
>
Decline all
savePreferences(draft)}
type="button"
>
Save choices
savePreferences({
chosen: true,
analytics: true,
device: true,
display: true,
locale: true,
referrer: true,
version: analyticsConsentVersion,
})
}
type="button"
>
Allow all
>
) : (
setIsConfiguring(true)}
type="button"
>
Configure
savePreferences({
chosen: true,
analytics: true,
device: true,
display: true,
locale: true,
referrer: true,
version: analyticsConsentVersion,
})
}
type="button"
>
Allow all
)}
)
}
function PreferenceToggle({ checked, description, disabled = false, label, onChange }) {
return (
onChange?.(event.target.checked)}
type="checkbox"
/>
{label}
{description}
)
}
function PrivacyPage() {
return (
Privacy notice
How we handle your data
Controller: Heidi
Contact through Discord: clippiii
Necessary cookies store your cookie and analytics choices. If you opt in to
viewer analytics, we collect page views, live viewing status, a pseudonymous
visitor ID, and a session ID. Optional settings control whether browser/device,
screen, language/timezone, and referrer details are included.
Necessary cookies are used to remember your privacy choices. Optional viewer
analytics are used only to power the public viewers page, understand basic site
activity, and keep abuse low.
Necessary cookies are used because they are needed to remember your consent
preferences. Optional analytics only run with your consent, and you can withdraw
that consent from the Cookie settings button at any time.
Analytics events are automatically deleted after the configured retention period,
currently documented as 30 days by default. Active viewer sessions expire after a
short inactivity window. If you decline analytics after previously allowing them,
the site asks the server to delete records linked to your current visitor and
session IDs, and removes the local visitor ID from your browser.
Viewer analytics are stored by this site and are not sold. Public viewer pages show
only consented analytics fields and never show raw IP addresses. Hosting providers
may process server logs as part of running the site.
You can ask for access, correction, deletion, restriction, objection, portability,
or withdrawal of consent by contacting Heidi on Discord at clippiii. If you are in
the UK, you can also complain to the ICO. If you are in the EU or EEA, you can
complain to your local data protection authority.
)
}
function PrivacySection({ children, title }) {
return (
{title}
{children}
)
}
function Landing({ live, matches, navigate }) {
const treeRef = useRef(null)
return (
BorisBot got nothin on THIS
Toothless' TSS Bot
Powered by Spectra. TSS analytics.
navigate('/teams')}
type="button"
>
Team leaderboard
navigate('/battle-logs')}
type="button"
>
Battle Logs
)
}
function RecentGamesSection({ live, matches, navigate }) {
const recentMatches = matches.slice(0, 6)
return (
Recent activity
Latest games
navigate('/battle-logs')}
type="button"
>
View Battle Logs
{recentMatches.map((match) => (
{match.map_name || 'Unknown map'}
{formatDate(match.timestamp)}
{match.result || 'Unknown'}
{match.team_name || 'TSS team'}
{formatNumber(match.player_count)} players
{formatNumber(match.stats?.ground_kills)} ground
{formatNumber(match.stats?.air_kills)} air
{formatNumber(match.stats?.deaths)} deaths
))}
{!recentMatches.length ? (
{live.status === 'loading' ? 'Loading latest games' : live.error || 'No games returned'}
) : null}
)
}
function PixelMountains() {
const canvasRef = useRef(null)
useEffect(() => {
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
const WORLD_W = 1920
const WORLD_H = 900
function interpolate(points, x) {
for (let i = 0; i < points.length - 1; i++) {
const [x0, y0] = points[i]
const [x1, y1] = points[i + 1]
if (x >= x0 && x <= x1) {
const t = (x - x0) / Math.max(1, x1 - x0)
return y0 + (y1 - y0) * t
}
}
return points.at(-1)[1]
}
function drawMountain(points, color, jitter = 0) {
const width = WORLD_W
const height = WORLD_H
ctx.fillStyle = color
for (let x = 0; x < width; x++) {
const wave = jitter
? Math.sin(x * 0.08) * jitter + Math.sin(x * 0.021) * jitter * 1.8
: 0
const y = Math.round(interpolate(points, x) + wave)
ctx.fillRect(x, y, 1, height - y)
}
}
function draw() {
const width = WORLD_W
const height = WORLD_H
canvas.width = WORLD_W
canvas.height = WORLD_H
ctx.imageSmoothingEnabled = false
ctx.clearRect(0, 0, WORLD_W, WORLD_H)
drawMountain(
[
[0, height * 0.82],
[width * 0.12, height * 0.73],
[width * 0.26, height * 0.66],
[width * 0.39, height * 0.76],
[width * 0.55, height * 0.58],
[width * 0.64, height * 0.46],
[width * 0.73, height * 0.62],
[width * 0.86, height * 0.56],
[width, height * 0.64],
],
'#fcfbcf',
1.1,
)
drawMountain(
[
[0, height * 0.86],
[width * 0.15, height * 0.78],
[width * 0.31, height * 0.81],
[width * 0.43, height * 0.69],
[width * 0.52, height * 0.76],
[width * 0.62, height * 0.43],
[width * 0.69, height * 0.31],
[width * 0.78, height * 0.48],
[width, height * 0.5],
],
'#fff2e6',
1.6,
)
drawMountain(
[
[0, height * 0.94],
[width * 0.17, height * 0.9],
[width * 0.32, height * 0.92],
[width * 0.47, height * 0.83],
[width * 0.58, height * 0.89],
[width * 0.66, height * 0.61],
[width * 0.71, height * 0.5],
[width * 0.84, height * 0.7],
[width, height * 0.68],
],
'#fee5cd',
2.1,
)
drawMountain(
[
[0, height * 0.98],
[width * 0.17, height * 0.96],
[width * 0.34, height * 0.99],
[width * 0.48, height * 0.93],
[width * 0.62, height * 0.97],
[width * 0.72, height * 0.88],
[width * 0.86, height * 0.95],
[width, height * 0.93],
],
'#fdca9b',
1.4,
)
}
draw()
}, [])
return
}
function TeamsPage({ leaderboard, navigate, teams }) {
return (
Team leaderboard
{leaderboard.status === 'loading'
? 'Loading leaderboard'
: leaderboard.error || `${teams.length} teams returned`}
{teams.map((team, index) => {
const name = bestTeamName(team)
return (
navigate(teamPath(name))}
type="button"
>
#{index + 1}
{name}
{team.long_name || team.short_name || 'Unresolved'}
{formatNumber(team.player_count)} players
{formatNumber(team.total_battles)} battles
{Number(team.win_rate || 0).toFixed(1)}% WR
{formatNumber(team.points?.total_points || team.total_kills)}
)
})}
{!teams.length ? (
{leaderboard.status === 'loading' ? 'Loading leaderboard' : 'No teams returned'}
) : null}
)
}
function TeamProfilePage({ navigate, profile, requestedTeam, teams }) {
const detail = profile.detail.data
const summary = detail?.team_summary || detail?.squadron_summary
const players = detail?.players || []
const games = profile.games.data?.games || []
const history = profile.history.data?.history || []
const ratingHourly = profile.history.data?.rating_hourly || []
const latestRating = ratingHourly.at(-1)?.rating || summary?.points?.total_points
const leaderboardTeam = teams.find((team) => bestTeamName(team) === requestedTeam)
const displayName = detail?.tag_name || bestTeamName(leaderboardTeam) || requestedTeam
const longName = detail?.long_name || leaderboardTeam?.long_name || ''
return (
navigate('/teams')}
type="button"
>
Back to leaderboard
Team profile
{displayName}
{profile.detail.error || longName || profile.detail.status}
Rating {formatNumber(latestRating)}
Clan {detail?.clan_id || leaderboardTeam?.clan_id || 'n/a'}
{detail?.data_set || 'tss'}
)
}
function RosterTable({ players, status }) {
const sortedPlayers = [...players].sort((a, b) => {
return (b.total_kills || 0) - (a.total_kills || 0) || String(a.nick || '').localeCompare(b.nick || '')
})
return (
Roster
{formatNumber(sortedPlayers.length)} players
{sortedPlayers.map((player) => (
{player.nick || player.uid}
{player.uid} · {formatNumber(player.points || player.sqb_points)} pts
{formatNumber(player.total_battles)} battles
{formatNumber(player.total_kills)} kills
{Number(player.win_rate || 0).toFixed(1)}% WR
{Number(player.kdr || 0).toFixed(1)} KDR
{formatNumber(player.assists)} assists
))}
{!sortedPlayers.length ? (
{status === 'loading' ? 'Loading roster' : 'No roster rows returned'}
) : null}
)
}
function RatingPanel({ history, ratingHourly, status }) {
const recentHistory = history.slice(-8)
const firstRating = ratingHourly[0]?.rating || 0
const latestRating = ratingHourly.at(-1)?.rating || 0
const ratingChange = latestRating - firstRating
return (
History
{ratingHourly.length ? `${formatNumber(ratingHourly.length)} rating snapshots` : status}
= 0 ? '+' : ''}${formatNumber(ratingChange)}`}
/>
{recentHistory.map((item) => (
{item.period}
{formatNumber(item.battles)} battles
{Number(item.win_rate || 0).toFixed(1)}% WR
))}
{!recentHistory.length ? (
{status === 'loading' ? 'Loading history' : 'No history rows returned'}
) : null}
)
}
function BattleResults({ games, status }) {
return (
Battle results
{formatNumber(games.length)} battles returned
{games.map((game) => (
{game.map_name || 'Unknown map'}
{formatDate(game.timestamp)} · {game.session_id}
{game.result || 'Unknown'}
{formatNumber(game.player_count)} players
{formatNumber(game.stats?.ground_kills)} ground
{formatNumber(game.stats?.air_kills)} air
{formatNumber(game.stats?.assists)} assists
{formatNumber(game.stats?.deaths)} deaths
))}
{!games.length ? (
{status === 'loading' ? 'Loading battle results' : 'No battle results returned'}
) : null}
)
}
function BattleLogsPage({ live, matches }) {
return (
Battle Logs
{live.status === 'loading' ? 'Loading battles' : live.error || `${matches.length} battles returned`}
{matches.map((match) => (
{match.map_name || 'Unknown map'}
{formatDate(match.timestamp)} · {match.session_id}
{match.team_name || 'TSS team'}
{match.long_name || 'TSS battle record'}
{match.result || 'Unknown'}
{formatNumber(match.player_count)} players
{formatNumber(match.stats?.ground_kills)} ground
{formatNumber(match.stats?.air_kills)} air
{formatNumber(match.stats?.deaths)} deaths
))}
{!matches.length ? (
{live.status === 'loading' ? 'Loading battles' : 'No battles returned'}
) : null}
)
}
function relativeSeconds(timestamp) {
if (!timestamp) return 'unknown'
const seconds = Math.max(0, Math.round((Date.now() - new Date(timestamp).getTime()) / 1000))
if (seconds < 60) return `${seconds}s ago`
return `${Math.round(seconds / 60)}m ago`
}
function ViewersPage({ viewers }) {
const data = viewers.data || {}
const active = data.active || []
const topPages = data.top_pages || []
const clients = data.clients || []
const totals = data.totals || {}
const generatedAt = data.generated_at ? dateFormat.format(new Date(data.generated_at)) : 'Waiting for data'
return (
Public analytics
viewers
Live consented browser sessions and page activity. Last refreshed {generatedAt}.
{viewers.status === 'loading' ? 'Loading' : `${formatNumber(totals.active_now)} active now`}
Currently viewing
Heartbeats within {formatNumber(data.active_window_seconds || 75)} seconds
{active.map((viewer) => (
{viewer.page_title || viewer.page_path}
{viewer.page_path}
{viewer.browser} on {viewer.os}
{viewer.device} · {viewer.screen || 'unknown screen'} · {viewer.timezone || 'unknown timezone'}
{viewer.language || 'unknown language'}
Seen {relativeSeconds(viewer.last_seen_at)}
#{viewer.session}
))}
{!active.length ? (
{viewers.error || 'No consented viewers are active right now'}
) : null}
Top pages
Page views over the last 24 hours
{topPages.map((page) => (
{page.page_title || page.page_path}
{page.page_path}
{formatNumber(page.views)}
))}
{!topPages.length ?
No page views recorded yet
: null}
Clients
Browsers, devices, and operating systems seen in 24 hours
{clients.map((client) => (
{client.browser} on {client.os}
{client.device}
{formatNumber(client.events)}
))}
{!clients.length ?
No client data recorded yet
: null}
Analytics are opt-in, retained for {formatNumber(data.privacy?.retention_days || 30)} days,
and public output excludes raw IP addresses.
)
}
function UptimePage({ uptime }) {
const checks = uptime.checks
const history = uptime.history
const operationalCount = checks.filter((check) => check.ok).length
const allOperational = checks.length > 0 && operationalCount === checks.length
const updatedAt = uptime.updatedAt ? dateFormat.format(new Date(uptime.updatedAt)) : 'Not checked yet'
const onlineSamples = history.filter((sample) => sample.ok).length
const availability = history.length ? (onlineSamples / history.length) * 100 : 0
return (
Website uptime
{allOperational ? 'All systems operational' : 'Status check'}
Last server snapshot {updatedAt}. The page refreshes once a minute.
{uptime.status === 'loading' ? 'Checking' : `${operationalCount}/${checks.length || 3} online`}
Availability timeline
Last {formatNumber(history.length)} persisted server snapshots
{history.length ? `${availability.toFixed(1)}% uptime` : 'Waiting for first sample'}
{history.map((sample) => {
const height = `${Math.max(12, (sample.onlineChecks / sample.totalChecks) * 100)}%`
return (
)
})}
{!history.length ? (
Checking website status
) : null}
Uptime
Downtime or degraded
{checks.map((check) => (
{check.name}
{check.detail}
{check.ok ? 'Online' : 'Issue'}
{check.label}
{formatNumber(check.latency)} ms response window
))}
{!checks.length ? (
Checking website status
) : null}
)
}
export default App