Repository navigation
Unify Today editing sheets (#929, conflict resolved) - #940
Conversation
# Conflicts: # Strand/Resources/Localizable.xcstrings
|
Verification of the merge itself, since that is the only thing here that is not @Quaii's reviewed work. Nothing on @Quaii's code is byte-identical to their commit. Diffed every file the PR touches between The catalogue union checks out arithmetically: base 3274, this PR adds 8, On the usual app-target caveat: |
Merges ryanbr/noop main at 9.3.0 (35 commits since the 9.2.1 sync) into the fork. Two-parent merge, no rebase — upstream's signatures and the shared ancestry stay intact. Conflict resolutions worth knowing about: - Today customization (ryanbr#940). Upstream's unified TodayCustomizationSheet replaces the fork's KeyMetricsEditorSheet, DashboardCardsEditorSheet and TodayArrangeSheet, all three deleted. The Coach banner needed no workaround to survive: EditableLayoutList is generic, and the sheet builds its list from TodayLayoutPrefs.decodeOrder, which backfills every allCases entry at its defaultOrder position. Only TodayCustomizationMetadata's icon/tint switches are exhaustive without a default:, so .coach and .dataSources were added there. The fork's "Tiles per row" picker moved onto the Key Metrics page as another draft binding, so Cancel now discards it too. - .dataSources stays a reorderable section; upstream's change pinning sourcesSection below the loop is deliberately not taken (would render twice). - HealthKitBridge: both sides had independently built a "re-ask when a read-only type is added" mechanism. Consolidated onto upstream's readTypeSignatureKey/persistReadTypeSignature, keeping the fork's richer signature content and both trigger points (sync + resume). - Localizable.xcstrings resolved as a JSON union, not a text merge: the fork's 4189 keys plus upstream's 32 new ones. Also fixes the i18n gate, which was already red on main: the watch String Catalogs were reset to upstream (their 2826-line fork diff was Xcode build churn that invented three untranslatable keys), the two fork changelog titles became R.string keys in all six locales, and the audit baselines ratcheted down (Android 215 -> 213, Apple 64 -> 16). Version identity is 9.3.0 DX Beta, build 215, Android versionCode 307. Both AppChangelog files carry a fork entry above upstream's own 9.3.0 entry. altstore-source.json still publishes 9.2.2 DX Beta on purpose — that is the IPA that exists; only upstream's appPermissions block was adopted. Verified: macOS Strand and iOS NOOPiOS both build; swift test green for WhoopProtocol (472), WhoopStore (334), StrandAnalytics (1209), StrandImport (221); Tools Python suite (53) green; i18n audit green. StrandTests green except CoachMemoryDedupTests.testSupersedingNeverDowngradesPinnedImportance, which was already failing on main and touches nothing this merge changes. NOT verified on hardware: BLEManager.swift merged automatically (+201 lines from upstream) and no test covers the CoreBluetooth path. Pair with a real strap and watch one offload before merging this to main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…he iOS Sleep tab "Add nap" (ryanbr#508) lives on the classic `SleepView`, which the iOS tab shell never presents: `RootTabView` binds the Sleep tab to `PremiumSleepView`, and `SleepView` is reachable on iOS only from the DEBUG screenshot harness. So on iPhone there was no way to record a nap — and no way to correct a night's sleep window either, since the pencil lives on the same unreachable screen. Both write paths (`Repository.addManualNap` / `editSleepTimes` / `deleteSleepSession`) were present and working the whole time; only the UI entry point was missing. - `PremiumSleepView` gains a Naps card: the day's sleep outside the main night, each row editable/deletable, plus "Add nap" and "Edit sleep times". The card is always present — an empty nap list is exactly when the button is needed, and the screen's previous silence is what made the feature look absent. - It presents the SHARED `SleepTimeEditor` (made internal, not private) rather than a second picker, so every existing guard applies identically: the ryanbr#940 future-bed clamp, the wake date derived from bed, and the ryanbr#68 delete confirm whose copy drops the re-detection promise for a hand-added row (ryanbr#65). - `PremiumSleepIntel` now carries `latestMainBlock` + `latestDayNaps`, split with the shared `SleepView.mainNightGroup` so a briefly-interrupted night's bridged fragments stay part of the night and never render as phantom naps (ryanbr#555). Derived from the raw day blocks, so a day whose main night was rejected as an impossible window can still show and add its naps. Also fixes a latent seed bug on both platforms. The picker anchored at wake + 1h unconditionally; right after a morning sync that window has not elapsed, so the picker opened on a FUTURE window that `SleepEditGuard.clampedEditWindow` then refuses outright — the user picks times, taps save, and nothing is written. `SleepEditGuard.napSeedWindow` (Swift + Kotlin twins) now uses the wake anchor only once its full window has elapsed and the wake is not stale, else seeds the half-hour that just ended, which is valid at any time of day. Verification: `NapSeedWindowTests` (StrandAnalytics, runs in the active swift-packages CI) sweeps a day of wake offsets and asserts every seed is a non-empty past window that `clampedEditWindow` accepts; `SleepEditGuardTest` carries the Kotlin twin. App-target compile validated via app-build (swift-packages does not compile app targets). Not yet exercised against a real strap on device. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N8yddLHcjwg2oaugr8aYuH
Co-authored-by: Quai <Luis.dee16@gmail.com>
@Quaii's #929 with the merge conflict resolved. Same commit, authored by @Quaii and unchanged —
this branch only adds a merge of
mainon top, because the fork could not be pushed to from here(
maintainerCanModifyis set, but the push is refused, and GitHub's own Update branch returns422 merge conflict between base and head).Please treat #929 as the PR of record for review and credit; this exists purely to carry it past the
conflict.
The conflict, and how it was resolved
Only
Strand/Resources/Localizable.xcstringsconflicted — the sixstrings.xmlfiles auto-merged. Bothsides had appended new entries to the same sorted JSON object, from #938 landing earlier today.
Resolved by unioning the two catalogues programmatically rather than hand-patching, then checking the
arithmetic: base 3274 strings, #929 adds 8,
mainadds 1, zero keys added on both sides withdifferent values, zero base entries edited on both sides → 3283. Verified afterwards that #938's
"Raw counts only — needs an import"still carries all 8 locales, that the keys are still sorted, andthat all ten
today_customize_*keys survive in each of the six locales.Tools/i18n_audit.py --ci mainandTools/doc_comment_lint.pyboth clean on the merged tree, and everystrings.xmlstill parses.Review
Reviewed and approved on #929. The short version: the new
today.hiddenSectionskey is additivealongside an untouched
today.sectionOrder, noTodaySectionraw value changed on either platform, soexisting saved layouts survive; the two platforms encode it byte-identically; and
visibleOrder_filtersHiddenWithoutChangingSavedOrderasserts the saved order still holds every sectionafter hiding, which is the reversibility proof that matters.
StrandTests/TodayLayoutPrefsTests.swiftis not run by CI (app-build.ymlis disabled), so the Swifthalf rests on @Quaii's local
xcodebuild ... testrun rather than on the green ticks here.