Restored to 'da5c38c23546b6ac8177dac9774fb40023d30283'

Replit-Restored-To: da5c38c235
This commit is contained in:
sebastjanartic
2025-08-04 20:37:56 +00:00
parent 5bbf9e6c1b
commit 76f430775e
9 changed files with 13 additions and 538 deletions
-154
View File
@@ -1,154 +0,0 @@
import { useState } from "react";
import { Copy, Check, Share2 } from "lucide-react";
import {
SiX,
SiFacebook,
SiLinkedin,
SiWhatsapp,
SiTelegram,
SiReddit
} from "react-icons/si";
import { Button } from "@/components/ui/button";
import { type Video } from "@shared/schema";
interface SocialShareProps {
video: Video;
className?: string;
}
export function SocialShare({ video, className = "" }: SocialShareProps) {
const [copied, setCopied] = useState(false);
const [shareOpen, setShareOpen] = useState(false);
// Generate shareable URL (in production, this would be your actual domain)
const shareUrl = `${window.location.origin}/?video=${video.id}`;
const shareText = `Check out this video: ${video.title}`;
const shareTextEncoded = encodeURIComponent(shareText);
const shareUrlEncoded = encodeURIComponent(shareUrl);
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(shareUrl);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('Failed to copy link:', err);
}
};
const socialPlatforms = [
{
name: "X",
icon: SiX,
url: `https://twitter.com/intent/tweet?text=${shareTextEncoded}&url=${shareUrlEncoded}`,
color: "hover:bg-gray-900 hover:text-white",
},
{
name: "Facebook",
icon: SiFacebook,
url: `https://www.facebook.com/sharer/sharer.php?u=${shareUrlEncoded}`,
color: "hover:bg-blue-600 hover:text-white",
},
{
name: "LinkedIn",
icon: SiLinkedin,
url: `https://www.linkedin.com/sharing/share-offsite/?url=${shareUrlEncoded}`,
color: "hover:bg-blue-700 hover:text-white",
},
{
name: "WhatsApp",
icon: SiWhatsapp,
url: `https://wa.me/?text=${shareTextEncoded}%20${shareUrlEncoded}`,
color: "hover:bg-green-500 hover:text-white",
},
{
name: "Telegram",
icon: SiTelegram,
url: `https://t.me/share/url?url=${shareUrlEncoded}&text=${shareTextEncoded}`,
color: "hover:bg-blue-400 hover:text-white",
},
{
name: "Reddit",
icon: SiReddit,
url: `https://reddit.com/submit?url=${shareUrlEncoded}&title=${shareTextEncoded}`,
color: "hover:bg-orange-500 hover:text-white",
},
];
const handlePlatformShare = (platform: typeof socialPlatforms[0]) => {
window.open(platform.url, '_blank', 'width=600,height=400');
};
return (
<div className={`relative ${className}`}>
<Button
variant="ghost"
size="sm"
onClick={() => setShareOpen(!shareOpen)}
className="text-gray-300 hover:text-white transition-colors"
data-testid="button-share-toggle"
>
<Share2 className="w-4 h-4 mr-2" />
Share
</Button>
{shareOpen && (
<>
{/* Backdrop */}
<div
className="fixed inset-0 z-40"
onClick={() => setShareOpen(false)}
/>
{/* Share Menu */}
<div className="absolute bottom-full right-0 mb-2 bg-gray-800 rounded-lg p-4 shadow-xl z-50 min-w-64">
<h3 className="text-white font-semibold mb-3">Share this video</h3>
{/* Copy Link */}
<div className="mb-4">
<Button
variant="secondary"
size="sm"
onClick={handleCopyLink}
className="w-full justify-start"
data-testid="button-copy-link"
>
{copied ? (
<>
<Check className="w-4 h-4 mr-2 text-green-500" />
Copied!
</>
) : (
<>
<Copy className="w-4 h-4 mr-2" />
Copy Link
</>
)}
</Button>
</div>
{/* Social Platforms */}
<div className="grid grid-cols-3 gap-2">
{socialPlatforms.map((platform) => {
const IconComponent = platform.icon;
return (
<Button
key={platform.name}
variant="ghost"
size="sm"
onClick={() => handlePlatformShare(platform)}
className={`flex flex-col items-center p-3 h-auto ${platform.color} transition-colors`}
data-testid={`button-share-${platform.name.toLowerCase()}`}
>
<IconComponent className="w-5 h-5 mb-1" />
<span className="text-xs">{platform.name}</span>
</Button>
);
})}
</div>
</div>
</>
)}
</div>
);
}
+1 -39
View File
@@ -1,6 +1,5 @@
import { Play, Share2 } from "lucide-react";
import { Play } from "lucide-react";
import { type Video } from "@shared/schema";
import { Button } from "@/components/ui/button";
interface VideoCardProps {
video: Video;
@@ -41,30 +40,6 @@ function formatDate(date: Date | string): string {
}
export default function VideoCard({ video, onClick }: VideoCardProps) {
const handleShare = async (e: React.MouseEvent) => {
e.stopPropagation(); // Prevent video modal from opening
const shareUrl = `${window.location.origin}/?video=${video.id}`;
const shareData = {
title: video.title,
text: `Check out this video: ${video.title}`,
url: shareUrl,
};
try {
if (navigator.share) {
// Use native share API if available (mobile devices)
await navigator.share(shareData);
} else {
// Fallback: copy to clipboard
await navigator.clipboard.writeText(shareUrl);
alert('Video link copied to clipboard!');
}
} catch (err) {
console.error('Error sharing:', err);
}
};
return (
<div
className="group cursor-pointer"
@@ -90,19 +65,6 @@ export default function VideoCard({ video, onClick }: VideoCardProps) {
{formatDuration(video.duration)}
</span>
</div>
{/* Share button */}
<div className="absolute top-3 right-3 opacity-0 group-hover:opacity-100 transition-opacity">
<Button
variant="secondary"
size="sm"
onClick={handleShare}
className="bg-black/80 hover:bg-black/90 text-white border-0 h-8 w-8 p-0"
data-testid={`button-share-${video.id}`}
>
<Share2 className="w-4 h-4" />
</Button>
</div>
</div>
<div className="space-y-2">
+11 -15
View File
@@ -1,10 +1,9 @@
import { useEffect, useRef } from "react";
import { X, Share2, Copy, Check } from "lucide-react";
import { X } from "lucide-react";
import { type Video } from "@shared/schema";
import { Button } from "@/components/ui/button";
import { apiRequest } from "@/lib/queryClient";
import Hls from "hls.js";
import { SocialShare } from "@/components/social-share";
interface VideoModalProps {
video: Video | null;
@@ -213,19 +212,16 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
>
{video.title}
</h3>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4 text-sm text-gray-300">
<span data-testid="text-modal-views">
{formatViews(video.views)}
</span>
<span data-testid="text-modal-date">
{formatDate(video.createdAt)}
</span>
<span data-testid="text-modal-duration">
{formatDuration(video.duration)}
</span>
</div>
<SocialShare video={video} />
<div className="flex items-center space-x-4 text-sm text-gray-300">
<span data-testid="text-modal-views">
{formatViews(video.views)}
</span>
<span data-testid="text-modal-date">
{formatDate(video.createdAt)}
</span>
<span data-testid="text-modal-duration">
{formatDuration(video.duration)}
</span>
</div>
{video.description && (
<p className="mt-3 text-gray-300 text-sm" data-testid="text-modal-description">