Skip to content
192 changes: 4 additions & 188 deletions client/modules/User/components/Collection.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import PropTypes from 'prop-types';
import React, { useState, useRef, useEffect } from 'react';
import React from 'react';
import { Helmet } from 'react-helmet';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { useTranslation, withTranslation } from 'react-i18next';
import classNames from 'classnames';

import Button from '../../../common/Button';
import { DropdownArrowIcon } from '../../../common/icons';
import * as ProjectActions from '../../IDE/actions/project';
import * as ProjectsActions from '../../IDE/actions/projects';
import * as CollectionsActions from '../../IDE/actions/collections';
Expand All @@ -17,61 +15,12 @@ import * as SortingActions from '../../IDE/actions/sorting';
import * as IdeActions from '../../IDE/actions/ide';
import { getCollection } from '../../IDE/selectors/collections';
import Loader from '../../App/components/loader';
import EditableInput from '../../IDE/components/EditableInput';
import Overlay from '../../App/components/Overlay';
import AddToCollectionSketchList from '../../IDE/components/AddToCollectionSketchList';
import CopyableInput from '../../IDE/components/CopyableInput';
import { SketchSearchbar } from '../../IDE/components/Searchbar';
import dates from '../../../utils/formatDate';

import ArrowUpIcon from '../../../images/sort-arrow-up.svg';
import ArrowDownIcon from '../../../images/sort-arrow-down.svg';
import RemoveIcon from '../../../images/close.svg';

const ShareURL = ({ value }) => {
const [showURL, setShowURL] = useState(false);
const node = useRef();
const { t } = useTranslation();

const handleClickOutside = (e) => {
if (node.current.contains(e.target)) {
return;
}
setShowURL(false);
};

useEffect(() => {
if (showURL) {
document.addEventListener('mousedown', handleClickOutside);
} else {
document.removeEventListener('mousedown', handleClickOutside);
}

return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [showURL]);

return (
<div className="collection-share" ref={node}>
<Button
onClick={() => setShowURL(!showURL)}
iconAfter={<DropdownArrowIcon />}
>
{t('Collection.Share')}
</Button>
{showURL && (
<div className="collection__share-dropdown">
<CopyableInput value={value} label={t('Collection.URLLink')} />
</div>
)}
</div>
);
};

ShareURL.propTypes = {
value: PropTypes.string.isRequired
};
import CollectionMetadata from './CollectionMetadata';

const CollectionItemRowBase = ({
collection,
Expand Down Expand Up @@ -172,12 +121,6 @@ class Collection extends React.Component {
this.props.getCollections(this.props.username);
this.props.resetSorting();
this._renderFieldHeader = this._renderFieldHeader.bind(this);
this.showAddSketches = this.showAddSketches.bind(this);
this.hideAddSketches = this.hideAddSketches.bind(this);

this.state = {
isAddingSketches: false
};
}

getTitle() {
Expand All @@ -195,10 +138,6 @@ class Collection extends React.Component {
: this.props.user.username;
}

getCollectionName() {
return this.props.collection.name;
}

isOwner() {
let isOwner = false;

Expand Down Expand Up @@ -226,115 +165,6 @@ class Collection extends React.Component {
return null;
}

_renderCollectionMetadata() {
const { id, name, description, items, owner } = this.props.collection;

const hostname = window.location.origin;
const { username } = this.props;

const baseURL = `${hostname}/${username}/collections/`;

const handleEditCollectionName = (value) => {
if (value === name) {
return;
}

this.props.editCollection(id, { name: value });
};

const handleEditCollectionDescription = (value) => {
if (value === description) {
return;
}

this.props.editCollection(id, { description: value });
};

//
// TODO: Implement UI for editing slug
//
// const handleEditCollectionSlug = (value) => {
// if (value === slug) {
// return;
// }
//
// this.props.editCollection(id, { slug: value });
// };

return (
<header
className={`collection-metadata ${
this.isOwner() ? 'collection-metadata--is-owner' : ''
}`}
>
<div className="collection-metadata__columns">
<div className="collection-metadata__column--left">
<h2 className="collection-metadata__name">
{this.isOwner() ? (
<EditableInput
value={name}
onChange={handleEditCollectionName}
validate={(value) => value !== ''}
/>
) : (
name
)}
</h2>

<p className="collection-metadata__description">
{this.isOwner() ? (
<EditableInput
InputComponent="textarea"
value={description}
onChange={handleEditCollectionDescription}
emptyPlaceholder={this.props.t(
'Collection.DescriptionPlaceholder'
)}
/>
) : (
description
)}
</p>

<p className="collection-metadata__user">
{this.props.t('Collection.By')}
<Link to={`${hostname}/${username}/sketches`}>
{owner.username}
</Link>
</p>

<p className="collection-metadata__user">
{this.props.t('Collection.NumSketches', { count: items.length })}
</p>
</div>

<div className="collection-metadata__column--right">
<p className="collection-metadata__share">
<ShareURL value={`${baseURL}${id}`} />
</p>
{this.isOwner() && (
<Button onClick={this.showAddSketches}>
{this.props.t('Collection.AddSketch')}
</Button>
)}
</div>
</div>
</header>
);
}

showAddSketches() {
this.setState({
isAddingSketches: true
});
}

hideAddSketches() {
this.setState({
isAddingSketches: false
});
}

_renderEmptyTable() {
if (this.hasCollection() && !this.hasCollectionItems()) {
return (
Expand Down Expand Up @@ -408,7 +238,6 @@ class Collection extends React.Component {
}

render() {
const title = this.hasCollection() ? this.getCollectionName() : null;
const isOwner = this.isOwner();

return (
Expand All @@ -421,7 +250,7 @@ class Collection extends React.Component {
<title>{this.getTitle()}</title>
</Helmet>
{this._renderLoader()}
{this.hasCollection() && this._renderCollectionMetadata()}
<CollectionMetadata collectionId={this.props.collectionId} />
<article className="collection-content">
<div className="collection-table-wrapper">
{this._renderEmptyTable()}
Expand Down Expand Up @@ -461,19 +290,6 @@ class Collection extends React.Component {
</tbody>
</table>
)}
{this.state.isAddingSketches && (
<Overlay
title={this.props.t('Collection.AddSketch')}
actions={<SketchSearchbar />}
closeOverlay={this.hideAddSketches}
isFixedHeight
>
<AddToCollectionSketchList
username={this.props.username}
collection={this.props.collection}
/>
</Overlay>
)}
</div>
</article>
</article>
Expand All @@ -483,6 +299,7 @@ class Collection extends React.Component {
}

Collection.propTypes = {
collectionId: PropTypes.string.isRequired,
user: PropTypes.shape({
username: PropTypes.string,
authenticated: PropTypes.bool.isRequired
Expand All @@ -501,7 +318,6 @@ Collection.propTypes = {
username: PropTypes.string,
loading: PropTypes.bool.isRequired,
toggleDirectionForField: PropTypes.func.isRequired,
editCollection: PropTypes.func.isRequired,
resetSorting: PropTypes.func.isRequired,
sorting: PropTypes.shape({
field: PropTypes.string.isRequired,
Expand Down
126 changes: 126 additions & 0 deletions client/modules/User/components/CollectionMetadata.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import Button from '../../../common/Button';
import Overlay from '../../App/components/Overlay';
import { editCollection } from '../../IDE/actions/collections';
import AddToCollectionSketchList from '../../IDE/components/AddToCollectionSketchList';
import EditableInput from '../../IDE/components/EditableInput';
import { SketchSearchbar } from '../../IDE/components/Searchbar';
import { getCollection } from '../../IDE/selectors/collections';
import ShareURL from './CollectionShareButton';

function CollectionMetadata({ collectionId }) {
const { t } = useTranslation();

const dispatch = useDispatch();

const collection = useSelector((state) => getCollection(state, collectionId));
const currentUsername = useSelector((state) => state.user.username);

const [isAddingSketches, setIsAddingSketches] = useState(false);

if (!collection) {
return null;
}

const { id, name, description, items, owner } = collection;
const { username } = owner;
const isOwner = !!currentUsername && currentUsername === username;

const hostname = window.location.origin;

const handleEditCollectionName = (value) => {
if (value === name) {
return;
}
dispatch(editCollection(id, { name: value }));
};

const handleEditCollectionDescription = (value) => {
if (value === description) {
return;
}
dispatch(editCollection(id, { description: value }));
};

// TODO: Implement UI for editing slug

return (
<header
className={classNames(
'collection-metadata',
isOwner && 'collection-metadata--is-owner'
)}
>
<div className="collection-metadata__columns">
<div className="collection-metadata__column--left">
<h2 className="collection-metadata__name">
{isOwner ? (
<EditableInput
value={name}
onChange={handleEditCollectionName}
validate={(value) => value !== ''}
/>
) : (
name
)}
</h2>

<p className="collection-metadata__description">
{isOwner ? (
<EditableInput
InputComponent="textarea"
value={description}
onChange={handleEditCollectionDescription}
emptyPlaceholder={t('Collection.DescriptionPlaceholder')}
/>
) : (
description
)}
</p>

<p className="collection-metadata__user">
{t('Collection.By')}
<Link to={`/${username}/sketches`}>{username}</Link>
</p>

<p className="collection-metadata__user">
{t('Collection.NumSketches', { count: items.length })}
</p>
</div>

<div className="collection-metadata__column--right">
<ShareURL value={`${hostname}/${username}/collections/${id}`} />
{isOwner && (
<Button onClick={() => setIsAddingSketches(true)}>
{t('Collection.AddSketch')}
</Button>
)}
</div>
</div>
{isAddingSketches && (
<Overlay
title={t('Collection.AddSketch')}
actions={<SketchSearchbar />}
closeOverlay={() => setIsAddingSketches(false)}
isFixedHeight
>
<AddToCollectionSketchList
username={username}
collection={collection}
/>
</Overlay>
)}
</header>
);
}

CollectionMetadata.propTypes = {
collectionId: PropTypes.string.isRequired
};

export default CollectionMetadata;
Loading