From 005b4543f6aba32a839ce186c8b2ee7a6f5c086c Mon Sep 17 00:00:00 2001 From: Mike Johnston Date: Thu, 19 Mar 2026 22:10:50 -0400 Subject: [PATCH] feat: add AutoRefresh component for automatic data refreshing on pages --- src/app/fund/[slug]/page.tsx | 2 ++ src/app/hashtag/[tag]/page.tsx | 4 +++- src/app/leaderboard/page.tsx | 2 ++ src/app/page.tsx | 3 ++- src/app/positions/page.tsx | 2 ++ src/app/stocks/page.tsx | 4 +++- src/components/AutoRefresh.tsx | 28 ++++++++++++++++++++++++++++ 7 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 src/components/AutoRefresh.tsx diff --git a/src/app/fund/[slug]/page.tsx b/src/app/fund/[slug]/page.tsx index 50ef2fd..d474250 100644 --- a/src/app/fund/[slug]/page.tsx +++ b/src/app/fund/[slug]/page.tsx @@ -6,6 +6,7 @@ import { formatCurrency, formatPnl, pnlColor } from '@/lib/utils' import { formatDistanceToNow } from 'date-fns' import Link from 'next/link' import { Building2, TrendingUp, TrendingDown } from 'lucide-react' +import { AutoRefresh } from '@/components/AutoRefresh' import { calcFundNav } from '@/lib/pricing' import InvestPanel from './InvestPanel' import { PriceChart } from '@/components/PriceChart' @@ -88,6 +89,7 @@ export default async function FundPage({ params }: { params: { slug: string } }) return (
+ {/* Header */}
diff --git a/src/app/hashtag/[tag]/page.tsx b/src/app/hashtag/[tag]/page.tsx index d055d10..650fe0d 100644 --- a/src/app/hashtag/[tag]/page.tsx +++ b/src/app/hashtag/[tag]/page.tsx @@ -9,6 +9,7 @@ import { ResearchPanel } from './ResearchPanel' import { Hash, Clock, Link as LinkIcon, AlertTriangle } from 'lucide-react' import { formatDistanceToNow } from 'date-fns' import Link from 'next/link' +import { AutoRefresh } from '@/components/AutoRefresh' const ZOMBIE_ZERO_COUNT = parseInt(process.env.ZOMBIE_ZERO_COUNT ?? '1000', 10) const PRICE_UPDATE_INTERVAL_MINUTES = parseInt(process.env.PRICE_UPDATE_INTERVAL_MINUTES ?? '60', 10) @@ -126,7 +127,8 @@ export default async function HashtagPage({ params, searchParams }: Props) { return (
- {/* Header */} + + {/* Header */}}

#{hashtag.displayTag}

diff --git a/src/app/leaderboard/page.tsx b/src/app/leaderboard/page.tsx index 47b0230..33987e7 100644 --- a/src/app/leaderboard/page.tsx +++ b/src/app/leaderboard/page.tsx @@ -5,6 +5,7 @@ import { formatCurrency } from '@/lib/utils' import { calcFundNav } from '@/lib/pricing' import Link from 'next/link' import { Trophy, TrendingUp, TrendingDown, Building2, Users } from 'lucide-react' +import { AutoRefresh } from '@/components/AutoRefresh' export const dynamic = 'force-dynamic' @@ -119,6 +120,7 @@ export default async function LeaderboardPage({ return (
+
diff --git a/src/app/page.tsx b/src/app/page.tsx index 6260335..0da1647 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -6,6 +6,7 @@ import { TrendingUp, Users, Hash, AlertTriangle } from 'lucide-react' import Link from 'next/link' import { formatPnl, pnlColor } from '@/lib/utils' import { formatDistanceToNow } from 'date-fns' +import { AutoRefresh } from '@/components/AutoRefresh' const ZOMBIE_ZERO_COUNT = parseInt(process.env.ZOMBIE_ZERO_COUNT ?? '1000', 10) const PRICE_UPDATE_INTERVAL_MINUTES = parseInt(process.env.PRICE_UPDATE_INTERVAL_MINUTES ?? '60', 10) @@ -93,7 +94,7 @@ export default async function HomePage() { return (
- {/* Hero */} + }

The{' '} diff --git a/src/app/positions/page.tsx b/src/app/positions/page.tsx index 1d7b4eb..e9fdba6 100644 --- a/src/app/positions/page.tsx +++ b/src/app/positions/page.tsx @@ -5,6 +5,7 @@ import { redirect } from 'next/navigation' import { formatCurrency, formatNumber, formatPnl, pnlColor } from '@/lib/utils' import Link from 'next/link' import { Coins, ChevronUp, ChevronDown, ChevronsUpDown } from 'lucide-react' +import { AutoRefresh } from '@/components/AutoRefresh' export const dynamic = 'force-dynamic' @@ -130,6 +131,7 @@ export default async function PositionsPage({ return (
+

Open Positions

diff --git a/src/app/stocks/page.tsx b/src/app/stocks/page.tsx index 7a5b5f3..6483031 100644 --- a/src/app/stocks/page.tsx +++ b/src/app/stocks/page.tsx @@ -2,6 +2,7 @@ import { prisma } from '@/lib/prisma' import { formatCurrency, pnlColor } from '@/lib/utils' import Link from 'next/link' import { ArrowUp, ArrowDown, ArrowUpDown, BarChart2, Building2 } from 'lucide-react' +import { AutoRefresh } from '@/components/AutoRefresh' import { formatDistanceToNow } from 'date-fns' import { calcFundNav } from '@/lib/pricing' @@ -197,7 +198,8 @@ export default async function StocksPage({ searchParams }: PageProps) { return (
- {/* Header */} + + {/* Header */}}
diff --git a/src/components/AutoRefresh.tsx b/src/components/AutoRefresh.tsx new file mode 100644 index 0000000..fc7ddb4 --- /dev/null +++ b/src/components/AutoRefresh.tsx @@ -0,0 +1,28 @@ +'use client' + +import { useRouter } from 'next/navigation' +import { useEffect } from 'react' + +/** + * Silently refreshes all server-component data on the current page by calling + * router.refresh() on an interval and whenever the tab regains focus. + * + * Drop this anywhere inside a server-component page — it renders nothing. + */ +export function AutoRefresh({ intervalMs = 30_000 }: { intervalMs?: number }) { + const router = useRouter() + + useEffect(() => { + const id = setInterval(() => router.refresh(), intervalMs) + + const onFocus = () => router.refresh() + document.addEventListener('visibilitychange', onFocus) + + return () => { + clearInterval(id) + document.removeEventListener('visibilitychange', onFocus) + } + }, [router, intervalMs]) + + return null +}