diff --git a/package.json b/package.json index 875e08a..4a445bb 100644 --- a/package.json +++ b/package.json @@ -279,4 +279,4 @@ } }, "packageManager": "yarn@4.5.3" -} \ No newline at end of file +} diff --git a/src/hooks/useRadarData.js b/src/hooks/useRadarData.js new file mode 100644 index 0000000..5de2453 --- /dev/null +++ b/src/hooks/useRadarData.js @@ -0,0 +1,27 @@ +import { useLazyQuery } from "@apollo/client"; +import { useCallback } from "react"; +import { RADAR_PEOPLE_COUNT_QUERY } from "~/scenes/SendAlert/gql"; + +export default function useRadarData() { + const [fetchRadarData, { data, loading: isLoading, error }] = useLazyQuery( + RADAR_PEOPLE_COUNT_QUERY, + { + fetchPolicy: "network-only", // Always fetch fresh data + errorPolicy: "all", + }, + ); + + const reset = useCallback(() => { + // Reset is handled by not calling the query again + // Apollo will manage the state internally + }, []); + + return { + data: data?.getOneRadarPeopleCount, + isLoading, + error, + fetchRadarData, + reset, + hasLocation: true, // Location is now handled server-side via authentication + }; +} diff --git a/src/scenes/SendAlert/NotificationsButton.js b/src/scenes/SendAlert/NotificationsButton.js index 5a97269..19299f4 100644 --- a/src/scenes/SendAlert/NotificationsButton.js +++ b/src/scenes/SendAlert/NotificationsButton.js @@ -6,7 +6,7 @@ import { createStyles } from "~/theme"; import Text from "~/components/Text"; import { MaterialIcons } from "@expo/vector-icons"; -export default function NotificationsButton() { +export default function NotificationsButton({ flex = 1 }) { const navigation = useNavigation(); const { hasRegisteredRelatives } = useParamsState(["hasRegisteredRelatives"]); const { newCount } = useNotificationsState(["newCount"]); @@ -50,9 +50,11 @@ export default function NotificationsButton() { const styles = useStyles(); return ( - + navigation.navigate("Notifications")} > ({ button: { flexDirection: "row", alignItems: "center", - alignSelf: "flex-end", + alignSelf: "stretch", marginTop: 10, marginBottom: 10, backgroundColor: colors.surface, borderRadius: 8, - width: "100%", + flex: 1, + minHeight: 48, // Consistent with RadarButton for accessibility paddingVertical: 12, paddingHorizontal: 16, shadowColor: colors.text, diff --git a/src/scenes/SendAlert/RadarButton.js b/src/scenes/SendAlert/RadarButton.js new file mode 100644 index 0000000..605c223 --- /dev/null +++ b/src/scenes/SendAlert/RadarButton.js @@ -0,0 +1,57 @@ +import React from "react"; +import { View } from "react-native"; +import { IconButton } from "react-native-paper"; +import { MaterialCommunityIcons } from "@expo/vector-icons"; +import { createStyles } from "~/theme"; + +export default function RadarButton({ onPress, isLoading = false, flex = 0.22 }) { + const styles = useStyles(); + + return ( + + ( + + )} + /> + + ); +} + +const useStyles = createStyles(({ wp, hp, theme: { colors, custom } }) => ({ + container: { + alignItems: "center", + justifyContent: "center", + marginTop: 10, + marginBottom: 10, + flex: 1, // Stretch to fill available space + }, + button: { + backgroundColor: colors.primary, + elevation: 4, + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 3.84, + minHeight: 48, // Match minimum touch target height + width: "100%", + maxWidth: 48, // Keep it square for radar button + }, + icon: { + color: colors.onPrimary, + }, +})); diff --git a/src/scenes/SendAlert/RadarModal.js b/src/scenes/SendAlert/RadarModal.js new file mode 100644 index 0000000..c56309d --- /dev/null +++ b/src/scenes/SendAlert/RadarModal.js @@ -0,0 +1,176 @@ +import React from "react"; +import { View, Text } from "react-native"; +import { Modal, Portal, Button, ActivityIndicator } from "react-native-paper"; +import { MaterialCommunityIcons } from "@expo/vector-icons"; +import { createStyles, useTheme } from "~/theme"; + +export default function RadarModal({ + visible, + onDismiss, + peopleCount = null, + isLoading = false, + error = null, +}) { + const { colors } = useTheme(); + const styles = useStyles(); + + const renderContent = () => { + if (isLoading) { + return ( + + + + Recherche d'utilisateurs Alerte-Secours disponibles aux alentours... + + + ); + } + + if (error) { + return ( + + + Erreur + + Impossible de récupérer les informations. Vérifiez votre connexion + et votre localisation. + + + ); + } + + if (peopleCount !== null) { + return ( + + {peopleCount} + + {peopleCount === 0 + ? "Aucun utilisateur d'Alerte-Secours disponible pour assistance dans un rayon de 25 km" + : peopleCount === 1 + ? "utilisateur d'Alerte-Secours prêt à porter secours dans un rayon de 25 km" + : "utilisateurs d'Alerte-Secours prêts à porter secours dans un rayon de 25 km"} + + + ); + } + + return null; + }; + + return ( + + + + + Utilisateurs aux alentours + + + + {renderContent()} + + + + + + + + ); +} + +const useStyles = createStyles(({ wp, hp, scaleText, theme: { colors } }) => ({ + modalContainer: { + margin: wp(5), + borderRadius: 8, + padding: wp(5), + elevation: 5, + }, + content: { + alignItems: "center", + }, + loadingContainer: { + alignItems: "center", + paddingVertical: hp(3), + }, + loadingText: { + ...scaleText({ fontSize: 16 }), + color: colors.onSurface, + marginTop: hp(2), + }, + errorContainer: { + alignItems: "center", + paddingVertical: hp(2), + }, + errorTitle: { + ...scaleText({ fontSize: 18 }), + fontWeight: "bold", + color: colors.error, + marginTop: hp(1), + marginBottom: hp(1), + }, + errorText: { + ...scaleText({ fontSize: 14 }), + color: colors.onSurface, + textAlign: "center", + lineHeight: 20, + }, + successContainer: { + alignItems: "center", + paddingVertical: hp(2), + }, + countText: { + ...scaleText({ fontSize: 36 }), + fontWeight: "bold", + color: colors.primary, + marginTop: hp(1), + }, + descriptionText: { + ...scaleText({ fontSize: 16 }), + color: colors.onSurface, + textAlign: "center", + marginTop: hp(1), + }, + buttonContainer: { + marginTop: hp(3), + width: "100%", + }, + closeButton: { + borderRadius: 8, + }, + modalHeader: { + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + marginBottom: hp(2), + }, + modalIcon: { + marginRight: wp(2), + }, + modalTitle: { + ...scaleText({ fontSize: 20 }), + fontWeight: "bold", + color: colors.onSurface, + textAlign: "center", + }, +})); diff --git a/src/scenes/SendAlert/TopButtonsBar.js b/src/scenes/SendAlert/TopButtonsBar.js new file mode 100644 index 0000000..67a9c0d --- /dev/null +++ b/src/scenes/SendAlert/TopButtonsBar.js @@ -0,0 +1,21 @@ +import React from "react"; +import { View } from "react-native"; +import { createStyles } from "~/theme"; + +export default function TopButtonsBar({ children }) { + const styles = useStyles(); + + return {children}; +} + +const useStyles = createStyles(({ wp, hp }) => ({ + container: { + flexDirection: "row", + alignItems: "stretch", // Ensures both buttons have same height + justifyContent: "space-between", + marginTop: hp(1), + marginBottom: hp(1), + gap: wp(3), // Slightly more space between the buttons + minHeight: 48, // Minimum touch target height for accessibility + }, +})); \ No newline at end of file diff --git a/src/scenes/SendAlert/gql.js b/src/scenes/SendAlert/gql.js new file mode 100644 index 0000000..351a6e3 --- /dev/null +++ b/src/scenes/SendAlert/gql.js @@ -0,0 +1,9 @@ +import { gql } from "@apollo/client"; + +export const RADAR_PEOPLE_COUNT_QUERY = gql` + query radarPeopleCount { + getOneRadarPeopleCount { + count + } + } +`; diff --git a/src/scenes/SendAlert/index.js b/src/scenes/SendAlert/index.js index 75790a7..72eae35 100644 --- a/src/scenes/SendAlert/index.js +++ b/src/scenes/SendAlert/index.js @@ -13,6 +13,10 @@ import HelpBlock from "./HelpBlock"; import RegisterRelativesButton from "./RegisterRelativesButton"; import NotificationsButton from "./NotificationsButton"; import ContributeButton from "./ContributeButton"; +import RadarButton from "./RadarButton"; +import RadarModal from "./RadarModal"; +import TopButtonsBar from "./TopButtonsBar"; +import useRadarData from "~/hooks/useRadarData"; export default function SendAlert() { const navigation = useNavigation(); @@ -20,10 +24,35 @@ export default function SendAlert() { const styles = useStyles(); const [helpVisible, setHelpVisible] = useState(false); + const [radarModalVisible, setRadarModalVisible] = useState(false); + + const { + data: radarData, + isLoading: radarIsLoading, + error: radarError, + fetchRadarData, + reset: resetRadarData, + hasLocation, + } = useRadarData(); + function toggleHelp() { setHelpVisible(!helpVisible); } + const handleRadarPress = useCallback(() => { + if (!hasLocation) { + // Could show a location permission alert here + return; + } + setRadarModalVisible(true); + fetchRadarData(); + }, [hasLocation, fetchRadarData]); + + const handleRadarModalClose = useCallback(() => { + setRadarModalVisible(false); + resetRadarData(); + }, [resetRadarData]); + const navigateTo = useCallback( (navOpts) => navigation.dispatch({ @@ -70,7 +99,14 @@ export default function SendAlert() { return ( - + + + + Quelle est votre situation ? @@ -218,6 +254,14 @@ export default function SendAlert() { + + );