import React, { useState, useRef, useEffect } from 'react'; import { MODELS } from '../constants'; import { Model } from '../types'; interface ModelGridProps { customModels: Model[]; onSelectModel: (model: Model) => void; onOpenCustom: () => void; onAddNewModel: () => void; onEditModel: (model: Model) => void; } const ModelGrid: React.FC = ({ customModels, onSelectModel, onOpenCustom, onAddNewModel, onEditModel }) => { const [activeTab, setActiveTab] = useState('all'); const [playingModelId, setPlayingModelId] = useState(null); const audioRef = useRef(null); const categories: { key: string; title: string; icon: string }[] = [ { key: 'singers', title: 'خوانندگان', icon: 'fa-music' }, { key: 'dubbers', title: 'دوبلورها', icon: 'fa-microphone-alt' }, { key: 'cartoons', title: 'کارتون', icon: 'fa-child' }, { key: 'famous', title: 'مشاهیر', icon: 'fa-star' }, { key: 'alpha', title: 'گویندگان آلفا', icon: 'fa-user-tie' }, ]; useEffect(() => { return () => { if (audioRef.current) { audioRef.current.pause(); audioRef.current = null; } }; }, []); const togglePreview = (e: React.MouseEvent, model: Model) => { e.stopPropagation(); if (playingModelId === model.id) { if (audioRef.current) { audioRef.current.pause(); audioRef.current = null; } setPlayingModelId(null); return; } if (audioRef.current) audioRef.current.pause(); let audioSrc = model.sampleAudio; // If no sample, and it's custom, maybe use the ref file if it's a blob? // Actually standard models have sampleAudio, custom ones might use their ref as sample if (!audioSrc && model.isCustom && typeof model.refFile !== 'string') { audioSrc = URL.createObjectURL(model.refFile); } if (audioSrc) { const audio = new Audio(audioSrc); audioRef.current = audio; audio.onerror = () => setPlayingModelId(null); audio.play().catch(() => setPlayingModelId(null)); setPlayingModelId(model.id); audio.onended = () => { setPlayingModelId(null); audioRef.current = null; }; } }; const renderModelCard = (model: Model) => { // Handling image URL for custom models (Blobs) const imageUrl = (model.isCustom && typeof model.image !== 'string') ? URL.createObjectURL(model.image as any) : model.image; return (
onSelectModel(model)} className="group relative cursor-pointer rounded-2xl overflow-hidden aspect-square shadow-sm hover:shadow-xl transition-all duration-300 active:scale-95 bg-gray-100" > {model.name}
{/* Play Button */} {/* Edit Button for Custom Models */} {model.isCustom && ( )}
{model.name} انتخاب مدل
); }; return (
{/* Banner / CTA */}

هنوز مدل دلخواهت رو پیدا نکردی؟

ساخت صدای اختصاصی با آپلود فایل

{/* Category Tabs */}
{categories.map((cat) => ( ))}
{/* Grid Content */}
{categories.map((category) => { if (activeTab !== 'all' && activeTab !== category.key) return null; const categoryModels = MODELS.filter(m => m.category === category.key); const isAlpha = category.key === 'alpha'; return (

{category.title}

{activeTab === 'all' &&
}
{categoryModels.map(renderModelCard)} {/* Special Logic: User Custom Models next to Maryam (Alpha) */} {isAlpha && customModels.map(renderModelCard)} {/* PLUS BUTTON */} {isAlpha && (
ساخت مدل صدای دلخواه
)}
); })}
); }; export default ModelGrid;