import { Check, Download, Pencil, Video, X } from 'lucide-react'; import type { Dispatch, MouseEvent as ReactMouseEvent, ReactNode, SetStateAction, } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useResolvedAssetReadUrl } from '../../hooks/useResolvedAssetReadUrl'; import { PlatformTextField } from '../common/PlatformTextField'; import { resolveAssetShowcaseSubmissionState } from './ImageCanvasAssetLibraryModel'; import type { UploadFilesOptions } from './ImageCanvasAssetLibraryPanelView'; import { ASSET_DRAG_MIME_TYPE, clamp, getDraggedAssetId, hasDataTransferType, } from './ImageCanvasEditorModel'; import { EditorIconButton, SidebarMediaItem, } from './ImageCanvasEditorPrimitives'; import type { AssetPointerDragState, EditorAsset, } from './ImageCanvasEditorTypes'; import { getCanvasMediaPreviewClassName, getCanvasMediaPreviewMarker, } from './ImageCanvasMediaModel'; import { type AssetSelectionInteraction, isSelectableAsset, } from './useImageCanvasAssetSelection'; const AUDIO_ASSET_COVER_SRC = '/creation-home/audio-asset-cover.png'; export type ImageCanvasAssetRowViewProps = { asset: EditorAsset; assetPointerDragRef: { current: AssetPointerDragState | null }; suppressAssetClickRef: { current: boolean }; isAssetSelectionMode: boolean; selectedAssetIds: Set; visibleAssetIds: string[]; renamingAsset: { assetId: string; value: string } | null; setRenamingAsset: Dispatch< SetStateAction<{ assetId: string; value: string } | null> >; setUploadDropTarget: Dispatch>; setAssetPointerDrag: Dispatch>; updateAssetMoveDropFolder: (folderId: string | null) => void; addUploadedFiles: ( files: FileList | File[], options?: UploadFilesOptions, ) => void; moveAssetToFolder: (assetId: string, folderId: string) => void; startRenamingAsset: (asset: EditorAsset) => void; commitAssetRename: (asset: EditorAsset) => void; deleteUploadedAsset: (asset: EditorAsset) => void; submitAssetToShowcase: (asset: EditorAsset) => void; toggleAssetSelected: ( assetId: string, interaction: AssetSelectionInteraction, ) => void; addAssetLayer: (asset: EditorAsset) => void; onDownloadAsset: (asset: EditorAsset) => void; }; export function ImageCanvasAssetRowView({ asset, assetPointerDragRef, suppressAssetClickRef, isAssetSelectionMode, selectedAssetIds, visibleAssetIds, renamingAsset, setRenamingAsset, setUploadDropTarget, setAssetPointerDrag, updateAssetMoveDropFolder, addUploadedFiles, moveAssetToFolder, startRenamingAsset, commitAssetRename, deleteUploadedAsset, submitAssetToShowcase, toggleAssetSelected, addAssetLayer, onDownloadAsset, }: ImageCanvasAssetRowViewProps) { const rowRef = useRef(null); const [menuPosition, setMenuPosition] = useState<{ x: number; y: number; } | null>(null); const isRenaming = renamingAsset?.assetId === asset.id; const isUploadingAsset = asset.uploadStatus === 'uploading'; const isFailedUpload = asset.uploadStatus === 'failed'; const canSelectAsset = isSelectableAsset(asset); const showcaseReviewStatus = asset.showcaseReviewStatus ?? null; const showcaseSubmissionState = resolveAssetShowcaseSubmissionState(asset); const canSubmitShowcaseAsset = showcaseSubmissionState.canSubmit; const showcaseMenuLabel = showcaseSubmissionState.label; const showcaseMenuReason = showcaseSubmissionState.reason; const closeMenu = useCallback(() => { setMenuPosition(null); }, []); useEffect(() => { if (isAssetSelectionMode) { closeMenu(); } }, [closeMenu, isAssetSelectionMode]); const selectAssetFromEvent = useCallback( (event: ReactMouseEvent) => { if ( !isAssetSelectionMode || !canSelectAsset || suppressAssetClickRef.current ) { return; } closeMenu(); toggleAssetSelected(asset.id, { visibleAssetIds, shiftKey: event.shiftKey, }); }, [ asset.id, canSelectAsset, closeMenu, isAssetSelectionMode, suppressAssetClickRef, toggleAssetSelected, visibleAssetIds, ], ); useEffect(() => { if (!menuPosition) { return undefined; } const handlePointerDown = (event: PointerEvent) => { if (rowRef.current?.contains(event.target as Node)) { return; } closeMenu(); }; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { closeMenu(); } }; window.addEventListener('pointerdown', handlePointerDown); window.addEventListener('keydown', handleKeyDown); window.addEventListener('scroll', closeMenu, true); window.addEventListener('resize', closeMenu); return () => { window.removeEventListener('pointerdown', handlePointerDown); window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('scroll', closeMenu, true); window.removeEventListener('resize', closeMenu); }; }, [closeMenu, menuPosition]); const uploadProgress = clamp(asset.uploadProgress ?? 0, 0, 100); const mediaPreviewMarker = getCanvasMediaPreviewMarker(asset); const mediaPreviewClassName = getCanvasMediaPreviewClassName(mediaPreviewMarker); const { resolvedUrl: resolvedVideoPreviewUrl } = useResolvedAssetReadUrl( asset.mediaType === 'video' ? asset.src : '', { enabled: asset.mediaType === 'video' && !asset.thumbnailSrc?.trim(), objectKey: asset.mediaType === 'video' ? asset.objectKey : undefined, refreshKey: asset.id, }, ); const videoPreview = asset.mediaType === 'video' && !asset.thumbnailSrc?.trim() ? (