feat(sync): built-in Nextcloud sync (Issue #49, stage 2)
Sign in to any Nextcloud server (also private ones in a subfolder, with their own port or a self-signed certificate) through the browser with Login Flow v2; the app gets an app password of its own, which "Disconnect" revokes again. A background thread syncs the storage folder with the folder ink-notes on the server every 30 seconds and shortly after saving, three-way against the state after the last pass: one-sided changes are copied, manifests and library.json are merged, a page changed on both sides is kept twice (the server's version as conflict copy, which disk_watch turns into a page), empty pages give way, deletions go into the trash on both sides and lose against changes. Changes to the open notebook wait while it has unsaved changes. Folder ETags spare listing unchanged notebooks; the pass stops instead of deleting when the storage folder or the server folder is missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c0ccdd394
commit
b8748c56d4
12 files changed
+3075
-1
No files matched your search
@@ -13,6 +13,7 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("1 Objekt | [R] Drehen [Entf] Löschen", "1 object | [R] Rotate [Del] Delete"),
|
||||
("Abbrechen", "Cancel"),
|
||||
("Abfrage fehlgeschlagen:", "Request failed:"),
|
||||
("Abgleich beenden und das App-Passwort widerrufen; die Notizbücher bleiben auf diesem Gerät und auf dem Server", "Stop syncing and revoke the app password; the notebooks stay on this device and on the server"),
|
||||
("Achsenkreuz", "Axis cross"),
|
||||
("Achteck (Oktagon)", "Octagon"),
|
||||
("Aktuelle Version:", "Current version:"),
|
||||
@@ -20,6 +21,9 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("Alle", "All"),
|
||||
("Alles gespeichert", "All saved"),
|
||||
("Als leere Seite bearbeiten (Schreibschutz aufheben)", "Edit as blank page (remove write protection)"),
|
||||
("Anmeldung abgelaufen – bitte erneut verbinden", "Sign-in expired – please connect again"),
|
||||
("Anmeldung abgelehnt – bitte neu verbinden", "Sign-in rejected – please connect again"),
|
||||
("Anmeldung im Browser; die App bekommt ein eigenes App-Passwort, das sich in Nextcloud jederzeit widerrufen lässt", "Sign in in the browser; the app gets an app password of its own, which can be revoked in Nextcloud at any time"),
|
||||
("AppImage (Portabel)", "AppImage (portable)"),
|
||||
("Asset-Name darf nicht leer sein.", "Asset name must not be empty."),
|
||||
("Auch per Strg+V aus der Zwischenablage oder durch Ziehen einer Bilddatei ins Fenster", "Also with Ctrl+V from the clipboard or by dragging an image file into the window"),
|
||||
@@ -44,6 +48,8 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("Bild mit transparentem Hintergrund, nur Inhalt", "Image with transparent background, content only"),
|
||||
("Bild nicht eingefügt: {err}", "Image not inserted: {err}"),
|
||||
("Bild, Text oder kopierte Objekte aus der Zwischenablage hier einfügen", "Paste an image, text or copied objects from the clipboard here"),
|
||||
("Bitte die Adresse des Nextcloud-Servers eingeben", "Please enter the address of the Nextcloud server"),
|
||||
("Bitte im Browser anmelden und den Zugriff erlauben …", "Please sign in in the browser and grant access …"),
|
||||
("Blanko", "Blank"),
|
||||
("Blau", "Blue"),
|
||||
("Code", "Code"),
|
||||
@@ -56,15 +62,34 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("Den Ordner gibt es nicht mehr", "The folder no longer exists"),
|
||||
("Den Ordner „{name}“ gibt es hier schon", "A folder “{name}” already exists here"),
|
||||
("Den Zielordner gibt es nicht mehr", "The target folder no longer exists"),
|
||||
("Der Ordner „ink-notes“ auf dem Server fehlt. Zur Sicherheit wird nichts gelöscht – zum erneuten Hochladen die Verbindung trennen und neu verbinden.", "The folder “ink-notes” is missing on the server. To be safe, nothing is deleted – to upload again, disconnect and connect again."),
|
||||
("Der Ordnername ist leer", "The folder name is empty"),
|
||||
("Der Ordnername ist zu lang (höchstens {max} Zeichen)", "The folder name is too long (at most {max} characters)"),
|
||||
("Der Server leitet nach {to} weiter – bitte diese Adresse verwenden", "The server redirects to {to} – please use that address"),
|
||||
("Der Speicherort {dir} fehlt. Zur Sicherheit wird nichts gelöscht.", "The storage location {dir} is missing. To be safe, nothing is deleted."),
|
||||
("Der übergeordnete Ordner existiert nicht mehr", "The parent folder no longer exists"),
|
||||
("DICKEN-PRESETS", "WIDTH PRESETS"),
|
||||
("Die Adresse muss mit https:// beginnen", "The address must start with https://"),
|
||||
("Die beschädigte Originaldatei wurde in Quarantäne gesichert:", "The damaged original file was saved to quarantine:"),
|
||||
("Die Datei ist kein lesbares PDF", "The file is not a readable PDF"),
|
||||
("Die einzige Seite eines Notizbuchs kann nicht gelöscht werden", "The only page of a notebook cannot be deleted"),
|
||||
("Die Flatpak-Version wird über die Software-Verwaltung aktualisiert.", "The Flatpak version is updated through the software manager."),
|
||||
("Die heruntergeladene Datei existiert nicht mehr.", "The downloaded file no longer exists."),
|
||||
("Inzwischen geändert", "Changed meanwhile"),
|
||||
("Kein Datenordner", "No data folder"),
|
||||
("NEXTCLOUD", "NEXTCLOUD"),
|
||||
("Nextcloud getrennt – die Notizbücher bleiben auf diesem Gerät", "Nextcloud disconnected – the notebooks stay on this device"),
|
||||
("Nicht gefunden", "Not found"),
|
||||
("Server nicht erreichbar: {msg}", "Server not reachable: {msg}"),
|
||||
("Server-Fehler {code}", "Server error {code}"),
|
||||
("Sofort mit dem Server abgleichen", "Sync with the server right away"),
|
||||
("Trennen", "Disconnect"),
|
||||
("Unerwartete Antwort des Servers", "Unexpected answer from the server"),
|
||||
("Ungültige Adresse: {e}", "Invalid address: {e}"),
|
||||
("Unter dieser Adresse antwortet kein Nextcloud-Server", "No Nextcloud server answers at this address"),
|
||||
("Vertrauen", "Trust"),
|
||||
("Zertifikat nicht vertrauenswürdig (SHA-256 {fingerprint})", "Certificate not trusted (SHA-256 {fingerprint})"),
|
||||
("Zugang nicht gespeichert: {err}", "Sign-in not saved: {err}"),
|
||||
("die oberste Ebene", "the top level"),
|
||||
("Die Zwischenablage ist leer", "The clipboard is empty"),
|
||||
("Diese Version wird über die Software-Verwaltung aktualisiert (z. B. GNOME Software oder „flatpak update“).", "This version is updated through the software manager (e.g. GNOME Software or “flatpak update”)."),
|
||||
@@ -124,6 +149,7 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("Graphit-Grau", "Graphite gray"),
|
||||
("Größere Schrift (jetzt {size} pt)", "Larger font (now {size} pt)"),
|
||||
("Grün", "Green"),
|
||||
("gerade eben", "just now"),
|
||||
("hart", "hard"),
|
||||
("Hell", "Light"),
|
||||
("Hellgrün", "Light green"),
|
||||
@@ -366,6 +392,8 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("Verzeichnis '{path}' enthält kein manifest.json", "Folder '{path}' contains no manifest.json"),
|
||||
("Verzeichnis '{path}' existiert nicht", "Folder '{path}' does not exist"),
|
||||
("Vorherige Seite (Bild auf)", "Previous page (Page Up)"),
|
||||
("vor {hours} h", "{hours} h ago"),
|
||||
("vor {minutes} min", "{minutes} min ago"),
|
||||
("weich", "soft"),
|
||||
("Weich: schon leichter Druck ergibt kräftige Linien.\nHart: erst fester Druck ergibt kräftige Linien.", "Soft: even light pressure gives bold lines.\nHard: only firm pressure gives bold lines."),
|
||||
("Weitere Funktionen – Update verfügbar", "More – update available"),
|
||||
@@ -385,6 +413,7 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("Zugriffsfehler (I/O). Die Datei existiert, kann aber nicht gelesen werden.\nDie Seite bleibt schreibgeschützt, um ein Überschreiben zu verhindern.", "Access error (I/O). The file exists but cannot be read.\nThe page stays read-only to prevent overwriting it."),
|
||||
("Zurück zu Seiten geht nur, solange alles im Startausschnitt liegt", "Back to pages only works while everything lies in the starting area"),
|
||||
("Zurücksetzen (Strg+0)", "Reset (Ctrl+0)"),
|
||||
("z. B. cloud.example.org", "e.g. cloud.example.org"),
|
||||
("{count} Objekt(e) eingefügt", "{count} object(s) pasted"),
|
||||
("{count} Objekt(e) gelöscht", "{count} object(s) deleted"),
|
||||
("{count} Objekt(e) kopiert – auch als Bild für andere Programme", "{count} object(s) copied – also as an image for other apps"),
|
||||
@@ -405,21 +434,28 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("„{title}“ verschieben nach:", "Move “{title}” to:"),
|
||||
("↩ Rückgängig", "↩ Undo"),
|
||||
("⏳ Update lädt … {percent} %", "⏳ Downloading update … {percent} %"),
|
||||
("⏳ Verbinde …", "⏳ Connecting …"),
|
||||
("⏷ {mm} mm Platz", "⏷ {mm} mm space"),
|
||||
("♾ Endlos", "♾ Endless"),
|
||||
("⚙ Erweiterte Server-Einstellungen", "⚙ Advanced server settings"),
|
||||
("⚠ Beschädigt", "⚠ Damaged"),
|
||||
("⚠ Das Zertifikat von {server} ist von keiner bekannten Stelle signiert. Nur vertrauen, wenn dieser Fingerabdruck mit dem des Servers übereinstimmt:", "⚠ The certificate of {server} isn't signed by a known authority. Only trust it if this fingerprint matches the server's:"),
|
||||
("⚠ Datenintegritäts-Warnung", "⚠ Data integrity warning"),
|
||||
("⚠ In anderer Instanz geöffnet (Schreibschutz)", "⚠ Opened in another instance (read-only)"),
|
||||
("⚠ Ohne https ist das App-Passwort im Netz lesbar – nur im eigenen Heimnetz verwenden", "⚠ Without https the app password can be read on the network – only use this in your own home network"),
|
||||
("⚠ Ungespeicherte Änderungen", "⚠ Unsaved changes"),
|
||||
("⚠ Unverschlüsselt (http)", "⚠ Unencrypted (http)"),
|
||||
("⚠\u{fe0f} {name} (Beschädigt - kein Manifest)", "⚠\u{fe0f} {name} (damaged - no manifest)"),
|
||||
("⚠\u{fe0f} {name} (Beschädigt)", "⚠\u{fe0f} {name} (damaged)"),
|
||||
("⚪ Offline – neuer Versuch in Kürze ({msg})", "⚪ Offline – trying again shortly ({msg})"),
|
||||
("⚫ Aktiv", "⚫ Active"),
|
||||
("✔ Synchronisiert {when}", "✔ Synced {when}"),
|
||||
("✔ Update bereit", "✔ Update ready"),
|
||||
("➕ Neue Seite einfügen", "➕ Insert new page"),
|
||||
("➕ Notizbuch", "➕ Notebook"),
|
||||
("⟲ Gelöschte Seite {number} wiederherstellen", "⟲ Restore deleted page {number}"),
|
||||
("⬇ Downloads", "⬇ Downloads"),
|
||||
("🌐 Browser öffnen", "🌐 Open browser"),
|
||||
("🌐 Release auf Gitea öffnen", "🌐 Open release on Gitea"),
|
||||
("🏠 Persönlicher Ordner", "🏠 Home folder"),
|
||||
("💾 Einstellungen speichern", "💾 Save settings"),
|
||||
@@ -435,10 +471,13 @@ pub const EN: &[(&str, &str)] = &[
|
||||
("📚 Oberste Ebene", "📚 Top level"),
|
||||
("📜 Durchgehend", "📜 Continuous"),
|
||||
("📦 Paket mit Software-Center öffnen / installieren", "📦 Open / install package with the software center"),
|
||||
("🔄 Jetzt", "🔄 Now"),
|
||||
("🔄 Suche nach Updates …", "🔄 Checking for updates …"),
|
||||
("🔄 Synchronisiere …", "🔄 Syncing …"),
|
||||
("🔄 Updates & Versionen", "🔄 Updates & versions"),
|
||||
("🔄 Backup", "🔄 Backup"),
|
||||
("🔄 Nach Updates suchen", "🔄 Check for updates"),
|
||||
("🔗 Verbinden …", "🔗 Connect …"),
|
||||
("🖥 Schreibtisch", "🖥 Desktop"),
|
||||
("🖼 Bild aus Datei …", "🖼 Image from file …"),
|
||||
("🖼 Bild einfügen … (Strg+I)", "🖼 Insert image … (Ctrl+I)"),
|
||||
|
||||
Reference in new issue
Block a user