From 597dafc46113e4b0acc4d0f87c60abf04a62600c Mon Sep 17 00:00:00 2001 From: Stefan Hacker Date: Sun, 12 Apr 2026 03:03:01 +0200 Subject: [PATCH] feat: File Lock beim Oeffnen + Entsperren per Rechtsklick Beim Oeffnen einer .cloud-Datei: - Download + Datei bleibt lokal (wie bisher) - Lock wird auf dem Server gesetzt (andere sehen "gesperrt von X") - Kein Auto-Unlock - Datei bleibt gesperrt bis manuell entsperrt Rechtsklick im Datei-Browser auf Offline-Dateien: - "Entsperren (Freigeben fuer andere)" - hebt den Lock auf - "Nicht mehr offline" - .cloud zurueck + automatisch unlock So bleiben Dateien gesperrt solange man daran arbeitet. Wenn fertig: Rechtsklick -> Entsperren. Einfach und explizit. Co-Authored-By: Claude Opus 4.6 (1M context) --- clients/desktop/src-tauri/src/lib.rs | 18 +++++++++++++++-- clients/desktop/src/App.vue | 30 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/clients/desktop/src-tauri/src/lib.rs b/clients/desktop/src-tauri/src/lib.rs index f751541..5c8fef1 100644 --- a/clients/desktop/src-tauri/src/lib.rs +++ b/clients/desktop/src-tauri/src/lib.rs @@ -256,11 +256,17 @@ async fn open_cloud_file(state: State<'_, AppState>, cloud_path: String) -> Resu } eprintln!("[OpenCloud] Downloaded {} bytes", std::fs::metadata(&real_path).map(|m| m.len()).unwrap_or(0)); - // Remove .cloud placeholder - file stays as real file (like "offline markieren") + // Remove .cloud placeholder - file stays as real file // Changes will be synced automatically by the file watcher - // User can manually "unmark offline" via right-click to get .cloud back + // User can "unmark offline" or "unlock" via right-click std::fs::remove_file(&path).ok(); + // Lock on server (prevents others from editing) + match api.lock_file(file_id, "Desktop Sync Client").await { + Ok(_) => eprintln!("[OpenCloud] Locked on server"), + Err(e) => eprintln!("[OpenCloud] Lock failed (file may be locked by someone else): {}", e), + } + // Open with default application for this file type eprintln!("[OpenCloud] Opening with default app: {}", real_path.display()); open::that(&real_path) @@ -269,6 +275,13 @@ async fn open_cloud_file(state: State<'_, AppState>, cloud_path: String) -> Resu Ok(real_path.to_string_lossy().to_string()) } +#[tauri::command] +async fn unlock_file_cmd(state: State<'_, AppState>, file_id: i64) -> Result { + let api = state.api.lock().unwrap().clone().ok_or("Nicht eingeloggt")?; + api.unlock_file(file_id).await?; + Ok("Datei entsperrt".to_string()) +} + #[tauri::command] async fn get_file_tree(state: State<'_, AppState>) -> Result { let api = state.api.lock().unwrap().clone().ok_or("Nicht eingeloggt")?; @@ -726,6 +739,7 @@ pub fn run() { open_cloud_file, get_file_tree, get_status, + unlock_file_cmd, browse_sync_folder, mark_offline, unmark_offline, diff --git a/clients/desktop/src/App.vue b/clients/desktop/src/App.vue index c560fe5..3ff126b 100644 --- a/clients/desktop/src/App.vue +++ b/clients/desktop/src/App.vue @@ -84,6 +84,33 @@ async function doMarkOffline(file) { } } +async function doUnlockFile(file) { + hideContextMenu(); + // Find file ID from server tree + const serverFile = findFileInTree(fileTree.value, file.name); + if (!serverFile) { + syncLog.value = [`[${ts()}] Fehler: Datei nicht auf Server gefunden`, ...syncLog.value]; + return; + } + try { + await invoke("unlock_file_cmd", { fileId: serverFile.id }); + syncLog.value = [`[${ts()}] Entsperrt: ${file.name}`, ...syncLog.value].slice(0, 200); + } catch (err) { + syncLog.value = [`[${ts()}] Fehler: ${err}`, ...syncLog.value].slice(0, 200); + } +} + +function findFileInTree(entries, name) { + for (const e of entries) { + if (e.name === name) return e; + if (e.children) { + const found = findFileInTree(e.children, name); + if (found) return found; + } + } + return null; +} + async function doUnmarkOffline(file) { hideContextMenu(); try { @@ -429,6 +456,9 @@ onUnmounted(() => { unlistenStatus?.(); unlistenLog?.(); unlistenError?.(); unli
☁ Nicht mehr offline (Platzhalter)
+
+ 🔓 Entsperren (Freigeben fuer andere) +
Abbrechen