Merge branch 'develop' into feature/switch-mobile-desktop
This commit is contained in:
		
						commit
						8b619c6673
					
				
					 16 changed files with 402 additions and 129 deletions
				
			
		|  | @ -1,6 +1,7 @@ | |||
| import PropTypes from 'prop-types'; | ||||
| import React from 'react'; | ||||
| import { browserHistory } from 'react-router'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| 
 | ||||
| import ExitIcon from '../../../images/exit.svg'; | ||||
| 
 | ||||
|  | @ -80,7 +81,7 @@ class Overlay extends React.Component { | |||
|               <h2 className="overlay__title">{title}</h2> | ||||
|               <div className="overlay__actions"> | ||||
|                 {actions} | ||||
|                 <button className="overlay__close-button" onClick={this.close} aria-label={`Close ${title} overlay`} > | ||||
|                 <button className="overlay__close-button" onClick={this.close} aria-label={this.props.t('Overlay.AriaLabel', { title })}> | ||||
|                   <ExitIcon focusable="false" aria-hidden="true" /> | ||||
|                 </button> | ||||
|               </div> | ||||
|  | @ -101,6 +102,7 @@ Overlay.propTypes = { | |||
|   ariaLabel: PropTypes.string, | ||||
|   previousPath: PropTypes.string, | ||||
|   isFixedHeight: PropTypes.bool, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| Overlay.defaultProps = { | ||||
|  | @ -113,4 +115,4 @@ Overlay.defaultProps = { | |||
|   isFixedHeight: false, | ||||
| }; | ||||
| 
 | ||||
| export default Overlay; | ||||
| export default withTranslation()(Overlay); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import React from 'react'; | |||
| import { Helmet } from 'react-helmet'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| 
 | ||||
| import * as ProjectActions from '../actions/project'; | ||||
| import * as ProjectsActions from '../actions/projects'; | ||||
|  | @ -42,9 +43,9 @@ class CollectionList extends React.Component { | |||
| 
 | ||||
|   getTitle() { | ||||
|     if (this.props.username === this.props.user.username) { | ||||
|       return 'p5.js Web Editor | My collections'; | ||||
|       return this.props.t('AddToCollectionList.Title'); | ||||
|     } | ||||
|     return `p5.js Web Editor | ${this.props.username}'s collections`; | ||||
|     return this.props.t('AddToCollectionList.AnothersTitle', { anotheruser: this.props.username }); | ||||
|   } | ||||
| 
 | ||||
|   handleCollectionAdd = (collection) => { | ||||
|  | @ -74,10 +75,11 @@ class CollectionList extends React.Component { | |||
|           items={collectionWithSketchStatus} | ||||
|           onAdd={this.handleCollectionAdd} | ||||
|           onRemove={this.handleCollectionRemove} | ||||
|           t={this.props.t} | ||||
|         /> | ||||
|       ); | ||||
|     } else { | ||||
|       content = 'No collections'; | ||||
|       content = this.props.t('AddToCollectionList.Empty'); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|  | @ -135,7 +137,8 @@ CollectionList.propTypes = { | |||
|     owner: PropTypes.shape({ | ||||
|       id: PropTypes.string | ||||
|     }) | ||||
|   }) | ||||
|   }), | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| CollectionList.defaultProps = { | ||||
|  | @ -164,4 +167,4 @@ function mapDispatchToProps(dispatch) { | |||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(CollectionList); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(CollectionList)); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import React from 'react'; | |||
| import { Helmet } from 'react-helmet'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| // import find from 'lodash/find'; | ||||
| import * as ProjectsActions from '../actions/projects'; | ||||
| import * as CollectionsActions from '../actions/collections'; | ||||
|  | @ -32,9 +33,9 @@ class SketchList extends React.Component { | |||
| 
 | ||||
|   getSketchesTitle() { | ||||
|     if (this.props.username === this.props.user.username) { | ||||
|       return 'p5.js Web Editor | My sketches'; | ||||
|       return this.props.t('AddToCollectionSketchList.Title'); | ||||
|     } | ||||
|     return `p5.js Web Editor | ${this.props.username}'s sketches`; | ||||
|     return this.props.t('AddToCollectionSketchList.AnothersTitle', { anotheruser: this.props.username }); | ||||
|   } | ||||
| 
 | ||||
|   handleCollectionAdd = (sketch) => { | ||||
|  | @ -68,7 +69,7 @@ class SketchList extends React.Component { | |||
|         /> | ||||
|       ); | ||||
|     } else { | ||||
|       content = 'No collections'; | ||||
|       content = this.props.t('AddToCollectionSketchList.NoCollections'); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|  | @ -113,6 +114,7 @@ SketchList.propTypes = { | |||
|   }).isRequired, | ||||
|   addToCollection: PropTypes.func.isRequired, | ||||
|   removeFromCollection: PropTypes.func.isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| SketchList.defaultProps = { | ||||
|  | @ -136,4 +138,4 @@ function mapDispatchToProps(dispatch) { | |||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(SketchList); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(SketchList)); | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import PropTypes from 'prop-types'; | ||||
| import React from 'react'; | ||||
| import { Helmet } from 'react-helmet'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import classNames from 'classnames'; | ||||
|  | @ -50,9 +51,9 @@ class CollectionList extends React.Component { | |||
| 
 | ||||
|   getTitle() { | ||||
|     if (this.props.username === this.props.user.username) { | ||||
|       return 'p5.js Web Editor | My collections'; | ||||
|       return this.props.t('CollectionList.Title'); | ||||
|     } | ||||
|     return `p5.js Web Editor | ${this.props.username}'s collections`; | ||||
|     return this.props.t('CollectionList.AnothersTitle', { anotheruser: this.props.username }); | ||||
|   } | ||||
| 
 | ||||
|   showAddSketches = (collectionId) => { | ||||
|  | @ -78,7 +79,7 @@ class CollectionList extends React.Component { | |||
| 
 | ||||
|   _renderEmptyTable() { | ||||
|     if (!this.props.loading && this.props.collections.length === 0) { | ||||
|       return (<p className="sketches-table__empty">No collections.</p>); | ||||
|       return (<p className="sketches-table__empty">{this.props.t('CollectionList.NoCollections')}</p>); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | @ -88,14 +89,14 @@ class CollectionList extends React.Component { | |||
|     let buttonLabel; | ||||
|     if (field !== fieldName) { | ||||
|       if (field === 'name') { | ||||
|         buttonLabel = `Sort by ${displayName} ascending.`; | ||||
|         buttonLabel = this.props.t('CollectionList.ButtonLabelAscendingARIA', { displayName }); | ||||
|       } else { | ||||
|         buttonLabel = `Sort by ${displayName} descending.`; | ||||
|         buttonLabel = this.props.t('CollectionList.ButtonLabelDescendingARIA', { displayName }); | ||||
|       } | ||||
|     } else if (direction === SortingActions.DIRECTION.ASC) { | ||||
|       buttonLabel = `Sort by ${displayName} descending.`; | ||||
|       buttonLabel = this.props.t('CollectionList.ButtonLabelDescendingARIA', { displayName }); | ||||
|     } else { | ||||
|       buttonLabel = `Sort by ${displayName} ascending.`; | ||||
|       buttonLabel = this.props.t('CollectionList.ButtonLabelAscendingARIA', { displayName }); | ||||
|     } | ||||
|     return buttonLabel; | ||||
|   } | ||||
|  | @ -116,10 +117,10 @@ class CollectionList extends React.Component { | |||
|         > | ||||
|           <span className={headerClass}>{displayName}</span> | ||||
|           {field === fieldName && direction === SortingActions.DIRECTION.ASC && | ||||
|             <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" /> | ||||
|             <ArrowUpIcon role="img" aria-label={this.props.t('CollectionList.DirectionAscendingARIA')} focusable="false" /> | ||||
|           } | ||||
|           {field === fieldName && direction === SortingActions.DIRECTION.DESC && | ||||
|             <ArrowDownIcon role="img" aria-label="Descending" focusable="false" /> | ||||
|             <ArrowDownIcon role="img" aria-label={this.props.t('CollectionList.DirectionDescendingARIA')} focusable="false" /> | ||||
|           } | ||||
|         </button> | ||||
|       </th> | ||||
|  | @ -142,10 +143,10 @@ class CollectionList extends React.Component { | |||
|           <table className="sketches-table" summary="table containing all collections"> | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 {this._renderFieldHeader('name', 'Name')} | ||||
|                 {this._renderFieldHeader('createdAt', `${mobile ? '' : 'Date '}Created`)} | ||||
|                 {this._renderFieldHeader('updatedAt', `${mobile ? '' : 'Date '}Updated`)} | ||||
|                 {this._renderFieldHeader('numItems', mobile ? 'Sketches' : '# sketches')} | ||||
|                 {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' : '' }))} | ||||
|                 <th scope="col"></th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|  | @ -165,7 +166,7 @@ class CollectionList extends React.Component { | |||
|         { | ||||
|           this.state.addingSketchesToCollectionId && ( | ||||
|             <Overlay | ||||
|               title="Add sketch" | ||||
|               title={this.props.t('CollectionList.AddSketch')} | ||||
|               actions={<SketchSearchbar />} | ||||
|               closeOverlay={this.hideAddSketches} | ||||
|               isFixedHeight | ||||
|  | @ -211,6 +212,7 @@ CollectionList.propTypes = { | |||
|       id: PropTypes.string | ||||
|     }) | ||||
|   }), | ||||
|   t: PropTypes.func.isRequired, | ||||
|   mobile: PropTypes.bool, | ||||
| }; | ||||
| 
 | ||||
|  | @ -242,4 +244,4 @@ function mapDispatchToProps(dispatch) { | |||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(CollectionList); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(CollectionList)); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import React from 'react'; | |||
| import { connect } from 'react-redux'; | ||||
| import { Link } from 'react-router'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| import * as ProjectActions from '../../actions/project'; | ||||
| import * as CollectionsActions from '../../actions/collections'; | ||||
| import * as IdeActions from '../../actions/ide'; | ||||
|  | @ -81,7 +82,7 @@ class CollectionListRowBase extends React.Component { | |||
| 
 | ||||
|   handleCollectionDelete = () => { | ||||
|     this.closeAll(); | ||||
|     if (window.confirm(`Are you sure you want to delete "${this.props.collection.name}"?`)) { | ||||
|     if (window.confirm(this.props.t('Common.DeleteConfirmation', { name: this.props.collection.name }))) { | ||||
|       this.props.deleteCollection(this.props.collection.id); | ||||
|     } | ||||
|   } | ||||
|  | @ -130,7 +131,7 @@ class CollectionListRowBase extends React.Component { | |||
|           onClick={this.toggleOptions} | ||||
|           onBlur={this.onBlurComponent} | ||||
|           onFocus={this.onFocusComponent} | ||||
|           aria-label="Toggle Open/Close collection options" | ||||
|           aria-label={this.props.t('CollectionListRow.ToggleCollectionOptionsARIA')} | ||||
|         > | ||||
|           <DownFilledTriangleIcon title="Menu" /> | ||||
|         </button> | ||||
|  | @ -145,7 +146,7 @@ class CollectionListRowBase extends React.Component { | |||
|                 onBlur={this.onBlurComponent} | ||||
|                 onFocus={this.onFocusComponent} | ||||
|               > | ||||
|                 Add sketch | ||||
|                 {this.props.t('CollectionListRow.AddSketch')} | ||||
|               </button> | ||||
|             </li> | ||||
|             {userIsOwner && | ||||
|  | @ -156,7 +157,7 @@ class CollectionListRowBase extends React.Component { | |||
|                   onBlur={this.onBlurComponent} | ||||
|                   onFocus={this.onFocusComponent} | ||||
|                 > | ||||
|                   Delete | ||||
|                   {this.props.t('CollectionListRow.Delete')} | ||||
|                 </button> | ||||
|               </li>} | ||||
|             {userIsOwner && | ||||
|  | @ -167,7 +168,7 @@ class CollectionListRowBase extends React.Component { | |||
|                   onBlur={this.onBlurComponent} | ||||
|                   onFocus={this.onFocusComponent} | ||||
|                 > | ||||
|                   Rename | ||||
|                   {this.props.t('CollectionListRow.Rename')} | ||||
|                 </button> | ||||
|               </li>} | ||||
|           </ul> | ||||
|  | @ -248,6 +249,7 @@ CollectionListRowBase.propTypes = { | |||
|   editCollection: PropTypes.func.isRequired, | ||||
|   onAddSketches: PropTypes.func.isRequired, | ||||
|   mobile: PropTypes.bool, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| CollectionListRowBase.defaultProps = { | ||||
|  | @ -258,4 +260,4 @@ function mapDispatchToPropsSketchListRow(dispatch) { | |||
|   return bindActionCreators(Object.assign({}, CollectionsActions, ProjectActions, IdeActions, ToastActions), dispatch); | ||||
| } | ||||
| 
 | ||||
| export default connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase); | ||||
| export default withTranslation()(connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase)); | ||||
|  |  | |||
|  | @ -1,11 +1,12 @@ | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { Link } from 'react-router'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| 
 | ||||
| import Icons from './Icons'; | ||||
| 
 | ||||
| const Item = ({ | ||||
|   isAdded, onSelect, name, url | ||||
|   isAdded, onSelect, name, url, t | ||||
| }) => { | ||||
|   const buttonLabel = isAdded ? 'Remove from collection' : 'Add to collection'; | ||||
|   return ( | ||||
|  | @ -20,7 +21,7 @@ const Item = ({ | |||
|         target="_blank" | ||||
|         onClick={e => e.stopPropogation()} | ||||
|       > | ||||
|         View | ||||
|         {t('QuickAddList.View')} | ||||
|       </Link> | ||||
|     </li> | ||||
|   ); | ||||
|  | @ -37,10 +38,11 @@ Item.propTypes = { | |||
|   url: PropTypes.string.isRequired, | ||||
|   isAdded: PropTypes.bool.isRequired, | ||||
|   onSelect: PropTypes.func.isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| const QuickAddList = ({ | ||||
|   items, onAdd, onRemove | ||||
|   items, onAdd, onRemove, t | ||||
| }) => { | ||||
|   const handleAction = (item) => { | ||||
|     if (item.isAdded) { | ||||
|  | @ -53,6 +55,7 @@ const QuickAddList = ({ | |||
|   return ( | ||||
|     <ul className="quick-add">{items.map(item => (<Item | ||||
|       key={item.id} | ||||
|       t={t} | ||||
|       {...item} | ||||
|       onSelect={ | ||||
|         (event) => { | ||||
|  | @ -70,6 +73,7 @@ QuickAddList.propTypes = { | |||
|   items: PropTypes.arrayOf(ItemType).isRequired, | ||||
|   onAdd: PropTypes.func.isRequired, | ||||
|   onRemove: PropTypes.func.isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| export default QuickAddList; | ||||
| export default withTranslation()(QuickAddList); | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import format from 'date-fns/format'; | |||
| import PropTypes from 'prop-types'; | ||||
| import React from 'react'; | ||||
| import { Helmet } from 'react-helmet'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { Link } from 'react-router'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
|  | @ -148,14 +149,14 @@ class SketchListRowBase extends React.Component { | |||
| 
 | ||||
|   handleSketchDelete = () => { | ||||
|     this.closeAll(); | ||||
|     if (window.confirm(`Are you sure you want to delete "${this.props.sketch.name}"?`)) { | ||||
|     if (window.confirm(this.props.t('Common.DeleteConfirmation', { name: this.props.sketch.name }))) { | ||||
|       this.props.deleteProject(this.props.sketch.id); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   renderViewButton = sketchURL => ( | ||||
|     <td className="sketch-list__dropdown-column"> | ||||
|       <Link to={sketchURL}>View</Link> | ||||
|       <Link to={sketchURL}>{this.props.t('SketchList.View')}</Link> | ||||
|     </td> | ||||
|   ) | ||||
| 
 | ||||
|  | @ -170,7 +171,7 @@ class SketchListRowBase extends React.Component { | |||
|           onClick={this.toggleOptions} | ||||
|           onBlur={this.onBlurComponent} | ||||
|           onFocus={this.onFocusComponent} | ||||
|           aria-label="Toggle Open/Close Sketch Options" | ||||
|           aria-label={this.props.t('SketchList.ToggleLabelARIA')} | ||||
|         > | ||||
|           <DownFilledTriangleIcon focusable="false" aria-hidden="true" /> | ||||
|         </button> | ||||
|  | @ -186,7 +187,7 @@ class SketchListRowBase extends React.Component { | |||
|                 onBlur={this.onBlurComponent} | ||||
|                 onFocus={this.onFocusComponent} | ||||
|               > | ||||
|                 Rename | ||||
|                 {this.props.t('SketchList.DropdownRename')} | ||||
|               </button> | ||||
|             </li>} | ||||
|             <li> | ||||
|  | @ -196,7 +197,7 @@ class SketchListRowBase extends React.Component { | |||
|                 onBlur={this.onBlurComponent} | ||||
|                 onFocus={this.onFocusComponent} | ||||
|               > | ||||
|                 Download | ||||
|                 {this.props.t('SketchList.DropdownDownload')} | ||||
|               </button> | ||||
|             </li> | ||||
|             {this.props.user.authenticated && | ||||
|  | @ -207,7 +208,7 @@ class SketchListRowBase extends React.Component { | |||
|                 onBlur={this.onBlurComponent} | ||||
|                 onFocus={this.onFocusComponent} | ||||
|               > | ||||
|                 Duplicate | ||||
|                 {this.props.t('SketchList.DropdownDuplicate')} | ||||
|               </button> | ||||
|             </li>} | ||||
|             {this.props.user.authenticated && | ||||
|  | @ -221,7 +222,7 @@ class SketchListRowBase extends React.Component { | |||
|                   onBlur={this.onBlurComponent} | ||||
|                   onFocus={this.onFocusComponent} | ||||
|                 > | ||||
|                   Add to collection | ||||
|                   {this.props.t('SketchList.DropdownAddToCollection')} | ||||
|                 </button> | ||||
|               </li>} | ||||
|             { /* <li> | ||||
|  | @ -242,7 +243,7 @@ class SketchListRowBase extends React.Component { | |||
|                 onBlur={this.onBlurComponent} | ||||
|                 onFocus={this.onFocusComponent} | ||||
|               > | ||||
|                 Delete | ||||
|                 {this.props.t('SketchList.DropdownDelete')} | ||||
|               </button> | ||||
|             </li>} | ||||
|           </ul>} | ||||
|  | @ -317,7 +318,8 @@ SketchListRowBase.propTypes = { | |||
|   exportProjectAsZip: PropTypes.func.isRequired, | ||||
|   changeProjectName: PropTypes.func.isRequired, | ||||
|   onAddToCollection: PropTypes.func.isRequired, | ||||
|   mobile: PropTypes.bool | ||||
|   mobile: PropTypes.bool, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| SketchListRowBase.defaultProps = { | ||||
|  | @ -352,9 +354,9 @@ class SketchList extends React.Component { | |||
| 
 | ||||
|   getSketchesTitle() { | ||||
|     if (this.props.username === this.props.user.username) { | ||||
|       return 'p5.js Web Editor | My sketches'; | ||||
|       return this.props.t('SketchList.Title'); | ||||
|     } | ||||
|     return `p5.js Web Editor | ${this.props.username}'s sketches`; | ||||
|     return this.props.t('SketchList.AnothersTitle', { anotheruser: this.props.username }); | ||||
|   } | ||||
| 
 | ||||
|   hasSketches() { | ||||
|  | @ -372,7 +374,7 @@ class SketchList extends React.Component { | |||
| 
 | ||||
|   _renderEmptyTable() { | ||||
|     if (!this.isLoading() && this.props.sketches.length === 0) { | ||||
|       return (<p className="sketches-table__empty">No sketches.</p>); | ||||
|       return (<p className="sketches-table__empty">{this.props.t('SketchList.NoSketches')}</p>); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | @ -382,14 +384,14 @@ class SketchList extends React.Component { | |||
|     let buttonLabel; | ||||
|     if (field !== fieldName) { | ||||
|       if (field === 'name') { | ||||
|         buttonLabel = `Sort by ${displayName} ascending.`; | ||||
|         buttonLabel = this.props.t('SketchList.ButtonLabelAscendingARIA', { displayName }); | ||||
|       } else { | ||||
|         buttonLabel = `Sort by ${displayName} descending.`; | ||||
|         buttonLabel = this.props.t('SketchList.ButtonLabelDescendingARIA', { displayName }); | ||||
|       } | ||||
|     } else if (direction === SortingActions.DIRECTION.ASC) { | ||||
|       buttonLabel = `Sort by ${displayName} descending.`; | ||||
|       buttonLabel = this.props.t('SketchList.ButtonLabelDescendingARIA', { displayName }); | ||||
|     } else { | ||||
|       buttonLabel = `Sort by ${displayName} ascending.`; | ||||
|       buttonLabel = this.props.t('SketchList.ButtonLabelAscendingARIA', { displayName }); | ||||
|     } | ||||
|     return buttonLabel; | ||||
|   } | ||||
|  | @ -410,10 +412,10 @@ class SketchList extends React.Component { | |||
|         > | ||||
|           <span className={headerClass}>{displayName}</span> | ||||
|           {field === fieldName && direction === SortingActions.DIRECTION.ASC && | ||||
|             <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" /> | ||||
|             <ArrowUpIcon role="img" aria-label={this.props.t('SketchList.DirectionAscendingARIA')} focusable="false" /> | ||||
|           } | ||||
|           {field === fieldName && direction === SortingActions.DIRECTION.DESC && | ||||
|             <ArrowDownIcon role="img" aria-label="Descending" focusable="false" /> | ||||
|             <ArrowDownIcon role="img" aria-label={this.props.t('SketchList.DirectionDescendingARIA')} focusable="false" /> | ||||
|           } | ||||
|         </button> | ||||
|       </th> | ||||
|  | @ -434,9 +436,9 @@ class SketchList extends React.Component { | |||
|           <table className="sketches-table" summary="table containing all saved projects"> | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 {this._renderFieldHeader('name', 'Sketch')} | ||||
|                 {this._renderFieldHeader('createdAt', `${mobile ? '' : 'Date '}Created`)} | ||||
|                 {this._renderFieldHeader('updatedAt', `${mobile ? '' : 'Date '}Updated`)} | ||||
|                 {this._renderFieldHeader('name', this.props.t('SketchList.HeaderName'))} | ||||
|                 {this._renderFieldHeader('createdAt', this.props.t('SketchList.HeaderCreatedAt', { context: mobile ? 'mobile' : '' }))} | ||||
|                 {this._renderFieldHeader('updatedAt', this.props.t('SketchList.HeaderUpdatedAt', { context: mobile ? 'mobile' : '' }))} | ||||
|                 <th scope="col"></th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|  | @ -451,6 +453,7 @@ class SketchList extends React.Component { | |||
|                   onAddToCollection={() => { | ||||
|                     this.setState({ sketchToAddToCollection: sketch }); | ||||
|                   }} | ||||
|                   t={this.props.t} | ||||
|                 />))} | ||||
|             </tbody> | ||||
|           </table>} | ||||
|  | @ -458,7 +461,7 @@ class SketchList extends React.Component { | |||
|           this.state.sketchToAddToCollection && | ||||
|             <Overlay | ||||
|               isFixedHeight | ||||
|               title="Add to collection" | ||||
|               title={this.props.t('SketchList.AddToCollectionOverlayTitle')} | ||||
|               closeOverlay={() => this.setState({ sketchToAddToCollection: null })} | ||||
|             > | ||||
|               <AddToCollectionList | ||||
|  | @ -494,6 +497,7 @@ SketchList.propTypes = { | |||
|     direction: PropTypes.string.isRequired | ||||
|   }).isRequired, | ||||
|   mobile: PropTypes.bool, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| SketchList.defaultProps = { | ||||
|  | @ -518,4 +522,4 @@ function mapDispatchToProps(dispatch) { | |||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(SketchList); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(SketchList)); | ||||
|  |  | |||
|  | @ -457,15 +457,15 @@ class IDEView extends React.Component { | |||
|           <Overlay | ||||
|             title={this.props.t('IDEView.SubmitFeedback')} | ||||
|             previousPath={this.props.ide.previousPath} | ||||
|             ariaLabel="submit-feedback" | ||||
|             ariaLabel={this.props.t('IDEView.SubmitFeedbackARIA')} | ||||
|           > | ||||
|             <Feedback previousPath={this.props.ide.previousPath} /> | ||||
|           </Overlay> | ||||
|         )} | ||||
|         {this.props.location.pathname.match(/add-to-collection$/) && ( | ||||
|           <Overlay | ||||
|             ariaLabel="add to collection" | ||||
|             title="Add to collection" | ||||
|             ariaLabel={this.props.t('IDEView.AddCollectionARIA')} | ||||
|             title={this.props.t('IDEView.AddCollectionTitle')} | ||||
|             previousPath={this.props.ide.previousPath} | ||||
|             actions={<CollectionSearchbar />} | ||||
|             isFixedHeight | ||||
|  | @ -479,8 +479,8 @@ class IDEView extends React.Component { | |||
|         )} | ||||
|         {this.props.ide.shareModalVisible && ( | ||||
|           <Overlay | ||||
|             title="Share" | ||||
|             ariaLabel="share" | ||||
|             title={this.props.t('IDEView.ShareTitle')} | ||||
|             ariaLabel={this.props.t('IDEView.ShareARIA')} | ||||
|             closeOverlay={this.props.closeShareModal} | ||||
|           > | ||||
|             <ShareModal | ||||
|  | @ -501,8 +501,8 @@ class IDEView extends React.Component { | |||
|         )} | ||||
|         {this.props.ide.errorType && ( | ||||
|           <Overlay | ||||
|             title="Error" | ||||
|             ariaLabel={this.props.t('Common.Error')} | ||||
|             title={this.props.t('Common.Error')} | ||||
|             ariaLabel={this.props.t('Common.ErrorARIA')} | ||||
|             closeOverlay={this.props.hideErrorModal} | ||||
|           > | ||||
|             <ErrorModal | ||||
|  |  | |||
|  | @ -229,7 +229,7 @@ export function updateSettings(formValues) { | |||
|         dispatch(updateSettingsSuccess(response.data)); | ||||
|         browserHistory.push('/'); | ||||
|         dispatch(showToast(5500)); | ||||
|         dispatch(setToastText('Settings saved.')); | ||||
|         dispatch(setToastText('Toast.SettingsSaved')); | ||||
|       }) | ||||
|       .catch((error) => { | ||||
|         const { response } = error; | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import { Helmet } from 'react-helmet'; | |||
| import { connect } from 'react-redux'; | ||||
| import { Link } from 'react-router'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| import classNames from 'classnames'; | ||||
| 
 | ||||
| import Button from '../../../common/Button'; | ||||
|  | @ -27,7 +28,7 @@ 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 ShareURL = ({ value, t }) => { | ||||
|   const [showURL, setShowURL] = useState(false); | ||||
|   const node = useRef(); | ||||
| 
 | ||||
|  | @ -56,11 +57,11 @@ const ShareURL = ({ value }) => { | |||
|         onClick={() => setShowURL(!showURL)} | ||||
|         iconAfter={<DropdownArrowIcon />} | ||||
|       > | ||||
|         Share | ||||
|         {t('Collection.Share')} | ||||
|       </Button> | ||||
|       { showURL && | ||||
|         <div className="collection__share-dropdown"> | ||||
|           <CopyableInput value={value} label="Link to Collection" /> | ||||
|           <CopyableInput value={value} label={t('Collection.URLLink')} /> | ||||
|         </div> | ||||
|       } | ||||
|     </div> | ||||
|  | @ -69,22 +70,23 @@ const ShareURL = ({ value }) => { | |||
| 
 | ||||
| ShareURL.propTypes = { | ||||
|   value: PropTypes.string.isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| const CollectionItemRowBase = ({ | ||||
|   collection, item, isOwner, removeFromCollection | ||||
|   collection, item, isOwner, removeFromCollection, t | ||||
| }) => { | ||||
|   const projectIsDeleted = item.isDeleted; | ||||
| 
 | ||||
|   const handleSketchRemove = () => { | ||||
|     const name = projectIsDeleted ? 'deleted sketch' : item.project.name; | ||||
| 
 | ||||
|     if (window.confirm(`Are you sure you want to remove "${name}" from this collection?`)) { | ||||
|     if (window.confirm(t('Collection.DeleteFromCollection', { name_sketch: name }))) { | ||||
|       removeFromCollection(collection.id, item.projectId); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const name = projectIsDeleted ? <span>Sketch was deleted</span> : ( | ||||
|   const name = projectIsDeleted ? <span>{t('Collection.SketchDeleted')}</span> : ( | ||||
|     <Link to={`/${item.project.user.username}/sketches/${item.projectId}`}> | ||||
|       {item.project.name} | ||||
|     </Link> | ||||
|  | @ -106,7 +108,7 @@ const CollectionItemRowBase = ({ | |||
|           <button | ||||
|             className="collection-row__remove-button" | ||||
|             onClick={handleSketchRemove} | ||||
|             aria-label="Remove sketch from collection" | ||||
|             aria-label={t('Collection.SketchRemoveARIA')} | ||||
|           > | ||||
|             <RemoveIcon focusable="false" aria-hidden="true" /> | ||||
|           </button> | ||||
|  | @ -138,7 +140,8 @@ CollectionItemRowBase.propTypes = { | |||
|     username: PropTypes.string, | ||||
|     authenticated: PropTypes.bool.isRequired | ||||
|   }).isRequired, | ||||
|   removeFromCollection: PropTypes.func.isRequired | ||||
|   removeFromCollection: PropTypes.func.isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| function mapDispatchToPropsSketchListRow(dispatch) { | ||||
|  | @ -163,9 +166,9 @@ class Collection extends React.Component { | |||
| 
 | ||||
|   getTitle() { | ||||
|     if (this.props.username === this.props.user.username) { | ||||
|       return 'p5.js Web Editor | My collections'; | ||||
|       return this.props.t('Collection.Title'); | ||||
|     } | ||||
|     return `p5.js Web Editor | ${this.props.username}'s collections`; | ||||
|     return this.props.t('Collection.AnothersTitle', { anotheruser: this.props.username }); | ||||
|   } | ||||
| 
 | ||||
|   getUsername() { | ||||
|  | @ -257,27 +260,27 @@ class Collection extends React.Component { | |||
|                     InputComponent="textarea" | ||||
|                     value={description} | ||||
|                     onChange={handleEditCollectionDescription} | ||||
|                     emptyPlaceholder="Add description" | ||||
|                     emptyPlaceholder={this.props.t('Collection.DescriptionPlaceholder')} | ||||
|                   /> : | ||||
|                   description | ||||
|               } | ||||
|             </p> | ||||
| 
 | ||||
|             <p className="collection-metadata__user">Collection by{' '} | ||||
|             <p className="collection-metadata__user">{this.props.t('Collection.By')} | ||||
|               <Link to={`${hostname}/${username}/sketches`}>{owner.username}</Link> | ||||
|             </p> | ||||
| 
 | ||||
|             <p className="collection-metadata__user">{items.length} sketch{items.length === 1 ? '' : 'es'}</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}`} /> | ||||
|               <ShareURL value={`${baseURL}${id}`} t={this.props.t} /> | ||||
|             </p> | ||||
|             { | ||||
|               this.isOwner() && | ||||
|               <Button onClick={this.showAddSketches}> | ||||
|                 Add Sketch | ||||
|                 {this.props.t('Collection.AddSketch')} | ||||
|               </Button> | ||||
|             } | ||||
|           </div> | ||||
|  | @ -304,7 +307,7 @@ class Collection extends React.Component { | |||
|       this.props.collection.items.length > 0; | ||||
| 
 | ||||
|     if (!isLoading && !hasCollectionItems) { | ||||
|       return (<p className="collection-empty-message">No sketches in collection</p>); | ||||
|       return (<p className="collection-empty-message">{this.props.t('Collection.NoSketches')}</p>); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | @ -314,14 +317,14 @@ class Collection extends React.Component { | |||
|     let buttonLabel; | ||||
|     if (field !== fieldName) { | ||||
|       if (field === 'name') { | ||||
|         buttonLabel = `Sort by ${displayName} ascending.`; | ||||
|         buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', { displayName }); | ||||
|       } else { | ||||
|         buttonLabel = `Sort by ${displayName} descending.`; | ||||
|         buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', { displayName }); | ||||
|       } | ||||
|     } else if (direction === SortingActions.DIRECTION.ASC) { | ||||
|       buttonLabel = `Sort by ${displayName} descending.`; | ||||
|       buttonLabel = this.props.t('Collection.ButtonLabelDescendingARIA', { displayName }); | ||||
|     } else { | ||||
|       buttonLabel = `Sort by ${displayName} ascending.`; | ||||
|       buttonLabel = this.props.t('Collection.ButtonLabelAscendingARIA', { displayName }); | ||||
|     } | ||||
|     return buttonLabel; | ||||
|   } | ||||
|  | @ -342,10 +345,10 @@ class Collection extends React.Component { | |||
|         > | ||||
|           <span className={headerClass}>{displayName}</span> | ||||
|           {field === fieldName && direction === SortingActions.DIRECTION.ASC && | ||||
|             <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" /> | ||||
|             <ArrowUpIcon role="img" aria-label={this.props.t('Collection.DirectionAscendingARIA')} focusable="false" /> | ||||
|           } | ||||
|           {field === fieldName && direction === SortingActions.DIRECTION.DESC && | ||||
|             <ArrowDownIcon role="img" aria-label="Descending" focusable="false" /> | ||||
|             <ArrowDownIcon role="img" aria-label={this.props.t('Collection.DirectionDescendingARIA')} focusable="false" /> | ||||
|           } | ||||
|         </button> | ||||
|       </th> | ||||
|  | @ -371,9 +374,9 @@ class Collection extends React.Component { | |||
|                 <table className="sketches-table" summary="table containing all collections"> | ||||
|                   <thead> | ||||
|                     <tr> | ||||
|                       {this._renderFieldHeader('name', 'Name')} | ||||
|                       {this._renderFieldHeader('createdAt', 'Date Added')} | ||||
|                       {this._renderFieldHeader('user', 'Owner')} | ||||
|                       {this._renderFieldHeader('name', this.props.t('Collection.HeaderName'))} | ||||
|                       {this._renderFieldHeader('createdAt', this.props.t('Collection.HeaderCreatedAt'))} | ||||
|                       {this._renderFieldHeader('user', this.props.t('Collection.HeaderUser'))} | ||||
|                       <th scope="col"></th> | ||||
|                     </tr> | ||||
|                   </thead> | ||||
|  | @ -386,6 +389,7 @@ class Collection extends React.Component { | |||
|                         username={this.getUsername()} | ||||
|                         collection={this.props.collection} | ||||
|                         isOwner={isOwner} | ||||
|                         t={this.props.t} | ||||
|                       />))} | ||||
|                   </tbody> | ||||
|                 </table> | ||||
|  | @ -393,7 +397,7 @@ class Collection extends React.Component { | |||
|               { | ||||
|                 this.state.isAddingSketches && ( | ||||
|                   <Overlay | ||||
|                     title="Add sketch" | ||||
|                     title={this.props.t('Collection.AddSketch')} | ||||
|                     actions={<SketchSearchbar />} | ||||
|                     closeOverlay={this.hideAddSketches} | ||||
|                     isFixedHeight | ||||
|  | @ -437,7 +441,8 @@ Collection.propTypes = { | |||
|   sorting: PropTypes.shape({ | ||||
|     field: PropTypes.string.isRequired, | ||||
|     direction: PropTypes.string.isRequired | ||||
|   }).isRequired | ||||
|   }).isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| Collection.defaultProps = { | ||||
|  | @ -468,4 +473,4 @@ function mapDispatchToProps(dispatch) { | |||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(Collection); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(Collection)); | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import PropTypes from 'prop-types'; | ||||
| import React from 'react'; | ||||
| import { Helmet } from 'react-helmet'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import * as CollectionsActions from '../../IDE/actions/collections'; | ||||
|  | @ -24,7 +25,7 @@ class CollectionCreate extends React.Component { | |||
|   } | ||||
| 
 | ||||
|   getTitle() { | ||||
|     return 'p5.js Web Editor | Create collection'; | ||||
|     return this.props.t('CollectionCreate.Title'); | ||||
|   } | ||||
| 
 | ||||
|   handleTextChange = field => (evt) => { | ||||
|  | @ -55,34 +56,34 @@ class CollectionCreate extends React.Component { | |||
|         </Helmet> | ||||
|         <div className="sketches-table-container"> | ||||
|           <form className="form" onSubmit={this.handleCreateCollection}> | ||||
|             {creationError && <span className="form-error">Couldn't create collection</span>} | ||||
|             {creationError && <span className="form-error">{this.props.t('CollectionCreate.FormError')}</span>} | ||||
|             <p className="form__field"> | ||||
|               <label htmlFor="name" className="form__label">Collection name</label> | ||||
|               <label htmlFor="name" className="form__label">{this.props.t('CollectionCreate.FormLabel')}</label> | ||||
|               <input | ||||
|                 className="form__input" | ||||
|                 aria-label="name" | ||||
|                 aria-label={this.props.t('CollectionCreate.FormLabelARIA')} | ||||
|                 type="text" | ||||
|                 id="name" | ||||
|                 value={name} | ||||
|                 placeholder={generatedCollectionName} | ||||
|                 onChange={this.handleTextChange('name')} | ||||
|               /> | ||||
|               {invalid && <span className="form-error">Collection name is required</span>} | ||||
|               {invalid && <span className="form-error">{this.props.t('CollectionCreate.NameRequired')}</span>} | ||||
|             </p> | ||||
|             <p className="form__field"> | ||||
|               <label htmlFor="description" className="form__label">Description (optional)</label> | ||||
|               <label htmlFor="description" className="form__label">{this.props.t('CollectionCreate.Description')}</label> | ||||
|               <textarea | ||||
|                 className="form__input form__input-flexible-height" | ||||
|                 aria-label="description" | ||||
|                 aria-label={this.props.t('CollectionCreate.DescriptionARIA')} | ||||
|                 type="text" | ||||
|                 id="description" | ||||
|                 value={description} | ||||
|                 onChange={this.handleTextChange('description')} | ||||
|                 placeholder="My fave sketches" | ||||
|                 placeholder={this.props.t('CollectionCreate.DescriptionPlaceholder')} | ||||
|                 rows="4" | ||||
|               /> | ||||
|             </p> | ||||
|             <Button type="submit" disabled={invalid}>Create collection</Button> | ||||
|             <Button type="submit" disabled={invalid}>{this.props.t('CollectionCreate.SubmitCollectionCreate')}</Button> | ||||
|           </form> | ||||
|         </div> | ||||
|       </div> | ||||
|  | @ -95,7 +96,8 @@ CollectionCreate.propTypes = { | |||
|     username: PropTypes.string, | ||||
|     authenticated: PropTypes.bool.isRequired | ||||
|   }).isRequired, | ||||
|   createCollection: PropTypes.func.isRequired | ||||
|   createCollection: PropTypes.func.isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| function mapStateToProps(state, ownProps) { | ||||
|  | @ -108,4 +110,4 @@ function mapDispatchToProps(dispatch) { | |||
|   return bindActionCreators(Object.assign({}, CollectionsActions), dispatch); | ||||
| } | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(CollectionCreate); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(CollectionCreate)); | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| import PropTypes from 'prop-types'; | ||||
| import React from 'react'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| import { Link } from 'react-router'; | ||||
| 
 | ||||
| const TabKey = { | ||||
|  | @ -28,12 +29,14 @@ Tab.propTypes = { | |||
|   to: PropTypes.string.isRequired, | ||||
| }; | ||||
| 
 | ||||
| const DashboardTabSwitcher = ({ currentTab, isOwner, username }) => ( | ||||
| const DashboardTabSwitcher = ({ | ||||
|   currentTab, isOwner, username, t | ||||
| }) => ( | ||||
|   <ul className="dashboard-header__switcher"> | ||||
|     <div className="dashboard-header__tabs"> | ||||
|       <Tab to={`/${username}/sketches`} isSelected={currentTab === TabKey.sketches}>Sketches</Tab> | ||||
|       <Tab to={`/${username}/collections`} isSelected={currentTab === TabKey.collections}>Collections</Tab> | ||||
|       {isOwner && <Tab to={`/${username}/assets`} isSelected={currentTab === TabKey.assets}>Assets</Tab>} | ||||
|       <Tab to={`/${username}/sketches`} isSelected={currentTab === TabKey.sketches}>{t('DashboardTabSwitcher.Sketches')}</Tab> | ||||
|       <Tab to={`/${username}/collections`} isSelected={currentTab === TabKey.collections}>{t('DashboardTabSwitcher.Collections')}</Tab> | ||||
|       {isOwner && <Tab to={`/${username}/assets`} isSelected={currentTab === TabKey.assets}>{t('DashboardTabSwitcher.Assets')}</Tab>} | ||||
|     </div> | ||||
|   </ul> | ||||
| ); | ||||
|  | @ -42,6 +45,9 @@ DashboardTabSwitcher.propTypes = { | |||
|   currentTab: PropTypes.string.isRequired, | ||||
|   isOwner: PropTypes.bool.isRequired, | ||||
|   username: PropTypes.string.isRequired, | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| export { DashboardTabSwitcher as default, TabKey }; | ||||
| 
 | ||||
| const DashboardTabSwitcherPublic = withTranslation()(DashboardTabSwitcher); | ||||
| export { DashboardTabSwitcherPublic as default, TabKey }; | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| import PropTypes from 'prop-types'; | ||||
| import React from 'react'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| 
 | ||||
| import Nav from '../../../components/Nav'; | ||||
| 
 | ||||
| import CollectionCreate from '../components/CollectionCreate'; | ||||
|  | @ -25,10 +27,10 @@ class CollectionView extends React.Component { | |||
| 
 | ||||
|   pageTitle() { | ||||
|     if (this.isCreatePage()) { | ||||
|       return 'Create collection'; | ||||
|       return this.props.t('CollectionView.TitleCreate'); | ||||
|     } | ||||
| 
 | ||||
|     return 'collection'; | ||||
|     return this.props.t('CollectionView.TitleDefault'); | ||||
|   } | ||||
| 
 | ||||
|   isOwner() { | ||||
|  | @ -87,6 +89,7 @@ CollectionView.propTypes = { | |||
|   user: PropTypes.shape({ | ||||
|     username: PropTypes.string, | ||||
|   }), | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(CollectionView); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(CollectionView)); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import React from 'react'; | |||
| import { connect } from 'react-redux'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { browserHistory } from 'react-router'; | ||||
| import { withTranslation } from 'react-i18next'; | ||||
| import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions'; | ||||
| 
 | ||||
| import Button from '../../../common/Button'; | ||||
|  | @ -17,7 +18,7 @@ import SketchList from '../../IDE/components/SketchList'; | |||
| import { CollectionSearchbar, SketchSearchbar } from '../../IDE/components/Searchbar'; | ||||
| 
 | ||||
| import CollectionCreate from '../components/CollectionCreate'; | ||||
| import DashboardTabSwitcher, { TabKey } from '../components/DashboardTabSwitcher'; | ||||
| import DashboardTabSwitcherPublic, { TabKey } from '../components/DashboardTabSwitcher'; | ||||
| 
 | ||||
| class DashboardView extends React.Component { | ||||
|   static defaultProps = { | ||||
|  | @ -75,7 +76,7 @@ class DashboardView extends React.Component { | |||
|     browserHistory.push(`/${this.ownerName()}/collections`); | ||||
|   } | ||||
| 
 | ||||
|   renderActionButton(tabKey, username) { | ||||
|   renderActionButton(tabKey, username, t) { | ||||
|     switch (tabKey) { | ||||
|       case TabKey.assets: | ||||
|         return this.isOwner() && <AssetSize />; | ||||
|  | @ -83,7 +84,7 @@ class DashboardView extends React.Component { | |||
|         return this.isOwner() && ( | ||||
|           <React.Fragment> | ||||
|             <Button to={`/${username}/collections/create`}> | ||||
|               Create collection | ||||
|               {t('DashboardView.CreateCollection')} | ||||
|             </Button> | ||||
|             <CollectionSearchbar /> | ||||
|           </React.Fragment>); | ||||
|  | @ -91,7 +92,7 @@ class DashboardView extends React.Component { | |||
|       default: | ||||
|         return ( | ||||
|           <React.Fragment> | ||||
|             {this.isOwner() && <Button to="/">New sketch</Button>} | ||||
|             {this.isOwner() && <Button to="/">{t('DashboardView.NewSketch')}</Button>} | ||||
|             <SketchSearchbar /> | ||||
|           </React.Fragment> | ||||
|         ); | ||||
|  | @ -114,7 +115,7 @@ class DashboardView extends React.Component { | |||
|     const currentTab = this.selectedTabKey(); | ||||
|     const isOwner = this.isOwner(); | ||||
|     const { username } = this.props.params; | ||||
|     const actions = this.renderActionButton(currentTab, username); | ||||
|     const actions = this.renderActionButton(currentTab, username, this.props.t); | ||||
| 
 | ||||
|     return ( | ||||
|       <div className="dashboard"> | ||||
|  | @ -124,7 +125,7 @@ class DashboardView extends React.Component { | |||
|           <div className="dashboard-header__header"> | ||||
|             <h2 className="dashboard-header__header__title">{this.ownerName()}</h2> | ||||
|             <div className="dashboard-header__nav"> | ||||
|               <DashboardTabSwitcher currentTab={currentTab} isOwner={isOwner} username={username} /> | ||||
|               <DashboardTabSwitcherPublic currentTab={currentTab} isOwner={isOwner} username={username} /> | ||||
|               {actions && | ||||
|                 <div className="dashboard-header__actions"> | ||||
|                   {actions} | ||||
|  | @ -139,7 +140,7 @@ class DashboardView extends React.Component { | |||
|         </main> | ||||
|         {this.isCollectionCreate() && | ||||
|           <Overlay | ||||
|             title="Create collection" | ||||
|             title={this.props.t('DashboardView.CreateCollectionOverlay')} | ||||
|             closeOverlay={this.returnToDashboard} | ||||
|           > | ||||
|             <CollectionCreate /> | ||||
|  | @ -176,6 +177,7 @@ DashboardView.propTypes = { | |||
|   user: PropTypes.shape({ | ||||
|     username: PropTypes.string, | ||||
|   }), | ||||
|   t: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| export default connect(mapStateToProps, mapDispatchToProps)(DashboardView); | ||||
| export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(DashboardView)); | ||||
|  |  | |||
|  | @ -88,7 +88,8 @@ | |||
|     "SketchSaved": "Sketch saved.", | ||||
|     "SketchFailedSave": "Failed to save sketch.", | ||||
|     "AutosaveEnabled": "Autosave enabled.", | ||||
|     "LangChange": "Language changed" | ||||
|     "LangChange": "Language changed", | ||||
|     "SettingsSaved": "Settings saved." | ||||
|   }, | ||||
|   "Toolbar": { | ||||
|     "Preview": "Preview", | ||||
|  | @ -198,12 +199,18 @@ | |||
|   }, | ||||
|   "Common": { | ||||
|     "Error": "Error", | ||||
|     "ErrorARIA": "Error", | ||||
|     "Save": "Save", | ||||
|     "p5logoARIA": "p5.js Logo", | ||||
|     "DeleteConfirmation": "Are you sure you want to delete {{name}}?" | ||||
|   }, | ||||
|   "IDEView": { | ||||
|     "SubmitFeedback": "Submit Feedback" | ||||
|     "SubmitFeedback": "Submit Feedback", | ||||
|     "SubmitFeedbackARIA": "submit-feedback", | ||||
|     "AddCollectionTitle": "Add to collection", | ||||
|     "AddCollectionARIA":"add to collection", | ||||
|     "ShareTitle": "Share", | ||||
|     "ShareARIA":"share" | ||||
|   }, | ||||
|   "NewFileModal": { | ||||
|     "Title": "Create File", | ||||
|  | @ -356,5 +363,116 @@ | |||
|     "Present": "Present", | ||||
|     "Fullscreen": "Fullscreen", | ||||
|     "Edit": "Edit" | ||||
|   }, | ||||
|   "CollectionView": { | ||||
|     "TitleCreate": "Create collection", | ||||
|     "TitleDefault": "collection" | ||||
|   }, | ||||
|   "Collection": { | ||||
|     "Title": "p5.js Web Editor | My collections", | ||||
|     "AnothersTitle": "p5.js Web Editor | {{anotheruser}}'s collections", | ||||
|     "Share": "Share", | ||||
|     "URLLink": "Link to Collection", | ||||
|     "AddSketch": "Add Sketch", | ||||
|     "DeleteFromCollection": "Are you sure you want to remove {{name_sketch}} from this collection?", | ||||
|     "SketchDeleted": "Sketch deleted", | ||||
|     "SketchRemoveARIA": "Remove sketch from collection", | ||||
|     "DescriptionPlaceholder": "Add description", | ||||
|     "Description": "description", | ||||
|     "NumSketches": "{{count}} sketch", | ||||
|     "NumSketches_plural": "{{count}} sketches", | ||||
|     "By":"Collection by ", | ||||
|     "NoSketches": "No sketches in collection", | ||||
|     "HeaderName": "Name", | ||||
|     "HeaderCreatedAt": "Date Added", | ||||
|     "HeaderUser": "Owner", | ||||
|     "DirectionAscendingARIA": "Ascending", | ||||
|     "DirectionDescendingARIA": "Descending", | ||||
|     "ButtonLabelAscendingARIA": "Sort by {{displayName}} ascending.", | ||||
|     "ButtonLabelDescendingARIA": "Sort by {{displayName}} descending." | ||||
|   }, | ||||
|   "AddToCollectionList": { | ||||
|     "Title": "p5.js Web Editor | My collections", | ||||
|     "AnothersTitle": "p5.js Web Editor | {{anotheruser}}'s collections", | ||||
|     "Empty": "No collections" | ||||
|   }, | ||||
|   "CollectionCreate": { | ||||
|     "Title": "p5.js Web Editor | Create collection", | ||||
|     "FormError": "Couldn't create collection", | ||||
|     "FormLabel": "Collection name", | ||||
|     "FormLabelARIA": "name", | ||||
|     "NameRequired": "Collection name is required", | ||||
|     "Description": "Description (optional)", | ||||
|     "DescriptionARIA": "description", | ||||
|     "DescriptionPlaceholder": "My fave sketches", | ||||
|     "SubmitCollectionCreate": "Create collection" | ||||
|   }, | ||||
|   "DashboardView": { | ||||
|     "CreateCollection": "Create collection", | ||||
|     "NewSketch": "New sketch", | ||||
|     "CreateCollectionOverlay": "Create collection" | ||||
|   }, | ||||
|   "DashboardTabSwitcher": { | ||||
|     "Sketches": "Sketches", | ||||
|     "Collections": "Collections", | ||||
|     "Assets": "Assets" | ||||
|   }, | ||||
|   "CollectionList": { | ||||
|     "Title": "p5.js Web Editor | My collections", | ||||
|     "AnothersTitle": "p5.js Web Editor | {{anotheruser}}'s collections", | ||||
|     "NoCollections": "No collections.", | ||||
|     "HeaderName": "Name", | ||||
|     "HeaderCreatedAt": "Date Created", | ||||
|     "HeaderCreatedAt_mobile": "Created", | ||||
|     "HeaderUpdatedAt": "Date Updated", | ||||
|     "HeaderUpdatedAt_mobile": "Updated", | ||||
|     "HeaderNumItems": "# sketches", | ||||
|     "HeaderNumItems_mobile": "# sketches", | ||||
|     "DirectionAscendingARIA": "Ascending", | ||||
|     "DirectionDescendingARIA": "Descending", | ||||
|     "ButtonLabelAscendingARIA": "Sort by {{displayName}} ascending.", | ||||
|     "ButtonLabelDescendingARIA": "Sort by {{displayName}} descending.", | ||||
|     "AddSketch": "Add Sketch" | ||||
|   }, | ||||
|   "CollectionListRow": { | ||||
|     "ToggleCollectionOptionsARIA": "Toggle Open/Close collection options", | ||||
|     "AddSketch": "Add sketch", | ||||
|     "Delete": "Delete", | ||||
|     "Rename": "Rename" | ||||
|   }, | ||||
|   "Overlay": { | ||||
|     "AriaLabel": "Close {{title}} overlay" | ||||
|   }, | ||||
|   "QuickAddList":{ | ||||
|     "ButtonLabelRemove": "Remove from collection", | ||||
|     "ButtonLabelAddToCollection": "Add to collection", | ||||
|     "View": "View" | ||||
|   }, | ||||
|   "SketchList": { | ||||
|     "View": "View", | ||||
|     "Title": "p5.js Web Editor | My sketches", | ||||
|     "AnothersTitle": "p5.js Web Editor | {{anotheruser}}'s sketches", | ||||
|     "ToggleLabelARIA": "Toggle Open/Close Sketch Options", | ||||
|     "DropdownRename": "Rename", | ||||
|     "DropdownDownload": "Download", | ||||
|     "DropdownDuplicate": "Duplicate", | ||||
|     "DropdownAddToCollection": "Add to collection", | ||||
|     "DropdownDelete": "Delete", | ||||
|     "DirectionAscendingARIA": "Ascending", | ||||
|     "DirectionDescendingARIA": "Descending", | ||||
|     "ButtonLabelAscendingARIA": "Sort by {{displayName}} ascending.", | ||||
|     "ButtonLabelDescendingARIA": "Sort by {{displayName}} descending.", | ||||
|     "AddToCollectionOverlayTitle": "Add to collection", | ||||
|     "HeaderName": "Sketch", | ||||
|     "HeaderCreatedAt": "Date Created", | ||||
|     "HeaderCreatedAt_mobile": "Created", | ||||
|     "HeaderUpdatedAt": "Date Updated", | ||||
|     "HeaderUpdatedAt_mobile": "Updated", | ||||
|     "NoSketches": "No sketches." | ||||
|   }, | ||||
|   "AddToCollectionSketchList": { | ||||
|     "Title": "p5.js Web Editor | My sketches", | ||||
|     "AnothersTitle": "p5.js Web Editor | {{anotheruser}}'s sketches", | ||||
|     "NoCollections": "No collections." | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -88,7 +88,8 @@ | |||
|     "SketchSaved": "Bosquejo guardado.", | ||||
|     "SketchFailedSave": "Fallo al guardar el bosquejo.", | ||||
|     "AutosaveEnabled": "Grabado automático activado.", | ||||
|     "LangChange": "Lenguaje cambiado" | ||||
|     "LangChange": "Lenguaje cambiado", | ||||
|     "SettingsSaved": "Configuración guardada." | ||||
|   }, | ||||
|   "Toolbar": { | ||||
|     "Preview": "Vista previa", | ||||
|  | @ -198,12 +199,18 @@ | |||
|   }, | ||||
|   "Common": { | ||||
|     "Error": "Error", | ||||
|     "ErrorARIA": "Error", | ||||
|     "Save": "Guardar", | ||||
|     "p5logoARIA": "Logo p5.js ", | ||||
|     "DeleteConfirmation": "¿Estás que quieres borrar {{name}}?" | ||||
|     "DeleteConfirmation": "¿Estás seguro que quieres borrar {{name}}?" | ||||
|   }, | ||||
|   "IDEView": { | ||||
|     "SubmitFeedback": "Enviar retroalimentación" | ||||
|     "SubmitFeedback": "Enviar retroalimentación", | ||||
|     "SubmitFeedbackARIA": "Enviar retroalimentación", | ||||
|     "AddCollectionTitle": "Agregar a colección", | ||||
|     "AddCollectionARIA":"Agregar a colección", | ||||
|     "ShareTitle": "Compartir", | ||||
|     "ShareARIA":"compartir" | ||||
|   }, | ||||
|   "NewFileModal": { | ||||
|     "Title": "Crear Archivo", | ||||
|  | @ -234,7 +241,7 @@ | |||
|   "ResetPasswordView": { | ||||
|     "Title": "Editor Web p5.js | Regenerar Contraseña", | ||||
|     "Reset": "Regenerar Contraseña", | ||||
|     "Submitted": "Your password reset email should arrive shortly. If you don't see it, check\n            in your spam folder as sometimes it can end up there.", | ||||
|     "Submitted": "Tu correo para regenerar la contraseña debe llegar pronto. Si no lo ves, revisa\n tu carpeta de spam puesto que algunas veces puede terminar ahí.", | ||||
|     "Login": "Ingresa", | ||||
|     "LoginOr": "o", | ||||
|     "SignUp": "Registráte" | ||||
|  | @ -282,7 +289,7 @@ | |||
|     "AccessTokensTab": "Tokens de acceso" | ||||
|   }, | ||||
|   "APIKeyForm": { | ||||
|     "ConfirmDelete": "¿Estas seguro que quieres borrar {{key_label}}?", | ||||
|     "ConfirmDelete": "¿Estás seguro que quieres borrar {{key_label}}?", | ||||
|     "Summary": " Los Tokens de acceso personal actuan como tu contraseña para permitir\n  a scripts automáticos acceder al  API del Editor. Crea un token por cada script \n que necesite acceso.", | ||||
|     "CreateToken": "Crear nuevo token", | ||||
|     "TokenLabel": "¿Para que es este token?", | ||||
|  | @ -356,5 +363,116 @@ | |||
|     "Present": "Presentar", | ||||
|     "Fullscreen": "Pantalla Completa", | ||||
|     "Edit": "Editar" | ||||
|   }, | ||||
|   "CollectionView": { | ||||
|     "TitleCreate": "Crear colección", | ||||
|     "TitleDefault": "colección" | ||||
|   }, | ||||
|   "Collection": { | ||||
|     "Title": "p5.js Web Editor | Mis colecciones", | ||||
|     "AnothersTitle": "Editor Web p5.js | Colecciones de {{anotheruser}}", | ||||
|     "Share": "Compartir", | ||||
|     "URLLink": "Liga a la Colección", | ||||
|     "AddSketch": "Agregar Bosquejo", | ||||
|     "DeleteFromCollection": "¿Estás seguro que quieres remover {{name_sketch}} de esta colección?", | ||||
|     "SketchDeleted": "El bosquejo fue eliminado", | ||||
|     "SketchRemoveARIA": "Remover bosquejo de la colección", | ||||
|     "DescriptionPlaceholder": "Agregar descripción", | ||||
|     "Description": "descripción", | ||||
|     "NumSketches": "{{count}} bosquejo", | ||||
|     "NumSketches_plural": "{{count}} bosquejos", | ||||
|     "By":"Colección por ", | ||||
|     "NoSketches": "No hay bosquejos en la colección", | ||||
|     "HeaderName": "Nombre", | ||||
|     "HeaderCreatedAt": "Fecha de agregación", | ||||
|     "HeaderUser": "Propietario", | ||||
|     "DirectionAscendingARIA": "Ascendente", | ||||
|     "DirectionDescendingARIA": "Descendente", | ||||
|     "ButtonLabelAscendingARIA": "Ordenar por {{displayName}} ascendente.", | ||||
|     "ButtonLabelDescendingARIA": "Ordenar por {{displayName}} descendente." | ||||
|   }, | ||||
|   "AddToCollectionList": { | ||||
|     "Title": "p5.js Web Editor | Mis colecciones", | ||||
|     "AnothersTitle": "Editor Web p5.js | Colecciones de {{anotheruser}}", | ||||
|     "Empty": "No hay colecciones" | ||||
|   }, | ||||
|   "CollectionCreate": { | ||||
|     "Title": "Editor Web p5.js | Crear colección", | ||||
|     "FormError": "No se pudo crear colección", | ||||
|     "FormLabel": "Nombre colección ", | ||||
|     "FormLabelARIA": "nombre de la colección", | ||||
|     "NameRequired": "Se requiere nombre de colección", | ||||
|     "Description": "Descripción (opcional)", | ||||
|     "DescriptionARIA": "descripción", | ||||
|     "DescriptionPlaceholder": "Mis bosquejos favoritos", | ||||
|     "SubmitCollectionCreate": "Crear colección" | ||||
|   }, | ||||
|   "DashboardView": { | ||||
|     "CreateCollection": "Crear colección", | ||||
|     "NewSketch": "Nuevo bosquejo", | ||||
|     "CreateCollectionOverlay": "Crear colección" | ||||
|   }, | ||||
|   "DashboardTabSwitcher": { | ||||
|     "Sketches": "Bosquejos", | ||||
|     "Collections": "Colecciones ", | ||||
|     "Assets": "Assets" | ||||
|   }, | ||||
|   "CollectionList": { | ||||
|     "Title": "p5.js Web Editor | Mis colecciones", | ||||
|     "AnothersTitle": "Editor Web p5.js | Colecciones de {{anotheruser}}", | ||||
|     "NoCollections": "No hay colecciones.", | ||||
|     "HeaderName": "Nombre", | ||||
|     "HeaderCreatedAt": "Fecha Creación", | ||||
|     "HeaderCreatedAt_mobile": "Creación", | ||||
|     "HeaderUpdatedAt": "Fecha Actualización", | ||||
|     "HeaderUpdatedAt_mobile": "Actualización", | ||||
|     "HeaderNumItems": "# bosquejos", | ||||
|     "HeaderNumItems_mobile": "Bosquejos", | ||||
|     "DirectionAscendingARIA": "Ascendente", | ||||
|     "DirectionDescendingARIA": "Descendente", | ||||
|     "ButtonLabelAscendingARIA": "Ordenar por {{displayName}} ascendente.", | ||||
|     "ButtonLabelDescendingARIA": "Ordenar por {{displayName}} descendente.", | ||||
|     "AddSketch": "Agregar Bosquejo" | ||||
|   }, | ||||
|   "CollectionListRow": { | ||||
|     "ToggleCollectionOptionsARIA": "Alternar Abrir/Cerrar opciones de colección", | ||||
|     "AddSketch": "Agregar bosquejo", | ||||
|     "Delete": "Borrar", | ||||
|     "Rename": "Renombrar" | ||||
|   }, | ||||
|   "Overlay": { | ||||
|     "AriaLabel": "Cerrar la capa {{title}}" | ||||
|   }, | ||||
|   "QuickAddList":{ | ||||
|     "ButtonLabelRemove": "Remove from collection", | ||||
|     "ButtonLabelAddToCollection": "Add to collection", | ||||
|     "View": "Ver" | ||||
|   }, | ||||
|   "SketchList": { | ||||
|     "View": "Ver", | ||||
|     "Title": "p5.js Web Editor | Mis bosquejos", | ||||
|     "AnothersTitle": "Editor Web p5.js | Bosquejos de {{anotheruser}}", | ||||
|     "ToggleLabelARIA": "Alternar Abrir/Cerrar Opciones de Bosquejo", | ||||
|     "DropdownRename": "Renombrar", | ||||
|     "DropdownDownload": "Descargar", | ||||
|     "DropdownDuplicate": "Duplicar", | ||||
|     "DropdownAddToCollection": "Agregar a Colección", | ||||
|     "DropdownDelete": "Borrar", | ||||
|     "DirectionAscendingARIA": "Ascendente", | ||||
|     "DirectionDescendingARIA": "Descendente", | ||||
|     "ButtonLabelAscendingARIA": "Ordenar por {{displayName}} ascendente.", | ||||
|     "ButtonLabelDescendingARIA": "Ordenar por {{displayName}} descendente.", | ||||
|     "AddToCollectionOverlayTitle": "Agregar a colección", | ||||
|     "HeaderName": "Bosquejo", | ||||
|     "HeaderCreatedAt": "Fecha Creación", | ||||
|     "HeaderCreatedAt_mobile": "Creación", | ||||
|     "HeaderUpdatedAt": "Fecha Actualización", | ||||
|     "HeaderUpdatedAt_mobile": "Actualización", | ||||
|     "NoSketches": "No hay bosquejos." | ||||
|   }, | ||||
|   "AddToCollectionSketchList": { | ||||
|     "Title": "p5.js Web Editor | Mis bosquejos", | ||||
|     "AnothersTitle": "Editor Web p5.js | Bosquejos de {{anotheruser}}", | ||||
|     "NoCollections": "No hay colecciones." | ||||
|   } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 ghalestrilo
						ghalestrilo