Create Collection
This commit is contained in:
parent
d02a413bf3
commit
dcf65c6f46
13 changed files with 394 additions and 12 deletions
|
@ -36,6 +36,10 @@ export const SET_PROJECT = 'SET_PROJECT';
|
||||||
export const SET_PROJECTS = 'SET_PROJECTS';
|
export const SET_PROJECTS = 'SET_PROJECTS';
|
||||||
|
|
||||||
export const SET_COLLECTIONS = 'SET_COLLECTIONS';
|
export const SET_COLLECTIONS = 'SET_COLLECTIONS';
|
||||||
|
export const CREATE_COLLECTION = 'CREATED_COLLECTION';
|
||||||
|
|
||||||
|
export const ADD_TO_COLLECTION = 'ADD_TO_COLLECTION';
|
||||||
|
export const REMOVE_FROM_COLLECTION = 'REMOVE_FROM_COLLECTION';
|
||||||
|
|
||||||
export const DELETE_PROJECT = 'DELETE_PROJECT';
|
export const DELETE_PROJECT = 'DELETE_PROJECT';
|
||||||
|
|
||||||
|
|
|
@ -32,3 +32,80 @@ export function getCollections(username) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createCollection(collection) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(startLoader());
|
||||||
|
const url = `${ROOT_URL}/collections`;
|
||||||
|
return axios.post(url, collection, { withCredentials: true })
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.CREATE_COLLECTION
|
||||||
|
});
|
||||||
|
dispatch(stopLoader());
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.ERROR,
|
||||||
|
error: response.data
|
||||||
|
});
|
||||||
|
dispatch(stopLoader());
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addToCollection(collectionId, projectId) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(startLoader());
|
||||||
|
const url = `${ROOT_URL}/collections/${collectionId}/${projectId}`;
|
||||||
|
return axios.post(url, { withCredentials: true })
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.ADD_TO_COLLECTION,
|
||||||
|
payload: response.data
|
||||||
|
});
|
||||||
|
dispatch(stopLoader());
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.ERROR,
|
||||||
|
error: response.data
|
||||||
|
});
|
||||||
|
dispatch(stopLoader());
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeFromCollection(collectionId, projectId) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(startLoader());
|
||||||
|
const url = `${ROOT_URL}/collections/${collectionId}/${projectId}`;
|
||||||
|
return axios.delete(url, { withCredentials: true })
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.REMOVE_FROM_COLLECTION,
|
||||||
|
payload: response.data
|
||||||
|
});
|
||||||
|
dispatch(stopLoader());
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.ERROR,
|
||||||
|
error: response.data
|
||||||
|
});
|
||||||
|
dispatch(stopLoader());
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@ const arrowDown = require('../../../images/sort-arrow-down.svg');
|
||||||
const downFilledTriangle = require('../../../images/down-filled-triangle.svg');
|
const downFilledTriangle = require('../../../images/down-filled-triangle.svg');
|
||||||
|
|
||||||
class CollectionListRowBase extends React.Component {
|
class CollectionListRowBase extends React.Component {
|
||||||
|
static projectInCollection(project, collection) {
|
||||||
|
return collection.items.find(item => item.project.id === project.id) != null;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -134,6 +138,14 @@ class CollectionListRowBase extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCollectionAdd = () => {
|
||||||
|
this.props.addToCollection(this.props.collection.id, this.props.project.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCollectionRemove = () => {
|
||||||
|
this.props.removeFromCollection(this.props.collection.id, this.props.project.id);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { collection, username } = this.props;
|
const { collection, username } = this.props;
|
||||||
const { renameOpen, optionsOpen, renameValue } = this.state;
|
const { renameOpen, optionsOpen, renameValue } = this.state;
|
||||||
|
@ -198,6 +210,8 @@ class CollectionListRowBase extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionListRowBase.propTypes = {
|
CollectionListRowBase.propTypes = {
|
||||||
|
addToCollection: PropTypes.func.isRequired,
|
||||||
|
removeFromCollection: PropTypes.func.isRequired,
|
||||||
collection: PropTypes.shape({
|
collection: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired
|
name: PropTypes.string.isRequired
|
||||||
|
@ -215,7 +229,7 @@ CollectionListRowBase.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapDispatchToPropsSketchListRow(dispatch) {
|
function mapDispatchToPropsSketchListRow(dispatch) {
|
||||||
return bindActionCreators(Object.assign({}, ProjectActions, IdeActions), dispatch);
|
return bindActionCreators(Object.assign({}, CollectionsActions, ProjectActions, IdeActions), dispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CollectionListRow = connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase);
|
const CollectionListRow = connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase);
|
||||||
|
@ -279,6 +293,9 @@ class CollectionList extends React.Component {
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{this.getTitle()}</title>
|
<title>{this.getTitle()}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
|
<Link to={`/${username}/collections/create`}>New collection</Link>
|
||||||
|
|
||||||
{this._renderLoader()}
|
{this._renderLoader()}
|
||||||
{this._renderEmptyTable()}
|
{this._renderEmptyTable()}
|
||||||
{this.hasCollections() &&
|
{this.hasCollections() &&
|
||||||
|
|
|
@ -4,6 +4,18 @@ const sketches = (state = [], action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.SET_COLLECTIONS:
|
case ActionTypes.SET_COLLECTIONS:
|
||||||
return action.collections;
|
return action.collections;
|
||||||
|
|
||||||
|
// The API returns the complete new collection
|
||||||
|
// with the items added or removed
|
||||||
|
case ActionTypes.ADD_TO_COLLECTION:
|
||||||
|
case ActionTypes.REMOVE_FROM_COLLECTION:
|
||||||
|
return state.map((collection) => {
|
||||||
|
if (collection.id === action.payload.id) {
|
||||||
|
return action.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
import friendlyWords from 'friendly-words';
|
|
||||||
import * as ActionTypes from '../../../constants';
|
import * as ActionTypes from '../../../constants';
|
||||||
|
import { generateProjectName } from '../../../utils/generateRandomName';
|
||||||
const generateRandomName = () => {
|
|
||||||
const adj = friendlyWords.predicates[Math.floor(Math.random() * friendlyWords.predicates.length)];
|
|
||||||
const obj = friendlyWords.objects[Math.floor(Math.random() * friendlyWords.objects.length)];
|
|
||||||
return `${adj} ${obj}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState = () => {
|
const initialState = () => {
|
||||||
const generatedString = generateRandomName();
|
const generatedString = generateProjectName();
|
||||||
const generatedName = generatedString.charAt(0).toUpperCase() + generatedString.slice(1);
|
const generatedName = generatedString.charAt(0).toUpperCase() + generatedString.slice(1);
|
||||||
return {
|
return {
|
||||||
name: generatedName,
|
name: generatedName,
|
||||||
|
|
124
client/modules/User/components/CollectionCreate.jsx
Normal file
124
client/modules/User/components/CollectionCreate.jsx
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import { Helmet } from 'react-helmet';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { browserHistory } from 'react-router';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import * as CollectionsActions from '../../IDE/actions/collections';
|
||||||
|
|
||||||
|
import { generateCollectionName } from '../../../utils/generateRandomName';
|
||||||
|
|
||||||
|
class CollectionCreate extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const name = generateCollectionName();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
generatedCollectionName: name,
|
||||||
|
collection: {
|
||||||
|
name,
|
||||||
|
description: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getTitle() {
|
||||||
|
return 'p5.js Web Editor | Create collection';
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTextChange = field => (evt) => {
|
||||||
|
this.setState({
|
||||||
|
collection: {
|
||||||
|
...this.state.collection,
|
||||||
|
[field]: evt.target.value,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCreateCollection = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.props.createCollection(this.state.collection)
|
||||||
|
.then(({ id, owner }) => {
|
||||||
|
browserHistory.replace(`/${owner.username}/collections/${id}`);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error creating collection', error);
|
||||||
|
this.setState({
|
||||||
|
creationError: error,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { generatedCollectionName, creationError } = this.state;
|
||||||
|
const { name, description } = this.state.collection;
|
||||||
|
|
||||||
|
const invalid = name === '' || name == null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sketches-table-container">
|
||||||
|
<Helmet>
|
||||||
|
<title>{this.getTitle()}</title>
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
<form className="form" onSubmit={this.handleCreateCollection}>
|
||||||
|
{creationError && <span className="form-error">Couldn't create collection</span>}
|
||||||
|
<p className="form__field">
|
||||||
|
<label htmlFor="name" className="form__label">Collection name</label>
|
||||||
|
<input
|
||||||
|
className="form__input"
|
||||||
|
aria-label="name"
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
value={name}
|
||||||
|
placeholder={generatedCollectionName}
|
||||||
|
onChange={this.handleTextChange('name')}
|
||||||
|
/>
|
||||||
|
{invalid && <span className="form-error">Collection name is required</span>}
|
||||||
|
</p>
|
||||||
|
<p className="form__field">
|
||||||
|
<label htmlFor="description" className="form__label">Description (optional)</label>
|
||||||
|
<textarea
|
||||||
|
className="form__input form__input-flexible-height"
|
||||||
|
aria-label="description"
|
||||||
|
type="text"
|
||||||
|
id="description"
|
||||||
|
value={description}
|
||||||
|
onChange={this.handleTextChange('description')}
|
||||||
|
placeholder="My fave sketches"
|
||||||
|
rows="4"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<input type="submit" disabled={invalid} value="Create collection" aria-label="create collection" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionCreate.propTypes = {
|
||||||
|
user: PropTypes.shape({
|
||||||
|
username: PropTypes.string,
|
||||||
|
authenticated: PropTypes.bool.isRequired
|
||||||
|
}).isRequired,
|
||||||
|
createCollection: PropTypes.func.isRequired,
|
||||||
|
collection: PropTypes.shape({}).isRequired, // TODO
|
||||||
|
sorting: PropTypes.shape({
|
||||||
|
field: PropTypes.string.isRequired,
|
||||||
|
direction: PropTypes.string.isRequired
|
||||||
|
}).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
function mapStateToProps(state, ownProps) {
|
||||||
|
return {
|
||||||
|
user: state.user,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return bindActionCreators(Object.assign({}, CollectionsActions), dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(CollectionCreate);
|
114
client/modules/User/pages/CollectionView.jsx
Normal file
114
client/modules/User/pages/CollectionView.jsx
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { browserHistory } from 'react-router';
|
||||||
|
import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions';
|
||||||
|
import NavBasic from '../../../components/NavBasic';
|
||||||
|
|
||||||
|
import CollectionCreate from '../components/CollectionCreate';
|
||||||
|
|
||||||
|
class CollectionView extends React.Component {
|
||||||
|
static defaultProps = {
|
||||||
|
user: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.closeAccountPage = this.closeAccountPage.bind(this);
|
||||||
|
this.gotoHomePage = this.gotoHomePage.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.body.className = this.props.theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeAccountPage() {
|
||||||
|
browserHistory.push(this.props.previousPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoHomePage() {
|
||||||
|
browserHistory.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
ownerName() {
|
||||||
|
if (this.props.params.username) {
|
||||||
|
return this.props.params.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.user.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageTitle() {
|
||||||
|
if (this.isCreatePage()) {
|
||||||
|
return 'Create collection';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'collection';
|
||||||
|
}
|
||||||
|
|
||||||
|
isOwner() {
|
||||||
|
return this.props.user.username === this.props.params.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCreatePage() {
|
||||||
|
const path = this.props.location.pathname;
|
||||||
|
return /create$/.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent() {
|
||||||
|
if (this.isCreatePage() && this.isOwner()) {
|
||||||
|
return <CollectionCreate />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'collection page';
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="dashboard">
|
||||||
|
<NavBasic onBack={this.closeAccountPage} />
|
||||||
|
|
||||||
|
<section className="dashboard-header">
|
||||||
|
<div className="dashboard-header__header">
|
||||||
|
<h2 className="dashboard-header__header__title">{this.pageTitle()}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="dashboard-content">
|
||||||
|
{this.renderContent()}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
previousPath: state.ide.previousPath,
|
||||||
|
user: state.user,
|
||||||
|
theme: state.preferences.theme
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return bindActionCreators({
|
||||||
|
updateSettings, initiateVerification, createApiKey, removeApiKey
|
||||||
|
}, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionView.propTypes = {
|
||||||
|
location: PropTypes.shape({
|
||||||
|
pathname: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
params: PropTypes.shape({
|
||||||
|
username: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
previousPath: PropTypes.string.isRequired,
|
||||||
|
theme: PropTypes.string.isRequired,
|
||||||
|
user: PropTypes.shape({
|
||||||
|
username: PropTypes.string.isRequired,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(CollectionView);
|
|
@ -9,6 +9,7 @@ import ResetPasswordView from './modules/User/pages/ResetPasswordView';
|
||||||
import EmailVerificationView from './modules/User/pages/EmailVerificationView';
|
import EmailVerificationView from './modules/User/pages/EmailVerificationView';
|
||||||
import NewPasswordView from './modules/User/pages/NewPasswordView';
|
import NewPasswordView from './modules/User/pages/NewPasswordView';
|
||||||
import AccountView from './modules/User/pages/AccountView';
|
import AccountView from './modules/User/pages/AccountView';
|
||||||
|
import CollectionView from './modules/User/pages/CollectionView';
|
||||||
import DashboardView from './modules/User/pages/DashboardView';
|
import DashboardView from './modules/User/pages/DashboardView';
|
||||||
import createRedirectWithUsername from './components/createRedirectWithUsername';
|
import createRedirectWithUsername from './components/createRedirectWithUsername';
|
||||||
import { getUser } from './modules/User/actions';
|
import { getUser } from './modules/User/actions';
|
||||||
|
@ -43,6 +44,7 @@ const routes = store => (
|
||||||
<Route path="/:username/sketches/:project_id" component={IDEView} />
|
<Route path="/:username/sketches/:project_id" component={IDEView} />
|
||||||
<Route path="/:username/sketches" component={IDEView} />
|
<Route path="/:username/sketches" component={IDEView} />
|
||||||
<Route path="/:username/collections" component={DashboardView} />
|
<Route path="/:username/collections" component={DashboardView} />
|
||||||
|
<Route path="/:username/collections/create" component={CollectionView} />
|
||||||
<Route path="/:username/collections/:collection_id" component={DashboardView} />
|
<Route path="/:username/collections/:collection_id" component={DashboardView} />
|
||||||
<Route path="/about" component={IDEView} />
|
<Route path="/about" component={IDEView} />
|
||||||
<Route path="/feedback" component={IDEView} />
|
<Route path="/feedback" component={IDEView} />
|
||||||
|
|
|
@ -6,13 +6,13 @@ html, body {
|
||||||
font-size: #{$base-font-size}px;
|
font-size: #{$base-font-size}px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body, input {
|
body, input, textarea {
|
||||||
@include themify() {
|
@include themify() {
|
||||||
color: getThemifyVariable('primary-text-color');
|
color: getThemifyVariable('primary-text-color');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body, input, button {
|
body, input, textarea, button {
|
||||||
font-family: Montserrat, sans-serif;
|
font-family: Montserrat, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,8 @@ input, button {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input,
|
||||||
|
textarea {
|
||||||
padding: #{5 / $base-font-size}rem;
|
padding: #{5 / $base-font-size}rem;
|
||||||
border: 1px solid ;
|
border: 1px solid ;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
|
@ -56,6 +56,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form__input-flexible-height {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.form__input::placeholder {
|
.form__input::placeholder {
|
||||||
@include themify() {
|
@include themify() {
|
||||||
color: getThemifyVariable('form-input-placeholder-text-color');
|
color: getThemifyVariable('form-input-placeholder-text-color');
|
||||||
|
@ -79,6 +83,15 @@
|
||||||
margin: #{39 / $base-font-size}rem 0 #{24 / $base-font-size}rem 0;
|
margin: #{39 / $base-font-size}rem 0 #{24 / $base-font-size}rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form [type="submit"][disabled] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.form--inline [type="submit"] {
|
.form--inline [type="submit"] {
|
||||||
margin: 0 0 0 #{24 / $base-font-size}rem;
|
margin: 0 0 0 #{24 / $base-font-size}rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form [type="submit"][disabled],
|
||||||
|
.form--inline [type="submit"][disabled] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
12
client/utils/generateRandomName.js
Normal file
12
client/utils/generateRandomName.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import friendlyWords from 'friendly-words';
|
||||||
|
|
||||||
|
export function generateProjectName() {
|
||||||
|
const adj = friendlyWords.predicates[Math.floor(Math.random() * friendlyWords.predicates.length)];
|
||||||
|
const obj = friendlyWords.objects[Math.floor(Math.random() * friendlyWords.objects.length)];
|
||||||
|
return `${adj} ${obj}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCollectionName() {
|
||||||
|
const adj = friendlyWords.predicates[Math.floor(Math.random() * friendlyWords.predicates.length)];
|
||||||
|
return `My ${adj} collection`;
|
||||||
|
}
|
|
@ -118,6 +118,18 @@ router.get('/:username/collections', (req, res) => {
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/:username/collections/create', (req, res) => {
|
||||||
|
userExists(req.params.username, exists => (
|
||||||
|
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html))
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/:username/sketches/:project_id/add-to-collection', (req, res) => {
|
||||||
|
projectForUserExists(req.params.username, req.params.project_id, exists => (
|
||||||
|
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html))
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/:username/collections/:id', (req, res) => {
|
router.get('/:username/collections/:id', (req, res) => {
|
||||||
collectionForUserExists(req.params.username, req.params.id, exists => (
|
collectionForUserExists(req.params.username, req.params.id, exists => (
|
||||||
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html))
|
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html))
|
||||||
|
|
Loading…
Reference in a new issue