// import { convertFileSrc } from "@tauri-apps/api/core" import { readFile } from "@tauri-apps/plugin-fs" import { Image } from "lucide-react" import { memo, useCallback, useEffect, useState } from "react" import { MediaFile } from "@/features/media/types/media" import { TimelineResource } from "@/features/resources/types" import { usePlayer } from "@/features/video-player" import { convertToAssetUrl } from "@/lib/tauri-utils" import { AddMediaButton } from "../layout/add-media-button" import { FavoriteButton } from "../layout/favorite-button" interface ImagePreviewProps { file: MediaFile size?: number showFileName?: boolean dimensions?: [number, number] } /** * Предварительный просмотр изображения * * Функционал: * - Отображает превью изображения с поддержкой ленивой загрузки * - Автоматически определяет и показывает разрешение изображения после загрузки * - Настраиваемое соотношение сторон контейнера (по умолчанию 16:9) * - Поддерживает два размера UI (стандартный и большой при size > 100) * - Опциональное отображение имени файла * - Кнопка добавления с состояниями (добавлено/не добавлено) * - Темная тема для UI элементов * * @param file - Объект файла с путем и метаданными * @param size - Размер превью в пикселях (по умолчанию 60) * @param showFileName - Флаг для отображения имени файла * @param dimensions - Соотношение сторон контейнера [ширина, высота], по умолчанию [16, 9] */ export const ImagePreview = memo(function ImagePreview({ file, size = 60, showFileName = false, dimensions = [16, 9], }: ImagePreviewProps) { const { playerSetSource, playerSetMedia } = usePlayer() const calculateWidth = (): number => { const [width, height] = dimensions return (size * width) / height } // Состояние для хранения объекта URL const [imageUrl, setImageUrl] = useState("") // Функция для чтения файла и создания объекта URL const loadImageFile = useCallback(async (path: string) => { try { console.log("[ImagePreview] Чтение файла через readFile:", path) const fileData = await readFile(path) const blob = new Blob([fileData], { type: "image/jpeg" }) // Можно определить тип по расширению файла const url = URL.createObjectURL(blob) console.log("[ImagePreview] Создан объект URL:", url) return url } catch (error) { console.error("[ImagePreview] Ошибка при загрузке изображения:", error) // В случае ошибки используем convertToAssetUrl const assetUrl = convertToAssetUrl(path) console.log("[ImagePreview] Используем asset URL:", assetUrl) return assetUrl } }, []) // Эффект для загрузки изображения при монтировании компонента useEffect(() => { let isMounted = true void loadImageFile(file.path).then((url) => { if (isMounted) { setImageUrl(url) } }) // Очистка объекта URL при размонтировании компонента return () => { isMounted = false if (imageUrl && imageUrl.startsWith("blob:")) { URL.revokeObjectURL(imageUrl) } } }, [file.path, loadImageFile]) // Убираем imageUrl из зависимостей // Обработчик клика для отправки изображения в плеер const handleImageClick = useCallback(async () => { try { await playerSetSource("browser") await playerSetMedia(file.id, 0) console.log(`[ImagePreview] Image sent to main player: ${file.name}`) } catch (error) { console.error("[ImagePreview] Failed to send image to main player:", error) } }, [file, playerSetSource, playerSetMedia]) return (
{showFileName && (
100 ? "top-1 left-1" : "top-0.5 left-0.5"} ${size > 100 ? "px-[4px] py-[2px]" : "px-[2px] py-0"} line-clamp-1 max-w-[calc(60%)] rounded-xs bg-black/50 text-xs leading-[16px]`} style={{ fontSize: size > 100 ? "13px" : "11px", color: "#fff", // Явно задаем чисто белый цвет для Tauri }} > {file.name}
)}
{} {file.name} { console.error("[ImagePreview] Ошибка загрузки изображения:", e) // Заменяем на иконку при ошибке e.currentTarget.style.display = "none" }} />
100 ? "bottom-1 left-1" : "bottom-0.5 left-0.5"} cursor-pointer rounded-xs bg-black/50 p-0.5`} style={{ color: "#fff", // Явно задаем чисто белый цвет для Tauri }} > 100 ? 16 : 12} />
) })