diff --git a/src/app/chain/page.tsx b/src/app/chain/page.tsx new file mode 100644 index 00000000..38c0b635 --- /dev/null +++ b/src/app/chain/page.tsx @@ -0,0 +1,191 @@ +"use client"; + +import { useState } from "react"; +import { useAccount } from "wagmi"; +import { useQuery } from "@tanstack/react-query"; +import { ConnectWallet } from "../../components/ConnectWallet"; +import { + validateContentLength, + MIN_CONTENT_LENGTH, + MAX_CONTENT_LENGTH, +} from "../../../lib/content"; +import { supabase, type Storyline } from "../../../lib/supabase"; +import { useChainPlot } from "../../hooks/useChainPlot"; +import type { PublishState } from "../../hooks/usePublish"; +import Link from "next/link"; + +const STATE_LABELS: Record = { + idle: "", + uploading: "Uploading to IPFS...", + confirming: "Confirm in wallet...", + pending: "Publishing to Base...", + indexing: "Indexing...", + published: "Published!", + error: "Error", +}; + +async function fetchWriterStorylines(address: string): Promise { + if (!supabase) return []; + const { data } = await supabase + .from("storylines") + .select("*") + .eq("writer_address", address.toLowerCase()) + .eq("hidden", false) + .eq("sunset", false) + .order("block_timestamp", { ascending: false }) + .returns(); + return data ?? []; +} + +export default function ChainPlotPage() { + const { address, isConnected } = useAccount(); + const [storylineId, setStorylineId] = useState(null); + const [content, setContent] = useState(""); + + const { data: storylines = [], isLoading: loadingStorylines } = useQuery({ + queryKey: ["writer-active-storylines", address], + queryFn: () => fetchWriterStorylines(address!), + enabled: isConnected && !!address, + }); + + const { state, error, chainPlot, reset } = useChainPlot(); + const { valid, charCount } = validateContentLength(content); + const canSubmit = + (state === "idle" || state === "error") && + storylineId !== null && + valid; + + if (!isConnected) { + return ( +
+

+ Connect your wallet to chain a plot. +

+ +
+ ); + } + + if (state === "published") { + return ( +
+

Plot chained!

+
+ {storylineId && ( + + View story + + )} + +
+
+ ); + } + + const busy = state !== "idle" && state !== "error"; + + return ( +
+

+ Chain Plot +

+ +
{ + e.preventDefault(); + if (canSubmit) chainPlot(storylineId, content); + }} + className="mt-8 space-y-6" + > + {/* Storyline selector */} +
+ + {loadingStorylines ? ( +

Loading storylines...

+ ) : storylines.length === 0 ? ( +

+ No active storylines.{" "} + + Create one + +

+ ) : ( + + )} +
+ + {/* Content */} +
+ +