import React, { useRef } from "react"; import { Button } from "../ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../ui/dialog"; import type { SftpClipboardUploadRequest } from "./clipboardUpload"; import { confirmSftpClipboardUpload, shouldStartClipboardUploadConfirm, sftpClipboardUploadStore, } from "./clipboardUpload"; interface SftpClipboardUploadDialogProps { request: SftpClipboardUploadRequest | null; currentPath?: string; onUploaded?: (targetPath: string) => void; } /** * Confirmation dialog for OS-clipboard / path-backed paste uploads. * * Important: clear the store request *before* awaiting the transfer so the * modal overlay does not block the app for the entire upload (issue #2478). * Progress and cancellation live in the transfer queue after handoff. */ export const SftpClipboardUploadDialog: React.FC = ({ request, currentPath, onUploaded, }) => { // Double-click guard is scoped to the request identity so a later paste can // confirm while an earlier background transfer is still running. const confirmStartedForRef = useRef(null); const open = !!request; const fileCount = request?.files.length ?? 0; const previewFiles = request?.files.slice(0, 5) ?? []; const remainingCount = Math.max(0, fileCount - previewFiles.length); const handleClose = (nextOpen: boolean) => { if (nextOpen) return; sftpClipboardUploadStore.clear(request); }; const handleConfirm = async () => { if (!request || !shouldStartClipboardUploadConfirm(request, confirmStartedForRef.current)) { return; } const confirmedRequest = request; confirmStartedForRef.current = confirmedRequest; try { // Close immediately so side-panel / standalone SFTP stay interactive while // the existing background transfer path runs. await confirmSftpClipboardUpload({ request: confirmedRequest, onUploaded }); } catch { // Transfer handlers toast failures; keep this path free of unhandled // rejections after the dialog has already closed. } }; return ( Upload clipboard files? Upload {fileCount} item{fileCount === 1 ? "" : "s"} to:
{request?.targetPath ?? currentPath}
{previewFiles.length > 0 && (
{previewFiles.map((file) => (
{file.name}
))} {remainingCount > 0 && (
and {remainingCount} more...
)}
)}
); };