mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 15:47:53 -05:00
interface with api add/remove project to/from studio works; addtostudio modal waits to close
This commit is contained in:
parent
a59d533a1c
commit
cf63c35e94
7 changed files with 77 additions and 88 deletions
|
@ -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,20 +19,26 @@ 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) {
|
||||
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}, () => {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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,14 +211,18 @@ class Preview extends React.Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
handleToggleStudio (studioId, isAdd) {
|
||||
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(
|
||||
isAdd,
|
||||
(studio.includesProject === false),
|
||||
studioId,
|
||||
this.props.projectInfo.id,
|
||||
this.props.user.token
|
||||
);
|
||||
}
|
||||
}
|
||||
handleFavoriteToggle () {
|
||||
this.props.setFavedStatus(
|
||||
!this.props.faved,
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue