From 6b0ccac1bf14dad4980ac46bc534d5356319425f Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Fri, 14 Oct 2022 00:20:02 +0530 Subject: [PATCH] feat: support adding and removing collections from workspaces --- .../src/components/Modal/StyledWrapper.js | 4 + .../bruno-app/src/components/Modal/index.js | 11 ++- .../RemoveCollectionFromWorkspace/index.js | 37 +++++++++ .../Sidebar/Collections/Collection/index.js | 9 ++ .../CreateOrAddCollection/index.js | 63 ++++++++++++++ .../SelectCollection/StyledWrapper.js | 18 ++++ .../Collections/SelectCollection/index.js | 30 +++++++ .../components/Sidebar/Collections/index.js | 23 +++++- .../src/components/Sidebar/TitleBar/index.js | 39 +++++++-- .../bruno-app/src/components/Welcome/index.js | 50 +++++++---- .../Workspaces/WorkspaceConfigurer/index.js | 3 +- .../ReduxStore/slices/collections/actions.js | 37 ++++++++- .../ReduxStore/slices/collections/index.js | 5 +- .../ReduxStore/slices/workspaces/actions.js | 82 ++++++++++++++++++- .../ReduxStore/slices/workspaces/index.js | 29 ++++++- packages/bruno-app/src/utils/common/index.js | 6 ++ 16 files changed, 401 insertions(+), 45 deletions(-) create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/Collection/RemoveCollectionFromWorkspace/index.js create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/CreateOrAddCollection/index.js create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/index.js diff --git a/packages/bruno-app/src/components/Modal/StyledWrapper.js b/packages/bruno-app/src/components/Modal/StyledWrapper.js index d0f894d9..98b0f147 100644 --- a/packages/bruno-app/src/components/Modal/StyledWrapper.js +++ b/packages/bruno-app/src/components/Modal/StyledWrapper.js @@ -40,18 +40,22 @@ const Wrapper = styled.div` &.modal-sm { min-width: 300px; + max-width: 500px; } &.modal-md { min-width: 500px; + max-width: 800px; } &.modal-lg { min-width: 800px; + max-width: 1140px; } &.modal-xl { min-width: 1140px; + max-width: calc(100% - 30px); } animation: fade-and-slide-in-from-top .50s forwards cubic-bezier(.19,1,.22,1); diff --git a/packages/bruno-app/src/components/Modal/index.js b/packages/bruno-app/src/components/Modal/index.js index 486f98e9..0d5badc8 100644 --- a/packages/bruno-app/src/components/Modal/index.js +++ b/packages/bruno-app/src/components/Modal/index.js @@ -47,7 +47,8 @@ const Modal = ({ handleConfirm, children, confirmDisabled, - hideCancel + hideCancel, + hideFooter }) => { const [isClosing, setIsClosing] = useState(false); const escFunction = (event) => { @@ -60,7 +61,7 @@ const Modal = ({ const closeModal = () => { setIsClosing(true); setTimeout(() => handleCancel(), 500); - } + }; useEffect(() => { document.addEventListener('keydown', escFunction, false); @@ -79,15 +80,17 @@ const Modal = ({
closeModal()} /> {children} - closeModal()} handleSubmit={handleConfirm} confirmDisabled={confirmDisabled} hideCancel={hideCancel} - /> + /> : null}
+ + {/* Clicking on backdrop closes the modal */}
closeModal()} /> ); diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/RemoveCollectionFromWorkspace/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/RemoveCollectionFromWorkspace/index.js new file mode 100644 index 00000000..94064dfa --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/RemoveCollectionFromWorkspace/index.js @@ -0,0 +1,37 @@ +import React from 'react'; +import toast from 'react-hot-toast'; +import Modal from 'components/Modal'; +import { useSelector, useDispatch } from 'react-redux'; +import { recursivelyGetAllItemUids } from 'utils/collections'; +import { removeCollectionFromWorkspace } from 'providers/ReduxStore/slices/workspaces/actions'; +import { closeTabs } from 'providers/ReduxStore/slices/tabs'; + +const RemoveCollectionFromWorkspace = ({onClose, collection}) => { + const dispatch = useDispatch(); + const { activeWorkspaceUid } = useSelector((state) => state.workspaces); + + const onConfirm = () =>{ + dispatch(removeCollectionFromWorkspace(activeWorkspaceUid, collection.uid)) + .then(() => { + dispatch(closeTabs({ + tabUids: recursivelyGetAllItemUids(collection.items) + })); + toast.success("Collection removed from workspace"); + }) + .catch((err) => console.log(err) && toast.error("An error occured while removing the collection")); + }; + + return ( + + Are you sure you want to remove the collection {collection.name} from this workspace? + + ); +}; + +export default RemoveCollectionFromWorkspace; 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 22fe299e..bc3f48c3 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js @@ -8,6 +8,7 @@ import { useDispatch } from 'react-redux'; import NewRequest from 'components/Sidebar/NewRequest'; import NewFolder from 'components/Sidebar/NewFolder'; import CollectionItem from './CollectionItem'; +import RemoveCollectionFromWorkspace from './RemoveCollectionFromWorkspace'; import { doesCollectionHaveItemsMatchingSearchText } from 'utils/collections/search'; import { isItemAFolder, isItemARequest } from 'utils/collections'; @@ -19,6 +20,7 @@ const Collection = ({collection, searchText}) => { const [showNewFolderModal, setShowNewFolderModal] = useState(false); const [showNewRequestModal, setShowNewRequestModal] = useState(false); const [showRenameCollectionModal, setShowRenameCollectionModal] = useState(false); + const [showRemoveCollectionFromWSModal, setShowRemoveCollectionFromWSModal] = useState(false); const [showDeleteCollectionModal, setShowDeleteCollectionModal] = useState(false); const [collectionIsCollapsed, setCollectionIsCollapsed] = useState(collection.collapsed); const dispatch = useDispatch(); @@ -63,6 +65,7 @@ const Collection = ({collection, searchText}) => { {showNewRequestModal && setShowNewRequestModal(false)}/>} {showNewFolderModal && setShowNewFolderModal(false)}/>} {showRenameCollectionModal && setShowRenameCollectionModal(false)}/>} + {showRemoveCollectionFromWSModal && setShowRemoveCollectionFromWSModal(false)}/>} {showDeleteCollectionModal && setShowDeleteCollectionModal(false)}/>}
@@ -93,6 +96,12 @@ const Collection = ({collection, searchText}) => { }}> Rename
+
{ + menuDropdownTippyRef.current.hide(); + setShowRemoveCollectionFromWSModal(true); + }}> + Remove from Workspace +
{ menuDropdownTippyRef.current.hide(); setShowDeleteCollectionModal(true); diff --git a/packages/bruno-app/src/components/Sidebar/Collections/CreateOrAddCollection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/CreateOrAddCollection/index.js new file mode 100644 index 00000000..5e6c8505 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/CreateOrAddCollection/index.js @@ -0,0 +1,63 @@ +import React, { useState } from 'react'; +import toast from 'react-hot-toast'; +import { useSelector, useDispatch } from 'react-redux'; +import CreateCollection from 'components/Sidebar/CreateCollection'; +import SelectCollection from 'components/Sidebar/Collections/SelectCollection'; +import { createCollection } from 'providers/ReduxStore/slices/collections/actions'; +import { addCollectionToWorkspace } from 'providers/ReduxStore/slices/workspaces/actions'; + +const CreateOrAddCollection = () => { + const dispatch = useDispatch(); + const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false); + const [addCollectionToWSModalOpen, setAddCollectionToWSModalOpen] = useState(false); + const { activeWorkspaceUid } = useSelector((state) => state.workspaces); + + const handleCreateCollection = (values) => { + setCreateCollectionModalOpen(false); + dispatch(createCollection(values.collectionName)) + .then(() => { + toast.success("Collection created"); + }) + .catch(() => toast.error("An error occured while creating the collection")); + }; + + const handleAddCollectionToWorkspace = (collectionUid) => { + setAddCollectionToWSModalOpen(false); + dispatch(addCollectionToWorkspace(activeWorkspaceUid, collectionUid)) + .then(() => { + toast.success("Collection added to workspace"); + }) + .catch(() => toast.error("An error occured while adding collection to workspace")); + }; + + const CreateLink = () => setCreateCollectionModalOpen(true)}>Create; + const AddLink = () => setAddCollectionToWSModalOpen(true)}>Add; + + return ( +
+ {createCollectionModalOpen ? ( + setCreateCollectionModalOpen(false)} + handleConfirm={handleCreateCollection} + /> + ) : null} + + {addCollectionToWSModalOpen ? ( + setAddCollectionToWSModalOpen(false)} + onSelect={handleAddCollectionToWorkspace} + /> + ): null} + +
+
No collections found.
+
+ or Collection to Workspace. +
+
+
+ ); +}; + +export default CreateOrAddCollection; \ No newline at end of file diff --git a/packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/StyledWrapper.js new file mode 100644 index 00000000..b37adfb1 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/StyledWrapper.js @@ -0,0 +1,18 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + div.collection { + padding: 4px 6px; + padding-left: 8px; + display: flex; + align-items: center; + border-radius: 3px; + cursor: pointer; + + &:hover { + background-color: #f4f4f4; + } + } +`; + +export default StyledWrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/index.js new file mode 100644 index 00000000..a96c770c --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/SelectCollection/index.js @@ -0,0 +1,30 @@ +import React from "react"; +import Modal from "components/Modal/index"; +import { IconFiles } from '@tabler/icons'; +import { useSelector } from "react-redux"; +import StyledWrapper from './StyledWrapper'; + +const SelectCollection = ({onClose, onSelect, title}) => { + const { collections } = useSelector((state) => state.collections); + + return ( + + +
    + {collections && collections.length && collections.map((c) => ( +
    onSelect(c.uid)}> + {c.name} +
    + ))} +
+
+
+ ); +} + +export default SelectCollection; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/index.js b/packages/bruno-app/src/components/Sidebar/Collections/index.js index f56672a0..1ad6865c 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/index.js @@ -1,14 +1,29 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { useSelector } from 'react-redux'; +import find from 'lodash/find'; +import filter from 'lodash/filter'; import Collection from './Collection'; +import CreateOrAddCollection from './CreateOrAddCollection'; const Collections = ({searchText}) => { - const collections = useSelector((state) => state.collections.collections); - console.log(collections); + const { collections } = useSelector((state) => state.collections); + const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces); + const activeWorkspace = find(workspaces, w => w.uid === activeWorkspaceUid); + + if(!activeWorkspace) { + return null; + } + + const { collectionUids } = activeWorkspace; + const collectionToDisplay = filter(collections, (c) => collectionUids.includes(c.uid)); + + if(!collectionToDisplay || !collectionToDisplay.length) { + return ; + } return (
- {collections && collections.length ? collections.map((c) => { + {collectionToDisplay && collectionToDisplay.length ? collectionToDisplay.map((c) => { return { - const [modalOpen, setModalOpen] = useState(false); + const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false); + const [addCollectionToWSModalOpen, setAddCollectionToWSModalOpen] = useState(false); + const { activeWorkspaceUid } = useSelector((state) => state.workspaces); const dispatch = useDispatch(); const menuDropdownTippyRef = useRef(); @@ -23,11 +27,10 @@ const TitleBar = () => { ); }); - const handleCancel = () => setModalOpen(false); const handleTitleClick = () => dispatch(showHomePage()); - const handleConfirm = (values) => { - setModalOpen(false); + const handleCreateCollection = (values) => { + setCreateCollectionModalOpen(false); dispatch(createCollection(values.collectionName)) .then(() => { toast.success("Collection created"); @@ -35,14 +38,31 @@ const TitleBar = () => { .catch(() => toast.error("An error occured while creating the collection")); }; + const handleAddCollectionToWorkspace = (collectionUid) => { + setAddCollectionToWSModalOpen(false); + dispatch(addCollectionToWorkspace(activeWorkspaceUid, collectionUid)) + .then(() => { + toast.success("Collection added to workspace"); + }) + .catch(() => toast.error("An error occured while adding collection to workspace")); + }; + return ( - {modalOpen ? ( + {createCollectionModalOpen ? ( setCreateCollectionModalOpen(false)} + handleConfirm={handleCreateCollection} /> ) : null} + + {addCollectionToWSModalOpen ? ( + setAddCollectionToWSModalOpen(false)} + onSelect={handleAddCollectionToWorkspace} + /> + ): null}
@@ -59,7 +79,7 @@ const TitleBar = () => { } placement='bottom-start'>
{ menuDropdownTippyRef.current.hide(); - setModalOpen(true); + setCreateCollectionModalOpen(true); }}> Create Collection
@@ -70,6 +90,7 @@ const TitleBar = () => {
{ menuDropdownTippyRef.current.hide(); + setAddCollectionToWSModalOpen(true); }}> Add Collection to Workspace
diff --git a/packages/bruno-app/src/components/Welcome/index.js b/packages/bruno-app/src/components/Welcome/index.js index 1c4094bb..1d1526d1 100644 --- a/packages/bruno-app/src/components/Welcome/index.js +++ b/packages/bruno-app/src/components/Welcome/index.js @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import toast from 'react-hot-toast'; import { IconPlus, IconUpload, @@ -9,19 +10,22 @@ import { IconSpeakerphone, IconDeviceDesktop } from '@tabler/icons'; -import { useDispatch } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; import { createCollection } from 'providers/ReduxStore/slices/collections/actions'; +import { addCollectionToWorkspace } from 'providers/ReduxStore/slices/workspaces/actions'; import Bruno from 'components/Bruno'; import CreateCollection from 'components/Sidebar/CreateCollection'; +import SelectCollection from 'components/Sidebar/Collections/SelectCollection'; import StyledWrapper from './StyledWrapper'; const Welcome = () => { - const [modalOpen, setModalOpen] = useState(false); const dispatch = useDispatch(); + const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false); + const [addCollectionToWSModalOpen, setAddCollectionToWSModalOpen] = useState(false); + const { activeWorkspaceUid } = useSelector((state) => state.workspaces); - const handleCancel = () => setModalOpen(false); - const handleConfirm = (values) => { - setModalOpen(false); + const handleCreateCollection = (values) => { + setCreateCollectionModalOpen(false); dispatch(createCollection(values.collectionName)) .then(() => { toast.success("Collection created"); @@ -29,14 +33,32 @@ const Welcome = () => { .catch(() => toast.error("An error occured while creating the collection")); }; + const handleAddCollectionToWorkspace = (collectionUid) => { + setAddCollectionToWSModalOpen(false); + dispatch(addCollectionToWorkspace(activeWorkspaceUid, collectionUid)) + .then(() => { + toast.success("Collection added to workspace"); + }) + .catch(() => toast.error("An error occured while adding collection to workspace")); + }; + return ( - {modalOpen ? ( + {createCollectionModalOpen ? ( setCreateCollectionModalOpen(false)} + handleConfirm={handleCreateCollection} /> ) : null} + + {addCollectionToWSModalOpen ? ( + setAddCollectionToWSModalOpen(false)} + onSelect={handleAddCollectionToWorkspace} + /> + ): null} +
@@ -44,12 +66,12 @@ const Welcome = () => {
Opensource API Client.
Collections
-
+
- setModalOpen(true)}>Create Collection + setCreateCollectionModalOpen(true)}>Create Collection
- Add Collection to Workspace + setAddCollectionToWSModalOpen(true)}>Add Collection to Workspace
Import Collection @@ -60,9 +82,9 @@ const Welcome = () => {
Local Collections
-
+
- setModalOpen(true)}>Create Collection + setCreateCollectionModalOpen(true)}>Create Collection
Open Collection @@ -70,7 +92,7 @@ const Welcome = () => {
Links
-
+
Chrome Extension
diff --git a/packages/bruno-app/src/components/Workspaces/WorkspaceConfigurer/index.js b/packages/bruno-app/src/components/Workspaces/WorkspaceConfigurer/index.js index 7c23c919..185aba2f 100644 --- a/packages/bruno-app/src/components/Workspaces/WorkspaceConfigurer/index.js +++ b/packages/bruno-app/src/components/Workspaces/WorkspaceConfigurer/index.js @@ -24,8 +24,7 @@ const WorkspaceConfigurer = ({onClose}) => { {openAddModal && setOpenAddModal(false)}/>} - ) - + ); } export default WorkspaceConfigurer; 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 debe0c86..ee04785a 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -5,6 +5,7 @@ import { recursivelyGetAllItemUids, transformCollectionToSaveToIdb } from 'utils/collections'; +import { waitForNextTick } from 'utils/common'; import { saveCollectionToIdb, deleteCollectionInIdb } from 'utils/idb'; import { @@ -13,9 +14,10 @@ import { deleteCollection as _deleteCollection, } from './index'; -import { closeTabs } from 'providers/ReduxStore/slices/tabs'; +import { closeTabs, addTab } from 'providers/ReduxStore/slices/tabs'; +import { addCollectionToWorkspace } from 'providers/ReduxStore/slices/workspaces/actions'; -export const createCollection = (collectionName) => (dispatch) => { +export const createCollection = (collectionName) => (dispatch, getState) => { const newCollection = { uid: uuid(), name: collectionName, @@ -23,9 +25,40 @@ export const createCollection = (collectionName) => (dispatch) => { environments: [], }; + const requestItem = { + uid: uuid(), + type: 'http-request', + name: 'Untitled', + request: { + method: 'GET', + url: '', + headers: [], + body: { + mode: 'none', + json: null, + text: null, + xml: null, + multipartForm: null, + formUrlEncoded: null + } + } + }; + + newCollection.items.push(requestItem) + + const state = getState(); + const { activeWorkspaceUid } = state.workspaces; + return new Promise((resolve, reject) => { saveCollectionToIdb(window.__idb, newCollection) .then(() => dispatch(_createCollection(newCollection))) + .then(waitForNextTick) + .then(() => dispatch(addCollectionToWorkspace(activeWorkspaceUid, newCollection.uid))) + .then(waitForNextTick) + .then(() => dispatch(addTab({ + uid: requestItem.uid, + collectionUid: newCollection.uid + }))) .then(resolve) .catch(reject); }); 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 6f4985fa..6e65dbcb 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -693,10 +693,7 @@ export const newHttpRequest = (params) => (dispatch, getState) => { .then((val) => resolve(val)) .catch((err) => reject(err)); }) - .catch((err) => { - reject(err); - console.log(err) - }); + .catch(reject); } }); }; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js index e4f04942..97cb54d6 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js @@ -1,11 +1,15 @@ import find from 'lodash/find'; +import filter from 'lodash/filter'; import { uuid } from 'utils/common'; +import cloneDeep from 'lodash/cloneDeep'; import { getWorkspacesFromIdb, saveWorkspaceToIdb, deleteWorkspaceInIdb } from 'utils/idb/workspaces'; import { loadWorkspaces, addWorkspace as _addWorkspace, renameWorkspace as _renameWorkspace, - deleteWorkspace as _deleteWorkspace + deleteWorkspace as _deleteWorkspace, + addCollectionToWorkspace as _addCollectionToWorkspace, + removeCollectionFromWorkspace as _removeCollectionFromWorkspace } from './index'; const seedWorkpace = () => { @@ -75,7 +79,7 @@ export const renameWorkspace = (newName, uid) => (dispatch, getState) => { name: newName }))) .then(resolve) - .catch((err) => console.log(err)); + .catch(reject); }); }; @@ -98,6 +102,76 @@ export const deleteWorkspace = (workspaceUid) => (dispatch, getState) => { workspaceUid: workspaceUid }))) .then(resolve) - .catch((err) => console.log(err)); + .catch(reject); }); -}; \ No newline at end of file +}; + +export const addCollectionToWorkspace = (workspaceUid, collectionUid) => (dispatch, getState) => { + const state = getState(); + + return new Promise((resolve, reject) => { + const workspace = find(state.workspaces.workspaces, (w) => w.uid === workspaceUid); + const collection = find(state.collections.collections, (c) => c.uid === collectionUid); + + if(!workspace) { + return reject(new Error('Workspace not found')); + } + + if(!collection) { + return reject(new Error('Collection not found')); + } + + const workspaceCopy = cloneDeep(workspace); + if(workspaceCopy.collectionUids && workspace.collectionUids.length) { + if(!workspaceCopy.collectionUids.includes(collectionUid)) { + workspaceCopy.collectionUids.push(collectionUid); + } + } else { + workspaceCopy.collectionUids = [collectionUid]; + } + + saveWorkspaceToIdb(window.__idb, workspaceCopy) + .then(() => dispatch(_addCollectionToWorkspace({ + workspaceUid: workspaceUid, + collectionUid: collectionUid + }))) + .then(resolve) + .catch(reject); + }); +}; + +export const removeCollectionFromWorkspace = (workspaceUid, collectionUid) => (dispatch, getState) => { + const state = getState(); + + return new Promise((resolve, reject) => { + const workspace = find(state.workspaces.workspaces, (w) => w.uid === workspaceUid); + const collection = find(state.collections.collections, (c) => c.uid === collectionUid); + + if(!workspace) { + return reject(new Error('Workspace not found')); + } + + if(!collection) { + return reject(new Error('Collection not found')); + } + + const workspaceCopy = cloneDeep(workspace); + if(workspaceCopy.collectionUids && workspace.collectionUids.length) { + workspaceCopy.collectionUids = filter(workspaceCopy.collectionUids, (uid) => uid !== collectionUid); + } + + saveWorkspaceToIdb(window.__idb, workspaceCopy) + .then(() => dispatch(_removeCollectionFromWorkspace({ + workspaceUid: workspaceUid, + collectionUid: collectionUid + }))) + .then(resolve) + .catch(reject); + }); +}; + +// TODO +// Workspaces can have collection uids that no longer exist +// or the user may have the collections access revoked (in teams) +// This action will have to be called at the beginning to purge any zombi collectionUids in the workspaces +export const removeZombieCollectionFromAllWorkspaces = (collectionUid) => (dispatch, getState) => {}; \ No newline at end of file diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js index 231d3edc..ad6befcc 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js @@ -1,9 +1,10 @@ import { createSlice } from '@reduxjs/toolkit'; -import each from 'lodash/each'; import find from 'lodash/find'; +import filter from 'lodash/filter'; const initialState = { workspaces: [], + collectionUids: [], activeWorkspaceUid: null }; @@ -37,6 +38,28 @@ export const workspacesSlice = createSlice({ }, addWorkspace: (state, action) => { state.workspaces.push(action.payload.workspace); + }, + addCollectionToWorkspace: (state, action) => { + const { workspaceUid, collectionUid } = action.payload; + const workspace = find(state.workspaces, (w) => w.uid === workspaceUid); + + if(workspace) { + if(workspace.collectionUids && workspace.collectionUids.length) { + if(!workspace.collectionUids.includes(collectionUid)) { + workspace.collectionUids.push(collectionUid); + } + } else { + workspace.collectionUids = [collectionUid]; + } + } + }, + removeCollectionFromWorkspace: (state, action) => { + const { workspaceUid, collectionUid } = action.payload; + const workspace = find(state.workspaces, (w) => w.uid === workspaceUid); + + if(workspace && workspace.collectionUids && workspace.collectionUids.length) { + workspace.collectionUids = filter(workspace.collectionUids, (uid) => uid !== collectionUid); + } } } }); @@ -46,7 +69,9 @@ export const { selectWorkspace, renameWorkspace, deleteWorkspace, - addWorkspace + addWorkspace, + addCollectionToWorkspace, + removeCollectionFromWorkspace } = workspacesSlice.actions; export default workspacesSlice.reducer; diff --git a/packages/bruno-app/src/utils/common/index.js b/packages/bruno-app/src/utils/common/index.js index 5ad2b7c5..d54475f0 100644 --- a/packages/bruno-app/src/utils/common/index.js +++ b/packages/bruno-app/src/utils/common/index.js @@ -18,3 +18,9 @@ export const simpleHash = str => { } return new Uint32Array([hash])[0].toString(36); }; + +export const waitForNextTick = () => { + return new Promise((resolve, reject) => { + setTimeout(() => resolve(), 0); + }); +};