();
+
+ const plots = plotRows ?? [];
+
+ return (
+
+
+
+ {plots.map((plot) => (
+
+ ))}
+
+ {plots.length === 0 && (
+
No plots yet.
+ )}
+
+ );
+}
+
+function StoryHeader({ storyline }: { storyline: Storyline }) {
+ return (
+
+
+ {storyline.title}
+
+
+
+ by{" "}
+
+ {truncateAddress(storyline.writer_address)}
+
+
+
+ {storyline.plot_count} {storyline.plot_count === 1 ? "plot" : "plots"}
+
+ {storyline.writer_type === 1 && (
+
+ agent
+
+ )}
+
+ {storyline.sunset ? (
+
+ Story complete
+
+ {storyline.plot_count} {storyline.plot_count === 1 ? "plot" : "plots"} total
+
+
+ ) : storyline.has_deadline && storyline.last_plot_time ? (
+
+ ) : null}
+
+ );
+}
+
+function PlotEntry({ plot }: { plot: Plot }) {
+ return (
+
+
+
+ {plot.plot_index === 0 ? "Genesis" : `Plot #${plot.plot_index}`}
+
+ {plot.block_timestamp && (
+
+ )}
+
+ {plot.content ? (
+
+ {plot.content}
+
+ ) : (
+
+ Content unavailable (CID: {plot.content_cid})
+
+ )}
+
+ );
+}
+
+function NotFound({ message }: { message: string }) {
+ return (
+
+ );
+}
diff --git a/src/components/DeadlineCountdown.tsx b/src/components/DeadlineCountdown.tsx
new file mode 100644
index 00000000..32ab7c14
--- /dev/null
+++ b/src/components/DeadlineCountdown.tsx
@@ -0,0 +1,45 @@
+"use client";
+
+import { useState, useEffect } from "react";
+
+const DEADLINE_HOURS = 72;
+
+export function DeadlineCountdown({ lastPlotTime }: { lastPlotTime: string }) {
+ const [remaining, setRemaining] = useState(() => calcRemaining(lastPlotTime));
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setRemaining(calcRemaining(lastPlotTime));
+ }, 1000);
+ return () => clearInterval(interval);
+ }, [lastPlotTime]);
+
+ if (remaining <= 0) {
+ return (
+
+ Deadline expired
+
+ );
+ }
+
+ const hours = Math.floor(remaining / 3600);
+ const minutes = Math.floor((remaining % 3600) / 60);
+ const seconds = remaining % 60;
+
+ return (
+
+ Deadline:
+
+ {String(hours).padStart(2, "0")}:{String(minutes).padStart(2, "0")}:
+ {String(seconds).padStart(2, "0")}
+
+ remaining
+
+ );
+}
+
+function calcRemaining(lastPlotTime: string): number {
+ const deadline =
+ new Date(lastPlotTime).getTime() + DEADLINE_HOURS * 60 * 60 * 1000;
+ return Math.max(0, Math.floor((deadline - Date.now()) / 1000));
+}