mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:10:45 +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 {
|
return {
|
||||||
connected: true,
|
connected: true,
|
||||||
chatLoading: false,
|
chatLoading: false,
|
||||||
|
chatRunId: null,
|
||||||
|
chatSending: false,
|
||||||
|
chatStream: null,
|
||||||
onboarding: false,
|
onboarding: false,
|
||||||
sessionKey: "main",
|
sessionKey: "main",
|
||||||
sessionsHideCron: true,
|
sessionsHideCron: true,
|
||||||
@@ -49,6 +52,17 @@ function createState(overrides: Partial<AppViewState> = {}) {
|
|||||||
} as unknown as 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)", () => {
|
describe("chat header controls (browser)", () => {
|
||||||
it("renders explicit hover tooltip metadata for the top-right action buttons", async () => {
|
it("renders explicit hover tooltip metadata for the top-right action buttons", async () => {
|
||||||
const container = document.createElement("div");
|
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 () => {
|
it("renders the cron session filter in the mobile dropdown controls", async () => {
|
||||||
const state = createState({
|
const state = createState({
|
||||||
sessionsResult: {
|
sessionsResult: {
|
||||||
|
|||||||
@@ -222,6 +222,12 @@ export function renderChatControls(state: AppViewState) {
|
|||||||
? t("chat.showCronSessionsHidden", { count: String(hiddenCronCount) })
|
? t("chat.showCronSessionsHidden", { count: String(hiddenCronCount) })
|
||||||
: t("chat.showCronSessions")
|
: t("chat.showCronSessions")
|
||||||
: t("chat.hideCronSessions");
|
: t("chat.hideCronSessions");
|
||||||
|
const refreshDisabled =
|
||||||
|
!state.connected ||
|
||||||
|
state.chatLoading ||
|
||||||
|
state.chatSending ||
|
||||||
|
Boolean(state.chatRunId) ||
|
||||||
|
state.chatStream !== null;
|
||||||
const toolCallsIcon = html`
|
const toolCallsIcon = html`
|
||||||
<svg
|
<svg
|
||||||
width="18"
|
width="18"
|
||||||
@@ -275,7 +281,7 @@ export function renderChatControls(state: AppViewState) {
|
|||||||
<div class="chat-controls">
|
<div class="chat-controls">
|
||||||
<button
|
<button
|
||||||
class="btn btn--sm btn--icon"
|
class="btn btn--sm btn--icon"
|
||||||
?disabled=${state.chatLoading || !state.connected}
|
?disabled=${refreshDisabled}
|
||||||
@click=${async () => {
|
@click=${async () => {
|
||||||
const app = state as unknown as ChatRefreshHost;
|
const app = state as unknown as ChatRefreshHost;
|
||||||
app.chatManualRefreshInFlight = true;
|
app.chatManualRefreshInFlight = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user