diff --git a/src/components/modal/addtostudio/container.jsx b/src/components/modal/addtostudio/container.jsx index 3f44ec619..fb7cf0b56 100644 --- a/src/components/modal/addtostudio/container.jsx +++ b/src/components/modal/addtostudio/container.jsx @@ -1,38 +1,15 @@ -// sample data: -// this.studios = [{name: 'Funny games', id: 1}, {name: 'Silly ideas', id: 2}]; -// studios data is like: -// [{ -// id: 1702295, -// description: "...", -// history: {created: "2015-11-15T00:24:35.000Z", -// modified: "2018-05-01T00:14:48.000Z"}, -// image: "http....png", -// owner: 10689298, -// stats: {followers: 0}, -// title: "Studio title" -// }, {...}] const bindAll = require('lodash.bindall'); -const truncate = require('lodash.truncate'); const PropTypes = require('prop-types'); const React = require('react'); const log = require('../../../lib/log.js'); - -const Form = require('../../forms/form.jsx'); -const Button = require('../../forms/button.jsx'); -const Select = require('../../forms/select.jsx'); -const Spinner = require('../../spinner/spinner.jsx'); -const TextArea = require('../../forms/textarea.jsx'); -const FlexRow = require('../../flex-row/flex-row.jsx'); const AddToStudioModalPresentation = require('./presentation.jsx'); const previewActions = require('../../../redux/preview.js'); -require('../../forms/button.scss'); -require('./modal.scss'); - class AddToStudioModal extends React.Component { constructor (props) { super(props); bindAll(this, [ + 'closeAndStopWaiting', 'handleSubmit' ]); @@ -42,21 +19,27 @@ class AddToStudioModal extends React.Component { } componentWillUpdate (prevProps) { - checkIfFinishedUpdating(); + this.checkIfFinishedUpdating(); } hasOutstandingUpdates () { - return (studios.some(studio => (studio.hasRequestOutstanding === true))); + return (this.props.studios.some(studio => (studio.hasRequestOutstanding === true))); } checkIfFinishedUpdating () { - if (waitingToClose === true && hasOutstandingUpdates() === false) { - this.setState({waitingToClose: false}, () => { - this.props.onRequestClose(); - }); + if (this.state.waitingToClose === true && this.hasOutstandingUpdates() === false) { + this.closeAndStopWaiting(); } } + // need to register here that we are no longer waiting for the modal to close. + // Otherwise, user may reopen modal only to have it immediately close. + closeAndStopWaiting () { + this.setState({waitingToClose: false}, () => { + this.props.onRequestClose(); + }); + } + handleSubmit (formData) { this.setState({waitingToClose: true}, () => { this.checkIfFinishedUpdating(); @@ -64,19 +47,14 @@ class AddToStudioModal extends React.Component { } render () { - const { - isOpen, - studios, - onToggleStudio, - onRequestClose - } = this.props; - return ( ); } @@ -89,4 +67,4 @@ AddToStudioModal.propTypes = { onRequestClose: PropTypes.func }; -module.exports = AddToStudioModalPresentation; +module.exports = AddToStudioModal; diff --git a/src/components/modal/addtostudio/presentation.jsx b/src/components/modal/addtostudio/presentation.jsx index 222dc4333..6f8b87b7c 100644 --- a/src/components/modal/addtostudio/presentation.jsx +++ b/src/components/modal/addtostudio/presentation.jsx @@ -12,7 +12,6 @@ const Form = require('../../forms/form.jsx'); const Button = require('../../forms/button.jsx'); const Select = require('../../forms/select.jsx'); const Spinner = require('../../spinner/spinner.jsx'); -const TextArea = require('../../forms/textarea.jsx'); const FlexRow = require('../../flex-row/flex-row.jsx'); require('../../forms/button.scss'); @@ -24,11 +23,6 @@ class AddToStudioModalPresentation extends React.Component { bindAll(this, [ 'handleSubmit' ]); - - this.state = { - waitingToClose: false, - studios: props.studios - }; } handleSubmit (formData) { @@ -36,13 +30,7 @@ class AddToStudioModalPresentation extends React.Component { } render () { - const { - intl, - studios, - onToggleStudio, - isOpen - } = this.props; - const contentLabel = intl.formatMessage({id: "addToStudio.title"}); + const contentLabel = this.props.intl.formatMessage({id: "addToStudio.title"}); const checkmark = checkmark-icon - const studioButtons = studios.map((studio, index) => { + const studioButtons = this.props.studios.map((studio, index) => { return (
@@ -116,7 +104,7 @@ class AddToStudioModalPresentation extends React.Component {
- {this.state.waitingToClose ? [ + {this.props.waitingToClose ? [
- ); } @@ -151,8 +138,10 @@ class AddToStudioModalPresentation extends React.Component { AddToStudioModalPresentation.propTypes = { intl: intlShape, + isOpen: PropTypes.bool, studios: PropTypes.arrayOf(PropTypes.object), - onAddToStudio: PropTypes.func, + waitingToClose: PropTypes.bool, + onToggleStudio: PropTypes.func, onRequestClose: PropTypes.func, onSubmit: PropTypes.func }; diff --git a/src/components/modal/report/modal.scss b/src/components/modal/report/modal.scss index 3cd6355ac..7498aafcf 100644 --- a/src/components/modal/report/modal.scss +++ b/src/components/modal/report/modal.scss @@ -14,7 +14,7 @@ } .report-modal-header { - border-radius: 1rem 1rem 0 0; + border-radius: 0.5rem 0.5rem 0 0; box-shadow: inset 0 -1px 0 0 $ui-coral-dark; background-color: $ui-coral; padding-top: .75rem; @@ -36,7 +36,7 @@ width: 80%; line-height: 1.5rem; font-size: .875rem; - + .validation-message { $arrow-border-width: 1rem; display: block; diff --git a/src/components/spinner/spinner.scss b/src/components/spinner/spinner.scss index f0ee416b9..67e206426 100644 --- a/src/components/spinner/spinner.scss +++ b/src/components/spinner/spinner.scss @@ -77,7 +77,7 @@ &:before { display: block; - animation: circleFadeDelaySmooth 1.2s infinite ease-in-out both; + animation: circleFadeDelaySmooth 1.8s infinite ease-in-out both; margin: 0 auto; border-radius: 100%; background-color: $ui-white; @@ -93,7 +93,7 @@ @for $i from 1 through 24 { $rotation: 15deg * ($i - 1); - $delay: -1.3s + $i * .05; + $delay: -1.9s + $i * .075; .circle#{$i} { transform: rotate($rotation); diff --git a/src/redux/preview.js b/src/redux/preview.js index a858beae3..178ece2d1 100644 --- a/src/redux/preview.js +++ b/src/redux/preview.js @@ -66,8 +66,7 @@ module.exports.previewReducer = (state, action) => { }); case 'ADD_TO_PROJECT_STUDIOS': return Object.assign({}, state, { - // NOTE: move this to calling fn, make this add object passed to me - projectStudios: state.projectStudios.concat({id: action.studioId}) + projectStudios: state.projectStudios.concat(action.studioStub) }); case 'REMOVE_FROM_PROJECT_STUDIOS': return Object.assign({}, state, { @@ -148,9 +147,9 @@ module.exports.setCuratedStudios = items => ({ items: items }); -module.exports.addToProjectStudios = studioId => ({ +module.exports.addToProjectStudios = studioStub => ({ type: 'ADD_TO_PROJECT_STUDIOS', - studioId: studioId + studioStub: studioStub }); module.exports.removeFromProjectStudios = studioId => ({ @@ -402,7 +401,7 @@ module.exports.getProjectStudios = id => (dispatch => { module.exports.getCuratedStudios = (username, token) => (dispatch => { dispatch(module.exports.setFetchStatus('curatedStudios', module.exports.Status.FETCHING)); api({ - uri: `/user/${username}/studios/curate`, + uri: `/users/${username}/studios/curate`, authentication: token }, (err, body, res) => { if (err) { @@ -439,10 +438,11 @@ module.exports.addToStudio = (studioId, projectId, token) => (dispatch => { return; } dispatch(module.exports.setStudioFetchStatus(studioId, module.exports.Status.FETCHED)); - // action: add studio to list - // NOTE: what is the content of the body in the response to this request? - // should we pass the rich object to addToProjectStudios ? - dispatch(module.exports.addToProjectStudios(studioId)); + // add studio to our studios-that-this-project-belongs-to list. + // Server response doesn't include full studio object, so just use a + // minimal stub object. + const studioStub = {id: studioId}; + dispatch(module.exports.addToProjectStudios(studioStub)); }); }); diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx index b9995f122..d83279f49 100644 --- a/src/views/preview/presentation.jsx +++ b/src/views/preview/presentation.jsx @@ -42,6 +42,7 @@ const PreviewPresentation = ({ projectInfo, remixes, report, + addToStudioOpen, projectStudios, curatedStudios, userOwnsProject, @@ -50,6 +51,8 @@ const PreviewPresentation = ({ onReportClicked, onReportClose, onReportSubmit, + onAddToStudioClicked, + onAddToStudioClosed, onToggleStudio, onSeeInside, onUpdate @@ -244,16 +247,16 @@ const PreviewPresentation = ({ , } @@ -338,8 +341,11 @@ PreviewPresentation.propTypes = { open: PropTypes.bool, waiting: PropTypes.bool }), + addToStudioOpen: PropTypes.bool, projectStudios: PropTypes.arrayOf(PropTypes.object), curatedStudios: PropTypes.arrayOf(PropTypes.object), + onAddToStudioClicked: PropTypes.func, + onAddToStudioClosed: PropTypes.func, onToggleStudio: PropTypes.func, userOwnsProject: PropTypes.bool }; diff --git a/src/views/preview/preview.jsx b/src/views/preview/preview.jsx index 76e8d22f1..0defec37e 100644 --- a/src/views/preview/preview.jsx +++ b/src/views/preview/preview.jsx @@ -35,6 +35,8 @@ class Preview extends React.Component { 'handleReportClick', 'handleReportClose', 'handleReportSubmit', + 'handleAddToStudioClick', + 'handleAddToStudioClose', 'handleSeeInside', 'handleUpdate', 'initCounts', @@ -53,6 +55,7 @@ class Preview extends React.Component { favoriteCount: 0, loveCount: 0, projectId: parts[1] === 'editor' ? 0 : parts[1], + addToStudioOpen: false, report: { category: '', notes: '', @@ -147,6 +150,13 @@ class Preview extends React.Component { handleReportClose () { this.setState({report: {...this.state.report, open: false}}); } + handleAddToStudioClick () { + this.setState({addToStudioOpen: true}); + } + handleAddToStudioClose () { + this.setState({addToStudioOpen: false}); + } + // NOTE: this is a copy, change it handleReportSubmit (formData) { this.setState({report: { category: formData.report_category, @@ -201,13 +211,17 @@ class Preview extends React.Component { ); } } - handleToggleStudio (studioId, isAdd) { - this.props.toggleStudio( - isAdd, - studioId, - this.props.projectInfo.id, - this.props.user.token - ); + handleToggleStudio (studioId) { + const studio = this.props.curatedStudios.find((studio) => {return studio.id === studioId}); + // only send add or leave request to server if we know current status + if (studio !== undefined && ('includesProject' in studio)) { + this.props.toggleStudio( + (studio.includesProject === false), + studioId, + this.props.projectInfo.id, + this.props.user.token + ); + } } handleFavoriteToggle () { this.props.setFavedStatus( @@ -309,9 +323,9 @@ class Preview extends React.Component { projectInfo={this.props.projectInfo} remixes={this.props.remixes} report={this.state.report} + addToStudioOpen={this.state.addToStudioOpen} projectStudios={this.props.projectStudios} curatedStudios={this.props.curatedStudios} - onToggleStudio={this.handleToggleStudio} user={this.props.user} userOwnsProject={this.userOwnsProject()} onFavoriteClicked={this.handleFavoriteToggle} @@ -319,6 +333,9 @@ class Preview extends React.Component { onReportClicked={this.handleReportClick} onReportClose={this.handleReportClose} onReportSubmit={this.handleReportSubmit} + onAddToStudioClicked={this.handleAddToStudioClick} + onAddToStudioClosed={this.handleAddToStudioClose} + onToggleStudio={this.handleToggleStudio} onSeeInside={this.handleSeeInside} onUpdate={this.handleUpdate} /> @@ -395,10 +412,9 @@ function consolidateStudiosInfo (curatedStudios, projectStudios, studioRequests) } }); // set studio state to leaving or joining if it's being fetched - if (studioCopy.id in status.studioRequests) { - const request = status.studioRequests[studioId]; - studioCopy.hasRequestOutstanding = (request === preview.Status.FETCHING); - } + studioCopy.hasRequestOutstanding = + ((studioCopy.id in studioRequests) && + (studioRequests[studioCopy.id] === previewActions.Status.FETCHING)); studios.push(studioCopy); }); // if there are any other studios this project is in that are NOT in the list