17 KiB
DAE / Defibrillator integration plan (agent-splittable)
Common plan prefix (include this at the top of every coding-agent prompt)
Product goal
Integrate defibrillator (DAE) discovery into the app using the embedded SQLite DB query helper getNearbyDefibs(). Provide:
- A new left-drawer link to a new view
DAEList. - In an active alert
Situationview, a buttonAfficher les défibrillateursthat:- enables DAE display around the 10km corridor around the segment between user location and alert location,
- then navigates to the alert map.
- In alert map, render DAE markers; tapping a DAE opens
DAEItem. DAEListscreen with bottom navigation:Liste(default) andCarte, showing defibs nearest→farthest around the user, within 10km.DAEItemscreen with bottom navigation:Infos(default) and a map/itinerary to reach the selected DAE (mimic alert routing).- During alert posting, if a cardiac-related keyword is detected in the alert subject, show a persistent modal (must remain on top even after redirect to the alert view, and must work offline) with two actions:
Chercher un défibrillateur→ go toDAEListNon merci→ dismiss
v1 decisions already made
- Availability filter: only use
disponible_24h === 1for now; no parsing ofhorairesstring yet (later iteration). - Corridor filter: within 10km of the user↔alert segment (not union of circles).
- Location permission denied: use last-known location; if none, show an explanatory empty state (no hard block).
Known architecture + relevant anchors in codebase
- Defib query wrapper:
src/data/getNearbyDefibs.jsexportsgetNearbyDefibs()which calls repo H3 query and falls back to bbox on error. - Repo query + distance rank:
src/db/defibsRepo.jsprovidesgetNearbyDefibs()andgetNearbyDefibsBbox(). - Embedded DB bootstrap:
getDb()expects a bundled assetrequire('../assets/db/geodae.db')insrc/db/openDb.js. Note: current repo listing showssrc/assets/db/empty, so packaging must be validated. - Drawer navigation screens declared in
src/navigation/Drawer.js. - Root stack only defines
MainandConnectivityErrorinsrc/navigation/RootStack.js. Drawer contains “hidden” stack-like screens (e.g.SendAlertConfirm) already. - Alert current tabs:
Situation,Messages,Carteinsrc/scenes/AlertCur/Tabs.js. - Alert map uses MapLibre and has route computation (OSRM) already in
src/scenes/AlertCurMap/index.js. - Map features clustering uses Supercluster in
useFeatures()and map press routing inuseOnPress(). - Persistent top-layer UI is implemented elsewhere via
react-native-paperPortal+Modal. - Alert posting flow navigates to
AlertCurOverviewinonSubmit()afteralertActions.setNavAlertCur()insrc/scenes/SendAlertConfirm/useOnSubmit.js.
Data model (canonical Defib object for UI)
Base DB row shape (from repo select) is:
id: stringlatitude: numberlongitude: numbernom: stringadresse: stringhoraires: string(unused in v1)acces: stringdisponible_24h: 0|1- plus computed
distanceMeters: number
Represent coordinates consistently as:
- Map coordinates arrays
[lon, lat]to match existing map usage (seealert.location.coordinatesinsrc/scenes/AlertCurMap/index.js).
Filtering logic requirements
Near-user list (DAEList):
- Input: user location (current if available, else last-known)
- Filter: distance ≤ 10_000m
- Sort: nearest→farthest
- Availability: if
disponible_24h === 1keep; else exclude in v1 (until horaires parsing)
Alert-axis corridor overlay (Alert map + Situation button):
- Input: user location + alert location
- Filter: points within
corridorMeters = 10_000of the line segment user→alert - Also restrict to a sensible max radius around user to limit query size (see “Query strategy” below)
Corridor math recommendation:
- Use existing Turf dependency already present in map stack:
@turf/helperslineString(),@turf/nearest-point-on-linenearestPointOnLine(), and a distance function (geolibor Turfdistance).
Query strategy (performance + offline)
Use the existing local SQLite query API (no network) via getNearbyDefibs().
- For near-user list: query radiusMeters =
10_000, limit = e.g. 200–500 (tune later). - For corridor overlay:
- First query a radius around the midpoint or around user large enough to include the whole segment + corridor.
- Practical v1 approach: compute
segmentLengthMetersand query radius =segmentLengthMeters/2 + corridorMetersaround the midpoint. - Then apply corridor filter in JS and cap results to a max marker count (e.g. 200) to keep map responsive.
Navigation and UX conventions
- Drawer items come from
<Drawer.Screen>options insrc/navigation/Drawer.jsand are rendered bymenuItem(). Hidden routes should setoptions.hidden = true. - Bottom tab patterns exist (see alert tabs in
src/scenes/AlertCur/Tabs.js). - For “persistent modal on top even after redirect”, implement modal at a global provider level (within
LayoutProviderstree) usingPortalso it survives navigation.
Split tasks (agent-ready prompts)
Each task below is designed to be handed to a coding agent. Include the Common plan prefix above in every prompt.
Task 1 — Validate embedded DB asset packaging and repo schema assumptions
Objective: Ensure the bundled SQLite geodae.db is present and accessible on-device, and confirm schema columns used by defibsRepo exist.
Implementation notes:
initDb()copiesrequire('../assets/db/geodae.db')to documents.- Current workspace shows
src/assets/db/empty; find where DB is stored or add it.
Acceptance criteria:
- App can open DB without throwing at
require('../assets/db/geodae.db'). - Query
SELECT ... FROM defibsworks with columns:id, latitude, longitude, nom, adresse, horaires, acces, disponible_24handh3.
Likely files touched:
src/db/openDb.js- DB asset under
src/assets/db/ - Possibly Expo config / bundling rules (if needed)
Task 2 — Define and implement defib filtering utilities (10km near-user + 10km corridor)
Objective: Create pure utility functions to:
- compute query radius for corridor overlay
- filter a list of defibs to those inside corridor
- normalize coordinates and compute distances
Implementation notes:
- Prefer reusing Turf already used in map stack (see imports in
src/scenes/AlertCurMap/index.js). - Keep utilities side-effect free and unit-testable.
Suggested exports:
computeCorridorQueryRadiusMeters({ userLonLat, alertLonLat, corridorMeters })filterDefibsInCorridor({ defibs, userLonLat, alertLonLat, corridorMeters })toLonLat({ latitude, longitude })
Acceptance criteria:
- Given synthetic points, corridor filter behaves as “distance to segment ≤ 10km”.
Likely files touched:
- New:
src/utils/geo/defibsCorridor.js - Possibly reuse existing
haversinelogic as reference
Task 3 — Add a Defibs store (zustand atom) to manage caching + overlay enablement + modal state
Objective: Add centralized state to avoid repeated DB queries and to coordinate UI across screens.
State concerns:
- cached near-user defibs list
- cached corridor defibs for current alert id
- flag
showDefibsOnAlertMap(ordefibsOverlayEnabledByAlertId) - selected defib id for
DAEItem - “DAE suggestion modal” visibility (global)
Integration points:
- Mirror patterns used by alert store in
createAtom().
Acceptance criteria:
- Other tasks can simply call actions like
defibsActions.loadNearUser()anddefibsActions.enableCorridorOverlay(alertId).
Likely files touched:
- New:
src/stores/defibs.js - Update exports/hooks in
src/stores/index.js
Task 4 — Add navigation routes: DAEList in drawer + DAEItem as hidden route
Objective: Add new screens to navigation so they can be opened from:
- left drawer (DAEList)
- map marker press (DAEItem)
- modal CTA (DAEList)
Implementation notes:
- Drawer routes are defined in
src/navigation/Drawer.js. - If
DAEItemis a detail view, setoptions.hidden = true(see existing hidden screens aroundSendAlertConfirm). - Decide where to place the DAE link in drawer sections:
- Sections are sliced by indices in
src/navigation/DrawerNav/DrawerItemList.js. Adding a new Drawer.Screen will shift indices; adjustindex1/index2or reorder screens.
- Sections are sliced by indices in
Acceptance criteria:
- A new drawer link navigates to
DAEList. DAEItemroute can be navigated to programmatically.
Likely files touched:
Task 5 — Situation button on active alert: enable DAE overlay and navigate to alert map
Objective: In Situation view for current alert, add button Afficher les défibrillateurs.
Behavior:
- Determine user coords (prefer current; fall back to last-known as in alert map’s
useLocationusage insrc/scenes/AlertCurMap/index.js). - Query defibs from DB with a computed radius around midpoint.
- Filter to corridor (10km).
- Store result + enable overlay flag.
- Navigate to map tab:
Main → AlertCur → AlertCurTab → AlertCurMap(same pattern used inAlertCurOverview).
Acceptance criteria:
- Button exists only when alert has coordinates.
- After tap, map opens and defib markers appear.
Likely files touched:
src/scenes/AlertCurOverview/index.js- Store from Task 3
- Utilities from Task 2
Task 6 — Alert map: render DAE markers and open DAEItem on tap
Objective: Display defib markers as a separate feature layer on alert map.
Implementation notes:
- Existing feature system creates a GeoJSON FeatureCollection for alerts in
useFeatures(). - Add DAE features when overlay is enabled.
- Add a new marker icon to map images by extending
FeatureImages. - Update press handler in
useOnPress()to recognizeproperties.defiband navigate toDAEItem.
Acceptance criteria:
- Markers appear/disappear based on overlay flag.
- Tapping a marker navigates to
DAEItem. - Clustering behavior is acceptable (either include in cluster or keep separate; v1 can skip clustering by rendering as a separate layer).
Likely files touched:
src/scenes/AlertCurMap/useFeatures.jssrc/scenes/AlertCurMap/useOnPress.jssrc/containers/Map/FeatureImages.js
Task 7 — Implement DAEList screen with bottom tabs: List (default) + Map
Objective: Create DAEList scene with bottom navigation and two tabs:
Liste: list view nearest→farthestCarte: map view of same defibs
Behavior:
- Load near-user defibs within 10km using
getNearbyDefibs(). - Filter by availability: keep
disponible_24h === 1only. - If no location available, show empty state with explanation.
- Tapping list item navigates to
DAEItem.
Implementation notes:
- Follow bottom tab pattern from
createBottomTabNavigator().
Acceptance criteria:
- Drawer link opens
DAEList. - List is sorted by
distanceMeters. - Map tab renders markers.
Likely files touched:
- New:
src/scenes/DAEList/index.js - New:
src/scenes/DAEList/Tabs.js - Possibly shared list row component
Task 8 — Implement DAEItem screen with bottom tabs: Infos (default) + Go-to map (itinerary)
Objective: Create DAEItem detail view for a selected defib.
Tabs:
Infos: name, address, access, availability badgeCarte: show route from user to DAE, mimicking alert routing implementation inAlertCurMap
Implementation notes:
- Reuse route calculation patterns (OSRM URL building, polyline decode, step list components) from alert map.
- Route target is defib coordinates instead of alert coordinates.
Acceptance criteria:
- From any entrypoint (alert map marker or list),
DAEItemshows correct defib. - Itinerary works when online; offline behavior is a clear message (route unavailable offline).
Likely files touched:
Task 9 — Keyword detection while posting an alert + persistent DAE suggestion modal
Objective: Detect cardiac-related terms during alert posting and display a global modal that remains visible even after navigation.
Detection requirements:
- Keywords include:
cardiaque,cardiac(typos),coeur,malaise,inconscient,évanouietc. - Should run locally/offline.
Implementation approach (recommended):
- Use fuzzy matching with
Fuse(already used infindAlertTitle()) or implement a lightweight normalization + substring/levenshtein. - Trigger detection in the confirm submit flow, before/around navigation in
onSubmit(). - Render modal at app root using
react-native-paperPortalinsideLayoutProvidersso it persists across navigation.
Modal UI:
- Title/text: explain quickly why looking for a DAE matters.
- Two buttons:
Chercher un défibrillateur→ navigate toDAEListNon merci→ dismiss
Acceptance criteria:
- Modal shows for matching terms even with no internet.
- Modal stays visible after redirect to current alert view.
Likely files touched:
src/scenes/SendAlertConfirm/useOnSubmit.js- New:
src/containers/DAESuggestModal/index.js src/layout/LayoutProviders.js- Store from Task 3
Task 10 — Verification checklist (manual) + minimal automated coverage
Objective: Provide a deterministic checklist and (where feasible) simple automated tests.
Manual verification checklist:
- Drawer: DAE link visible, opens list.
- DAEList:
- permission granted → list populated, sorted
- permission denied + last-known available → list uses last-known
- permission denied + no last-known → empty state
- Alert Situation button: enables overlay and opens alert map.
- Alert map: DAE markers render; tap → DAEItem.
- DAEItem routing: online route works; offline shows message.
- Keyword modal:
- trigger term in subject → modal shows
- redirect to alert occurs and modal remains on top
- CTA navigates to DAEList
Acceptance criteria:
- Checklist documented and reproducible.
Likely files touched:
- New:
plans/DAE-manual-test-checklist.md - Optional: e2e tests under
e2e/
Mermaid overview
flowchart TD
A[User posts alert] --> B[Keyword detection]
B -->|match| M[Show persistent DAE modal]
B -->|no match| C[Navigate to AlertCur Situation]
M --> C
M -->|Chercher un defibrillateur| L[DAEList]
M -->|Non merci| C
C --> S[Button Afficher les defibrillateurs]
S --> E[Enable overlay in store]
E --> MAP[AlertCurMap]
MAP -->|tap DAE marker| ITEM[DAEItem]
L -->|tap list item| ITEM