1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-30 13:41:57 +02:00

Fetch piece data in AdditionalDetailForm

Although this moves state further down the hierarchy, it allows the
AdditionalDataForm to be used more easily. Parent components would
otherwise have to have the piece prop carried down through multiple
levels or create a makeshift object.
This commit is contained in:
Brett Sun 2015-11-05 11:56:17 +01:00
parent 3fb4ae55bf
commit adf0d411d6
6 changed files with 111 additions and 102 deletions

View File

@ -31,7 +31,10 @@ let Property = React.createClass({
footer: React.PropTypes.element, footer: React.PropTypes.element,
handleChange: React.PropTypes.func, handleChange: React.PropTypes.func,
ignoreFocus: React.PropTypes.bool, ignoreFocus: React.PropTypes.bool,
name: React.PropTypes.string.isRequired, name: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number
]).isRequired,
className: React.PropTypes.string, className: React.PropTypes.string,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,

View File

@ -9,18 +9,14 @@ import LumenusAclButtonList from '../lumenus_buttons/lumenus_acl_button_list';
import EditionContainer from '../../../../../ascribe_detail/edition_container'; import EditionContainer from '../../../../../ascribe_detail/edition_container';
let LumenusEditionContainer = React.createClass({ let LumenusEditionContainer = React.createClass({
propTypes: { propTypes: EditionContainer.propTypes,
params: React.PropTypes.object,
location: React.PropTypes.object
},
render() { render() {
return ( return (
<EditionContainer <EditionContainer
params={this.props.params} {...this.props}
actionPanelButtonListType={LumenusAclButtonList} actionPanelButtonListType={LumenusAclButtonList}
furtherDetailsType={LumenusFurtherDetails} furtherDetailsType={LumenusFurtherDetails} />
location={this.props.location} />
); );
} }
}); });

View File

@ -4,43 +4,20 @@ import React from 'react';
import LumenusAdditionalDataForm from '../lumenus_forms/lumenus_additional_data_form' import LumenusAdditionalDataForm from '../lumenus_forms/lumenus_additional_data_form'
import GlobalNotificationModel from '../../../../../../models/global_notification_model'; let LumenusFurtherDetails = React.createClass({
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
let FurtherDetails = React.createClass({
propTypes: { propTypes: {
pieceId: React.PropTypes.number, pieceId: React.PropTypes.number,
extraData: React.PropTypes.object,
otherData: React.PropTypes.arrayOf(React.PropTypes.object),
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
location: React.PropTypes.object
},
showNotification() {
this.props.handleSuccess();
let notification = new GlobalNotificationModel('Further details updated', 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
}, },
render() { render() {
const { pieceId, extraData, otherData, handleSuccess, location } = this.props;
// Instead of grabbing the entire piece from the PieceStore and making this component
// stateful, we can put together a piece for the additional form solely based on the props
const piece = {
id: pieceId,
extra_data: extraData,
other_data: otherData
};
return ( return (
<LumenusAdditionalDataForm <LumenusAdditionalDataForm
piece={piece} {...this.props}
handleSuccess={this.showNotification}
isInline isInline
location={location} /> showNotification />
); );
} }
}); });
export default FurtherDetails; export default LumenusFurtherDetails;

View File

@ -7,17 +7,13 @@ import LumenusFurtherDetails from './lumenus_further_details';
import PieceContainer from '../../../../../ascribe_detail/piece_container'; import PieceContainer from '../../../../../ascribe_detail/piece_container';
let LumenusPieceContainer = React.createClass({ let LumenusPieceContainer = React.createClass({
propTypes: { propTypes: PieceContainer.propTypes,
params: React.PropTypes.object,
location: React.PropTypes.object
},
render() { render() {
return ( return (
<PieceContainer <PieceContainer
params={this.props.params} {...this.props}
furtherDetailsType={LumenusFurtherDetails} furtherDetailsType={LumenusFurtherDetails} />
location={this.props.location} />
); );
} }
}); });

View File

@ -14,38 +14,70 @@ import GlobalNotificationActions from '../../../../../../actions/global_notifica
import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils'; import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils';
import PieceActions from '../../../../../../actions/piece_actions';
import PieceStore from '../../../../../../stores/piece_store';
import ApiUrls from '../../../../../../constants/api_urls'; import ApiUrls from '../../../../../../constants/api_urls';
import AppConstants from '../../../../../../constants/application_constants'; import AppConstants from '../../../../../../constants/application_constants';
import requests from '../../../../../../utils/requests'; import requests from '../../../../../../utils/requests';
import { mergeOptions } from '../../../../../../utils/general_utils';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
let LumenusAdditionalDataForm = React.createClass({ let LumenusAdditionalDataForm = React.createClass({
propTypes: { propTypes: {
handleSuccess: React.PropTypes.func, pieceId: React.PropTypes.oneOfType([
piece: React.PropTypes.shape({ React.PropTypes.number,
id: React.PropTypes.number, React.PropTypes.string
extra_data: React.PropTypes.object, ]),
other_data: React.PropTypes.arrayOf(React.PropTypes.object) isInline: React.PropTypes.bool,
}).isRequired, showHeading: React.PropTypes.bool,
isInline: React.PropTypes.bool showNotification: React.PropTypes.bool,
}, handleSuccess: React.PropTypes.func
getDefaultProps() {
return {
isInline: false
};
}, },
getInitialState() { getInitialState() {
return { const pieceStore = PieceStore.getState();
isUploadReady: false
}; return mergeOptions(
pieceStore,
{
// Allow the form to be submitted if there's already an additional image uploaded
isUploadReady: this.isUploadReadyOnChange(pieceStore.piece),
forceUpdateKey: 0,
});
}, },
handleSuccess() { componentDidMount() {
let notification = new GlobalNotificationModel(getLangText('Further details successfully updated'), 'success', 10000); PieceStore.listen(this.onChange);
GlobalNotificationActions.appendGlobalNotification(notification);
// If the Piece store doesn't already have the piece we want loaded, load it
const { pieceId } = this.props;
if (pieceId && this.state.piece.id !== pieceId) {
PieceActions.fetchOne(pieceId);
}
},
componentWillUnmount() {
PieceStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
this.setState({
// Allow the form to be submitted if the updated piece already has an additional image uploaded
isUploadReady: this.isUploadReadyOnChange(state.piece),
/**
* Increment the forceUpdateKey to force the form to rerender on each change
*
* THIS IS A HACK TO MAKE SURE THE FORM ALWAYS DISPLAYS THE MOST RECENT STATE
* BECAUSE SOME OF OUR FORM ELEMENTS DON'T UPDATE FROM PROP CHANGES (ie.
* InputTextAreaToggable).
*/
forceUpdateKey: this.state.forceUpdateKey + 1
});
}, },
getFormData() { getFormData() {
@ -61,10 +93,23 @@ let LumenusAdditionalDataForm = React.createClass({
return { return {
extradata: extradata, extradata: extradata,
piece_id: this.props.piece.id piece_id: this.state.piece.id
}; };
}, },
isUploadReadyOnChange(piece) {
return piece && piece.other_data && piece.other_data.length > 0 ? true : false;
},
handleSuccessWithNotification() {
if (typeof this.props.handleSuccess === 'function') {
this.props.handleSuccess();
}
let notification = new GlobalNotificationModel(getLangText('Further details successfully updated'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
},
uploadStarted() { uploadStarted() {
this.setState({ this.setState({
isUploadReady: false isUploadReady: false
@ -78,7 +123,8 @@ let LumenusAdditionalDataForm = React.createClass({
}, },
render() { render() {
let { piece, isInline, handleSuccess } = this.props; const { isInline, handleSuccess, showHeading, showNotification } = this.props;
const { piece } = this.state;
let buttons, spinner, heading; let buttons, spinner, heading;
if (!isInline) { if (!isInline) {
@ -97,13 +143,13 @@ let LumenusAdditionalDataForm = React.createClass({
</div> </div>
); );
heading = ( heading = showHeading ? (
<div className="ascribe-form-header"> <div className="ascribe-form-header">
<h3> <h3>
{getLangText('Provide additional details')} {getLangText('Provide additional details')}
</h3> </h3>
</div> </div>
); ) : null;
} }
if (piece && piece.id) { if (piece && piece.id) {
@ -111,8 +157,9 @@ let LumenusAdditionalDataForm = React.createClass({
<Form <Form
className="ascribe-form-bordered" className="ascribe-form-bordered"
ref='form' ref='form'
key={this.state.forceUpdateKey}
url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: piece.id})} url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: piece.id})}
handleSuccess={handleSuccess || this.handleSuccess} handleSuccess={showNotification ? this.handleSuccessWithNotification : handleSuccess}
getFormData={this.getFormData} getFormData={this.getFormData}
buttons={buttons} buttons={buttons}
spinner={spinner}> spinner={spinner}>
@ -144,11 +191,11 @@ let LumenusAdditionalDataForm = React.createClass({
required /> required />
</Property> </Property>
<Property <Property
name='tech_details' name='technology_details'
label={getLangText('Technology Details')}> label={getLangText('Technology Details')}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.tech_details} defaultValue={piece.extra_data.technology_details}
placeholder={getLangText('Enter technological details about the work was produced...')} placeholder={getLangText('Enter technological details about the work was produced...')}
required /> required />
</Property> </Property>

View File

@ -13,7 +13,6 @@ import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import UserStore from '../../../../../stores/user_store'; import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions'; import UserActions from '../../../../../actions/user_actions';
import PieceStore from '../../../../../stores/piece_store';
import PieceActions from '../../../../../actions/piece_actions'; import PieceActions from '../../../../../actions/piece_actions';
import PieceListStore from '../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../actions/piece_list_actions';
@ -35,7 +34,6 @@ let LumenusRegisterPiece = React.createClass({
return mergeOptions( return mergeOptions(
UserStore.getState(), UserStore.getState(),
PieceListStore.getState(), PieceListStore.getState(),
PieceStore.getState(),
{ {
selectedLicense: 0, selectedLicense: 0,
isFineUploaderActive: false, isFineUploaderActive: false,
@ -46,33 +44,22 @@ let LumenusRegisterPiece = React.createClass({
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange); UserStore.listen(this.onChange);
PieceStore.listen(this.onChange);
UserActions.fetchCurrentUser(); UserActions.fetchCurrentUser();
let queryParams = this.props.location.query; // Reset the piece store to make sure that we don't display old data
// if the user repeatedly registers works
// Since every step of this register process is atomic, PieceActions.updatePiece({});
// we may need to enter the process at step 1 or 2.
// If this is the case, we'll need the piece number to complete submission.
// It is encoded in the URL as a queryParam and we're checking for it here.
//
// We're using 'in' here as we want to know if 'piece_id' is present in the url,
// we don't care about the value.
if(queryParams && 'piece_id' in queryParams) {
PieceActions.fetchOne(queryParams.piece_id);
}
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange); UserStore.unlisten(this.onChange);
PieceStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
this.setState(state); this.setState(state);
if(this.state.currentUser && this.state.currentUser.email) { if (this.state.currentUser && this.state.currentUser.email) {
// we should also make the fineuploader component editable again // we should also make the fineuploader component editable again
this.setState({ this.setState({
isFineUploaderActive: true isFineUploaderActive: true
@ -80,26 +67,21 @@ let LumenusRegisterPiece = React.createClass({
} }
}, },
handleRegisterSuccess(response){ handleRegisterSuccess(response) {
this.refreshPieceList(); this.refreshPieceList();
// also start loading the piece for the next step // Use the response's piece for the next step if available
if(response && response.piece) { let pieceId = null;
PieceActions.updatePiece({}); if (response && response.piece) {
pieceId = response.piece.id;
PieceActions.updatePiece(response.piece); PieceActions.updatePiece(response.piece);
} }
this.incrementStep(); this.incrementStep();
this.refs.slidesContainer.nextSlide({ piece_id: pieceId });
this.refs.slidesContainer.nextSlide({ piece_id: response.piece.id });
}, },
handleAdditionalDataSuccess() { handleAdditionalDataSuccess() {
// We need to refetch the piece again after submitting the additional data
// since we want it's otherData to be displayed when the user choses to click
// on the browsers back button.
PieceActions.fetchOne(this.state.piece.id);
this.refreshPieceList(); this.refreshPieceList();
this.history.pushState(null, `/collection`); this.history.pushState(null, `/collection`);
@ -107,13 +89,21 @@ let LumenusRegisterPiece = React.createClass({
// We need to increase the step to lock the forms that are already filled out // We need to increase the step to lock the forms that are already filled out
incrementStep() { incrementStep() {
// also increase step
let newStep = this.state.step + 1;
this.setState({ this.setState({
step: newStep step: this.state.step + 1
}); });
}, },
getPieceFromQueryParam() {
const queryParams = this.props.location.query;
// Since every step of this register process is atomic,
// we may need to enter the process at step 1 or 2.
// If this is the case, we'll need the piece number to complete submission.
// It is encoded in the URL as a queryParam and we're checking for it here.
return queryParams && queryParams.piece_id;
},
refreshPieceList() { refreshPieceList() {
PieceListActions.fetchPieceList( PieceListActions.fetchPieceList(
this.state.page, this.state.page,
@ -161,7 +151,7 @@ let LumenusRegisterPiece = React.createClass({
type="number" type="number"
placeholder="(e.g. 32)" placeholder="(e.g. 32)"
min={0} min={0}
required/> required />
</Property> </Property>
</RegisterPieceForm> </RegisterPieceForm>
</Col> </Col>
@ -172,8 +162,8 @@ let LumenusRegisterPiece = React.createClass({
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}> <Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<LumenusAdditionalDataForm <LumenusAdditionalDataForm
handleSuccess={this.handleAdditionalDataSuccess} handleSuccess={this.handleAdditionalDataSuccess}
piece={this.state.piece} pieceId={this.getPieceFromQueryParam()}
location={this.props.location}/> showHeading />
</Col> </Col>
</Row> </Row>
</div> </div>