Compare commits
4 Commits
33ee18d7e2
...
bfc6069a87
Author | SHA1 | Date | |
---|---|---|---|
bfc6069a87 | |||
a4a0981b2c | |||
246dae446f | |||
38117ade07 |
@ -1,8 +1,11 @@
|
|||||||
import { useAddTrainMutation } from '@/hooks/mutations/useTrainMutation'
|
import { useAddTrainMutation } from '@/hooks/mutations/useTrainMutation'
|
||||||
import { useAuth } from '@/hooks/useAuth'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
|
import { useTrain } from '@/hooks/useTrain'
|
||||||
|
import { TrainTrip } from '@/utils/features/train/trainSlice'
|
||||||
|
import { FontAwesome6 } from '@expo/vector-icons'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { StyleSheet } from 'react-native'
|
import { FlatList, StyleSheet } from 'react-native'
|
||||||
import { Button, Dialog, FAB, HelperText, Portal, Surface, Text, TextInput } from 'react-native-paper'
|
import { Button, Dialog, Divider, FAB, HelperText, List, Portal, Surface, Text, TextInput } from 'react-native-paper'
|
||||||
|
|
||||||
export default function TrainScreen() {
|
export default function TrainScreen() {
|
||||||
const [addTrainVisible, setAddTrainVisible] = useState(false)
|
const [addTrainVisible, setAddTrainVisible] = useState(false)
|
||||||
@ -15,9 +18,15 @@ export default function TrainScreen() {
|
|||||||
onPostSuccess: () => setAddTrainVisible(false)
|
onPostSuccess: () => setAddTrainVisible(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const trains = useTrain()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Surface style={{ flex: 1 }}>
|
<Surface style={{ flex: 1 }}>
|
||||||
<Text variant='bodyMedium'>Ici on aura la page pour ajouter un trajet en train depuis Rail Planner</Text>
|
<FlatList
|
||||||
|
data={trains.trains}
|
||||||
|
keyExtractor={(train) => train.id}
|
||||||
|
ItemSeparatorComponent={() => <Divider />}
|
||||||
|
renderItem={(item) => <TrainListItem train={item.item} />} />
|
||||||
<FAB
|
<FAB
|
||||||
icon='plus'
|
icon='plus'
|
||||||
style={styles.addTrainButton}
|
style={styles.addTrainButton}
|
||||||
@ -63,3 +72,22 @@ const styles = StyleSheet.create({
|
|||||||
bottom: 25,
|
bottom: 25,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function TrainListItem({ train }: { train: TrainTrip }) {
|
||||||
|
const depDateTime = new Date(train.departureTime)
|
||||||
|
const depDate = depDateTime.toLocaleDateString()
|
||||||
|
const depTime = depDateTime.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })
|
||||||
|
const arrDateTime = new Date(train.arrivalTime)
|
||||||
|
const arrTime = arrDateTime.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })
|
||||||
|
const durationInMinutes = (arrDateTime.getTime() - depDateTime.getTime()) / 1000 / 60
|
||||||
|
const duration = `${Math.floor(durationInMinutes / 60).toString().padStart(2, '0')}:${Math.floor(durationInMinutes % 60).toString().padStart(2, '0')}`
|
||||||
|
const title = `${train.from} ${depTime} => ${train.to} ${arrTime} (${depDate})`
|
||||||
|
const distanceKm = Math.ceil(train.distance / 1000)
|
||||||
|
const cost = 10 * distanceKm
|
||||||
|
return <>
|
||||||
|
<List.Item
|
||||||
|
title={title}
|
||||||
|
description={<><Text>Durée : {duration}, distance : {distanceKm} km, coût : {cost}</Text> <FontAwesome6 name='coins' /></>}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { useAuth } from '@/hooks/useAuth'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
import { useGame, useUpdateGameState, useUpdateMoney } from '@/hooks/useGame'
|
import { useGame, useUpdateGameState, useUpdateMoney } from '@/hooks/useGame'
|
||||||
|
import { useDownloadTrains } from '@/hooks/useTrain'
|
||||||
|
import { isAuthValid } from '@/utils/features/auth/authSlice'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { ReactNode, useEffect } from 'react'
|
import { ReactNode, useEffect } from 'react'
|
||||||
|
|
||||||
@ -8,32 +10,46 @@ export default function GameProvider({ children }: { children: ReactNode }) {
|
|||||||
const game = useGame()
|
const game = useGame()
|
||||||
const updateGameState = useUpdateGameState()
|
const updateGameState = useUpdateGameState()
|
||||||
const updateMoney = useUpdateMoney()
|
const updateMoney = useUpdateMoney()
|
||||||
|
const downloadTrains = useDownloadTrains()
|
||||||
|
|
||||||
const gameQuery = useQuery({
|
const gameQuery = useQuery({
|
||||||
queryKey: ['get-game'],
|
queryKey: ['get-game', auth.token],
|
||||||
queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/`, {
|
queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/game/`, {
|
||||||
headers: { "Authorization": `Bearer ${auth.token}` }}
|
headers: { "Authorization": `Bearer ${auth.token}` }}
|
||||||
).then(resp => resp.json()),
|
).then(resp => resp.json()),
|
||||||
enabled: auth.loggedIn,
|
enabled: isAuthValid(auth),
|
||||||
refetchInterval: 5000,
|
refetchInterval: 5000,
|
||||||
})
|
})
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gameQuery.isSuccess && gameQuery.data)
|
if (gameQuery.isSuccess && gameQuery.data)
|
||||||
updateGameState(gameQuery.data)
|
updateGameState(gameQuery.data)
|
||||||
}, [gameQuery])
|
}, [gameQuery.status, gameQuery.dataUpdatedAt])
|
||||||
|
|
||||||
const playerQuery = useQuery({
|
const playerQuery = useQuery({
|
||||||
queryKey: ['get-player'],
|
queryKey: ['get-player', game.playerId, auth.token],
|
||||||
queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/players/${game.playerId}/`, {
|
queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/players/${game.playerId}/`, {
|
||||||
headers: { "Authorization": `Bearer ${auth.token}` }}
|
headers: { "Authorization": `Bearer ${auth.token}` }}
|
||||||
).then(resp => resp.json()),
|
).then(resp => resp.json()),
|
||||||
enabled: auth.loggedIn && !!game.playerId,
|
enabled: isAuthValid(auth) && !!game.playerId,
|
||||||
refetchInterval: 5000,
|
refetchInterval: 5000,
|
||||||
})
|
})
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (playerQuery.isSuccess && playerQuery.data)
|
if (playerQuery.isSuccess && playerQuery.data)
|
||||||
updateMoney(playerQuery.data.money)
|
updateMoney(playerQuery.data.money)
|
||||||
}, [playerQuery])
|
}, [playerQuery.status, playerQuery.dataUpdatedAt])
|
||||||
|
|
||||||
|
const trainsQuery = useQuery({
|
||||||
|
queryKey: ['get-trains', game.playerId, auth.token],
|
||||||
|
queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/trains/?playerId=${game.playerId}`, {
|
||||||
|
headers: { "Authorization": `Bearer ${auth.token}` }}
|
||||||
|
).then(resp => resp.json()),
|
||||||
|
enabled: isAuthValid(auth) && !game.playerId,
|
||||||
|
refetchInterval: 5000,
|
||||||
|
})
|
||||||
|
useEffect(() => {
|
||||||
|
if (trainsQuery.isSuccess && trainsQuery.data && trainsQuery)
|
||||||
|
downloadTrains(trainsQuery.data)
|
||||||
|
}, [trainsQuery.status, trainsQuery.dataUpdatedAt])
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{children}
|
{children}
|
||||||
|
@ -31,7 +31,7 @@ export const useGameStartMutation = ({ auth, updateGameState, onPostSuccess, onE
|
|||||||
}).then(resp => resp.json())
|
}).then(resp => resp.json())
|
||||||
},
|
},
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
if (data.error) {
|
if (data.statusCode) {
|
||||||
if (onError)
|
if (onError)
|
||||||
onError({ response: data })
|
onError({ response: data })
|
||||||
return
|
return
|
||||||
@ -59,7 +59,7 @@ export const useGameStopMutation = ({ auth, updateGameState, onPostSuccess, onEr
|
|||||||
}).then(resp => resp.json())
|
}).then(resp => resp.json())
|
||||||
},
|
},
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
if (data.error) {
|
if (data.statusCode) {
|
||||||
if (onError)
|
if (onError)
|
||||||
onError({ response: data })
|
onError({ response: data })
|
||||||
return
|
return
|
||||||
@ -87,7 +87,7 @@ export const useGameSwitchPlayerMutation = ({ auth, updateGameState, onPostSucce
|
|||||||
}).then(resp => resp.json())
|
}).then(resp => resp.json())
|
||||||
},
|
},
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
if (data.error) {
|
if (data.statusCode) {
|
||||||
if (onError)
|
if (onError)
|
||||||
onError({ response: data })
|
onError({ response: data })
|
||||||
return
|
return
|
||||||
@ -115,7 +115,7 @@ export const useGameRepairMutation = ({ auth, onPostSuccess, onError }: GameProp
|
|||||||
}).then(resp => resp.json())
|
}).then(resp => resp.json())
|
||||||
},
|
},
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
if (data.error) {
|
if (data.statusCode) {
|
||||||
if (onError)
|
if (onError)
|
||||||
onError({ response: data })
|
onError({ response: data })
|
||||||
return
|
return
|
||||||
@ -142,7 +142,7 @@ export const useGameResetMutation = ({ auth, updateGameState, onPostSuccess, onE
|
|||||||
}).then(resp => resp.json())
|
}).then(resp => resp.json())
|
||||||
},
|
},
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
if (data.error) {
|
if (data.statusCode) {
|
||||||
if (onError)
|
if (onError)
|
||||||
onError({ response: data })
|
onError({ response: data })
|
||||||
return
|
return
|
||||||
|
@ -33,7 +33,7 @@ export const useLoginMutation = ({ authLogin, onPostSuccess, onError }: LoginPro
|
|||||||
},
|
},
|
||||||
networkMode: 'always',
|
networkMode: 'always',
|
||||||
onSuccess: async (data, { name, password }: LoginForm) => {
|
onSuccess: async (data, { name, password }: LoginForm) => {
|
||||||
if (data.error) {
|
if (data.statusCode) {
|
||||||
if (onError)
|
if (onError)
|
||||||
onError({ response: data })
|
onError({ response: data })
|
||||||
return
|
return
|
||||||
|
@ -33,7 +33,7 @@ export const useAddTrainMutation = ({ auth, onPostSuccess, onError }: TrainProps
|
|||||||
}).then(resp => resp.json())
|
}).then(resp => resp.json())
|
||||||
},
|
},
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
if (data.error) {
|
if (data.statusCode) {
|
||||||
if (onError)
|
if (onError)
|
||||||
onError({ response: data })
|
onError({ response: data })
|
||||||
return
|
return
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
import { useAppSelector } from "./useStore"
|
import { downloadTrains, TrainsPayload } from "@/utils/features/train/trainSlice"
|
||||||
|
import { useAppDispatch, useAppSelector } from "./useStore"
|
||||||
|
|
||||||
export const useTrain = () => useAppSelector((state) => state.train)
|
export const useTrain = () => useAppSelector((state) => state.train)
|
||||||
|
export const useDownloadTrains = () => {
|
||||||
|
const dispath = useAppDispatch()
|
||||||
|
return (trainsData: TrainsPayload) => dispath(downloadTrains(trainsData))
|
||||||
|
}
|
||||||
|
@ -20,6 +20,16 @@ const initialState: AuthState = {
|
|||||||
token: null,
|
token: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isAuthValid({ loggedIn, token }: AuthState): boolean {
|
||||||
|
if (!loggedIn || token === null)
|
||||||
|
return false
|
||||||
|
const arrayToken = token.split('.')
|
||||||
|
const tokenPayload = JSON.parse(atob(arrayToken[1]))
|
||||||
|
const expTime: number = tokenPayload.exp * 1000
|
||||||
|
const now: number = Math.floor(new Date().getTime())
|
||||||
|
return expTime >= now
|
||||||
|
}
|
||||||
|
|
||||||
export const authSlice = createSlice({
|
export const authSlice = createSlice({
|
||||||
name: 'auth',
|
name: 'auth',
|
||||||
initialState: initialState,
|
initialState: initialState,
|
||||||
|
@ -1,28 +1,4 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
export interface InterrailLeg {
|
|
||||||
infoJson?: string
|
|
||||||
info?: InterrailLegInfo
|
|
||||||
sortOrder: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailTravel {
|
|
||||||
date: string
|
|
||||||
infoJson?: string
|
|
||||||
info?: InterrailTravelInfo
|
|
||||||
from: string
|
|
||||||
to: string
|
|
||||||
type: number
|
|
||||||
legs: InterrailLeg[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailJourneyData {
|
|
||||||
travels: InterrailTravel[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailJourney {
|
|
||||||
data: InterrailJourneyData
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterrailTime {
|
export interface InterrailTime {
|
||||||
hours: number
|
hours: number
|
||||||
@ -85,6 +61,8 @@ export interface TrainTrip {
|
|||||||
to: string,
|
to: string,
|
||||||
departureTime: number,
|
departureTime: number,
|
||||||
arrivalTime: number,
|
arrivalTime: number,
|
||||||
|
infoJson?: string,
|
||||||
|
info?: InterrailLegInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TrainsState {
|
export interface TrainsState {
|
||||||
@ -95,13 +73,44 @@ const initialState: TrainsState = {
|
|||||||
trains: []
|
trains: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PaginationMeta {
|
||||||
|
currentPage: number
|
||||||
|
lastPage: number
|
||||||
|
nextPage: number
|
||||||
|
prevPage: number
|
||||||
|
total: number
|
||||||
|
totalPerPage: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrainsPayload {
|
||||||
|
data: TrainTrip[]
|
||||||
|
meta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
export const trainSlice = createSlice({
|
export const trainSlice = createSlice({
|
||||||
name: 'train',
|
name: 'train',
|
||||||
initialState: initialState,
|
initialState: initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
|
downloadTrains(state, action: PayloadAction<TrainsPayload>) {
|
||||||
|
if (state.trains)
|
||||||
|
state.trains = state.trains.filter(train => action.payload.data.filter(dlTrain => dlTrain.id === train.id) === null)
|
||||||
|
for (const dlTrain of action.payload.data) {
|
||||||
|
const info = dlTrain.infoJson ? JSON.parse(dlTrain.infoJson) : undefined
|
||||||
|
state.trains.push({
|
||||||
|
id: dlTrain.id,
|
||||||
|
distance: dlTrain.distance,
|
||||||
|
from: dlTrain.from,
|
||||||
|
to: dlTrain.to,
|
||||||
|
departureTime: dlTrain.departureTime,
|
||||||
|
arrivalTime: dlTrain.arrivalTime,
|
||||||
|
info: info,
|
||||||
|
})
|
||||||
|
state.trains.sort((t1, t2) => t1.departureTime > t2.departureTime ? -1 : t1.departureTime == t2.arrivalTime ? 0 : 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { } = trainSlice.actions
|
export const {downloadTrains } = trainSlice.actions
|
||||||
|
|
||||||
export default trainSlice.reducer
|
export default trainSlice.reducer
|
||||||
|
@ -37,8 +37,8 @@ export class GeolocationsController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOkResponsePaginated(GeolocationEntity)
|
@ApiOkResponsePaginated(GeolocationEntity)
|
||||||
async findAll(@Query() queryPagination?: QueryPaginationDto, @Query() playerFilter?: PlayerFilterDto): Promise<PaginateOutputDto<GeolocationEntity>> {
|
async findAll(@Query() queryPagination: QueryPaginationDto, @Query() { playerId }: PlayerFilterDto): Promise<PaginateOutputDto<GeolocationEntity>> {
|
||||||
const [geolocations, total] = await this.geolocationsService.findAll(queryPagination, playerFilter)
|
const [geolocations, total] = await this.geolocationsService.findAll(queryPagination, { playerId })
|
||||||
return paginateOutput<GeolocationEntity>(geolocations.map(geolocation => new GeolocationEntity(geolocation)), total, queryPagination)
|
return paginateOutput<GeolocationEntity>(geolocations.map(geolocation => new GeolocationEntity(geolocation)), total, queryPagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ export class MoneyUpdatesController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOkResponsePaginated(MoneyUpdateEntity)
|
@ApiOkResponsePaginated(MoneyUpdateEntity)
|
||||||
async findAll(@Query() queryPagination: QueryPaginationDto, @Query() playerFilter: PlayerFilterDto): Promise<PaginateOutputDto<MoneyUpdateEntity>> {
|
async findAll(@Query() queryPagination: QueryPaginationDto, @Query() { playerId }: PlayerFilterDto): Promise<PaginateOutputDto<MoneyUpdateEntity>> {
|
||||||
const [trains, total] = await this.moneyUpdatesService.findAll(queryPagination, playerFilter)
|
const [trains, total] = await this.moneyUpdatesService.findAll(queryPagination, { playerId })
|
||||||
return paginateOutput<MoneyUpdateEntity>(trains.map(train => new MoneyUpdateEntity(train)), total, queryPagination)
|
return paginateOutput<MoneyUpdateEntity>(trains.map(train => new MoneyUpdateEntity(train)), total, queryPagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ export class TrainsController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOkResponsePaginated(TrainEntity)
|
@ApiOkResponsePaginated(TrainEntity)
|
||||||
async findAll(@Query() queryPagination: QueryPaginationDto, @Query() playerFilter: PlayerFilterDto): Promise<PaginateOutputDto<TrainEntity>> {
|
async findAll(@Query() queryPagination: QueryPaginationDto, @Query() { playerId }: PlayerFilterDto): Promise<PaginateOutputDto<TrainEntity>> {
|
||||||
const [trains, total] = await this.trainsService.findAll(queryPagination, playerFilter)
|
const [trains, total] = await this.trainsService.findAll(queryPagination, { playerId })
|
||||||
return paginateOutput<TrainEntity>(trains.map(train => new TrainEntity(train)), total, queryPagination)
|
return paginateOutput<TrainEntity>(trains.map(train => new TrainEntity(train)), total, queryPagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user