diff --git a/client/modules/IDE/components/CollectionList/CollectionList.jsx b/client/modules/IDE/components/CollectionList/CollectionList.jsx index 9d641f5e66..d29ed5b9bd 100644 --- a/client/modules/IDE/components/CollectionList/CollectionList.jsx +++ b/client/modules/IDE/components/CollectionList/CollectionList.jsx @@ -1,313 +1,203 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; -import { withTranslation } from 'react-i18next'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; +import { useParams } from 'react-router-dom'; import { find } from 'lodash'; -import * as ProjectActions from '../../actions/project'; -import * as ProjectsActions from '../../actions/projects'; -import * as CollectionsActions from '../../actions/collections'; -import * as ToastActions from '../../actions/toast'; -import * as SortingActions from '../../actions/sorting'; -import getSortedCollections from '../../selectors/collections'; import Loader from '../../../App/components/loader'; import Overlay from '../../../App/components/Overlay'; import AddToCollectionSketchList from '../AddToCollectionSketchList'; import { SketchSearchbar } from '../Searchbar'; - -import CollectionListRow from './CollectionListRow'; - import ArrowUpIcon from '../../../../images/sort-arrow-up.svg'; import ArrowDownIcon from '../../../../images/sort-arrow-down.svg'; +import { getProject } from '../../actions/project'; +import { getCollections } from '../../actions/collections'; +import getSortedCollections from '../../selectors/collections'; +import { DIRECTION, toggleDirectionForField } from '../../actions/sorting'; +import CollectionListRow from './CollectionListRow'; -class CollectionList extends React.Component { - constructor(props) { - super(props); - - if (props.projectId) { - props.getProject(props.projectId); - } - - this.props.getCollections(this.props.username); - this.props.resetSorting(); - - this.state = { - hasLoadedData: false, - addingSketchesToCollectionId: null - }; - } - - componentDidUpdate(prevProps, prevState) { - if (prevProps.loading === true && this.props.loading === false) { - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ - hasLoadedData: true - }); +const CollectionList = (props) => { + const dispatch = useDispatch(); + const { t } = useTranslation(); + const { project_id: projectId } = useParams() || { project_id: null }; + const [hasLoadedData, setHasLoadedData] = useState(false); + + const user = useSelector((state) => state.user); + const sorting = useSelector((state) => state.sorting); + const loading = useSelector((state) => state.loading); + const collections = useSelector((state) => getSortedCollections(state)); + + const [ + addingSketchesToCollectionId, + setAddingSketchesToCollectionId + ] = useState(null); + + useEffect(() => { + if (projectId) { + dispatch(getProject(props.username)); } - } + dispatch(getCollections(props.username)).then(() => setHasLoadedData(true)); + }, [dispatch, props.username]); - getTitle() { - if (this.props.username === this.props.user.username) { - return this.props.t('CollectionList.Title'); + const getTitle = () => { + if (props.username === user.username) { + return t('CollectionList.Title'); } - return this.props.t('CollectionList.AnothersTitle', { - anotheruser: this.props.username - }); - } + return t('CollectionList.AnothersTitle', { anotheruser: props.username }); + }; - showAddSketches = (collectionId) => { - this.setState({ - addingSketchesToCollectionId: collectionId - }); + const showAddSketches = (collectionId) => { + setAddingSketchesToCollectionId(collectionId); }; - hideAddSketches = () => { - this.setState({ - addingSketchesToCollectionId: null - }); + const hideAddSketches = () => { + setAddingSketchesToCollectionId(null); }; - hasCollections() { - return ( - (!this.props.loading || this.state.hasLoadedData) && - this.props.collections.length > 0 - ); - } + const hasCollections = () => + !loading && hasLoadedData && collections.length > 0; - _renderLoader() { - if (this.props.loading && !this.state.hasLoadedData) return ; - return null; - } + const renderLoader = () => (loading && !hasLoadedData ? : null); - _renderEmptyTable() { - if (!this.props.loading && this.props.collections.length === 0) { + const renderEmptyTable = () => { + if (!loading && collections.length === 0) { return ( + // eslint-disable-next-line react/react-in-jsx-scope

- {this.props.t('CollectionList.NoCollections')} + {t('CollectionList.NoCollections')}

); } return null; - } - - _getButtonLabel = (fieldName, displayName) => { - const { field, direction } = this.props.sorting; + }; + const getButtonLabel = (fieldName, displayName) => { + const { field, direction } = sorting; let buttonLabel; if (field !== fieldName) { - if (field === 'name') { - buttonLabel = this.props.t('CollectionList.ButtonLabelAscendingARIA', { - displayName - }); - } else { - buttonLabel = this.props.t('CollectionList.ButtonLabelDescendingARIA', { - displayName - }); - } - } else if (direction === SortingActions.DIRECTION.ASC) { - buttonLabel = this.props.t('CollectionList.ButtonLabelDescendingARIA', { + buttonLabel = + field === 'name' + ? t('CollectionList.ButtonLabelAscendingARIA', { displayName }) + : t('CollectionList.ButtonLabelDescendingARIA', { displayName }); + } else if (direction === DIRECTION.ASC) { + buttonLabel = t('CollectionList.ButtonLabelDescendingARIA', { displayName }); } else { - buttonLabel = this.props.t('CollectionList.ButtonLabelAscendingARIA', { + buttonLabel = t('CollectionList.ButtonLabelAscendingARIA', { displayName }); } return buttonLabel; }; - - _renderFieldHeader = (fieldName, displayName) => { - const { field, direction } = this.props.sorting; + const renderFieldHeader = (fieldName, displayName) => { + const { field, direction } = sorting; const headerClass = classNames({ 'sketches-table__header': true, 'sketches-table__header--selected': field === fieldName }); - const buttonLabel = this._getButtonLabel(fieldName, displayName); + const buttonLabel = getButtonLabel(fieldName, displayName); return ( ); }; - render() { - const username = - this.props.username !== undefined - ? this.props.username - : this.props.user.username; - const { mobile } = this.props; - - return ( -
- - {this.getTitle()} - - - {this._renderLoader()} - {this._renderEmptyTable()} - {this.hasCollections() && ( - - - - {this._renderFieldHeader( - 'name', - this.props.t('CollectionList.HeaderName') - )} - {this._renderFieldHeader( - 'createdAt', - this.props.t('CollectionList.HeaderCreatedAt', { - context: mobile ? 'mobile' : '' - }) - )} - {this._renderFieldHeader( - 'updatedAt', - this.props.t('CollectionList.HeaderUpdatedAt', { - context: mobile ? 'mobile' : '' - }) - )} - {this._renderFieldHeader( - 'numItems', - this.props.t('CollectionList.HeaderNumItems', { - context: mobile ? 'mobile' : '' - }) - )} - - - - - {this.props.collections.map((collection) => ( - this.showAddSketches(collection.id)} - /> - ))} - -
- )} - {this.state.addingSketchesToCollectionId && ( - } - closeOverlay={this.hideAddSketches} - isFixedHeight - > - - - )} -
- ); - } -} - -CollectionList.propTypes = { - user: PropTypes.shape({ - username: PropTypes.string, - authenticated: PropTypes.bool.isRequired - }).isRequired, - projectId: PropTypes.string, - getCollections: PropTypes.func.isRequired, - getProject: PropTypes.func.isRequired, - collections: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - description: PropTypes.string, - createdAt: PropTypes.string.isRequired, - updatedAt: PropTypes.string.isRequired - }) - ).isRequired, - username: PropTypes.string, - loading: PropTypes.bool.isRequired, - toggleDirectionForField: PropTypes.func.isRequired, - resetSorting: PropTypes.func.isRequired, - sorting: PropTypes.shape({ - field: PropTypes.string.isRequired, - direction: PropTypes.string.isRequired - }).isRequired, - project: PropTypes.shape({ - id: PropTypes.string, - owner: PropTypes.shape({ - id: PropTypes.string - }) - }), - t: PropTypes.func.isRequired, - mobile: PropTypes.bool + return ( +
+ + {getTitle()} + + + {renderLoader()} + {renderEmptyTable()} + {hasCollections() && ( + + + + {renderFieldHeader('name', t('CollectionList.HeaderName'))} + {renderFieldHeader( + 'createdAt', + t('CollectionList.HeaderCreatedAt', { + context: props.mobile ? 'mobile' : '' + }) + )} + {renderFieldHeader( + 'updatedAt', + t('CollectionList.HeaderUpdatedAt', { + context: props.mobile ? 'mobile' : '' + }) + )} + {renderFieldHeader( + 'numItems', + t('CollectionList.HeaderNumItems', { + context: props.mobile ? 'mobile' : '' + }) + )} + + + + + {collections.map((collection) => ( + showAddSketches(collection.id)} + /> + ))} + +
+ )} + {addingSketchesToCollectionId && ( + } + closeOverlay={hideAddSketches} + isFixedHeight + > + + + )} +
+ ); }; - -CollectionList.defaultProps = { - projectId: undefined, - project: { - id: undefined, - owner: undefined - }, - username: undefined, - mobile: false +CollectionList.propTypes = { + username: PropTypes.string.isRequired, + mobile: PropTypes.bool.isRequired }; -function mapStateToProps(state, ownProps) { - return { - user: state.user, - collections: getSortedCollections(state), - sorting: state.sorting, - loading: state.loading, - project: state.project, - projectId: ownProps && ownProps.params ? ownProps.params.project_id : null - }; -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - Object.assign( - {}, - CollectionsActions, - ProjectsActions, - ProjectActions, - ToastActions, - SortingActions - ), - dispatch - ); -} - -export default withTranslation()( - connect(mapStateToProps, mapDispatchToProps)(CollectionList) -); +export default CollectionList; diff --git a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx index dafbe21517..3281060415 100644 --- a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx +++ b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx @@ -1,21 +1,19 @@ import PropTypes from 'prop-types'; import React, { useState, useRef } from 'react'; -import { connect } from 'react-redux'; +import { useDispatch } from 'react-redux'; +import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; -import { bindActionCreators } from 'redux'; -import { withTranslation } from 'react-i18next'; import MenuItem from '../../../../components/Dropdown/MenuItem'; import TableDropdown from '../../../../components/Dropdown/TableDropdown'; -import * as ProjectActions from '../../actions/project'; -import * as CollectionsActions from '../../actions/collections'; -import * as IdeActions from '../../actions/ide'; -import * as ToastActions from '../../actions/toast'; import dates from '../../../../utils/formatDate'; +import { deleteCollection, editCollection } from '../../actions/collections'; const formatDateCell = (date, mobile = false) => dates.format(date, { showTime: !mobile }); -const CollectionListRowBase = (props) => { +const CollectionListRow = (props) => { + const { t } = useTranslation(); + const dispatch = useDispatch(); const [renameOpen, setRenameOpen] = useState(false); const [renameValue, setRenameValue] = useState(''); const renameInput = useRef(null); @@ -27,9 +25,11 @@ const CollectionListRowBase = (props) => { const updateName = () => { const isValid = renameValue.trim().length !== 0; if (isValid) { - props.editCollection(props.collection.id, { - name: renameValue.trim() - }); + dispatch( + editCollection(props.collection.id, { + name: renameValue.trim() + }) + ); } }; @@ -42,12 +42,12 @@ const CollectionListRowBase = (props) => { closeAll(); if ( window.confirm( - props.t('Common.DeleteConfirmation', { + t('Common.DeleteConfirmation', { name: props.collection.name }) ) ) { - props.deleteCollection(props.collection.id); + dispatch(deleteCollection(props.collection.id)); } }; @@ -81,16 +81,16 @@ const CollectionListRowBase = (props) => { return ( - {props.t('CollectionListRow.AddSketch')} + {t('CollectionListRow.AddSketch')} - {props.t('CollectionListRow.Delete')} + {t('CollectionListRow.Delete')} - {props.t('CollectionListRow.Rename')} + {t('CollectionListRow.Rename')} ); @@ -141,7 +141,7 @@ const CollectionListRowBase = (props) => { ); }; -CollectionListRowBase.propTypes = { +CollectionListRow.propTypes = { collection: PropTypes.shape({ id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, @@ -163,30 +163,12 @@ CollectionListRowBase.propTypes = { username: PropTypes.string, authenticated: PropTypes.bool.isRequired }).isRequired, - deleteCollection: PropTypes.func.isRequired, - editCollection: PropTypes.func.isRequired, onAddSketches: PropTypes.func.isRequired, - mobile: PropTypes.bool, - t: PropTypes.func.isRequired + mobile: PropTypes.bool }; -CollectionListRowBase.defaultProps = { +CollectionListRow.defaultProps = { mobile: false }; -function mapDispatchToPropsSketchListRow(dispatch) { - return bindActionCreators( - Object.assign( - {}, - CollectionsActions, - ProjectActions, - IdeActions, - ToastActions - ), - dispatch - ); -} - -export default withTranslation()( - connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase) -); +export default CollectionListRow;