From d56db2637800ddc5449772af66a8739b7370dc34 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Fri, 13 Feb 2026 09:01:44 +0100 Subject: [PATCH] feat(share): add native share button with fallback menu --- web/src/components/ShareButton.tsx | 123 +++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 web/src/components/ShareButton.tsx diff --git a/web/src/components/ShareButton.tsx b/web/src/components/ShareButton.tsx new file mode 100644 index 0000000..d4a84ba --- /dev/null +++ b/web/src/components/ShareButton.tsx @@ -0,0 +1,123 @@ +import { useState } from 'react'; +import type { Article } from '../api/articles'; + +interface ShareButtonProps { + article: Article; + className?: string; +} + +export function ShareButton({ article, className = "" }: ShareButtonProps) { + const [isOpen, setIsOpen] = useState(false); + const [copied, setCopied] = useState(false); + + const shareData = { + title: article.title, + text: article.summary ? article.summary.substring(0, 100) + '...' : article.title, + url: article.url, + }; + + const handleShare = async () => { + // Try Native Share API first (Mobile & Supported Browsers) + if (navigator.share) { + try { + await navigator.share(shareData); + return; + } catch (err) { + console.log('Error sharing:', err); + // If user cancelled, do nothing. If error, maybe fallback? + // Usually if navigator.share exists, we trust it unless it throws. + // But on some desktops it might exist but fail. + } + } + + // Fallback to custom menu + setIsOpen(!isOpen); + }; + + const copyToClipboard = () => { + navigator.clipboard.writeText(article.url); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + setIsOpen(false); + }; + + const openLink = (url: string) => { + window.open(url, '_blank', 'noopener,noreferrer'); + setIsOpen(false); + }; + + return ( +
+ + + {/* Desktop Fallback Menu */} + {isOpen && ( + <> +
setIsOpen(false)}>
+
+
+ + + + +
+ +
+
+ + )} +
+ ); +}