forked from extern/bruno
feat: support adding and removing collections from workspaces
This commit is contained in:
parent
f8fbc88239
commit
6b0ccac1bf
@ -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);
|
||||
|
@ -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 = ({
|
||||
<div className={`bruno-modal-card modal-${size}`}>
|
||||
<ModalHeader title={title} handleCancel={() => closeModal()} />
|
||||
<ModalContent>{children}</ModalContent>
|
||||
<ModalFooter
|
||||
{!hideFooter ? <ModalFooter
|
||||
confirmText={confirmText}
|
||||
cancelText={cancelText}
|
||||
handleCancel={() => closeModal()}
|
||||
handleSubmit={handleConfirm}
|
||||
confirmDisabled={confirmDisabled}
|
||||
hideCancel={hideCancel}
|
||||
/>
|
||||
/> : null}
|
||||
</div>
|
||||
|
||||
{/* Clicking on backdrop closes the modal */}
|
||||
<div className="bruno-modal-backdrop" onClick={() => closeModal()} />
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
@ -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 (
|
||||
<Modal
|
||||
size="sm"
|
||||
title="Remove Collection from Workspace"
|
||||
confirmText="Remove"
|
||||
handleConfirm={onConfirm}
|
||||
handleCancel={onClose}
|
||||
>
|
||||
Are you sure you want to remove the collection <span className="font-semibold">{collection.name}</span> from this workspace?
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RemoveCollectionFromWorkspace;
|
@ -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 && <NewRequest collection={collection} onClose={() => setShowNewRequestModal(false)}/>}
|
||||
{showNewFolderModal && <NewFolder collection={collection} onClose={() => setShowNewFolderModal(false)}/>}
|
||||
{showRenameCollectionModal && <RenameCollection collection={collection} onClose={() => setShowRenameCollectionModal(false)}/>}
|
||||
{showRemoveCollectionFromWSModal && <RemoveCollectionFromWorkspace collection={collection} onClose={() => setShowRemoveCollectionFromWSModal(false)}/>}
|
||||
{showDeleteCollectionModal && <DeleteCollection collection={collection} onClose={() => setShowDeleteCollectionModal(false)}/>}
|
||||
<div className="flex py-1 collection-name items-center">
|
||||
<div className="flex flex-grow items-center" onClick={handleClick}>
|
||||
@ -93,6 +96,12 @@ const Collection = ({collection, searchText}) => {
|
||||
}}>
|
||||
Rename
|
||||
</div>
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowRemoveCollectionFromWSModal(true);
|
||||
}}>
|
||||
Remove from Workspace
|
||||
</div>
|
||||
<div className="dropdown-item delete-collection" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowDeleteCollectionModal(true);
|
||||
|
@ -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 = () => <span className='underline text-link cursor-pointer' onClick={() => setCreateCollectionModalOpen(true)}>Create</span>;
|
||||
const AddLink = () => <span className='underline text-link cursor-pointer' onClick={() => setAddCollectionToWSModalOpen(true)}>Add</span>;
|
||||
|
||||
return (
|
||||
<div className='px-2 mt-4 text-gray-600'>
|
||||
{createCollectionModalOpen ? (
|
||||
<CreateCollection
|
||||
handleCancel={() => setCreateCollectionModalOpen(false)}
|
||||
handleConfirm={handleCreateCollection}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{addCollectionToWSModalOpen ? (
|
||||
<SelectCollection
|
||||
title='Add Collection to Workspace'
|
||||
onClose={() => setAddCollectionToWSModalOpen(false)}
|
||||
onSelect={handleAddCollectionToWorkspace}
|
||||
/>
|
||||
): null}
|
||||
|
||||
<div className='text-xs text-center'>
|
||||
<div>No collections found.</div>
|
||||
<div className='mt-2'>
|
||||
<CreateLink /> or <AddLink /> Collection to Workspace.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateOrAddCollection;
|
@ -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;
|
@ -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 (
|
||||
<StyledWrapper>
|
||||
<Modal
|
||||
size="sm"
|
||||
title={title || "Select Collection"}
|
||||
hideFooter={true}
|
||||
handleCancel={onClose}
|
||||
>
|
||||
<ul className="mb-2" >
|
||||
{collections && collections.length && collections.map((c) => (
|
||||
<div className="collection" key={c.uid} onClick={() => onSelect(c.uid)}>
|
||||
<IconFiles size={18} strokeWidth={1.5}/> <span className="ml-2">{c.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default SelectCollection;
|
@ -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 <CreateOrAddCollection />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-4 flex flex-col">
|
||||
{collections && collections.length ? collections.map((c) => {
|
||||
{collectionToDisplay && collectionToDisplay.length ? collectionToDisplay.map((c) => {
|
||||
return <Collection
|
||||
searchText={searchText}
|
||||
collection={c}
|
||||
|
@ -2,15 +2,19 @@ import React, { useState, forwardRef, useRef } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import Bruno from 'components/Bruno';
|
||||
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 { showHomePage } from 'providers/ReduxStore/slices/app';
|
||||
import { IconDots } from '@tabler/icons';
|
||||
import CreateCollection from '../CreateCollection';
|
||||
import SelectCollection from 'components/Sidebar/Collections/SelectCollection';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const TitleBar = () => {
|
||||
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,12 +38,29 @@ 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 (
|
||||
<StyledWrapper className="px-2 py-2">
|
||||
{modalOpen ? (
|
||||
{createCollectionModalOpen ? (
|
||||
<CreateCollection
|
||||
handleCancel={handleCancel}
|
||||
handleConfirm={handleConfirm}
|
||||
handleCancel={() => setCreateCollectionModalOpen(false)}
|
||||
handleConfirm={handleCreateCollection}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{addCollectionToWSModalOpen ? (
|
||||
<SelectCollection
|
||||
title='Add Collection to Workspace'
|
||||
onClose={() => setAddCollectionToWSModalOpen(false)}
|
||||
onSelect={handleAddCollectionToWorkspace}
|
||||
/>
|
||||
): null}
|
||||
|
||||
@ -59,7 +79,7 @@ const TitleBar = () => {
|
||||
<Dropdown onCreate={onMenuDropdownCreate} icon={<MenuIcon />} placement='bottom-start'>
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setModalOpen(true);
|
||||
setCreateCollectionModalOpen(true);
|
||||
}}>
|
||||
Create Collection
|
||||
</div>
|
||||
@ -70,6 +90,7 @@ const TitleBar = () => {
|
||||
</div>
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setAddCollectionToWSModalOpen(true);
|
||||
}}>
|
||||
Add Collection to Workspace
|
||||
</div>
|
||||
|
@ -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 (
|
||||
<StyledWrapper className="pb-4 px-6 mt-6">
|
||||
{modalOpen ? (
|
||||
{createCollectionModalOpen ? (
|
||||
<CreateCollection
|
||||
handleCancel={handleCancel}
|
||||
handleConfirm={handleConfirm}
|
||||
handleCancel={() => setCreateCollectionModalOpen(false)}
|
||||
handleConfirm={handleCreateCollection}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{addCollectionToWSModalOpen ? (
|
||||
<SelectCollection
|
||||
title='Add Collection to Workspace'
|
||||
onClose={() => setAddCollectionToWSModalOpen(false)}
|
||||
onSelect={handleAddCollectionToWorkspace}
|
||||
/>
|
||||
): null}
|
||||
|
||||
<div className="">
|
||||
<Bruno width={50} />
|
||||
</div>
|
||||
@ -44,12 +66,12 @@ const Welcome = () => {
|
||||
<div className="mt-4">Opensource API Client.</div>
|
||||
|
||||
<div className="uppercase font-semibold create-request mt-10">Collections</div>
|
||||
<div className="mt-4 flex items-center collection-options">
|
||||
<div className="mt-4 flex items-center collection-options select-none">
|
||||
<div className="flex items-center">
|
||||
<IconPlus size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setModalOpen(true)}>Create Collection</span>
|
||||
<IconPlus size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setCreateCollectionModalOpen(true)}>Create Collection</span>
|
||||
</div>
|
||||
<div className="flex items-center ml-6">
|
||||
<IconFiles size={18} strokeWidth={2}/><span className="label ml-2">Add Collection to Workspace</span>
|
||||
<IconFiles size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setAddCollectionToWSModalOpen(true)}>Add Collection to Workspace</span>
|
||||
</div>
|
||||
<div className="flex items-center ml-6">
|
||||
<IconUpload size={18} strokeWidth={2}/><span className="label ml-2">Import Collection</span>
|
||||
@ -60,9 +82,9 @@ const Welcome = () => {
|
||||
</div>
|
||||
|
||||
<div className="uppercase font-semibold create-request mt-10 pt-6">Local Collections</div>
|
||||
<div className="mt-4 flex items-center collection-options">
|
||||
<div className="mt-4 flex items-center collection-options select-none">
|
||||
<div className="flex items-center">
|
||||
<IconPlus size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setModalOpen(true)}>Create Collection</span>
|
||||
<IconPlus size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setCreateCollectionModalOpen(true)}>Create Collection</span>
|
||||
</div>
|
||||
<div className="flex items-center ml-6">
|
||||
<IconFolders size={18} strokeWidth={2}/><span className="label ml-2">Open Collection</span>
|
||||
@ -70,7 +92,7 @@ const Welcome = () => {
|
||||
</div>
|
||||
|
||||
<div className="uppercase font-semibold create-request mt-10 pt-6">Links</div>
|
||||
<div className="mt-4 flex flex-col collection-options">
|
||||
<div className="mt-4 flex flex-col collection-options select-none">
|
||||
<div className="flex items-center">
|
||||
<IconBrandChrome size={18} strokeWidth={2}/><span className="label ml-2">Chrome Extension</span>
|
||||
</div>
|
||||
|
@ -24,8 +24,7 @@ const WorkspaceConfigurer = ({onClose}) => {
|
||||
</ul>
|
||||
{openAddModal && <AddWorkspace onClose={() => setOpenAddModal(false)}/>}
|
||||
</Modal>
|
||||
)
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default WorkspaceConfigurer;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
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) => {};
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user