Fix broken routing by unifying game state via Context API
This commit is contained in:
parent
6fc0820831
commit
82cae002f8
8 changed files with 53 additions and 20 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
14
src/App.jsx
14
src/App.jsx
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
21
src/context/GameStateContext.tsx
Normal file
21
src/context/GameStateContext.tsx
Normal 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;
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue