diff --git a/client/app/(tabs)/challenges.tsx b/client/app/(tabs)/challenges.tsx
index 543fad9..bcc0fa3 100644
--- a/client/app/(tabs)/challenges.tsx
+++ b/client/app/(tabs)/challenges.tsx
@@ -5,7 +5,7 @@ import { useAuth } from '@/hooks/useAuth'
import { useChallengeActions } from '@/hooks/useChallengeActions'
import { useChallenges } from '@/hooks/useChallenges'
import { useGame } from '@/hooks/useGame'
-import { FontAwesome6 } from '@expo/vector-icons'
+import { FontAwesome6, MaterialCommunityIcons } from '@expo/vector-icons'
import { useQueryClient } from '@tanstack/react-query'
import { useRouter } from 'expo-router'
import { useEffect, useMemo, useState } from 'react'
@@ -91,10 +91,11 @@ function ChallengeScreenBody() {
style={{ flex: 1, margin: 20 }} />}
{!loading && !game.penaltyEnd && !currentChallenge && game.currentRunner && <>
- Aucun défi n'est en cours.
+ icon='vanish'>
+ Aucun défi n'est en cours. Veuillez tirer un défi en cliquant sur le bouton central.
+ Pour rappel, il faut être hors d'un train pour tirer un défi.
{
setSuccessVisible(true)
setSuccessMessage("Réparation du jeu effectuée avec succès")
diff --git a/client/app/challenges-list.tsx b/client/app/challenges-list.tsx
index e4c96cd..bef8b36 100644
--- a/client/app/challenges-list.tsx
+++ b/client/app/challenges-list.tsx
@@ -1,20 +1,29 @@
import ChallengeCard from "@/components/ChallengeCard"
-import { useAddChallengeMutation, useDeleteChallengeMutation, useEditChallengeMutation } from "@/hooks/mutations/useChallengeMutation"
+import { useAddChallengeMutation, useAttachNewChallenge, useDeleteChallengeMutation, useEditChallengeMutation } from "@/hooks/mutations/useChallengeMutation"
import { useAuth } from "@/hooks/useAuth"
+import { useChallengeActions } from "@/hooks/useChallengeActions"
import { useChallenges } from "@/hooks/useChallenges"
+import { useGame } from "@/hooks/useGame"
import { Challenge } from "@/utils/features/challenges/challengesSlice"
import { FontAwesome6 } from "@expo/vector-icons"
import { useQueryClient } from "@tanstack/react-query"
import { useRouter } from "expo-router"
-import { useState } from "react"
+import React, { ReactNode, useMemo, useState } from "react"
import { FlatList, StyleSheet } from "react-native"
-import { Appbar, Button, Dialog, Divider, FAB, List, MD3Colors, Modal, Portal, Snackbar, Surface, Text, TextInput } from "react-native-paper"
+import { ActivityIndicator, Appbar, Button, Dialog, Divider, FAB, List, MD3Colors, Modal, Portal, Snackbar, Surface, Text, TextInput, Tooltip } from "react-native-paper"
export default function ChallengesList() {
const router = useRouter()
const queryClient = useQueryClient()
const auth = useAuth()
+ const game = useGame()
const challenges = useChallenges()
+ const challengeActions = useChallengeActions()
+ const currentChallengeAction = useMemo(() => {
+ if (!game.activeChallengeId)
+ return null
+ return challengeActions.find((action) => action.id === game.activeChallengeId)
+ }, [game, challengeActions])
const [editChallengeVisible, setEditChallengeVisible] = useState(false)
const [editChallengeTitle, setEditChallengeTitle] = useState("")
@@ -23,6 +32,8 @@ export default function ChallengesList() {
const [editChallengeId, setEditChallengeId] = useState(null)
const [displayedChallenge, setDisplayedChallenge] = useState(null)
const [confirmDeletedVisible, setConfirmDeleteVisible] = useState(false)
+ const [challengeToAttach, setChallengeToAttach] = useState(null)
+
const [successSnackbarVisible, setSuccessSnackbarVisible] = useState(false)
const [successMessage, setSuccessMessage] = useState("")
const [errorVisible, setErrorVisible] = useState(false)
@@ -80,6 +91,22 @@ export default function ChallengesList() {
setError([400, error.message])
},
})
+ const attachNewChallengeMutation = useAttachNewChallenge({
+ auth,
+ onPostSuccess: () => {
+ setChallengeToAttach(null)
+ setSuccessMessage("Le défi en cours a bien été modifié !")
+ queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === 'get-challenges' })
+ },
+ onError: ({ response, error }) => {
+ setChallengeToAttach(null)
+ setErrorVisible(true)
+ if (response)
+ setError([response.statusCode, response.message])
+ else if (error)
+ setError([400, error.message])
+ },
+ })
function sendEditChallenge() {
if (editChallengeId) {
@@ -103,17 +130,26 @@ export default function ChallengesList() {
displayedChallenge && deleteChallengeMutation.mutate(displayedChallenge)
}
+ function sendAttachNewChallenge() {
+ if (!challengeToAttach || !currentChallengeAction) return
+ attachNewChallengeMutation.mutate({ challengeActionId: currentChallengeAction.id, newChallengeId: challengeToAttach.id })
+ }
+
return (
- {router.canGoBack() ? router.back()} /> : undefined}
+ router.canGoBack() ? router.back() : router.navigate('/(tabs)/challenges')} />
`challenge-list-item-${challenge.id}`}
ItemSeparatorComponent={() => }
- renderItem={(item) => setDisplayedChallenge(item.item)} />} />
+ renderItem={(item) =>
+ setDisplayedChallenge(item.item)}
+ onLongPress={!currentChallengeAction ? undefined : () => setChallengeToAttach(item.item)} />} />
Confirmer
+
)
}
-function ChallengeListItem({ challenge, onPress }: { challenge: Challenge, onPress?: () => void }) {
+type ChallengeListItemProps = {
+ challenge: Challenge,
+ activeRunner: boolean,
+ onPress?: () => void,
+ onLongPress?: () => void,
+}
+
+function ChallengeListItem({ challenge, activeRunner, onPress, onLongPress }: ChallengeListItemProps) {
+ const challengeActions = useChallengeActions()
+ const attachedAction = challengeActions.find(challengeAction => challengeAction.challengeId === challenge.id)
const description = Récompense : {challenge.reward}
+ const icon: ReactNode = useMemo(() => {
+ if (!activeRunner)
+ return undefined
+ else if (!attachedAction)
+ return
+ else if (!attachedAction.end)
+ return
+ else if (attachedAction.success)
+ return
+ else
+ return
+ }, [activeRunner, attachedAction])
return (
+ right={() => icon}
+ onPress={onPress}
+ onLongPress={attachedAction ? undefined : onLongPress} />
)
}
diff --git a/client/hooks/mutations/useChallengeMutation.ts b/client/hooks/mutations/useChallengeMutation.ts
index baa6cb8..a930ec0 100644
--- a/client/hooks/mutations/useChallengeMutation.ts
+++ b/client/hooks/mutations/useChallengeMutation.ts
@@ -81,6 +81,36 @@ export const useEndChallenge = ({ auth, onPostSuccess, onError }: ChallengeActio
})
}
+export const useAttachNewChallenge = ({ auth, onPostSuccess, onError }: ChallengeActionProps) => {
+ return useMutation({
+ mutationFn: async ({ challengeActionId, newChallengeId }: { challengeActionId: number, newChallengeId: number }) => {
+ return fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/challenge-actions/${challengeActionId}/`, {
+ method: "PATCH",
+ headers: {
+ "Authorization": `Bearer ${auth.token}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ challengeId: newChallengeId,
+ })
+ }).then(resp => resp.json())
+ },
+ onSuccess: async (data) => {
+ if (data.statusCode) {
+ if (onError)
+ onError({ response: data })
+ return
+ }
+ if (onPostSuccess)
+ onPostSuccess()
+ },
+ onError: async (error: Error) => {
+ if (onError)
+ onError({ error: error })
+ }
+ })
+}
+
export const useDeleteChallengeActionMutation = ({ auth, onPostSuccess, onError }: ChallengeActionProps) => {
return useMutation({
mutationFn: async (challengeActionId: number) => {
diff --git a/client/utils/features/challenges/challengesSlice.ts b/client/utils/features/challenges/challengesSlice.ts
index 06e91a8..23f80b7 100644
--- a/client/utils/features/challenges/challengesSlice.ts
+++ b/client/utils/features/challenges/challengesSlice.ts
@@ -37,7 +37,7 @@ export const challengesSlice = createSlice({
reward: dlChallenge.reward,
})
}
- state.challenges.sort((c1, c2) => c2.id - c1.id)
+ state.challenges.sort((c1, c2) => c1.title.localeCompare(c2.title))
},
},
})