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 97989baf3..5f7de14b2 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 acc9883f7..4ecee5393 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 a9ae22597..5d09b7611 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 8c8263809..70d6bc64d 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) => {