fix
This commit is contained in:
+25
-5
@@ -192,12 +192,12 @@ const LEAF_COLORS = ['', '#ed5145', '#fdb068', '#fee5cd'];
|
||||
|
||||
export const TRUNK_TOP_CSS = (H - 150) + 10;
|
||||
|
||||
const Tree = forwardRef<HTMLCanvasElement>(function Tree(_, ref) {
|
||||
const internalRef = useRef<HTMLCanvasElement>(null);
|
||||
const canvasRef = (ref as React.RefObject<HTMLCanvasElement>) ?? internalRef;
|
||||
let cachedTreeCanvas: HTMLCanvasElement | null = null;
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current!;
|
||||
function renderTreeCanvas() {
|
||||
if (cachedTreeCanvas) return cachedTreeCanvas;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
canvas.width = W * PX;
|
||||
canvas.height = H * PX;
|
||||
@@ -258,6 +258,26 @@ const Tree = forwardRef<HTMLCanvasElement>(function Tree(_, ref) {
|
||||
ctx.fillRect((x + 1) * PX, (surfaceY - 1) * PX, PX, PX);
|
||||
}
|
||||
}
|
||||
|
||||
cachedTreeCanvas = canvas;
|
||||
return canvas;
|
||||
}
|
||||
|
||||
export function prewarmTreeCanvas() {
|
||||
renderTreeCanvas();
|
||||
}
|
||||
|
||||
const Tree = forwardRef<HTMLCanvasElement>(function Tree(_, ref) {
|
||||
const internalRef = useRef<HTMLCanvasElement>(null);
|
||||
const canvasRef = (ref as React.RefObject<HTMLCanvasElement>) ?? internalRef;
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current!;
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
canvas.width = W * PX;
|
||||
canvas.height = H * PX;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(renderTreeCanvas(), 0, 0);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
+52
-11
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import L from 'leaflet'
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
import Tree from '../Tree/Tree'
|
||||
import Tree, { prewarmTreeCanvas } from '../Tree/Tree'
|
||||
import FallingLeaves from '../Tree/FallingLeaves'
|
||||
|
||||
const numberFormat = new Intl.NumberFormat('en-GB')
|
||||
@@ -36,6 +36,7 @@ const analyticsPreferencesKey = 'tssbot.analyticsPreferences'
|
||||
const analyticsPreferencesCookie = 'tssbot_analytics_preferences'
|
||||
const analyticsVisitorKey = 'tssbot.analyticsVisitor'
|
||||
const analyticsConsentVersion = 3
|
||||
const liveRefreshMs = 15000
|
||||
|
||||
const turnstileSiteKey = import.meta.env.VITE_TURNSTILE_SITE_KEY || ''
|
||||
|
||||
@@ -518,7 +519,7 @@ function App() {
|
||||
function AppContent() {
|
||||
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 [live, setLive] = useState({ status: 'idle', data: null, error: null, updatedAt: 0 })
|
||||
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())
|
||||
@@ -536,6 +537,11 @@ function AppContent() {
|
||||
[leaderboard.data],
|
||||
)
|
||||
const matches = live.data?.matches || []
|
||||
const liveRef = useRef(live)
|
||||
|
||||
useEffect(() => {
|
||||
liveRef.current = live
|
||||
}, [live])
|
||||
|
||||
function navigate(path) {
|
||||
window.history.pushState({}, '', path)
|
||||
@@ -555,6 +561,20 @@ function AppContent() {
|
||||
return () => window.removeEventListener('scroll', onScroll)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (route.page === 'home') return
|
||||
|
||||
const prewarm = () => {
|
||||
renderPixelMountainsCanvas()
|
||||
prewarmTreeCanvas()
|
||||
}
|
||||
const requestIdle = window.requestIdleCallback || ((callback) => window.setTimeout(callback, 250))
|
||||
const cancelIdle = window.cancelIdleCallback || window.clearTimeout
|
||||
const id = requestIdle(prewarm)
|
||||
|
||||
return () => cancelIdle(id)
|
||||
}, [route.page])
|
||||
|
||||
useEffect(() => {
|
||||
const title =
|
||||
route.page === 'team' && route.teamName
|
||||
@@ -747,17 +767,21 @@ function AppContent() {
|
||||
useEffect(() => {
|
||||
if (!['home', 'battle-logs'].includes(route.page)) return
|
||||
if (!teams.length) return
|
||||
const currentLive = liveRef.current
|
||||
if (currentLive.status === 'ready' && Date.now() - currentLive.updatedAt < liveRefreshMs) return
|
||||
|
||||
const controller = new AbortController()
|
||||
setLive((current) =>
|
||||
current.status === 'ready' ? current : { status: 'loading', data: null, error: null },
|
||||
current.status === 'ready'
|
||||
? current
|
||||
: { status: 'loading', data: null, error: null, updatedAt: current.updatedAt || 0 },
|
||||
)
|
||||
|
||||
fetchRecentTssGames(teams, controller.signal)
|
||||
.then((data) => setLive({ status: 'ready', data, error: null }))
|
||||
.then((data) => setLive({ status: 'ready', data, error: null, updatedAt: Date.now() }))
|
||||
.catch((error) => {
|
||||
if (!controller.signal.aborted) {
|
||||
setLive({ status: 'error', data: null, error: error.message })
|
||||
setLive({ status: 'error', data: null, error: error.message, updatedAt: Date.now() })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -771,13 +795,13 @@ function AppContent() {
|
||||
const controller = new AbortController()
|
||||
const timer = window.setInterval(() => {
|
||||
fetchRecentTssGames(teams, controller.signal)
|
||||
.then((data) => setLive({ status: 'ready', data, error: null }))
|
||||
.then((data) => setLive({ status: 'ready', data, error: null, updatedAt: Date.now() }))
|
||||
.catch((error) => {
|
||||
if (!controller.signal.aborted) {
|
||||
setLive((current) => ({ ...current, error: error.message }))
|
||||
}
|
||||
})
|
||||
}, 15000)
|
||||
}, liveRefreshMs)
|
||||
|
||||
return () => {
|
||||
window.clearInterval(timer)
|
||||
@@ -1771,11 +1795,12 @@ function RecentGamesSection({ live, matches, navigate }) {
|
||||
)
|
||||
}
|
||||
|
||||
function PixelMountains() {
|
||||
const canvasRef = useRef(null)
|
||||
let cachedPixelMountainsCanvas = null
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current
|
||||
function renderPixelMountainsCanvas() {
|
||||
if (cachedPixelMountainsCanvas) return cachedPixelMountainsCanvas
|
||||
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
const WORLD_W = 1920
|
||||
const WORLD_H = 900
|
||||
@@ -1881,6 +1906,22 @@ function PixelMountains() {
|
||||
}
|
||||
|
||||
draw()
|
||||
cachedPixelMountainsCanvas = canvas
|
||||
return canvas
|
||||
}
|
||||
|
||||
function PixelMountains() {
|
||||
const canvasRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current
|
||||
const source = renderPixelMountainsCanvas()
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
canvas.width = source.width
|
||||
canvas.height = source.height
|
||||
ctx.imageSmoothingEnabled = false
|
||||
ctx.drawImage(source, 0, 0)
|
||||
}, [])
|
||||
|
||||
return <canvas ref={canvasRef} className="pixel-mountains" aria-hidden="true" />
|
||||
|
||||
Reference in New Issue
Block a user