Fix broken routing by unifying game state via Context API

This commit is contained in:
10x Developer 2026-05-08 21:35:14 +02:00
parent 6fc0820831
commit 82cae002f8
8 changed files with 53 additions and 20 deletions

View file

@ -21,10 +21,13 @@ Mobile-first React web app for tracking Schaffhauser (Jass) card game rounds usi
6. History Screen - Game storage and export functionality 6. History Screen - Game storage and export functionality
### Development Commands ### Development Commands
- `npm run dev` - Start development server
- `npm run build` - Build production version - `npm run build` - Build production version
- `npm run preview` - Preview production build - `npm run preview` - Preview production build
### Development Server
- A development server is always guaranteed to be running on localhost:5173
- **Do not start the development server manually under any circumstances**
### Camera & Detection ### Camera & Detection
- Uses HTML5 Camera API with environment-facing camera preference - Uses HTML5 Camera API with environment-facing camera preference
- Card detection implemented with custom image processing techniques - Card detection implemented with custom image processing techniques

View file

@ -2,14 +2,15 @@
"name": "tschausepp", "name": "tschausepp",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --host 0.0.0.0",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@tensorflow/tfjs": "^4.19.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"@tensorflow/tfjs": "^4.19.0" "react-router-dom": "^7.15.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.3.5", "@types/react": "^18.3.5",

View file

@ -1,12 +1,20 @@
import React from 'react'; import React from 'react';
import { GameStateProvider, useGameStateContext } from './context/GameStateContext';
import SetupScreen from './components/Setup/SetupScreen'; import SetupScreen from './components/Setup/SetupScreen';
import CameraScreen from './components/Camera/CameraScreen'; import CameraScreen from './components/Camera/CameraScreen';
import ResultsScreen from './components/Results/ResultsScreen'; import ResultsScreen from './components/Results/ResultsScreen';
import HistoryScreen from './components/History/HistoryScreen'; import HistoryScreen from './components/History/HistoryScreen';
import useGameState from './hooks/useGameState';
const App = () => { const App = () => {
const { gameState } = useGameState(); return (
<GameStateProvider>
<AppContent />
</GameStateProvider>
);
};
const AppContent = () => {
const { gameState } = useGameStateContext();
const renderScreen = () => { const renderScreen = () => {
switch (gameState.currentScreen) { switch (gameState.currentScreen) {
@ -30,4 +38,4 @@ const App = () => {
); );
}; };
export default App; export default App;

View file

@ -1,16 +1,16 @@
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
import { GameState } from '../../types'; import { GameState } from '../../types';
import useGameState from '../../hooks/useGameState'; import { useGameStateContext } from '../../context/GameStateContext';
import Detection from '../Detection/Detection'; import Detection from '../Detection/Detection';
import Assignment from '../Assignment/Assignment'; import Assignment from '../Assignment/Assignment';
const CameraScreen: React.FC = () => { const CameraScreen: React.FC = () => {
const { const {
gameState, gameState,
setCurrentScreen,
setCameraStream, setCameraStream,
scanTable, scanTable
showResults } = useGameStateContext();
} = useGameState();
const videoRef = useRef<HTMLVideoElement>(null); const videoRef = useRef<HTMLVideoElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
@ -99,7 +99,7 @@ const CameraScreen: React.FC = () => {
<div className="screen camera-screen"> <div className="screen camera-screen">
<div className="camera-header"> <div className="camera-header">
<h2>🎥 Round <span>{gameState.currentRound}</span></h2> <h2>🎥 Round <span>{gameState.currentRound}</span></h2>
<button onClick={() => window.location.hash = '#setup'} className="secondary"> <button onClick={() => gameState.setCurrentScreen('setup')} className="secondary">
End Round End Round
</button> </button>
</div> </div>

View file

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import { Game } from '../../types'; import { Game } from '../../types';
import useGameState from '../../hooks/useGameState'; import { useGameStateContext } from '../../context/GameStateContext';
const HistoryScreen: React.FC = () => { const HistoryScreen: React.FC = () => {
const { gameState, setCurrentScreen } = useGameState(); const { gameState, setCurrentScreen } = useGameStateContext();
const renderHistory = () => { const renderHistory = () => {
if (!gameState.gameHistory || gameState.gameHistory.length === 0) { if (!gameState.gameHistory || gameState.gameHistory.length === 0) {

View file

@ -1,13 +1,13 @@
import React from 'react'; import React from 'react';
import { GameState } from '../../types'; import { GameState } from '../../types';
import useGameState from '../../hooks/useGameState'; import { useGameStateContext } from '../../context/GameStateContext';
const ResultsScreen: React.FC = () => { const ResultsScreen: React.FC = () => {
const { const {
gameState, gameState,
setCurrentScreen, saveGame,
saveGame setCurrentScreen
} = useGameState(); } = useGameStateContext();
const calculateScores = () => { const calculateScores = () => {
// Scoring logic based on card values // Scoring logic based on card values

View file

@ -1,6 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Player, GameState } from '../../types'; import { Player, GameState } from '../../types';
import useGameState from '../../hooks/useGameState'; import { useGameStateContext } from '../../context/GameStateContext';
const SetupScreen: React.FC = () => { const SetupScreen: React.FC = () => {
const { const {
@ -9,7 +9,7 @@ const SetupScreen: React.FC = () => {
updateCardValues, updateCardValues,
addPlayer, addPlayer,
startNewGame startNewGame
} = useGameState(); } = useGameStateContext();
const [newPlayerName, setNewPlayerName] = useState(''); const [newPlayerName, setNewPlayerName] = useState('');
@ -147,7 +147,7 @@ const SetupScreen: React.FC = () => {
<button onClick={startGame} disabled={gameState.players.length < 2}> <button onClick={startGame} disabled={gameState.players.length < 2}>
Start Game Start Game
</button> </button>
<button onClick={() => window.location.hash = '#history'} className="secondary"> <button onClick={() => gameState.setCurrentScreen('history')} className="secondary">
View History View History
</button> </button>
</div> </div>

View file

@ -0,0 +1,21 @@
import React, { createContext, useContext } from 'react';
import useGameState from '../hooks/useGameState';
const GameStateContext = createContext<any>(null);
export const GameStateProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const value = useGameState();
return (
<GameStateContext.Provider value={value}>
{children}
</GameStateContext.Provider>
);
};
export const useGameStateContext = () => {
const context = useContext(GameStateContext);
if (!context) {
throw new Error('useGameStateContext must be used within a GameStateProvider');
}
return context;
};