Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions client/modules/IDE/actions/collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ export function createCollection(collection) {
};
}

export function createCollectionAndRefresh(collection, username) {
return (dispatch) => {
dispatch(startLoader());
const url = '/collections';
return apiClient
.post(url, collection)
.then((response) => {
dispatch({ type: ActionTypes.CREATE_COLLECTION });
dispatch(stopLoader());

const newCollection = response.data;
dispatch(setToastText(`Created "${newCollection.name}"`));
dispatch(showToast(TOAST_DISPLAY_TIME_MS));

return dispatch(getCollections(username));
})
.catch((error) => {
dispatch({
type: ActionTypes.ERROR,
error: error?.response?.data
});
dispatch(stopLoader());
});
};
}

export function addToCollection(collectionId, projectId) {
return (dispatch) => {
dispatch(startLoader());
Expand Down
73 changes: 70 additions & 3 deletions client/modules/IDE/components/AddToCollectionList.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { Loader } from '../../App/components/Loader';
import { Button } from '../../../common/Button';
import { generateCollectionName } from '../../../utils/generateRandomName';
import {
addToCollection,
createCollectionAndRefresh,
getCollections,
removeFromCollection
} from '../actions/collections';
import getSortedCollections from '../selectors/collections';
import QuickAddList from './QuickAddList';
import { remSize } from '../../../theme';
import { remSize, prop } from '../../../theme';

export const CollectionAddSketchWrapper = styled.div`
width: ${remSize(600)};
Expand All @@ -27,6 +30,29 @@ export const QuickAddWrapper = styled.div`
height: 100%;
`;

const EmptyText = styled.p`
text-align: center;
color: ${prop('primaryTextColor')};
margin-bottom: ${remSize(16)};
`;

const CreateFormWrapper = styled.form`
display: flex;
align-items: center;
gap: ${remSize(8)};
margin-top: ${remSize(12)};
`;

const CreateInput = styled.input`
flex: 1;
padding: ${remSize(8)};
border: 1px solid ${prop('Button.primary.default.border')};
border-radius: 2px;
background-color: ${prop('inputSecondaryBackground')};
color: ${prop('primaryTextColor')};
font-size: ${remSize(14)};
`;

const AddToCollectionList = ({ projectId }) => {
const { t } = useTranslation();

Expand All @@ -41,6 +67,10 @@ const AddToCollectionList = ({ projectId }) => {
const [hasLoadedData, setHasLoadedData] = useState(false);
const showLoader = loading && !hasLoadedData;

const [showCreateForm, setShowCreateForm] = useState(false);
const generatedName = useMemo(() => generateCollectionName(), []);
const [newCollectionName, setNewCollectionName] = useState(generatedName);

useEffect(() => {
dispatch(getCollections(username)).then(() => setHasLoadedData(true));
}, [dispatch, username]);
Expand All @@ -53,6 +83,17 @@ const AddToCollectionList = ({ projectId }) => {
dispatch(removeFromCollection(collection.id, projectId));
};

const handleCreateCollection = (e) => {
e.preventDefault();
if (!newCollectionName.trim()) return;
dispatch(
createCollectionAndRefresh({ name: newCollectionName.trim() }, username)
).then(() => {
setShowCreateForm(false);
setNewCollectionName(generateCollectionName());
});
};

const collectionWithSketchStatus = collections.map((collection) => ({
...collection,
url: `/${collection.owner.username}/collections/${collection.id}`,
Expand All @@ -63,7 +104,33 @@ const AddToCollectionList = ({ projectId }) => {
if (showLoader) {
return <Loader />;
} else if (collections.length === 0) {
return t('AddToCollectionList.Empty');
return (
<div>
<EmptyText>{t('AddToCollectionList.Empty')}</EmptyText>
{showCreateForm ? (
<CreateFormWrapper onSubmit={handleCreateCollection}>
<CreateInput
type="text"
value={newCollectionName}
onChange={(e) => setNewCollectionName(e.target.value)}
placeholder={generatedName}
aria-label={t('AddToCollectionList.CollectionNameARIA')}
/>
<Button
type="submit"
disabled={!newCollectionName.trim()}
onClick={handleCreateCollection}
>
{t('AddToCollectionList.CreateSubmit')}
</Button>
</CreateFormWrapper>
) : (
<Button onClick={() => setShowCreateForm(true)}>
{t('AddToCollectionList.CreateCollection')}
</Button>
)}
</div>
);
}
return (
<QuickAddList
Expand Down
5 changes: 4 additions & 1 deletion translations/locales/en-US/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,10 @@
"AddToCollectionList": {
"Title": "p5.js Web Editor | My collections",
"AnothersTitle": "p5.js Web Editor | {{anotheruser}}'s collections",
"Empty": "No collections"
"Empty": "No collections",
"CreateCollection": "Create collection",
"CreateSubmit": "Create",
"CollectionNameARIA": "Collection name"
},
"CollectionCreate": {
"Title": "p5.js Web Editor | Create collection",
Expand Down
Loading