You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: Create a self-updating TimeAgo React component that avoids unnecessary re-renders (#3564)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## Summary
Closes#3563
All `timeAgo` strings across the UI were static — they only refreshed
when the user interacted with the UI. This PR creates a self-updating
`TimeAgo` React component and replaces all static `formatTimeAgo()`
usages with it, so timestamps stay accurate without user interaction.
## Changes
### New `TimeAgo` component (`web_src/src/components/TimeAgo/`)
- Self-updating React component that renders a human-readable relative
time string (e.g., `3s ago`, `2m ago`)
- Uses a **single shared global timer** (`setInterval` at 1s) for all
instances — efficient even with many events visible
- Only re-renders when the displayed text actually changes (e.g., `"2m
ago"` stays stable for a minute), minimizing render work for older
timestamps
- Wrapped in `React.memo` to prevent unnecessary parent re-renders
- Properly cleans up the global timer when all instances unmount (no
memory leaks)
- Split into separate files for ESLint `react-refresh` compliance:
- `TimeAgo.tsx` — the component itself
- `helpers.ts` — `renderTimeAgo(date)` and `renderWithTimeAgo(prefix,
date, separator?)`
- `index.ts` — barrel re-exports
### Replaced all static `timeAgo` usages
- **Direct UI components**: `componentBase`, `ChainItem`,
`ExecutionChainPage` now use `<TimeAgo>` directly
- **All mapper subtitle functions** (~160+ files): Replaced
`formatTimeAgo()` with `renderTimeAgo()` for standalone usage or
`renderWithTimeAgo()` for "prefix · timeAgo" patterns
- **All trigger renderers** (~40+ files): Updated to use
`renderTimeAgo()` / `renderWithTimeAgo()` for trigger event subtitles
- **Utility functions** (`utils.ts`): Updated sidebar event and queue
item subtitle generation
- **Type definitions**: Updated `TriggerLastEventData.subtitle`,
`TriggerRenderer.getTitleAndSubtitle`, and various mapper types to
support `React.ReactNode`
### Bug fixes
- Fixed tooltip showing `[object Object]` in `componentBase/index.tsx`
by using a `typeof` check instead of `String()` coercion for ReactNode
subtitles
- Fixed template literal interpolation of `renderTimeAgo()` results
(which return `React.ReactNode`) that would produce `[object Object]` —
replaced with `renderWithTimeAgo()` helper
### What's preserved
- `formatTimeAgo()` utility function is kept in `@/utils/date` for
non-React contexts (e.g., `.replace(" ago", "")` duration text
extraction in `utils.ts`)
<!-- CURSOR_AGENT_PR_BODY_END -->
<div><a
href="https://cursor.com/agents/bc-b9343003-3036-498a-8fae-d541bf47c5a9"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a
href="https://cursor.com/background-agent?bcId=bc-b9343003-3036-498a-8fae-d541bf47c5a9"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
---------
Signed-off-by: Pedro F. Leao <pedroforestileao@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Pedro F. Leao <pedroforestileao@gmail.com>
0 commit comments