Fix Android note creation delay: throttle file watcher IPC (#191)

This commit is contained in:
Yuri Karamian
2026-06-30 19:48:19 +02:00
parent 015726f896
commit 13fd70f944
4 changed files with 47 additions and 46 deletions
+26 -38
View File
@@ -5,6 +5,22 @@ use crate::vault::{operations, watcher};
use std::path::Path; use std::path::Path;
use tauri::{AppHandle, Manager, State}; use tauri::{AppHandle, Manager, State};
fn index_note_bg(state: &State<'_, AppState>, path: &str) {
let search = state.search_index.lock().ok().and_then(|g| g.clone());
if let Some(search) = search {
let p = path.to_string();
std::thread::spawn(move || { let _ = search.index_note(&p); });
}
}
fn remove_note_bg(state: &State<'_, AppState>, path: &str) {
let search = state.search_index.lock().ok().and_then(|g| g.clone());
if let Some(search) = search {
let p = path.to_string();
std::thread::spawn(move || { let _ = search.remove_note(&p); });
}
}
// ── Vault Management ── // ── Vault Management ──
#[tauri::command] #[tauri::command]
@@ -338,12 +354,8 @@ pub fn save_note(
operations::save_note(&path, &meta, &body)?; operations::save_note(&path, &meta, &body)?;
// Re-index note so search picks up changes immediately // Re-index note so search picks up changes (background to avoid blocking on FUSE fsync)
if let Ok(search_guard) = state.search_index.lock() { index_note_bg(&state, &path);
if let Some(ref search) = *search_guard {
let _ = search.index_note(&path);
}
}
Ok(()) Ok(())
} }
@@ -358,12 +370,8 @@ pub fn create_note(
let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; let vault_path = config.active_vault.as_ref().ok_or("No active vault")?;
let entry = operations::create_note(vault_path, notebook_relative.as_deref(), &title)?; let entry = operations::create_note(vault_path, notebook_relative.as_deref(), &title)?;
// Index new note // Index new note (background to avoid blocking on FUSE fsync)
if let Ok(search_guard) = state.search_index.lock() { index_note_bg(&state, &entry.path);
if let Some(ref search) = *search_guard {
let _ = search.index_note(&entry.path);
}
}
Ok(entry) Ok(entry)
} }
@@ -374,11 +382,7 @@ pub fn create_daily_note(state: State<'_, AppState>, date: Option<String>) -> Re
let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; let vault_path = config.active_vault.as_ref().ok_or("No active vault")?;
let entry = operations::create_daily_note(vault_path, date.as_deref(), &config.daily_title_format)?; let entry = operations::create_daily_note(vault_path, date.as_deref(), &config.daily_title_format)?;
if let Ok(search_guard) = state.search_index.lock() { index_note_bg(&state, &entry.path);
if let Some(ref search) = *search_guard {
let _ = search.index_note(&entry.path);
}
}
Ok(entry) Ok(entry)
} }
@@ -397,12 +401,8 @@ pub fn delete_note(state: State<'_, AppState>, path: String) -> Result<(), Strin
let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; let vault_path = config.active_vault.as_ref().ok_or("No active vault")?;
operations::delete_note(vault_path, &path)?; operations::delete_note(vault_path, &path)?;
// Remove from index // Remove from index (background to avoid blocking on FUSE fsync)
if let Ok(search_guard) = state.search_index.lock() { remove_note_bg(&state, &path);
if let Some(ref search) = *search_guard {
let _ = search.remove_note(&path);
}
}
Ok(()) Ok(())
} }
@@ -781,11 +781,7 @@ pub fn set_task_done(
} }
operations::save_note(&note_path, &meta, &new_body)?; operations::save_note(&note_path, &meta, &new_body)?;
if let Ok(guard) = state.search_index.lock() { index_note_bg(&state, &note_path);
if let Some(search) = guard.as_ref() {
let _ = search.index_note(&note_path);
}
}
Ok(()) Ok(())
} }
@@ -842,11 +838,7 @@ pub fn set_task_priority(
} }
operations::save_note(&note_path, &meta, &new_body)?; operations::save_note(&note_path, &meta, &new_body)?;
if let Ok(guard) = state.search_index.lock() { index_note_bg(&state, &note_path);
if let Some(search) = guard.as_ref() {
let _ = search.index_note(&note_path);
}
}
Ok(()) Ok(())
} }
@@ -902,11 +894,7 @@ pub fn set_task_due(
} }
operations::save_note(&note_path, &meta, &new_body)?; operations::save_note(&note_path, &meta, &new_body)?;
if let Ok(guard) = state.search_index.lock() { index_note_bg(&state, &note_path);
if let Some(search) = guard.as_ref() {
let _ = search.index_note(&note_path);
}
}
Ok(()) Ok(())
} }
+9
View File
@@ -61,6 +61,15 @@ pub fn start_watcher(app: AppHandle, vault_path: String) -> Result<RecommendedWa
}; };
let _ = app.emit("file-changed", &fe); let _ = app.emit("file-changed", &fe);
} }
// On mobile, throttle event emission to prevent IPC flooding
// on FUSE filesystems where Syncthing/other apps generate
// constant file activity that blocks the Tauri command channel.
#[cfg(mobile)]
{
std::thread::sleep(Duration::from_secs(2));
while rx.try_recv().is_ok() {}
}
} }
Err(e) => { Err(e) => {
log::error!("File watcher error: {}", e); log::error!("File watcher error: {}", e);
+2 -2
View File
@@ -915,7 +915,7 @@
<Sidebar bind:this={sidebar} onViewChanged={handleViewChanged} /> <Sidebar bind:this={sidebar} onViewChanged={handleViewChanged} />
</div> </div>
<div class="mobile-panel" class:active={$mobileView === 'notelist'}> <div class="mobile-panel" class:active={$mobileView === 'notelist'}>
<NoteList bind:this={noteList} onNoteSelected={handleNoteSelected} onBeforeNoteSwitch={() => editor?.flushSave()} onNoteMoved={() => sidebar?.refresh()} onNoteCreated={() => { editor?.focusTitle(); sidebar?.refresh(); }} onToggleTask={toggleTask} onSetTaskPriority={changeTaskPriority} onSetTaskDue={changeTaskDue} /> <NoteList bind:this={noteList} onNoteSelected={handleNoteSelected} onBeforeNoteSwitch={() => editor?.flushSave()} onNoteMoved={() => sidebar?.refresh()} onNoteCreated={() => { editor?.focusTitle(); }} onToggleTask={toggleTask} onSetTaskPriority={changeTaskPriority} onSetTaskDue={changeTaskDue} />
</div> </div>
<div class="mobile-panel" class:active={$mobileView === 'editor'}> <div class="mobile-panel" class:active={$mobileView === 'editor'}>
<Editor bind:this={editor} /> <Editor bind:this={editor} />
@@ -976,7 +976,7 @@
{#if !$notelistCollapsed} {#if !$notelistCollapsed}
<div class="notelist-panel" style="width: {$notelistWidth}px"> <div class="notelist-panel" style="width: {$notelistWidth}px">
<NoteList bind:this={noteList} onNoteSelected={handleNoteSelected} onBeforeNoteSwitch={() => editor?.flushSave()} onNoteMoved={() => sidebar?.refresh()} onNoteCreated={() => { editor?.focusTitle(); sidebar?.refresh(); }} onToggleTask={toggleTask} onSetTaskPriority={changeTaskPriority} onSetTaskDue={changeTaskDue} /> <NoteList bind:this={noteList} onNoteSelected={handleNoteSelected} onBeforeNoteSwitch={() => editor?.flushSave()} onNoteMoved={() => sidebar?.refresh()} onNoteCreated={() => { editor?.focusTitle(); }} onToggleTask={toggleTask} onSetTaskPriority={changeTaskPriority} onSetTaskDue={changeTaskDue} />
</div> </div>
<ResizeHandle onResize={handleNotelistResize} /> <ResizeHandle onResize={handleNotelistResize} />
+6 -2
View File
@@ -442,8 +442,12 @@
const entry = await createNote(nbRelative, 'Untitled'); const entry = await createNote(nbRelative, 'Untitled');
if ($sortMode === 'custom') appendManualNoteOrder(entry.path); if ($sortMode === 'custom') appendManualNoteOrder(entry.path);
noteCache.clear(); noteCache.clear();
await refresh(); $notes = [entry, ...$notes];
await selectNote(entry); onBeforeNoteSwitch();
$activeNote = { path: entry.path, meta: entry.meta, content: '\n', raw: '' };
$activeNotePath = entry.path;
$editorDirty = false;
onNoteSelected(entry.path, '\n');
onNoteCreated(); onNoteCreated();
} catch (e) { } catch (e) {
console.error('Failed to create note:', e); console.error('Failed to create note:', e);