Conversation
📝 WalkthroughWalkthroughThis pull request migrates the frontend from React to Preact, replaces the Axios HTTP client with a fetch-based implementation, and adds lazy loading for page components with Suspense boundaries. All API endpoints are refactored to use the new client while maintaining existing function signatures and behavior. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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: 1
🧹 Nitpick comments (2)
frontend/src/api/transactions.ts (1)
36-54: Consider logging unexpected response shapes for debugging.The defensive extraction logic handles multiple response formats gracefully, which is good for robustness. However, silently returning an empty array when the response shape is unexpected could mask API contract changes or bugs.
Consider adding a console warning in development when the response doesn't match expected shapes:
💡 Optional improvement
export async function getTxErc20Transfers(txHash: string): Promise<TxErc20Transfer[]> { const body = await client.get<unknown>(`/transactions/${txHash}/erc20-transfers`); if (Array.isArray(body)) return body as TxErc20Transfer[]; if (typeof body === 'object' && body !== null && 'data' in body) { const data = (body as { data?: unknown }).data; if (Array.isArray(data)) return data as TxErc20Transfer[]; } + if (import.meta.env.DEV && body !== null && body !== undefined) { + console.warn('Unexpected response shape for erc20-transfers:', body); + } return []; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/api/transactions.ts` around lines 36 - 54, Add a development-only warning when the API response shape is unexpected in both getTxErc20Transfers and getTxNftTransfers: after the existing checks that return arrays, detect the fallback path and call a non-production log (e.g., console.warn guarded by NODE_ENV !== 'production' or a logger.isDev flag) including the txHash, the endpoint string, and the actual body (or typeof body) so developers can see unexpected response shapes instead of silently returning [].frontend/src/App.tsx (1)
36-50: Consider consolidating Suspense boundaries.The current implementation wraps each route element individually with
<Suspense>. This works correctly but is verbose. You could simplify by wrapping at the<Layout>level or creating a helper:💡 Optional: Helper component to reduce repetition
function LazyRoute({ component: Component }: { component: React.LazyExoticComponent<() => JSX.Element> }) { return ( <Suspense fallback={<PageLoader />}> <Component /> </Suspense> ); } // Usage: <Route path="blocks" element={<LazyRoute component={BlocksPage} />} />Alternatively, a single
<Suspense>wrapping the<Outlet />in Layout would work for most cases. The current approach is valid if you need per-route fallback customization in the future.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.tsx` around lines 36 - 50, The route list wraps every element in <Suspense fallback={<PageLoader />}> which is verbose; either move a single <Suspense fallback={<PageLoader />}> into the Layout component around the <Outlet /> (so all routes inherit the same fallback) or implement a small helper component (e.g., LazyRoute) that takes a lazy component prop and returns it wrapped with <Suspense fallback={<PageLoader />>) and replace per-route wrappers with <Route ... element={<LazyRoute component={BlocksPage} />} />; update usages of Suspense, PageLoader, Layout, Outlet, and Route elements accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/api/client.ts`:
- Around line 66-68: The code currently returns response.json() directly in the
client function, which will throw a raw SyntaxError for 2xx responses with
invalid or empty JSON; wrap the JSON parsing in a try/catch around the
response.json() call (in the same function that currently returns
response.json() as Promise<T>) and on parse failure create and throw the
structured ApiError used elsewhere (include response.status, response.statusText
and the original parse error/message) so callers receive a consistent ApiError
instead of an unhandled SyntaxError.
---
Nitpick comments:
In `@frontend/src/api/transactions.ts`:
- Around line 36-54: Add a development-only warning when the API response shape
is unexpected in both getTxErc20Transfers and getTxNftTransfers: after the
existing checks that return arrays, detect the fallback path and call a
non-production log (e.g., console.warn guarded by NODE_ENV !== 'production' or a
logger.isDev flag) including the txHash, the endpoint string, and the actual
body (or typeof body) so developers can see unexpected response shapes instead
of silently returning [].
In `@frontend/src/App.tsx`:
- Around line 36-50: The route list wraps every element in <Suspense
fallback={<PageLoader />}> which is verbose; either move a single <Suspense
fallback={<PageLoader />}> into the Layout component around the <Outlet /> (so
all routes inherit the same fallback) or implement a small helper component
(e.g., LazyRoute) that takes a lazy component prop and returns it wrapped with
<Suspense fallback={<PageLoader />>) and replace per-route wrappers with <Route
... element={<LazyRoute component={BlocksPage} />} />; update usages of
Suspense, PageLoader, Layout, Outlet, and Route elements accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9e2e8551-9c3f-4ede-933e-ab70317bc9ce
⛔ Files ignored due to path filters (1)
frontend/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (14)
frontend/package.jsonfrontend/src/App.tsxfrontend/src/api/addresses.tsfrontend/src/api/blocks.tsfrontend/src/api/client.tsfrontend/src/api/logs.tsfrontend/src/api/nfts.tsfrontend/src/api/proxies.tsfrontend/src/api/search.tsfrontend/src/api/status.tsfrontend/src/api/tokens.tsfrontend/src/api/transactions.tsfrontend/src/pages/AddressPage.tsxfrontend/vite.config.ts
|
|
||
| return response.json() as Promise<T>; | ||
| } |
There was a problem hiding this comment.
Handle JSON parse failure on successful responses.
If the server returns a 2xx response with invalid JSON (or empty body), response.json() will reject with a SyntaxError. This would propagate as an unhandled error rather than a structured ApiError.
🛡️ Proposed defensive fix
- return response.json() as Promise<T>;
+ try {
+ return await response.json() as T;
+ } catch {
+ throw { error: 'Invalid response from server' } as ApiError;
+ }📝 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.
| return response.json() as Promise<T>; | |
| } | |
| try { | |
| return await response.json() as T; | |
| } catch { | |
| throw { error: 'Invalid response from server' } as ApiError; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/api/client.ts` around lines 66 - 68, The code currently returns
response.json() directly in the client function, which will throw a raw
SyntaxError for 2xx responses with invalid or empty JSON; wrap the JSON parsing
in a try/catch around the response.json() call (in the same function that
currently returns response.json() as Promise<T>) and on parse failure create and
throw the structured ApiError used elsewhere (include response.status,
response.statusText and the original parse error/message) so callers receive a
consistent ApiError instead of an unhandled SyntaxError.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/src/App.tsx (1)
38-54: Optional: Reduce Suspense wrapper repetition.Each route duplicates the same
<Suspense fallback={<PageLoader />}>wrapper. A small helper could reduce boilerplate while keeping the individual boundary behavior.♻️ Optional helper to reduce repetition
// Helper component function LazyRoute({ component: Component }: { component: React.LazyExoticComponent<() => JSX.Element> }) { return ( <Suspense fallback={<PageLoader />}> <Component /> </Suspense> ); } // Usage in routes: <Route index element={<LazyRoute component={WelcomePage} />} /> <Route path="blocks" element={<LazyRoute component={BlocksPage} />} /> // ... etc🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/App.tsx` around lines 38 - 54, Many routes repeat the same Suspense fallback with PageLoader; create a small helper component (e.g., LazyRoute) that wraps Suspense and renders a passed lazy component to eliminate boilerplate. Implement LazyRoute (accepting a prop like component or children) that returns <Suspense fallback={<PageLoader />}><Component /></Suspense>, then replace occurrences such as element={<Suspense fallback={<PageLoader />}><WelcomePage /></Suspense>} with element={<LazyRoute component={WelcomePage} />} (and similarly for BlocksPage, BlockDetailPage, TransactionsPage, SearchResultsPage, AddressesPage, TransactionDetailPage, AddressPage, NFTsPage, NFTContractPage, NFTTokenPage, StatusPage, TokensPage, TokenDetailPage, FaucetPage, NotFoundPage>).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/App.tsx`:
- Around line 24-30: The loader's text color in PageLoader doesn't adapt to dark
mode; update the JSX rendering inside the PageLoader component to use a Tailwind
dark-mode variant for the span's text class (e.g., add a dark:... class
alongside text-gray-500) so the loader text is readable in both light and dark
themes.
---
Nitpick comments:
In `@frontend/src/App.tsx`:
- Around line 38-54: Many routes repeat the same Suspense fallback with
PageLoader; create a small helper component (e.g., LazyRoute) that wraps
Suspense and renders a passed lazy component to eliminate boilerplate. Implement
LazyRoute (accepting a prop like component or children) that returns <Suspense
fallback={<PageLoader />}><Component /></Suspense>, then replace occurrences
such as element={<Suspense fallback={<PageLoader />}><WelcomePage /></Suspense>}
with element={<LazyRoute component={WelcomePage} />} (and similarly for
BlocksPage, BlockDetailPage, TransactionsPage, SearchResultsPage, AddressesPage,
TransactionDetailPage, AddressPage, NFTsPage, NFTContractPage, NFTTokenPage,
StatusPage, TokensPage, TokenDetailPage, FaucetPage, NotFoundPage>).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0fb4f61a-8a3a-4426-a114-f4d090c71eff
📒 Files selected for processing (4)
frontend/src/App.tsxfrontend/src/api/faucet.tsfrontend/src/api/status.tsfrontend/src/pages/BlocksPage.tsx
| function PageLoader() { | ||
| return ( | ||
| <div className="flex items-center justify-center h-64"> | ||
| <span className="text-gray-500 text-sm">Loading...</span> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Consider dark mode support for the loader text.
The text-gray-500 class won't adapt when dark mode is active. Since the app uses ThemeProvider, consider adding a dark mode variant.
🎨 Proposed fix for dark mode support
function PageLoader() {
return (
<div className="flex items-center justify-center h-64">
- <span className="text-gray-500 text-sm">Loading...</span>
+ <span className="text-gray-500 dark:text-gray-400 text-sm">Loading...</span>
</div>
);
}Based on learnings: "Frontend uses Tailwind CSS for styling across all components and pages."
📝 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.
| function PageLoader() { | |
| return ( | |
| <div className="flex items-center justify-center h-64"> | |
| <span className="text-gray-500 text-sm">Loading...</span> | |
| </div> | |
| ); | |
| } | |
| function PageLoader() { | |
| return ( | |
| <div className="flex items-center justify-center h-64"> | |
| <span className="text-gray-500 dark:text-gray-400 text-sm">Loading...</span> | |
| </div> | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/App.tsx` around lines 24 - 30, The loader's text color in
PageLoader doesn't adapt to dark mode; update the JSX rendering inside the
PageLoader component to use a Tailwind dark-mode variant for the span's text
class (e.g., add a dark:... class alongside text-gray-500) so the loader text is
readable in both light and dark themes.
Overview
Bundle Size Shrink — Change Synopsis
react/react-dom/react/jsx-runtime → preact/compat
10s timeout, same Retry-After parsing
return type
shared components
import
elsewhere
Net result: Initial JS download 108 kB gzip → 17 kB gzip (~84% reduction). Build time
1.5s → 811ms.
Summary by CodeRabbit
New Features
Improvements