From 47928ce9f2ac480a02cf11624ed0859ea4d76327 Mon Sep 17 00:00:00 2001 From: devthejo Date: Sat, 7 Mar 2026 20:55:37 +0100 Subject: [PATCH] fix: hide unavailable by default --- src/scenes/DAEList/Liste.js | 134 +++++++++++++++++++++++--- src/scenes/DAEList/useNearbyDefibs.js | 28 ++++-- src/stores/defibs.js | 5 + 3 files changed, 144 insertions(+), 23 deletions(-) diff --git a/src/scenes/DAEList/Liste.js b/src/scenes/DAEList/Liste.js index f6f102d..ba9c9b5 100644 --- a/src/scenes/DAEList/Liste.js +++ b/src/scenes/DAEList/Liste.js @@ -1,11 +1,12 @@ import React, { useCallback } from "react"; import { View, FlatList, StyleSheet } from "react-native"; -import { Button } from "react-native-paper"; +import { Button, Switch } from "react-native-paper"; import { MaterialCommunityIcons } from "@expo/vector-icons"; import Text from "~/components/Text"; import Loader from "~/components/Loader"; import { useTheme } from "~/theme"; +import { defibsActions } from "~/stores"; import useNearbyDefibs from "./useNearbyDefibs"; import DefibRow from "./DefibRow"; @@ -89,10 +90,81 @@ function EmptyNoResults() { const keyExtractor = (item) => item.id; +function EmptyNoAvailable({ showUnavailable }) { + const { colors } = useTheme(); + return ( + + + Aucun défibrillateur disponible + + Aucun défibrillateur actuellement ouvert dans un rayon de 10 km. Activez + l'option « Afficher les indisponibles » pour voir tous les + défibrillateurs. + + + ); +} + +function AvailabilityToggle({ showUnavailable, allCount, filteredCount }) { + const { colors } = useTheme(); + const onToggle = useCallback(() => { + defibsActions.setShowUnavailable(!showUnavailable); + }, [showUnavailable]); + + const countLabel = + !showUnavailable && allCount > filteredCount + ? ` (${allCount - filteredCount} masqués)` + : ""; + + return ( + + + + + Afficher les indisponibles{countLabel} + + + + + ); +} + export default React.memo(function DAEListListe() { const { colors } = useTheme(); - const { defibs, loading, error, noLocation, hasLocation, reload } = - useNearbyDefibs(); + const { + defibs, + allDefibs, + loading, + error, + noLocation, + hasLocation, + reload, + showUnavailable, + } = useNearbyDefibs(); const renderItem = useCallback(({ item }) => , []); @@ -102,23 +174,27 @@ export default React.memo(function DAEListListe() { } // Loading initial data - if (loading && defibs.length === 0) { + if (loading && allDefibs.length === 0) { return ; } // Error state (non-blocking if we have stale data) - if (error && defibs.length === 0) { + if (error && allDefibs.length === 0) { return ; } - // No results - if (!loading && defibs.length === 0 && hasLocation) { + // No results at all + if (!loading && allDefibs.length === 0 && hasLocation) { return ; } + // Has defibs but none available (filtered to empty) + const showEmptyAvailable = + !loading && defibs.length === 0 && allDefibs.length > 0 && !showUnavailable; + return ( - {error && defibs.length > 0 && ( + {error && allDefibs.length > 0 && ( )} - + {showEmptyAvailable ? ( + + ) : ( + + )} ); }); @@ -194,4 +279,21 @@ const styles = StyleSheet.create({ fontSize: 12, flex: 1, }, + toggleRow: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + paddingHorizontal: 16, + paddingVertical: 8, + borderBottomWidth: StyleSheet.hairlineWidth, + }, + toggleLabelContainer: { + flexDirection: "row", + alignItems: "center", + gap: 8, + flex: 1, + }, + toggleLabel: { + fontSize: 13, + }, }); diff --git a/src/scenes/DAEList/useNearbyDefibs.js b/src/scenes/DAEList/useNearbyDefibs.js index f0176da..ff46779 100644 --- a/src/scenes/DAEList/useNearbyDefibs.js +++ b/src/scenes/DAEList/useNearbyDefibs.js @@ -1,20 +1,24 @@ -import { useEffect, useRef, useCallback, useState } from "react"; +import { useEffect, useRef, useCallback, useMemo, useState } from "react"; import useLocation from "~/hooks/useLocation"; import { defibsActions, useDefibsState } from "~/stores"; +import { getDefibAvailability } from "~/utils/dae/getDefibAvailability"; const RADIUS_METERS = 10_000; /** * Shared hook: loads defibs near user and exposes location + loading state. * The results live in the zustand store so both Liste and Carte tabs share them. + * By default, only available (open) defibs are returned; toggle showUnavailable to see all. */ export default function useNearbyDefibs() { const { coords, isLastKnown, lastKnownTimestamp } = useLocation(); - const { nearUserDefibs, loadingNearUser, errorNearUser } = useDefibsState([ - "nearUserDefibs", - "loadingNearUser", - "errorNearUser", - ]); + const { nearUserDefibs, loadingNearUser, errorNearUser, showUnavailable } = + useDefibsState([ + "nearUserDefibs", + "loadingNearUser", + "errorNearUser", + "showUnavailable", + ]); const hasLocation = coords && coords.latitude !== null && coords.longitude !== null; @@ -58,8 +62,17 @@ export default function useNearbyDefibs() { return () => clearTimeout(timer); }, [hasLocation]); + const filteredDefibs = useMemo(() => { + if (showUnavailable) return nearUserDefibs; + return nearUserDefibs.filter((d) => { + const { status } = getDefibAvailability(d.horaires_std, d.disponible_24h); + return status === "open"; + }); + }, [nearUserDefibs, showUnavailable]); + return { - defibs: nearUserDefibs, + defibs: filteredDefibs, + allDefibs: nearUserDefibs, loading: loadingNearUser, error: errorNearUser, hasLocation, @@ -68,5 +81,6 @@ export default function useNearbyDefibs() { lastKnownTimestamp, coords, reload: loadDefibs, + showUnavailable, }; } diff --git a/src/stores/defibs.js b/src/stores/defibs.js index 3af6b2f..6417823 100644 --- a/src/stores/defibs.js +++ b/src/stores/defibs.js @@ -26,6 +26,10 @@ export default createAtom(({ merge, reset }) => { merge({ showDaeSuggestModal }); }, + setShowUnavailable: (showUnavailable) => { + merge({ showUnavailable }); + }, + loadNearUser: async ({ userLonLat, radiusMeters = DEFAULT_NEAR_USER_RADIUS_M, @@ -103,6 +107,7 @@ export default createAtom(({ merge, reset }) => { showDefibsOnAlertMap: false, selectedDefib: null, showDaeSuggestModal: false, + showUnavailable: false, loadingNearUser: false, loadingCorridor: false,