From 7c4788afbacb1d8f89caadd5e2c1bf713ed665aa Mon Sep 17 00:00:00 2001 From: Admin <1+admin@noreply.git.dcntrlized.org> Date: Mon, 19 Oct 2026 02:34:20 +0100 Subject: [PATCH] [feat](client): surface expiry, endings, and device health in the app The alert detail screen now creates with a lifetime (1/3/7/14 days) and shows expiresAt, lists subscriber rows by their non-identifying fields, and renders each ending (delivered, expired, disarmed, deleted) as a terminal state instead of a stale alert. Settings configures the single instance origin and reports the real readiness/listening checklist; the trigger pad keeps its 1200ms hold, and time helpers format the deadline and last-seen values. --- src/components/TriggerPad.tsx | 47 +++++- src/lib/time.ts | 28 ++++ src/screens/AlertDetailScreen.tsx | 254 +++++++++++++++++++++++------- src/screens/HomeScreen.tsx | 39 ++++- src/screens/LiveAlarmScreen.tsx | 10 +- src/screens/SettingsScreen.tsx | 184 +++++++++++++--------- 6 files changed, 421 insertions(+), 141 deletions(-) diff --git a/src/components/TriggerPad.tsx b/src/components/TriggerPad.tsx index 0498e4a..7ac50af 100644 --- a/src/components/TriggerPad.tsx +++ b/src/components/TriggerPad.tsx @@ -92,6 +92,7 @@ export function TriggerPad({ confirmTitle, }: TriggerPadProps) { const [confirming, setConfirming] = useState(false); + const [confirmingDisarm, setConfirmingDisarm] = useState(false); const [width, setWidth] = useState(0); const fill = useRef(new Animated.Value(0)).current; const scale = useRef(new Animated.Value(1)).current; @@ -179,12 +180,52 @@ export function TriggerPad({ label="Disarm" variant="secondary" height={56} - onPress={onDisarm} + onPress={() => setConfirmingDisarm(true)} disabled={disabled} icon={OctagonX} - accessibilityLabel="Disarm the alarm on every device" + accessibilityLabel="Disarm the alarm and end this alert" /> - Disarm stops the alarm on every device. One tap, no confirm. + + Disarm stops the alarm for everyone and ends the alert. + + + {/* Disarm is destructive now: a triggered alert is destroyed, and an armed + one is destroyed too, so it always takes a confirmation. */} + setConfirmingDisarm(false)} + statusBarTranslucent + > + + + + {ringing ? 'Disarm and end this alert?' : 'Delete this alert by disarming?'} + + + {ringing + ? 'Disarm stops the alarm for everyone and ends the alert. It cannot be triggered again.' + : 'Disarming deletes this alert for every subscriber — they will not be able to join again, and the QR code stops working.'} + +