import { useState, useEffect, useRef } from 'react' import { createPortal } from 'react-dom' import { useNavigate } from 'react-router-dom' import { Search as SearchIcon, ChevronLeft, ChevronRight, Download, X, Plus, ArrowLeft } from 'lucide-react' import { searchFurry, getFurryTags } from '../utils/api' import { hapticFeedback, getTelegramUser } from '../utils/telegram' import CreatePostModal from '../components/CreatePostModal' import api from '../utils/api' import './MediaSearch.css' export default function MediaFurry({ user }) { const navigate = useNavigate() const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [loading, setLoading] = useState(false) const [loadingMore, setLoadingMore] = useState(false) const [tagSuggestions, setTagSuggestions] = useState([]) const [showTagSuggestions, setShowTagSuggestions] = useState(true) const [currentPage, setCurrentPage] = useState(1) const [hasMore, setHasMore] = useState(false) const [currentIndex, setCurrentIndex] = useState(0) const [showViewer, setShowViewer] = useState(false) const [selectedImages, setSelectedImages] = useState([]) const [selectionMode, setSelectionMode] = useState(false) const [showCreatePost, setShowCreatePost] = useState(false) const touchStartX = useRef(0) const touchEndX = useRef(0) const isVideoUrl = (url = '') => { if (!url) return false const clean = url.split('?')[0].toLowerCase() return clean.endsWith('.mp4') || clean.endsWith('.webm') || clean.endsWith('.mov') || clean.endsWith('.m4v') } useEffect(() => { if (query.length > 1 && showTagSuggestions) { loadTagSuggestions() } else { setTagSuggestions([]) } }, [query, showTagSuggestions]) const loadTagSuggestions = async () => { try { const queryParts = query.trim().split(/\s+/) const lastTag = queryParts[queryParts.length - 1] || query.trim() if (!lastTag || lastTag.length < 1) { setTagSuggestions([]) return } const furryTags = await getFurryTags(lastTag) if (furryTags && Array.isArray(furryTags)) { setTagSuggestions(furryTags.slice(0, 10)) } } catch (error) { console.error('Ошибка загрузки тегов:', error) setTagSuggestions([]) } } const handleSearch = async (searchQuery = query, page = 1, append = false) => { if (!searchQuery.trim()) return try { if (page === 1) { setLoading(true) setResults([]) } else { setLoadingMore(true) } hapticFeedback('light') setShowTagSuggestions(false) const furryResults = await searchFurry(searchQuery, { limit: 320, page }) const allResults = Array.isArray(furryResults) ? furryResults : [] const hasMoreResults = allResults.length === 320 if (append) { setResults(prev => [...prev, ...allResults]) } else { setResults(allResults) setCurrentPage(1) } setHasMore(hasMoreResults) setCurrentPage(page) setTagSuggestions([]) if (allResults.length > 0) { hapticFeedback('success') } else if (page === 1) { hapticFeedback('error') } } catch (error) { console.error('Ошибка поиска:', error) hapticFeedback('error') if (page === 1) { setResults([]) } } finally { setLoading(false) setLoadingMore(false) } } const loadMore = () => { if (!loadingMore && hasMore && query.trim()) { handleSearch(query, currentPage + 1, true) } } const handleTagClick = (tagName) => { const queryParts = query.trim().split(/\s+/) const existingTags = queryParts.slice(0, -1).filter(t => t.trim()) const newQuery = existingTags.length > 0 ? [...existingTags, tagName].join(' ') : tagName setQuery(newQuery) setShowTagSuggestions(false) handleSearch(newQuery) } const openViewer = (index) => { if (selectionMode) { toggleImageSelection(index) } else { setCurrentIndex(index) setShowViewer(true) hapticFeedback('light') } } const toggleImageSelection = (index) => { const imageId = `${results[index].source}-${results[index].id}` if (selectedImages.includes(imageId)) { setSelectedImages(selectedImages.filter(id => id !== imageId)) } else { setSelectedImages([...selectedImages, imageId]) } hapticFeedback('light') } const toggleSelectionMode = () => { setSelectionMode(!selectionMode) setSelectedImages([]) hapticFeedback('light') } const handleSendSelected = async () => { if (selectedImages.length === 0) return try { hapticFeedback('light') const telegramUser = getTelegramUser() if (telegramUser) { const selectedPhotos = results.filter((img, index) => { const imageId = `${img.source}-${img.id}` return selectedImages.includes(imageId) }) const photos = selectedPhotos.map(img => ({ url: img.url, caption: `${img.source} - ${img.id}` })) await api.post('/bot/send-photos', { userId: telegramUser.id, photos: photos }) hapticFeedback('success') alert(`✅ ${selectedImages.length} изображений отправлено в ваш Telegram!`) setSelectedImages([]) setSelectionMode(false) } else { alert('Функция доступна только в Telegram') } } catch (error) { console.error('Ошибка:', error) hapticFeedback('error') alert('Ошибка отправки') } } const handleNext = () => { if (currentIndex < results.length - 1) { setCurrentIndex(currentIndex + 1) hapticFeedback('light') } } const handlePrev = () => { if (currentIndex > 0) { setCurrentIndex(currentIndex - 1) hapticFeedback('light') } } const handleTouchStart = (e) => { touchStartX.current = e.touches[0].clientX } const handleTouchMove = (e) => { touchEndX.current = e.touches[0].clientX } const handleTouchEnd = () => { const diff = touchStartX.current - touchEndX.current const threshold = 50 if (Math.abs(diff) > threshold) { if (diff > 0) { handleNext() } else { handlePrev() } } } const handleKeyDown = (e) => { if (e.key === 'ArrowLeft') { handlePrev() } else if (e.key === 'ArrowRight') { handleNext() } else if (e.key === 'Escape') { setShowViewer(false) } } useEffect(() => { if (showViewer) { window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) } }, [showViewer, currentIndex]) const handleDownload = async () => { const currentImage = results[currentIndex] if (!currentImage) return try { hapticFeedback('light') const telegramUser = getTelegramUser() if (telegramUser) { const caption = `${currentImage.source} - ID: ${currentImage.id}\nТеги: ${currentImage.tags.slice(0, 3).join(', ')}` await api.post('/bot/send-photo', { userId: telegramUser.id, photoUrl: currentImage.url, caption: caption }) hapticFeedback('success') alert('✅ Изображение отправлено в ваш Telegram!') } else { const response = await fetch(currentImage.url) const blob = await response.blob() const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `nakama-${currentImage.id}.jpg` document.body.appendChild(a) a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) hapticFeedback('success') } } catch (error) { console.error('Ошибка:', error) hapticFeedback('error') alert('Ошибка отправки. Проверьте настройки бота.') } } const handleCreatePost = () => { const currentImage = results[currentIndex] setShowViewer(false) setShowCreatePost(true) hapticFeedback('light') } return (

Furry

{results.length > 0 && ( )}
{ setQuery(e.target.value) setShowTagSuggestions(true) }} onKeyPress={e => { if (e.key === 'Enter') { handleSearch() } }} /> {query && ( )}
{tagSuggestions.length > 0 && showTagSuggestions && (
{tagSuggestions.map((tag, index) => ( ))}
)}
{loading ? (

Поиск...

) : results.length === 0 && query ? (

Ничего не найдено

Попробуйте другие теги
) : results.length === 0 ? (

Введите теги для поиска

Используйте e621 теги
) : ( <>
{results.map((item, index) => { const imageId = `${item.source}-${item.id}` const isSelected = selectedImages.includes(imageId) return (
openViewer(index)} > {`Result
{item.source} {item.rating}
{selectionMode && (
{isSelected && }
)}
) })}
{hasMore && !loadingMore && (
)} {loadingMore && (

Загрузка...

)} {selectionMode && selectedImages.length > 0 && (
)} )}
{showViewer && results[currentIndex] && createPortal(
{currentIndex + 1} / {results.length}
e.stopPropagation()} > {isVideoUrl(results[currentIndex].url) ? (
{results[currentIndex].tags.slice(0, 5).map((tag, i) => ( {tag} ))}
Score: {results[currentIndex].score} Source: {results[currentIndex].source}
, document.body )} {showCreatePost && ( setShowCreatePost(false)} onPostCreated={() => { setShowCreatePost(false) setShowViewer(false) }} initialImage={results[currentIndex]?.url} /> )}
) }