Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
277 lines
6.9 KiB
TypeScript
277 lines
6.9 KiB
TypeScript
'use client'
|
|
|
|
import {
|
|
createContext,
|
|
useContext,
|
|
useReducer,
|
|
useCallback,
|
|
ReactNode,
|
|
} from 'react'
|
|
import {
|
|
AOIResponse,
|
|
AOITheme,
|
|
AOIQuality,
|
|
Difficulty,
|
|
GeoJSONPolygon,
|
|
LearningNode,
|
|
GeoLernweltState,
|
|
} from '@/app/geo-lernwelt/types'
|
|
|
|
// Initial state
|
|
const initialState: GeoLernweltState = {
|
|
currentAOI: null,
|
|
drawnPolygon: null,
|
|
selectedTheme: 'topographie',
|
|
quality: 'medium',
|
|
difficulty: 'mittel',
|
|
learningNodes: [],
|
|
selectedNode: null,
|
|
isDrawing: false,
|
|
isLoading: false,
|
|
error: null,
|
|
unityReady: false,
|
|
unityProgress: 0,
|
|
}
|
|
|
|
// Action types
|
|
type GeoAction =
|
|
| { type: 'SET_AOI'; payload: AOIResponse | null }
|
|
| { type: 'SET_POLYGON'; payload: GeoJSONPolygon | null }
|
|
| { type: 'SET_THEME'; payload: AOITheme }
|
|
| { type: 'SET_QUALITY'; payload: AOIQuality }
|
|
| { type: 'SET_DIFFICULTY'; payload: Difficulty }
|
|
| { type: 'SET_LEARNING_NODES'; payload: LearningNode[] }
|
|
| { type: 'ADD_LEARNING_NODE'; payload: LearningNode }
|
|
| { type: 'UPDATE_LEARNING_NODE'; payload: LearningNode }
|
|
| { type: 'REMOVE_LEARNING_NODE'; payload: string }
|
|
| { type: 'SELECT_NODE'; payload: LearningNode | null }
|
|
| { type: 'SET_DRAWING'; payload: boolean }
|
|
| { type: 'SET_LOADING'; payload: boolean }
|
|
| { type: 'SET_ERROR'; payload: string | null }
|
|
| { type: 'SET_UNITY_READY'; payload: boolean }
|
|
| { type: 'SET_UNITY_PROGRESS'; payload: number }
|
|
| { type: 'RESET' }
|
|
|
|
// Reducer
|
|
function geoReducer(state: GeoLernweltState, action: GeoAction): GeoLernweltState {
|
|
switch (action.type) {
|
|
case 'SET_AOI':
|
|
return { ...state, currentAOI: action.payload }
|
|
|
|
case 'SET_POLYGON':
|
|
return { ...state, drawnPolygon: action.payload }
|
|
|
|
case 'SET_THEME':
|
|
return { ...state, selectedTheme: action.payload }
|
|
|
|
case 'SET_QUALITY':
|
|
return { ...state, quality: action.payload }
|
|
|
|
case 'SET_DIFFICULTY':
|
|
return { ...state, difficulty: action.payload }
|
|
|
|
case 'SET_LEARNING_NODES':
|
|
return { ...state, learningNodes: action.payload }
|
|
|
|
case 'ADD_LEARNING_NODE':
|
|
return {
|
|
...state,
|
|
learningNodes: [...state.learningNodes, action.payload],
|
|
}
|
|
|
|
case 'UPDATE_LEARNING_NODE':
|
|
return {
|
|
...state,
|
|
learningNodes: state.learningNodes.map((node) =>
|
|
node.id === action.payload.id ? action.payload : node
|
|
),
|
|
}
|
|
|
|
case 'REMOVE_LEARNING_NODE':
|
|
return {
|
|
...state,
|
|
learningNodes: state.learningNodes.filter(
|
|
(node) => node.id !== action.payload
|
|
),
|
|
selectedNode:
|
|
state.selectedNode?.id === action.payload ? null : state.selectedNode,
|
|
}
|
|
|
|
case 'SELECT_NODE':
|
|
return { ...state, selectedNode: action.payload }
|
|
|
|
case 'SET_DRAWING':
|
|
return { ...state, isDrawing: action.payload }
|
|
|
|
case 'SET_LOADING':
|
|
return { ...state, isLoading: action.payload }
|
|
|
|
case 'SET_ERROR':
|
|
return { ...state, error: action.payload }
|
|
|
|
case 'SET_UNITY_READY':
|
|
return { ...state, unityReady: action.payload }
|
|
|
|
case 'SET_UNITY_PROGRESS':
|
|
return { ...state, unityProgress: action.payload }
|
|
|
|
case 'RESET':
|
|
return initialState
|
|
|
|
default:
|
|
return state
|
|
}
|
|
}
|
|
|
|
// Context types
|
|
interface GeoContextValue {
|
|
state: GeoLernweltState
|
|
dispatch: React.Dispatch<GeoAction>
|
|
// Convenience actions
|
|
setAOI: (aoi: AOIResponse | null) => void
|
|
setPolygon: (polygon: GeoJSONPolygon | null) => void
|
|
setTheme: (theme: AOITheme) => void
|
|
setQuality: (quality: AOIQuality) => void
|
|
setDifficulty: (difficulty: Difficulty) => void
|
|
setLearningNodes: (nodes: LearningNode[]) => void
|
|
addNode: (node: LearningNode) => void
|
|
updateNode: (node: LearningNode) => void
|
|
removeNode: (nodeId: string) => void
|
|
selectNode: (node: LearningNode | null) => void
|
|
setDrawing: (drawing: boolean) => void
|
|
setLoading: (loading: boolean) => void
|
|
setError: (error: string | null) => void
|
|
setUnityReady: (ready: boolean) => void
|
|
setUnityProgress: (progress: number) => void
|
|
reset: () => void
|
|
}
|
|
|
|
// Create context
|
|
const GeoContext = createContext<GeoContextValue | null>(null)
|
|
|
|
// Provider component
|
|
export function GeoProvider({ children }: { children: ReactNode }) {
|
|
const [state, dispatch] = useReducer(geoReducer, initialState)
|
|
|
|
// Convenience action creators
|
|
const setAOI = useCallback(
|
|
(aoi: AOIResponse | null) => dispatch({ type: 'SET_AOI', payload: aoi }),
|
|
[]
|
|
)
|
|
|
|
const setPolygon = useCallback(
|
|
(polygon: GeoJSONPolygon | null) =>
|
|
dispatch({ type: 'SET_POLYGON', payload: polygon }),
|
|
[]
|
|
)
|
|
|
|
const setTheme = useCallback(
|
|
(theme: AOITheme) => dispatch({ type: 'SET_THEME', payload: theme }),
|
|
[]
|
|
)
|
|
|
|
const setQuality = useCallback(
|
|
(quality: AOIQuality) => dispatch({ type: 'SET_QUALITY', payload: quality }),
|
|
[]
|
|
)
|
|
|
|
const setDifficulty = useCallback(
|
|
(difficulty: Difficulty) =>
|
|
dispatch({ type: 'SET_DIFFICULTY', payload: difficulty }),
|
|
[]
|
|
)
|
|
|
|
const setLearningNodes = useCallback(
|
|
(nodes: LearningNode[]) =>
|
|
dispatch({ type: 'SET_LEARNING_NODES', payload: nodes }),
|
|
[]
|
|
)
|
|
|
|
const addNode = useCallback(
|
|
(node: LearningNode) => dispatch({ type: 'ADD_LEARNING_NODE', payload: node }),
|
|
[]
|
|
)
|
|
|
|
const updateNode = useCallback(
|
|
(node: LearningNode) =>
|
|
dispatch({ type: 'UPDATE_LEARNING_NODE', payload: node }),
|
|
[]
|
|
)
|
|
|
|
const removeNode = useCallback(
|
|
(nodeId: string) =>
|
|
dispatch({ type: 'REMOVE_LEARNING_NODE', payload: nodeId }),
|
|
[]
|
|
)
|
|
|
|
const selectNode = useCallback(
|
|
(node: LearningNode | null) => dispatch({ type: 'SELECT_NODE', payload: node }),
|
|
[]
|
|
)
|
|
|
|
const setDrawing = useCallback(
|
|
(drawing: boolean) => dispatch({ type: 'SET_DRAWING', payload: drawing }),
|
|
[]
|
|
)
|
|
|
|
const setLoading = useCallback(
|
|
(loading: boolean) => dispatch({ type: 'SET_LOADING', payload: loading }),
|
|
[]
|
|
)
|
|
|
|
const setError = useCallback(
|
|
(error: string | null) => dispatch({ type: 'SET_ERROR', payload: error }),
|
|
[]
|
|
)
|
|
|
|
const setUnityReady = useCallback(
|
|
(ready: boolean) => dispatch({ type: 'SET_UNITY_READY', payload: ready }),
|
|
[]
|
|
)
|
|
|
|
const setUnityProgress = useCallback(
|
|
(progress: number) => dispatch({ type: 'SET_UNITY_PROGRESS', payload: progress }),
|
|
[]
|
|
)
|
|
|
|
const reset = useCallback(() => dispatch({ type: 'RESET' }), [])
|
|
|
|
const value: GeoContextValue = {
|
|
state,
|
|
dispatch,
|
|
setAOI,
|
|
setPolygon,
|
|
setTheme,
|
|
setQuality,
|
|
setDifficulty,
|
|
setLearningNodes,
|
|
addNode,
|
|
updateNode,
|
|
removeNode,
|
|
selectNode,
|
|
setDrawing,
|
|
setLoading,
|
|
setError,
|
|
setUnityReady,
|
|
setUnityProgress,
|
|
reset,
|
|
}
|
|
|
|
return <GeoContext.Provider value={value}>{children}</GeoContext.Provider>
|
|
}
|
|
|
|
// Hook to use context
|
|
export function useGeo() {
|
|
const context = useContext(GeoContext)
|
|
if (!context) {
|
|
throw new Error('useGeo must be used within a GeoProvider')
|
|
}
|
|
return context
|
|
}
|
|
|
|
// Hook for just the state (read-only)
|
|
export function useGeoState() {
|
|
const { state } = useGeo()
|
|
return state
|
|
}
|