mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix: patch 3 file-watcher bugs blocking merge
1. Task leak: add watch::channel shutdown signal to WatcherRuntime so the debounce loop exits when stop_file_watcher drops the runtime. Adds tokio 'macros' feature for select!. 2. ID collision: use crypto.randomUUID() for synthetic diff message IDs instead of seconds-precision timestamps that collide on rapid writes. 3. Channel switch cleanup: call stopFileWatcher in the useEffect cleanup so navigating away stops the backend watcher. Also converts the pointless dynamic import to a static import.
This commit is contained in:
Generated
+12
@@ -6263,9 +6263,21 @@ dependencies = [
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
|
||||
@@ -35,7 +35,7 @@ tauri-plugin-updater = "2"
|
||||
tauri-plugin-process = "2"
|
||||
infer = "0.19"
|
||||
hex = "0.4"
|
||||
tokio = { version = "1", features = ["fs", "sync", "rt"] }
|
||||
tokio = { version = "1", features = ["fs", "sync", "rt", "macros"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
nostr = "0.37"
|
||||
|
||||
@@ -43,6 +43,8 @@ pub struct ProjectDirConfig {
|
||||
/// Runtime state for an active file watcher.
|
||||
struct WatcherRuntime {
|
||||
_watcher: RecommendedWatcher,
|
||||
/// Dropping the sender signals the debounce loop to exit.
|
||||
_shutdown_tx: tokio::sync::watch::Sender<bool>,
|
||||
}
|
||||
|
||||
/// App-level state that holds all active file watchers.
|
||||
@@ -227,6 +229,9 @@ fn start_watcher(
|
||||
let project_dir_owned = project_dir.to_path_buf();
|
||||
let snapshots_ref = Arc::clone(&fw_state.snapshots);
|
||||
|
||||
// Shutdown signal: when the sender is dropped the loop exits.
|
||||
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(false);
|
||||
|
||||
// Debounce state: track last event time per file.
|
||||
let pending: Arc<Mutex<HashMap<PathBuf, Instant>>> = Arc::new(Mutex::new(HashMap::new()));
|
||||
let pending_clone = Arc::clone(&pending);
|
||||
@@ -240,7 +245,11 @@ fn start_watcher(
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let debounce_delay = Duration::from_millis(500);
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_millis(250)).await;
|
||||
// Exit when the WatcherRuntime (and its shutdown_tx) is dropped.
|
||||
tokio::select! {
|
||||
_ = shutdown_rx.changed() => break,
|
||||
_ = tokio::time::sleep(Duration::from_millis(250)) => {}
|
||||
}
|
||||
|
||||
let ready_paths: Vec<PathBuf> = {
|
||||
let mut pending_guard = match pending_clone.lock() {
|
||||
@@ -369,6 +378,7 @@ fn start_watcher(
|
||||
|
||||
Ok(WatcherRuntime {
|
||||
_watcher: watcher,
|
||||
_shutdown_tx: shutdown_tx,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@ import { useLocalFileDiffs } from "@/features/messages/useLocalFileDiffs";
|
||||
import { PresenceBadge } from "@/features/presence/ui/PresenceBadge";
|
||||
import { useUsersBatchQuery } from "@/features/profile/hooks";
|
||||
import { mergeCurrentProfileIntoLookup } from "@/features/profile/lib/identity";
|
||||
import {
|
||||
getProjectDir,
|
||||
startFileWatcher,
|
||||
stopFileWatcher,
|
||||
} from "@/shared/api/tauri";
|
||||
import type {
|
||||
Channel,
|
||||
Identity,
|
||||
@@ -108,9 +113,6 @@ export function ChannelScreen({
|
||||
let cancelled = false;
|
||||
void (async () => {
|
||||
try {
|
||||
const { getProjectDir, startFileWatcher } = await import(
|
||||
"@/shared/api/tauri"
|
||||
);
|
||||
const dir = await getProjectDir(activeChannelId);
|
||||
if (dir && !cancelled) {
|
||||
await startFileWatcher(activeChannelId);
|
||||
@@ -121,6 +123,8 @@ export function ChannelScreen({
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
// Stop the backend watcher so we don't leak one per channel visited.
|
||||
void stopFileWatcher(activeChannelId).catch(() => {});
|
||||
};
|
||||
}, [activeChannelId]);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export function useLocalFileDiffs(channelId: string | null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const syntheticId = `local-diff-${payload.timestamp}-${payload.filePath}`;
|
||||
const syntheticId = `local-diff-${crypto.randomUUID()}`;
|
||||
|
||||
const message: TimelineMessage = {
|
||||
id: syntheticId,
|
||||
|
||||
Reference in New Issue
Block a user