mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-25 23:35:00 +03:00
fix(frontend): close panel update confirmation before progress (#6766)
* fix(frontend): close panel update confirmation promptly * chore(ci): rerun flaky checks
This commit is contained in:
@@ -80,6 +80,39 @@ export default function PanelUpdateModal({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runPanelUpdate() {
|
||||||
|
const baseTip = t('pages.index.dontRefresh');
|
||||||
|
const tip = info.latestVersion ? `${baseTip} (${info.latestVersion})` : baseTip;
|
||||||
|
onClose();
|
||||||
|
onBusy({ busy: true, tip });
|
||||||
|
const result = await HttpUtil.post<{ runId: string }>('/panel/api/server/updatePanel');
|
||||||
|
if (!result?.success) {
|
||||||
|
onBusy({ busy: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const outcome = await pollUpdateStatus(result.obj?.runId ?? '');
|
||||||
|
onBusy({ busy: false });
|
||||||
|
if (outcome === 'success') {
|
||||||
|
await PromiseUtil.sleep(800);
|
||||||
|
window.location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modal[outcome === 'failed' ? 'error' : 'warning']({
|
||||||
|
title: t(
|
||||||
|
outcome === 'failed'
|
||||||
|
? 'pages.index.panelUpdateFailedTitle'
|
||||||
|
: 'pages.index.panelUpdateUnknownTitle',
|
||||||
|
),
|
||||||
|
content: t(
|
||||||
|
outcome === 'failed'
|
||||||
|
? 'pages.index.panelUpdateFailedDesc'
|
||||||
|
: 'pages.index.panelUpdateUnknownDesc',
|
||||||
|
),
|
||||||
|
okText: t('refresh'),
|
||||||
|
onOk: () => window.location.reload(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updatePanel() {
|
function updatePanel() {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: t('pages.index.panelUpdateDialog'),
|
title: t('pages.index.panelUpdateDialog'),
|
||||||
@@ -89,37 +122,8 @@ export default function PanelUpdateModal({
|
|||||||
),
|
),
|
||||||
okText: t('confirm'),
|
okText: t('confirm'),
|
||||||
cancelText: t('cancel'),
|
cancelText: t('cancel'),
|
||||||
onOk: async () => {
|
onOk: () => {
|
||||||
const baseTip = t('pages.index.dontRefresh');
|
void runPanelUpdate();
|
||||||
const tip = info.latestVersion ? `${baseTip} (${info.latestVersion})` : baseTip;
|
|
||||||
onClose();
|
|
||||||
onBusy({ busy: true, tip });
|
|
||||||
const result = await HttpUtil.post<{ runId: string }>('/panel/api/server/updatePanel');
|
|
||||||
if (!result?.success) {
|
|
||||||
onBusy({ busy: false });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const outcome = await pollUpdateStatus(result.obj?.runId ?? '');
|
|
||||||
onBusy({ busy: false });
|
|
||||||
if (outcome === 'success') {
|
|
||||||
await PromiseUtil.sleep(800);
|
|
||||||
window.location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
modal[outcome === 'failed' ? 'error' : 'warning']({
|
|
||||||
title: t(
|
|
||||||
outcome === 'failed'
|
|
||||||
? 'pages.index.panelUpdateFailedTitle'
|
|
||||||
: 'pages.index.panelUpdateUnknownTitle',
|
|
||||||
),
|
|
||||||
content: t(
|
|
||||||
outcome === 'failed'
|
|
||||||
? 'pages.index.panelUpdateFailedDesc'
|
|
||||||
: 'pages.index.panelUpdateUnknownDesc',
|
|
||||||
),
|
|
||||||
okText: t('refresh'),
|
|
||||||
onOk: () => window.location.reload(),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { fireEvent, screen } from '@testing-library/react';
|
||||||
|
import { Modal } from 'antd';
|
||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import PanelUpdateModal from '@/pages/index/PanelUpdateModal';
|
||||||
|
import { HttpUtil } from '@/utils';
|
||||||
|
import { renderWithProviders } from './test-utils';
|
||||||
|
|
||||||
|
let restoreUseModal = () => {};
|
||||||
|
|
||||||
|
describe('panel update modal', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
restoreUseModal();
|
||||||
|
restoreUseModal = () => {};
|
||||||
|
vi.mocked(HttpUtil.post).mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lets the confirmation close before the update request finishes', () => {
|
||||||
|
const confirm = vi.fn();
|
||||||
|
const useModal = vi.spyOn(Modal, 'useModal').mockReturnValue([
|
||||||
|
{
|
||||||
|
confirm,
|
||||||
|
error: vi.fn(),
|
||||||
|
warning: vi.fn(),
|
||||||
|
} as unknown as ReturnType<typeof Modal.useModal>[0],
|
||||||
|
<span key="modal-context" />,
|
||||||
|
]);
|
||||||
|
restoreUseModal = () => useModal.mockRestore();
|
||||||
|
vi.mocked(HttpUtil.post).mockReturnValue(new Promise(() => {}));
|
||||||
|
const onClose = vi.fn();
|
||||||
|
const onBusy = vi.fn();
|
||||||
|
renderWithProviders(
|
||||||
|
<PanelUpdateModal
|
||||||
|
open
|
||||||
|
info={{
|
||||||
|
channel: 'dev',
|
||||||
|
currentVersion: 'dev+old',
|
||||||
|
latestVersion: 'dev+new',
|
||||||
|
currentCommit: 'old',
|
||||||
|
latestCommit: 'new',
|
||||||
|
updateAvailable: true,
|
||||||
|
}}
|
||||||
|
devChannelEnable
|
||||||
|
onClose={onClose}
|
||||||
|
onBusy={onBusy}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /Update Panel/ }));
|
||||||
|
expect(confirm).toHaveBeenCalledOnce();
|
||||||
|
const config = confirm.mock.calls[0]?.[0];
|
||||||
|
if (!config?.onOk) throw new Error('Confirmation onOk handler was not registered');
|
||||||
|
|
||||||
|
expect(config.onOk()).toBeUndefined();
|
||||||
|
expect(onClose).toHaveBeenCalledOnce();
|
||||||
|
expect(HttpUtil.post).toHaveBeenCalledWith('/panel/api/server/updatePanel');
|
||||||
|
expect(onBusy).toHaveBeenCalledWith({
|
||||||
|
busy: true,
|
||||||
|
tip: 'Installation is in progress, please do not refresh this page (dev+new)',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user