diff --git a/apps/web/src/components/JobWorkspace.tsx b/apps/web/src/components/JobWorkspace.tsx index e4261ac..4d19095 100644 --- a/apps/web/src/components/JobWorkspace.tsx +++ b/apps/web/src/components/JobWorkspace.tsx @@ -37,8 +37,7 @@ function explorerHref(transactionHash: string | undefined): string | undefined { const USER_WALLET_PAYMENT_CHECK_DELAY_MS = 500; const USER_WALLET_PAYMENT_CHECK_ATTEMPTS = 30; -const RESULT_CHECK_DELAY_MS = 500; -const RESULT_CHECK_ATTEMPTS = 30; +const RESULT_REFRESH_DELAY_MS = 1000; function waitForPaymentCheck(): Promise { return new Promise((resolve) => { @@ -46,9 +45,9 @@ function waitForPaymentCheck(): Promise { }); } -function waitForResultCheck(): Promise { +function waitForResultRefresh(): Promise { return new Promise((resolve) => { - window.setTimeout(resolve, RESULT_CHECK_DELAY_MS); + window.setTimeout(resolve, RESULT_REFRESH_DELAY_MS); }); } @@ -70,27 +69,6 @@ async function resolveUserWalletPayment( return latest; } -async function waitForResumedResult( - client: JobApiClient, - jobId: string, - initial: JobView, -): Promise { - let latest = initial; - for ( - let attempt = 0; - attempt < RESULT_CHECK_ATTEMPTS && - !latest.result && - latest.delivery_state !== 'RETRIEVAL_FAILED'; - attempt += 1 - ) { - await waitForResultCheck(); - const matchingJob = (await client.list()).find((job) => job.job_id === jobId); - if (!matchingJob) throw new Error('The resumed job is no longer available'); - latest = matchingJob; - } - return latest; -} - export function SupplierQuotePanel({ quote, heading = 'Supplier quote', @@ -494,14 +472,16 @@ export function JobList(props: { const [resumingJobId, setResumingJobId] = useState(null); const [checkingPaymentJobId, setCheckingPaymentJobId] = useState(null); - async function refresh(): Promise { + async function refresh(): Promise { setLoading(true); try { const listed = await props.client.list(); setRequests(listed); setError(''); + return listed; } catch { setError('Requests could not be loaded. Check API readiness and your workspace session.'); + return null; } finally { setLoading(false); } @@ -512,9 +492,12 @@ export function JobList(props: { setError(''); try { const resumed = await props.client.resume(jobId); - const latest = await waitForResumedResult(props.client, jobId, resumed); - await refresh(); - if (!latest.result) { + if (!resumed.result && resumed.delivery_state === 'PENDING') { + await waitForResultRefresh(); + } + const listed = await refresh(); + const latest = listed?.find((job) => job.job_id === jobId); + if (latest && !latest.result) { setError( latest.delivery_state === 'RETRIEVAL_FAILED' ? 'The supplier result could not be retrieved. No new payment was submitted.' diff --git a/apps/web/test/components.test.tsx b/apps/web/test/components.test.tsx index e3aa74e..2057d83 100644 --- a/apps/web/test/components.test.tsx +++ b/apps/web/test/components.test.tsx @@ -181,7 +181,7 @@ describe('IntentStatusView', () => { }); describe('JobWorkspace payment inputs', () => { - it('observes the queued resume until the existing result is available', async () => { + it('refreshes once after the queued resume without polling jobs repeatedly', async () => { const user = userEvent.setup(); const pendingJob = resumableJob('PENDING'); const availableJob: JobView = { @@ -197,7 +197,7 @@ describe('JobWorkspace payment inputs', () => { const client = { list: vi.fn(async () => { listCalls += 1; - return [listCalls < 3 ? pendingJob : availableJob]; + return [listCalls === 1 ? pendingJob : availableJob]; }), resume: vi.fn(async () => pendingJob), }; @@ -211,7 +211,7 @@ describe('JobWorkspace payment inputs', () => { { timeout: 3000 }, ); expect(client.resume).toHaveBeenCalledWith(pendingJob.job_id); - expect(listCalls).toBeGreaterThanOrEqual(3); + expect(listCalls).toBe(2); expect(screen.getByText('Result ready:')).toBeTruthy(); });