diff --git a/src/app/discover/page.tsx b/src/app/discover/page.tsx new file mode 100644 index 00000000..8a5a1d02 --- /dev/null +++ b/src/app/discover/page.tsx @@ -0,0 +1,82 @@ +import { createServerClient, type Storyline } from "../../../lib/supabase"; +import { StoryCard } from "../../components/StoryCard"; +import { TabNav } from "../../components/TabNav"; + +type SearchParams = Promise<{ tab?: string }>; + +const TABS = ["new", "trending", "rising", "completed"] as const; +type Tab = (typeof TABS)[number]; + +export default async function DiscoverPage({ + searchParams, +}: { + searchParams: SearchParams; +}) { + const { tab: rawTab } = await searchParams; + const tab: Tab = TABS.includes(rawTab as Tab) ? (rawTab as Tab) : "new"; + + const supabase = createServerClient(); + if (!supabase) { + return ( +
+

Database unavailable

+
+ ); + } + + let storylines: Storyline[] = []; + + if (tab === "completed") { + const { data } = await supabase + .from("storylines") + .select("*") + .eq("hidden", false) + .eq("sunset", true) + .order("plot_count", { ascending: false }) + .limit(50) + .returns(); + storylines = data ?? []; + } else { + // "new" is the default; "trending" and "rising" fall back to "new" ordering + // until trading data is available (Phase 5) + const { data } = await supabase + .from("storylines") + .select("*") + .eq("hidden", false) + .eq("sunset", false) + .order("block_timestamp", { ascending: false }) + .limit(50) + .returns(); + storylines = data ?? []; + } + + return ( +
+

+ Discover +

+

+ Browse stories on PlotLink +

+ + + + {(tab === "trending" || tab === "rising") && ( +

+ Ranking by recency — trading-based ranking available after Phase 5. +

+ )} + +
+ {storylines.map((s) => ( + + ))} + {storylines.length === 0 && ( +

+ No stories found. +

+ )} +
+
+ ); +} diff --git a/src/components/StoryCard.tsx b/src/components/StoryCard.tsx new file mode 100644 index 00000000..ef18a37f --- /dev/null +++ b/src/components/StoryCard.tsx @@ -0,0 +1,47 @@ +import Link from "next/link"; +import type { Storyline } from "../../lib/supabase"; + +function truncateAddress(address: string): string { + return `${address.slice(0, 6)}...${address.slice(-4)}`; +} + +export function StoryCard({ + storyline, + genre, +}: { + storyline: Storyline; + genre?: string; +}) { + return ( + +
+

+ {storyline.title} +

+ {storyline.sunset && ( + complete + )} +
+
+ {truncateAddress(storyline.writer_address)} + + {storyline.plot_count}{" "} + {storyline.plot_count === 1 ? "plot" : "plots"} + + {genre && ( + + {genre} + + )} + {storyline.writer_type === 1 && ( + + agent + + )} +
+ + ); +} diff --git a/src/components/TabNav.tsx b/src/components/TabNav.tsx new file mode 100644 index 00000000..caf3f55b --- /dev/null +++ b/src/components/TabNav.tsx @@ -0,0 +1,29 @@ +import Link from "next/link"; + +export function TabNav({ + tabs, + active, + className, +}: { + tabs: readonly string[]; + active: string; + className?: string; +}) { + return ( + + ); +}