Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/admin/import/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export default function ImportPage() {
</div>
) : (
<div className="py-8 text-center text-muted-foreground">
<AlertCircle className="mx-auto mb-4 size-12 opacity-50" />
<AlertCircle className="size-12 mx-auto mb-4 opacity-50" />
<p>Import results will appear here after running an import.</p>
</div>
)}
Expand Down
62 changes: 58 additions & 4 deletions app/api/articles/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server"

import { getArticlesByTopics } from "@/lib/articles"
import { getArticlesByTopics, getRecentArticles } from "@/lib/articles"

// Cache for 5 minutes
export const revalidate = 300
Expand All @@ -16,11 +16,65 @@ export async function GET(request: NextRequest) {
const topicTypesParam = searchParams.get("topicTypes")
const sortBy = searchParams.get("sortBy") || "score"

// Validate required parameters
// If no topics provided, return recent articles
if (!topicsParam) {
// Validate timeFilter
const validTimeFilters = ["24h", "7d", "30d", "all"]
if (!validTimeFilters.includes(timeFilter)) {
return NextResponse.json(
{ error: "timeFilter must be one of: 24h, 7d, 30d, all" },
{ status: 400 }
)
}

// Convert timeFilter to hours
let timeWindow: number | null = null
switch (timeFilter) {
case "24h":
timeWindow = 24
break
case "7d":
timeWindow = 168
break
case "30d":
timeWindow = 720
break
case "all":
timeWindow = null
break
}

// Get recent articles
const articles = await getRecentArticles({
timeWindow,
limit: 50,
})

// Always sort by time for recent articles
const sortedArticles = [...articles].sort(
(a, b) =>
new Date(b.published_at).getTime() -
new Date(a.published_at).getTime()
)

return NextResponse.json(
{ error: "topics parameter is required" },
{ status: 400 }
{
articles: sortedArticles,
metadata: {
totalResults: sortedArticles.length,
timeFilter,
searchType: "recent" as const,
sortBy: "time",
generatedAt: new Date().toISOString(),
},
},
{
headers: {
"Cache-Control": "public, s-maxage=300, stale-while-revalidate=600",
"CDN-Cache-Control": "public, s-maxage=300",
"Vercel-CDN-Cache-Control": "public, s-maxage=300",
},
}
)
}

Expand Down
4 changes: 2 additions & 2 deletions app/api/topics/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export async function GET(request: NextRequest) {
)
}

if (limit < 1 || limit > 50) {
if (limit < 1 || limit > 200) {
return NextResponse.json(
{ error: "limit must be between 1 and 50" },
{ error: "limit must be between 1 and 200" },
{ status: 400 }
)
}
Expand Down
10 changes: 5 additions & 5 deletions app/deep-dive/burger-infographic/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -419,31 +419,31 @@ export default function BurgerInfographic() {
your best burger yet.
</p>
<div className="flex flex-col items-center justify-center space-y-4 md:flex-row md:space-x-4 md:space-y-0">
<div className="flex size-24 shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<div className="size-24 flex shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<span>1. Preheat Pan</span>
</div>
<div className="mx-4 rotate-90 text-4xl text-[#00796B] md:rotate-0">
&rarr;
</div>
<div className="flex size-24 shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<div className="size-24 flex shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<span>2. Add Patty</span>
</div>
<div className="mx-4 rotate-90 text-4xl text-[#00796B] md:rotate-0">
&rarr;
</div>
<div className="flex size-24 shrink-0 items-center justify-center rounded-full bg-[#FF5E5B] text-center font-bold text-[#FFFFEA]">
<div className="size-24 flex shrink-0 items-center justify-center rounded-full bg-[#FF5E5B] text-center font-bold text-[#FFFFEA]">
<span>3. Flip every 30s</span>
</div>
<div className="mx-4 rotate-90 text-4xl text-[#00796B] md:rotate-0">
&rarr;
</div>
<div className="flex size-24 shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<div className="size-24 flex shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<span>4. Add Cheese</span>
</div>
<div className="mx-4 rotate-90 text-4xl text-[#00796B] md:rotate-0">
&rarr;
</div>
<div className="flex size-24 shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<div className="size-24 flex shrink-0 items-center justify-center rounded-full bg-[#00CECB] text-center font-bold text-[#FFFFEA]">
<span>5. Rest & Serve</span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/deep-dive/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function DeepDivePage() {
<section className="px-4 py-8">
<div className="container mx-auto max-w-4xl text-center">
<h1 className="mb-6 text-4xl font-bold text-foreground">Deep Dive</h1>
<p className="mb-12 text-pretty text-xl text-muted-foreground">
<p className="text-pretty mb-12 text-xl text-muted-foreground">
Immerse yourself in comprehensive explorations of the most
wonderfully pointless topics. From detailed infographics to in-depth
articles, discover the hidden depths of useless knowledge.
Expand Down
118 changes: 112 additions & 6 deletions app/discover/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export default function DiscoverPage() {
const [searchState, setSearchState] = useState<SearchState>({
mode: "topic",
topics: [],
timeFilter: "7d",
sortBy: "score",
timeFilter: "24h",
sortBy: "time",
textQuery: "",
articles: [],
isLoading: false,
Expand Down Expand Up @@ -80,6 +80,78 @@ export default function DiscoverPage() {
fetchStats()
}, [])

// Function to fetch recent articles
const fetchRecentArticles = useCallback(async () => {
setSearchState((prev) => {
// Only fetch if no topics selected and no text query
if (prev.topics.length === 0 && !prev.textQuery) {
// Start loading
const loadingState = { ...prev, isLoading: true, error: null }

// Fetch articles asynchronously
const params = new URLSearchParams({
timeFilter: prev.timeFilter,
sortBy: "time",
})

fetch(`/api/articles?${params}`)
.then((response) => {
if (!response.ok) {
return response.json().then((errorData) => {
throw new Error(errorData.error || "Failed to fetch articles")
})
}
return response.json()
})
.then((data) => {
setSearchState((current) => ({
...current,
articles: data.articles,
isLoading: false,
error: null,
}))
})
.catch((error) => {
console.error("Error fetching recent articles:", error)
setSearchState((current) => ({
...current,
isLoading: false,
error:
error instanceof Error
? error.message
: "Failed to fetch recent articles",
}))
})

return loadingState
}
return prev
})
}, [])

// Fetch recent articles on mount when no search is active
useEffect(() => {
fetchRecentArticles()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) // Only run on mount

// Refetch recent articles when time filter changes (if no active search)
useEffect(() => {
if (
shouldTriggerSearch.current &&
searchState.topics.length === 0 &&
!searchState.textQuery
) {
shouldTriggerSearch.current = false
fetchRecentArticles()
}
}, [
searchState.timeFilter,
searchState.topics.length,
searchState.textQuery,
fetchRecentArticles,
])

const performTextSearch = useCallback(
async (query: string) => {
setSearchState((prev) => ({ ...prev, isLoading: true, error: null }))
Expand Down Expand Up @@ -184,6 +256,10 @@ export default function DiscoverPage() {
articles: [],
error: null,
}))
// If switching away from search with no active search, fetch recent articles
if (searchState.topics.length === 0 && !searchState.textQuery) {
shouldTriggerSearch.current = true
}
}

const handleTopicsChange = (topics: string[]) => {
Expand All @@ -192,6 +268,10 @@ export default function DiscoverPage() {

const handleTimeFilterChange = (timeFilter: TimeFilter) => {
setSearchState((prev) => ({ ...prev, timeFilter }))
// If no active search, refetch recent articles with new time filter
if (searchState.topics.length === 0 && !searchState.textQuery) {
shouldTriggerSearch.current = true
}
}

const handleSortChange = (sortBy: SortBy) => {
Expand All @@ -210,6 +290,9 @@ export default function DiscoverPage() {
searchState.textQuery.length >= 3
) {
performTextSearch(searchState.textQuery)
} else if (searchState.topics.length === 0 && !searchState.textQuery) {
// Refetch recent articles if no active search
fetchRecentArticles()
}
}
}, [
Expand All @@ -219,6 +302,7 @@ export default function DiscoverPage() {
searchState.textQuery,
performTopicSearch,
performTextSearch,
fetchRecentArticles,
])

const handleTextQueryChange = (textQuery: string) => {
Expand Down Expand Up @@ -275,7 +359,7 @@ export default function DiscoverPage() {
: "text-foreground opacity-50"
}`}
>
<TrendingUp className="h-4 w-4 text-primary sm:size-6" />
<TrendingUp className="sm:size-6 h-4 w-4 text-primary" />
<span className="whitespace-nowrap">By Topics</span>
</Button>
<Button
Expand All @@ -288,7 +372,7 @@ export default function DiscoverPage() {
: "text-foreground opacity-50"
}`}
>
<Search className="h-4 w-4 text-primary sm:size-6" />
<Search className="sm:size-6 h-4 w-4 text-primary" />
<span className="whitespace-nowrap">Vector Search</span>
</Button>
</div>
Expand Down Expand Up @@ -406,6 +490,28 @@ export default function DiscoverPage() {
</Card>
)}

{/* Default View Message */}
{searchState.topics.length === 0 &&
!searchState.textQuery &&
!searchState.isLoading &&
searchState.articles.length > 0 && (
<Card className="mb-6 border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-900/20">
<CardContent className="p-4">
<p className="text-sm text-blue-800 dark:text-blue-200">
<span className="font-medium">
Displaying most recent news from{" "}
{TIME_FILTER_OPTIONS.find(
(opt) => opt.value === searchState.timeFilter
)?.label.toLowerCase()}
.
</span>{" "}
Select topics and search articles, or adjust the time filter
to explore more content.
</p>
</CardContent>
</Card>
)}

{/* Active Filters Display */}
{(searchState.topics.length > 0 || searchState.textQuery) && (
<Card className="mb-6">
Expand All @@ -429,7 +535,7 @@ export default function DiscoverPage() {
)}

<Badge variant="outline">
<Clock className="mr-1 w-3 h-3" />
<Clock className="mr-1 h-3 w-3" />
{
TIME_FILTER_OPTIONS.find(
(opt) => opt.value === searchState.timeFilter
Expand All @@ -438,7 +544,7 @@ export default function DiscoverPage() {
</Badge>

<Badge variant="outline">
<Filter className="mr-1 w-3 h-3" />
<Filter className="mr-1 h-3 w-3" />
{searchState.sortBy === "score"
? "Most Relevant"
: "Most Recent"}
Expand Down
2 changes: 1 addition & 1 deletion app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function NotFound() {
<div className="container mx-auto max-w-2xl text-center">
<Card className="border-primary/20 from-primary/5 to-secondary/5 bg-gradient-to-br">
<CardHeader className="text-center">
<div className="from-primary/20 to-secondary/20 mx-auto mb-4 flex size-20 items-center justify-center rounded-full bg-gradient-to-br">
<div className="from-primary/20 to-secondary/20 size-20 mx-auto mb-4 flex items-center justify-center rounded-full bg-gradient-to-br">
<div className="text-4xl">🤔</div>
</div>
<CardTitle className="text-4xl font-bold text-foreground">
Expand Down
6 changes: 3 additions & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ export default function UselessFactsHome() {
{/* Hero Section */}
<section className="px-4 py-8">
<div className="container mx-auto max-w-4xl text-center">
<h2 className="mb-6 text-balance text-3xl font-semibold text-foreground">
<h2 className="text-balance mb-6 text-3xl font-semibold text-foreground">
Discover Facts You&apos;ll Never Need
</h2>
<p className="mb-6 text-pretty text-xl text-muted-foreground">
<p className="text-pretty mb-6 text-xl text-muted-foreground">
Expand your mind with wonderfully pointless information that&apos;s
guaranteed to impress absolutely no one.
</p>
Expand All @@ -38,7 +38,7 @@ export default function UselessFactsHome() {
</Link>{" "}
to discover the most loved and hated facts in our collection.
</p>
<p className="mt-6 text-pretty text-lg text-muted-foreground">
<p className="text-pretty mt-6 text-lg text-muted-foreground">
Prefer to browse everything yourself? Explore our full catalog on
the{" "}
<Link
Expand Down
4 changes: 2 additions & 2 deletions components/article-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export function ArticleList({
<Card className="border-muted">
<CardContent className="p-8">
<div className="text-center">
<div className="bg-muted/50 mx-auto mb-4 flex size-12 items-center justify-center rounded-full">
<div className="bg-muted/50 size-12 mx-auto mb-4 flex items-center justify-center rounded-full">
<TrendingUp className="size-6 text-muted-foreground" />
</div>
<h3 className="mb-2 text-lg font-semibold">No articles found</h3>
Expand Down Expand Up @@ -265,7 +265,7 @@ export function ArticleList({
variant="outline"
className="border-blue-200 bg-blue-100 text-xs text-blue-800 hover:bg-blue-200 dark:border-blue-800 dark:bg-blue-900/20 dark:text-blue-300 dark:hover:bg-blue-900/30"
>
<Hash className="mr-1 w-3 h-3" />
<Hash className="mr-1 h-3 w-3" />
{topic}
</Badge>
))}
Expand Down
Loading