Repository navigation
Resolve cloud conflicts item by item so Edit placement stays open - #266
Conversation
Resolving an unrelated cloud conflict (Use cloud, Keep both) reloads the page on screen, which closed Edit placement and threw away the user's drags. A reload of the same page, the one that keeps undo history, now keeps the edit open on whichever of its lineups survive. A drag stays while the reload has its end where the drag started; an end the cloud moved or removed shows the cloud's version, with a toast. Opening another page still ends the edit without writing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reload in the middle of a drag (pointer still down) left the editor's drag based on the old position: the next pointer move put back the drag the reload had just undone, and when the dragged end was gone, the next drag of another end started from its numbers. The editor now lets a drag carry on only if the reload kept its end where the drag began. fromHive sets the reopened edit in one assignment, so nothing watching sees the edit end on the way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor compared a reload against where its own gesture began, which for a second drag of an end is the earlier draft, not the saved position fromHive compares against. The two could disagree: a reload that moved the spot to the first draft dropped the drag, and the editor then put it back. The editor now asks the reopened edit whether it kept the draft. A drag taken back on an end the reload removed no longer outlives its gesture, and dragging another end clears it. The screenshot view's lineup fake takes fromHive's new samePage flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reload that took a drag back and a second reload that removed its end, both before the pointer lifted, left the marker set: when a later reload brought the end back, its first drag was ignored. Every reopen now clears a marker whose end is no longer in the edit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 49 minutes. View limit details
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to Resolving a sync conflict now updates only the affected items, so an open placement edit on another lineup stays open. The changes come with targeted tests for failure and concurrency cases. No outstanding merge-blocking issues were identified. Pre-merge checks |
|
Use cloud and Keep both reloaded the whole page to show the cloud's version, which closed Edit placement and threw away its drags even when the conflict was on another lineup. Teammates' changes already apply item by item (#208); conflict resolution now goes the same way, holding what the user has open except what they chose the cloud's version of. A placement edit of a lineup in the conflict ends. This replaces the earlier commits' reopen-after-reload logic in the lineup provider and editor, which go back to #265's versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When part of Use cloud's discard fails, the redraw that follows puts the user's version back on every refused entity, held or not; holding one kept the cloud's version on screen while the work still waited. A teammate's change held back by a hold the user let go of during the discard waited for a reapply that conflict resolution then cleared. It now resumes once resolution ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a server that merges by field, Save names only the spots the user dragged, measured from the version the edit started from: the user's drag wins over a teammate's move of that spot, and the teammate's move of another spot the edit held back stays. The test that Save waits as a conflict now says it runs on a server that checks whole groups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This is the Windows release build of this branch with main merged in, so it includes #269 and #270. It runs as two instances on two test accounts against the dev deployment. The user quit with an unsent note on the B-site lineup. A teammate changed the same note, so when the user reopened, it waited as a conflict. The user opens Edit placement on the A-site lineup and drags its landing, while the teammate moves that same landing. Use cloud gives B site the cloud's version, and the edit stays open with its drag. Save lands the drag, which is the later write, on both screens.
#265 is merged, and this PR now targets main.
In #265, resolving a cloud conflict closed Edit placement and threw away the user's drags, with a red toast: "The page changed, so the placement edit closed without saving." That happened even when the conflict was on a different lineup. The cause was that Use cloud and Keep both reloaded the whole page to show the cloud's version, and a reload ends the edit.
The page doesn't need to reload. Since #208, a teammate's change to the page on screen applies one item at a time, leaving alone whatever the user is in the middle of changing. Use cloud and Keep both never moved to that path. Now they do, so they replace only the lineups and items in conflict.
Since #270, edits made online merge field by field, so conflicts are rarer. The prompt now appears only when an edit made offline, or left unsent before the app closed, collides with a teammate's change to the same field. That's the case this PR handles.
What the user sees
How
_useCloudVersionsFornow draws through_showCloudVersions. For the page on screen, that runs the merge teammates' changes use (_mergeRemotePage). The merge leaves alone what the user has open on the canvas: the placement edit's lineups, a lineup being placed, open text drafts and items under the pointer. The exception is anything in the conflicts being resolved, which takes the page data's version. Any other page still loads whole, as before.Leaving the edit's lineups alone also keeps the version the user started from (
markPageHydrated(keepBaseFor:)). Save's changed fields are measured from that version, which is why a teammate's move of a spot the user didn't drag survives the Save. Without it, Save would also name that spot and move it back. The new test fails that way whenkeepBaseForis switched off.editorHeldEntitiesProviderreturns null while any press off the canvas is down, so teammates' changes wait. Conflict resolution doesn't wait, because it's what the user just asked for. The set of held items therefore moved intoeditorHeldCanvasItemsProvider, which the conflict path reads directly.Two edge paths changed with it, both found in review:
Ground truth
On main's sync (this branch with main merged in), on dev, with two test accounts. The account in the GIF quit with an unsent note on the B-site lineup, and the teammate changed the same note.
field_conflict).landings/lA, which landed at revision 3.Before #269, on the server that refused any change to a group someone else had changed. These runs used the same two accounts:
A scratch driver that isn't committed sent pointer events through Flutter's hit testing and recorded state after each step.
Tests
These tests are in
strategy_page_session_provider_test, "lineup history through cloud sync", and run on the real op queue against the fake server. Each new one fails on #265 or without its fix:openOnRealQueuecan now give the fake server's header a theme. Without one, any redraw queued a map and theme patch. Its acknowledgement re-read the page, which hid the held-back-change bug.The full suite passes locally with main merged in (1851 tests, 9 skipped), and analyze is clean.
Review
Astra did two rounds. Round 1 found the two edge paths above, and both are fixed here, each with a test. Round 2 found an existing race from #265's base: a re-apply waiting when the user's own edit was acknowledged could run before the refreshed page arrived, briefly showing the old version. #270 fixed it at its root, and that fix is on main now.
ci: analyze test web
🤖 Generated with Claude Code