Просмотр исходного кода

fix(tui): debounce shared storage reloads (#42579)

Kit Langton 2 дней назад
Родитель
Сommit
baf5ce3b7e

+ 11 - 2
packages/tui/src/context/storage.tsx

@@ -110,10 +110,19 @@ function createStorage(root: string, channel: string) {
     },
   }
 
-  const watcher = watch(directory, () => entries.forEach((entry) => entry.reload()))
+  let reload: ReturnType<typeof setTimeout> | undefined
+  const watcher = watch(directory, () => {
+    clearTimeout(reload)
+    // Atomic writes notify for the temporary file before its final rename, and some
+    // platforms coalesce the rename event. Reload after the event burst has settled.
+    reload = setTimeout(() => entries.forEach((entry) => entry.reload()), 50)
+  })
   return {
     storage,
-    close: () => watcher.close(),
+    close: () => {
+      clearTimeout(reload)
+      watcher.close()
+    },
   }
 }
 

+ 8 - 4
packages/tui/test/context/session-tabs.test.tsx

@@ -18,10 +18,10 @@ import { TestTuiContexts } from "../fixture/tui-environment"
 import { tmpdir } from "../fixture/fixture"
 import { createTuiResolvedConfig } from "../fixture/tui-runtime"
 
-async function wait(fn: () => boolean | Promise<boolean>, timeout = 2_000) {
+async function wait(fn: () => boolean | Promise<boolean>, timeout = 2_000, label = "condition") {
   const start = Date.now()
   while (!(await fn())) {
-    if (Date.now() - start > timeout) throw new Error("timed out waiting for condition")
+    if (Date.now() - start > timeout) throw new Error(`timed out waiting for ${label}`)
     await Bun.sleep(10)
   }
 }
@@ -219,11 +219,11 @@ test("only the foreground TUI mutates unread state", async () => {
   let background: Awaited<ReturnType<typeof renderSessionTabs>> | undefined
 
   try {
-    foreground = await renderSessionTabs("first", { state: temporary.path })
+    foreground = await renderSessionTabs("first", { state: temporary.path, persisted: ["first", "second"] })
     background = await renderSessionTabs("second", { state: temporary.path })
     foreground.focus()
     background.blur()
-    await wait(() => foreground?.tabs.tabs().length === 2 && background?.tabs.tabs().length === 2)
+    await wait(() => foreground?.tabs.tabs().length === 2 && background?.tabs.tabs().length === 2, 2_000, "shared tabs")
 
     const firstDone = executionSucceeded("first")
     foreground.emit(firstDone)
@@ -239,6 +239,8 @@ test("only the foreground TUI mutates unread state", async () => {
       () =>
         foreground?.tabs.status("second").unread === "activity" &&
         background?.tabs.status("second").unread === "activity",
+      10_000,
+      "shared unread activity",
     )
 
     foreground.tabs.select("second")
@@ -246,6 +248,8 @@ test("only the foreground TUI mutates unread state", async () => {
       () =>
         foreground?.tabs.status("second").unread === undefined &&
         background?.tabs.status("second").unread === undefined,
+      10_000,
+      "shared unread clearing",
     )
   } finally {
     if (foreground) await foreground.destroy()