mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-24 17:03:47 +01:00
feat: wireup local collections open and create buttons
This commit is contained in:
parent
9fae7f72d4
commit
8a96a0ce71
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import find from 'lodash/find';
|
||||
import filter from 'lodash/filter';
|
||||
|
@ -1,10 +1,18 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { browserLocalDirectory } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { isElectron } from 'utils/common/platform';
|
||||
import { createCollection, createLocalCollection } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import toast from 'react-hot-toast';
|
||||
import Modal from 'components/Modal';
|
||||
|
||||
const CreateCollection = ({handleConfirm, handleCancel}) => {
|
||||
const CreateCollection = ({onClose, isLocal}) => {
|
||||
const inputRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
const isPlatformElectron = isElectron();
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
@ -18,9 +26,26 @@ const CreateCollection = ({handleConfirm, handleCancel}) => {
|
||||
.required('name is required')
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
handleConfirm(values);
|
||||
const action = isLocal && isPlatformElectron ? createLocalCollection : createCollection;
|
||||
dispatch(action(values.collectionName, values.collectionLocation))
|
||||
.then(() => {
|
||||
toast.success("Collection created");
|
||||
onClose();
|
||||
})
|
||||
.catch(() => toast.error("An error occured while creating the collection"));
|
||||
}
|
||||
});
|
||||
|
||||
const browse = () => {
|
||||
dispatch(browserLocalDirectory())
|
||||
.then((dirPath) => {
|
||||
formik.setFieldValue('collectionLocation', dirPath);
|
||||
})
|
||||
.catch((error) => {
|
||||
formik.setFieldValue('collectionLocation', '');
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(inputRef && inputRef.current) {
|
||||
@ -36,7 +61,7 @@ const CreateCollection = ({handleConfirm, handleCancel}) => {
|
||||
title='Create Collection'
|
||||
confirmText='Create'
|
||||
handleConfirm={onSubmit}
|
||||
handleCancel={handleCancel}
|
||||
handleCancel={onClose}
|
||||
>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div>
|
||||
@ -54,6 +79,31 @@ const CreateCollection = ({handleConfirm, handleCancel}) => {
|
||||
{formik.touched.collectionName && formik.errors.collectionName ? (
|
||||
<div className="text-red-500">{formik.errors.collectionName}</div>
|
||||
) : null}
|
||||
|
||||
{isLocal && isPlatformElectron ? (
|
||||
<>
|
||||
<label htmlFor="collectionLocation" className="block font-semibold mt-3">Location</label>
|
||||
<input
|
||||
id="collection-location"
|
||||
type="text"
|
||||
name="collectionLocation"
|
||||
readOnly={true}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
|
||||
value={formik.values.collectionLocation || ''}
|
||||
onClick={browse}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{isLocal && isPlatformElectron && formik.touched.collectionLocation && formik.errors.collectionLocation ? (
|
||||
<div className="text-red-500">{formik.errors.collectionLocation}</div>
|
||||
) : null}
|
||||
|
||||
{isLocal && isPlatformElectron ? (
|
||||
<div className="mt-1">
|
||||
<span className="text-link cursor-pointer hover:underline" onClick={browse}>Browse</span>
|
||||
</div>
|
||||
) : null }
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
@ -1,15 +1,19 @@
|
||||
import React, { useRef, forwardRef } from 'react';
|
||||
import React, { useState, useRef, forwardRef } from 'react';
|
||||
import filter from 'lodash/filter';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import { openLocalCollection } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { IconArrowForwardUp, IconCaretDown, IconFolders, IconPlus } from '@tabler/icons';
|
||||
import Collection from '../Collections/Collection';
|
||||
import CreateCollection from '../CreateCollection';
|
||||
import { isLocalCollection } from 'utils/collections';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const LocalCollections = ({searchText}) => {
|
||||
const dropdownTippyRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
const { collections } = useSelector((state) => state.collections);
|
||||
const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false);
|
||||
|
||||
const collectionToDisplay = filter(collections, (c) => isLocalCollection(c));
|
||||
|
||||
@ -35,17 +39,30 @@ const LocalCollections = ({searchText}) => {
|
||||
|
||||
const onDropdownCreate = (ref) => dropdownTippyRef.current = ref;
|
||||
|
||||
|
||||
const handleOpenLocalCollection = () => {
|
||||
dispatch(openLocalCollection())
|
||||
.catch((err) => console.log(err) && toast.error("An error occured while opening the local collection"));
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
{createCollectionModalOpen ? (
|
||||
<CreateCollection
|
||||
isLocal={true}
|
||||
onClose={() => setCreateCollectionModalOpen(false)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="items-center cursor-pointer mt-6 relative">
|
||||
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement='bottom-end'>
|
||||
<div className="dropdown-item" onClick={() => {}}>
|
||||
<div className="dropdown-item" onClick={() => setCreateCollectionModalOpen(true)}>
|
||||
<div className="pr-2 text-gray-600">
|
||||
<IconPlus size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
<span>Create Collection</span>
|
||||
</div>
|
||||
<div className="dropdown-item" onClick={() => {}}>
|
||||
<div className="dropdown-item" onClick={handleOpenLocalCollection}>
|
||||
<div className="pr-2 text-gray-600">
|
||||
<IconArrowForwardUp size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
|
@ -1,6 +1,15 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.local-collection-label {
|
||||
background-color: var(--color-sidebar-background);
|
||||
}
|
||||
|
||||
.local-collections-unavailable {
|
||||
padding: 0.35rem 0.6rem;
|
||||
border-top: solid 1px #ddd;
|
||||
font-size: 11px;
|
||||
}
|
||||
.collection-dropdown {
|
||||
color: rgb(110 110 110);
|
||||
|
||||
|
@ -2,21 +2,24 @@ import React, { useState, forwardRef, useRef } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import Bruno from 'components/Bruno';
|
||||
import { IconFolders } from '@tabler/icons';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { collectionImported } from 'providers/ReduxStore/slices/collections';
|
||||
import { createCollection } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { openLocalCollection } 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 importCollection from 'utils/collections/import';
|
||||
import { isElectron } from 'utils/common/platform';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const TitleBar = () => {
|
||||
const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false);
|
||||
const [addCollectionToWSModalOpen, setAddCollectionToWSModalOpen] = useState(false);
|
||||
const { activeWorkspaceUid } = useSelector((state) => state.workspaces);
|
||||
const isPlatformElectron = isElectron();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const menuDropdownTippyRef = useRef();
|
||||
@ -31,14 +34,10 @@ const TitleBar = () => {
|
||||
|
||||
const handleTitleClick = () => dispatch(showHomePage());
|
||||
|
||||
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 handleOpenLocalCollection = () => {
|
||||
dispatch(openLocalCollection())
|
||||
.catch((err) => console.log(err) && toast.error("An error occured while opening the local collection"));
|
||||
}
|
||||
|
||||
const handleAddCollectionToWorkspace = (collectionUid) => {
|
||||
setAddCollectionToWSModalOpen(false);
|
||||
@ -62,8 +61,8 @@ const TitleBar = () => {
|
||||
<StyledWrapper className="px-2 py-2">
|
||||
{createCollectionModalOpen ? (
|
||||
<CreateCollection
|
||||
handleCancel={() => setCreateCollectionModalOpen(false)}
|
||||
handleConfirm={handleCreateCollection}
|
||||
isLocal={createCollectionModalOpen === 'local' ? true : false}
|
||||
onClose={() => setCreateCollectionModalOpen(false)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -106,6 +105,37 @@ const TitleBar = () => {
|
||||
}}>
|
||||
Add Collection to Workspace
|
||||
</div>
|
||||
{isPlatformElectron ? (
|
||||
<>
|
||||
<div className="font-medium label-item font-medium local-collection-label">
|
||||
<div className='flex items-center'>
|
||||
<span className='mr-2'>
|
||||
<IconFolders size={18} strokeWidth={1.5}/>
|
||||
</span>
|
||||
<span>
|
||||
Local Collections
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
setCreateCollectionModalOpen('local');
|
||||
menuDropdownTippyRef.current.hide();
|
||||
}}>
|
||||
Create Local Collection
|
||||
</div>
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
handleOpenLocalCollection();
|
||||
menuDropdownTippyRef.current.hide();
|
||||
}}>
|
||||
Open Local Collection
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center select-none text-gray-400 text-xs local-collections-unavailable">
|
||||
Note: Local collections are only available on the desktop app.
|
||||
</div>
|
||||
)}
|
||||
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
} from '@tabler/icons';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { collectionImported } from 'providers/ReduxStore/slices/collections';
|
||||
import { createCollection } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { openLocalCollection } 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';
|
||||
@ -29,15 +29,6 @@ const Welcome = () => {
|
||||
const { activeWorkspaceUid } = useSelector((state) => state.workspaces);
|
||||
const isPlatformElectron = isElectron();
|
||||
|
||||
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))
|
||||
@ -69,12 +60,17 @@ const Welcome = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenLocalCollection = () => {
|
||||
dispatch(openLocalCollection())
|
||||
.catch((err) => console.log(err) && toast.error("An error occured while opening the local collection"));
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledWrapper className="pb-4 px-6 mt-6">
|
||||
{createCollectionModalOpen ? (
|
||||
<CreateCollection
|
||||
handleCancel={() => setCreateCollectionModalOpen(false)}
|
||||
handleConfirm={handleCreateCollection}
|
||||
isLocal={createCollectionModalOpen === 'local' ? true : false}
|
||||
onClose={() => setCreateCollectionModalOpen(false)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -112,10 +108,10 @@ const Welcome = () => {
|
||||
{isPlatformElectron ? (
|
||||
<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={() => setCreateCollectionModalOpen(true)}>Create Collection</span>
|
||||
<IconPlus size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setCreateCollectionModalOpen('local')}>Create Collection</span>
|
||||
</div>
|
||||
<div className="flex items-center ml-6">
|
||||
<IconFolders size={18} strokeWidth={2}/><span className="label ml-2">Open Collection</span>
|
||||
<IconFolders size={18} strokeWidth={2}/><span className="label ml-2" onClick={handleOpenLocalCollection}>Open Collection</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
@ -801,4 +801,37 @@ export const removeLocalCollection = (collectionUid) => (dispatch, getState) =>
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
export const browserLocalDirectory = () => (dispatch, getState) => {
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcRenderer
|
||||
.invoke('renderer:browse-directory')
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
export const createLocalCollection = (collectionName, collectionLocation) => () => {
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcRenderer
|
||||
.invoke('renderer:create-collection', collectionName, collectionLocation)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
export const openLocalCollection = () => () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
ipcRenderer
|
||||
.invoke('renderer:open-collection')
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
@ -30,22 +30,24 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
||||
try {
|
||||
const dirPath = path.join(collectionLocation, collectionName);
|
||||
if (fs.existsSync(dirPath)){
|
||||
throw new Error(`collection: ${dir} already exists`);
|
||||
throw new Error(`collection: ${dirPath} already exists`);
|
||||
}
|
||||
|
||||
if(!isValidPathname(dirPath)) {
|
||||
throw new Error(`collection: invaid pathname - ${dir}`);
|
||||
throw new Error(`collection: invalid pathname - ${dir}`);
|
||||
}
|
||||
|
||||
await createDirectory(dirPath);
|
||||
|
||||
const uid = uuid();
|
||||
const content = await stringifyJson({
|
||||
version: '1.0',
|
||||
uid: uid,
|
||||
name: collectionName,
|
||||
type: 'collection'
|
||||
});
|
||||
await writeFile(path.join(dirPath, 'bruno.json'), content);
|
||||
|
||||
const uid = uuid();
|
||||
mainWindow.webContents.send('main:collection-opened', dirPath, uid);
|
||||
ipcMain.emit('main:collection-opened', mainWindow, dirPath, uid);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user