From 6ce657d8919397c46ddf5c70afbc4d66d99e602a Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Fri, 11 Nov 2022 03:53:51 +0530 Subject: [PATCH 1/3] feat: drag and drop events (#57) --- packages/bruno-app/package.json | 12 ++++---- .../CollectionItem/StyledWrapper.js | 2 +- .../Collection/CollectionItem/index.js | 26 +++++++++++++++-- .../Sidebar/Collections/Collection/index.js | 28 +++++++++++-------- .../src/pageComponents/Index/StyledWrapper.js | 11 -------- packages/bruno-app/src/pages/_app.js | 12 +++++++- packages/bruno-graphql-docs/package.json | 7 ++--- 7 files changed, 61 insertions(+), 37 deletions(-) diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index 75a40dcd..7ca31f8a 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -1,5 +1,6 @@ { "name": "@usebruno/app", + "version": "0.3.0", "private": true, "scripts": { "dev": "next dev", @@ -15,8 +16,8 @@ "@reduxjs/toolkit": "^1.8.0", "@tabler/icons": "^1.46.0", "@tippyjs/react": "^4.2.6", - "@usebruno/schema": "0.2.0", "@usebruno/graphql-docs": "0.1.0", + "@usebruno/schema": "0.2.0", "axios": "^0.26.0", "classnames": "^2.3.1", "codemirror": "^5.65.2", @@ -34,16 +35,17 @@ "markdown-it": "^13.0.1", "mousetrap": "^1.6.5", "nanoid": "3.3.4", - "next": "12.3.1", + "next": "12.3.3", "path": "^0.12.7", "platform": "^1.3.6", "posthog-node": "^2.1.0", "qs": "^6.11.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "18.2.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "18.2.0", "react-hot-toast": "^2.4.0", "react-redux": "^7.2.6", - "react-tabs": "^3.2.3", "reckonjs": "^0.1.2", "sass": "^1.46.0", "split-on-first": "^3.0.0", diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/StyledWrapper.js index 11f945b3..ea58fe94 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/StyledWrapper.js @@ -70,7 +70,7 @@ const Wrapper = styled.div` } } - &.is-dragging .collection-item-name { + &.is-sidebar-dragging .collection-item-name { cursor: inherit; } `; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index b41940c9..97989baf 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -2,6 +2,7 @@ import React, { useState, useRef, forwardRef, useEffect } from 'react'; import range from 'lodash/range'; import filter from 'lodash/filter'; import classnames from 'classnames'; +import { useDrag, useDrop } from 'react-dnd'; import { IconChevronRight, IconDots } from '@tabler/icons'; import { useSelector, useDispatch } from 'react-redux'; import { addTab, focusTab } from 'providers/ReduxStore/slices/tabs'; @@ -23,8 +24,25 @@ import StyledWrapper from './StyledWrapper'; const CollectionItem = ({ item, collection, searchText }) => { const tabs = useSelector((state) => state.tabs.tabs); const activeTabUid = useSelector((state) => state.tabs.activeTabUid); - const isDragging = useSelector((state) => state.app.isDragging); + const isSidebarDragging = useSelector((state) => state.app.isDragging); const dispatch = useDispatch(); + const ref = useRef(null) + + const [{isDragging}, drag] = useDrag(() => ({ + type: 'collection-item', + collect: monitor => ({ + isDragging: !!monitor.isDragging(), + }) + })); + const [{ isOver, canDrop }, drop] = useDrop({ + accept: 'collection-item', + drop: () => console.log('dropped'), + canDrop: () => true, + collect: monitor => ({ + isOver: !!monitor.isOver(), + canDrop: !!monitor.canDrop() + }), + }); const [renameItemModalOpen, setRenameItemModalOpen] = useState(false); const [cloneItemModalOpen, setCloneItemModalOpen] = useState(false); @@ -91,7 +109,7 @@ const CollectionItem = ({ item, collection, searchText }) => { const isFolder = isItemAFolder(item); const className = classnames('flex flex-col w-full', { - 'is-dragging': isDragging + 'is-sidebar-dragging': isSidebarDragging }); if (searchText && searchText.length) { @@ -109,6 +127,8 @@ const CollectionItem = ({ item, collection, searchText }) => { const requestItems = filter(item.items, (i) => isItemARequest(i)); const folderItems = filter(item.items, (i) => isItemAFolder(i)); + drag(drop(ref)) + return ( {renameItemModalOpen && setRenameItemModalOpen(false)} />} @@ -116,7 +136,7 @@ const CollectionItem = ({ item, collection, searchText }) => { {deleteItemModalOpen && setDeleteItemModalOpen(false)} />} {newRequestModalOpen && setNewRequestModalOpen(false)} />} {newFolderModalOpen && setNewFolderModalOpen(false)} />} -
+
{indents && indents.length ? indents.map((i) => { diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js index 37fc8513..b0c44a8b 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js @@ -2,6 +2,8 @@ import React, { useState, forwardRef, useRef, useEffect } from 'react'; import classnames from 'classnames'; import filter from 'lodash/filter'; import cloneDeep from 'lodash/cloneDeep'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { IconChevronRight, IconDots } from '@tabler/icons'; import Dropdown from 'components/Dropdown'; import { collectionClicked } from 'providers/ReduxStore/slices/collections'; @@ -162,19 +164,21 @@ const Collection = ({ collection, searchText }) => {
{!collectionIsCollapsed ? ( -
- {requestItems && requestItems.length - ? requestItems.map((i) => { - return ; - }) - : null} + +
+ {requestItems && requestItems.length + ? requestItems.map((i) => { + return ; + }) + : null} - {folderItems && folderItems.length - ? folderItems.map((i) => { - return ; - }) - : null} -
+ {folderItems && folderItems.length + ? folderItems.map((i) => { + return ; + }) + : null} +
+ ) : null}
diff --git a/packages/bruno-app/src/pageComponents/Index/StyledWrapper.js b/packages/bruno-app/src/pageComponents/Index/StyledWrapper.js index ad638b66..4a70f838 100644 --- a/packages/bruno-app/src/pageComponents/Index/StyledWrapper.js +++ b/packages/bruno-app/src/pageComponents/Index/StyledWrapper.js @@ -21,17 +21,6 @@ const Wrapper = styled.div` .fw-600 { font-weight: 600; } - - .react-tabs { - .react-tabs__tab-list { - padding-left: 1rem; - border-bottom: 1px solid #cfcfcf; - - .react-tabs__tab--selected { - border-color: #cfcfcf; - } - } - } `; export default Wrapper; diff --git a/packages/bruno-app/src/pages/_app.js b/packages/bruno-app/src/pages/_app.js index c896d77c..f0b5bbce 100644 --- a/packages/bruno-app/src/pages/_app.js +++ b/packages/bruno-app/src/pages/_app.js @@ -1,3 +1,4 @@ +import { useState, useEffect } from 'react'; import { Provider } from 'react-redux'; import { AppProvider } from 'providers/App'; import { ToastProvider } from 'providers/Toaster'; @@ -9,7 +10,6 @@ import ThemeProvider from 'providers/Theme/index'; import '../styles/app.scss'; import '../styles/globals.css'; import 'tailwindcss/dist/tailwind.min.css'; -import 'react-tabs/style/react-tabs.css'; import 'codemirror/lib/codemirror.css'; import 'graphiql/graphiql.min.css'; @@ -28,6 +28,16 @@ function NoSsr({ children }) { } function MyApp({ Component, pageProps }) { + const [domLoaded, setDomLoaded] = useState(false); + + useEffect(() => { + setDomLoaded(true); + }, []); + + if(!domLoaded) { + return null; + } + return ( diff --git a/packages/bruno-graphql-docs/package.json b/packages/bruno-graphql-docs/package.json index 8aee19f0..5eb4bfcd 100644 --- a/packages/bruno-graphql-docs/package.json +++ b/packages/bruno-graphql-docs/package.json @@ -19,8 +19,8 @@ "graphql": "^16.6.0", "markdown-it": "^13.0.1", "postcss": "^8.4.18", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "18.2.0", + "react-dom": "18.2.0", "rollup": "3.2.5", "rollup-plugin-dts": "^5.0.0", "rollup-plugin-peer-deps-external": "^2.2.4", @@ -30,8 +30,7 @@ }, "peerDependencies": { "graphql": "^16.6.0", - "markdown-it": "^13.0.1", - "react": "^17.0.2" + "markdown-it": "^13.0.1" }, "overrides": { "rollup": "3.2.5" From ee4734c95740562b52f19edfaf79769c6b70058c Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Wed, 28 Dec 2022 04:48:49 +0530 Subject: [PATCH 2/3] feat: move requests across folders --- .../Collection/CollectionItem/index.js | 45 +++++++------ .../ReduxStore/slices/collections/actions.js | 65 +++++++++++++++++++ .../ReduxStore/slices/collections/index.js | 19 ++++++ .../bruno-app/src/utils/collections/index.js | 27 +++++++- 4 files changed, 135 insertions(+), 21 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index 97989baf..5f7de14b 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -7,6 +7,7 @@ import { IconChevronRight, IconDots } from '@tabler/icons'; import { useSelector, useDispatch } from 'react-redux'; import { addTab, focusTab } from 'providers/ReduxStore/slices/tabs'; import { collectionFolderClicked } from 'providers/ReduxStore/slices/collections'; +import { moveItem } from 'providers/ReduxStore/slices/collections/actions'; import Dropdown from 'components/Dropdown'; import NewRequest from 'components/Sidebar/NewRequest'; import NewFolder from 'components/Sidebar/NewFolder'; @@ -26,23 +27,6 @@ const CollectionItem = ({ item, collection, searchText }) => { const activeTabUid = useSelector((state) => state.tabs.activeTabUid); const isSidebarDragging = useSelector((state) => state.app.isDragging); const dispatch = useDispatch(); - const ref = useRef(null) - - const [{isDragging}, drag] = useDrag(() => ({ - type: 'collection-item', - collect: monitor => ({ - isDragging: !!monitor.isDragging(), - }) - })); - const [{ isOver, canDrop }, drop] = useDrop({ - accept: 'collection-item', - drop: () => console.log('dropped'), - canDrop: () => true, - collect: monitor => ({ - isOver: !!monitor.isOver(), - canDrop: !!monitor.canDrop() - }), - }); const [renameItemModalOpen, setRenameItemModalOpen] = useState(false); const [cloneItemModalOpen, setCloneItemModalOpen] = useState(false); @@ -51,6 +35,29 @@ const CollectionItem = ({ item, collection, searchText }) => { const [newFolderModalOpen, setNewFolderModalOpen] = useState(false); const [itemIsCollapsed, setItemisCollapsed] = useState(item.collapsed); + const [{ isDragging }, drag] = useDrag({ + type: 'COLLECTION_ITEM', + item: item, + collect: (monitor) => ({ + isDragging: monitor.isDragging() + }) + }); + + const [{ isOver }, drop] = useDrop({ + accept: 'COLLECTION_ITEM', + drop: (draggedItem) => { + if (draggedItem.uid !== item.uid) { + dispatch(moveItem(collection.uid, draggedItem.uid, item.uid)); + } + }, + canDrop: (draggedItem) => { + return draggedItem.id !== item.uid; + }, + collect: (monitor) => ({ + isOver: monitor.isOver() + }) + }); + useEffect(() => { if (searchText && searchText.length) { setItemisCollapsed(false); @@ -127,8 +134,6 @@ const CollectionItem = ({ item, collection, searchText }) => { const requestItems = filter(item.items, (i) => isItemARequest(i)); const folderItems = filter(item.items, (i) => isItemAFolder(i)); - drag(drop(ref)) - return ( {renameItemModalOpen && setRenameItemModalOpen(false)} />} @@ -136,7 +141,7 @@ const CollectionItem = ({ item, collection, searchText }) => { {deleteItemModalOpen && setDeleteItemModalOpen(false)} />} {newRequestModalOpen && setNewRequestModalOpen(false)} />} {newFolderModalOpen && setNewFolderModalOpen(false)} />} -
+
drag(drop(node))}>
{indents && indents.length ? indents.map((i) => { diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index acc9883f..4ecee539 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -7,6 +7,7 @@ import { uuid } from 'utils/common'; import cloneDeep from 'lodash/cloneDeep'; import { findItemInCollection, + moveCollectionItem, findCollectionByUid, recursivelyGetAllItemUids, transformCollectionToSaveToIdb, @@ -33,6 +34,7 @@ import { renameItem as _renameItem, cloneItem as _cloneItem, deleteItem as _deleteItem, + moveItem as _moveItem, saveRequest as _saveRequest, addEnvironment as _addEnvironment, renameEnvironment as _renameEnvironment, @@ -550,6 +552,69 @@ export const deleteItem = (itemUid, collectionUid) => (dispatch, getState) => { }); }; +export const moveItem = (collectionUid, draggedItemUid, targetItemUid) => (dispatch, getState) => { + const state = getState(); + const collection = findCollectionByUid(state.collections.collections, collectionUid); + + return new Promise((resolve, reject) => { + if (!collection) { + return reject(new Error('Collection not found')); + } + + if (isLocalCollection(collection)) { + const draggedItem = findItemInCollection(collection, draggedItemUid); + const targetItem = findItemInCollection(collection, targetItemUid); + + if (!draggedItem) { + return reject(new Error('Dragged item not found')); + } + + if (!targetItem) { + return reject(new Error('Target item not found')); + } + + const { ipcRenderer } = window; + + ipcRenderer + .invoke('renderer:move-item', draggedItem.pathname, targetItem.pathname) + .then(() => resolve()) + .catch((error) => reject(error)); + return; + } + + const collectionCopy = cloneDeep(collection); + const draggedItem = findItemInCollection(collectionCopy, draggedItemUid); + const targetItem = findItemInCollection(collectionCopy, targetItemUid); + + if (!draggedItem) { + return reject(new Error('Dragged item not found')); + } + + if (!targetItem) { + return reject(new Error('Target item not found')); + } + + moveCollectionItem(collectionCopy, draggedItem, targetItem); + + const collectionToSave = transformCollectionToSaveToIdb(collectionCopy); + + collectionSchema + .validate(collectionToSave) + .then(() => saveCollectionToIdb(window.__idb, collectionToSave)) + .then(() => { + dispatch( + _moveItem({ + collectionUid: collectionUid, + draggedItemUid: draggedItemUid, + targetItemUid: targetItemUid + }) + ); + }) + .then(() => resolve()) + .catch((error) => reject(error)); + }); +}; + export const newHttpRequest = (params) => (dispatch, getState) => { const { requestName, requestType, requestUrl, requestMethod, collectionUid, itemUid } = params; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index a9ae2259..5d09b761 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -14,6 +14,7 @@ import { findEnvironmentInCollection, findItemInCollectionByPathname, addDepth, + moveCollectionItem, collapseCollection, deleteItemInCollection, isItemARequest @@ -181,6 +182,23 @@ export const collectionsSlice = createSlice({ } } }, + moveItem: (state, action) => { + const collection = findCollectionByUid(state.collections, action.payload.collectionUid); + const draggedItemUid = action.payload.draggedItemUid; + const targetItemUid = action.payload.targetItemUid; + + if (collection) { + const draggedItem = findItemInCollection(collection, draggedItemUid); + const targetItem = findItemInCollection(collection, targetItemUid); + + if (!draggedItem || !targetItem) { + return; + } + + moveCollectionItem(collection, draggedItem, targetItem); + addDepth(collection.items); + } + }, requestSent: (state, action) => { const { itemUid, collectionUid, cancelTokenUid } = action.payload; const collection = findCollectionByUid(state.collections, collectionUid); @@ -785,6 +803,7 @@ export const { deleteItem, renameItem, cloneItem, + moveItem, requestSent, requestCancelled, responseReceived, diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index 8c826380..70d6bc64 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -1,7 +1,7 @@ -import reckon from 'reckonjs'; import get from 'lodash/get'; import each from 'lodash/each'; import find from 'lodash/find'; +import findIndex from 'lodash/findIndex'; import isString from 'lodash/isString'; import map from 'lodash/map'; import filter from 'lodash/filter'; @@ -122,6 +122,31 @@ export const findEnvironmentInCollection = (collection, envUid) => { return find(collection.environments, (e) => e.uid === envUid); }; +export const moveCollectionItem = (collection, draggedItem, targetItem) => { + let draggedItemParent = findParentItemInCollection(collection, draggedItem.uid); + + if (draggedItemParent) { + draggedItemParent.items = filter(draggedItemParent.items, (i) => i.uid !== draggedItem.uid); + } else { + collection.items = filter(collection.items, (i) => i.uid !== draggedItem.uid); + } + + if (targetItem.type === 'folder') { + targetItem.items = targetItem.items || []; + targetItem.items.push(draggedItem); + } else { + let targetItemParent = findParentItemInCollection(collection, targetItem.uid); + + if (targetItemParent) { + let targetItemIndex = findIndex(targetItemParent.items, (i) => i.uid === targetItem.uid); + targetItemParent.items.splice(targetItemIndex + 1, 0, draggedItem); + } else { + let targetItemIndex = findIndex(collection.items, (i) => i.uid === targetItem.uid); + collection.items.splice(targetItemIndex + 1, 0, draggedItem); + } + } +}; + export const transformCollectionToSaveToIdb = (collection, options = {}) => { const copyHeaders = (headers) => { return map(headers, (header) => { From 36d0550472f03424f49b3c8ac9da34e1b278c71e Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 10 Jan 2023 09:37:09 +0530 Subject: [PATCH 3/3] feat: drag item to root of collection --- .../Collection/CollectionItem/index.js | 2 +- .../Sidebar/Collections/Collection/index.js | 47 ++++++++++------ .../components/Sidebar/Collections/index.js | 8 ++- .../ReduxStore/slices/collections/actions.js | 54 +++++++++++++++++++ .../ReduxStore/slices/collections/index.js | 16 ++++++ .../bruno-app/src/utils/collections/index.js | 12 +++++ 6 files changed, 120 insertions(+), 19 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index 5f7de14b..46c8f5b9 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -51,7 +51,7 @@ const CollectionItem = ({ item, collection, searchText }) => { } }, canDrop: (draggedItem) => { - return draggedItem.id !== item.uid; + return draggedItem.uid !== item.uid; }, collect: (monitor) => ({ isOver: monitor.isOver() diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js index b0c44a8b..b847ece9 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js @@ -2,11 +2,11 @@ import React, { useState, forwardRef, useRef, useEffect } from 'react'; import classnames from 'classnames'; import filter from 'lodash/filter'; import cloneDeep from 'lodash/cloneDeep'; -import { DndProvider } from 'react-dnd'; -import { HTML5Backend } from 'react-dnd-html5-backend'; +import { useDrop } from 'react-dnd'; import { IconChevronRight, IconDots } from '@tabler/icons'; import Dropdown from 'components/Dropdown'; import { collectionClicked } from 'providers/ReduxStore/slices/collections'; +import { moveItemToRootOfCollection } from 'providers/ReduxStore/slices/collections/actions'; import { useDispatch } from 'react-redux'; import NewRequest from 'components/Sidebar/NewRequest'; import NewFolder from 'components/Sidebar/NewFolder'; @@ -73,6 +73,21 @@ const Collection = ({ collection, searchText }) => { const isLocal = isLocalCollection(collection); + const [{ isOver }, drop] = useDrop({ + accept: 'COLLECTION_ITEM', + drop: (draggedItem) => { + console.log('drop', draggedItem); + dispatch(moveItemToRootOfCollection(collection.uid, draggedItem.uid)); + }, + canDrop: (draggedItem) => { + // todo need to make sure that draggedItem belongs to the collection + return true; + }, + collect: (monitor) => ({ + isOver: monitor.isOver() + }) + }); + return ( {showNewRequestModal && setShowNewRequestModal(false)} />} @@ -81,7 +96,7 @@ const Collection = ({ collection, searchText }) => { {showRemoveCollectionFromWSModal && setShowRemoveCollectionFromWSModal(false)} />} {showDeleteCollectionModal && setShowDeleteCollectionModal(false)} />} {showRemoveLocalCollectionModal && setShowRemoveLocalCollectionModal(false)} />} -
+
drop(node)}>
@@ -164,21 +179,19 @@ const Collection = ({ collection, searchText }) => {
{!collectionIsCollapsed ? ( - -
- {requestItems && requestItems.length - ? requestItems.map((i) => { - return ; - }) - : null} +
+ {requestItems && requestItems.length + ? requestItems.map((i) => { + return ; + }) + : null} - {folderItems && folderItems.length - ? folderItems.map((i) => { - return ; - }) - : null} -
- + {folderItems && folderItems.length + ? folderItems.map((i) => { + return ; + }) + : null} +
) : null}
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/index.js b/packages/bruno-app/src/components/Sidebar/Collections/index.js index 3d712c8c..eb5847e2 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/index.js @@ -1,5 +1,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import find from 'lodash/find'; import filter from 'lodash/filter'; import Collection from './Collection'; @@ -26,7 +28,11 @@ const Collections = ({ searchText }) => {
{collectionToDisplay && collectionToDisplay.length ? collectionToDisplay.map((c) => { - return ; + return ( + + ; + + ); }) : null}
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 4ecee539..46996f6a 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -8,6 +8,7 @@ import cloneDeep from 'lodash/cloneDeep'; import { findItemInCollection, moveCollectionItem, + moveCollectionItemToRootOfCollection, findCollectionByUid, recursivelyGetAllItemUids, transformCollectionToSaveToIdb, @@ -35,6 +36,7 @@ import { cloneItem as _cloneItem, deleteItem as _deleteItem, moveItem as _moveItem, + moveItemToRootOfCollection as _moveItemToRootOfCollection, saveRequest as _saveRequest, addEnvironment as _addEnvironment, renameEnvironment as _renameEnvironment, @@ -615,6 +617,58 @@ export const moveItem = (collectionUid, draggedItemUid, targetItemUid) => (dispa }); }; +export const moveItemToRootOfCollection = (collectionUid, draggedItemUid) => (dispatch, getState) => { + const state = getState(); + const collection = findCollectionByUid(state.collections.collections, collectionUid); + + return new Promise((resolve, reject) => { + if (!collection) { + return reject(new Error('Collection not found')); + } + + if (isLocalCollection(collection)) { + const draggedItem = findItemInCollection(collection, draggedItemUid); + + if (!draggedItem) { + return reject(new Error('Dragged item not found')); + } + + const { ipcRenderer } = window; + + ipcRenderer + .invoke('renderer:move-item-to-root-of-collection', draggedItem.pathname) + .then(() => resolve()) + .catch((error) => reject(error)); + return; + } + + const collectionCopy = cloneDeep(collection); + const draggedItem = findItemInCollection(collectionCopy, draggedItemUid); + + if (!draggedItem) { + return reject(new Error('Dragged item not found')); + } + + moveCollectionItemToRootOfCollection(collectionCopy, draggedItem); + + const collectionToSave = transformCollectionToSaveToIdb(collectionCopy); + + collectionSchema + .validate(collectionToSave) + .then(() => saveCollectionToIdb(window.__idb, collectionToSave)) + .then(() => { + dispatch( + _moveItemToRootOfCollection({ + collectionUid: collectionUid, + draggedItemUid: draggedItemUid + }) + ); + }) + .then(() => resolve()) + .catch((error) => reject(error)); + }); +}; + export const newHttpRequest = (params) => (dispatch, getState) => { const { requestName, requestType, requestUrl, requestMethod, collectionUid, itemUid } = params; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index 5d09b761..1a9b3ec1 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -199,6 +199,21 @@ export const collectionsSlice = createSlice({ addDepth(collection.items); } }, + moveItemToRootOfCollection: (state, action) => { + const collection = findCollectionByUid(state.collections, action.payload.collectionUid); + const draggedItemUid = action.payload.draggedItemUid; + + if (collection) { + const draggedItem = findItemInCollection(collection, draggedItemUid); + + if (!draggedItem) { + return; + } + + moveCollectionItemToRootOfCollection(collection, draggedItem); + addDepth(collection.items); + } + }, requestSent: (state, action) => { const { itemUid, collectionUid, cancelTokenUid } = action.payload; const collection = findCollectionByUid(state.collections, collectionUid); @@ -804,6 +819,7 @@ export const { renameItem, cloneItem, moveItem, + moveItemToRootOfCollection, requestSent, requestCancelled, responseReceived, diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index 70d6bc64..f01a0267 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -147,6 +147,18 @@ export const moveCollectionItem = (collection, draggedItem, targetItem) => { } }; +export const moveCollectionItemToRootOfCollection = (collection, draggedItem) => { + let draggedItemParent = findParentItemInCollection(collection, draggedItem.uid); + + if (draggedItemParent) { + draggedItemParent.items = filter(draggedItemParent.items, (i) => i.uid !== draggedItem.uid); + } else { + collection.items = filter(collection.items, (i) => i.uid !== draggedItem.uid); + } + + collection.items.push(draggedItem); +}; + export const transformCollectionToSaveToIdb = (collection, options = {}) => { const copyHeaders = (headers) => { return map(headers, (header) => {