import { useState, useEffect, useRef } from 'react' import { Search as SearchIcon, ChevronLeft, ChevronRight, Download, X, Plus } from 'lucide-react' import { searchFurry, searchAnime, getFurryTags, getAnimeTags } from '../utils/api' import { hapticFeedback, getTelegramUser } from '../utils/telegram' import CreatePostModal from '../components/CreatePostModal' import api from '../utils/api' import './Search.css' export default function Search({ user }) { const [mode, setMode] = useState(user.settings?.searchPreference || 'mixed') const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [loading, setLoading] = useState(false) const [tagSuggestions, setTagSuggestions] = useState([]) 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 [imageForPost, setImageForPost] = useState(null) const touchStartX = useRef(0) const touchEndX = useRef(0) useEffect(() => { if (query.length > 1) { loadTagSuggestions() } else { setTagSuggestions([]) } }, [query, mode]) const loadTagSuggestions = async () => { try { let tags = [] if (mode === 'furry' || mode === 'mixed') { const furryTags = await getFurryTags(query) tags = [...tags, ...furryTags.map(t => ({ ...t, source: 'e621' }))] } if (mode === 'anime' || mode === 'mixed') { const animeTags = await getAnimeTags(query) tags = [...tags, ...animeTags.map(t => ({ ...t, source: 'gelbooru' }))] } // Убрать дубликаты const uniqueTags = tags.reduce((acc, tag) => { if (!acc.find(t => t.name === tag.name)) { acc.push(tag) } return acc }, []) setTagSuggestions(uniqueTags.slice(0, 10)) } catch (error) { console.error('Ошибка загрузки тегов:', error) } } const handleSearch = async (searchQuery = query) => { if (!searchQuery.trim()) return try { setLoading(true) hapticFeedback('light') setResults([]) let allResults = [] if (mode === 'furry' || mode === 'mixed') { const furryResults = await searchFurry(searchQuery, { limit: 30 }) allResults = [...allResults, ...furryResults] } if (mode === 'anime' || mode === 'mixed') { const animeResults = await searchAnime(searchQuery, { limit: 30 }) allResults = [...allResults, ...animeResults] } // Перемешать результаты если mixed режим if (mode === 'mixed') { allResults = allResults.sort(() => Math.random() - 0.5) } setResults(allResults) setTagSuggestions([]) if (allResults.length > 0) { hapticFeedback('success') } } catch (error) { console.error('Ошибка поиска:', error) hapticFeedback('error') } finally { setLoading(false) } } const handleTagClick = (tagName) => { setQuery(tagName) handleSearch(tagName) } 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) { // Отправить через backend в ЛС с ботом 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 { // Fallback - обычное скачивание 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] setImageForPost(currentImage.url) setShowViewer(false) setShowCreatePost(true) hapticFeedback('light') } const handlePostCreated = (newPost) => { setShowCreatePost(false) setImageForPost(null) hapticFeedback('success') alert('✅ Пост создан!') } return (
{/* Хедер */}

Поиск

{results.length > 0 && ( )}
{/* Режимы поиска */}
{/* Строка поиска */}
setQuery(e.target.value)} onKeyPress={e => e.key === 'Enter' && handleSearch()} /> {query && ( )}
{/* Подсказки тегов */} {tagSuggestions.length > 0 && (
{tagSuggestions.map((tag, index) => ( ))}
)}
{/* Результаты */}
{loading ? (

Поиск...

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

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

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

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

Используйте e621 и gelbooru
) : ( <>
{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 && }
)}
) })}
{/* Кнопка отправки выбранных */} {selectionMode && selectedImages.length > 0 && (
)} )}
{/* Просмотрщик изображений */} {showViewer && results[currentIndex] && (
{currentIndex + 1} / {results.length}
Full view {/* Индикатор свайпа */}
0 ? 1 : 0.3 }} /> Свайпайте для переключения
{results[currentIndex].tags.slice(0, 5).map((tag, i) => ( {tag} ))}
Score: {results[currentIndex].score} Source: {results[currentIndex].source}
)}
) }