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 ( + + + + ); +}