feat: WebDAV sync + batch DnD + clean shutdown + center-modal UX bundle
- Sync (WebDAV, auto-merge): UUID + tombstones foundations (server + JS), THTTPClient bridge cmds (get/put/test), runSyncNow engine with pull/merge/push flow, Settings UI, pre-sync backup option. Test connection now treats 404 as OK (snapshot not yet created) and 401/ 403 as auth failure with dedicated toast. - Batch drag-drop: cards + table rows carry checked-set ids (CSV) when dragged from an active selection; folder + trash drop handlers parse and apply in batch via new moveEntriesToFolder helper that preserves TOTP / custom_fields / kind in the full PUT payload. - Clean shutdown: WM_QUERYENDSESSION / WM_ENDSESSION captured in the bridge message-only window; FormCloseQuery bypasses the tray-minimize intercept on system shutdown / restart / logoff so FireDAC closes the SQLite WAL cleanly instead of leaving -shm / -wal residue after a force-kill. - Center-mode modal: blur+dim backdrop via body::before pseudo-element in editor-position=center, swallows clicks below the panel so the existing outside-click handlers reliably dismiss the slideover / settings panel. - Batch bar state fixes: state.checked cleared before render in moveEntriesToFolder, emptyTrash, and per-card restoreEntry / permanentDelete / deleteEntry so the action bar disappears once the selection is fully processed. - Save-then-discard duplicate fix: soState reset to null before openSlideOver re-opens the freshly saved entry, otherwise the dirty check fired on the soState.id=null → newId switch and a Cancel left the form in new-entry mode (second Save → POST duplicate). - TEST_SYNC.md: end-to-end checklist for validating the WebDAV sync with 2 real instances. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -314,6 +314,42 @@ begin
|
||||
// Drives the card/table label so notes-with-fields read as "Credit card"
|
||||
// instead of the generic "Encrypted note" placeholder.
|
||||
AddColumnIfMissing('vault_entries', 'template', 'TEXT');
|
||||
// Stable identity that survives export/import + cross-device sync.
|
||||
// SQLite `id` is autoincrement local-only — useless to match the same
|
||||
// logical entry across two installs. Populate existing rows with a
|
||||
// fresh UUID v4 below so the migration is non-destructive.
|
||||
AddColumnIfMissing('vault_entries', 'uuid', 'TEXT');
|
||||
FConn.ExecSQL(
|
||||
'CREATE INDEX IF NOT EXISTS idx_entries_uuid ' +
|
||||
' ON vault_entries(uuid)');
|
||||
// Backfill UUIDs for legacy rows that landed before the column existed.
|
||||
// SQLite has no native uuid() — emit one via hex(randomblob) + manual
|
||||
// dashes (RFC 4122 v4 = 8-4-4-4-12 hex, version nibble forced to 4,
|
||||
// variant nibble high bits 10).
|
||||
FConn.ExecSQL(
|
||||
'UPDATE vault_entries SET uuid = ' +
|
||||
' lower(hex(randomblob(4))) || ''-'' || ' +
|
||||
' lower(hex(randomblob(2))) || ''-4'' || ' +
|
||||
' substr(lower(hex(randomblob(2))), 2) || ''-'' || ' +
|
||||
' substr(''89ab'', 1 + (abs(random()) % 4), 1) || ' +
|
||||
' substr(lower(hex(randomblob(2))), 2) || ''-'' || ' +
|
||||
' lower(hex(randomblob(6))) ' +
|
||||
'WHERE uuid IS NULL OR uuid = ''''');
|
||||
// Tombstones: every hard-delete inserts a row here so the sync engine
|
||||
// can propagate deletes to other devices without leaving deleted
|
||||
// entries to silently reappear at next pull.
|
||||
FConn.ExecSQL(
|
||||
'CREATE TABLE IF NOT EXISTS entry_tombstones (' +
|
||||
' id INTEGER PRIMARY KEY AUTOINCREMENT,' +
|
||||
' user_id INTEGER NOT NULL,' +
|
||||
' uuid TEXT NOT NULL,' +
|
||||
' deleted_at DATETIME DEFAULT CURRENT_TIMESTAMP,' +
|
||||
' FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,' +
|
||||
' UNIQUE(user_id, uuid)' +
|
||||
')');
|
||||
FConn.ExecSQL(
|
||||
'CREATE INDEX IF NOT EXISTS idx_tombstones_user ' +
|
||||
' ON entry_tombstones(user_id, deleted_at DESC)');
|
||||
// Per-folder customisation. NULL = no override → JS uses the default
|
||||
// accent + i-folder symbol.
|
||||
AddColumnIfMissing('folders', 'color', 'TEXT');
|
||||
|
||||
Reference in New Issue
Block a user