diff --git a/src/app/page.tsx b/src/app/page.tsx index 3ccaefa..6260335 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,9 +2,13 @@ import { prisma } from '@/lib/prisma' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { HashtagCard } from '@/components/HashtagCard' -import { TrendingUp, Users, Hash } from 'lucide-react' +import { TrendingUp, Users, Hash, AlertTriangle } from 'lucide-react' import Link from 'next/link' import { formatPnl, pnlColor } from '@/lib/utils' +import { formatDistanceToNow } from 'date-fns' + +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) export const dynamic = 'force-dynamic' export const revalidate = 0 @@ -59,10 +63,33 @@ async function getHoldings(userId: string) { } } +async function getZombieWarnings(userId: string) { + const threshold = Math.floor(ZOMBIE_ZERO_COUNT * 0.9) + const positions = await prisma.position.findMany({ + where: { userId, shares: { gt: 0 }, hashtag: { zeroCount: { gte: threshold } } }, + select: { hashtag: { select: { tag: true, displayTag: true, zeroCount: true } } }, + }) + return positions.map((p) => ({ + tag: p.hashtag.tag, + displayTag: p.hashtag.displayTag, + zombieSince: formatDistanceToNow( + new Date(Date.now() - p.hashtag.zeroCount * PRICE_UPDATE_INTERVAL_MINUTES * 60 * 1000), + ), + zombieEta: formatDistanceToNow( + new Date(Date.now() + (ZOMBIE_ZERO_COUNT - p.hashtag.zeroCount) * PRICE_UPDATE_INTERVAL_MINUTES * 60 * 1000), + { addSuffix: true }, + ), + })) +} + export default async function HomePage() { const session = await getServerSession(authOptions) - const [{ userCount, hashtagCount, tradeCount, topHashtags, recentTrades }, holdings] = - await Promise.all([getStats(), session ? getHoldings(session.user.id) : Promise.resolve(null)]) + const [{ userCount, hashtagCount, tradeCount, topHashtags, recentTrades }, holdings, zombieWarnings] = + await Promise.all([ + getStats(), + session ? getHoldings(session.user.id) : Promise.resolve(null), + session ? getZombieWarnings(session.user.id) : Promise.resolve([]), + ]) return (
+ The following hashtags have had no activity for an extended period and may be auto-liquidated soon. + Consider closing these positions manually. +
+