From 2dd28540f123321ccc597df7c42bfb1d892dcd48 Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:47:03 +0300 Subject: [PATCH] [ENG-1288] Copy, cut, and paste shortcuts (#1591) * copy paste shortcut * add cut shortcut --- .../app/$libraryId/Explorer/View/index.tsx | 9 ++- interface/hooks/index.ts | 1 + interface/hooks/useKeyCopyCutPaste.ts | 79 +++++++++++++++++++ 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 interface/hooks/useKeyCopyCutPaste.ts diff --git a/interface/app/$libraryId/Explorer/View/index.tsx b/interface/app/$libraryId/Explorer/View/index.tsx index 7b404fb9a..40eb3bd2a 100644 --- a/interface/app/$libraryId/Explorer/View/index.tsx +++ b/interface/app/$libraryId/Explorer/View/index.tsx @@ -12,9 +12,9 @@ import { import { createPortal } from 'react-dom'; import { useKey, useKeys } from 'rooks'; import { getItemObject, useLibraryContext, type Object } from '@sd/client'; -import { dialogManager, ModifierKeys, toast } from '@sd/ui'; +import { dialogManager, ModifierKeys } from '@sd/ui'; import { Loader } from '~/components'; -import { useKeyMatcher, useOperatingSystem } from '~/hooks'; +import { useKeyCopyPaste, useKeyMatcher, useOperatingSystem } from '~/hooks'; import { isNonEmpty } from '~/util'; import { usePlatform } from '~/util/Platform'; @@ -63,7 +63,6 @@ export default memo( const quickPreview = useQuickPreviewContext(); const quickPreviewStore = useQuickPreviewStore(); const os = useOperatingSystem(); - const { doubleClick } = useViewItemDoubleClick(); const { layoutMode } = explorer.useSettingsSnapshot(); @@ -109,6 +108,8 @@ export default memo( doubleClick(); }); + useKeyCopyPaste(); + return ( <>
+
{props.icon ? isValidElement(props.icon) ? props.icon diff --git a/interface/hooks/index.ts b/interface/hooks/index.ts index 2afd384e5..92e9dfd3e 100644 --- a/interface/hooks/index.ts +++ b/interface/hooks/index.ts @@ -21,3 +21,4 @@ export * from './useZodRouteParams'; export * from './useZodSearchParams'; export * from './useIsTextTruncated'; export * from './useKeyMatcher'; +export * from './useKeyCopyCutPaste'; diff --git a/interface/hooks/useKeyCopyCutPaste.ts b/interface/hooks/useKeyCopyCutPaste.ts new file mode 100644 index 000000000..40b9c0c2a --- /dev/null +++ b/interface/hooks/useKeyCopyCutPaste.ts @@ -0,0 +1,79 @@ +import { getExplorerStore, useExplorerStore } from "~/app/$libraryId/Explorer/store"; +import { useExplorerSearchParams } from "~/app/$libraryId/Explorer/util"; +import { useExplorerContext } from "~/app/$libraryId/Explorer/Context"; +import { useKeyMatcher } from "./useKeyMatcher"; +import { useKeys } from "rooks"; +import {useItemsAsFilePaths, useLibraryMutation} from '@sd/client'; +import {toast} from '@sd/ui'; + + +export const useKeyCopyPaste = () => { + const {cutCopyState} = useExplorerStore(); + const [{ path }] = useExplorerSearchParams(); + + const metaCtrlKey = useKeyMatcher('Meta').key; + const copyFiles = useLibraryMutation('files.copyFiles'); + const cutFiles = useLibraryMutation('files.cutFiles'); + const explorer = useExplorerContext(); + const selectedFilePaths = useItemsAsFilePaths(Array.from(explorer.selectedItems)); + + useKeys([metaCtrlKey, 'KeyC'], (e) => { + e.stopPropagation(); +; if (explorer.parent?.type === 'Location') { + getExplorerStore().cutCopyState = { + sourceParentPath: path ?? '/', + sourceLocationId: explorer.parent.location.id, + sourcePathIds: selectedFilePaths.map((p) => p.id), + type: 'Copy' + }; + } + }); + + useKeys([metaCtrlKey, 'KeyX'], (e) => { + e.stopPropagation(); + if (explorer.parent?.type === 'Location') { + getExplorerStore().cutCopyState = { + sourceParentPath: path ?? '/', + sourceLocationId: explorer.parent.location.id, + sourcePathIds: selectedFilePaths.map((p) => p.id), + type: 'Cut' + }; + } + }); + + useKeys([metaCtrlKey, 'KeyV'], async (e) => { + e.stopPropagation(); + const parent = explorer.parent; + if (parent?.type === 'Location' && cutCopyState.type !== 'Idle' && path) { + const { type, sourcePathIds, sourceParentPath, sourceLocationId } = cutCopyState; + + const sameLocation = + sourceLocationId === parent.location.id && sourceParentPath === path; + + try { + if (type == 'Copy') { + await copyFiles.mutateAsync({ + source_location_id: sourceLocationId, + sources_file_path_ids: [...sourcePathIds], + target_location_id: parent.location.id, + target_location_relative_directory_path: path + }); + } else if (sameLocation) { + toast.error('File already exists in this location'); + } else { + await cutFiles.mutateAsync({ + source_location_id: sourceLocationId, + sources_file_path_ids: [...sourcePathIds], + target_location_id: parent.location.id, + target_location_relative_directory_path: path + }); + } + } catch (error) { + toast.error({ + title: `Failed to ${type.toLowerCase()} file`, + body: `Error: ${error}.` + }); + } + } + }); +}