import { useState, useRef, useEffect, useCallback } from 'react'; import { createPortal } from 'react-dom'; import Picker from '@emoji-mart/react'; import data from '@emoji-mart/data'; import { Search, TrendingUp, Loader2 } from 'lucide-react'; import { useLang } from '../lib/i18n'; import { useAuthStore } from '../stores/authStore'; import { api } from '../lib/api'; interface KlipyGif { id: string; files?: any; file?: any; media_formats?: any; media?: any; images?: any; title?: string; } interface EmojiPickerProps { onSelect: (emoji: string) => void; onSelectGif?: (url: string, preview: string) => void; onClose: () => void; } export default function EmojiPicker({ onSelect, onSelectGif, onClose }: EmojiPickerProps) { const { lang, t } = useLang(); const { config } = useAuthStore(); const [tab, setTab] = useState<'emoji' | 'gif'>('emoji'); const [gifQuery, setGifQuery] = useState(''); const [gifs, setGifs] = useState([]); const [gifLoading, setGifLoading] = useState(false); const [trendingGifs, setTrendingGifs] = useState([]); const gifSearchRef = useRef(null); const debounceRef = useRef>(undefined); const initialFetchDone = useRef(false); // Helper to safely extract GIF array from various possible Klipy API responses const extractGifs = (d: any): KlipyGif[] => { if (!d) return []; if (d.data && Array.isArray(d.data.data)) return d.data.data; if (Array.isArray(d)) return d; if (Array.isArray(d.data)) return d.data; if (Array.isArray(d.result)) return d.result; if (d.result && Array.isArray(d.result.data)) return d.result.data; if (Array.isArray(d.gifs)) return d.gifs; return []; }; // Load trending GIFs (Klipy) useEffect(() => { if (tab === 'gif' && config?.enableKlipy && !initialFetchDone.current) { initialFetchDone.current = true; setGifLoading(true); api.getTrendingGifs() .then(d => { setTrendingGifs(extractGifs(d)); setGifLoading(false); }) .catch((e) => { console.error('Klipy trending error:', e); setTrendingGifs([]); setGifLoading(false); }); } }, [tab, config?.enableKlipy]); const searchGifs = useCallback((q: string) => { if (!config?.enableKlipy || !q.trim()) { setGifs([]); setGifLoading(false); return; } setGifLoading(true); api.searchKlipyGifs(q) .then(d => { setGifs(extractGifs(d)); setGifLoading(false); }) .catch((e) => { console.error('Klipy search error:', e); setGifs([]); setGifLoading(false); }); }, [config?.enableKlipy]); const handleGifSearch = (q: string) => { setGifQuery(q); if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => searchGifs(q), 400); }; const getGifUrl = (gif: any): string => { return gif.files?.hd?.gif?.url || gif.files?.sd?.gif?.url || gif.file?.hd?.gif?.url || gif.file?.sd?.gif?.url || gif.media_formats?.gif?.url || gif.media?.[0]?.gif?.url || gif.images?.original?.url || ''; }; const getGifPreview = (gif: any, fullUrl: string): string => { return gif.files?.sd?.webp?.url || gif.files?.sd?.gif?.url || gif.file?.sd?.webp?.url || gif.file?.sd?.gif?.url || gif.media_formats?.tinygif?.url || gif.media?.[0]?.tinygif?.url || gif.images?.fixed_height_small?.url || fullUrl; }; const pickGif = (gif: KlipyGif) => { const url = getGifUrl(gif); const preview = getGifPreview(gif, url); if (onSelectGif && url) { onSelectGif(url, preview); } }; const displayGifs = gifQuery.trim() ? gifs : trendingGifs; const anchorRef = useRef(null); const [pos, setPos] = useState<{ top: number; left: number } | null>(null); useEffect(() => { const update = () => { const el = anchorRef.current?.parentElement; if (!el) return; const rect = el.getBoundingClientRect(); const w = tab === 'gif' ? 360 : 352; let left = rect.right - w; if (left < 8) left = 8; setPos({ top: rect.top - 8, left }); }; update(); window.addEventListener('resize', update); return () => window.removeEventListener('resize', update); }, [tab]); const pickerWidth = tab === 'gif' ? 360 : 352; return ( <>
{createPortal( <>
{/* Tabs */}
{config?.enableKlipy && onSelectGif && ( )}
{/* Emoji tab */} {tab === 'emoji' && ( onSelect(e.native)} theme="dark" locale={lang === 'ru' ? 'ru' : 'en'} set="native" previewPosition="none" skinTonePosition="search" perLine={9} emojiSize={28} emojiButtonSize={36} maxFrequentRows={2} navPosition="bottom" dynamicWidth={false} /> )} {/* GIF tab */} {config?.enableKlipy && tab === 'gif' && (
handleGifSearch(e.target.value)} placeholder={t('searchGifs')} className="w-full pl-8 pr-3 py-2 rounded-lg bg-surface-tertiary/80 text-sm text-white placeholder-zinc-500 border border-border/30 focus:border-accent/50 outline-none transition-colors" />
{!gifQuery.trim() && !gifLoading && (
{t('trending')}
)}
{gifLoading ? (
) : displayGifs.length === 0 ? (

{gifQuery ? t('nothingFound') : ''}

) : (
{displayGifs.map((gif) => ( ))}
)}
)}
, document.body )} ); }