| … | … | @@ -0,0 +1,361 @@ | |
| 290 | + | // Sweep them only when the inbox entry was consumed: before that, the | |
| 291 | + | // inbox still references these files and keeps them for a later import. | |
| 292 | + | const mergedAttachments = deps.readDraft(draftKey).attachments; | |
| 293 | + | await deps.restoreDraft(draftKey, entry.backup); | |
🟠 High threads/incomingShareImport.ts:293
Retrying a detached cancel overwrites edits made to the draft while the restore is pending, and can recreate a draft that was deleted in the meantime. restoreDraft publishes the backup before its persistence promise resolves, but needsRestore stays true until that promise succeeds, so a failed restore repeats the backup on retry; guard the restore against intervening draft changes or keep the draft locked until cancellation settles.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
| 1 | In file @apps/mobile/src/features/threads/incomingShareImport.ts around line 293: |
| 2 | |
| 3 | Retrying a detached cancel overwrites edits made to the draft while the restore is pending, and can recreate a draft that was deleted in the meantime. `restoreDraft` publishes the backup before its persistence promise resolves, but `needsRestore` stays true until that promise succeeds, so a failed restore repeats the backup on retry; guard the restore against intervening draft changes or keep the draft locked until cancellation settles. |
Confirmed and fixed in 6292d3b7a1. restoreComposerDraftSnapshot shows the backup before it persists it, and needsRestore stayed true after a failed persist. A retried cancel then put the backup back over a later edit, and a deleted draft came back.
The import now records the draft as its merge left it (entry.merged). Cancel restores only when the draft still matches that, or when it already shows the backup, in which case the restore just persists it again. Any other draft is an edit or deletion made since, and cancel leaves it. Cancel still releases the reservation in that case. The sweep of merged attachments now uses the merged draft's attachments, not whatever the draft holds at retry time.
Test: "a retried cancel keeps a draft the user edited/deleted after the restore was shown" in incomingShareImport.test.ts. Both cases failed before the fix and pass after.