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,