diff --git a/frontend/app/view/preview/directorypreview.tsx b/frontend/app/view/preview/directorypreview.tsx index 6e03d8998..a5c600760 100644 --- a/frontend/app/view/preview/directorypreview.tsx +++ b/frontend/app/view/preview/directorypreview.tsx @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { Button } from "@/app/element/button"; +import { CopyButton } from "@/app/element/copybutton"; import { Input } from "@/app/element/input"; +import { useDimensionsWithCallbackRef } from "@/app/hook/useDimensions"; import { ContextMenuModel } from "@/app/store/contextmenu"; import { PLATFORM, atoms, createBlock, getApi, globalStore } from "@/app/store/global"; import { RpcApi } from "@/app/store/wshclientapi"; @@ -34,6 +36,12 @@ import "./directorypreview.scss"; const PageJumpSize = 20; +type FileCopyStatus = { + copyData: CommandFileCopyData; + copyError: string; + allowRetry: boolean; +}; + declare module "@tanstack/react-table" { interface TableMeta { updateName: (path: string) => void; @@ -790,6 +798,7 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) { const conn = useAtomValue(model.connection); const blockData = useAtomValue(model.blockAtom); const dirPath = useAtomValue(model.normFilePath); + const [copyStatus, setCopyStatus] = useState(null); useEffect(() => { model.refreshCallback = () => { @@ -900,6 +909,27 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) { middleware: [offset(({ rects }) => -rects.reference.height / 2 - rects.floating.height / 2)], }); + const handleDropCopy = useCallback( + async (data: CommandFileCopyData) => { + try { + await RpcApi.FileCopyCommand(TabRpcClient, data, { timeout: data.opts.timeout }); + setCopyStatus(null); + } catch (e) { + console.log("copy failed:", e); + const copyError = `${e}`; + const allowRetry = copyError.endsWith("overwrite not specified"); + const copyStatus: FileCopyStatus = { + copyError, + copyData: data, + allowRetry, + }; + setCopyStatus(copyStatus); + } + model.refreshCallback(); + }, + [setCopyStatus, model.refreshCallback] + ); + const [, drop] = useDrop( () => ({ accept: "FILE_ITEM", //a name of file drop type @@ -925,17 +955,12 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) { desturi, opts, }; - try { - await RpcApi.FileCopyCommand(TabRpcClient, data, { timeout: timeoutYear }); - } catch (e) { - console.log("copy failed:", e); - } - model.refreshCallback(); + await handleDropCopy(data); } }, // TODO: mabe add a hover option? }), - [dirPath, model.formatRemoteUri, model.refreshCallback] + [dirPath, model.formatRemoteUri, model.refreshCallback, setCopyStatus] ); useEffect(() => { @@ -1049,6 +1074,13 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) { onContextMenu={(e) => handleFileContextMenu(e)} onClick={() => setEntryManagerProps(undefined)} > + {copyStatus != null && ( + + )} void; + handleDropCopy: (data: CommandFileCopyData) => Promise; + }) => { + const [overlayRefCallback, _, domRect] = useDimensionsWithCallbackRef(30); + const width = domRect?.width; + + const handleRetryCopy = React.useCallback(async () => { + if (!copyStatus) { + return; + } + const updatedData = { + ...copyStatus.copyData, + opts: { ...copyStatus.copyData.opts, overwrite: true }, + }; + await handleDropCopy(updatedData); + }, [copyStatus.copyData]); + + let statusText = "Copy Error"; + let errorMsg = `error: ${copyStatus?.copyError}`; + if (copyStatus?.allowRetry) { + statusText = "Confirm Overwrite File(s)"; + errorMsg = "This copy operation will overwrite an existing file. Would you like to continue?"; + } + + const buttonClassName = "outlined grey font-size-11 vertical-padding-3 horizontal-padding-7"; + + const handleRemoveCopyError = React.useCallback(async () => { + setCopyStatus(null); + }, [setCopyStatus]); + + const handleCopyToClipboard = React.useCallback(async () => { + await navigator.clipboard.writeText(errorMsg); + }, [errorMsg]); + + return ( +
+
+
+ + +
+
+ {statusText} +
+ + + +
{errorMsg}
+
+ + {copyStatus?.allowRetry && ( +
+ + +
+ )} +
+ + {!copyStatus?.allowRetry && ( +
+
+ )} +
+
+
+ ); + } +); + export { DirectoryPreview };