diff --git a/.env b/.env index afca7d3f..e336a239 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ VITE_ENABLED_EDITORS=ARBLTIPGrantee -VITE_RENOWN_URL=http://localhost:3000 +VITE_RENOWN_URL=https://renown.vercel.app/ VITE_DEFAULT_DRIVE_URL=https://apps.powerhouse.io/alpha/arbitrum/switchboard/d/arbitrum VITE_BASE_HREF=/ diff --git a/.github/workflows/build-and-deploy-makerdao-staging.yaml b/.github/workflows/build-and-deploy-makerdao-staging.yaml index eab51d5b..c7ff0ba2 100644 --- a/.github/workflows/build-and-deploy-makerdao-staging.yaml +++ b/.github/workflows/build-and-deploy-makerdao-staging.yaml @@ -22,5 +22,5 @@ jobs: email: ${{ secrets.HEROKU_EMAIL }} heroku_api_key: ${{ secrets.HEROKU_API_KEY }} heroku_app_name: ${{ secrets.HEROKU_APP_NAME }} - docker_options: "--build-arg BASE_PATH=/alpha/makerdao/connect --build-arg VITE_BASE_HREF=/alpha/makerdao/connect/ --build-arg VITE_ROUTER_BASENAME=/alpha/makerdao/connect --build-arg VITE_SENTRY_DSN=${{ secrets.SENTRY_DSN }} --build-arg VITE_SENTRY_ENV=${{ secrets.SENTRY_ENV }} --build-arg VITE_DEFAULT_DRIVE_URL=https://apps.powerhouse.io/alpha/makerdao/switchboard/d/monetalis" + docker_options: "--build-arg BASE_PATH=/alpha/makerdao/connect --build-arg VITE_BASE_HREF=/alpha/makerdao/connect/ --build-arg VITE_ROUTER_BASENAME=/alpha/makerdao/connect --build-arg VITE_SENTRY_DSN=${{ secrets.SENTRY_DSN }} --build-arg VITE_SENTRY_ENV=${{ secrets.SENTRY_ENV }} --build-arg VITE_DEFAULT_DRIVE_URL=https://apps.powerhouse.io/alpha/makerdao/switchboard/d/monetalis --build-arg VITE_CREATE_DOCUMENT_ALLOW_LIST=${{secrets.VITE_CREATE_DOCUMENT_ALLOW_LIST}}" process_type: web \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a3a4ecb8..c6a86875 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,9 @@ ENV VITE_DISABLE_DELETE_LOCAL_DRIVES=${VITE_DISABLE_DELETE_LOCAL_DRIVES} ARG VITE_LOCAL_DRIVES_ENABLED=false ENV VITE_LOCAL_DRIVES_ENABLED=${VITE_LOCAL_DRIVES_ENABLED} +ARG VITE_CREATE_DOCUMENT_ALLOW_LIST="" +ENV VITE_CREATE_DOCUMENT_ALLOW_LIST=${VITE_CREATE_DOCUMENT_ALLOW_LIST} + WORKDIR /opt/app COPY . . RUN npm install -g husky vite diff --git a/package-lock.json b/package-lock.json index c4e19ec7..d7d3365d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,9 @@ "@powerhousedao/design-system": "1.0.0-alpha.100", "@sentry/react": "^7.109.0", "did-key-creator": "^1.2.0", - "document-drive": "^1.0.0-alpha.46", - "document-model": "^1.0.52", - "document-model-libs": "^1.37.0-arbitrum.1", + "document-drive": "^1.0.0-alpha.50", + "document-model": "^1.0.53", + "document-model-libs": "1.39.0-arbitrum.1", "electron-is-dev": "^3.0.1", "electron-squirrel-startup": "^1.0.0", "electron-store": "^8.1.0", @@ -2961,9 +2961,9 @@ } }, "node_modules/@headlessui/react": { - "version": "1.7.18", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.18.tgz", - "integrity": "sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==", + "version": "1.7.19", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", + "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==", "dependencies": { "@tanstack/react-virtual": "^3.0.0-beta.60", "client-only": "^0.0.1" @@ -6412,11 +6412,11 @@ } }, "node_modules/@tanstack/react-virtual": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.2.1.tgz", - "integrity": "sha512-i9Nt0ssIh2bSjomJZlr6Iq5usT/9+ewo2/fKHRNk6kjVKS8jrhXbnO8NEawarCuBx/efv0xpoUUKKGxa0cQb4Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.4.0.tgz", + "integrity": "sha512-GZN4xn/Tg5w7gvYeVcMVCeL4pEyUhvg+Cp6KX2Z01C4FRNxIWMgIQ9ibgMarNQfo+gt0PVLcEER4A9sNv/jlow==", "dependencies": { - "@tanstack/virtual-core": "3.2.1" + "@tanstack/virtual-core": "3.4.0" }, "funding": { "type": "github", @@ -6428,9 +6428,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.2.1.tgz", - "integrity": "sha512-nO0d4vRzsmpBQCJYyClNHPPoUMI4nXNfrm6IcCRL33ncWMoNVpURh9YebEHPw8KrtsP2VSJIHE4gf4XFGk1OGg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.4.0.tgz", + "integrity": "sha512-75jXqXxqq5M5Veb9KP1STi8kA5u408uOOAefk2ftHDGCpUk3RP6zX++QqfbmHJTBiU72NQ+ghgCZVts/Wocz8Q==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -6721,6 +6721,7 @@ }, "node_modules/@types/prop-types": { "version": "15.7.12", + "devOptional": true, "license": "MIT" }, "node_modules/@types/qs": { @@ -6735,22 +6736,13 @@ }, "node_modules/@types/react": { "version": "18.2.79", + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, - "node_modules/@types/react-datepicker": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-6.2.0.tgz", - "integrity": "sha512-+JtO4Fm97WLkJTH8j8/v3Ldh7JCNRwjMYjRaKh4KHH0M3jJoXtwiD3JBCsdlg3tsFIw9eQSqyAPeVDN2H2oM9Q==", - "dependencies": { - "@floating-ui/react": "^0.26.2", - "@types/react": "*", - "date-fns": "^3.3.1" - } - }, "node_modules/@types/react-dom": { "version": "18.2.25", "devOptional": true, @@ -9032,6 +9024,7 @@ }, "node_modules/csstype": { "version": "3.1.3", + "devOptional": true, "license": "MIT" }, "node_modules/dargs": { @@ -9441,9 +9434,9 @@ } }, "node_modules/document-drive": { - "version": "1.0.0-alpha.46", - "resolved": "https://registry.npmjs.org/document-drive/-/document-drive-1.0.0-alpha.46.tgz", - "integrity": "sha512-vInSQsqy0u1OVlGGe7GcToUZpdauq7m4eRZ7UO4CSsehFp9g6JkC2RXtwAPJt7hqmeCZVVBJE9WfooxuniBCrw==", + "version": "1.0.0-alpha.50", + "resolved": "https://registry.npmjs.org/document-drive/-/document-drive-1.0.0-alpha.50.tgz", + "integrity": "sha512-SRZTnzvKxupeh5nj7b+ZtYmDOx6dRDdUKlwTlcCoWeC31vpPTRqNssp8O8K3yldl/ZomIMVlVxGv/66pzqUUoA==", "dependencies": { "graphql": "^16.8.1", "graphql-request": "^6.1.0", @@ -9459,14 +9452,14 @@ "sqlite3": "^5.1.7" }, "peerDependencies": { - "document-model": "^1.0.52", - "document-model-libs": "^1.36.0" + "document-model": "^1.0.53", + "document-model-libs": "^1.37.0" } }, "node_modules/document-model": { - "version": "1.0.52", - "resolved": "https://registry.npmjs.org/document-model/-/document-model-1.0.52.tgz", - "integrity": "sha512-zP6jVkVVJWelsdYpagOtcIouHWUYnVVRWeAwSC2YC0SOxowERG+NtSRx2Uk2v6UFVWOA78sPAeY4NQaNB0yWOw==", + "version": "1.0.53", + "resolved": "https://registry.npmjs.org/document-model/-/document-model-1.0.53.tgz", + "integrity": "sha512-Ld+ECcBFEP6l83YArZJH/TmUqYGX1n+sC9dvKw2icnvlzxnlkjzMJUdsVBcGX8pLRx+pHd6v2Vu5N3ffaKMsqg==", "dependencies": { "change-case": "^5.4.3", "immer": "^10.0.3", @@ -9477,26 +9470,24 @@ } }, "node_modules/document-model-libs": { - "version": "1.37.0-arbitrum.1", - "resolved": "https://registry.npmjs.org/document-model-libs/-/document-model-libs-1.37.0-arbitrum.1.tgz", - "integrity": "sha512-wgYOTUW4aZLmU4Q9/8xZj+Bq4ppo2mSMvEFgT+n3HUsCGx/GahTAHY59ZpkndtlY0tAAygjoZVQkuZ8Z+uZTag==", + "version": "1.39.0-arbitrum.1", + "resolved": "https://registry.npmjs.org/document-model-libs/-/document-model-libs-1.39.0-arbitrum.1.tgz", + "integrity": "sha512-ta1xsAok9S+yZgdPOhSAgqYjp8DXpJV2+j/Hh14DcIo0XnWfZbzZBAGJWM+71slzcs3BvRnHDcMPGceI+8np1A==", "dependencies": { "@acaldas/graphql-codegen-typescript-validation-schema": "^0.12.3", "@graphql-codegen/core": "^4.0.2", "@graphql-codegen/typescript": "^4.0.6", - "@headlessui/react": "^1.7.18", + "@headlessui/react": "^1.7.19", "@internationalized/date": "^3.5.1", "@storybook/test": "^8.0.2", - "@types/react-datepicker": "^6.2.0", "copy-anything": "^3.0.5", "date-fns": "^3.3.1", "deep-object-diff": "^1.1.9", "jsonc-parser": "^3.2.1", "jszip": "^3.10.1", "react-aria-components": "1.1.1", - "react-datepicker": "^6.5.0", - "tailwind-merge": "^2.2.1", - "uuid": "^9.0.1" + "react-datepicker": "^6.9.0", + "tailwind-merge": "^2.2.1" }, "peerDependencies": { "react": "^18.2.0", @@ -19499,9 +19490,9 @@ } }, "node_modules/react-datepicker": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-6.6.0.tgz", - "integrity": "sha512-ERC0/Q4pPC9bNIcGUpdCbHc+oCxhkU3WI3UOGHkyJ3A9fqALCYpEmLc5S5xvAd7DuCDdbsyW97oRPM6pWWwjww==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-6.9.0.tgz", + "integrity": "sha512-QTxuzeem7BUfVFWv+g5WuvzT0c5BPo+XTCNbMTZKSZQLU+cMMwSUHwspaxuIcDlwNcOH0tiJ+bh1fJ2yxOGYWA==", "dependencies": { "@floating-ui/react": "^0.26.2", "clsx": "^2.1.0", diff --git a/package.json b/package.json index 4ab38582..a5b992e7 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "lint": "eslint --ext .js,.ts,.tsx . && tsc --noemit --project tsconfig.spec.json", "format": "prettier --write \"**/*.+(js|ts|jsx|tsx|json)\"", "dev:web": "vite -c vite.renderer.config.ts", + "dev:nocache": "rm -rf node_modules/.vite && rm -rf node_modules/.cache && npm run dev:web", "build:web": "vite build -c vite.renderer.config.ts", "release": "semantic-release", "prepare": "npm run prepare:scripts", @@ -92,9 +93,9 @@ "@powerhousedao/design-system": "1.0.0-alpha.100", "@sentry/react": "^7.109.0", "did-key-creator": "^1.2.0", - "document-drive": "^1.0.0-alpha.46", - "document-model": "^1.0.52", - "document-model-libs": "^1.37.0-arbitrum.1", + "document-drive": "^1.0.0-alpha.50", + "document-model": "^1.0.53", + "document-model-libs": "1.39.0-arbitrum.1", "electron-is-dev": "^3.0.1", "electron-squirrel-startup": "^1.0.0", "electron-store": "^8.1.0", diff --git a/src/components/file-item/file-item.tsx b/src/components/file-item/file-item.tsx index dc1b8034..3cd12df9 100644 --- a/src/components/file-item/file-item.tsx +++ b/src/components/file-item/file-item.tsx @@ -16,7 +16,7 @@ import { useIsAllowedToCreateDocuments } from 'src/hooks/useIsAllowedToCreateDoc import { useOpenSwitchboardLink } from 'src/hooks/useOpenSwitchboardLink'; import { useModal } from '../modal'; -const allowedItemOptions = ['delete', 'rename']; +const allowedItemOptions = ['delete', 'rename', 'duplicate']; const defaultItemOptions = defaultDropdownMenuOptions.filter(option => allowedItemOptions.includes(option.id), @@ -34,7 +34,7 @@ export const FileItem: React.FC = ({ file, drive, onFileSelected }) => { const [isWriteMode, setIsWriteMode] = useState(false); const getReadableItemPath = useGetReadableItemPath(); const getDocumentById = useGetDocumentById(); - const { updateNodeName } = useDrivesContainer(); + const { updateNodeName, onSubmitInput } = useDrivesContainer(); const { showModal } = useModal(); const isAllowedToCreateDocuments = useIsAllowedToCreateDocuments(); @@ -51,6 +51,12 @@ export const FileItem: React.FC = ({ file, drive, onFileSelected }) => { type: 'file', }); } + if (optionId === 'duplicate') { + await onSubmitInput({ + ...file, + action: 'UPDATE_AND_COPY', + }); + } if (optionId === 'rename') { setIsWriteMode(true); diff --git a/src/components/folder-item/folder-item.tsx b/src/components/folder-item/folder-item.tsx index 86752b4c..9690d72d 100644 --- a/src/components/folder-item/folder-item.tsx +++ b/src/components/folder-item/folder-item.tsx @@ -9,7 +9,7 @@ import { useDrivesContainer } from 'src/hooks/useDrivesContainer'; import { useIsAllowedToCreateDocuments } from 'src/hooks/useIsAllowedToCreateDocuments'; import { useOnDropEvent } from 'src/hooks/useOnDropEvent'; -const allowedItemOptions = ['delete', 'rename']; +const allowedItemOptions = ['delete', 'rename', 'duplicate']; const itemOptions = defaultDropdownMenuOptions.filter(option => allowedItemOptions.includes(option.id), @@ -26,11 +26,14 @@ export const FolderItem: React.FC = props => { const isAllowedToCreateDocuments = useIsAllowedToCreateDocuments(); const { showModal } = useModal(); - const { updateNodeName } = useDrivesContainer(); + const { updateNodeName, onSubmitInput } = useDrivesContainer(); const [isWriteMode, setIsWriteMode] = useState(false); const onDropEvent = useOnDropEvent(); - const onFolderOptionsClick = (optionId: string, folderNode: TreeItem) => { + const onFolderOptionsClick = async ( + optionId: string, + folderNode: TreeItem, + ) => { if (optionId === 'delete') { showModal('deleteItem', { driveId: decodedDriveID, @@ -40,6 +43,13 @@ export const FolderItem: React.FC = props => { }); } + if (optionId === 'duplicate') { + await onSubmitInput({ + ...folder, + action: 'UPDATE_AND_COPY', + }); + } + if (optionId === 'rename') { setIsWriteMode(true); } diff --git a/src/hooks/useDocumentDriveServer.ts b/src/hooks/useDocumentDriveServer.ts index 9871db96..73c30e24 100644 --- a/src/hooks/useDocumentDriveServer.ts +++ b/src/hooks/useDocumentDriveServer.ts @@ -244,63 +244,65 @@ export function useDocumentDriveServer( return node; } - async function copyOrMoveNode( - driveId: string, - srcId: string, - targetId: string, - operation: string, - targetName?: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use this later for sorting - sortOptions?: SortOptions, - ) { - if (!isAllowedToCreateDocuments) { - throw new Error('User is not allowed to copy or move nodes'); - } - if (srcId === targetId) return; + async function moveNode(params: { + srcId: string; + decodedDriveId: string; + decodedTargetId: string; + }) { + const { decodedDriveId, srcId, decodedTargetId } = params; + + await _addDriveOperation( + decodedDriveId, + actions.moveNode({ + srcFolder: srcId, + targetParentFolder: decodedTargetId, + }), + ); + } + + async function copyNode(params: { + srcId: string; + srcName: string; + decodedDriveId: string; + decodedTargetId: string; + }) { + const { decodedDriveId, srcId, srcName, decodedTargetId } = params; + + if (srcId === decodedTargetId) return; const drive = documentDrives.find( - drive => drive.state.global.id === driveId, + drive => drive.state.global.id === decodedDriveId, ); - if (operation === 'copy' && drive) { - const targetParentFolder = targetId === '' ? null : targetId; - const generateId = () => utils.hashKey(); - - const copyNodesInput = documentDriveUtils.generateNodesCopy( - { - srcId, - targetParentFolder, - ...(targetName && { targetName }), - }, - generateId, - drive.state.global.nodes, - ); + if (!drive) return; - const copyActions = copyNodesInput.map(copyNodeInput => - actions.copyNode(copyNodeInput), - ); + const generateId = () => utils.hashKey(); - const result = await server.addDriveActions(driveId, copyActions); - if (result.operations.length) { - await refreshDocumentDrives(); - } else if (result.status !== 'SUCCESS') { - console.error( - `Error copying files: ${result.status}`, - result.error, - ); - } - } else { - await _addDriveOperation( - driveId, - actions.moveNode({ - srcFolder: srcId, - targetParentFolder: targetId, - }), - ); + const copyNodesInput = documentDriveUtils.generateNodesCopy( + { + srcId, + targetParentFolder: decodedTargetId, + targetName: srcName, + }, + generateId, + drive.state.global.nodes, + ); - if (targetName) { - await renameNode(driveId, srcId, targetName); - } + const copyActions = copyNodesInput.map(copyNodeInput => + actions.copyNode(copyNodeInput), + ); + + const result = await server.addDriveActions( + decodedDriveId, + copyActions, + ); + if (result.operations.length) { + await refreshDocumentDrives(); + } else if (result.status !== 'SUCCESS') { + console.error( + `Error copying files: ${result.status}`, + result.error, + ); } } @@ -364,9 +366,6 @@ export function useDocumentDriveServer( } async function addRemoteDrive(url: string, options: RemoteDriveOptions) { - if (!isAllowedToCreateDocuments) { - throw new Error('User is not allowed to create drives'); - } await server.addRemoteDrive(url, options); await refreshDocumentDrives(); } @@ -467,7 +466,8 @@ export function useDocumentDriveServer( addFolder, deleteNode, renameNode, - copyOrMoveNode, + moveNode, + copyNode, addOperation, addOperations, getChildren, diff --git a/src/hooks/useDrivesContainer.ts b/src/hooks/useDrivesContainer.ts index 1a672407..b15bd503 100644 --- a/src/hooks/useDrivesContainer.ts +++ b/src/hooks/useDrivesContainer.ts @@ -19,7 +19,7 @@ import { import path from 'path'; import { useTranslation } from 'react-i18next'; import { useModal } from 'src/components/modal'; -import { getLastIndexFromPath, sanitizePath } from 'src/utils'; +import { getLastIndexFromPath } from 'src/utils'; import { v4 as uuid } from 'uuid'; import { useDocumentDriveServer } from './useDocumentDriveServer'; import { useNavigateToItemId } from './useNavigateToItemId'; @@ -74,7 +74,8 @@ export function useDrivesContainer() { setDriveAvailableOffline, setDriveSharingType, documentDrives, - copyOrMoveNode, + copyNode, + moveNode, getSyncStatus, } = useDocumentDriveServer(); @@ -106,17 +107,9 @@ export function useDrivesContainer() { }); } - async function addNewFolder( - item: TreeItem, - driveID: string, - onCancel?: () => void, - ) { + async function addNewFolder(item: TreeItem, driveID: string) { const basePathComponents = item.path.split('/').slice(1, -1); - const basePath = basePathComponents.join('/'); - const newPath = path.join(basePath, sanitizePath(item.label)); - // TODO is this needed? - if (newPath === '.') return onCancel?.(); const decodedDriveID = decodeID(driveID); const parentFolder = basePathComponents.pop(); await addFolder( @@ -190,6 +183,12 @@ export function useDrivesContainer() { decodeID(item.id), item.sharingType?.toLowerCase() as TreeItemSharingType, ); + break; + case 'duplicate': + await onSubmitInput({ + ...item, + action: 'UPDATE_AND_COPY', + }); } }; @@ -198,40 +197,49 @@ export function useDrivesContainer() { await renameNode(decodedDriveID, item.id, item.label); } - const onSubmitInput = (item: TreeItem, onCancel?: () => void) => { - const driveID = item.path.split('/')[0]; + async function onSubmitInput(item: TreeItem, onCancel?: () => void) { + const driveId = getRootPath(item.path); - if (item.action === 'NEW') { + const isCreateNewOperation = item.action === 'NEW'; + const isMoveOperation = item.action === 'UPDATE_AND_MOVE'; + const isCopyOperation = item.action === 'UPDATE_AND_COPY'; + + if (isCreateNewOperation) { actions.deleteVirtualItem(item.id); - addNewFolder(item, driveID, onCancel); + await addNewFolder(item, driveId); return; } - if ( - item.action === 'UPDATE_AND_COPY' || - item.action === 'UPDATE_AND_MOVE' - ) { - actions.deleteVirtualItem(item.id); + const srcId = item.id; + const srcName = item.label; + const targetPath = path.dirname(item.path); + let targetId = targetPath.split('/').pop() ?? ''; + + if (targetId === driveId || targetId == '.') { + targetId = ''; + } + + const decodedDriveId = decodeID(driveId); + const decodedTargetId = decodeID(targetId); + + if (isMoveOperation) { + await moveNode({ + decodedDriveId, + srcId, + decodedTargetId, + }); + + return; + } + + if (isCopyOperation) { + await copyNode({ + decodedDriveId, + srcId, + decodedTargetId, + srcName, + }); - const driveID = getRootPath(item.path); - const srcID = item.id.replace('(from)', ''); - const targetPath = path.dirname(item.path); - const operation = - item.action === 'UPDATE_AND_COPY' ? 'copy' : 'move'; - - let targetId = targetPath.split('/').pop() ?? ''; - - if (targetId === driveID || targetId == '.') { - targetId = ''; - } - - copyOrMoveNode( - decodeID(driveID), - srcID, - decodeID(targetId), - operation, - item.label, - ); return; } @@ -242,8 +250,8 @@ export function useDrivesContainer() { return; } - updateNodeName(item, driveID); - }; + await updateNodeName(item, driveId); + } async function driveToBaseItems(drive: DocumentDriveDocument) { const driveID = encodeID(drive.state.global.id); diff --git a/src/hooks/useGetReadableItemPath.ts b/src/hooks/useGetReadableItemPath.ts index c437e953..6377dd25 100644 --- a/src/hooks/useGetReadableItemPath.ts +++ b/src/hooks/useGetReadableItemPath.ts @@ -10,7 +10,7 @@ export const useGetReadableItemPath = () => { const pathSegments = filteredItem.path.split('/'); const pathNames = pathSegments.map(segmentId => { const segmentItem = items.find( - item => item.id === decodeID(segmentId) + item => item.id === decodeID(segmentId), ); if (!segmentItem) return ''; diff --git a/src/hooks/useOnDropEvent.ts b/src/hooks/useOnDropEvent.ts index d8b2e331..dfe5c68f 100644 --- a/src/hooks/useOnDropEvent.ts +++ b/src/hooks/useOnDropEvent.ts @@ -2,30 +2,24 @@ import { TreeItem, UseDraggableTargetProps, decodeID, - encodeID, getRootPath, - useFilterPathContent, - useItemActions, } from '@powerhousedao/design-system'; import path from 'path'; -import { - SortOptions, - useDocumentDriveServer, -} from 'src/hooks/useDocumentDriveServer'; +import { useDocumentDriveServer } from 'src/hooks/useDocumentDriveServer'; export const useOnDropEvent = () => { - const { copyOrMoveNode, addFile } = useDocumentDriveServer(); - const actions = useItemActions(); - const filterPathContent = useFilterPathContent(); + const { copyNode, moveNode, addFile } = useDocumentDriveServer(); const onDropEventHandler: UseDraggableTargetProps['onDropEvent'] = async (item, target, event) => { - const driveID = getRootPath(target.path); + const driveId = getRootPath(target.path); const isDropAfter = !!item.dropAfterItem; - const sortOptions: SortOptions | undefined = isDropAfter - ? { afterNodePath: target.id } - : undefined; + const isFileUpload = item.kind === 'file'; + + // const sortOptions: SortOptions | undefined = isDropAfter + // ? { afterNodePath: target.id } + // : undefined; const targetPath = isDropAfter && !target.expanded @@ -34,54 +28,50 @@ export const useOnDropEvent = () => { let targetId = targetPath.split('/').pop() ?? ''; - if (targetId === driveID || targetId == '.') { + if (targetId === driveId || targetId == '.') { targetId = ''; } - const decodedDriveID = decodeID(driveID); - - if (item.kind === 'object') { - const filterPath = filterPathContent( - treeItem => - treeItem.label === item.data.label && - treeItem.id !== item.data.id, - { path: targetPath }, - ); + const decodedDriveId = decodeID(driveId); + const decodedTargetId = decodeID(targetId); - if (filterPath.length > 0) { - actions.setExpandedItem(target.id, true); - actions.newVirtualItem({ - id: `(from)${item.data.id}`, - label: `${item.data.label} (2)`, - path: path.join(targetPath, encodeID(item.data.id)), - type: item.data.type, - action: - event.dropOperation === 'copy' - ? 'UPDATE_AND_COPY' - : 'UPDATE_AND_MOVE', - sharingType: item.data.sharingType, - availableOffline: item.data.availableOffline, - }); - return; - } - - copyOrMoveNode( - decodedDriveID, - item.data.id, - decodeID(targetId), - event.dropOperation, - undefined, - sortOptions, - ).catch(console.error); - } else { + if (isFileUpload) { const file = await item.getFile(); + await addFile( file, - decodedDriveID, + decodedDriveId, undefined, decodeID(targetId), ); + + return; } + + if (target.type === 'FILE') { + throw new Error('Cannot move a node into a file'); + } + + const isMoveOperation = event.dropOperation === 'move'; + const srcId = item.data.id; + const srcName = item.data.label; + + if (isMoveOperation) { + await moveNode({ + decodedDriveId, + srcId, + decodedTargetId, + }); + + return; + } + + await copyNode({ + decodedDriveId, + srcId, + decodedTargetId, + srcName, + }); }; return onDropEventHandler; diff --git a/src/pages/content.tsx b/src/pages/content.tsx index b1325170..5bf2999a 100644 --- a/src/pages/content.tsx +++ b/src/pages/content.tsx @@ -119,30 +119,44 @@ const Content = () => { // builds the path from the url checking if the nodes exist const path = [encodeID(drive.state.global.id)]; + let currentNodes = drive.state.global.nodes.filter( + node => node.parentFolder === null, + ); if (params['*']) { - for (const nodeId of decodeURIComponent(params['*']).split( - '/', - )) { - const node = drive.state.global.nodes.find( - node => node.name === nodeId || node.id === nodeId, + const nodeNames = decodeURIComponent(params['*']).split('/'); + + for (const nodeName of nodeNames) { + const node = currentNodes.find( + node => node.name === nodeName, ); - if (node) { - // if the node is a file, then opens it instead of adding it to the path - if (isFileNode(node)) { - if ( - selectedFileNode?.drive !== - drive.state.global.id || - selectedFileNode.id !== node.id - ) { - setSelectedFileNode({ - drive: drive.state.global.id, - id: node.id, - }); - } - break; + + if (!node) { + console.error('Node not found:', nodeName); + break; + } + + // if the node is a file, then opens it instead of adding it to the path + if (isFileNode(node)) { + if ( + selectedFileNode?.drive !== drive.state.global.id || + selectedFileNode.id !== node.id + ) { + setSelectedFileNode({ + drive: drive.state.global.id, + id: node.id, + }); } - path.push(encodeID(node.id)); + break; } + path.push(encodeID(node.id)); + + const nextNodes = drive.state.global.nodes.filter( + n => n.parentFolder === node.id, + ); + + if (!nextNodes.length) break; + + currentNodes = nextNodes; } } setSelectedPath(path.join('/')); diff --git a/vercel.json b/vercel.json index 0967ef42..9e940b3a 100644 --- a/vercel.json +++ b/vercel.json @@ -1 +1,3 @@ -{} +{ + "rewrites": [{ "source": "/(.*)", "destination": "/" }] +}