1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-28 00:28:00 +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,
handleChange: React.PropTypes.func,
ignoreFocus: React.PropTypes.bool,
name: React.PropTypes.string.isRequired,
name: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number
]).isRequired,
className: React.PropTypes.string,
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';
let LumenusEditionContainer = React.createClass({
propTypes: {
params: React.PropTypes.object,
location: React.PropTypes.object
},
propTypes: EditionContainer.propTypes,
render() {
return (
<EditionContainer
params={this.props.params}
{...this.props}
actionPanelButtonListType={LumenusAclButtonList}
furtherDetailsType={LumenusFurtherDetails}
location={this.props.location} />
furtherDetailsType={LumenusFurtherDetails} />
);
}
});

View File

@ -4,43 +4,20 @@ import React from 'react';
import LumenusAdditionalDataForm from '../lumenus_forms/lumenus_additional_data_form'
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
let FurtherDetails = React.createClass({
let LumenusFurtherDetails = React.createClass({
propTypes: {
pieceId: React.PropTypes.number,
extraData: React.PropTypes.object,
otherData: React.PropTypes.arrayOf(React.PropTypes.object),
handleSuccess: React.PropTypes.func,
location: React.PropTypes.object
},
showNotification() {
this.props.handleSuccess();
let notification = new GlobalNotificationModel('Further details updated', 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
},
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 (
<LumenusAdditionalDataForm
piece={piece}
handleSuccess={this.showNotification}
{...this.props}
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';
let LumenusPieceContainer = React.createClass({
propTypes: {
params: React.PropTypes.object,
location: React.PropTypes.object
},
propTypes: PieceContainer.propTypes,
render() {
return (
<PieceContainer
params={this.props.params}
furtherDetailsType={LumenusFurtherDetails}
location={this.props.location} />
{...this.props}
furtherDetailsType={LumenusFurtherDetails} />
);
}
});

View File

@ -14,38 +14,70 @@ import GlobalNotificationActions from '../../../../../../actions/global_notifica
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 AppConstants from '../../../../../../constants/application_constants';
import requests from '../../../../../../utils/requests';
import { mergeOptions } from '../../../../../../utils/general_utils';
import { getLangText } from '../../../../../../utils/lang_utils';
let LumenusAdditionalDataForm = React.createClass({
propTypes: {
handleSuccess: React.PropTypes.func,
piece: React.PropTypes.shape({
id: React.PropTypes.number,
extra_data: React.PropTypes.object,
other_data: React.PropTypes.arrayOf(React.PropTypes.object)
}).isRequired,
isInline: React.PropTypes.bool
},
getDefaultProps() {
return {
isInline: false
};
pieceId: React.PropTypes.oneOfType([
React.PropTypes.number,
React.PropTypes.string
]),
isInline: React.PropTypes.bool,
showHeading: React.PropTypes.bool,
showNotification: React.PropTypes.bool,
handleSuccess: React.PropTypes.func
},
getInitialState() {
return {
isUploadReady: false
};
const pieceStore = PieceStore.getState();
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() {
let notification = new GlobalNotificationModel(getLangText('Further details successfully updated'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
componentDidMount() {
PieceStore.listen(this.onChange);
// 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() {
@ -61,10 +93,23 @@ let LumenusAdditionalDataForm = React.createClass({
return {
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() {
this.setState({
isUploadReady: false
@ -78,7 +123,8 @@ let LumenusAdditionalDataForm = React.createClass({
},
render() {
let { piece, isInline, handleSuccess } = this.props;
const { isInline, handleSuccess, showHeading, showNotification } = this.props;
const { piece } = this.state;
let buttons, spinner, heading;
if (!isInline) {
@ -97,13 +143,13 @@ let LumenusAdditionalDataForm = React.createClass({
</div>
);
heading = (
heading = showHeading ? (
<div className="ascribe-form-header">
<h3>
{getLangText('Provide additional details')}
</h3>
</div>
);
) : null;
}
if (piece && piece.id) {
@ -111,8 +157,9 @@ let LumenusAdditionalDataForm = React.createClass({
<Form
className="ascribe-form-bordered"
ref='form'
key={this.state.forceUpdateKey}
url={requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: piece.id})}
handleSuccess={handleSuccess || this.handleSuccess}
handleSuccess={showNotification ? this.handleSuccessWithNotification : handleSuccess}
getFormData={this.getFormData}
buttons={buttons}
spinner={spinner}>
@ -144,11 +191,11 @@ let LumenusAdditionalDataForm = React.createClass({
required />
</Property>
<Property
name='tech_details'
name='technology_details'
label={getLangText('Technology Details')}>
<InputTextAreaToggable
rows={1}
defaultValue={piece.extra_data.tech_details}
defaultValue={piece.extra_data.technology_details}
placeholder={getLangText('Enter technological details about the work was produced...')}
required />
</Property>

View File

@ -13,7 +13,6 @@ import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import PieceStore from '../../../../../stores/piece_store';
import PieceActions from '../../../../../actions/piece_actions';
import PieceListStore from '../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../actions/piece_list_actions';
@ -35,7 +34,6 @@ let LumenusRegisterPiece = React.createClass({
return mergeOptions(
UserStore.getState(),
PieceListStore.getState(),
PieceStore.getState(),
{
selectedLicense: 0,
isFineUploaderActive: false,
@ -46,33 +44,22 @@ let LumenusRegisterPiece = React.createClass({
componentDidMount() {
PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
PieceStore.listen(this.onChange);
UserActions.fetchCurrentUser();
let 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.
//
// 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);
}
// Reset the piece store to make sure that we don't display old data
// if the user repeatedly registers works
PieceActions.updatePiece({});
},
componentWillUnmount() {
PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceStore.unlisten(this.onChange);
},
onChange(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
this.setState({
isFineUploaderActive: true
@ -80,26 +67,21 @@ let LumenusRegisterPiece = React.createClass({
}
},
handleRegisterSuccess(response){
handleRegisterSuccess(response) {
this.refreshPieceList();
// also start loading the piece for the next step
if(response && response.piece) {
PieceActions.updatePiece({});
// Use the response's piece for the next step if available
let pieceId = null;
if (response && response.piece) {
pieceId = response.piece.id;
PieceActions.updatePiece(response.piece);
}
this.incrementStep();
this.refs.slidesContainer.nextSlide({ piece_id: response.piece.id });
this.refs.slidesContainer.nextSlide({ piece_id: pieceId });
},
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.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
incrementStep() {
// also increase step
let newStep = this.state.step + 1;
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() {
PieceListActions.fetchPieceList(
this.state.page,
@ -161,7 +151,7 @@ let LumenusRegisterPiece = React.createClass({
type="number"
placeholder="(e.g. 32)"
min={0}
required/>
required />
</Property>
</RegisterPieceForm>
</Col>
@ -172,8 +162,8 @@ let LumenusRegisterPiece = React.createClass({
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<LumenusAdditionalDataForm
handleSuccess={this.handleAdditionalDataSuccess}
piece={this.state.piece}
location={this.props.location}/>
pieceId={this.getPieceFromQueryParam()}
showHeading />
</Col>
</Row>
</div>