diff --git a/src/app/story/[storylineId]/page.tsx b/src/app/story/[storylineId]/page.tsx
index 1ca218ff..eb74791c 100644
--- a/src/app/story/[storylineId]/page.tsx
+++ b/src/app/story/[storylineId]/page.tsx
@@ -8,7 +8,7 @@ import { PriceChart } from "../../../components/PriceChart";
import { DonateWidget } from "../../../components/DonateWidget";
import { RatingWidget } from "../../../components/RatingWidget";
import { RatingSummary } from "../../../components/RatingSummary";
-import { ShareToFarcaster } from "../../../components/ShareToFarcaster";
+import { ShareButtons } from "../../../components/ShareButtons";
import { getTokenPrice, type TokenPriceInfo } from "../../../../lib/price";
import { RESERVE_LABEL, STORY_FACTORY } from "../../../../lib/contracts/constants";
import { formatPrice, formatSupply } from "../../../../lib/format";
@@ -164,9 +164,9 @@ export default async function StoryPage({ params }: { params: Params }) {
/>
)}
- {/* Share — visible on mobile (sidebar hidden) */}
-
-
+ {/* Share buttons — below chapters */}
+
+
@@ -185,7 +185,6 @@ export default async function StoryPage({ params }: { params: Params }) {
{sl.token_address && (
)}
-
diff --git a/src/components/ShareButtons.tsx b/src/components/ShareButtons.tsx
new file mode 100644
index 00000000..d961753d
--- /dev/null
+++ b/src/components/ShareButtons.tsx
@@ -0,0 +1,151 @@
+"use client";
+
+import { useState, useCallback } from "react";
+import { usePlatformDetection } from "../hooks/usePlatformDetection";
+
+interface ShareButtonsProps {
+ storylineId: number;
+ title: string;
+}
+
+export function ShareButtons({ storylineId, title }: ShareButtonsProps) {
+ const { platform } = usePlatformDetection();
+ const [copied, setCopied] = useState(false);
+
+ const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
+ const storyUrl = `${appUrl}/story/${storylineId}`;
+ const shareText = `Check out "${title}" on PlotLink`;
+
+ const handleShareX = useCallback(() => {
+ const fullText = `${shareText}\n${storyUrl}`;
+ window.open(
+ `https://twitter.com/intent/tweet?text=${encodeURIComponent(fullText)}`,
+ "_blank",
+ );
+ }, [shareText, storyUrl]);
+
+ const handleShareFarcaster = useCallback(async () => {
+ if (platform === "farcaster") {
+ try {
+ const { sdk } = await import("@farcaster/miniapp-sdk");
+ await sdk.actions.composeCast({
+ text: shareText,
+ embeds: [storyUrl],
+ });
+ return;
+ } catch {
+ // Fall through to intent URL
+ }
+ }
+ const fullText = `${shareText}\n${storyUrl}`;
+ window.open(
+ `https://farcaster.com/~/compose?text=${encodeURIComponent(fullText)}`,
+ "_blank",
+ );
+ }, [platform, shareText, storyUrl]);
+
+ const handleCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(storyUrl);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch {
+ // Clipboard API may fail in some contexts
+ }
+ }, [storyUrl]);
+
+ return (
+
+ {/* Share to X */}
+
+
+ {/* Share to Farcaster */}
+
+
+ {/* Copy Link */}
+
+
+ );
+}
+
+function XIcon() {
+ return (
+
+ );
+}
+
+function FarcasterIcon() {
+ return (
+
+ );
+}
+
+function CopyIcon() {
+ return (
+
+ );
+}
+
+function CheckIcon() {
+ return (
+
+ );
+}