Queue document writes durably and surface sync trouble in the UI

iCloud Drive writes now flow through a persistent WriteBacklog sidecar
(drained with backoff, flushed on backgrounding, wiped with the cache on
account change), so a save can never be lost to a transient coordinator
error. A status banner on the workout list surfaces stuck syncing.
Also: the split picker gains a Recent section with day labels, split
rows fold SplitItem into SplitListView, and list rows dim the multiply
sign in sets-by-reps.

Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
This commit is contained in:
2026-07-08 12:48:12 -04:00
parent 495fce1e5a
commit c05e83cff7
13 changed files with 1175 additions and 144 deletions
@@ -19,10 +19,15 @@ struct WorkoutLogListView: View {
/// race against the cache, which lags local writes by a beat.
@State private var doc: WorkoutDocument
/// The live cache entity, kept only to observe `updatedAt` for the absorb
/// below all rendering and editing goes through the `doc` snapshot.
private let workout: Workout
@State private var showingExercisePicker = false
@State private var selectedLogID: String?
init(workout: Workout) {
self.workout = workout
_doc = State(initialValue: WorkoutDocument(from: workout))
}
@@ -94,6 +99,20 @@ struct WorkoutLogListView: View {
}
}
.navigationTitle(doc.splitName ?? Split.unnamed)
// Absorb phone edits while this screen is open (H1's watch-side gap):
// without this, the snapshot stays frozen at init, and the next watch
// tick made against a state that's missing the phone's edit would be
// stamped fresh and clobber it wholesale on the phone. Strictly-newer
// guard only: an equal timestamp is our own edit echoing back through
// the phone's push, and a stale push in flight (the phone pushed before
// ingesting our latest edit) must not regress what the user is looking
// at. Clock skew across devices degrades this to a skipped absorb
// never to a clobber of local state.
.onChange(of: workout.updatedAt) { _, incoming in
if incoming > doc.updatedAt {
doc = WorkoutDocument(from: workout)
}
}
.navigationDestination(item: $selectedLogID) { logID in
ExerciseProgressView(
doc: $doc,