import React, { useCallback, useEffect, useMemo, useState } from "react" import { MediaFile } from "@/features/media/types/media" import { calculateDimensionsWithAspectRatio } from "@/features/media/utils/preview-sizes" import { useResources } from "@/features/resources" import { TemplateResource, TimelineResource } from "@/features/resources/types" import { usePlayer, useVideoSelection } from "@/features/video-player" import { AddMediaButton, ApplyButton, FavoriteButton } from "../../browser/components/layout" import { MediaTemplate } from "../lib/templates" /** * Интерфейс пропсов для компонента TemplatePreview * @interface TemplatePreviewProps * @property {MediaTemplate} template - Объект шаблона для предпросмотра * @property {Function} onClick - Функция обработки клика по превью * @property {number} size - Базовый размер превью в пикселях * @property {[number, number]} dimensions - Размеры шаблона [ширина, высота] */ interface TemplatePreviewProps { template: MediaTemplate onClick: () => void size: number dimensions: [number, number] } /** * Компонент для отображения превью шаблона * Показывает визуальное представление шаблона и позволяет добавить его в проект * * @param {TemplatePreviewProps} props - Пропсы компонента * @returns {JSX.Element} Компонент превью шаблона */ export function TemplatePreview({ template, onClick, size, dimensions }: TemplatePreviewProps) { const [width, height] = dimensions // Извлекаем ширину и высоту из размеров // Локальное состояние для отслеживания добавления шаблона // Используется для мгновенного обновления UI без ожидания обновления из хранилища const [localIsAdded, setLocalIsAdded] = useState(false) const { applyTemplate } = usePlayer() // Получаем метод для применения шаблона const { getVideosForPreview } = useVideoSelection() // Получаем видео для применения шаблона // Получаем вычисленные размеры превью с минимумом 150px для шаблонов const { height: previewHeight, width: previewWidth } = calculateDimensionsWithAspectRatio( size, { width, height }, true, // isTemplate = true для применения минимума 150px ) // Получаем методы для работы с ресурсами шаблонов const { addTemplate, isTemplateAdded, removeResource, templateResources } = useResources() // Создаем клон элемента с добавлением ключа для предотвращения предупреждения React const renderedTemplate = template.render?.() ||
// Проверяем, добавлен ли шаблон уже в хранилище ресурсов // Мемоизируем результат для оптимизации const isAddedFromStore = useMemo(() => { return isTemplateAdded(template) }, [isTemplateAdded, template]) /** * Эффект для синхронизации локального состояния с состоянием из хранилища * При изменении состояния в хранилище, обновляем локальное состояние */ useEffect(() => { setLocalIsAdded(isAddedFromStore) }, [isAddedFromStore]) // Используем комбинированное состояние - либо из хранилища, либо локальное // Это позволяет мгновенно обновлять UI при добавлении/удалении шаблона const isAdded = isAddedFromStore || localIsAdded // Обработчик применения шаблона const handleApplyTemplate = useCallback( (_resource: TimelineResource, _type: string) => { console.log("[TemplatePreview] Applying template:", template.id) const videos = getVideosForPreview() applyTemplate( { id: template.id, name: template.id, }, videos, ) }, [template, applyTemplate, getVideosForPreview], ) /** * Обработчик добавления шаблона в проект * * @param {React.MouseEvent} e - Событие клика * @param {MediaFile} _file - Объект файла (не используется) */ const handleAddTemplate = (e: React.MouseEvent, _file: MediaFile) => { e.stopPropagation() // Предотвращаем всплытие события // Немедленно обновляем локальное состояние для мгновенного отклика UI setLocalIsAdded(true) // Добавляем шаблон в хранилище ресурсов void addTemplate(template) // Принудительно обновляем состояние через небольшую задержку, // чтобы кнопка стала видимой сразу после добавления setTimeout(() => { // Это вызовет перерисовку компонента const isAdded = isTemplateAdded(template) console.log(`Шаблон ${template.id} добавлен: ${isAdded}`) }, 10) } /** * Обработчик удаления шаблона из проекта * * @param {React.MouseEvent} e - Событие клика * @param {MediaFile} _file - Объект файла (не используется) */ const handleRemoveTemplate = (e: React.MouseEvent, _file: MediaFile) => { e.stopPropagation() // Предотвращаем всплытие события // Немедленно обновляем локальное состояние для мгновенного отклика UI setLocalIsAdded(false) // Находим ресурс с этим шаблоном в списке ресурсов const resource = templateResources.find((res: TemplateResource) => res.resourceId === template.id) if (resource) { // Удаляем ресурс из хранилища void removeResource(resource.id) } else { console.warn(`Не удалось найти ресурс шаблона с ID ${template.id} для удаления`) } } return (
{/* Рендерим шаблон с добавлением ключа для React */} {React.cloneElement(renderedTemplate, { key: `template-preview-${template.id}`, })} {/* Кнопка добавления в избранное */} {/* Кнопка применения шаблона */} {/* Контейнер для кнопки добавления/удаления шаблона */}
{/* Кнопка добавления/удаления шаблона */}
) }