import React, { useState, useRef, useEffect } from 'react'; import { Model } from '../types'; interface ModelModalProps { model: Model | null; isOpen: boolean; onClose: () => void; onConfirm: (data: { file?: File; text?: string; mode: 'vc' | 'tts'; pitch: number }) => void; isLoading: boolean; subscriptionStatus?: 'free' | 'paid'; onUpgrade?: () => void; } const ModelModal: React.FC = ({ model, isOpen, onClose, onConfirm, isLoading, subscriptionStatus = 'free', onUpgrade }) => { const [activeMode, setActiveMode] = useState<'vc' | 'tts'>('vc'); const [file, setFile] = useState(null); const [fileUrl, setFileUrl] = useState(null); const [text, setText] = useState(''); const [pitch, setPitch] = useState(null); // Null initially to force selection const inputRef = useRef(null); // Reset state when modal opens useEffect(() => { if (isOpen) { setFile(null); setFileUrl(null); setText(''); setPitch(null); // Reset pitch } }, [isOpen]); // Clean up object URL useEffect(() => { return () => { if (fileUrl) URL.revokeObjectURL(fileUrl); }; }, [fileUrl]); const handleFileChange = (selectedFile: File) => { if (fileUrl) URL.revokeObjectURL(fileUrl); setFile(selectedFile); setFileUrl(URL.createObjectURL(selectedFile)); }; if (!isOpen || !model) return null; const handleConfirm = () => { if (activeMode === 'vc' && file) { onConfirm({ file, mode: 'vc', pitch: pitch ?? 0 }); } else if (activeMode === 'tts' && text.trim().length > 0) { onConfirm({ text, mode: 'tts', pitch: pitch ?? 0 }); } }; const isLegacy = model.type === 'legacy_rvc'; // Logic for pitch selection based on TARGET GENDER const targetIsFemale = model.targetGender === 'female'; const setPitchForGender = (inputGender: 'male' | 'female') => { if (targetIsFemale) { if (inputGender === 'male') setPitch(12); else setPitch(0); } else { if (inputGender === 'male') setPitch(0); else setPitch(-12); } }; const isMaleSelected = targetIsFemale ? pitch === 12 : pitch === 0; const isFemaleSelected = targetIsFemale ? pitch === 0 : pitch === -12; const isReady = activeMode === 'vc' ? (!!file && (!isLegacy || pitch !== null)) : (text.trim().length > 0 && (subscriptionStatus !== 'free' || text.length <= 500)); const activeColor = activeMode === 'vc' ? 'bg-gradient-to-r from-blue-500 to-indigo-600' : 'bg-gradient-to-r from-teal-400 to-teal-600'; const containerBorder = activeMode === 'vc' ? 'border-blue-100 bg-blue-50/30' : 'border-teal-100 bg-teal-50/30'; // Handle Model Image for Custom Models (Handle File/Blob) const getModelImage = () => { if (model.isCustom && model.image && typeof model.image !== 'string') { try { return URL.createObjectURL(model.image as any); } catch (e) { return ''; } } return (model.image as string) || ''; }; return (
{model.name}

{model.name}

{activeMode === 'vc' ? (
{!file && (
inputRef.current?.click()} className="flex-1 border-2 border-dashed border-blue-200 rounded-xl p-5 text-center cursor-pointer hover:border-blue-400 hover:bg-white/50 transition-all flex flex-col items-center justify-center min-h-[140px]" > e.target.files && e.target.files[0] && handleFileChange(e.target.files[0])} />

برای انتخاب فایل صوتی کلیک کنید

یا صدای خود را ضبط کنید

)} {file && (

پیش‌نمایش صدای شما

{isLegacy && (

جنسیت صدای این فایل چیست؟

setPitchForGender('male')} className={`flex-1 p-3 rounded-xl border-2 cursor-pointer transition-all flex flex-col items-center gap-2 shadow-sm ${isMaleSelected ? 'border-blue-500 bg-blue-600 text-white transform scale-105 shadow-blue-200' : 'border-gray-200 bg-white text-gray-500 hover:border-blue-200 hover:bg-gray-50'}`} > مرد
setPitchForGender('female')} className={`flex-1 p-3 rounded-xl border-2 cursor-pointer transition-all flex flex-col items-center gap-2 shadow-sm ${isFemaleSelected ? 'border-pink-500 bg-pink-500 text-white transform scale-105 shadow-pink-200' : 'border-gray-200 bg-white text-gray-500 hover:border-pink-200 hover:bg-gray-50'}`} > زن
)} {!isLegacy && (

برای شروع پردازش دکمه پایین را بزنید

)}
)}
) : (
{/* شمارشگر و وضعیت کاراکترها */}
500 && subscriptionStatus === 'free' ? 'text-red-500 font-bold' : 'text-gray-400'}> تعداد حروف: {text.length} {subscriptionStatus === 'free' && حداکثر ۵۰۰ حرف برای نسخه رایگان}
{/* باکس پیغام زیبای محدودیت کاراکتر و ارتقا */} {text.length > 500 && subscriptionStatus === 'free' && (

تعداد حروف بیشتر از حد مجاز است!

در نسخه رایگان حداکثر می‌توانید ۵۰۰ حرف را به گفتار تبدیل کنید. برای خروجی‌های طولانی‌تر لطفاً حساب خود را ارتقا دهید.

)}
)}
); }; export default ModelModal;