Android: Sleep screen — in-app bed/wake time editing with live metric recompute#260
Android: Sleep screen — in-app bed/wake time editing with live metric recompute#260ujix wants to merge 4 commits into
Conversation
…ckerDialog Redesigns NightNavHeader to match DayNavBar: left/right chevrons flanking an accent-tinted center block showing the night label and date. The time range moves to a separate row below. Tapping the block opens a DatePickerDialog to jump to any recorded night by calendar date. Also fixes nightOffset reset: moves it into LaunchedEffect(days) so it only resets on a real sync/import, not on every optimistic sleeps update.
|
This is genuinely impressive work — and it's the one PR in this set that's a product-direction call, not a code-review call, so I want to flag it honestly rather than quietly fold it in. In-app editing of recorded bed/wake times touches a core NOOP promise: your data is yours, on-device, and correctable. Letting people fix a mis-detected night fits that ethos well. But it also means the app now lets you rewrite recorded sleep, and it adds a The implementation itself is solid: delete-then-upsert is safe and backward-compatible, the optimistic in-memory update avoids UI lag with the DB write wrapped in So: holding this open as the canonical version of your Sleep-screen series. If we go ahead, it supersedes #258/#259 (same analytics cards, without the editing). I'll come back with a decision. Two small notes for whenever it lands: the analytics chart uses a hardcoded |
… card HoursVsNeededCard: score %, trend arrow, gradient progress bar, stacked component bar (Healthy / Strain / Debt), slept/needed/debt footer. SleepConsistencyCard: Canvas vertical bar chart (bed-time top, wake-time bottom), dashed typical overlay lines, Y-axis time labels, X-axis day labels. Score is count-based (nights where both bed and wake are within 45 min of the user's typical); the previous SD formula always returned 0 %.
4a7c4de to
f62c8cb
Compare
Each SparkTile in MetricGrid now accepts an onClick lambda; tapping any tile sets detailMetricKey and slides up a ModalBottomSheet scoped to SleepScreen. SparkTile gains an onClick param — when set, the card modifier gets .clickable so the whole tile is the touch target. AppRoot adds SleepMetricDetail as a nav destination (kept for deep-link compatibility). MainActivity adds KEY_LAST_JOURNAL_PROMPT to NoopPrefs.
Tapping the edit icon in NightNavHeader opens a Strand-styled AlertDialog showing current bed and wake times; choosing either opens a TimePickerDialog. Optimistic in-memory sleeps update re-renders the header immediately; the DB write goes via WhoopRepository.updateSleepSessionTimes (delete + re-insert). Stage breakdown derives inBedMin from session.endTs - session.startTs so it reflects edits instantly. buildSleepModel adds metricsWindow: the selected night is substituted with sessionDurationMin so all per-tile metrics recompute against the edited window without skewing typicalTotalMin. WhoopDao gains deleteSleepSession; WhoopRepository and AppViewModel wire up the new method.
f62c8cb to
f18076f
Compare
|
Both issues addressed:
On the product-direction point — fully understood, and appreciate the honest framing. The delete-then-upsert data path and the iOS/macOS parity question are real considerations worth deliberating. Happy to wait on that call. Branch updated and force-pushed. |
|
Thanks — |
|
Adopted ✅ — the Sleep-screen umbrella shipped in v2.9.0: in-app bed/wake editing with live recompute, Hours-vs-Needed + Consistency cards, night navigation, detail sheets. Re-authored under the project account (hardcoded paint swapped for a Palette token, a11y labels added). Thanks @ujix — this was a big one! Closing as adopted. |
What this PR does
Executive summary: Adds in-app editing of bed and wake times from the Sleep nav header — an
AlertDialogselects which time to edit, aTimePickerDialogsets the new value, and all metrics recompute instantly against the edited window with no DB round-trip wait.Details:
Sleep screen — time editing
NightNavHeaderopens a Strand-styledAlertDialogwith two rows (Bedtime / Wake-up), each showing the current time and an accent edit icon; choosing either row opens its ownTimePickerDialogsleepslist update; no DB round-trip wait requiredsession.endTs − session.startTsso it reflects the edited window instantly rather than the stale stored valuebuildSleepModelnow builds ametricsWindowthat substitutessessionDurationMinfor the selected night'stotalSleepMin, while keepingtypicalTotalMinfrom the unmodified historical windowData layer
WhoopDao.deleteSleepSession(deviceId, startTs)—@Query DELETEused as part of the delete-then-upsert update patternWhoopRepository.updateSleepSessionTimes(session, newStartTs, newEndTs)— deletes the old row and upserts the corrected copyAppViewModel.updateSleepSessionTimes— thin suspend wrapper withrunCatchingso a DB error never surfaces as an unhandled exceptionValidation:
:app:assembleFullDebug,testFullDebugUnitTest, andtestDemoDebugUnitTestall green.Type of change
How it was tested
Android 16. Real Samsung device. WHOOP 4.0. Tested: editing bed time independently, editing wake time independently, both in the same session, header clock updates immediately, all metric cards reflect edited duration, navigation position preserved after edit, data persists across app restart.
Checklist
swift testinPackages/<name>)android/(./gradlew testFullDebugUnitTest)StrandDesigntokens — no hardcoded colors, fonts, or spacingdocs/CONTRIBUTING.mdStrand.xcodeproj/) or any secrets/keystoresRelated issues