mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:20:43 +00:00
fix(control-ui): disable refresh during active runs
Disable the Control UI refresh button while chat is disconnected, loading, sending, running, or streaming. This prevents manual chat-history refresh from racing active run/stream state and adds browser render coverage for the disabled-state matrix. Closes #65522. Validation: - Exact PR head `1511a086614a727fc4200730e7ad9622134bb7d3` reached `CLEAN` merge state. - GitHub CI for the exact head completed with no failed or pending checks.
This commit is contained in:
@@ -15,6 +15,9 @@ function createState(overrides: Partial<AppViewState> = {}) {
|
||||
return {
|
||||
connected: true,
|
||||
chatLoading: false,
|
||||
chatRunId: null,
|
||||
chatSending: false,
|
||||
chatStream: null,
|
||||
onboarding: false,
|
||||
sessionKey: "main",
|
||||
sessionsHideCron: true,
|
||||
@@ -49,6 +52,17 @@ function createState(overrides: Partial<AppViewState> = {}) {
|
||||
} as unknown as AppViewState;
|
||||
}
|
||||
|
||||
function renderRefreshButton(overrides: Partial<AppViewState> = {}) {
|
||||
const container = document.createElement("div");
|
||||
render(renderChatControls(createState(overrides)), container);
|
||||
|
||||
const button = container.querySelector<HTMLButtonElement>(
|
||||
`.chat-controls .btn--icon[data-tooltip="${t("chat.refreshTitle")}"]`,
|
||||
);
|
||||
expect(button).not.toBeNull();
|
||||
return button!;
|
||||
}
|
||||
|
||||
describe("chat header controls (browser)", () => {
|
||||
it("renders explicit hover tooltip metadata for the top-right action buttons", async () => {
|
||||
const container = document.createElement("div");
|
||||
@@ -76,6 +90,19 @@ describe("chat header controls (browser)", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
["connected and idle", {}, false],
|
||||
["chat history loading", { chatLoading: true }, true],
|
||||
["chat send in flight", { chatSending: true }, true],
|
||||
["active run", { chatRunId: "run-123" }, true],
|
||||
["active stream", { chatStream: "streaming" }, true],
|
||||
["disconnected", { connected: false }, true],
|
||||
] as const)("sets refresh disabled state while %s", (_name, overrides, disabled) => {
|
||||
const button = renderRefreshButton(overrides);
|
||||
|
||||
expect(button.disabled).toBe(disabled);
|
||||
});
|
||||
|
||||
it("renders the cron session filter in the mobile dropdown controls", async () => {
|
||||
const state = createState({
|
||||
sessionsResult: {
|
||||
|
||||
@@ -222,6 +222,12 @@ export function renderChatControls(state: AppViewState) {
|
||||
? t("chat.showCronSessionsHidden", { count: String(hiddenCronCount) })
|
||||
: t("chat.showCronSessions")
|
||||
: t("chat.hideCronSessions");
|
||||
const refreshDisabled =
|
||||
!state.connected ||
|
||||
state.chatLoading ||
|
||||
state.chatSending ||
|
||||
Boolean(state.chatRunId) ||
|
||||
state.chatStream !== null;
|
||||
const toolCallsIcon = html`
|
||||
<svg
|
||||
width="18"
|
||||
@@ -275,7 +281,7 @@ export function renderChatControls(state: AppViewState) {
|
||||
<div class="chat-controls">
|
||||
<button
|
||||
class="btn btn--sm btn--icon"
|
||||
?disabled=${state.chatLoading || !state.connected}
|
||||
?disabled=${refreshDisabled}
|
||||
@click=${async () => {
|
||||
const app = state as unknown as ChatRefreshHost;
|
||||
app.chatManualRefreshInFlight = true;
|
||||
|
||||
Reference in New Issue
Block a user