fix(panel): 控制台、玩家页、权限页、文件页和仪表盘自动刷新状态,唤醒或停止后立即显示进行中

This commit is contained in:
Lemon-miaow committed 2026-09-26 22:42:33 +08:00
1 parent d65cf3af30
commit fda14e9e45
24 files changed
+971 -116

No files matched your search

+56 -2
View File
@@ -1,7 +1,7 @@
// @vitest-environment jsdom
import { describe, it, expect } from "vitest";
import { describe, it, expect, vi, afterEach } from "vitest";
import { act, renderHook } from "@testing-library/react";
import { useAsync, type AsyncOptions } from "./hooks";
import { useAsync, usePolling, type AsyncOptions } from "./hooks";
// A request the test settles by hand, one per call, in call order.
function requests() {
@@ -100,3 +100,57 @@ describe("useAsync", () => {
expect(h.result.current.data).toBe("server b");
});
});
describe("usePolling", () => {
afterEach(() => {
vi.useRealTimers();
Object.defineProperty(document, "hidden", { configurable: true, value: false });
});
function setHidden(hidden: boolean) {
Object.defineProperty(document, "hidden", { configurable: true, value: hidden });
document.dispatchEvent(new Event("visibilitychange"));
}
it("reloads on every tick while the tab is in view, and at once when it comes back", () => {
vi.useFakeTimers();
const reload = vi.fn();
renderHook(() => usePolling(reload, 1000));
vi.advanceTimersByTime(2500);
expect(reload).toHaveBeenCalledTimes(2);
setHidden(true);
vi.advanceTimersByTime(5000);
expect(reload).toHaveBeenCalledTimes(2);
setHidden(false);
expect(reload).toHaveBeenCalledTimes(3);
});
it("follows a new interval and stops when paused or unmounted", () => {
vi.useFakeTimers();
const reload = vi.fn();
const hook = renderHook(({ ms }: { ms: number | null }) => usePolling(reload, ms), {
initialProps: { ms: 1000 as number | null },
});
vi.advanceTimersByTime(1000);
expect(reload).toHaveBeenCalledTimes(1);
hook.rerender({ ms: 5000 });
vi.advanceTimersByTime(4000);
expect(reload).toHaveBeenCalledTimes(1);
vi.advanceTimersByTime(1000);
expect(reload).toHaveBeenCalledTimes(2);
hook.rerender({ ms: null });
vi.advanceTimersByTime(20000);
setHidden(false);
expect(reload).toHaveBeenCalledTimes(2);
hook.rerender({ ms: 1000 });
hook.unmount();
vi.advanceTimersByTime(5000);
expect(reload).toHaveBeenCalledTimes(2);
});
});
+23
View File
@@ -112,3 +112,26 @@ export function useUnsavedGuard(dirty: boolean): void {
return () => window.removeEventListener("beforeunload", onBeforeUnload);
}, [dirty]);
}
/** How often a server page rereads its status: fast while it waits on the
* server (coming up, going down, or needed up), slow while nothing is due. */
export const STATUS_POLL_FAST_MS = 4_000;
export const STATUS_POLL_SLOW_MS = 15_000;
/** usePolling re-runs `reload` every `ms` while the tab is visible, and at once
* when the tab comes back into view, so a page watching a server follows it
* without a manual refresh. `ms` null pauses it. */
export function usePolling(reload: () => void, ms: number | null): void {
useEffect(() => {
if (ms === null) return;
const tick = () => {
if (!document.hidden) reload();
};
const id = window.setInterval(tick, ms);
document.addEventListener("visibilitychange", tick);
return () => {
window.clearInterval(id);
document.removeEventListener("visibilitychange", tick);
};
}, [reload, ms]);
}