diff --git a/client/modules/User/components/Collection.jsx b/client/modules/User/components/Collection.jsx index 3301dfbb5c..101ec7df7f 100644 --- a/client/modules/User/components/Collection.jsx +++ b/client/modules/User/components/Collection.jsx @@ -1,5 +1,5 @@ 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'; @@ -7,8 +7,6 @@ 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'; @@ -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 ( -
- - {showURL && ( -
- -
- )} -
- ); -}; - -ShareURL.propTypes = { - value: PropTypes.string.isRequired -}; +import CollectionMetadata from './CollectionMetadata'; const CollectionItemRowBase = ({ collection, @@ -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() { @@ -195,10 +138,6 @@ class Collection extends React.Component { : this.props.user.username; } - getCollectionName() { - return this.props.collection.name; - } - isOwner() { let isOwner = false; @@ -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 ( -
-
-
-

- {this.isOwner() ? ( - value !== ''} - /> - ) : ( - name - )} -

- -

- {this.isOwner() ? ( - - ) : ( - description - )} -

- -

- {this.props.t('Collection.By')} - - {owner.username} - -

- -

- {this.props.t('Collection.NumSketches', { count: items.length })} -

-
- -
-

- -

- {this.isOwner() && ( - - )} -
-
-
- ); - } - - showAddSketches() { - this.setState({ - isAddingSketches: true - }); - } - - hideAddSketches() { - this.setState({ - isAddingSketches: false - }); - } - _renderEmptyTable() { if (this.hasCollection() && !this.hasCollectionItems()) { return ( @@ -408,7 +238,6 @@ class Collection extends React.Component { } render() { - const title = this.hasCollection() ? this.getCollectionName() : null; const isOwner = this.isOwner(); return ( @@ -421,7 +250,7 @@ class Collection extends React.Component { {this.getTitle()} {this._renderLoader()} - {this.hasCollection() && this._renderCollectionMetadata()} +
{this._renderEmptyTable()} @@ -461,19 +290,6 @@ class Collection extends React.Component { )} - {this.state.isAddingSketches && ( - } - closeOverlay={this.hideAddSketches} - isFixedHeight - > - - - )}
@@ -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 @@ -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, diff --git a/client/modules/User/components/CollectionMetadata.jsx b/client/modules/User/components/CollectionMetadata.jsx new file mode 100644 index 0000000000..4b1be7ff5f --- /dev/null +++ b/client/modules/User/components/CollectionMetadata.jsx @@ -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 ( +
+
+
+

+ {isOwner ? ( + value !== ''} + /> + ) : ( + name + )} +

+ +

+ {isOwner ? ( + + ) : ( + description + )} +

+ +

+ {t('Collection.By')} + {username} +

+ +

+ {t('Collection.NumSketches', { count: items.length })} +

+
+ +
+ + {isOwner && ( + + )} +
+
+ {isAddingSketches && ( + } + closeOverlay={() => setIsAddingSketches(false)} + isFixedHeight + > + + + )} +
+ ); +} + +CollectionMetadata.propTypes = { + collectionId: PropTypes.string.isRequired +}; + +export default CollectionMetadata; diff --git a/client/modules/User/components/CollectionShareButton.jsx b/client/modules/User/components/CollectionShareButton.jsx new file mode 100644 index 0000000000..c4e0bba915 --- /dev/null +++ b/client/modules/User/components/CollectionShareButton.jsx @@ -0,0 +1,54 @@ +import PropTypes from 'prop-types'; +import React, { useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import Button from '../../../common/Button'; +import { DropdownArrowIcon } from '../../../common/icons'; +import CopyableInput from '../../IDE/components/CopyableInput'; + +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 ( +
+ + {showURL && ( +
+ +
+ )} +
+ ); +}; + +ShareURL.propTypes = { + value: PropTypes.string.isRequired +}; + +export default ShareURL;