Skip to content

fix: blocks page stale auto-update, timer drift, and Promise anti-pattern#601

Merged
samufacanha2 merged 3 commits intodevelopfrom
fix/blocks-page-auto-update-timer-and-api-pattern
Mar 9, 2026
Merged

fix: blocks page stale auto-update, timer drift, and Promise anti-pattern#601
samufacanha2 merged 3 commits intodevelopfrom
fix/blocks-page-auto-update-timer-and-api-pattern

Conversation

@ianakiara
Copy link
Member

@ianakiara ianakiara commented Mar 6, 2026

Summary

Three code quality fixes on the blocks page and API calls:

  1. Stale auto-updatetotalStatisticsCall was never refetched during auto-update intervals. Now both refetchYesterday() and refetchTotal() are called.

  2. Timer drift — The "X ago" timer in card headers tracked component mount time, not last data fetch. Now uses React Query's dataUpdatedAt timestamp which resets on every successful refetch.

  3. Promise anti-patternblockCall used async executor inside new Promise, silently swallowing errors. Replaced with standard async/await + throw pattern.

Changes

src/pages/blocks/index.tsx

  • Extract refetch: refetchTotal from totalStatisticsCall query
  • Rename refetchrefetchYesterday for clarity
  • Call both refetch functions in the auto-update interval
  • Extract dataUpdatedAt from yesterday query, pass to CardContent
  • Replace internal uptime state with dataUpdatedAt prop
  • Re-run age calculation when dataUpdatedAt changes

src/services/apiCalls.ts

  • Replace new Promise(async (resolve, reject) => { ... }) with standard async/await + throw
  • Add missing return flow (previously resolve() fell through to reject())

src/types/blocks.ts

  • Add dataUpdatedAt?: number to ICard interface

Test plan

  • Enable auto-update on blocks page — verify both "24h" and "Cumulative" card values refresh
  • Verify "X ago" timer resets to "0 secs ago" after each auto-update refetch
  • Verify blocks table still loads and paginates correctly (blockCall fix)
  • Verify no console errors on the blocks page

Closes #599

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements

    • More accurate timestamp aggregation for card update ages and improved age display logic (only shown when available).
    • Background refresh now reliably triggers both recent and total data refreshes for up-to-date cards.
    • Simplified and more consistent error behavior for data fetches, improving reliability and error signaling.
  • Bug Fixes

    • Fixed age initialization and recomputation so cards reflect correct update times.

…tern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

Walkthrough

Auto-update now refetches both total and yesterday statistics; card age is derived from query-provided dataUpdatedAt timestamps; CardContent accepts a required dataUpdatedAt prop; API calls removed Promise-constructor anti-pattern and now throw Errors on failure.

Changes

Cohort / File(s) Summary
Blocks page core
src/pages/blocks/index.tsx
Destructure refetch as refetchYesterday and refetchTotal from queries; compute totalUpdatedAt, yesterdayUpdatedAt, and derived dataUpdatedAt (min of available timestamps or 0); polling now calls both refetches.
CardContent props & age logic
src/pages/blocks/index.tsx
Introduce CardContentProps extends ICard with required dataUpdatedAt: number; initialize and recompute card age from dataUpdatedAt; render age only when available; pass dataUpdatedAt into each CardContent.
API call error handling
src/services/apiCalls.ts
Refactored blockCall, yesterdayStatisticsCall, and totalStatisticsCall to use direct async/await (no new Promise wrapper); on failure they throw Error with descriptive messages instead of returning error payloads.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(135,206,235,0.5)
    participant Browser
    end
    rect rgba(144,238,144,0.5)
    participant BlocksComponent
    end
    rect rgba(255,182,193,0.5)
    participant ReactQuery
    end
    rect rgba(255,255,0,0.5)
    participant APIService
    end

    Browser->>BlocksComponent: load /blocks
    BlocksComponent->>ReactQuery: useQuery totalStatisticsCall
    BlocksComponent->>ReactQuery: useQuery yesterdayStatisticsCall
    ReactQuery->>APIService: fetch total/yesterday
    APIService-->>ReactQuery: return stats (+updatedAt)
    ReactQuery-->>BlocksComponent: provide data + dataUpdatedAt
    BlocksComponent->>BlocksComponent: compute dataUpdatedAt = min(...)
    BlocksComponent->>CardContent: render with dataUpdatedAt
    Note right of BlocksComponent: if polling enabled
    BlocksComponent->>ReactQuery: refetchTotal()
    BlocksComponent->>ReactQuery: refetchYesterday()
    ReactQuery->>APIService: refetch endpoints
    APIService-->>ReactQuery: return fresh stats (+updatedAt)
    ReactQuery-->>BlocksComponent: updated data + dataUpdatedAt
    BlocksComponent->>CardContent: update age from dataUpdatedAt
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped to fetch both paths today,

Timestamps now tell how fresh they stay.
Promise shells cracked, async flows sing,
Cards tick true with each refetching ping.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main fixes: stale auto-update, timer drift, and Promise anti-pattern in API calls.
Linked Issues check ✅ Passed The PR addresses all three coding objectives from issue #599: fixes auto-update refetch for both queries, derives age from dataUpdatedAt to reset timer on refetch, and replaces Promise anti-pattern with async/await.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the three issues specified in #599; no unrelated modifications detected in blocks page logic or API calls.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/blocks-page-auto-update-timer-and-api-pattern

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/pages/blocks/index.tsx (3)

264-272: Age doesn't immediately update when data refreshes.

When dataUpdatedAt changes (after a refetch), the effect clears the old interval and creates a new one, but the age state won't update until the next 1-second tick. This causes a brief delay where the displayed age is stale.

Consider setting the age immediately when dataUpdatedAt changes:

♻️ Proposed fix
     useEffect(() => {
+      setAge(getAge(new Date(dataUpdatedAt ?? Date.now())));
+
       const interval = setInterval(() => {
         setAge(getAge(new Date(dataUpdatedAt ?? Date.now())));
       }, 1 * 1000); // 1 sec

       return () => {
         clearInterval(interval);
       };
     }, [dataUpdatedAt]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/blocks/index.tsx` around lines 264 - 272, The age display is stale
for up to 1s after dataUpdatedAt changes because the effect only schedules the
first update on the next interval tick; modify the effect that uses setInterval
(the effect watching dataUpdatedAt) to call setAge(getAge(new Date(dataUpdatedAt
?? Date.now()))) immediately before creating the interval so the age updates
synchronously when dataUpdatedAt changes, then continue with the existing
interval/clearInterval logic in the same effect.

175-183: Missing dependencies in useEffect.

Both refetchYesterday and refetchTotal are used inside the effect but not included in the dependency array. While React Query's refetch functions are stable references, including them follows React best practices and prevents ESLint warnings.

♻️ Proposed fix
   useEffect(() => {
     if (blocksInterval) {
       const intervalId = setInterval(() => {
         refetchYesterday();
         refetchTotal();
       }, blocksWatcherInterval);
       return () => clearInterval(intervalId);
     }
-  }, [blocksInterval]);
+  }, [blocksInterval, refetchYesterday, refetchTotal]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/blocks/index.tsx` around lines 175 - 183, The effect uses
refetchYesterday, refetchTotal and blocksWatcherInterval but only lists
blocksInterval in its dependency array; update the useEffect dependency array to
include refetchYesterday, refetchTotal and blocksWatcherInterval (in addition to
blocksInterval) so the effect re-runs when any of those stable references change
and to satisfy React/ESLint expectations for useEffect, keeping the existing
interval setup (setInterval/clearInterval) and behavior intact.

254-296: Component defined inside parent causes unnecessary remounts.

CardContent is defined inside the Blocks component, which means it's recreated on every render. React treats each recreation as a new component type, causing full remounts that reset internal state (the age timer) and restart effects. This could cause the timer to flicker or reset unexpectedly during parent re-renders.

Consider moving CardContent outside the Blocks component to make it a stable reference.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/blocks/index.tsx` around lines 254 - 296, The CardContent component
is currently declared inside the Blocks component causing remounts; to fix, move
the CardContent declaration to module scope (outside Blocks) and keep its
signature as const CardContent: React.FC<PropsWithChildren<ICard>> = ({ title,
headers, values, dataUpdatedAt }) => { ... } so it receives the same props
(including dataUpdatedAt and getAge usage) but is a stable component reference;
optionally wrap the moved CardContent in React.memo(CardContent) to prevent
unnecessary re-renders when parent props don't change.
src/services/apiCalls.ts (1)

20-42: Inconsistent error handling in statistics functions.

While blockCall was correctly refactored to throw on error, yesterdayStatisticsCall and totalStatisticsCall still return res.error (a string) on the error path. This violates the declared Promise<IBlockStats> return type and prevents errors from propagating to React Query's error state.

Consider applying the same pattern used in blockCall for consistency:

♻️ Proposed fix for consistent error handling
 export const yesterdayStatisticsCall = async (): Promise<IBlockStats> => {
   const res = await api.get({
     route: 'block/statistics-by-day/1',
   });

   if (!res.error || res.error === '') {
     return res.data.block_stats_by_day[0];
-  } else {
-    return res.error;
   }
+
+  throw new Error(res.error);
 };

 export const totalStatisticsCall = async (): Promise<IBlockStats> => {
   const res = await api.get({
     route: 'block/statistics-total/0',
   });

   if (!res.error || res.error === '') {
     return res.data.block_stats_total;
-  } else {
-    return res.error;
   }
+
+  throw new Error(res.error);
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/apiCalls.ts` around lines 20 - 42, yesterdayStatisticsCall and
totalStatisticsCall currently return res.error (a string) on failure which
violates Promise<IBlockStats> and prevents errors from propagating; change them
to follow the same pattern as blockCall: if res.error is present (res.error &&
res.error !== '') throw a new Error(res.error) so the promise rejects, otherwise
return the correct data (res.data.block_stats_by_day[0] for
yesterdayStatisticsCall and res.data.block_stats_total for totalStatisticsCall);
reference the functions yesterdayStatisticsCall, totalStatisticsCall and the
api.get response object res when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/pages/blocks/index.tsx`:
- Around line 264-272: The age display is stale for up to 1s after dataUpdatedAt
changes because the effect only schedules the first update on the next interval
tick; modify the effect that uses setInterval (the effect watching
dataUpdatedAt) to call setAge(getAge(new Date(dataUpdatedAt ?? Date.now())))
immediately before creating the interval so the age updates synchronously when
dataUpdatedAt changes, then continue with the existing interval/clearInterval
logic in the same effect.
- Around line 175-183: The effect uses refetchYesterday, refetchTotal and
blocksWatcherInterval but only lists blocksInterval in its dependency array;
update the useEffect dependency array to include refetchYesterday, refetchTotal
and blocksWatcherInterval (in addition to blocksInterval) so the effect re-runs
when any of those stable references change and to satisfy React/ESLint
expectations for useEffect, keeping the existing interval setup
(setInterval/clearInterval) and behavior intact.
- Around line 254-296: The CardContent component is currently declared inside
the Blocks component causing remounts; to fix, move the CardContent declaration
to module scope (outside Blocks) and keep its signature as const CardContent:
React.FC<PropsWithChildren<ICard>> = ({ title, headers, values, dataUpdatedAt })
=> { ... } so it receives the same props (including dataUpdatedAt and getAge
usage) but is a stable component reference; optionally wrap the moved
CardContent in React.memo(CardContent) to prevent unnecessary re-renders when
parent props don't change.

In `@src/services/apiCalls.ts`:
- Around line 20-42: yesterdayStatisticsCall and totalStatisticsCall currently
return res.error (a string) on failure which violates Promise<IBlockStats> and
prevents errors from propagating; change them to follow the same pattern as
blockCall: if res.error is present (res.error && res.error !== '') throw a new
Error(res.error) so the promise rejects, otherwise return the correct data
(res.data.block_stats_by_day[0] for yesterdayStatisticsCall and
res.data.block_stats_total for totalStatisticsCall); reference the functions
yesterdayStatisticsCall, totalStatisticsCall and the api.get response object res
when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d3c17b8b-1e2f-44f1-bf78-b8cb6d78cfc6

📥 Commits

Reviewing files that changed from the base of the PR and between bf05ad2 and be69e99.

📒 Files selected for processing (3)
  • src/pages/blocks/index.tsx
  • src/services/apiCalls.ts
  • src/types/blocks.ts

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 6, 2026
… separation

- Fix yesterdayStatisticsCall and totalStatisticsCall to throw on error
  instead of silently returning error string as IBlockStats (was causing
  silent display of zeros on API failures)
- Normalize error messages with typeof guard for non-string errors
- Revert dataUpdatedAt from shared ICard type to local CardContentProps
- Track both queries' dataUpdatedAt using Math.min for accurate staleness
- Remove silent Date.now() fallback, show empty string before first fetch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/blocks/index.tsx (1)

260-306: ⚠️ Potential issue | 🟠 Major

Move CardContent component and CardContentProps interface outside the Blocks function.

Defining a component inside another component causes it to be recreated on every parent render. React treats each new function reference as a different component type, triggering unmount/remount cycles. This causes:

  1. Loss of internal state (age) on every parent re-render
  2. Interval being cleared and recreated unnecessarily
  3. Potential UI flickering
♻️ Proposed fix: Extract to module scope

Move the interface and component above the Blocks component:

+interface CardContentProps extends ICard {
+  dataUpdatedAt: number;
+}
+
+const CardContent: React.FC<PropsWithChildren<CardContentProps>> = ({
+  title,
+  headers,
+  values,
+  dataUpdatedAt,
+}) => {
+  const [age, setAge] = useState(
+    dataUpdatedAt ? getAge(new Date(dataUpdatedAt)) : '',
+  );
+
+  useEffect(() => {
+    const interval = setInterval(() => {
+      setAge(dataUpdatedAt ? getAge(new Date(dataUpdatedAt)) : '');
+    }, 1 * 1000);
+
+    return () => {
+      clearInterval(interval);
+    };
+  }, [dataUpdatedAt]);
+
+  return (
+    <Card>
+      <div>
+        <span>
+          <strong>{title}</strong>
+        </span>
+        <p>{age} ago</p>
+      </div>
+      <div>
+        <span>
+          <small>{headers[0]}</small>
+        </span>
+        <span>
+          <small>{headers[1]}</small>
+        </span>
+      </div>
+      <div>
+        <span>{values[0]}</span>
+        <span>{values[1]}</span>
+      </div>
+    </Card>
+  );
+};
+
 const Blocks: React.FC<PropsWithChildren<IBlocks>> = () => {
   // ... remove CardContentProps and CardContent from here
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/blocks/index.tsx` around lines 260 - 306, The CardContentProps
interface and CardContent component are currently declared inside the Blocks
function which causes remounts and state/interval loss; extract the interface
CardContentProps and the CardContent React.FC (which uses getAge and state/
useEffect for age) to module scope (move them above the Blocks component) and
keep the same props (title, headers, values, dataUpdatedAt) so Blocks simply
renders <CardContent ... />; ensure the useEffect still depends on dataUpdatedAt
and that the component remains in the same file or is exported/imported if moved
to a separate module.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/blocks/index.tsx`:
- Around line 270-272: The current initialization of age via const [age, setAge]
= useState(dataUpdatedAt ? getAge(new Date(dataUpdatedAt)) : '') can leave age
as an empty string and cause the UI to render " ago" before data loads; change
the initialization to a clear loading/placeholder value (e.g., null or
"Loading...") and update rendering to conditionally show the "ago" suffix only
when age is non-empty (adjust the render logic that uses age), and apply the
same fix to the other age state usage (the similar useState call around getAge)
so neither instance shows a stray " ago" while dataUpdatedAt is falsy.

---

Outside diff comments:
In `@src/pages/blocks/index.tsx`:
- Around line 260-306: The CardContentProps interface and CardContent component
are currently declared inside the Blocks function which causes remounts and
state/interval loss; extract the interface CardContentProps and the CardContent
React.FC (which uses getAge and state/ useEffect for age) to module scope (move
them above the Blocks component) and keep the same props (title, headers,
values, dataUpdatedAt) so Blocks simply renders <CardContent ... />; ensure the
useEffect still depends on dataUpdatedAt and that the component remains in the
same file or is exported/imported if moved to a separate module.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 31d30b1c-1846-47dd-828e-8ca8ee3ca81d

📥 Commits

Reviewing files that changed from the base of the PR and between be69e99 and d2a0b01.

📒 Files selected for processing (2)
  • src/pages/blocks/index.tsx
  • src/services/apiCalls.ts

@codecov
Copy link

codecov bot commented Mar 6, 2026

Codecov Report

❌ Patch coverage is 0% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.46%. Comparing base (bf05ad2) to head (e45c501).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
src/pages/blocks/index.tsx 0.00% 12 Missing ⚠️
src/services/apiCalls.ts 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           develop    #601      +/-   ##
==========================================
- Coverage     0.46%   0.46%   -0.01%     
==========================================
  Files          530     530              
  Lines        20951   20952       +1     
  Branches      4982    5287     +305     
==========================================
  Hits            97      97              
+ Misses       20854   20043     -811     
- Partials         0     812     +812     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/pages/blocks/index.tsx (2)

181-189: Consider adding refetch functions to the dependency array.

While refetch functions from React Query are typically stable references, including them in the dependency array satisfies the exhaustive-deps lint rule and makes the effect's dependencies explicit.

♻️ Suggested fix
   useEffect(() => {
     if (blocksInterval) {
       const intervalId = setInterval(() => {
         refetchYesterday();
         refetchTotal();
       }, blocksWatcherInterval);
       return () => clearInterval(intervalId);
     }
-  }, [blocksInterval]);
+  }, [blocksInterval, refetchYesterday, refetchTotal]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/blocks/index.tsx` around lines 181 - 189, The effect watching
blocksInterval should explicitly include its external dependencies: add
refetchYesterday, refetchTotal and blocksWatcherInterval to the useEffect
dependency array so the effect re-runs correctly and satisfies exhaustive-deps;
update the useEffect that references blocksInterval, refetchYesterday,
refetchTotal, and blocksWatcherInterval (and keep the existing clearInterval
cleanup) to include those symbols in the dependency list.

260-306: Move CardContent and CardContentProps outside of the Blocks component.

Defining a component inside another component causes React to create a new function reference on every parent render. This leads to unmounting and remounting all CardContent instances, which:

  • Resets internal state and restarts intervals unnecessarily
  • Causes extra DOM operations affecting performance

While the current behavior still works correctly (due to dataUpdatedAt prop re-initialization), extracting the component avoids these inefficiencies.

♻️ Suggested refactor

Move the interface and component to module scope (before the Blocks component):

+interface CardContentProps extends ICard {
+  dataUpdatedAt: number;
+}
+
+const CardContent: React.FC<PropsWithChildren<CardContentProps>> = ({
+  title,
+  headers,
+  values,
+  dataUpdatedAt,
+}) => {
+  const [age, setAge] = useState(
+    dataUpdatedAt ? getAge(new Date(dataUpdatedAt)) : '',
+  );
+
+  useEffect(() => {
+    const interval = setInterval(() => {
+      setAge(dataUpdatedAt ? getAge(new Date(dataUpdatedAt)) : '');
+    }, 1 * 1000);
+
+    return () => {
+      clearInterval(interval);
+    };
+  }, [dataUpdatedAt]);
+
+  return (
+    <Card>
+      <div>
+        <span>
+          <strong>{title}</strong>
+        </span>
+        <p>{age ? `${age} ago` : ''}</p>
+      </div>
+      <div>
+        <span>
+          <small>{headers[0]}</small>
+        </span>
+        <span>
+          <small>{headers[1]}</small>
+        </span>
+      </div>
+      <div>
+        <span>{values[0]}</span>
+        <span>{values[1]}</span>
+      </div>
+    </Card>
+  );
+};
+
 const Blocks: React.FC<PropsWithChildren<IBlocks>> = () => {
   // ... remove the internal CardContentProps and CardContent definitions
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/blocks/index.tsx` around lines 260 - 306, The CardContentProps
interface and CardContent component are defined inside the Blocks component
which causes a new function reference each render and forces remounts; move the
CardContentProps interface and the CardContent React.FC (including its
useState/useEffect logic that depends on dataUpdatedAt and getAge) to module
scope (i.e., outside and above the Blocks component) so CardContent is stable
across renders and no longer recreated on each Blocks render; ensure imports
(React, useState, useEffect, getAge, Card, ICard, PropsWithChildren) remain
available and update any references to CardContent and CardContentProps inside
Blocks accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/pages/blocks/index.tsx`:
- Around line 181-189: The effect watching blocksInterval should explicitly
include its external dependencies: add refetchYesterday, refetchTotal and
blocksWatcherInterval to the useEffect dependency array so the effect re-runs
correctly and satisfies exhaustive-deps; update the useEffect that references
blocksInterval, refetchYesterday, refetchTotal, and blocksWatcherInterval (and
keep the existing clearInterval cleanup) to include those symbols in the
dependency list.
- Around line 260-306: The CardContentProps interface and CardContent component
are defined inside the Blocks component which causes a new function reference
each render and forces remounts; move the CardContentProps interface and the
CardContent React.FC (including its useState/useEffect logic that depends on
dataUpdatedAt and getAge) to module scope (i.e., outside and above the Blocks
component) so CardContent is stable across renders and no longer recreated on
each Blocks render; ensure imports (React, useState, useEffect, getAge, Card,
ICard, PropsWithChildren) remain available and update any references to
CardContent and CardContentProps inside Blocks accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a7e18600-1119-44dc-93a0-e4c749d10740

📥 Commits

Reviewing files that changed from the base of the PR and between d2a0b01 and e45c501.

📒 Files selected for processing (1)
  • src/pages/blocks/index.tsx

@samufacanha2 samufacanha2 merged commit 2e46340 into develop Mar 9, 2026
4 of 5 checks passed
@samufacanha2 samufacanha2 deleted the fix/blocks-page-auto-update-timer-and-api-pattern branch March 9, 2026 15:08
samufacanha2 added a commit that referenced this pull request Mar 9, 2026
* Merge pull request #593 from klever-io/chore/remove-sc-24h-txs

chore: remove sc 24h txs

* Merge pull request #602 from klever-io/fix/nft-precision-tx-list

fix: nft precision tx list

* fix: blocks page stale auto-update, timer drift, and Promise anti-pattern (#601)

* fix: blocks page stale auto-update, timer drift, and Promise anti-pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review findings - consistent error handling, proper type separation

- Fix yesterdayStatisticsCall and totalStatisticsCall to throw on error
  instead of silently returning error string as IBlockStats (was causing
  silent display of zeros on API failures)
- Normalize error messages with typeof guard for non-string errors
- Revert dataUpdatedAt from shared ICard type to local CardContentProps
- Track both queries' dataUpdatedAt using Math.min for accurate staleness
- Remove silent Date.now() fallback, show empty string before first fetch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent rendering bare " ago" before data loads

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: David Ianakiara <david@klever.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blocks page: stale auto-update, broken timer, and Promise anti-pattern

2 participants