FE: features/bounties participant data layer (escrow client + hooks)#629
FE: features/bounties participant data layer (escrow client + hooks)#629natemiller23 wants to merge 2 commits into
Conversation
|
@natemiller23 is attempting to deploy a commit to the Threadflow Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning Review limit reached
More reviews will be available in 51 minutes and 2 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a client-side bounty hook module for listing, detail, applications, submission, withdrawal, and activity flows, plus React Query cache invalidation and toast/error handling. A Vitest file verifies the bounty list fetch and escrow submit request. ChangesBounty participant hooks and tests
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@features/bounties/hooks/use-bounty-escrow.ts`:
- Around line 225-235: `useMyBountyActivity` is only mapping
`useMyBountyApplications` data inside a query that never re-runs when that data
changes, and it currently omits the `'submission'` activity variant. Update the
hook so the activity result is derived from both applications and submissions in
a way that stays in sync with refetches, using the existing
`useMyBountyApplications` and any submission source you already have instead of
relying on a stale `queryFn` read. Ensure the returned
`ParticipantActivityItem[]` includes both `'application'` and `'submission'`
entries, and adjust the query key/enabled logic in `useMyBountyActivity` so the
combined activity recomputes whenever the underlying participant data changes.
- Line 51: The queryFn return type in use-bounty-escrow.ts has a syntax error
because the Promise generic is not closed before the arrow function, which
breaks parsing. Fix the type annotation on queryFn so the Promise<{ bounties:
BountySummary[]; pagination: Record<string, unknown> }> return type is properly
closed, and verify the surrounding useQuery/async callback syntax in
use-bounty-escrow stays valid.
- Line 194: The invalidation key in useBountyEscrow does not match the bounty
detail query key, so the cached detail entry is not being refreshed after
successful submission or withdrawal. Update the queryClient.invalidateQueries
calls in useBountyEscrow, including the paths in useWithdrawSubmission, to use
the same ['bounties', 'detail', id] shape registered by useBounty so React Query
matches the cache entry correctly.
- Around line 170-192: The submission payload is being dropped in the escrow
flow, so update runEscrowOp and useSubmitBounty to forward the validated
submission data instead of only sending scope/mode. Make sure useSubmitBounty
passes bountyId, description, and links into the /api/escrow/ops request body,
and use the expected parameter in runEscrowOp if it is meant to control the
operation mode. Keep the existing validation, but ensure the mutationFn actually
submits the bounty work data so the backend can associate it with the correct
bounty.
In `@tests/hooks/bounties/use-bounty-escrow.test.ts`:
- Line 20: The test wrapper in use-bounty-escrow.test.ts references
React.ReactNode without importing React, so the type name is unresolved. Update
the wrapper typing to use an imported ReactNode type or add the appropriate
React import near the test setup, and keep the wrapper signature in sync so the
JSX children typing compiles cleanly.
- Around line 44-46: The test in use-bounty-escrow.test.ts is calling act inside
the mutation flow without importing it, which causes a ReferenceError. Update
the test setup by adding act to the existing imports used alongside renderHook
and waitFor, using the same source as the rest of the React testing utilities in
this file. Make sure the act call in the mutateAsync block is covered by that
import so the hook test can run successfully.
- Line 48: The test in use-bounty-escrow.test.ts is using an untyped captured
entry with any, which violates the typing guideline. Update the captured
collection’s element type to a proper Trustless Work entity or request/log type
used by the hook, and then narrow the predicate in the expectation accordingly
so the /api/escrow/ops URL check no longer relies on any.
- Line 15: The test import for the bounty escrow hook points to a missing
root-level module, so update the import in the use-bounty-escrow test to
reference the actual hook location under features/bounties/hooks. Use the
existing useBountyEscrow import in
tests/hooks/bounties/use-bounty-escrow.test.ts as the symbol to locate it, and
change the module path from the old `@/hooks/bounties/use-bounty-escrow` alias to
the correct `@/features/bounties/hooks/use-bounty-escrow` path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1a8a09bd-4d73-440e-9cd5-d7ba1c02d1b9
📒 Files selected for processing (2)
features/bounties/hooks/use-bounty-escrow.tstests/hooks/bounties/use-bounty-escrow.test.ts
- close Promise<> generic in useBountiesList - forward payload through runEscrowOp / useSubmitBounty - invalidate bounty detail cache after submit/withdraw - include submissions in useMyBountyActivity - update test import path and typing
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@features/bounties/hooks/use-bounty-escrow.ts`:
- Around line 229-233: The file declares ParticipantActivityItem twice, causing
a TypeScript duplicate identifier error; remove the later duplicate type alias
in use-bounty-escrow.ts and keep only the original definition, ensuring
ParticipantSubmission still references the single shared ParticipantActivityItem
union used by the related bounty activity logic.
In `@tests/hooks/bounties/use-bounty-escrow.test.ts`:
- Around line 38-39: The test is waiting on result.current.isSuccess without
making the waitFor callback throw, so it can resolve before the query is
actually successful. Update the use-bounty-escrow test to assert inside waitFor
against result.current.isSuccess (and, if needed, the bounty length) so it keeps
polling until the hook state is ready, using the result.current access pattern
already in the test.
- Around line 42-51: Restore the mocked global fetch in the use-bounty-escrow
test setup so it does not leak into other tests. Update the useSubmitBounty test
to use vi.stubGlobal for fetch (or save the original global.fetch) and add
cleanup in afterEach with vi.unstubAllGlobals or an explicit restore. Keep the
change localized to the test file and reference the useSubmitBounty test case
and the global.fetch mock.
- Around line 18-25: The test helper in wrap uses JSX via QueryClientProvider,
so the file should be treated as TSX. Rename use-bounty-escrow.test.ts to use
the .tsx extension (or replace the JSX with React.createElement), and keep the
existing wrap/renderHook setup intact so TypeScript can parse it under the
current tsconfig.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d8f9ab91-3934-4dc4-8e87-d2cb76428d00
📒 Files selected for processing (2)
features/bounties/hooks/use-bounty-escrow.tstests/hooks/bounties/use-bounty-escrow.test.ts
| function wrap(hook: () => unknown) { | ||
| const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } }); | ||
| return renderHook(() => hook(), { | ||
| wrapper: ({ children }: { children: ReactNode }) => ( | ||
| <QueryClientProvider client={qc}>{children}</QueryClientProvider> | ||
| ), | ||
| }); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm extension and whether the project transforms JSX in .ts files
fd -t f 'use-bounty-escrow.test' tests
fd -t f 'vitest.config*' -X cat
fd -t f 'tsconfig*.json' -X sh -c 'echo "== $1 =="; cat "$1"' _ {}Repository: boundlessfi/boundless
Length of output: 928
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the test file, package scripts, and any Vitest/Babel/TS config that could affect JSX parsing in .ts files.
sed -n '1,120p' tests/hooks/bounties/use-bounty-escrow.test.ts
printf '\n== package.json ==\n'
sed -n '1,220p' package.json
printf '\n== vitest-related files ==\n'
fd -a 'vitest*' .
printf '\n== babel-related files ==\n'
fd -a '.babel*|babel.config*' .Repository: boundlessfi/boundless
Length of output: 8872
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check whether TypeScript treats this file as JSX-capable and whether a .ts file with JSX is syntactically valid in this repo.
python3 - <<'PY'
from pathlib import Path
import re
path = Path("tests/hooks/bounties/use-bounty-escrow.test.ts")
text = path.read_text()
# Minimal scan for JSX-like syntax in the file.
print("contains_jsx_opening_tag=", "<QueryClientProvider" in text)
print("contains_jsx_closing_tag=", "</QueryClientProvider>" in text)
# Show the exact line with JSX.
for i, line in enumerate(text.splitlines(), 1):
if "QueryClientProvider" in line:
print(f"line_{i}={line}")
PY
printf '\n== package json test tooling ==\n'
grep -n '"vitest"\|"test"' -n package.json || true
printf '\n== TypeScript compiler availability ==\n'
command -v tsc || true
tsc --version || trueRepository: boundlessfi/boundless
Length of output: 488
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect whether any test tooling overrides the default TypeScript/esbuild JSX handling for .ts files.
printf '== files named vitest or vite config ==\n'
fd -a 'vitest.config.*|vite.config.*|vitest.*' . || true
printf '\n== test-related scripts in package.json ==\n'
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
for (const [name, cmd] of Object.entries(pkg.scripts || {})) {
if (/(vitest|test|tsx?|esbuild|vite)/i.test(cmd)) console.log(name + ': ' + cmd);
}
JS
printf '\n== JSX syntax in the file ==\n'
python3 - <<'PY'
from pathlib import Path
path = Path("tests/hooks/bounties/use-bounty-escrow.test.ts")
for i, line in enumerate(path.read_text().splitlines(), 1):
if "QueryClientProvider" in line or "renderHook" in line:
print(f"{i}: {line}")
PYRepository: boundlessfi/boundless
Length of output: 561
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check whether JSX appears anywhere else in .ts files and whether TypeScript's parser rejects this file as .ts.
printf '== JSX-in-.ts occurrences ==\n'
rg -n --glob '*.ts' '<[A-Z][A-Za-z0-9]*\b' tests src app . || true
printf '\n== TypeScript parse probe ==\n'
node - <<'JS'
try {
const ts = require('typescript');
const fs = require('fs');
const source = fs.readFileSync('tests/hooks/bounties/use-bounty-escrow.test.ts', 'utf8');
const sf = ts.createSourceFile(
'tests/hooks/bounties/use-bounty-escrow.test.ts',
source,
ts.ScriptTarget.Latest,
/*setParentNodes*/ true,
ts.ScriptKind.TS,
);
console.log('parseDiagnostics=' + sf.parseDiagnostics.length);
for (const d of sf.parseDiagnostics.slice(0, 5)) {
console.log(ts.flattenDiagnosticMessageText(d.messageText, '\n'));
}
} catch (err) {
console.log('require_typescript_failed=' + err.message);
}
JSRepository: boundlessfi/boundless
Length of output: 50377
Rename tests/hooks/bounties/use-bounty-escrow.test.ts to .tsx — it contains JSX (<QueryClientProvider />), and tsconfig.json includes *.ts files in tsc --noEmit, so this will break TypeScript parsing unless the file becomes .tsx or the JSX is replaced with createElement.
🧰 Tools
🪛 Biome (2.5.0)
[error] 22-22: expected > but instead found client
(parse)
[error] 22-22: Invalid assignment to <QueryClientProvider client
(parse)
[error] 22-22: unterminated regex literal
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/hooks/bounties/use-bounty-escrow.test.ts` around lines 18 - 25, The
test helper in wrap uses JSX via QueryClientProvider, so the file should be
treated as TSX. Rename use-bounty-escrow.test.ts to use the .tsx extension (or
replace the JSX with React.createElement), and keep the existing wrap/renderHook
setup intact so TypeScript can parse it under the current tsconfig.
Source: Linters/SAST tools
| await waitFor(() => result.current.isSuccess); | ||
| expect(result.current.data?.bounties).toHaveLength(1); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
waitFor callback never asserts — the test can pass before the query succeeds.
waitFor only retries when the callback throws; returning a falsy boolean is treated as success, so it resolves on the first tick regardless of isSuccess. The subsequent toHaveLength(1) then runs against possibly-unsettled data. Assert inside the callback so waitFor actually polls.
🐛 Proposed fix
- await waitFor(() => result.current.isSuccess);
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await waitFor(() => result.current.isSuccess); | |
| expect(result.current.data?.bounties).toHaveLength(1); | |
| await waitFor(() => expect(result.current.isSuccess).toBe(true)); | |
| expect(result.current.data?.bounties).toHaveLength(1); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/hooks/bounties/use-bounty-escrow.test.ts` around lines 38 - 39, The
test is waiting on result.current.isSuccess without making the waitFor callback
throw, so it can resolve before the query is actually successful. Update the
use-bounty-escrow test to assert inside waitFor against result.current.isSuccess
(and, if needed, the bounty length) so it keeps polling until the hook state is
ready, using the result.current access pattern already in the test.
…essfi#629 - remove duplicate ParticipantActivityItem type alias - await success assertion inside waitFor callback - stub/restore global.fetch in tests with vi.stubGlobal + unstub - rename test to .tsx for JSX support
Closes #621
Implemented participant-facing bounty escrow hooks and validation for the Bounty Builder Lifecycle.
Changes:
Fixes payload handling in useSubmitBounty so submission data is not silently dropped, and corrects useMyBountyActivity to comply with Rules of Hooks by wiring active query state instead of calling hooks from queryFn.
Summary by CodeRabbit
New Features
Tests