diff --git a/src/app/stocks/page.tsx b/src/app/stocks/page.tsx
index 73a3cab..aa9d04b 100644
--- a/src/app/stocks/page.tsx
+++ b/src/app/stocks/page.tsx
@@ -1,5 +1,5 @@
import { prisma } from '@/lib/prisma'
-import { formatCurrency } from '@/lib/utils'
+import { formatCurrency, pnlColor } from '@/lib/utils'
import Link from 'next/link'
import { ArrowUp, ArrowDown, ArrowUpDown, BarChart2, Building2 } from 'lucide-react'
import { formatDistanceToNow } from 'date-fns'
@@ -265,8 +265,6 @@ export default async function StocksPage({ searchParams }: PageProps) {
const prev = stock.previousPrice
const change = prev != null ? stock.currentPrice - prev : null
const changePct = prev != null && prev > 0 ? ((stock.currentPrice - prev) / prev) * 100 : null
- const up = change == null ? null : change >= 0
-
return (
—
) : (
-
- {up ? '+' : ''}{formatCurrency(change)}
+
+ {change > 0 ? '+' : ''}{formatCurrency(change)}
-
- {up ? '+' : ''}{changePct!.toFixed(2)}%
+
+ {changePct! > 0 ? '+' : ''}{changePct!.toFixed(2)}%
)}
diff --git a/src/components/HashtagCard.tsx b/src/components/HashtagCard.tsx
index b2297ab..7b297f6 100644
--- a/src/components/HashtagCard.tsx
+++ b/src/components/HashtagCard.tsx
@@ -1,5 +1,5 @@
import Link from 'next/link'
-import { TrendingUp, TrendingDown } from 'lucide-react'
+import { TrendingUp, TrendingDown, Minus } from 'lucide-react'
import { formatCurrency } from '@/lib/utils'
interface Props {
@@ -16,7 +16,7 @@ export function HashtagCard({ tag, displayTag, currentPrice, previousPrice, post
? ((currentPrice - previousPrice) / previousPrice) * 100
: null
- const up = pctChange === null ? null : pctChange >= 0
+ const up = pctChange === null ? null : pctChange > 0 ? 'up' : pctChange < 0 ? 'down' : 'flat'
return (
{formatCurrency(currentPrice)}
{pctChange !== null && (
- {up ? (
+ {up === 'up' ? (
- ) : (
+ ) : up === 'down' ? (
+ ) : (
+
)}
- {up ? '+' : ''}
+ {up === 'up' ? '+' : ''}
{pctChange.toFixed(1)}%
)}
diff --git a/src/lib/mastodon.ts b/src/lib/mastodon.ts
index 43d0d0b..10069f3 100644
--- a/src/lib/mastodon.ts
+++ b/src/lib/mastodon.ts
@@ -31,12 +31,11 @@ function extractTagsFromHtml(html: string): string[] {
return results
}
-async function fetchPage(tag: string, maxId?: string): Promise
{
+async function fetchPage(tag: string, maxId?: string, postLimit = 20): Promise {
const instance = process.env.MASTODON_INSTANCE
if (!instance) throw new Error('MASTODON_INSTANCE is not configured')
- let url = `${instance}/api/v1/timelines/tag/${encodeURIComponent(tag)}`
- // ?limit=50 was here but it seemed too aggressive. Default is 20 which feels more balanced for pricing purposes and reduces risk of hitting rate limits on very active tags.
+ let url = `${instance}/api/v1/timelines/tag/${encodeURIComponent(tag)}?limit=${postLimit}`
if (maxId) url += `&max_id=${maxId}`
const headers: HeadersInit = { Accept: 'application/json' }
@@ -83,6 +82,7 @@ export async function getPostsData(
tag: string,
): Promise<{ postsPerHour: number; relatedTags: string[]; displayTag?: string }> {
const maxPages = parseInt(process.env.MAX_PAGES_PER_HASHTAG ?? '5', 10)
+ const postLimit = Math.min(parseInt(process.env.MASTODON_POST_LIMIT ?? '20', 10), 40)
const ONE_HOUR_MS = 60 * 60 * 1000
const now = Date.now()
const cutoff = now - ONE_HOUR_MS
@@ -91,13 +91,13 @@ export async function getPostsData(
let maxId: string | undefined
for (let page = 0; page < maxPages; page++) {
- const { posts, nextMaxId } = await fetchPage(tag, maxId)
+ const { posts, nextMaxId } = await fetchPage(tag, maxId, postLimit)
if (posts.length === 0) break
allPosts = [...allPosts, ...posts]
// End of timeline or no more pages
- if (posts.length < 40 || !nextMaxId) break
+ if (posts.length < postLimit || !nextMaxId) break
// If the oldest post in this batch is already beyond 1 hour, we have a full window
const oldestInBatch = Math.min(...posts.map((p) => new Date(p.created_at).getTime()))