Cleanup
This commit is contained in:
parent
b4a104a860
commit
38f9b7a33a
1 changed files with 15 additions and 39 deletions
54
src/App.tsx
54
src/App.tsx
|
@ -31,20 +31,19 @@ import { clamp } from './utils/math';
|
|||
import './App.css';
|
||||
|
||||
function App() {
|
||||
// Core state
|
||||
// Simulation state
|
||||
const [targets, setTargets] = useState<Target[]>([]);
|
||||
const [currentTarget, setCurrentTarget] = useState<number | null>(null);
|
||||
const [zoomLevel, setZoomLevel] = useState<number>(CONSTANTS.ZOOM_LEVELS.default);
|
||||
const [stats, setStats] = useState<Stats>(createInitialStats());
|
||||
|
||||
// Study mode state
|
||||
// Experiment mode configuration
|
||||
const [mode, setMode] = useState<AppMode>('freestyle');
|
||||
const [currentPreset, setCurrentPreset] = useState<ZoomPreset>('A');
|
||||
const [presetsCompleted, setPresetsCompleted] = useState<ZoomPreset[]>([]);
|
||||
const [targetCount, setTargetCount] = useState(0);
|
||||
const [studyComplete, setStudyComplete] = useState(false);
|
||||
|
||||
// Load settings from localStorage or use defaults
|
||||
const [settings, setSettings] = useState<AccelerationSettings>(() => {
|
||||
const savedState = loadState();
|
||||
const defaultSettings = createDefaultSettings();
|
||||
|
@ -56,30 +55,28 @@ function App() {
|
|||
return savedState?.rounds ?? [];
|
||||
});
|
||||
|
||||
// UI state
|
||||
// UI visibility states
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [showRounds, setShowRounds] = useState(false);
|
||||
const [showInstructions, setShowInstructions] = useState(false);
|
||||
const [showResizeWarning, setShowResizeWarning] = useState(false);
|
||||
const [viewportOffset, setViewportOffset] = useState<ViewportOffset>({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
// Add state to track drag operations
|
||||
// Interaction tracking refs
|
||||
const isDragging = useRef(false);
|
||||
const dragStartTime = useRef(0);
|
||||
const dragStartPos = useRef({ x: 0, y: 0 });
|
||||
const DRAG_THRESHOLD = 5; // pixels
|
||||
|
||||
// Refs for handling zoom behavior
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const zoomSpeedSamples = useRef<number[]>([]);
|
||||
const touchStartDistance = useRef<number>(0);
|
||||
const wasReset = useRef(false);
|
||||
|
||||
// Add a new state for the resize warning popup
|
||||
const [showResizeWarning, setShowResizeWarning] = useState(false);
|
||||
|
||||
// Load saved state on initial mount
|
||||
useEffect(() => {
|
||||
const savedState = loadState();
|
||||
if (savedState?.stats) {
|
||||
|
@ -88,6 +85,7 @@ function App() {
|
|||
setShowInstructions(!savedState?.rounds || savedState.rounds.length === 0);
|
||||
}, []);
|
||||
|
||||
// Persist state when it changes
|
||||
useEffect(() => {
|
||||
saveState({
|
||||
rounds,
|
||||
|
@ -96,6 +94,7 @@ function App() {
|
|||
});
|
||||
}, [rounds, stats, settings]);
|
||||
|
||||
// Initialize the simulation on first load
|
||||
useEffect(() => {
|
||||
initializeGame();
|
||||
}, []);
|
||||
|
@ -115,7 +114,7 @@ function App() {
|
|||
setStudyComplete(false);
|
||||
};
|
||||
|
||||
// Apply preset when it changes
|
||||
// Configure settings when preset changes
|
||||
useEffect(() => {
|
||||
if (currentPreset) {
|
||||
setSettings(prev => ({
|
||||
|
@ -127,32 +126,29 @@ function App() {
|
|||
}
|
||||
}, [currentPreset]);
|
||||
|
||||
// Handle moving to next preset in study mode
|
||||
// Progress through presets in study mode
|
||||
useEffect(() => {
|
||||
if (mode !== 'study') return;
|
||||
|
||||
if (targetCount >= CONSTANTS.STUDY_TARGETS_PER_PRESET) {
|
||||
// Add current preset to completed list
|
||||
if (!presetsCompleted.includes(currentPreset)) {
|
||||
setPresetsCompleted(prev => [...prev, currentPreset]);
|
||||
}
|
||||
|
||||
// Directly move to the next preset without showing break screen
|
||||
const allPresets: ZoomPreset[] = ['A', 'B', 'C', 'D'];
|
||||
const currentIndex = allPresets.indexOf(currentPreset);
|
||||
|
||||
if (currentIndex < allPresets.length - 1) {
|
||||
// Move to next preset
|
||||
const nextPreset = allPresets[currentIndex + 1];
|
||||
setCurrentPreset(nextPreset);
|
||||
setTargetCount(0);
|
||||
} else {
|
||||
// All presets completed
|
||||
setStudyComplete(true);
|
||||
}
|
||||
}
|
||||
}, [targetCount, currentPreset, mode, presetsCompleted]);
|
||||
|
||||
// Handle zoom wheel events and track timing data
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
@ -171,15 +167,12 @@ function App() {
|
|||
const scrollAmount = processWheelDelta(-event.deltaY);
|
||||
const now = Date.now();
|
||||
|
||||
// Determine zoom direction (positive scrollAmount = zoom in, negative = zoom out)
|
||||
const direction = scrollAmount > 0 ? 'in' : 'out';
|
||||
|
||||
// Update zoom timing stats
|
||||
setStats(previous => {
|
||||
// If we're already zooming in the same direction, add to the time
|
||||
if (previous.isZooming && previous.zoomDirection === direction && previous.lastZoomTime) {
|
||||
const timeDelta = now - previous.lastZoomTime;
|
||||
// Only count if it's a reasonable time (less than 500ms between events)
|
||||
// Only accumulate time if events are close together (continuous zooming)
|
||||
const shouldCount = timeDelta < 500;
|
||||
|
||||
return {
|
||||
|
@ -191,7 +184,6 @@ function App() {
|
|||
};
|
||||
}
|
||||
|
||||
// Otherwise start new zoom tracking
|
||||
return {
|
||||
...previous,
|
||||
isZooming: true,
|
||||
|
@ -233,7 +225,6 @@ function App() {
|
|||
}
|
||||
};
|
||||
|
||||
// Stop zoom timing when wheel events stop
|
||||
const handleZoomEnd = () => {
|
||||
setStats(previous => ({
|
||||
...previous,
|
||||
|
@ -245,7 +236,7 @@ function App() {
|
|||
|
||||
container.addEventListener('wheel', handleWheelEvent, { passive: false });
|
||||
|
||||
// Add a listener to detect when zooming stops (after 300ms of no wheel events)
|
||||
// Detect when zooming stops after a brief pause
|
||||
let zoomEndTimer: number;
|
||||
container.addEventListener('wheel', () => {
|
||||
clearTimeout(zoomEndTimer);
|
||||
|
@ -334,7 +325,6 @@ function App() {
|
|||
if (distanceToTarget <= target.radius) {
|
||||
const timeElapsed = Date.now() - stats.startTime;
|
||||
|
||||
// Create and save the round with zoom timing data
|
||||
const newRound = createNewRound(
|
||||
timeElapsed,
|
||||
stats.misclicks,
|
||||
|
@ -348,7 +338,6 @@ function App() {
|
|||
|
||||
setRounds(previous => [...previous, newRound]);
|
||||
|
||||
// Update stats and reset zoom timing data
|
||||
setStats({
|
||||
startTime: Date.now(),
|
||||
misclicks: 0,
|
||||
|
@ -361,10 +350,8 @@ function App() {
|
|||
zoomDirection: null
|
||||
});
|
||||
|
||||
// Set next target
|
||||
setCurrentTarget(getRandomTarget(currentTarget, CONSTANTS.TARGET_COUNT));
|
||||
|
||||
// If in study mode, increment target count
|
||||
if (mode === 'study') {
|
||||
setTargetCount(prev => prev + 1);
|
||||
}
|
||||
|
@ -384,24 +371,19 @@ function App() {
|
|||
wasReset.current = true;
|
||||
initializeGame();
|
||||
|
||||
// Close any open panels
|
||||
setShowSettings(false);
|
||||
setShowRounds(false);
|
||||
|
||||
// Show the initial instructions screen
|
||||
setShowInstructions(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleModeChange = (newMode: AppMode) => {
|
||||
// Reset data when changing modes
|
||||
if (newMode !== mode) {
|
||||
if (window.confirm('Changing modes will reset your current data. Continue?')) {
|
||||
setMode(newMode);
|
||||
setRounds([]);
|
||||
setStats(createInitialStats());
|
||||
|
||||
// Reset study mode stats when changing modes
|
||||
if (newMode === 'study') {
|
||||
setTargetCount(0);
|
||||
setPresetsCompleted([]);
|
||||
|
@ -409,7 +391,6 @@ function App() {
|
|||
setStudyComplete(false);
|
||||
}
|
||||
|
||||
// Reset the game
|
||||
initializeGame();
|
||||
}
|
||||
} else {
|
||||
|
@ -417,15 +398,13 @@ function App() {
|
|||
}
|
||||
};
|
||||
|
||||
// Calculate average time
|
||||
const averageTime = calculateAverageTime(stats.times);
|
||||
|
||||
// Add a resize handler to regenerate targets when window size changes
|
||||
// Regenerate targets when window is resized
|
||||
useEffect(() => {
|
||||
let resizeTimer: number;
|
||||
|
||||
const handleResize = () => {
|
||||
// Don't regenerate if in study mode to avoid disrupting the test
|
||||
if (mode !== 'study') {
|
||||
clearTimeout(resizeTimer);
|
||||
|
||||
|
@ -433,14 +412,12 @@ function App() {
|
|||
const newTargets = generateTargets();
|
||||
setTargets(newTargets);
|
||||
|
||||
// If we're targeting something, make sure it's still valid
|
||||
if (currentTarget !== null) {
|
||||
setCurrentTarget(getRandomTarget(currentTarget, CONSTANTS.TARGET_COUNT));
|
||||
}
|
||||
|
||||
// Show resize warning
|
||||
setShowResizeWarning(true);
|
||||
}, 500); // Add a delay to detect when resizing is "done"
|
||||
}, 500); // Delay to detect when resizing is complete
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -569,7 +546,6 @@ function App() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Resize warning popup */}
|
||||
{showResizeWarning && (
|
||||
<div className="resize-warning">
|
||||
<div className="resize-warning-content">
|
||||
|
|
Loading…
Add table
Reference in a new issue