Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3da50e6ce | |||
| a445256d86 |
@@ -19,7 +19,7 @@ use std::collections::HashMap;
|
|||||||
/// Tracks a file opened from a .cloud placeholder
|
/// Tracks a file opened from a .cloud placeholder
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct OpenedFile {
|
struct OpenedFile {
|
||||||
file_id: i64,
|
_file_id: i64,
|
||||||
real_path: PathBuf,
|
real_path: PathBuf,
|
||||||
cloud_name: String, // original .cloud filename
|
cloud_name: String, // original .cloud filename
|
||||||
}
|
}
|
||||||
@@ -230,10 +230,8 @@ async fn run_sync_now(state: State<'_, AppState>) -> Result<Vec<String>, String>
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn open_cloud_file(state: State<'_, AppState>, cloud_path: String) -> Result<String, String> {
|
async fn open_cloud_file(state: State<'_, AppState>, cloud_path: String) -> Result<String, String> {
|
||||||
let engine = {
|
let engine = state.api.lock().unwrap().clone()
|
||||||
let guard = state.sync_engine.lock().unwrap();
|
.ok_or("Nicht eingeloggt")?;
|
||||||
guard.as_ref().ok_or("Sync nicht gestartet")?.api.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = PathBuf::from(&cloud_path);
|
let path = PathBuf::from(&cloud_path);
|
||||||
let content = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;
|
let content = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;
|
||||||
@@ -255,7 +253,7 @@ async fn open_cloud_file(state: State<'_, AppState>, cloud_path: String) -> Resu
|
|||||||
|
|
||||||
// Track opened file for auto-close detection
|
// Track opened file for auto-close detection
|
||||||
state.opened_files.lock().unwrap().insert(file_id, OpenedFile {
|
state.opened_files.lock().unwrap().insert(file_id, OpenedFile {
|
||||||
file_id,
|
_file_id: file_id,
|
||||||
real_path: real_path.clone(),
|
real_path: real_path.clone(),
|
||||||
cloud_name: path.file_name().unwrap().to_string_lossy().to_string(),
|
cloud_name: path.file_name().unwrap().to_string_lossy().to_string(),
|
||||||
});
|
});
|
||||||
@@ -592,7 +590,7 @@ fn start_background_sync(
|
|||||||
|
|
||||||
let state = app_w.state::<AppState>();
|
let state = app_w.state::<AppState>();
|
||||||
let watchers = state.watchers.lock().unwrap();
|
let watchers = state.watchers.lock().unwrap();
|
||||||
let mut had_changes = false;
|
let mut _had_changes = false;
|
||||||
|
|
||||||
for watcher in watchers.iter() {
|
for watcher in watchers.iter() {
|
||||||
while let Ok(change) = watcher.receiver.try_recv() {
|
while let Ok(change) = watcher.receiver.try_recv() {
|
||||||
@@ -608,7 +606,7 @@ fn start_background_sync(
|
|||||||
ChangeKind::Deleted => format!("Geloescht: {}", name),
|
ChangeKind::Deleted => format!("Geloescht: {}", name),
|
||||||
};
|
};
|
||||||
let _ = app_w.emit("file-change", msg);
|
let _ = app_w.emit("file-change", msg);
|
||||||
had_changes = true;
|
_had_changes = true;
|
||||||
last_change = std::time::Instant::now();
|
last_change = std::time::Instant::now();
|
||||||
pending = true;
|
pending = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,14 @@ pub struct SyncTreeResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct SyncChangesResponse {
|
pub struct SyncChangesResponse {
|
||||||
pub changes: Vec<FileEntry>,
|
pub changes: Vec<FileEntry>,
|
||||||
pub server_time: String,
|
pub server_time: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct LockResponse {
|
pub struct LockResponse {
|
||||||
pub locked: Option<bool>,
|
pub locked: Option<bool>,
|
||||||
pub locked_by: Option<String>,
|
pub locked_by: Option<String>,
|
||||||
@@ -127,6 +129,7 @@ impl MiniCloudApi {
|
|||||||
Ok(data.tree)
|
Ok(data.tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn get_changes(&self, since: &str) -> Result<SyncChangesResponse, String> {
|
pub async fn get_changes(&self, since: &str) -> Result<SyncChangesResponse, String> {
|
||||||
let url = format!("{}/api/sync/changes?since={}", self.server_url, since);
|
let url = format!("{}/api/sync/changes?since={}", self.server_url, since);
|
||||||
let resp = self.client.get(&url)
|
let resp = self.client.get(&url)
|
||||||
|
|||||||
@@ -109,8 +109,7 @@ impl SyncEngine {
|
|||||||
let local_modified = std::fs::metadata(&local_path)
|
let local_modified = std::fs::metadata(&local_path)
|
||||||
.and_then(|m| m.modified()).ok();
|
.and_then(|m| m.modified()).ok();
|
||||||
let server_modified = entry.updated_at.as_deref()
|
let server_modified = entry.updated_at.as_deref()
|
||||||
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
|
.and_then(parse_server_time);
|
||||||
.map(|dt| std::time::SystemTime::from(dt));
|
|
||||||
|
|
||||||
let server_is_newer = match (local_modified, server_modified) {
|
let server_is_newer = match (local_modified, server_modified) {
|
||||||
(Some(lt), Some(st)) => st > lt,
|
(Some(lt), Some(st)) => st > lt,
|
||||||
@@ -344,8 +343,7 @@ impl SyncEngine {
|
|||||||
let local_modified = std::fs::metadata(&path)
|
let local_modified = std::fs::metadata(&path)
|
||||||
.and_then(|m| m.modified()).ok();
|
.and_then(|m| m.modified()).ok();
|
||||||
let server_modified = se.updated_at.as_deref()
|
let server_modified = se.updated_at.as_deref()
|
||||||
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
|
.and_then(parse_server_time);
|
||||||
.map(|dt| std::time::SystemTime::from(dt));
|
|
||||||
|
|
||||||
let server_is_newer = match (local_modified, server_modified) {
|
let server_is_newer = match (local_modified, server_modified) {
|
||||||
(Some(lt), Some(st)) => st > lt,
|
(Some(lt), Some(st)) => st > lt,
|
||||||
@@ -376,13 +374,14 @@ impl SyncEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Open a .cloud placeholder file: download the real file, rename, return path
|
/// Open a .cloud placeholder file: download the real file, rename, return path
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn open_cloud_file(&self, cloud_path: &Path) -> Result<PathBuf, String> {
|
pub async fn open_cloud_file(&self, cloud_path: &Path) -> Result<PathBuf, String> {
|
||||||
let content = std::fs::read_to_string(cloud_path)
|
let content = std::fs::read_to_string(cloud_path)
|
||||||
.map_err(|e| format!("Platzhalter lesen: {}", e))?;
|
.map_err(|e| format!("Platzhalter lesen: {}", e))?;
|
||||||
let placeholder: CloudPlaceholder = serde_json::from_str(&content)
|
let placeholder: CloudPlaceholder = serde_json::from_str(&content)
|
||||||
.map_err(|e| format!("Platzhalter ungueltig: {}", e))?;
|
.map_err(|e| format!("Platzhalter ungueltig: {}", e))?;
|
||||||
|
|
||||||
let real_path = cloud_path.with_extension("");
|
let _real_path = cloud_path.with_extension("");
|
||||||
// Remove .cloud extension to get real filename
|
// Remove .cloud extension to get real filename
|
||||||
let real_path = cloud_path.parent().unwrap().join(&placeholder.name);
|
let real_path = cloud_path.parent().unwrap().join(&placeholder.name);
|
||||||
|
|
||||||
@@ -399,6 +398,7 @@ impl SyncEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Close a previously opened file: sync back, recreate .cloud, unlock
|
/// Close a previously opened file: sync back, recreate .cloud, unlock
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn close_cloud_file(&self, real_path: &Path, file_id: i64) -> Result<(), String> {
|
pub async fn close_cloud_file(&self, real_path: &Path, file_id: i64) -> Result<(), String> {
|
||||||
// Upload changes
|
// Upload changes
|
||||||
// We need the parent_id - for now upload to the same location
|
// We need the parent_id - for now upload to the same location
|
||||||
@@ -446,6 +446,24 @@ fn find_subtree(tree: &[FileEntry], folder_id: i64) -> Option<Vec<FileEntry>> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a server timestamp (may or may not have timezone)
|
||||||
|
fn parse_server_time(s: &str) -> Option<std::time::SystemTime> {
|
||||||
|
// Try with timezone first (RFC3339)
|
||||||
|
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(s) {
|
||||||
|
return Some(std::time::SystemTime::from(dt));
|
||||||
|
}
|
||||||
|
// Try without timezone (naive, assume UTC)
|
||||||
|
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f") {
|
||||||
|
let utc = dt.and_utc();
|
||||||
|
return Some(std::time::SystemTime::from(utc));
|
||||||
|
}
|
||||||
|
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") {
|
||||||
|
let utc = dt.and_utc();
|
||||||
|
return Some(std::time::SystemTime::from(utc));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compute_file_hash(path: &Path) -> String {
|
pub fn compute_file_hash(path: &Path) -> String {
|
||||||
let data = match std::fs::read(path) {
|
let data = match std::fs::read(path) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
|
|||||||
Reference in New Issue
Block a user