fix(ui): непрерывная индикация анимации на кнопке обновления без мигания

- При получении 'Another component action is already running' или сетевого
  RPC-сбоя спиннер на нажатой кнопке больше не сбрасывается сразу.
- Добавлено повторное связывание (fallback lookups) с 800ms задержкой, чтобы
  бэкенд успевал прописать запущенный job в state до снятия индикатора.
- При отслеживании запущенного задания теперь учитываются любые фоновые
  акшны для того же компонента.
This commit is contained in:
Dushnilin
2026-08-10 21:38:46 +03:00
parent dd3546f29a
commit 8be248f9fd
2 changed files with 45 additions and 14 deletions
@@ -626,13 +626,19 @@ async function followAlreadyRunningComponentAction(
return false;
}
const state = uiState.actions.component.find(
let state = uiState.actions.component.find(
(item) =>
item.running &&
item.component === button.component &&
item.action === button.action,
);
if (!state) {
state = uiState.actions.component.find(
(item) => item.running && item.component === button.component,
);
}
if (!state) {
return false;
}
@@ -717,18 +723,28 @@ async function handleComponentAction(button: ComponentActionButton) {
if (!startResponse.success) {
if (isComponentActionAlreadyRunningError(startResponse.error)) {
setActionLoading(button.key, false);
if (!(await followAlreadyRunningComponentAction(button))) {
await refreshComponentActionState();
if (await followAlreadyRunningComponentAction(button)) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 800));
if (await followAlreadyRunningComponentAction(button)) {
return;
}
setActionLoading(button.key, false);
await refreshComponentActionState();
return;
}
if (isTransientRpcError(startResponse.error)) {
if (!(await followAlreadyRunningComponentAction(button))) {
setActionLoading(button.key, false);
await refreshComponentActionState();
if (await followAlreadyRunningComponentAction(button)) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 800));
if (await followAlreadyRunningComponentAction(button)) {
return;
}
setActionLoading(button.key, false);
await refreshComponentActionState();
return;
}
@@ -14304,9 +14304,14 @@ async function followAlreadyRunningComponentAction(button) {
if (!uiState) {
return false;
}
const state = uiState.actions.component.find(
let state = uiState.actions.component.find(
(item) => item.running && item.component === button.component && item.action === button.action
);
if (!state) {
state = uiState.actions.component.find(
(item) => item.running && item.component === button.component
);
}
if (!state) {
return false;
}
@@ -14374,17 +14379,27 @@ async function handleComponentAction(button) {
);
if (!startResponse.success) {
if (isComponentActionAlreadyRunningError(startResponse.error)) {
setActionLoading(button.key, false);
if (!await followAlreadyRunningComponentAction(button)) {
await refreshComponentActionState();
if (await followAlreadyRunningComponentAction(button)) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 800));
if (await followAlreadyRunningComponentAction(button)) {
return;
}
setActionLoading(button.key, false);
await refreshComponentActionState();
return;
}
if (isTransientRpcError(startResponse.error)) {
if (!await followAlreadyRunningComponentAction(button)) {
setActionLoading(button.key, false);
await refreshComponentActionState();
if (await followAlreadyRunningComponentAction(button)) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 800));
if (await followAlreadyRunningComponentAction(button)) {
return;
}
setActionLoading(button.key, false);
await refreshComponentActionState();
return;
}
throw new Error(startResponse.error);