Boutons de démarrage du jeu fonctionnels

This commit is contained in:
Emmy D'Anello 2024-12-11 21:33:51 +01:00
parent c28097d443
commit 61b0cd51ae
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
9 changed files with 116 additions and 41 deletions

View File

@ -1,35 +1,39 @@
import { useGameRepairMutation, useGameResetMutation, useGameStartMutation, useGameStopMutation, useGameSwitchPlayerMutation } from '@/hooks/mutations/useGameMutation' import { useGameRepairMutation, useGameResetMutation, useGameStartMutation, useGameStopMutation, useGameSwitchPlayerMutation } from '@/hooks/mutations/useGameMutation'
import { useAuth } from '@/hooks/useAuth' import { useAuth } from '@/hooks/useAuth'
import { useGame } from '@/hooks/useGame' import { useGame, useUpdateGameState } from '@/hooks/useGame'
import { useRouter } from 'expo-router' import { useRouter } from 'expo-router'
import { FAB, List, Surface } from 'react-native-paper' import { useState } from 'react'
import { Button, Dialog, FAB, List, Portal, Surface, Text } from 'react-native-paper'
export default function HistoryScreen() { export default function HistoryScreen() {
const router = useRouter() const router = useRouter()
const auth = useAuth() const auth = useAuth()
const game = useGame() const game = useGame()
const updateGameState = useUpdateGameState()
const gameStartMutation = useGameStartMutation({ const gameStartMutation = useGameStartMutation({
auth, auth,
game, updateGameState,
}) })
const gameStopMutation = useGameStopMutation({ const gameStopMutation = useGameStopMutation({
auth, auth,
game, updateGameState,
}) })
const gameSwitchMutation = useGameSwitchPlayerMutation({ const gameSwitchMutation = useGameSwitchPlayerMutation({
auth, auth,
game, updateGameState,
}) })
const gameRepairMutation = useGameRepairMutation({ const gameRepairMutation = useGameRepairMutation({
auth, auth,
game, updateGameState,
}) })
const gameResetMutation = useGameResetMutation({ const gameResetMutation = useGameResetMutation({
auth, auth,
game, updateGameState,
}) })
const [resetConfirmVisible, setResetConfirmVisible] = useState(false)
return ( return (
<Surface <Surface
style={{ flex: 1 }}> style={{ flex: 1 }}>
@ -74,8 +78,23 @@ export default function HistoryScreen() {
title="Réinitialiser les données de jeu" title="Réinitialiser les données de jeu"
description="Permet de détruire toutes les données. À manipuler avec précaution." description="Permet de détruire toutes les données. À manipuler avec précaution."
right={() => <FAB icon="reload-alert" size="small" variant={'tertiary'} />} right={() => <FAB icon="reload-alert" size="small" variant={'tertiary'} />}
onPress={() => gameResetMutation.mutate()} /> onPress={() => setResetConfirmVisible(true)} />
</List.Section> </List.Section>
<Portal>
<Dialog key="confirmReset" visible={resetConfirmVisible} onDismiss={() => setResetConfirmVisible(false)}>
<Dialog.Title>Confirmer</Dialog.Title>
<Dialog.Content>
<Text variant="bodyMedium">
Cette action va réinitialiser TOUTES les données de jeu : l'historique des positions, les défis réalisés et les trains empruntés.
Êtes-vous réellement sûre de vouloir tout supprimer ?
</Text>
</Dialog.Content>
<Dialog.Actions>
<Button onPress={() => setResetConfirmVisible(false)}>Annuler</Button>
<Button onPress={() => { setResetConfirmVisible(false); gameResetMutation.mutate() }}>Confirmer</Button>
</Dialog.Actions>
</Dialog>
</Portal>
</Surface> </Surface>
) )
} }

View File

@ -14,6 +14,7 @@ import store from '@/utils/store'
import { useStartBackgroundFetchServiceEffect } from '@/utils/background' import { useStartBackgroundFetchServiceEffect } from '@/utils/background'
import LoginProvider from '@/components/LoginProvider' import LoginProvider from '@/components/LoginProvider'
import GeolocationProvider from '@/components/GeolocationProvider' import GeolocationProvider from '@/components/GeolocationProvider'
import GameProvider from '@/components/GameProvider'
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
@ -45,16 +46,18 @@ export default function RootLayout() {
onSuccess={() => queryClient.resumePausedMutations().then(() => queryClient.invalidateQueries())}> onSuccess={() => queryClient.resumePausedMutations().then(() => queryClient.invalidateQueries())}>
<LoginProvider loginRedirect={'/login'}> <LoginProvider loginRedirect={'/login'}>
<GeolocationProvider> <GeolocationProvider>
<PaperProvider theme={colorScheme === 'dark' ? MD3DarkTheme : MD3LightTheme}> <GameProvider>
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}> <PaperProvider theme={colorScheme === 'dark' ? MD3DarkTheme : MD3LightTheme}>
<Stack> <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} /> <Stack>
<Stack.Screen name="login" options={{ headerShown: false }} /> <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" /> <Stack.Screen name="login" options={{ headerShown: false }} />
</Stack> <Stack.Screen name="+not-found" />
<StatusBar style="auto" /> </Stack>
</ThemeProvider> <StatusBar style="auto" />
</PaperProvider> </ThemeProvider>
</PaperProvider>
</GameProvider>
</GeolocationProvider> </GeolocationProvider>
</LoginProvider> </LoginProvider>
</PersistQueryClientProvider> </PersistQueryClientProvider>

View File

@ -0,0 +1,26 @@
import { useAuth } from '@/hooks/useAuth'
import { useUpdateGameState } from '@/hooks/useGame'
import { useQuery } from '@tanstack/react-query'
import { ReactNode, useEffect } from 'react'
export default function GameProvider({ children }: { children: ReactNode }) {
const auth = useAuth()
const updateGameState = useUpdateGameState()
const gameQuery = useQuery({
queryKey: ['update-game'],
queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/`, {
headers: { "Authorization": `Bearer ${auth.token}` }}
).then(resp => resp.json()),
enabled: auth.loggedIn,
refetchInterval: 5000,
})
const game = gameQuery.data
useEffect(() => {
if (game)
updateGameState(game)
}, [game])
return <>
{children}
</>
}

View File

@ -1,5 +1,5 @@
import { AuthState } from "@/utils/features/auth/authSlice" import { AuthState } from "@/utils/features/auth/authSlice"
import { GameState } from "@/utils/features/game/gameSlice" import { GamePayload, GameState } from "@/utils/features/game/gameSlice"
import { useMutation } from "@tanstack/react-query" import { useMutation } from "@tanstack/react-query"
type ErrorResponse = { type ErrorResponse = {
@ -13,13 +13,13 @@ type ErrorFuncProps = { response?: ErrorResponse, error?: Error }
type onErrorFunc = (props: ErrorFuncProps) => void type onErrorFunc = (props: ErrorFuncProps) => void
type GameProps = { type GameProps = {
game: GameState updateGameState: (payload: GamePayload) => { payload: GamePayload, type: "game/updateGameState" }
auth: AuthState auth: AuthState
onPostSuccess?: onPostSuccessFunc onPostSuccess?: onPostSuccessFunc
onError?: onErrorFunc onError?: onErrorFunc
} }
export const useGameStartMutation = ({ game, auth, onPostSuccess, onError }: GameProps) => { export const useGameStartMutation = ({ auth, updateGameState, onPostSuccess, onError }: GameProps) => {
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/start/`, { return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/start/`, {
@ -36,6 +36,7 @@ export const useGameStartMutation = ({ game, auth, onPostSuccess, onError }: Gam
onError({ response: data }) onError({ response: data })
return return
} }
updateGameState(data)
if (onPostSuccess) if (onPostSuccess)
onPostSuccess() onPostSuccess()
}, },
@ -46,7 +47,7 @@ export const useGameStartMutation = ({ game, auth, onPostSuccess, onError }: Gam
}) })
} }
export const useGameStopMutation = ({ auth, game, onPostSuccess, onError }: GameProps) => { export const useGameStopMutation = ({ auth, updateGameState, onPostSuccess, onError }: GameProps) => {
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/stop/`, { return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/stop/`, {
@ -63,6 +64,7 @@ export const useGameStopMutation = ({ auth, game, onPostSuccess, onError }: Game
onError({ response: data }) onError({ response: data })
return return
} }
updateGameState(data)
if (onPostSuccess) if (onPostSuccess)
onPostSuccess() onPostSuccess()
}, },
@ -73,7 +75,7 @@ export const useGameStopMutation = ({ auth, game, onPostSuccess, onError }: Game
}) })
} }
export const useGameSwitchPlayerMutation = ({ auth, game, onPostSuccess, onError }: GameProps) => { export const useGameSwitchPlayerMutation = ({ auth, updateGameState, onPostSuccess, onError }: GameProps) => {
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/switch-running-player/`, { return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/switch-running-player/`, {
@ -90,6 +92,7 @@ export const useGameSwitchPlayerMutation = ({ auth, game, onPostSuccess, onError
onError({ response: data }) onError({ response: data })
return return
} }
updateGameState(data)
if (onPostSuccess) if (onPostSuccess)
onPostSuccess() onPostSuccess()
}, },
@ -100,7 +103,7 @@ export const useGameSwitchPlayerMutation = ({ auth, game, onPostSuccess, onError
}) })
} }
export const useGameRepairMutation = ({ auth, game, onPostSuccess, onError }: GameProps) => { export const useGameRepairMutation = ({ auth, onPostSuccess, onError }: GameProps) => {
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/repair/`, { return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/repair/`, {
@ -127,7 +130,7 @@ export const useGameRepairMutation = ({ auth, game, onPostSuccess, onError }: Ga
}) })
} }
export const useGameResetMutation = ({ auth, game, onPostSuccess, onError }: GameProps) => { export const useGameResetMutation = ({ auth, updateGameState, onPostSuccess, onError }: GameProps) => {
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/reset/`, { return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/reset/`, {
@ -144,6 +147,7 @@ export const useGameResetMutation = ({ auth, game, onPostSuccess, onError }: Gam
onError({ response: data }) onError({ response: data })
return return
} }
updateGameState(data)
if (onPostSuccess) if (onPostSuccess)
onPostSuccess() onPostSuccess()
}, },

View File

@ -8,11 +8,6 @@ type ErrorResponse = {
statusCode: number statusCode: number
} }
type LoginForm = {
name: string
password: string
}
type onPostSuccessFunc = (data: any, variables: LocationObject, context: unknown) => void type onPostSuccessFunc = (data: any, variables: LocationObject, context: unknown) => void
type ErrorFuncProps = { response?: ErrorResponse, error?: Error } type ErrorFuncProps = { response?: ErrorResponse, error?: Error }
type onErrorFunc = (props: ErrorFuncProps) => void type onErrorFunc = (props: ErrorFuncProps) => void

View File

@ -1,5 +1,5 @@
import { useAppDispatch, useAppSelector } from "./useStore" import { useAppDispatch, useAppSelector } from "./useStore"
import { setPlayerId, updateMoney } from "@/utils/features/game/gameSlice" import { GamePayload, setPlayerId, updateGameState, updateMoney } from "@/utils/features/game/gameSlice"
export const useGame = () => useAppSelector((state) => state.game) export const useGame = () => useAppSelector((state) => state.game)
export const useSetPlayerId = () => { export const useSetPlayerId = () => {
@ -10,3 +10,7 @@ export const useUpdateMoney = () => {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
return (money: number) => dispatch(updateMoney(money)) return (money: number) => dispatch(updateMoney(money))
} }
export const useUpdateGameState = () => {
const dispatch = useAppDispatch()
return (game: GamePayload) => dispatch(updateGameState(game))
}

View File

@ -7,10 +7,10 @@ export interface ChallengeAction {
description: string, description: string,
reward: number, reward: number,
success: boolean, success: boolean,
start: Date, start: number, // date
end: Date | null, end: number | null, // date
penaltyStart: Date | null, penaltyStart: number | null, // date
penaltyEnd: Date | null, penaltyEnd: number | null, // date
} }
export interface ActionsState { export interface ActionsState {

View File

@ -1,13 +1,28 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { createSlice, PayloadAction } from '@reduxjs/toolkit'
export interface RunPayload {
id: number
gameId: number
runnerId: number
start: string
end: string | null
}
export interface GamePayload {
id: number
started: boolean
currentRunId: number | null
currentRun: RunPayload | null
}
export interface GameState { export interface GameState {
playerId: number | null playerId: number | null
gameStarted: boolean gameStarted: boolean
money: number money: number
currentRunner: boolean currentRunner: boolean
chaseFreeTime: Date | null chaseFreeTime: number | null // date
penaltyStart: Date | null penaltyStart: number | null // date
penaltyEnd: Date | null penaltyEnd: number | null // date
} }
const initialState: GameState = { const initialState: GameState = {
@ -30,9 +45,18 @@ export const gameSlice = createSlice({
updateMoney: (state, action: PayloadAction<number>) => { updateMoney: (state, action: PayloadAction<number>) => {
state.money = action.payload state.money = action.payload
}, },
updateGameState: (state, action: PayloadAction<GamePayload>) => {
const game: GamePayload = action.payload
state.gameStarted = game.started
state.currentRunner = state.playerId === game.currentRun?.runnerId
if (state.currentRunner)
state.chaseFreeTime = null
else if (game.currentRun)
state.chaseFreeTime = new Date(game.currentRun?.start).getTime() + 45 * 60 * 1000
}
}, },
}) })
export const { setPlayerId, updateMoney } = gameSlice.actions export const { setPlayerId, updateMoney, updateGameState } = gameSlice.actions
export default gameSlice.reducer export default gameSlice.reducer

View File

@ -83,8 +83,8 @@ export interface TrainTrip {
distance: number, distance: number,
from: string, from: string,
to: string, to: string,
departureTime: Date, departureTime: number,
arrivalTime: Date, arrivalTime: number,
} }
export interface TrainsState { export interface TrainsState {