Per request: remove the WebAudio jingle (+ its 🔊 toggle and sound state); replace the one-shot confetti with a continuous rain of 🌼🌸🐸🇸🇪🌿 over the screen (MidsummerRain, gated by the theme, reduced-motion aware, leak-free); and replace player-dot markers with frogs themselves (override the inline dot color/border) instead of a flower-crown on top. Still toggled by the 🐸 Midsommar switch. Includes rebuilt static bundle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
81 lines
3.1 KiB
TypeScript
81 lines
3.1 KiB
TypeScript
import React, { useCallback, useState, useMemo, useEffect } from 'react';
|
|
import { apiFetch } from '../../api/client';
|
|
import { WindowManagerProvider, useWindowManager } from '../../contexts/WindowManagerContext';
|
|
import { MapView } from './MapView';
|
|
import { Sidebar } from './Sidebar';
|
|
import { WindowRenderer } from '../windows/WindowRenderer';
|
|
import { RareNotification } from '../effects/RareNotification';
|
|
import { DeathNotification } from '../effects/DeathNotification';
|
|
import { usePlayerColors } from '../../hooks/usePlayerColors';
|
|
import { MidsummerBanner } from '../midsummer/MidsummerBanner';
|
|
import { MidsummerRain } from '../midsummer/MidsummerRain';
|
|
import type { DashboardState } from '../../hooks/useLiveData';
|
|
|
|
interface Props {
|
|
data: DashboardState;
|
|
}
|
|
|
|
export const MapLayout: React.FC<Props> = ({ data }) => {
|
|
const getColor = usePlayerColors();
|
|
const [showHeatmap, setShowHeatmap] = useState(false);
|
|
const [showPortals, setShowPortals] = useState(false);
|
|
const [selectedPlayer, setSelectedPlayer] = useState<string | null>(null);
|
|
|
|
const players = useMemo(() =>
|
|
Array.from(data.characters.values()).filter(c => c.telemetry).map(c => c.telemetry!),
|
|
[data.characters]);
|
|
|
|
const vitalsMap = useMemo(() =>
|
|
new Map(Array.from(data.characters.values()).filter(c => c.vitals).map(c => [c.name, c.vitals!])),
|
|
[data.characters]);
|
|
|
|
const [version, setVersion] = useState('');
|
|
useEffect(() => {
|
|
// /api-version is the actual route — apiFetch adds /api prefix, so use raw fetch
|
|
fetch('/api/api-version', { credentials: 'include' }).then(r => r.json()).then(d => setVersion(d.version ?? '')).catch(() => {});
|
|
}, []);
|
|
|
|
const handleSelectPlayer = useCallback((name: string) => {
|
|
setSelectedPlayer(prev => prev === name ? null : name);
|
|
}, []);
|
|
|
|
|
|
return (
|
|
<WindowManagerProvider>
|
|
<div className="ml-layout">
|
|
<MidsummerBanner />
|
|
<MidsummerRain />
|
|
<Sidebar
|
|
players={players}
|
|
vitals={vitalsMap}
|
|
serverHealth={data.serverHealth}
|
|
totalRares={data.totalRares}
|
|
totalKills={data.totalKills}
|
|
getColor={getColor}
|
|
onSelectPlayer={handleSelectPlayer}
|
|
showHeatmap={showHeatmap}
|
|
showPortals={showPortals}
|
|
onToggleHeatmap={setShowHeatmap}
|
|
onTogglePortals={setShowPortals}
|
|
version={version}
|
|
selectedPlayer={selectedPlayer}
|
|
/>
|
|
|
|
<MapView
|
|
players={players}
|
|
getColor={getColor}
|
|
onSelectPlayer={handleSelectPlayer}
|
|
showHeatmap={showHeatmap}
|
|
showPortals={showPortals}
|
|
selectedPlayer={selectedPlayer}
|
|
/>
|
|
<WindowRenderer characters={data.characters} chatMessages={data.chatMessages}
|
|
nearbyObjects={data.nearbyObjects} inventoryVersions={data.inventoryVersions}
|
|
equipmentCantrips={data.equipmentCantrips} characterStats={data.characterStats}
|
|
socket={data.socketRef.current} />
|
|
<RareNotification recentRares={data.recentRares} />
|
|
<DeathNotification deathAlerts={data.deathAlerts} />
|
|
</div>
|
|
</WindowManagerProvider>
|
|
);
|
|
};
|