Flush auto-save before publishing card on Cmd (Ctrl) + Enter#2812
Flush auto-save before publishing card on Cmd (Ctrl) + Enter#2812diogovernier wants to merge 1 commit intobasecamp:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a race where using Cmd/Ctrl+Enter could publish a card before the title auto-save PATCH completed, causing the board to render with a stale title until reload. It does this by having the publish “clicker” await the auto-save controller before triggering the publish click.
Changes:
- Add a Stimulus outlet from
clicker→auto-saveon the publish buttons soclicker#clickcan await pending saves. - Update
clicker_controllerto callautoSaveOutlet.submit()before clicking. - Update
auto_save_controllerto track an in-flight save promise so external callers can await completion.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| app/views/cards/container/footer/_create.html.erb | Wires publish buttons’ clicker controller to the #card_form auto-save controller via an outlet. |
| app/javascript/controllers/clicker_controller.js | Awaits auto-save completion (when connected) before clicking publish. |
| app/javascript/controllers/auto_save_controller.js | Adds tracking for an in-flight save promise so callers can await ongoing saves. |
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this.#savePromise = submitForm(this.element) | ||
| try { | ||
| await this.#savePromise | ||
| } finally { | ||
| this.#savePromise = null |
There was a problem hiding this comment.
#save() overwrites #savePromise unconditionally. If a save is already in-flight (high latency) and another save is triggered (e.g., via change scheduling a new timer and then submit() calling #save()), multiple concurrent PATCHes can be sent and #savePromise will only track the most recent one. That means callers awaiting submit() may return while an earlier request is still in-flight, and responses can apply out of order, potentially persisting stale data last. Consider serializing saves (await/chain any existing #savePromise before starting a new submitForm) and avoid clearing a newer promise in the finally of an older save.
| this.#savePromise = submitForm(this.element) | |
| try { | |
| await this.#savePromise | |
| } finally { | |
| this.#savePromise = null | |
| const previousSavePromise = this.#savePromise || Promise.resolve() | |
| const savePromise = previousSavePromise.then(() => submitForm(this.element)) | |
| this.#savePromise = savePromise | |
| try { | |
| await savePromise | |
| } finally { | |
| if (this.#savePromise === savePromise) { | |
| this.#savePromise = null | |
| } |
Fixes #2778
Typing a card title quickly and hitting Cmd (Ctrl) + Enter could publish the card before auto-save finished, showing a stale title on the board (correct after reload).
Cmd+Enter fires two concurrent async handlers:
auto-save#submitstarts the PATCH, andclicker#clickwaits one animation frame (~16ms) then clicks the publish button. The publish redirect renders the board before the save round-trip completes.Solution: wire the clicker to the auto-save controller via a Stimulus outlet so it awaits the pending save before clicking publish.
Before:
before_fix.mp4
After:
after_fix.mp4