189 lines
8.8 KiB
TypeScript
189 lines
8.8 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { X, Copy, Check, Share2, Smartphone, Link as LinkIcon } from 'lucide-react';
|
|
|
|
/**
|
|
* ShareModal 컴포넌트의 Props 정의
|
|
* @interface ShareModalProps
|
|
* @property {string} videoUrl - 공유할 비디오 파일의 URL
|
|
* @property {string} posterUrl - (현재 사용되지 않지만, 공유 데이터에 포함될 수 있는) 포스터 이미지 URL
|
|
* @property {string} businessName - 비디오의 비즈니스 이름 (파일 이름 등에 사용)
|
|
* @property {() => void} onClose - 모달을 닫을 때 호출될 콜백 함수
|
|
*/
|
|
interface ShareModalProps {
|
|
videoUrl: string;
|
|
posterUrl: string; // 현재 사용되지 않음
|
|
businessName: string;
|
|
onClose: () => void;
|
|
}
|
|
|
|
/**
|
|
* 공유 모달 컴포넌트
|
|
* 생성된 비디오를 공유하기 위한 옵션을 제공합니다. (링크 복사, 파일 직접 공유 등)
|
|
*/
|
|
const ShareModal: React.FC<ShareModalProps> = ({ videoUrl, businessName, onClose }) => {
|
|
const [isGenerating, setIsGenerating] = useState(true); // 공유 링크 생성 중 여부
|
|
const [shareLink, setShareLink] = useState(''); // 생성된 공유 링크
|
|
const [copied, setCopied] = useState(false); // 링크 복사 성공 여부
|
|
const [canShareFile, setCanShareFile] = useState(false); // Web Share API로 파일 공유 가능한지 여부
|
|
|
|
/**
|
|
* 컴포넌트 마운트 시 공유 링크를 생성하고 Web Share API 지원 여부를 확인합니다.
|
|
*/
|
|
useEffect(() => {
|
|
// 고유 ID를 기반으로 목업 공유 링크를 생성합니다. (실제 서비스에서는 백엔드에서 생성)
|
|
const uniqueId = Math.random().toString(36).substring(2, 10);
|
|
const mockLink = `${window.location.origin}/share/${uniqueId}`; // 실제 앱에서는 호스팅된 비디오 URL이 됩니다.
|
|
|
|
// 링크 생성 시뮬레이션 (1.5초 후 완료)
|
|
const timer = setTimeout(() => {
|
|
setShareLink(mockLink);
|
|
setIsGenerating(false);
|
|
}, 1500);
|
|
|
|
// Web Share API (파일 공유) 지원 여부 확인
|
|
if (navigator.share && navigator.canShare) {
|
|
setCanShareFile(true);
|
|
}
|
|
|
|
return () => clearTimeout(timer); // 컴포넌트 언마운트 시 타이머 정리
|
|
}, []);
|
|
|
|
/**
|
|
* 공유 링크를 클립보드에 복사하는 핸들러
|
|
*/
|
|
const handleCopy = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(shareLink);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000); // 2초 후 복사 상태 초기화
|
|
} catch (err) {
|
|
console.error('링크 복사 실패:', err);
|
|
alert("링크 복사에 실패했습니다. 수동으로 복사해주세요.");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 네이티브 웹 공유 API를 사용하여 비디오 파일을 직접 공유하는 핸들러
|
|
* Instagram, KakaoTalk 등 모바일 앱으로 직접 공유할 때 유용합니다.
|
|
*/
|
|
const handleNativeShare = async () => {
|
|
try {
|
|
// 비디오 Blob URL을 File 객체로 변환하여 공유 데이터에 포함시킵니다.
|
|
const response = await fetch(videoUrl);
|
|
const blob = await response.blob();
|
|
// 파일 이름은 업체명과 광고로 구성
|
|
const file = new File([blob], `${businessName.replace(/\s+/g, '_')}_광고.mp4`, { type: 'video/mp4' });
|
|
|
|
const shareData = {
|
|
title: `${businessName} AI 광고 영상`,
|
|
text: 'BizVibe로 제작된 AI 음악 비디오 광고를 확인해보세요!',
|
|
files: [file] // 공유할 파일 배열
|
|
};
|
|
|
|
// 파일 공유가 가능한지 다시 확인 후 공유
|
|
if (navigator.canShare(shareData)) {
|
|
await navigator.share(shareData);
|
|
} else {
|
|
// 파일 공유가 지원되지 않을 경우, 텍스트와 링크만 공유하는 폴백
|
|
await navigator.share({
|
|
title: shareData.title,
|
|
text: shareData.text,
|
|
url: shareLink
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.error('공유 중 오류 발생:', err);
|
|
alert("공유 기능 사용 중 오류가 발생했습니다.");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm animate-in fade-in duration-200"> {/* 모달 배경 및 페이드인 애니메이션 */}
|
|
<div className="relative w-full max-w-md mx-4 bg-[#1a1a1d] border border-purple-500/30 rounded-2xl shadow-2xl overflow-hidden">
|
|
|
|
{/* 모달 헤더 */}
|
|
<div className="p-6 border-b border-white/10 flex items-center justify-between bg-white/5">
|
|
<h3 className="text-xl font-bold text-white flex items-center gap-2">
|
|
<Share2 className="w-5 h-5 text-purple-400" />
|
|
공유하기
|
|
</h3>
|
|
<button
|
|
onClick={onClose} // 모달 닫기 버튼
|
|
className="p-2 rounded-full hover:bg-white/10 text-gray-400 hover:text-white transition-colors"
|
|
>
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="p-6 space-y-6">
|
|
{isGenerating ? (
|
|
// 링크 생성 중일 때 로딩 스피너 표시
|
|
<div className="text-center py-8 space-y-4">
|
|
<div className="relative w-16 h-16 mx-auto">
|
|
<div className="absolute inset-0 border-4 border-purple-500/30 rounded-full"></div>
|
|
<div className="absolute inset-0 border-4 border-t-purple-500 rounded-full animate-spin"></div>
|
|
</div>
|
|
<p className="text-gray-300 animate-pulse">고유 공유 링크 생성 중...</p>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{/* 고유 링크 섹션 */}
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-bold text-gray-400 uppercase tracking-wider flex items-center gap-1">
|
|
<LinkIcon className="w-3 h-3" /> 공개 링크
|
|
</label>
|
|
<div className="flex items-center gap-2 p-2 bg-black/50 rounded-xl border border-gray-700">
|
|
<input
|
|
type="text"
|
|
readOnly // 읽기 전용
|
|
value={shareLink}
|
|
className="flex-1 bg-transparent text-sm text-purple-300 outline-none font-mono"
|
|
/>
|
|
<button
|
|
onClick={handleCopy} // 복사 버튼
|
|
className={`p-2 rounded-lg transition-all ${
|
|
copied
|
|
? 'bg-green-500/20 text-green-400' // 복사 완료 시 초록색 강조
|
|
: 'bg-white/10 hover:bg-white/20 text-white'
|
|
}`}
|
|
>
|
|
{copied ? <Check className="w-4 h-4" /> : <Copy className="w-4 h-4" />} {/* 복사 아이콘 변경 */}
|
|
</button>
|
|
</div>
|
|
<p className="text-[10px] text-gray-500">
|
|
* 이 링크는 데모용이며 실제로는 호스팅되지 않습니다. 비디오 파일을 직접 공유하려면 아래 버튼을 사용하세요.
|
|
</p>
|
|
</div>
|
|
|
|
{/* 네이티브 공유 섹션 */}
|
|
{canShareFile && (
|
|
<button
|
|
onClick={handleNativeShare} // 네이티브 공유 버튼
|
|
className="w-full py-4 bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-500 hover:to-pink-500 text-white font-bold rounded-xl shadow-lg shadow-purple-900/30 transform transition-all hover:scale-[1.02] flex items-center justify-center gap-3"
|
|
>
|
|
<Smartphone className="w-5 h-5" />
|
|
영상 파일 직접 공유 (Instagram, Kakao 등)
|
|
</button>
|
|
)}
|
|
|
|
{/* 소셜 아이콘 목업 (클릭 시 새 탭 열림) */}
|
|
<div className="grid grid-cols-3 gap-3 pt-2">
|
|
{['Twitter', 'Facebook', 'LinkedIn'].map((platform) => (
|
|
<button
|
|
key={platform}
|
|
className="py-3 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 text-gray-400 hover:text-white text-xs font-medium transition-all"
|
|
onClick={() => window.open(`https://twitter.com/intent/tweet?text=${encodeURIComponent('BizVibe로 만든 AI 음악 비디오를 확인해보세요! ' + shareLink)}`, '_blank')} // 소셜 공유 링크 생성
|
|
>
|
|
{platform}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ShareModal; |