feat(sync): WebDAV vault sync
All checks were successful
CI / Windows build (push) Successful in 12m32s
All checks were successful
CI / Windows build (push) Successful in 12m32s
Two-way sync of the vault folder to a user-configured WebDAV server, so annotations (which travel with the file) sync with the file. - WebDavSyncService.syncNow: per-file decision — local-only uploads, remote-only downloads, and when BOTH sides changed since the last sync it keeps the loser as <file>.conflict-<mtime> on both sides (last-write-wins by mtime) so no data is ever lost. Creates dirs as needed; deletes are conservative. - The decision logic is pure and unit-tested against a fake WebDAV client; the real client is a thin http adapter (no dio dependency). - Settings: WebDAV URL / user / password / remote folder, Test connection, Sync now (with status + last-synced), and an auto-sync toggle (default OFF). Real server round-trips are device/server-validated. Credentials are in SharedPreferences for now (TODO secure-storage). analyze clean, 432 tests.
This commit is contained in:
@@ -379,4 +379,77 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get vaultUpdated => 'Vault folder updated';
|
||||
|
||||
@override
|
||||
String get syncSection => 'Sync (WebDAV)';
|
||||
|
||||
@override
|
||||
String get syncServerUrl => 'Server URL';
|
||||
|
||||
@override
|
||||
String get syncServerUrlHint =>
|
||||
'https://dav.example.com/remote.php/dav/files/me';
|
||||
|
||||
@override
|
||||
String get syncUsername => 'Username';
|
||||
|
||||
@override
|
||||
String get syncPassword => 'Password';
|
||||
|
||||
@override
|
||||
String get syncRemoteFolder => 'Remote folder';
|
||||
|
||||
@override
|
||||
String get syncRemoteFolderHint => 'BadNote';
|
||||
|
||||
@override
|
||||
String get syncSave => 'Save';
|
||||
|
||||
@override
|
||||
String get syncSaved => 'Sync settings saved';
|
||||
|
||||
@override
|
||||
String get syncTestConnection => 'Test connection';
|
||||
|
||||
@override
|
||||
String get syncTestOk => 'Connection OK';
|
||||
|
||||
@override
|
||||
String syncTestFailed(String error) {
|
||||
return 'Connection failed: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get syncNow => 'Sync now';
|
||||
|
||||
@override
|
||||
String get syncRunning => 'Syncing…';
|
||||
|
||||
@override
|
||||
String get syncNeverRun => 'Never synced';
|
||||
|
||||
@override
|
||||
String syncLastRun(String when) {
|
||||
return 'Last synced: $when';
|
||||
}
|
||||
|
||||
@override
|
||||
String syncResultSummary(int uploaded, int downloaded, int conflicts) {
|
||||
return '$uploaded uploaded · $downloaded downloaded · $conflicts conflicts';
|
||||
}
|
||||
|
||||
@override
|
||||
String syncFailed(String error) {
|
||||
return 'Sync failed: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get syncAuto => 'Sync automatically on launch';
|
||||
|
||||
@override
|
||||
String get syncCredentialsNote =>
|
||||
'Credentials are stored locally in plain text. Use a dedicated app password.';
|
||||
|
||||
@override
|
||||
String get syncNotConfigured => 'Enter a server URL to enable sync.';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user