feat(app): sensor lifecycle center with expiry/offboarding - #14
Open
shroominic wants to merge 1 commit into
Open
feat(app): sensor lifecycle center with expiry/offboarding#14shroominic wants to merge 1 commit into
shroominic wants to merge 1 commit into
Conversation
Add a self-contained "sensor lifecycle center" card to the dashboard covering the sensor's full 15-day life cycle, derived purely from the session timing (sessionStart / warmupMinutes) plus expiry/stopped health flags. No new wiring beyond a single, clearly-delimited insert in the dashboard so concurrent branches merge cleanly. The card renders, by lifecycle phase: - active: sensor age, % of 15-day life used (progress ring), time remaining, total life, last-sync time - warmup: the ~1h warmup countdown + a "warming up" heads-up banner - expiringSoon: an amber "expiring soon" heads-up to have a replacement ready (threshold 12h) - expired: a full offboarding state — clear "sensor expired" messaging, the last-known readings kept (frozen, not blanked), a "history is preserved" note, a 3-step remove/replace flow, and a "Replace sensor" action 14 -> 15 day correctness (TASK-043): the sensor-life duration is now a single source of truth (kSensorLifeDuration = 15 days in session_presentation.dart) consumed by the lifecycle math, the dashboard header, and the mock harness (mock_scenarios.dart no longer defines its own constant). sensorLifeText also rounds the days up so a freshly started 15-day sensor reads "15 days left" instead of "14". Lifecycle math (computeSensorLifecycle), the 15-day constant, the compact-duration / last-sync formatters, and the expired/warmup/ expiringSoon card rendering are covered by new unit + widget tests. Verified in the iOS simulator across the warmup, activeNormal, expiringSoon, and expired mock scenarios. Refs: TASK-008, TASK-043 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sensor lifecycle center (TASK-008, TASK-043 + founder-flagged expiry/offboarding)
Adds a self-contained sensor lifecycle center card to the dashboard, derived purely from the session timing (
sessionStart/warmupMinutes) plus the expiry/stopped health flags. Wellness framing throughout (sensor logistics, not medical).1. Lifecycle card (
SensorLifecycleCard)New, fully self-contained widget (
openhealth/lib/src/sensor_lifecycle_card.dart) inserted into the dashboard via a single, clearly-delimited block (// --- Sensor lifecycle center ---) inmain.dartso other branches touching the dashboard merge cleanly. By phase:The card is stateless and recomputes on each parent rebuild (the dashboard already rebuilds on controller notifications + the hero card's 1s ticker), so it owns no perpetual timer.
2. 14 → 15 day fix (TASK-043)
The whole app was searched (
grep -rnacross.dart/.swift/.kt/.md). There was no remaining hardcoded "14-day" sensor-life constant — earlier commits had already moved the life math to 15 days. The remaining drift risk was a duplicated 15-day literal (insession_presentation.dartandmock_scenarios.dart). This PR makes it a single source of truth:kSensorLifeDuration = Duration(days: 15)insession_presentation.dart, consumed by the lifecycle math, the dashboard header, and the mock harness (mock_scenarios.dartnow references it instead of its own_sensorLife).sensorLifeTextnow rounds days up so a freshly-started sensor reads "15 days left" instead of "14" (a partial first day still counts). This fixed a real header artifact seen in the warmup screenshot.3. Expiry / offboarding UI (founder-flagged)
When the sensor is expired (mock
expired/ session stopped /health.expired):expiringSoongets a proactive heads-up bannerVerification (iOS simulator — iPhone 15, mock scenarios)
/tmp/og-life-warmup.png— 40 min countdown, age 20m, 15-day total/tmp/og-life-active.png— 4% used, 14d 7h left, header "15 days left"/tmp/og-life-expiring.png— 99% used, 2h 59m left, amber heads-up/tmp/og-life-expired.png— offboarding flow, last reading 112 mg/dL preservedTests
New + existing unit/widget tests cover the life-% math, the 15-day constant, the compact-duration/last-sync formatters, and the warmup/expiringSoon/expired card rendering (incl. "expired preserves data, does not blank").
flutter analyzeclean. 48/48 tests pass across all suites except the pre-existingwidget_test.dart, which hangs inpumpAndSettledue to the dashboard hero card's existing 1-secondTimer.periodic(reproduced on the unmodified base branch — independent of this PR; left untouched to avoid scope creep on the stacked chain).🤖 Generated with Claude Code