folx-tv/client/src/components/share-buttons.tsx
sebastjanartic 7c72650f90 Improve social media sharing functionality and image handling
Update social sharing buttons to use popup windows for a more reliable user experience. Adjust Facebook sharer URL format and ensure optimal OG image delivery by defaulting to JPG format and setting cache headers. Update image URLs in gallery data.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1f7e7e89-a520-4970-9645-37daadc466dc
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 4a846f8c-2e0e-44e4-bcf6-7ac503fb3b19
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/1f7e7e89-a520-4970-9645-37daadc466dc/ZApZ5Qi
Replit-Helium-Checkpoint-Created: true
2026-03-05 14:39:35 +00:00

143 lines
4.9 KiB
TypeScript

import { SiFacebook, SiLinkedin, SiX, SiWhatsapp, SiTelegram, SiPinterest } from "react-icons/si";
import { Mail, Link2, Check } from "lucide-react";
import { useState } from "react";
interface ShareButtonsProps {
url: string;
title: string;
image?: string;
}
const CANONICAL_DOMAIN = "https://www.folx.tv";
function canonicalUrl(url: string): string {
try {
const parsed = new URL(url);
return `${CANONICAL_DOMAIN}${parsed.pathname}${parsed.search}${parsed.hash}`;
} catch {
return url;
}
}
function openPopup(shareUrl: string) {
const w = 600;
const h = 500;
const left = window.screenX + (window.outerWidth - w) / 2;
const top = window.screenY + (window.outerHeight - h) / 2;
window.open(shareUrl, "share", `width=${w},height=${h},left=${left},top=${top},toolbar=no,menubar=no,scrollbars=yes`);
}
interface ShareTarget {
name: string;
icon: typeof SiFacebook;
color: string;
getUrl: (url: string, title: string, image?: string) => string;
popup?: boolean;
}
const SHARE_TARGETS: ShareTarget[] = [
{
name: "Facebook",
icon: SiFacebook,
color: "hover:bg-[#1877F2] hover:text-white",
getUrl: (url: string) => `https://www.facebook.com/sharer.php?u=${encodeURIComponent(url)}`,
popup: true,
},
{
name: "X",
icon: SiX,
color: "hover:bg-[#000] hover:text-white",
getUrl: (url: string, title: string) => `https://x.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`,
popup: true,
},
{
name: "LinkedIn",
icon: SiLinkedin,
color: "hover:bg-[#0A66C2] hover:text-white",
getUrl: (url: string) => `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`,
popup: true,
},
{
name: "WhatsApp",
icon: SiWhatsapp,
color: "hover:bg-[#25D366] hover:text-white",
getUrl: (url: string, title: string) => `https://wa.me/?text=${encodeURIComponent(title + " " + url)}`,
},
{
name: "Telegram",
icon: SiTelegram,
color: "hover:bg-[#26A5E4] hover:text-white",
getUrl: (url: string, title: string) => `https://t.me/share/url?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`,
},
{
name: "Pinterest",
icon: SiPinterest,
color: "hover:bg-[#BD081C] hover:text-white",
getUrl: (url: string, title: string, image?: string) => `https://pinterest.com/pin/create/button/?url=${encodeURIComponent(url)}&description=${encodeURIComponent(title)}${image ? "&media=" + encodeURIComponent(image) : ""}`,
popup: true,
},
{
name: "E-Mail",
icon: Mail,
color: "hover:bg-muted-foreground hover:text-white",
getUrl: (url: string, title: string) => `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(title + "\n\n" + url)}`,
},
];
export default function ShareButtons({ url, title, image }: ShareButtonsProps) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(canonicalUrl(url));
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch {
const input = document.createElement("input");
input.value = canonicalUrl(url);
document.body.appendChild(input);
input.select();
document.execCommand("copy");
document.body.removeChild(input);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
const handleShare = (target: ShareTarget, e: React.MouseEvent) => {
const shareUrl = target.getUrl(canonicalUrl(url), title, image ? canonicalUrl(image) : undefined);
if (target.popup) {
e.preventDefault();
openPopup(shareUrl);
}
};
return (
<div className="flex flex-wrap items-center gap-2" data-testid="share-buttons">
<span className="text-sm text-muted-foreground mr-1">Teilen:</span>
{SHARE_TARGETS.map((target) => (
<a
key={target.name}
href={target.getUrl(canonicalUrl(url), title, image ? canonicalUrl(image) : undefined)}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => handleShare(target, e)}
className={`inline-flex items-center justify-center w-9 h-9 rounded-full border border-border text-muted-foreground transition-all duration-200 ${target.color}`}
aria-label={`Auf ${target.name} teilen`}
data-testid={`button-share-${target.name.toLowerCase()}`}
>
<target.icon className="w-4 h-4" />
</a>
))}
<button
onClick={handleCopy}
className={`inline-flex items-center justify-center w-9 h-9 rounded-full border border-border transition-all duration-200 ${copied ? "bg-green-600 text-white border-green-600" : "text-muted-foreground hover:bg-muted-foreground hover:text-white"}`}
aria-label="Link kopieren"
data-testid="button-share-copy"
>
{copied ? <Check className="w-4 h-4" /> : <Link2 className="w-4 h-4" />}
</button>
</div>
);
}