super-productivity/src
Johannes Millan 3ff0eebf92
fix(sync): make 'Use Server Data' recoverable + guard the destructive choice (#8107) (#8151)
* fix(sync): back up local state before USE_REMOTE replace #8107

forceDownloadRemoteState cleared all unsynced local ops and replaced
NgRx state with the server snapshot with no recovery point, making an
unintended 'Use Server Data' conflict choice irreversibly destructive.

Capture a pre-wipe snapshot via the existing single-slot IMPORT_BACKUP
store (BackupService.captureImportBackup) before clearUnsyncedOps, and
abort the replace if the backup fails. Offer a 30s Undo snack afterwards
that restores it (BackupService.restoreImportBackup). Covers both the
conflict-dialog USE_REMOTE path and the manual force-download.

* fix(sync): durable restore entry + one-shot recovery for USE_REMOTE backup #8107

Addresses multi-review findings on the pre-wipe backup:

- Add a persistent 'Restore data from before last sync replace' button to
  the sync settings (all providers, gated on a remaining backup), so
  recovery survives a missed/replaced Undo snack or a failure that aborts
  after the wipe — the snack was previously the only reader of the backup.
- Make restore one-shot: consume the IMPORT_BACKUP slot after a successful
  restore, preventing a second restore from toggling back to the replaced
  state and avoiding a lingering plaintext snapshot.
- Undo snack: WARNING type (honest framing + dismiss control) and no
  auto-dismiss timer instead of SUCCESS/30s.

* test(sync): e2e for USE_REMOTE pre-replace backup + undo #8107

WebDAV two-client conflict: Client B's local task is wiped by 'Keep
remote' (USE_REMOTE), then the Undo snack restores it. Reproduces the
#8107 data loss and verifies recovery. Modeled on the existing
webdav-first-sync-conflict USE_REMOTE test.

Runnable via npm run e2e:webdav:file (needs the docker WebDAV server;
not runnable in the sandbox where published ports are unreachable).

* fix(sync): guard USE_REMOTE in conflict dialog; drop durable restore button #8107

Swap the over-built recovery surface for a root-cause fix:

- Add a confirmation guard before 'Use Server Data' (USE_REMOTE) discards
  pending local changes (INCOMING_IMPORT with local ops), mirroring the
  existing USE_LOCAL guard. The dialog frames the server as 'recommended',
  so this stops a misclick from silently wiping data the user can't tell
  is newer than the server's — attacking the cause, not just recovery.
- Revert the durable settings restore button + its one-shot clear /
  hasImportBackup (beyond the minimal fix; only that button used them).

Keeps the minimal recovery net: pre-wipe capture + sticky WARNING undo
snack (and its passing WebDAV e2e).

* fix(sync): explain encryption-blocked restore instead of generic error #8107

Defect 2: server-side Restore-from-History can't replay end-to-end-
encrypted ops, so it rejects with a 4xx whose reason mentions encryption
(the provider embeds that reason in the thrown error message). The client
showed a meaningless 'Failed to restore data' (bbinet's screenshot).

Detect the encryption reason in the restore catch and show a dedicated
RESTORE_ENCRYPTED message explaining the limitation; falls back to the
generic message for any other failure.

* fix(sync): guard USE_REMOTE undo against superseded backup slot #8107

The pre-replace backup uses a single IndexedDB slot shared with the
backup-import flow. The Undo snack never expires, so an intervening
import or a second 'Use Server Data' could overwrite the slot before
the user clicks Undo, silently restoring the wrong snapshot.

Thread the backup's savedAt token from capture through the snack to
restore; restoreImportBackup() now refuses when the stored backup no
longer matches the token. Also clear the slot after a successful
restore so a full copy of the replaced state stops lingering in IDB
(uses the previously-uncalled clearImportBackup).
2026-06-08 16:34:59 +02:00
..
app fix(sync): make 'Use Server Data' recoverable + guard the destructive choice (#8107) (#8151) 2026-06-08 16:34:59 +02:00
assets fix(sync): make 'Use Server Data' recoverable + guard the destructive choice (#8107) (#8151) 2026-06-08 16:34:59 +02:00
environments 18.9.1 2026-06-05 22:45:35 +02:00
static fix(security): harden XSS sinks and add Origin check (#7413) 2026-05-06 21:37:06 +02:00
styles feat(project): project completion experience (#8036) 2026-06-08 13:43:38 +02:00
test-helpers build: improve testing setup to give indication about which test hangs 2025-07-18 12:11:05 +02:00
typings refactor: improve typing 2025-09-16 18:04:40 +02:00
_common.scss refactor: modernize scss syntax and get rid of most of the warnings 2025-09-04 20:29:24 +02:00
favicon.ico
hammer-config.class.ts feat: replace hammerjs with custom swipe and pan directives 2025-08-10 12:56:11 +02:00
index.html fix(splash): keep productivity-tip heading readable on dark theme 2026-04-21 21:50:52 +02:00
karma.conf.js feat(op-log): validate SQLite backend + IDB→SQLite migration + backend-aware init (#7931) (#7954) 2026-06-02 17:26:23 +02:00
main.ts fix(android): route hardware back through Capacitor 2026-06-03 15:58:07 +02:00
manifest.json
polyfills.ts fix(schedule): indicate tasks beyond available time 2026-05-11 14:30:55 +02:00
styles.scss fix(styles): use package imports for fontsource instead of relative paths 2026-03-18 13:14:10 +01:00
test.ts test(karma): guard against leaked rxjs scheduler action crash 2026-06-08 12:38:51 +02:00
tsconfig.app.json fix(build): exclude test helpers from production build 2026-02-25 16:46:51 +01:00
tsconfig.spec.json feat(sync): add OneDrive sync provider with PKCE auth (#7523) 2026-05-26 15:31:06 +02:00
tsconfig.worker.json build: update configuration files 2025-06-27 10:25:00 +02:00