Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 12 additions & 29 deletions apps/web/src/components/JobWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,17 @@ 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<void> {
return new Promise((resolve) => {
window.setTimeout(resolve, USER_WALLET_PAYMENT_CHECK_DELAY_MS);
});
}

function waitForResultCheck(): Promise<void> {
function waitForResultRefresh(): Promise<void> {
return new Promise((resolve) => {
window.setTimeout(resolve, RESULT_CHECK_DELAY_MS);
window.setTimeout(resolve, RESULT_REFRESH_DELAY_MS);
});
}

Expand All @@ -70,27 +69,6 @@ async function resolveUserWalletPayment(
return latest;
}

async function waitForResumedResult(
client: JobApiClient,
jobId: string,
initial: JobView,
): Promise<JobView> {
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',
Expand Down Expand Up @@ -494,14 +472,16 @@ export function JobList(props: {
const [resumingJobId, setResumingJobId] = useState<string | null>(null);
const [checkingPaymentJobId, setCheckingPaymentJobId] = useState<string | null>(null);

async function refresh(): Promise<void> {
async function refresh(): Promise<readonly JobView[] | null> {
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);
}
Expand All @@ -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.'
Expand Down
6 changes: 3 additions & 3 deletions apps/web/test/components.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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),
};
Expand All @@ -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();
});

Expand Down
Loading