From ac9d4d18b032b3c86cd4b4c576504fe76b6b0341 Mon Sep 17 00:00:00 2001
From: Linda Paiste
Date: Sat, 17 Jun 2023 15:35:30 -0500
Subject: [PATCH 1/3] Duplicate files.
---
.../User/components/CollectionMetadata.jsx | 553 ++++++++++++++++++
.../User/components/CollectionShareButton.jsx | 553 ++++++++++++++++++
2 files changed, 1106 insertions(+)
create mode 100644 client/modules/User/components/CollectionMetadata.jsx
create mode 100644 client/modules/User/components/CollectionShareButton.jsx
diff --git a/client/modules/User/components/CollectionMetadata.jsx b/client/modules/User/components/CollectionMetadata.jsx
new file mode 100644
index 0000000000..563fd7d6ff
--- /dev/null
+++ b/client/modules/User/components/CollectionMetadata.jsx
@@ -0,0 +1,553 @@
+import PropTypes from 'prop-types';
+import React, { useState, useRef, useEffect } from 'react';
+import { Helmet } from 'react-helmet';
+import { connect } from 'react-redux';
+import { Link } from 'react-router';
+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';
+import * as ToastActions from '../../IDE/actions/toast';
+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
+};
+
+const CollectionItemRowBase = ({
+ collection,
+ item,
+ isOwner,
+ removeFromCollection
+}) => {
+ const { t } = useTranslation();
+
+ const projectIsDeleted = item.isDeleted;
+
+ const handleSketchRemove = () => {
+ const name = projectIsDeleted ? 'deleted sketch' : item.project.name;
+
+ if (
+ window.confirm(
+ t('Collection.DeleteFromCollection', { name_sketch: name })
+ )
+ ) {
+ removeFromCollection(collection.id, item.projectId);
+ }
+ };
+
+ const name = projectIsDeleted ? (
+ {t('Collection.SketchDeleted')}
+ ) : (
+
+ {item.project.name}
+
+ );
+
+ const sketchOwnerUsername = projectIsDeleted
+ ? null
+ : item.project.user.username;
+
+ return (
+
+ {name} |
+ {dates.format(item.createdAt)} |
+ {sketchOwnerUsername} |
+
+ {isOwner && (
+
+ )}
+ |
+
+ );
+};
+
+CollectionItemRowBase.propTypes = {
+ collection: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ item: PropTypes.shape({
+ createdAt: PropTypes.string.isRequired,
+ projectId: PropTypes.string.isRequired,
+ isDeleted: PropTypes.bool.isRequired,
+ project: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ user: PropTypes.shape({
+ username: PropTypes.string.isRequired
+ })
+ }).isRequired
+ }).isRequired,
+ isOwner: PropTypes.bool.isRequired,
+ user: PropTypes.shape({
+ username: PropTypes.string,
+ authenticated: PropTypes.bool.isRequired
+ }).isRequired,
+ removeFromCollection: PropTypes.func.isRequired
+};
+
+function mapDispatchToPropsSketchListRow(dispatch) {
+ return bindActionCreators(
+ Object.assign({}, CollectionsActions, ProjectActions, IdeActions),
+ dispatch
+ );
+}
+
+const CollectionItemRow = connect(
+ null,
+ mapDispatchToPropsSketchListRow
+)(CollectionItemRowBase);
+
+class Collection extends React.Component {
+ constructor(props) {
+ super(props);
+ 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() {
+ if (this.props.username === this.props.user.username) {
+ return this.props.t('Collection.Title');
+ }
+ return this.props.t('Collection.AnothersTitle', {
+ anotheruser: this.props.username
+ });
+ }
+
+ getUsername() {
+ return this.props.username !== undefined
+ ? this.props.username
+ : this.props.user.username;
+ }
+
+ getCollectionName() {
+ return this.props.collection.name;
+ }
+
+ isOwner() {
+ let isOwner = false;
+
+ if (
+ this.props.user != null &&
+ this.props.user.username &&
+ this.props.collection.owner.username === this.props.user.username
+ ) {
+ isOwner = true;
+ }
+
+ return isOwner;
+ }
+
+ hasCollection() {
+ return !this.props.loading && this.props.collection != null;
+ }
+
+ hasCollectionItems() {
+ return this.hasCollection() && this.props.collection.items.length > 0;
+ }
+
+ _renderLoader() {
+ if (this.props.loading) return ;
+ 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() {
+ const isLoading = this.props.loading;
+ const hasCollectionItems =
+ this.props.collection != null && this.props.collection.items.length > 0;
+
+ if (!isLoading && !hasCollectionItems) {
+ return (
+
+ {this.props.t('Collection.NoSketches')}
+
+ );
+ }
+ return null;
+ }
+
+ _getButtonLabel = (fieldName, displayName) => {
+ const { field, direction } = this.props.sorting;
+ let buttonLabel;
+ if (field !== fieldName) {
+ if (field === 'name') {
+ buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
+ displayName
+ });
+ } else {
+ buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
+ displayName
+ });
+ }
+ } else if (direction === SortingActions.DIRECTION.ASC) {
+ buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
+ displayName
+ });
+ } else {
+ buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
+ displayName
+ });
+ }
+ return buttonLabel;
+ };
+
+ _renderFieldHeader(fieldName, displayName) {
+ const { field, direction } = this.props.sorting;
+ const headerClass = classNames({
+ arrowDown: true,
+ 'sketches-table__header--selected': field === fieldName
+ });
+ const buttonLabel = this._getButtonLabel(fieldName, displayName);
+ return (
+
+
+ |
+ );
+ }
+
+ render() {
+ const title = this.hasCollection() ? this.getCollectionName() : null;
+ const isOwner = this.isOwner();
+
+ return (
+
+
+
+ {this.getTitle()}
+
+ {this._renderLoader()}
+ {this.hasCollection() && this._renderCollectionMetadata()}
+
+
+ {this._renderEmptyTable()}
+ {this.hasCollectionItems() && (
+
+
+
+ {this._renderFieldHeader(
+ 'name',
+ this.props.t('Collection.HeaderName')
+ )}
+ {this._renderFieldHeader(
+ 'createdAt',
+ this.props.t('Collection.HeaderCreatedAt')
+ )}
+ {this._renderFieldHeader(
+ 'user',
+ this.props.t('Collection.HeaderUser')
+ )}
+ |
+
+
+
+ {this.props.collection.items.map((item) => (
+
+ ))}
+
+
+ )}
+ {this.state.isAddingSketches && (
+
}
+ closeOverlay={this.hideAddSketches}
+ isFixedHeight
+ >
+
+
+ )}
+
+
+
+
+ );
+ }
+}
+
+Collection.propTypes = {
+ user: PropTypes.shape({
+ username: PropTypes.string,
+ authenticated: PropTypes.bool.isRequired
+ }).isRequired,
+ getCollections: PropTypes.func.isRequired,
+ collection: PropTypes.shape({
+ id: PropTypes.string,
+ name: PropTypes.string,
+ slug: PropTypes.string,
+ description: PropTypes.string,
+ owner: PropTypes.shape({
+ username: PropTypes.string
+ }).isRequired,
+ items: PropTypes.arrayOf(PropTypes.shape({}))
+ }),
+ 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,
+ direction: PropTypes.string.isRequired
+ }).isRequired,
+ t: PropTypes.func.isRequired
+};
+
+Collection.defaultProps = {
+ username: undefined,
+ collection: {
+ id: undefined,
+ items: [],
+ owner: {
+ username: undefined
+ }
+ }
+};
+
+function mapStateToProps(state, ownProps) {
+ return {
+ user: state.user,
+ collection: getCollection(state, ownProps.collectionId),
+ sorting: state.sorting,
+ loading: state.loading,
+ project: state.project
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return bindActionCreators(
+ Object.assign(
+ {},
+ CollectionsActions,
+ ProjectsActions,
+ ToastActions,
+ SortingActions
+ ),
+ dispatch
+ );
+}
+
+export default withTranslation()(
+ connect(mapStateToProps, mapDispatchToProps)(Collection)
+);
diff --git a/client/modules/User/components/CollectionShareButton.jsx b/client/modules/User/components/CollectionShareButton.jsx
new file mode 100644
index 0000000000..563fd7d6ff
--- /dev/null
+++ b/client/modules/User/components/CollectionShareButton.jsx
@@ -0,0 +1,553 @@
+import PropTypes from 'prop-types';
+import React, { useState, useRef, useEffect } from 'react';
+import { Helmet } from 'react-helmet';
+import { connect } from 'react-redux';
+import { Link } from 'react-router';
+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';
+import * as ToastActions from '../../IDE/actions/toast';
+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
+};
+
+const CollectionItemRowBase = ({
+ collection,
+ item,
+ isOwner,
+ removeFromCollection
+}) => {
+ const { t } = useTranslation();
+
+ const projectIsDeleted = item.isDeleted;
+
+ const handleSketchRemove = () => {
+ const name = projectIsDeleted ? 'deleted sketch' : item.project.name;
+
+ if (
+ window.confirm(
+ t('Collection.DeleteFromCollection', { name_sketch: name })
+ )
+ ) {
+ removeFromCollection(collection.id, item.projectId);
+ }
+ };
+
+ const name = projectIsDeleted ? (
+ {t('Collection.SketchDeleted')}
+ ) : (
+
+ {item.project.name}
+
+ );
+
+ const sketchOwnerUsername = projectIsDeleted
+ ? null
+ : item.project.user.username;
+
+ return (
+
+ {name} |
+ {dates.format(item.createdAt)} |
+ {sketchOwnerUsername} |
+
+ {isOwner && (
+
+ )}
+ |
+
+ );
+};
+
+CollectionItemRowBase.propTypes = {
+ collection: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ item: PropTypes.shape({
+ createdAt: PropTypes.string.isRequired,
+ projectId: PropTypes.string.isRequired,
+ isDeleted: PropTypes.bool.isRequired,
+ project: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ user: PropTypes.shape({
+ username: PropTypes.string.isRequired
+ })
+ }).isRequired
+ }).isRequired,
+ isOwner: PropTypes.bool.isRequired,
+ user: PropTypes.shape({
+ username: PropTypes.string,
+ authenticated: PropTypes.bool.isRequired
+ }).isRequired,
+ removeFromCollection: PropTypes.func.isRequired
+};
+
+function mapDispatchToPropsSketchListRow(dispatch) {
+ return bindActionCreators(
+ Object.assign({}, CollectionsActions, ProjectActions, IdeActions),
+ dispatch
+ );
+}
+
+const CollectionItemRow = connect(
+ null,
+ mapDispatchToPropsSketchListRow
+)(CollectionItemRowBase);
+
+class Collection extends React.Component {
+ constructor(props) {
+ super(props);
+ 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() {
+ if (this.props.username === this.props.user.username) {
+ return this.props.t('Collection.Title');
+ }
+ return this.props.t('Collection.AnothersTitle', {
+ anotheruser: this.props.username
+ });
+ }
+
+ getUsername() {
+ return this.props.username !== undefined
+ ? this.props.username
+ : this.props.user.username;
+ }
+
+ getCollectionName() {
+ return this.props.collection.name;
+ }
+
+ isOwner() {
+ let isOwner = false;
+
+ if (
+ this.props.user != null &&
+ this.props.user.username &&
+ this.props.collection.owner.username === this.props.user.username
+ ) {
+ isOwner = true;
+ }
+
+ return isOwner;
+ }
+
+ hasCollection() {
+ return !this.props.loading && this.props.collection != null;
+ }
+
+ hasCollectionItems() {
+ return this.hasCollection() && this.props.collection.items.length > 0;
+ }
+
+ _renderLoader() {
+ if (this.props.loading) return ;
+ 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() {
+ const isLoading = this.props.loading;
+ const hasCollectionItems =
+ this.props.collection != null && this.props.collection.items.length > 0;
+
+ if (!isLoading && !hasCollectionItems) {
+ return (
+
+ {this.props.t('Collection.NoSketches')}
+
+ );
+ }
+ return null;
+ }
+
+ _getButtonLabel = (fieldName, displayName) => {
+ const { field, direction } = this.props.sorting;
+ let buttonLabel;
+ if (field !== fieldName) {
+ if (field === 'name') {
+ buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
+ displayName
+ });
+ } else {
+ buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
+ displayName
+ });
+ }
+ } else if (direction === SortingActions.DIRECTION.ASC) {
+ buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
+ displayName
+ });
+ } else {
+ buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
+ displayName
+ });
+ }
+ return buttonLabel;
+ };
+
+ _renderFieldHeader(fieldName, displayName) {
+ const { field, direction } = this.props.sorting;
+ const headerClass = classNames({
+ arrowDown: true,
+ 'sketches-table__header--selected': field === fieldName
+ });
+ const buttonLabel = this._getButtonLabel(fieldName, displayName);
+ return (
+
+
+ |
+ );
+ }
+
+ render() {
+ const title = this.hasCollection() ? this.getCollectionName() : null;
+ const isOwner = this.isOwner();
+
+ return (
+
+
+
+ {this.getTitle()}
+
+ {this._renderLoader()}
+ {this.hasCollection() && this._renderCollectionMetadata()}
+
+
+ {this._renderEmptyTable()}
+ {this.hasCollectionItems() && (
+
+
+
+ {this._renderFieldHeader(
+ 'name',
+ this.props.t('Collection.HeaderName')
+ )}
+ {this._renderFieldHeader(
+ 'createdAt',
+ this.props.t('Collection.HeaderCreatedAt')
+ )}
+ {this._renderFieldHeader(
+ 'user',
+ this.props.t('Collection.HeaderUser')
+ )}
+ |
+
+
+
+ {this.props.collection.items.map((item) => (
+
+ ))}
+
+
+ )}
+ {this.state.isAddingSketches && (
+
}
+ closeOverlay={this.hideAddSketches}
+ isFixedHeight
+ >
+
+
+ )}
+
+
+
+
+ );
+ }
+}
+
+Collection.propTypes = {
+ user: PropTypes.shape({
+ username: PropTypes.string,
+ authenticated: PropTypes.bool.isRequired
+ }).isRequired,
+ getCollections: PropTypes.func.isRequired,
+ collection: PropTypes.shape({
+ id: PropTypes.string,
+ name: PropTypes.string,
+ slug: PropTypes.string,
+ description: PropTypes.string,
+ owner: PropTypes.shape({
+ username: PropTypes.string
+ }).isRequired,
+ items: PropTypes.arrayOf(PropTypes.shape({}))
+ }),
+ 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,
+ direction: PropTypes.string.isRequired
+ }).isRequired,
+ t: PropTypes.func.isRequired
+};
+
+Collection.defaultProps = {
+ username: undefined,
+ collection: {
+ id: undefined,
+ items: [],
+ owner: {
+ username: undefined
+ }
+ }
+};
+
+function mapStateToProps(state, ownProps) {
+ return {
+ user: state.user,
+ collection: getCollection(state, ownProps.collectionId),
+ sorting: state.sorting,
+ loading: state.loading,
+ project: state.project
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return bindActionCreators(
+ Object.assign(
+ {},
+ CollectionsActions,
+ ProjectsActions,
+ ToastActions,
+ SortingActions
+ ),
+ dispatch
+ );
+}
+
+export default withTranslation()(
+ connect(mapStateToProps, mapDispatchToProps)(Collection)
+);
From ccdae49558bdec6e31fdf3412ba3d84eaa05a00f Mon Sep 17 00:00:00 2001
From: Linda Paiste
Date: Sat, 17 Jun 2023 18:16:20 -0500
Subject: [PATCH 2/3] Extract `CollectionMetadata` into its own component and
file.
---
client/modules/User/components/Collection.jsx | 192 +-----
.../User/components/CollectionMetadata.jsx | 615 +++---------------
.../User/components/CollectionShareButton.jsx | 507 +--------------
3 files changed, 102 insertions(+), 1212 deletions(-)
diff --git a/client/modules/User/components/Collection.jsx b/client/modules/User/components/Collection.jsx
index 563fd7d6ff..0db510cfec 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';
@@ -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() {
const isLoading = this.props.loading;
const hasCollectionItems =
@@ -412,7 +242,6 @@ class Collection extends React.Component {
}
render() {
- const title = this.hasCollection() ? this.getCollectionName() : null;
const isOwner = this.isOwner();
return (
@@ -425,7 +254,7 @@ class Collection extends React.Component {
{this.getTitle()}
{this._renderLoader()}
- {this.hasCollection() && this._renderCollectionMetadata()}
+
{this._renderEmptyTable()}
@@ -465,19 +294,6 @@ class Collection extends React.Component {
)}
- {this.state.isAddingSketches && (
-
}
- closeOverlay={this.hideAddSketches}
- isFixedHeight
- >
-
-
- )}
@@ -487,6 +303,7 @@ class Collection extends React.Component {
}
Collection.propTypes = {
+ collectionId: PropTypes.string.isRequired,
user: PropTypes.shape({
username: PropTypes.string,
authenticated: PropTypes.bool.isRequired
@@ -505,7 +322,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
index 563fd7d6ff..108a40850d 100644
--- a/client/modules/User/components/CollectionMetadata.jsx
+++ b/client/modules/User/components/CollectionMetadata.jsx
@@ -1,553 +1,126 @@
+import classNames from 'classnames';
import PropTypes from 'prop-types';
-import React, { useState, useRef, useEffect } from 'react';
-import { Helmet } from 'react-helmet';
-import { connect } from 'react-redux';
+import React, { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router';
-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';
-import * as ToastActions from '../../IDE/actions/toast';
-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 { editCollection } from '../../IDE/actions/collections';
import AddToCollectionSketchList from '../../IDE/components/AddToCollectionSketchList';
-import CopyableInput from '../../IDE/components/CopyableInput';
+import EditableInput from '../../IDE/components/EditableInput';
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 { getCollection } from '../../IDE/selectors/collections';
+import ShareURL from './CollectionShareButton';
-const CollectionItemRowBase = ({
- collection,
- item,
- isOwner,
- removeFromCollection
-}) => {
+function CollectionMetadata({ collectionId }) {
const { t } = useTranslation();
- const projectIsDeleted = item.isDeleted;
-
- const handleSketchRemove = () => {
- const name = projectIsDeleted ? 'deleted sketch' : item.project.name;
-
- if (
- window.confirm(
- t('Collection.DeleteFromCollection', { name_sketch: name })
- )
- ) {
- removeFromCollection(collection.id, item.projectId);
- }
- };
-
- const name = projectIsDeleted ? (
- {t('Collection.SketchDeleted')}
- ) : (
-
- {item.project.name}
-
- );
-
- const sketchOwnerUsername = projectIsDeleted
- ? null
- : item.project.user.username;
-
- return (
-
- {name} |
- {dates.format(item.createdAt)} |
- {sketchOwnerUsername} |
-
- {isOwner && (
-
- )}
- |
-
- );
-};
+ const dispatch = useDispatch();
-CollectionItemRowBase.propTypes = {
- collection: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }).isRequired,
- item: PropTypes.shape({
- createdAt: PropTypes.string.isRequired,
- projectId: PropTypes.string.isRequired,
- isDeleted: PropTypes.bool.isRequired,
- project: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- user: PropTypes.shape({
- username: PropTypes.string.isRequired
- })
- }).isRequired
- }).isRequired,
- isOwner: PropTypes.bool.isRequired,
- user: PropTypes.shape({
- username: PropTypes.string,
- authenticated: PropTypes.bool.isRequired
- }).isRequired,
- removeFromCollection: PropTypes.func.isRequired
-};
-
-function mapDispatchToPropsSketchListRow(dispatch) {
- return bindActionCreators(
- Object.assign({}, CollectionsActions, ProjectActions, IdeActions),
- dispatch
- );
-}
+ const collection = useSelector((state) => getCollection(state, collectionId));
+ const currentUsername = useSelector((state) => state.user.username);
-const CollectionItemRow = connect(
- null,
- mapDispatchToPropsSketchListRow
-)(CollectionItemRowBase);
-
-class Collection extends React.Component {
- constructor(props) {
- super(props);
- 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() {
- if (this.props.username === this.props.user.username) {
- return this.props.t('Collection.Title');
- }
- return this.props.t('Collection.AnothersTitle', {
- anotheruser: this.props.username
- });
- }
-
- getUsername() {
- return this.props.username !== undefined
- ? this.props.username
- : this.props.user.username;
- }
-
- getCollectionName() {
- return this.props.collection.name;
- }
-
- isOwner() {
- let isOwner = false;
-
- if (
- this.props.user != null &&
- this.props.user.username &&
- this.props.collection.owner.username === this.props.user.username
- ) {
- isOwner = true;
- }
+ const [isAddingSketches, setIsAddingSketches] = useState(false);
- return isOwner;
- }
-
- hasCollection() {
- return !this.props.loading && this.props.collection != null;
- }
-
- hasCollectionItems() {
- return this.hasCollection() && this.props.collection.items.length > 0;
- }
-
- _renderLoader() {
- if (this.props.loading) return ;
+ if (!collection) {
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
- )}
-
+ const { id, name, description, items, owner } = collection;
+ const { username } = owner;
+ const isOwner = !!currentUsername && currentUsername === username;
-
- {this.isOwner() ? (
-
- ) : (
- description
- )}
-
+ const hostname = window.location.origin;
-
- {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() {
- const isLoading = this.props.loading;
- const hasCollectionItems =
- this.props.collection != null && this.props.collection.items.length > 0;
-
- if (!isLoading && !hasCollectionItems) {
- return (
-
- {this.props.t('Collection.NoSketches')}
-
- );
+ const handleEditCollectionName = (value) => {
+ if (value === name) {
+ return;
}
- return null;
- }
+ dispatch(editCollection(id, { name: value }));
+ };
- _getButtonLabel = (fieldName, displayName) => {
- const { field, direction } = this.props.sorting;
- let buttonLabel;
- if (field !== fieldName) {
- if (field === 'name') {
- buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
- displayName
- });
- } else {
- buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
- displayName
- });
- }
- } else if (direction === SortingActions.DIRECTION.ASC) {
- buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
- displayName
- });
- } else {
- buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
- displayName
- });
+ const handleEditCollectionDescription = (value) => {
+ if (value === description) {
+ return;
}
- return buttonLabel;
+ dispatch(editCollection(id, { description: value }));
};
- _renderFieldHeader(fieldName, displayName) {
- const { field, direction } = this.props.sorting;
- const headerClass = classNames({
- arrowDown: true,
- 'sketches-table__header--selected': field === fieldName
- });
- const buttonLabel = this._getButtonLabel(fieldName, displayName);
- return (
-
- |
- );
- }
-
- render() {
- const title = this.hasCollection() ? this.getCollectionName() : null;
- const isOwner = this.isOwner();
-
- return (
-
-
-
- {this.getTitle()}
-
- {this._renderLoader()}
- {this.hasCollection() && this._renderCollectionMetadata()}
-
-
- {this._renderEmptyTable()}
- {this.hasCollectionItems() && (
-
-
-
- {this._renderFieldHeader(
- 'name',
- this.props.t('Collection.HeaderName')
- )}
- {this._renderFieldHeader(
- 'createdAt',
- this.props.t('Collection.HeaderCreatedAt')
- )}
- {this._renderFieldHeader(
- 'user',
- this.props.t('Collection.HeaderUser')
- )}
- |
-
-
-
- {this.props.collection.items.map((item) => (
-
- ))}
-
-
- )}
- {this.state.isAddingSketches && (
-
}
- closeOverlay={this.hideAddSketches}
- isFixedHeight
- >
-
-
- )}
-
-
-
-
- );
- }
-}
+
-Collection.propTypes = {
- user: PropTypes.shape({
- username: PropTypes.string,
- authenticated: PropTypes.bool.isRequired
- }).isRequired,
- getCollections: PropTypes.func.isRequired,
- collection: PropTypes.shape({
- id: PropTypes.string,
- name: PropTypes.string,
- slug: PropTypes.string,
- description: PropTypes.string,
- owner: PropTypes.shape({
- username: PropTypes.string
- }).isRequired,
- items: PropTypes.arrayOf(PropTypes.shape({}))
- }),
- 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,
- direction: PropTypes.string.isRequired
- }).isRequired,
- t: PropTypes.func.isRequired
-};
+
+ {t('Collection.By')}
+ {username}
+
-Collection.defaultProps = {
- username: undefined,
- collection: {
- id: undefined,
- items: [],
- owner: {
- username: undefined
- }
- }
-};
-
-function mapStateToProps(state, ownProps) {
- return {
- user: state.user,
- collection: getCollection(state, ownProps.collectionId),
- sorting: state.sorting,
- loading: state.loading,
- project: state.project
- };
-}
+
+ {t('Collection.NumSketches', { count: items.length })}
+
+
-function mapDispatchToProps(dispatch) {
- return bindActionCreators(
- Object.assign(
- {},
- CollectionsActions,
- ProjectsActions,
- ToastActions,
- SortingActions
- ),
- dispatch
+
+
+ {isOwner && (
+
+ )}
+
+
+ {isAddingSketches && (
+ }
+ closeOverlay={() => setIsAddingSketches(false)}
+ isFixedHeight
+ >
+
+
+ )}
+
);
}
-export default withTranslation()(
- connect(mapStateToProps, mapDispatchToProps)(Collection)
-);
+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
index 563fd7d6ff..c4e0bba915 100644
--- a/client/modules/User/components/CollectionShareButton.jsx
+++ b/client/modules/User/components/CollectionShareButton.jsx
@@ -1,32 +1,10 @@
import PropTypes from 'prop-types';
-import React, { useState, useRef, useEffect } from 'react';
-import { Helmet } from 'react-helmet';
-import { connect } from 'react-redux';
-import { Link } from 'react-router';
-import { bindActionCreators } from 'redux';
-import { useTranslation, withTranslation } from 'react-i18next';
-import classNames from 'classnames';
+import React, { useEffect, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
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';
-import * as ToastActions from '../../IDE/actions/toast';
-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);
@@ -34,7 +12,7 @@ const ShareURL = ({ value }) => {
const { t } = useTranslation();
const handleClickOutside = (e) => {
- if (node.current.contains(e.target)) {
+ if (node.current?.contains(e.target)) {
return;
}
setShowURL(false);
@@ -73,481 +51,4 @@ ShareURL.propTypes = {
value: PropTypes.string.isRequired
};
-const CollectionItemRowBase = ({
- collection,
- item,
- isOwner,
- removeFromCollection
-}) => {
- const { t } = useTranslation();
-
- const projectIsDeleted = item.isDeleted;
-
- const handleSketchRemove = () => {
- const name = projectIsDeleted ? 'deleted sketch' : item.project.name;
-
- if (
- window.confirm(
- t('Collection.DeleteFromCollection', { name_sketch: name })
- )
- ) {
- removeFromCollection(collection.id, item.projectId);
- }
- };
-
- const name = projectIsDeleted ? (
- {t('Collection.SketchDeleted')}
- ) : (
-
- {item.project.name}
-
- );
-
- const sketchOwnerUsername = projectIsDeleted
- ? null
- : item.project.user.username;
-
- return (
-
- {name} |
- {dates.format(item.createdAt)} |
- {sketchOwnerUsername} |
-
- {isOwner && (
-
- )}
- |
-
- );
-};
-
-CollectionItemRowBase.propTypes = {
- collection: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }).isRequired,
- item: PropTypes.shape({
- createdAt: PropTypes.string.isRequired,
- projectId: PropTypes.string.isRequired,
- isDeleted: PropTypes.bool.isRequired,
- project: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- user: PropTypes.shape({
- username: PropTypes.string.isRequired
- })
- }).isRequired
- }).isRequired,
- isOwner: PropTypes.bool.isRequired,
- user: PropTypes.shape({
- username: PropTypes.string,
- authenticated: PropTypes.bool.isRequired
- }).isRequired,
- removeFromCollection: PropTypes.func.isRequired
-};
-
-function mapDispatchToPropsSketchListRow(dispatch) {
- return bindActionCreators(
- Object.assign({}, CollectionsActions, ProjectActions, IdeActions),
- dispatch
- );
-}
-
-const CollectionItemRow = connect(
- null,
- mapDispatchToPropsSketchListRow
-)(CollectionItemRowBase);
-
-class Collection extends React.Component {
- constructor(props) {
- super(props);
- 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() {
- if (this.props.username === this.props.user.username) {
- return this.props.t('Collection.Title');
- }
- return this.props.t('Collection.AnothersTitle', {
- anotheruser: this.props.username
- });
- }
-
- getUsername() {
- return this.props.username !== undefined
- ? this.props.username
- : this.props.user.username;
- }
-
- getCollectionName() {
- return this.props.collection.name;
- }
-
- isOwner() {
- let isOwner = false;
-
- if (
- this.props.user != null &&
- this.props.user.username &&
- this.props.collection.owner.username === this.props.user.username
- ) {
- isOwner = true;
- }
-
- return isOwner;
- }
-
- hasCollection() {
- return !this.props.loading && this.props.collection != null;
- }
-
- hasCollectionItems() {
- return this.hasCollection() && this.props.collection.items.length > 0;
- }
-
- _renderLoader() {
- if (this.props.loading) return ;
- 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() {
- const isLoading = this.props.loading;
- const hasCollectionItems =
- this.props.collection != null && this.props.collection.items.length > 0;
-
- if (!isLoading && !hasCollectionItems) {
- return (
-
- {this.props.t('Collection.NoSketches')}
-
- );
- }
- return null;
- }
-
- _getButtonLabel = (fieldName, displayName) => {
- const { field, direction } = this.props.sorting;
- let buttonLabel;
- if (field !== fieldName) {
- if (field === 'name') {
- buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
- displayName
- });
- } else {
- buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
- displayName
- });
- }
- } else if (direction === SortingActions.DIRECTION.ASC) {
- buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', {
- displayName
- });
- } else {
- buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', {
- displayName
- });
- }
- return buttonLabel;
- };
-
- _renderFieldHeader(fieldName, displayName) {
- const { field, direction } = this.props.sorting;
- const headerClass = classNames({
- arrowDown: true,
- 'sketches-table__header--selected': field === fieldName
- });
- const buttonLabel = this._getButtonLabel(fieldName, displayName);
- return (
-
-
- |
- );
- }
-
- render() {
- const title = this.hasCollection() ? this.getCollectionName() : null;
- const isOwner = this.isOwner();
-
- return (
-
-
-
- {this.getTitle()}
-
- {this._renderLoader()}
- {this.hasCollection() && this._renderCollectionMetadata()}
-
-
- {this._renderEmptyTable()}
- {this.hasCollectionItems() && (
-
-
-
- {this._renderFieldHeader(
- 'name',
- this.props.t('Collection.HeaderName')
- )}
- {this._renderFieldHeader(
- 'createdAt',
- this.props.t('Collection.HeaderCreatedAt')
- )}
- {this._renderFieldHeader(
- 'user',
- this.props.t('Collection.HeaderUser')
- )}
- |
-
-
-
- {this.props.collection.items.map((item) => (
-
- ))}
-
-
- )}
- {this.state.isAddingSketches && (
-
}
- closeOverlay={this.hideAddSketches}
- isFixedHeight
- >
-
-
- )}
-
-
-
-
- );
- }
-}
-
-Collection.propTypes = {
- user: PropTypes.shape({
- username: PropTypes.string,
- authenticated: PropTypes.bool.isRequired
- }).isRequired,
- getCollections: PropTypes.func.isRequired,
- collection: PropTypes.shape({
- id: PropTypes.string,
- name: PropTypes.string,
- slug: PropTypes.string,
- description: PropTypes.string,
- owner: PropTypes.shape({
- username: PropTypes.string
- }).isRequired,
- items: PropTypes.arrayOf(PropTypes.shape({}))
- }),
- 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,
- direction: PropTypes.string.isRequired
- }).isRequired,
- t: PropTypes.func.isRequired
-};
-
-Collection.defaultProps = {
- username: undefined,
- collection: {
- id: undefined,
- items: [],
- owner: {
- username: undefined
- }
- }
-};
-
-function mapStateToProps(state, ownProps) {
- return {
- user: state.user,
- collection: getCollection(state, ownProps.collectionId),
- sorting: state.sorting,
- loading: state.loading,
- project: state.project
- };
-}
-
-function mapDispatchToProps(dispatch) {
- return bindActionCreators(
- Object.assign(
- {},
- CollectionsActions,
- ProjectsActions,
- ToastActions,
- SortingActions
- ),
- dispatch
- );
-}
-
-export default withTranslation()(
- connect(mapStateToProps, mapDispatchToProps)(Collection)
-);
+export default ShareURL;
From 17376571d337babdeaaf4b74a99b54802769ddbe Mon Sep 17 00:00:00 2001
From: Linda Paiste
Date: Mon, 7 Aug 2023 19:21:32 -0500
Subject: [PATCH 3/3] Update Link import for react-router v5.
---
client/modules/User/components/CollectionMetadata.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/modules/User/components/CollectionMetadata.jsx b/client/modules/User/components/CollectionMetadata.jsx
index 108a40850d..4b1be7ff5f 100644
--- a/client/modules/User/components/CollectionMetadata.jsx
+++ b/client/modules/User/components/CollectionMetadata.jsx
@@ -3,7 +3,7 @@ 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';
+import { Link } from 'react-router-dom';
import Button from '../../../common/Button';
import Overlay from '../../App/components/Overlay';
import { editCollection } from '../../IDE/actions/collections';