Skip to content

Unify Today editing sheets (#929, conflict resolved) - #940

Merged
ryanbr merged 2 commits into
mainfrom
pr929-rebased
Jul 29, 2026
Merged

ryanbr merged 2 commits into
mainfrom
pr929-rebased

Conversation

@ryanbr

@ryanbr ryanbr commented Jul 29, 2026

Copy link
Copy Markdown
Owner

@Quaii's #929 with the merge conflict resolved. Same commit, authored by @Quaii and unchanged —
this branch only adds a merge of main on top, because the fork could not be pushed to from here
(maintainerCanModify is set, but the push is refused, and GitHub's own Update branch returns
422 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.xcstrings conflicted — the six strings.xml files auto-merged. Both
sides 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, main adds 1, zero keys added on both sides with
different 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, and
that all ten today_customize_* keys survive in each of the six locales.

Tools/i18n_audit.py --ci main and Tools/doc_comment_lint.py both clean on the merged tree, and every
strings.xml still parses.

Review

Reviewed and approved on #929. The short version: the new today.hiddenSections key is additive
alongside an untouched today.sectionOrder, no TodaySection raw value changed on either platform, so
existing saved layouts survive; the two platforms encode it byte-identically; and
visibleOrder_filtersHiddenWithoutChangingSavedOrder asserts the saved order still holds every section
after hiding, which is the reversibility proof that matters.

StrandTests/TodayLayoutPrefsTests.swift is not run by CI (app-build.yml is disabled), so the Swift
half rests on @Quaii's local xcodebuild ... test run rather than on the green ticks here.

Quaii and others added 2 commits July 28, 2026 11:39
# Conflicts:
#	Strand/Resources/Localizable.xcstrings
@ryanbr

ryanbr commented Jul 29, 2026

Copy link
Copy Markdown
Owner Author

Verification of the merge itself, since that is the only thing here that is not @Quaii's reviewed work.

Nothing on main had touched any file this changes. Zero commits since v9.2.1 modified
TodayView.swift, LiquidTodayView.swift, Components.swift, TodayScreen.kt or Theme.kt. So there
is no semantic conflict available to get wrong — the only genuine overlap was the string catalogues.

@Quaii's code is byte-identical to their commit. Diffed every file the PR touches between d03e6c51
and this branch: all fifteen code files identical, and the only seven that differ are the catalogues
(Localizable.xcstrings + the six strings.xml), which is exactly where main's entries had to land.

The catalogue union checks out arithmetically: base 3274, this PR adds 8, main adds 1, zero keys
added on both sides with different values, zero base entries edited on both sides → 3283. Top-level
metadata (sourceLanguage, version) preserved. Keys still sorted. #938's string still carries its 8
locales; all ten today_customize_* keys present in each of the six locales.

On the usual app-target caveat: app-build.yml is disabled, so nothing in CI compiles
TodayView.swift or the new sheets — but since no commit on main touched them, they are byte-identical
to what @Quaii compiled locally with xcodebuild ... test and ran in the Simulator. The merge adds no
app-target risk that their own build did not already cover.

@ryanbr
ryanbr merged commit 4983044 into main Jul 29, 2026
11 checks passed
@ryanbr
ryanbr deleted the pr929-rebased branch July 29, 2026 03:07
@ryanbr ryanbr mentioned this pull request Jul 29, 2026
9 of 12 tasks
DX23876 added a commit to DX23876/noop that referenced this pull request Aug 1, 2026
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>
santiagoduenas044-sudo pushed a commit to santiagoduenas044-sudo/noop that referenced this pull request Aug 5, 2026
…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
simoncad7 pushed a commit to simoncad7/noop that referenced this pull request Aug 17, 2026
Co-authored-by: Quai <Luis.dee16@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants