interface with api add/remove project to/from studio works; addtostudio modal waits to close

This commit is contained in:
Ben Wheeler 2018-07-18 18:52:15 -04:00
parent a59d533a1c
commit cf63c35e94
7 changed files with 77 additions and 88 deletions

View file

@ -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 (
<AddToStudioModalPresentation
studios={studios}
isOpen={isOpen}
onToggleStudio={onToggleStudio}
onSubmit={handleSubmit}
studios={this.props.studios}
isOpen={this.props.isOpen}
waitingToClose={this.state.waitingToClose}
onRequestClose={this.closeAndStopWaiting}
onToggleStudio={this.props.onToggleStudio}
onSubmit={this.handleSubmit}
/>
);
}
@ -89,4 +67,4 @@ AddToStudioModal.propTypes = {
onRequestClose: PropTypes.func
};
module.exports = AddToStudioModalPresentation;
module.exports = AddToStudioModal;

View file

@ -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 = <img alt="checkmark-icon"
className="studio-status-icon-checkmark-img"
src="/svgs/modal/confirm.svg"
@ -51,7 +39,7 @@ class AddToStudioModalPresentation extends React.Component {
className="studio-status-icon-plus-img"
src="/svgs/modal/add.svg"
/>
const studioButtons = studios.map((studio, index) => {
const studioButtons = this.props.studios.map((studio, index) => {
return (
<div className={"studio-selector-button " +
(studio.hasRequestOutstanding ? "studio-selector-button-waiting" :
@ -80,7 +68,7 @@ class AddToStudioModalPresentation extends React.Component {
className="mod-addToStudio"
contentLabel={contentLabel}
onRequestClose={this.props.onRequestClose}
isOpen={isOpen}
isOpen={this.props.isOpen}
>
<div>
<div className="addToStudio-modal-header">
@ -116,7 +104,7 @@ class AddToStudioModalPresentation extends React.Component {
<FormattedMessage id="general.close" />
</div>
</Button>
{this.state.waitingToClose ? [
{this.props.waitingToClose ? [
<Button
className="action-button submit-button submit-button-waiting"
disabled="disabled"
@ -143,7 +131,6 @@ class AddToStudioModalPresentation extends React.Component {
</Form>
</div>
</div>
</Modal>
);
}
@ -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
};

View file

@ -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;

View file

@ -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);

View file

@ -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));
});
});

View file

@ -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 = ({
<React.Fragment>
<Button className="action-button studio-button"
key="add-to-studio-button"
onClick={this.handleAddToStudioClick}
onClick={onAddToStudioClicked}
>
Add to Studio
</Button>,
<AddToStudioModal
isOpen={this.state.addToStudioOpen}
isOpen={addToStudioOpen}
key="add-to-studio-modal"
studios={curatedStudios}
onToggleStudio={onToggleStudio}
onRequestClose={this.handleAddToStudioClose}
onRequestClose={onAddToStudioClosed}
/>
</React.Fragment>
}
@ -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
};

View file

@ -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