fix(panel): 服务器列表的刷新按钮只为自己的点击转圈和禁用,后台轮询时保持可点

This commit is contained in:
Lemon-miaow committed 2026-09-27 15:25:22 +08:00
1 parent b6357ad68b
commit c1bf1a81a2
2 files changed
+51 -3

No files matched your search

@@ -428,3 +428,41 @@ describe("ServersPage with nothing matching", () => {
expect(await tableOrder()).toEqual(names.slice(0, 20));
});
});
describe("ServersPage refresh button", () => {
afterEach(() => {
vi.useRealTimers();
});
it("stays still and clickable through the background reread, and spins for its own click", async () => {
vi.useFakeTimers({ shouldAdvanceTime: true });
calls.fleet.mockResolvedValue([row("survival", {})]);
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
render(
<MemoryRouter>
<ServersPage />
</MemoryRouter>,
);
await tableOrder();
const button = screen.getByRole("button", { name: "Refresh now" }) as HTMLButtonElement;
const spinning = () => button.querySelector("svg")?.classList.contains("animate-spin");
// The background reread hangs; the button does not show it.
let finish: (v: FleetServer[]) => void = () => {};
calls.fleet.mockReturnValueOnce(new Promise<FleetServer[]>((r) => (finish = r)));
await act(() => vi.advanceTimersByTimeAsync(10_000));
expect(calls.fleet).toHaveBeenCalledTimes(2);
expect(button.disabled).toBe(false);
expect(spinning()).toBe(false);
calls.fleet.mockReturnValueOnce(new Promise<FleetServer[]>((r) => (finish = r)));
await user.click(button);
expect(calls.fleet).toHaveBeenCalledTimes(3);
expect(button.disabled).toBe(true);
expect(spinning()).toBe(true);
await act(async () => finish([row("survival", {})]));
expect(button.disabled).toBe(false);
expect(spinning()).toBe(false);
});
});
+13 -3
View File
@@ -148,6 +148,16 @@ export function ServersPage() {
usePolling(reload, REFRESH_MS);
// The refresh button answers its own click alone. The background reread every
// few seconds leaves it still and clickable; a spin and a disabled button on
// each tick read as the page stalling.
const [refreshing, setRefreshing] = useState(false);
if (refreshing && !loading) setRefreshing(false);
const refresh = () => {
setRefreshing(true);
reload();
};
const servers = useMemo<UnifiedServer[]>(() => {
if (!data) return [];
if (isAdmin) {
@@ -253,12 +263,12 @@ export function ServersPage() {
<Button
variant="outline"
size="sm"
onClick={reload}
disabled={loading}
onClick={refresh}
disabled={refreshing}
aria-label={t("fleet_refresh")}
title={t("fleet_refresh")}
>
<RefreshCw className={cn("h-4 w-4", loading && "animate-spin")} />
<RefreshCw className={cn("h-4 w-4", refreshing && "animate-spin")} />
</Button>
{isAdmin && cfg && <CreateServerDialog cfg={cfg} onCreated={reload} />}
</div>