mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
* fix(local-backup): don't overwrite a good backup with empty state (#7901)
After a WebView IndexedDB eviction the live NgRx store can boot empty.
The 5-min local-backup timer would then clobber the last good copy in
durable storage (Android SQLite KeyValStore, iOS file, Electron file)
with nothing. Gate _backup() with hasMeaningfulStateData() so empty/
degraded state can never overwrite a good backup, mirroring the existing
snapshot/compaction empty-overwrite guard.
* feat(local-backup): keep a second backup generation on mobile (#7901)
A single overwritten row/file is fragile: one bad or corrupt write cycle
can erase the only copy. Add a two-generation ring on the single-slot
mobile platforms (Android KeyValStore key, iOS file) — promote the
current backup to a 'prev' slot before overwriting, and restore from the
newest usable slot, falling back to prev when the primary is corrupt or
empty. Electron already keeps rotated, timestamped backups, so it is left
unchanged.
Adds backup-ring.util (isUsableBackupStr / selectBestBackupStr) with full
unit coverage; the iOS write is split behind _writeIOSFile so the promote
ordering is testable without the Capacitor Filesystem proxy.
* fix(android): store a real timestamp in KeyValStore KEY_CREATED_AT (#7901)
ContentValues binds "time('now')" as a literal string rather than letting
SQLite evaluate it, so every row's KEY_CREATED_AT held a constant text
value instead of a write timestamp. Store System.currentTimeMillis() so
the column is actually usable for ordering/debugging.
* fix(local-backup): harden restore paths from multi-agent review (#7901)
- loadBackupIOS: return '' instead of throwing when no usable backup
exists. askForFileStoreBackupIfAvailable runs from the fire-and-forget
_initBackups() at startup, so a throw became an unhandled rejection; ''
degrades to the existing import-error snack and mirrors loadBackupAndroid.
- Consolidate the Android newline escape into loadBackupAndroid (the single
escape site) and drop the now-redundant re-escape + log line at the call
site, removing a full-string pass on the restore path.
* feat(local-backup): restore fuller generation, show contents (#7901)
Two restore-path improvements (issue #7901 item 4):
- selectBestBackupStr now prefers the ring generation carrying MORE
data when both slots are usable (tie -> primary). After an eviction
the live store can boot near-empty and a 5-min backup may write that
degraded state to the primary; the full copy survives in prev, and
restore must surface the full one rather than the newer-but-smaller.
- The mobile restore prompt loads the backup first and names its task
and project counts (new RESTORE_FILE_BACKUP_MOBILE string), so a user
never blindly discards the only copy of their data. Falls back to the
generic prompt for an unparseable blob.
Note: a 'defer backups until the restore decision resolves' guard was
considered but is already structurally guaranteed -- init() (the only
thing starting the 5-min timer) runs after the synchronous restore
confirm in _initBackups, so the timer cannot fire before the decision.
* fix(local-backup): restore the newest backup, not the larger one (#7901)
Multi-agent review of
|
||
|---|---|---|
| .. | ||
| src | ||
| .gitignore | ||
| app_config.properties | ||
| build.gradle | ||
| capacitor.build.gradle | ||
| config.gradle | ||
| google.properties | ||
| proguard-rules.pro | ||