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) <noreply@anthropic.com>
This commit is contained in:
@@ -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));
|
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
|
// 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();
|
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
|
// Open with default application for this file type
|
||||||
eprintln!("[OpenCloud] Opening with default app: {}", real_path.display());
|
eprintln!("[OpenCloud] Opening with default app: {}", real_path.display());
|
||||||
open::that(&real_path)
|
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())
|
Ok(real_path.to_string_lossy().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn unlock_file_cmd(state: State<'_, AppState>, file_id: i64) -> Result<String, String> {
|
||||||
|
let api = state.api.lock().unwrap().clone().ok_or("Nicht eingeloggt")?;
|
||||||
|
api.unlock_file(file_id).await?;
|
||||||
|
Ok("Datei entsperrt".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn get_file_tree(state: State<'_, AppState>) -> Result<serde_json::Value, String> {
|
async fn get_file_tree(state: State<'_, AppState>) -> Result<serde_json::Value, String> {
|
||||||
let api = state.api.lock().unwrap().clone().ok_or("Nicht eingeloggt")?;
|
let api = state.api.lock().unwrap().clone().ok_or("Nicht eingeloggt")?;
|
||||||
@@ -726,6 +739,7 @@ pub fn run() {
|
|||||||
open_cloud_file,
|
open_cloud_file,
|
||||||
get_file_tree,
|
get_file_tree,
|
||||||
get_status,
|
get_status,
|
||||||
|
unlock_file_cmd,
|
||||||
browse_sync_folder,
|
browse_sync_folder,
|
||||||
mark_offline,
|
mark_offline,
|
||||||
unmark_offline,
|
unmark_offline,
|
||||||
|
|||||||
@@ -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) {
|
async function doUnmarkOffline(file) {
|
||||||
hideContextMenu();
|
hideContextMenu();
|
||||||
try {
|
try {
|
||||||
@@ -429,6 +456,9 @@ onUnmounted(() => { unlistenStatus?.(); unlistenLog?.(); unlistenError?.(); unli
|
|||||||
<div v-if="contextMenu.file?.is_offline" class="cm-item" @click="doUnmarkOffline(contextMenu.file)">
|
<div v-if="contextMenu.file?.is_offline" class="cm-item" @click="doUnmarkOffline(contextMenu.file)">
|
||||||
☁ Nicht mehr offline (Platzhalter)
|
☁ Nicht mehr offline (Platzhalter)
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="contextMenu.file?.is_offline" class="cm-item" @click="doUnlockFile(contextMenu.file)">
|
||||||
|
🔓 Entsperren (Freigeben fuer andere)
|
||||||
|
</div>
|
||||||
<div class="cm-item" @click="hideContextMenu">Abbrechen</div>
|
<div class="cm-item" @click="hideContextMenu">Abbrechen</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user