+ );
+ }
+ }
+});
+
+export default LumenusAdditionalDataForm;
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_register_piece.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_register_piece.js
new file mode 100644
index 00000000..9d671ee0
--- /dev/null
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_register_piece.js
@@ -0,0 +1,195 @@
+'use strict';
+
+import React from 'react';
+import { History } from 'react-router';
+
+import Col from 'react-bootstrap/lib/Col';
+import Row from 'react-bootstrap/lib/Row';
+
+import Property from '../../../../ascribe_forms/property';
+import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
+
+import WhitelabelActions from '../../../../../actions/whitelabel_actions';
+import WhitelabelStore from '../../../../../stores/whitelabel_store';
+
+import PieceListStore from '../../../../../stores/piece_list_store';
+import PieceListActions from '../../../../../actions/piece_list_actions';
+
+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 LumenusAdditionalDataForm from './lumenus_forms/lumenus_additional_data_form';
+
+import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
+
+import { getLangText } from '../../../../../utils/lang_utils';
+import { setDocumentTitle } from '../../../../../utils/dom_utils';
+import { mergeOptions } from '../../../../../utils/general_utils';
+
+
+let LumenusRegisterPiece = React.createClass({
+ propTypes: {
+ location: React.PropTypes.object
+ },
+
+ mixins: [History],
+
+ getInitialState(){
+ return mergeOptions(
+ UserStore.getState(),
+ PieceListStore.getState(),
+ PieceStore.getState(),
+ WhitelabelStore.getState(),
+ {
+ selectedLicense: 0,
+ isFineUploaderActive: false,
+ step: 0
+ });
+ },
+
+ componentDidMount() {
+ PieceListStore.listen(this.onChange);
+ UserStore.listen(this.onChange);
+ PieceStore.listen(this.onChange);
+ WhitelabelStore.listen(this.onChange);
+ UserActions.fetchCurrentUser();
+ WhitelabelActions.fetchWhitelabel();
+
+ 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);
+ }
+ },
+
+ componentWillUnmount() {
+ PieceListStore.unlisten(this.onChange);
+ UserStore.unlisten(this.onChange);
+ PieceStore.unlisten(this.onChange);
+ WhitelabelStore.unlisten(this.onChange);
+ },
+
+ onChange(state) {
+ this.setState(state);
+
+ if(this.state.currentUser && this.state.currentUser.email) {
+ // we should also make the fineuploader component editable again
+ this.setState({
+ isFineUploaderActive: true
+ });
+ }
+ },
+
+ handleRegisterSuccess(response){
+ this.refreshPieceList();
+
+ // also start loading the piece for the next step
+ if(response && response.piece) {
+ PieceActions.updatePiece({});
+ PieceActions.updatePiece(response.piece);
+ }
+
+ this.incrementStep();
+
+ this.refs.slidesContainer.nextSlide({ piece_id: response.piece.id });
+ },
+
+ 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`);
+ },
+
+ // 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
+ });
+ },
+
+ refreshPieceList() {
+ PieceListActions.fetchPieceList(
+ this.state.page,
+ this.state.pageSize,
+ this.state.searchTerm,
+ this.state.orderBy,
+ this.state.orderAsc,
+ this.state.filterBy
+ );
+ },
+
+ // basically redirects to the second slide (index: 1), when the user is not logged in
+ onLoggedOut() {
+ this.history.pushState(null, '/login');
+ },
+
+ render() {
+ setDocumentTitle(getLangText('Register a new piece'));
+
+ return (
+
+
+
+ );
+ }
+});
+
+export default LumenusRegisterPiece;
diff --git a/js/components/whitelabel/wallet/wallet_routes.js b/js/components/whitelabel/wallet/wallet_routes.js
index e258dfa7..e170d671 100644
--- a/js/components/whitelabel/wallet/wallet_routes.js
+++ b/js/components/whitelabel/wallet/wallet_routes.js
@@ -30,6 +30,7 @@ import IkonotvPieceContainer from './components/ikonotv/ikonotv_detail/ikonotv_p
import IkonotvContractNotifications from './components/ikonotv/ikonotv_contract_notifications';
import LumenusPieceList from './components/lumenus/lumenus_piece_list';
+import LumenusRegisterPiece from './components/lumenus/lumenus_register_piece';
import CCRegisterPiece from './components/cc/cc_register_piece';
@@ -174,7 +175,7 @@ let ROUTES = {
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(ContractSettings)}/>
Date: Thu, 22 Oct 2015 14:14:06 +0200
Subject: [PATCH 05/83] Fix typo in edition api url
---
.../ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js | 1 -
js/components/whitelabel/wallet/constants/wallet_api_urls.js | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js
index f5d97842..2a4d4301 100644
--- a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js
+++ b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js
@@ -137,7 +137,6 @@ let PieceListBulkModal = React.createClass({
} else {
return null;
}
-
}
});
diff --git a/js/components/whitelabel/wallet/constants/wallet_api_urls.js b/js/components/whitelabel/wallet/constants/wallet_api_urls.js
index 8f4543c7..87ee6b14 100644
--- a/js/components/whitelabel/wallet/constants/wallet_api_urls.js
+++ b/js/components/whitelabel/wallet/constants/wallet_api_urls.js
@@ -21,7 +21,7 @@ function getWalletApiUrls(subdomain) {
}
else if (subdomain === 'lumenus'){
return {
- 'editions': walletConstants.walletApiEndpoint + subdomain + 'editions/',
+ 'editions': walletConstants.walletApiEndpoint + subdomain + '/editions/',
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/',
'user': walletConstants.walletApiEndpoint + subdomain + '/users/'
};
From 3dedc93d2e979fc33c3d821343256691a03122b8 Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Fri, 23 Oct 2015 14:19:44 +0200
Subject: [PATCH 06/83] Force consignee to be submissions@lumenus.co
Special white label form settings only defined for consign form for
now, but could be added to others as needed.
---
js/components/ascribe_buttons/acl_button.js | 70 ++++++++-----------
js/components/ascribe_forms/form_consign.js | 10 ++-
.../cyland/cyland_register_piece.js | 7 +-
js/constants/application_constants.js | 8 ++-
js/utils/form_utils.js | 57 ++++++++++++---
5 files changed, 97 insertions(+), 55 deletions(-)
diff --git a/js/components/ascribe_buttons/acl_button.js b/js/components/ascribe_buttons/acl_button.js
index e3c7fa1c..ee337149 100644
--- a/js/components/ascribe_buttons/acl_button.js
+++ b/js/components/ascribe_buttons/acl_button.js
@@ -34,15 +34,19 @@ let AclButton = React.createClass({
className: React.PropTypes.string
},
- isPiece(){
+ isPiece() {
return this.props.pieceOrEditions.constructor !== Array;
},
- actionProperties(){
+ actionProperties() {
+ let message = getAclFormMessage({
+ aclName: this.props.action,
+ entities: this.props.pieceOrEditions,
+ isPiece: this.isPiece(),
+ senderName: this.props.currentUser.username
+ });
- let message = getAclFormMessage(this.props.action, this.getTitlesString(), this.props.currentUser.username);
-
- if (this.props.action === 'acl_consign'){
+ if (this.props.action === 'acl_consign') {
return {
title: getLangText('Consign artwork'),
tooltip: getLangText('Have someone else sell the artwork'),
@@ -51,11 +55,10 @@ let AclButton = React.createClass({
message={message}
id={this.getFormDataId()}
url={ApiUrls.ownership_consigns}/>
- ),
+ ),
handleSuccess: this.showNotification
};
- }
- if (this.props.action === 'acl_unconsign'){
+ } else if (this.props.action === 'acl_unconsign') {
return {
title: getLangText('Unconsign artwork'),
tooltip: getLangText('Have the owner manage his sales again'),
@@ -64,10 +67,10 @@ let AclButton = React.createClass({
message={message}
id={this.getFormDataId()}
url={ApiUrls.ownership_unconsigns}/>
- ),
+ ),
handleSuccess: this.showNotification
};
- }else if (this.props.action === 'acl_transfer') {
+ } else if (this.props.action === 'acl_transfer') {
return {
title: getLangText('Transfer artwork'),
tooltip: getLangText('Transfer the ownership of the artwork'),
@@ -79,32 +82,32 @@ let AclButton = React.createClass({
),
handleSuccess: this.showNotification
};
- }
- else if (this.props.action === 'acl_loan'){
+ } else if (this.props.action === 'acl_loan') {
return {
title: getLangText('Loan artwork'),
tooltip: getLangText('Loan your artwork for a limited period of time'),
- form: (
+ url={this.isPiece() ? ApiUrls.ownership_loans_pieces
+ : ApiUrls.ownership_loans_editions}/>
),
handleSuccess: this.showNotification
};
- }
- else if (this.props.action === 'acl_loan_request'){
+ } else if (this.props.action === 'acl_loan_request') {
return {
title: getLangText('Loan artwork'),
tooltip: getLangText('Someone requested you to loan your artwork for a limited period of time'),
- form: (
),
handleSuccess: this.showNotification
};
- }
- else if (this.props.action === 'acl_share'){
+ } else if (this.props.action === 'acl_share') {
return {
title: getLangText('Share artwork'),
tooltip: getLangText('Share the artwork'),
@@ -112,8 +115,9 @@ let AclButton = React.createClass({
- ),
+ url={this.isPiece() ? ApiUrls.ownership_shares_pieces
+ : ApiUrls.ownership_shares_editions}/>
+ ),
handleSuccess: this.showNotification
};
} else {
@@ -121,32 +125,18 @@ let AclButton = React.createClass({
}
},
- showNotification(response){
+ showNotification(response) {
this.props.handleSuccess();
- if(response.notification) {
+ if (response.notification) {
let notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
}
},
- // plz move to share form
- getTitlesString(){
- if (this.isPiece()){
- return '\"' + this.props.pieceOrEditions.title + '\"';
- }
- else {
- return this.props.pieceOrEditions.map(function(edition) {
- return '- \"' + edition.title + ', ' + getLangText('edition') + ' ' + edition.edition_number + '\"\n';
- }).join('');
- }
-
- },
-
getFormDataId(){
if (this.isPiece()) {
return {piece_id: this.props.pieceOrEditions.id};
- }
- else {
+ } else {
return {bitcoin_id: this.props.pieceOrEditions.map(function(edition){
return edition.bitcoin_id;
}).join()};
@@ -162,7 +152,7 @@ let AclButton = React.createClass({
},
render() {
- if (this.props.availableAcls){
+ if (this.props.availableAcls) {
let shouldDisplay = this.props.availableAcls[this.props.action];
let aclProps = this.actionProperties();
let buttonClassName = this.props.buttonAcceptClassName ? this.props.buttonAcceptClassName : '';
@@ -183,4 +173,4 @@ let AclButton = React.createClass({
}
});
-export default AclButton;
\ No newline at end of file
+export default AclButton;
diff --git a/js/components/ascribe_forms/form_consign.js b/js/components/ascribe_forms/form_consign.js
index f57e0045..7e3233e8 100644
--- a/js/components/ascribe_forms/form_consign.js
+++ b/js/components/ascribe_forms/form_consign.js
@@ -11,6 +11,7 @@ import InputTextAreaToggable from './input_textarea_toggable';
import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang_utils.js';
+import { getSubdomainFormSettings } from '../../utils/form_utils';
let ConsignForm = React.createClass({
propTypes: {
@@ -25,6 +26,8 @@ let ConsignForm = React.createClass({
},
render() {
+ let envSettings = getSubdomainFormSettings('consign');
+
return (
-
);
}
});
-
export default Form;
diff --git a/js/components/ascribe_forms/form_consign.js b/js/components/ascribe_forms/form_consign.js
index 9617acd4..db18ba4f 100644
--- a/js/components/ascribe_forms/form_consign.js
+++ b/js/components/ascribe_forms/form_consign.js
@@ -9,6 +9,9 @@ import Property from './property';
import InputTextAreaToggable from './input_textarea_toggable';
import AscribeSpinner from '../ascribe_spinner';
+
+import AclInformation from '../ascribe_buttons/acl_information';
+
import { getLangText } from '../../utils/lang_utils.js';
let ConsignForm = React.createClass({
@@ -47,6 +50,7 @@ let ConsignForm = React.createClass({
}>
+
}>
+
{getLangText('Are you sure you would like to permanently delete this edition')}?
{getLangText('This is an irrevocable action%s', '.')}
diff --git a/js/components/ascribe_forms/form_delete_piece.js b/js/components/ascribe_forms/form_delete_piece.js
index 4b0c9e39..ee066d3f 100644
--- a/js/components/ascribe_forms/form_delete_piece.js
+++ b/js/components/ascribe_forms/form_delete_piece.js
@@ -4,6 +4,8 @@ import React from 'react';
import Form from '../ascribe_forms/form';
+import AclInformation from '../ascribe_buttons/acl_information';
+
import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner';
@@ -51,6 +53,7 @@ let PieceDeleteForm = React.createClass({
}>
+
{getLangText('Are you sure you would like to permanently delete this piece')}?
{getLangText('This is an irrevocable action%s', '.')}
diff --git a/js/components/ascribe_forms/form_loan.js b/js/components/ascribe_forms/form_loan.js
index 919b6118..d6102f14 100644
--- a/js/components/ascribe_forms/form_loan.js
+++ b/js/components/ascribe_forms/form_loan.js
@@ -19,7 +19,7 @@ import AscribeSpinner from '../ascribe_spinner';
import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
-
+import AclInformation from '../ascribe_buttons/acl_information';
let LoanForm = React.createClass({
propTypes: {
@@ -232,6 +232,7 @@ let LoanForm = React.createClass({
{this.props.loanHeading}
+
}>
+
diff --git a/js/components/ascribe_forms/form_transfer.js b/js/components/ascribe_forms/form_transfer.js
index 010c4829..3fb95ff6 100644
--- a/js/components/ascribe_forms/form_transfer.js
+++ b/js/components/ascribe_forms/form_transfer.js
@@ -9,6 +9,8 @@ import Form from './form';
import Property from './property';
import InputTextAreaToggable from './input_textarea_toggable';
+import AclInformation from '../ascribe_buttons/acl_information';
+
import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang_utils.js';
@@ -52,6 +54,7 @@ let TransferForm = React.createClass({
}>
+
diff --git a/js/components/ascribe_modal/modal_wrapper.js b/js/components/ascribe_modal/modal_wrapper.js
index f00eee9e..5c3ce742 100644
--- a/js/components/ascribe_modal/modal_wrapper.js
+++ b/js/components/ascribe_modal/modal_wrapper.js
@@ -65,7 +65,7 @@ let ModalWrapper = React.createClass({
{this.props.title}
-
+
{this.renderChildren()}
diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js
index 16886def..38de2af6 100644
--- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js
+++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js
@@ -7,7 +7,7 @@ import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import { getLangText } from '../../utils/lang_utils.js';
-let PieceListToolbarFilterWidgetFilter = React.createClass({
+let PieceListToolbarFilterWidget = React.createClass({
propTypes: {
filterParams: React.PropTypes.arrayOf(
React.PropTypes.shape({
@@ -83,6 +83,7 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({
return (
{/* We iterate over filterParams, to receive the label and then for each
@@ -139,4 +140,4 @@ let PieceListToolbarFilterWidgetFilter = React.createClass({
}
});
-export default PieceListToolbarFilterWidgetFilter;
\ No newline at end of file
+export default PieceListToolbarFilterWidget;
\ No newline at end of file
diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js
index a3615aec..c38144b0 100644
--- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js
+++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_order_widget.js
@@ -54,6 +54,7 @@ let PieceListToolbarOrderWidget = React.createClass({
return (
@@ -72,7 +73,7 @@ let PieceListToolbarOrderWidget = React.createClass({
-1} />
+
);
}
});
From 85eb45b5cd8cb443441e1da40bde06dc1caf604c Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Wed, 4 Nov 2015 00:41:12 +0100
Subject: [PATCH 18/83] Add small changes for previous merges that were missed
---
js/components/piece_list.js | 2 +-
.../lumenus/lumenus_buttons/lumenus_acl_button_list.js | 8 +++++++-
.../lumenus_forms/lumenus_additional_data_form.js | 6 ++----
.../wallet/components/lumenus/lumenus_piece_list.js | 6 +++---
js/stores/edition_list_store.js | 2 +-
js/utils/requests.js | 10 +++-------
6 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/js/components/piece_list.js b/js/components/piece_list.js
index 3099d31a..69f5b23f 100644
--- a/js/components/piece_list.js
+++ b/js/components/piece_list.js
@@ -184,7 +184,7 @@ let PieceList = React.createClass({
this.fetchSelectedPieceEditionList()
.forEach((pieceId) => {
- EditionListActions.refreshEditionList({pieceId, filterBy: {}});
+ EditionListActions.refreshEditionList({pieceId});
});
EditionListActions.clearAllEditionSelections();
},
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
index b902b93b..76608032 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
@@ -6,6 +6,7 @@ import LumenusSubmitButton from './lumenus_submit_button';
import DeleteButton from '../../../../../ascribe_buttons/delete_button';
import ShareButton from '../../../../../ascribe_buttons/acls/share_button';
+import TransferButton from '../../../../../ascribe_buttons/acls/transfer_button';
import UserActions from '../../../../../../actions/user_actions';
import UserStore from '../../../../../../stores/user_store';
@@ -46,13 +47,18 @@ let LumenusAclButtonList = React.createClass({
+
{this.props.children}
);
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js
index e08a8bc7..3e67c21a 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js
@@ -28,8 +28,7 @@ let LumenusAdditionalDataForm = React.createClass({
extra_data: React.PropTypes.object,
other_data: React.PropTypes.arrayOf(React.PropTypes.object)
}).isRequired,
- isInline: React.PropTypes.bool,
- location: React.PropTypes.object
+ isInline: React.PropTypes.bool
},
getDefaultProps() {
@@ -125,8 +124,7 @@ let LumenusAdditionalDataForm = React.createClass({
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
pieceId={piece.id}
- otherData={piece.other_data}
- location={this.props.location}/>
+ otherData={piece.other_data} />
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
index 14dac214..ccfb7e1c 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
@@ -22,10 +22,10 @@ let LumenusPieceList = React.createClass({
redirectTo="/register_piece?slide_num=0"
bulkModalButtonListType={LumenusAclButtonList}
filterParams={[{
- label: getLangText('Show works I have'),
+ label: getLangText('Show works I can'),
items: [{
- key: 'acl_consigned',
- label: getLangText('consigned to Lumenus')
+ key: 'acl_consign',
+ label: getLangText('consign to Lumenus')
}]
}]}
location={this.props.location}/>
diff --git a/js/stores/edition_list_store.js b/js/stores/edition_list_store.js
index 4ccada4e..107f9af4 100644
--- a/js/stores/edition_list_store.js
+++ b/js/stores/edition_list_store.js
@@ -60,7 +60,7 @@ class EditionListStore {
* We often just have to refresh the edition list for a certain pieceId,
* this method provides exactly that functionality without any side effects
*/
- onRefreshEditionList({pieceId, filterBy}) {
+ onRefreshEditionList({pieceId, filterBy = {}}) {
// It may happen that the user enters the site logged in already
// through /editions
// If he then tries to delete a piece/edition and this method is called,
diff --git a/js/utils/requests.js b/js/utils/requests.js
index 8f015a11..112588e0 100644
--- a/js/utils/requests.js
+++ b/js/utils/requests.js
@@ -99,8 +99,7 @@ class Requests {
return newUrl;
}
- request(verb, url, options) {
- options = options || {};
+ request(verb, url, options = {}) {
let merged = Object.assign({}, this.httpOptions, options);
let csrftoken = getCookie(AppConstants.csrftoken);
if (csrftoken) {
@@ -128,13 +127,10 @@ class Requests {
}
_putOrPost(url, paramsAndBody, method) {
- let paramsCopy = Object.assign({}, paramsAndBody);
let params = omitFromObject(paramsAndBody, ['body']);
let newUrl = this.prepareUrl(url, params);
- let body = null;
- if (paramsCopy && paramsCopy.body) {
- body = JSON.stringify(paramsCopy.body);
- }
+ let body = paramsAndBody && paramsAndBody.body ? JSON.stringify(paramsAndBody.body)
+ : null;
return this.request(method, newUrl, { body });
}
From adf0d411d67a06331f1b002f663fb50cf4f9426e Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Thu, 5 Nov 2015 11:56:17 +0100
Subject: [PATCH 19/83] 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.
---
js/components/ascribe_forms/property.js | 5 +-
.../lumenus_edition_container.js | 10 +-
.../lumenus_detail/lumenus_further_details.js | 31 +-----
.../lumenus_detail/lumenus_piece_container.js | 10 +-
.../lumenus_additional_data_form.js | 99 ++++++++++++++-----
.../lumenus/lumenus_register_piece.js | 58 +++++------
6 files changed, 111 insertions(+), 102 deletions(-)
diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js
index 793be538..b4baf9df 100644
--- a/js/components/ascribe_forms/property.js
+++ b/js/components/ascribe_forms/property.js
@@ -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,
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_edition_container.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_edition_container.js
index 77cca99c..c81fb0bb 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_edition_container.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_edition_container.js
@@ -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 (
+ furtherDetailsType={LumenusFurtherDetails} />
);
}
});
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_further_details.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_further_details.js
index 17dbad20..79199b68 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_further_details.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_further_details.js
@@ -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 (
+ showNotification />
);
}
});
-export default FurtherDetails;
+export default LumenusFurtherDetails;
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_piece_container.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_piece_container.js
index 5b7cf8ee..391a7cb5 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_piece_container.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_detail/lumenus_piece_container.js
@@ -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 (
+ {...this.props}
+ furtherDetailsType={LumenusFurtherDetails} />
);
}
});
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js
index 3e67c21a..3b69ce81 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_forms/lumenus_additional_data_form.js
@@ -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({
);
- heading = (
+ heading = showHeading ? (
{getLangText('Provide additional details')}
- );
+ ) : null;
}
if (piece && piece.id) {
@@ -111,8 +157,9 @@ let LumenusAdditionalDataForm = React.createClass({
@@ -144,11 +191,11 @@ let LumenusAdditionalDataForm = React.createClass({
required />
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_register_piece.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_register_piece.js
index 342c4a32..3d43418d 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_register_piece.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_register_piece.js
@@ -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 />
@@ -172,8 +162,8 @@ let LumenusRegisterPiece = React.createClass({
+ pieceId={this.getPieceFromQueryParam()}
+ showHeading />
From 4ca8ca8feb8a992e89fba0c93380536181497d1c Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Thu, 5 Nov 2015 11:58:32 +0100
Subject: [PATCH 20/83] Show AdditionalDetailsModal from SubmitButton when
details need to be filled in
---
.../lumenus_buttons/lumenus_submit_button.js | 102 +++++++++++++++---
1 file changed, 87 insertions(+), 15 deletions(-)
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js
index 941b3ec0..6722beb7 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js
@@ -3,19 +3,30 @@
import React from 'react';
import classNames from 'classnames';
+import LumenusAdditionalDataForm from '../lumenus_forms/lumenus_additional_data_form';
+
import ConsignButton from '../../../../../ascribe_buttons/acls/consign_button';
+import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory';
+import ConsignForm from '../../../../../ascribe_forms/form_consign';
+
+import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
+
+import PieceActions from '../../../../../../actions/piece_actions';
import WhitelabelActions from '../../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../../stores/whitelabel_store';
+import ApiUrls from '../../../../../../constants/api_urls';
+
+import { getAclFormDataId } from '../../../../../../utils/form_utils';
import { getLangText } from '../../../../../../utils/lang_utils';
let LumenusSubmitButton = React.createClass({
propTypes: {
availableAcls: React.PropTypes.object.isRequired,
- currentUser: React.PropTypes.object,
- pieceOrEditions: React.PropTypes.array,
- handleSuccess: React.PropTypes.func,
+ currentUser: React.PropTypes.object.isRequired,
+ editions: React.PropTypes.array.isRequired,
+ handleSuccess: React.PropTypes.func.isRequired,
className: React.PropTypes.string,
},
@@ -25,6 +36,7 @@ let LumenusSubmitButton = React.createClass({
componentDidMount() {
WhitelabelStore.listen(this.onChange);
+
WhitelabelActions.fetchWhitelabel();
},
@@ -36,19 +48,79 @@ let LumenusSubmitButton = React.createClass({
this.setState(state);
},
- render() {
- const { availableAcls, currentUser, className, pieceOrEditions, handleSuccess } = this.props;
+ getFormDataId() {
+ return getAclFormDataId(false, this.props.editions);
+ },
- return (
-
- );
+ getAggregateEditionDetails() {
+ const { editions } = this.props;
+
+ // Currently, we only care if all the given editions are from the same parent piece
+ // and if they can be submitted
+ return editions.reduce((details, curEdition) => {
+ return {
+ solePieceId: details.solePieceId === curEdition.parent ? details.solePieceId : null,
+ canSubmit: details.canSubmit && curEdition.acl.acl_wallet_submit
+ };
+ }, {
+ solePieceId: editions.length > 0 ? editions[0].parent : null,
+ canSubmit: editions.length > 0 ? editions[0].acl.acl_wallet_submit : false
+ });
+ },
+
+ handleAdditionalDataSuccess(pieceId) {
+ // Fetch newly updated piece to update the views
+ PieceActions.fetchOne(pieceId);
+
+ this.refs.consignModal.show();
+ },
+
+ render() {
+ const { availableAcls, currentUser, className, editions, handleSuccess } = this.props;
+ const buttonTitle = getLangText('CONSIGN TO LUMENUS');
+
+ const { solePieceId, canSubmit } = this.getAggregateEditionDetails();
+
+ if (solePieceId && !canSubmit) {
+ return (
+
+
+ {buttonTitle}
+
+ }
+ handleSuccess={this.handleAdditionalDataSuccess.bind(this, solePieceId)}
+ title={getLangText('Add additional information')}>
+
+
+
+
+
+
+
+ );
+ } else {
+ return (
+
+ );
+ }
}
});
From 6223248ea03e58c2af3a460455c112a44ee3ab3d Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Thu, 5 Nov 2015 13:51:40 +0100
Subject: [PATCH 21/83] Fix lumenus whitelabel api endpoints
---
.../whitelabel/wallet/constants/wallet_api_urls.js | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/js/components/whitelabel/wallet/constants/wallet_api_urls.js b/js/components/whitelabel/wallet/constants/wallet_api_urls.js
index 87ee6b14..11e29a39 100644
--- a/js/components/whitelabel/wallet/constants/wallet_api_urls.js
+++ b/js/components/whitelabel/wallet/constants/wallet_api_urls.js
@@ -4,25 +4,26 @@ import walletConstants from './wallet_application_constants';
// gets subdomain as a parameter
function getWalletApiUrls(subdomain) {
- if (subdomain === 'cyland'){
+ if (subdomain === 'cyland') {
return {
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/',
'piece_extradata': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/extradata/',
'user': walletConstants.walletApiEndpoint + subdomain + '/users/'
};
- }
- else if (subdomain === 'ikonotv'){
+ } else if (subdomain === 'ikonotv') {
return {
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/',
'user': walletConstants.walletApiEndpoint + subdomain + '/users/'
};
- }
- else if (subdomain === 'lumenus'){
+ } else if (subdomain === 'lumenus') {
return {
- 'editions': walletConstants.walletApiEndpoint + subdomain + '/editions/',
+ 'editions_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/editions/',
+ 'edition': walletConstants.walletApiEndpoint + subdomain + '/editions/${edition_id}/',
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/',
+ 'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/',
+ 'piece_extradata': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/extradata/',
'user': walletConstants.walletApiEndpoint + subdomain + '/users/'
};
}
From 04e453b28cf6041b4c4e92c2f662259ed00f77d8 Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Thu, 5 Nov 2015 16:05:20 +0100
Subject: [PATCH 22/83] Fix lumenus edition endpoint
---
js/components/whitelabel/wallet/constants/wallet_api_urls.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/components/whitelabel/wallet/constants/wallet_api_urls.js b/js/components/whitelabel/wallet/constants/wallet_api_urls.js
index 11e29a39..27be363d 100644
--- a/js/components/whitelabel/wallet/constants/wallet_api_urls.js
+++ b/js/components/whitelabel/wallet/constants/wallet_api_urls.js
@@ -20,7 +20,7 @@ function getWalletApiUrls(subdomain) {
} else if (subdomain === 'lumenus') {
return {
'editions_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/editions/',
- 'edition': walletConstants.walletApiEndpoint + subdomain + '/editions/${edition_id}/',
+ 'edition': walletConstants.walletApiEndpoint + subdomain + '/editions/${bitcoin_id}/',
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/',
'piece_extradata': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/extradata/',
From fb62d2d2e0461b2511eb38d55f333e11d9332916 Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Fri, 6 Nov 2015 14:00:58 +0100
Subject: [PATCH 23/83] Check for completion of additional data on front end
In the end, it made more sense to check if all the additional details
have been filled in on the front end than receiving an acl or flag from
the backend.
---
.../lumenus_buttons/lumenus_submit_button.js | 28 +++++++++++++++----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js
index 6722beb7..7046c069 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_submit_button.js
@@ -12,6 +12,8 @@ import ConsignForm from '../../../../../ascribe_forms/form_consign';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
+import AclProxy from '../../../../../acl_proxy';
+
import PieceActions from '../../../../../../actions/piece_actions';
import WhitelabelActions from '../../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../../stores/whitelabel_store';
@@ -48,6 +50,20 @@ let LumenusSubmitButton = React.createClass({
this.setState(state);
},
+ canEditionBeSubmitted(edition) {
+ if (edition && edition.extra_data && edition.other_data) {
+ const { extra_data, other_data } = edition;
+
+ if (extra_data.artist_bio && extra_data.work_description &&
+ extra_data.technology_details && extra_data.display_instructions &&
+ other_data.length > 0) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
getFormDataId() {
return getAclFormDataId(false, this.props.editions);
},
@@ -60,11 +76,11 @@ let LumenusSubmitButton = React.createClass({
return editions.reduce((details, curEdition) => {
return {
solePieceId: details.solePieceId === curEdition.parent ? details.solePieceId : null,
- canSubmit: details.canSubmit && curEdition.acl.acl_wallet_submit
+ canSubmit: details.canSubmit && this.canEditionBeSubmitted(curEdition)
};
}, {
solePieceId: editions.length > 0 ? editions[0].parent : null,
- canSubmit: editions.length > 0 ? editions[0].acl.acl_wallet_submit : false
+ canSubmit: this.canEditionBeSubmitted(editions[0])
});
},
@@ -83,7 +99,9 @@ let LumenusSubmitButton = React.createClass({
if (solePieceId && !canSubmit) {
return (
-
+
@@ -107,12 +125,12 @@ let LumenusSubmitButton = React.createClass({
pieceOrEditions={editions}
showNotification />
-
+
);
} else {
return (
Date: Fri, 6 Nov 2015 14:01:26 +0100
Subject: [PATCH 24/83] Show unconsign button
---
.../lumenus/lumenus_buttons/lumenus_acl_button_list.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
index 76608032..77657aca 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
@@ -7,6 +7,7 @@ import LumenusSubmitButton from './lumenus_submit_button';
import DeleteButton from '../../../../../ascribe_buttons/delete_button';
import ShareButton from '../../../../../ascribe_buttons/acls/share_button';
import TransferButton from '../../../../../ascribe_buttons/acls/transfer_button';
+import UnconsignButton from '../../../../../ascribe_buttons/acls/transfer_button';
import UserActions from '../../../../../../actions/user_actions';
import UserStore from '../../../../../../stores/user_store';
@@ -59,6 +60,11 @@ let LumenusAclButtonList = React.createClass({
currentUser={this.state.currentUser}
pieceOrEditions={pieceOrEditions}
handleSuccess={handleSuccess} />
+
{this.props.children}
);
From 318a0bf4b2c0f2e8b383342de5a54d4ad117b158 Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Fri, 6 Nov 2015 14:10:54 +0100
Subject: [PATCH 25/83] Filter the collection to only show the consignable
items by default
---
.../piece_list_bulk_modal.js | 31 -------------------
.../piece_list_toolbar_filter_widget.js | 10 +++---
js/components/piece_list.js | 23 +++++++++++++-
.../components/lumenus/lumenus_piece_list.js | 3 +-
4 files changed, 29 insertions(+), 38 deletions(-)
diff --git a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js
index 1380f21d..bb8d4ccc 100644
--- a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js
+++ b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js
@@ -6,12 +6,6 @@ import { mergeOptions } from '../../utils/general_utils';
import EditionListActions from '../../actions/edition_list_actions';
-import UserStore from '../../stores/user_store';
-import UserActions from '../../actions/user_actions';
-
-import PieceListStore from '../../stores/piece_list_store';
-import PieceListActions from '../../actions/piece_list_actions';
-
import PieceListBulkModalSelectedEditionsWidget from './piece_list_bulk_modal_selected_editions_widget';
import { getLangText } from '../../utils/lang_utils.js';
@@ -30,31 +24,6 @@ let PieceListBulkModal = React.createClass({
])
},
- getInitialState() {
- return mergeOptions(
- UserStore.getState(),
- PieceListStore.getState()
- );
- },
-
- componentDidMount() {
- UserStore.listen(this.onChange);
- PieceListStore.listen(this.onChange);
-
- UserActions.fetchCurrentUser();
- PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
- this.state.orderBy, this.state.orderAsc, this.state.filterBy);
- },
-
- componentWillUnmount() {
- PieceListStore.unlisten(this.onChange);
- UserStore.unlisten(this.onChange);
- },
-
- onChange(state) {
- this.setState(state);
- },
-
clearAllSelections() {
EditionListActions.clearAllEditionSelections();
EditionListActions.closeAllEditionLists();
diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js
index 38de2af6..cea41e3b 100644
--- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js
+++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js
@@ -28,7 +28,7 @@ let PieceListToolbarFilterWidget = React.createClass({
},
generateFilterByStatement(param) {
- let filterBy = this.props.filterBy;
+ const { filterBy } = this.props;
if(filterBy) {
// we need hasOwnProperty since the values are all booleans
@@ -56,13 +56,13 @@ let PieceListToolbarFilterWidget = React.createClass({
*/
filterBy(param) {
return () => {
- let filterBy = this.generateFilterByStatement(param);
+ const filterBy = this.generateFilterByStatement(param);
this.props.applyFilterBy(filterBy);
};
},
isFilterActive() {
- let trueValuesOnly = Object.keys(this.props.filterBy).filter((acl) => acl);
+ const trueValuesOnly = Object.keys(this.props.filterBy).filter((acl) => acl);
// We're hiding the star in that complicated matter so that,
// the surrounding button is not resized up on appearance
@@ -74,7 +74,7 @@ let PieceListToolbarFilterWidget = React.createClass({
},
render() {
- let filterIcon = (
+ const filterIcon = (
*
@@ -140,4 +140,4 @@ let PieceListToolbarFilterWidget = React.createClass({
}
});
-export default PieceListToolbarFilterWidget;
\ No newline at end of file
+export default PieceListToolbarFilterWidget;
diff --git a/js/components/piece_list.js b/js/components/piece_list.js
index 69f5b23f..1ea64ff6 100644
--- a/js/components/piece_list.js
+++ b/js/components/piece_list.js
@@ -60,11 +60,17 @@ let PieceList = React.createClass({
}]
};
},
+
getInitialState() {
- return mergeOptions(
+ const stores = mergeOptions(
PieceListStore.getState(),
EditionListStore.getState()
);
+
+ // Use the default filters but use the stores' settings if they're available
+ stores.filterBy = Object.assign(this.getDefaultFilterBy(), stores.filterBy);
+
+ return stores;
},
componentDidMount() {
@@ -96,6 +102,21 @@ let PieceList = React.createClass({
this.setState(state);
},
+ getDefaultFilterBy() {
+ const { filterParams } = this.props;
+ const defaultFilterBy = {};
+
+ filterParams.forEach(({ label, items }) => {
+ items.forEach((item) => {
+ if (typeof item === 'object' && item.defaultValue) {
+ defaultFilterBy[item.key] = true;
+ }
+ });
+ });
+
+ return defaultFilterBy;
+ },
+
paginationGoToPage(page) {
return () => {
// if the users clicks a pager of the pagination,
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
index ccfb7e1c..58ad7813 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
@@ -25,7 +25,8 @@ let LumenusPieceList = React.createClass({
label: getLangText('Show works I can'),
items: [{
key: 'acl_consign',
- label: getLangText('consign to Lumenus')
+ label: getLangText('consign to Lumenus'),
+ defaultValue: true
}]
}]}
location={this.props.location}/>
From b100fdd80a35ae138afd2a33c9c9391e50d6f0bc Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Fri, 6 Nov 2015 16:13:47 +0100
Subject: [PATCH 26/83] Change default collection filter for lumens admin
---
.../lumenus_acl_button_list.js | 2 +-
.../components/lumenus/lumenus_piece_list.js | 39 +++++++++++++++++--
2 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
index 77657aca..8398dbda 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_buttons/lumenus_acl_button_list.js
@@ -7,7 +7,7 @@ import LumenusSubmitButton from './lumenus_submit_button';
import DeleteButton from '../../../../../ascribe_buttons/delete_button';
import ShareButton from '../../../../../ascribe_buttons/acls/share_button';
import TransferButton from '../../../../../ascribe_buttons/acls/transfer_button';
-import UnconsignButton from '../../../../../ascribe_buttons/acls/transfer_button';
+import UnconsignButton from '../../../../../ascribe_buttons/acls/unconsign_button';
import UserActions from '../../../../../../actions/user_actions';
import UserStore from '../../../../../../stores/user_store';
diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
index 58ad7813..ebf90b3c 100644
--- a/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
+++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_piece_list.js
@@ -6,15 +6,48 @@ import LumenusAclButtonList from './lumenus_buttons/lumenus_acl_button_list';
import PieceList from '../../../../piece_list';
-import { getLangText } from '../../../../../utils/lang_utils';
+import UserActions from '../../../../../actions/user_actions';
+import UserStore from '../../../../../stores/user_store';
+import WhitelabelActions from '../../../../../actions/whitelabel_actions';
+import WhitelabelStore from '../../../../../stores/whitelabel_store';
+
import { setDocumentTitle } from '../../../../../utils/dom_utils';
+import { mergeOptions } from '../../../../../utils/general_utils';
+import { getLangText } from '../../../../../utils/lang_utils';
let LumenusPieceList = React.createClass({
propTypes: {
location: React.PropTypes.object
},
+ getInitialState() {
+ return mergeOptions(
+ UserStore.getState(),
+ WhitelabelStore.getState()
+ );
+ },
+
+ componentDidMount() {
+ UserStore.listen(this.onChange);
+ WhitelabelStore.listen(this.onChange);
+
+ UserActions.fetchCurrentUser();
+ WhitelabelActions.fetchWhitelabel();
+ },
+
+ componentWillUnmount() {
+ UserStore.unlisten(this.onChange);
+ WhitelabelStore.unlisten(this.onChange);
+ },
+
+ onChange(state) {
+ this.setState(state);
+ },
+
render() {
+ const { currentUser, whitelabel } = this.state;
+ const isUserAdmin = currentUser.email === whitelabel.user;
+
setDocumentTitle(getLangText('Collection'));
return (
@@ -24,8 +57,8 @@ let LumenusPieceList = React.createClass({
filterParams={[{
label: getLangText('Show works I can'),
items: [{
- key: 'acl_consign',
- label: getLangText('consign to Lumenus'),
+ key: isUserAdmin ? 'acl_transfer' : 'acl_consign',
+ label: getLangText(isUserAdmin ? 'transfer' : 'consign to Lumenus'),
defaultValue: true
}]
}]}
From 84e8e4612f23809c4ff19c719a1bc0df9450f731 Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Tue, 10 Nov 2015 18:24:46 +0100
Subject: [PATCH 27/83] Autofocus message field in consignment form and use
custom labels
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
For now, we’ll let the artist specify their suggested price in the
consignment form’s message field.
---
.../ascribe_buttons/acls/acl_button.js | 2 +-
.../ascribe_forms/acl_form_factory.js | 10 ++-
js/components/ascribe_forms/form_consign.js | 24 ++++---
.../ascribe_forms/input_textarea_toggable.js | 6 ++
.../list_form_request_actions.js | 4 +-
js/components/ascribe_forms/property.js | 7 ++
.../lumenus_buttons/lumenus_submit_button.js | 64 +++++++++++--------
.../lumenus_additional_data_form.js | 6 +-
js/utils/form_utils.js | 4 ++
9 files changed, 84 insertions(+), 43 deletions(-)
diff --git a/js/components/ascribe_buttons/acls/acl_button.js b/js/components/ascribe_buttons/acls/acl_button.js
index 82650bb6..8b21f92a 100644
--- a/js/components/ascribe_buttons/acls/acl_button.js
+++ b/js/components/ascribe_buttons/acls/acl_button.js
@@ -31,7 +31,7 @@ export default function ({ action, displayName, title, tooltip }) {
availableAcls: React.PropTypes.object.isRequired,
buttonAcceptName: React.PropTypes.string,
buttonAcceptClassName: React.PropTypes.string,
- currentUser: React.PropTypes.object.isRequired,
+ currentUser: React.PropTypes.object,
email: React.PropTypes.string,
pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object,
diff --git a/js/components/ascribe_forms/acl_form_factory.js b/js/components/ascribe_forms/acl_form_factory.js
index d5494c2d..9422a351 100644
--- a/js/components/ascribe_forms/acl_form_factory.js
+++ b/js/components/ascribe_forms/acl_form_factory.js
@@ -20,9 +20,11 @@ import { getAclFormMessage, getAclFormDataId } from '../../utils/form_utils';
let AclFormFactory = React.createClass({
propTypes: {
action: React.PropTypes.oneOf(AppConstants.aclList).isRequired,
- currentUser: React.PropTypes.object.isRequired,
+ autoFocusProperty: React.PropTypes.string,
+ currentUser: React.PropTypes.object,
email: React.PropTypes.string,
message: React.PropTypes.string,
+ labels: React.PropTypes.object,
pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.array
@@ -53,10 +55,12 @@ let AclFormFactory = React.createClass({
render() {
const {
action,
+ autoFocusProperty,
pieceOrEditions,
currentUser,
email,
message,
+ labels,
handleSuccess,
showNotification } = this.props;
@@ -64,14 +68,16 @@ let AclFormFactory = React.createClass({
aclName: action,
entities: pieceOrEditions,
isPiece: this.isPiece(),
- senderName: currentUser.username
+ senderName: currentUser && currentUser.username
});
if (action === 'acl_consign') {
return (
diff --git a/js/components/ascribe_forms/form_consign.js b/js/components/ascribe_forms/form_consign.js
index b8417961..c659610d 100644
--- a/js/components/ascribe_forms/form_consign.js
+++ b/js/components/ascribe_forms/form_consign.js
@@ -17,8 +17,10 @@ let ConsignForm = React.createClass({
propTypes: {
url: React.PropTypes.string,
id: React.PropTypes.object,
+ autoFocusProperty: React.PropTypes.string,
email: React.PropTypes.string,
message: React.PropTypes.string,
+ labels: React.PropTypes.object,
handleSuccess: React.PropTypes.func
},
@@ -27,10 +29,12 @@ let ConsignForm = React.createClass({
},
render() {
+ const { autoFocusProperty, email, id, handleSuccess, message, labels, url } = this.props;
+
return (
+
+ );
+ }
+ }
+ },
+
+ getContractCheckbox() {
+ const { contractAgreementList } = this.state;
+
+ if (contractAgreementList && contractAgreementList.length > 0) {
+ // we need to define a key on the InputCheckboxes as otherwise
+ // react is not rerendering them on a store switch and is keeping
+ // the default value of the component (which is in that case true)
+ const contractAgreement = contractAgreementList[0];
+ const { issuer: contractIssuer, blob: { url_safe: contractUrl } } = contractAgreement.contract;
+
+ if (contractAgreement.datetime_accepted) {
+ return (
+
+
+
+ {getLangText('Download contract')}
+
+ {/* We still need to send the server information that we're accepting */}
+
+
+ );
+ } else {
+ return (
+
+
+
+ {getLangText('I agree to the')}
+
+ {getLangText('terms of ')} {contractIssuer}
+
+
+
+
+ );
+ }
+ } else {
+ return (
+
+
+
+ );
+ }
+ },
+
+ render() {
+ return (
+
+ );
+ }
+});
+
+export default ContractAgreementProperty;
diff --git a/js/utils/regex_utils.js b/js/utils/regex_utils.js
new file mode 100644
index 00000000..af948b2b
--- /dev/null
+++ b/js/utils/regex_utils.js
@@ -0,0 +1,7 @@
+'use strict'
+
+export function isEmail(string) {
+ // This is a bit of a weak test for an email, but you really can't win them all
+ // http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address
+ return !!string && string.match(/.*@.*\..*/);
+}
From 328d60b279d51328656f22f1087f862769e6e426 Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Thu, 3 Dec 2015 15:22:37 +0100
Subject: [PATCH 65/83] Rename ContractAgreementForm to avoid confusion with
ContractAgreementProperty
---
...ontract_agreement.js => form_send_contract_agreement.js} | 6 +++---
js/components/whitelabel/wallet/wallet_routes.js | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
rename js/components/ascribe_forms/{form_contract_agreement.js => form_send_contract_agreement.js} (97%)
diff --git a/js/components/ascribe_forms/form_contract_agreement.js b/js/components/ascribe_forms/form_send_contract_agreement.js
similarity index 97%
rename from js/components/ascribe_forms/form_contract_agreement.js
rename to js/components/ascribe_forms/form_send_contract_agreement.js
index fa73fe42..8d0170fd 100644
--- a/js/components/ascribe_forms/form_contract_agreement.js
+++ b/js/components/ascribe_forms/form_send_contract_agreement.js
@@ -21,7 +21,7 @@ import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils';
-let ContractAgreementForm = React.createClass({
+let SendContractAgreementForm = React.createClass({
propTypes: {
handleSuccess: React.PropTypes.func
},
@@ -55,7 +55,7 @@ let ContractAgreementForm = React.createClass({
},
handleSubmitSuccess() {
- let notification = 'Contract agreement send';
+ let notification = 'Contract agreement sent';
notification = new GlobalNotificationModel(notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
@@ -148,4 +148,4 @@ let ContractAgreementForm = React.createClass({
}
});
-export default ContractAgreementForm;
\ No newline at end of file
+export default SendContractAgreementForm;
diff --git a/js/components/whitelabel/wallet/wallet_routes.js b/js/components/whitelabel/wallet/wallet_routes.js
index 92b53dc6..f65e20fb 100644
--- a/js/components/whitelabel/wallet/wallet_routes.js
+++ b/js/components/whitelabel/wallet/wallet_routes.js
@@ -25,7 +25,7 @@ import CylandPieceList from './components/cyland/cyland_piece_list';
import IkonotvLanding from './components/ikonotv/ikonotv_landing';
import IkonotvPieceList from './components/ikonotv/ikonotv_piece_list';
-import ContractAgreementForm from '../../../components/ascribe_forms/form_contract_agreement';
+import SendContractAgreementForm from '../../../components/ascribe_forms/form_send_contract_agreement';
import IkonotvRegisterPiece from './components/ikonotv/ikonotv_register_piece';
import IkonotvPieceContainer from './components/ikonotv/ikonotv_detail/ikonotv_piece_container';
import IkonotvContractNotifications from './components/ikonotv/ikonotv_contract_notifications';
@@ -135,7 +135,7 @@ let ROUTES = {
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(ContractSettings)}/>
Date: Thu, 3 Dec 2015 17:17:52 +0100
Subject: [PATCH 66/83] Add hot fix from AD-1313 for using refs in properties
https://github.com/ascribe/onion/blob/AD-1313-Attach-thumbnail-to-piece-
in-register-form/js/components/ascribe_forms/form.js
---
js/components/ascribe_forms/form.js | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js
index 0e3517a2..850de032 100644
--- a/js/components/ascribe_forms/form.js
+++ b/js/components/ascribe_forms/form.js
@@ -238,7 +238,15 @@ let Form = React.createClass({
renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => {
if (child) {
- return ReactAddons.addons.cloneWithProps(child, {
+ // Since refs will be overwritten by this functions return statement,
+ // we still want to be able to define refs for nested `Form` or `Property`
+ // children, which is why we're upfront simply invoking the callback-ref-
+ // function before overwriting it.
+ if(typeof child.ref === 'function' && this.refs[child.props.name]) {
+ child.ref(this.refs[child.props.name]);
+ }
+
+ return React.cloneElement(child, {
handleChange: this.handleChangeChild,
ref: child.props.name,
key: i,
From f5a527b251b1518f7958b60828a4bf08f58b5fbe Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Thu, 3 Dec 2015 17:19:17 +0100
Subject: [PATCH 67/83] Fix references to form due to nested property elements
---
js/components/ascribe_forms/form.js | 8 ++-
js/components/ascribe_forms/form_loan.js | 17 +++---
.../property_contract_agreement.js | 55 +++++++++++++------
sass/ascribe_notification_page.scss | 4 +-
4 files changed, 57 insertions(+), 27 deletions(-)
diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js
index 850de032..6be7fa2d 100644
--- a/js/components/ascribe_forms/form.js
+++ b/js/components/ascribe_forms/form.js
@@ -124,8 +124,12 @@ let Form = React.createClass({
getFormData() {
let data = {};
- for (let ref in this.refs) {
- data[this.refs[ref].props.name] = this.refs[ref].state.value;
+ for (let refName in this.refs) {
+ const ref = this.refs[refName];
+
+ if (ref.state && 'value' in ref.state) {
+ data[ref.props.name] = ref.state.value;
+ }
}
if (typeof this.props.getFormData === 'function') {
diff --git a/js/components/ascribe_forms/form_loan.js b/js/components/ascribe_forms/form_loan.js
index a0b1240f..0b4f4775 100644
--- a/js/components/ascribe_forms/form_loan.js
+++ b/js/components/ascribe_forms/form_loan.js
@@ -57,13 +57,6 @@ let LoanForm = React.createClass({
};
},
- getFormData() {
- return mergeOptions(
- this.props.id,
- this.refs.contractAgreement.getFormDataForProperty()
- );
- },
-
handleEmailOnChange(event) {
// event.target.value is the submitted email of the loanee
this.setState({
@@ -75,6 +68,13 @@ let LoanForm = React.createClass({
this.handleEmailOnChange();
},
+ getFormData() {
+ return mergeOptions(
+ this.props.id,
+ this.refs.contractAgreement.getFormDataForProperty()
+ );
+ },
+
getButtons() {
if(this.props.loanHeading) {
return (
@@ -191,10 +191,11 @@ let LoanForm = React.createClass({
required={showPersonalMessage}/>
this.refs.contractAgreement = ref}
createPublicContractAgreement={createPublicContractAgreement}
email={email}
embedClassName={'loan-form'}
+ name='contract_agreement'
label={getLangText('Loan Contract')} />
0) {
- ContractAgreementListActions.flushContractAgreementList();
+ if (this.props.email !== nextEmail) {
+ if (isEmail(nextEmail)) {
+ this.getContractAgreementsOrCreatePublic(nextEmail);
+ } else if (contractAgreementList && contractAgreementList.length > 0) {
+ ContractAgreementListActions.flushContractAgreementList();
+ }
}
},
@@ -53,14 +61,21 @@ let ContractAgreementProperty = React.createClass({
},
getFormDataForProperty() {
- return this.getContractAgreementId();
+ const contractAgreementId = this.getContractAgreementId();
+ const formData = {
+ 'terms': this.refs.terms.state.value
+ };
+
+ if (contractAgreementId) {
+ formData.contract_agreement_id = contractAgreementId;
+ }
+
+ return formData;
},
getContractAgreementId() {
if (this.state.contractAgreementList && this.state.contractAgreementList.length > 0) {
- return { 'contract_agreement_id': this.state.contractAgreementList[0].id };
- } else {
- return {};
+ return this.state.contractAgreementList[0].id;
}
},
@@ -82,7 +97,8 @@ let ContractAgreementProperty = React.createClass({
return (
+ label={getLangText('Appendix')}
+ handleChange={this.props.handleChange}>
{appendix.default}
);
@@ -91,24 +107,27 @@ let ContractAgreementProperty = React.createClass({
},
getContractCheckbox() {
+ const { embedClassName, handleChange, label } = this.props;
const { contractAgreementList } = this.state;
if (contractAgreementList && contractAgreementList.length > 0) {
- // we need to define a key on the InputCheckboxes as otherwise
- // react is not rerendering them on a store switch and is keeping
- // the default value of the component (which is in that case true)
const contractAgreement = contractAgreementList[0];
const { issuer: contractIssuer, blob: { url_safe: contractUrl } } = contractAgreement.contract;
+ // we need to define a key on the InputCheckboxes as otherwise
+ // react is not rerendering them on a store switch and is keeping
+ // the default value of the component (which is in that case true)
if (contractAgreement.datetime_accepted) {
return (
@@ -125,8 +144,10 @@ let ContractAgreementProperty = React.createClass({
} else {
return (
Date: Thu, 3 Dec 2015 17:43:15 +0100
Subject: [PATCH 68/83] Revert "Merge with AD-1443"
Revert https://github.com/ascribe/onion/pull/40, as that needs more
work.
---
js/components/piece_list.js | 5 ++--
js/stores/edition_list_store.js | 43 +++++++++++++++++++++++----------
2 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/js/components/piece_list.js b/js/components/piece_list.js
index 9208df92..4a96cbd8 100644
--- a/js/components/piece_list.js
+++ b/js/components/piece_list.js
@@ -189,14 +189,13 @@ let PieceList = React.createClass({
this.state.pieceList
.forEach((piece) => {
// but only if they're actually open
- const isEditionListOpenForPiece = this.state.isEditionListOpenForPieceId[piece.id];
-
- if (isEditionListOpenForPiece && isEditionListOpenForPiece.show) {
+ if(this.state.isEditionListOpenForPieceId[piece.id].show) {
EditionListActions.refreshEditionList({
pieceId: piece.id,
filterBy
});
}
+
});
});
diff --git a/js/stores/edition_list_store.js b/js/stores/edition_list_store.js
index 6b4d64f9..107f9af4 100644
--- a/js/stores/edition_list_store.js
+++ b/js/stores/edition_list_store.js
@@ -32,7 +32,7 @@ class EditionListStore {
// page
let storeEditionIndex = (page - 1) * pageSize + i;
let editionsForPieces = this.editionList[pieceId];
-
+
// if edition already exists, just merge
if(editionsForPieces[storeEditionIndex]) {
editionsForPieces[storeEditionIndex] = React.addons.update(editionsForPieces[storeEditionIndex], {$merge: editionListOfPiece[i]});
@@ -60,29 +60,46 @@ class EditionListStore {
* We often just have to refresh the edition list for a certain pieceId,
* this method provides exactly that functionality without any side effects
*/
- onRefreshEditionList({pieceId, filterBy = this.editionList[pieceId].filterBy}) {
- const pieceEditionList = this.editionList[pieceId];
-
+ onRefreshEditionList({pieceId, filterBy = {}}) {
// It may happen that the user enters the site logged in already
// through /editions
// If he then tries to delete a piece/edition and this method is called,
// we'll not be able to refresh his edition list since its not yet there.
// Therefore we can just return, since there is no data to be refreshed
- if (!pieceEditionList) {
+ if(!this.editionList[pieceId]) {
return;
}
+ let prevEditionListLength = this.editionList[pieceId].length;
+ let prevEditionListPage = this.editionList[pieceId].page;
+ let prevEditionListPageSize = this.editionList[pieceId].pageSize;
+
+ // we can also refresh the edition list using filterBy,
+ // if we decide not to do that then the old filter will just be applied.
+ if(filterBy && Object.keys(filterBy).length <= 0) {
+ filterBy = this.editionList[pieceId].filterBy;
+ prevEditionListLength = 10;
+ prevEditionListPage = 1;
+ prevEditionListPageSize = 10;
+ }
+
// to clear an array, david walsh recommends to just set it's length to zero
// http://davidwalsh.name/empty-array
- pieceEditionList.length = 0;
+ this.editionList[pieceId].length = 0;
- // refetch editions from the beginning with the previous settings
- EditionsListActions
- .fetchEditionList(pieceId, 1, pieceEditionList.pageSize,
- pieceEditionList.orderBy,
- pieceEditionList.orderAsc,
- filterBy)
- .catch(console.logGlobal);
+ // refetch editions with adjusted page size
+ EditionsListActions.fetchEditionList(pieceId, 1, prevEditionListLength,
+ this.editionList[pieceId].orderBy,
+ this.editionList[pieceId].orderAsc,
+ filterBy)
+ .then(() => {
+ // reset back to the normal pageSize and page
+ this.editionList[pieceId].page = prevEditionListPage;
+ this.editionList[pieceId].pageSize = prevEditionListPageSize;
+ })
+ .catch((err) => {
+ console.logGlobal(err);
+ });
}
onSelectEdition({pieceId, editionId, toValue}) {
From cb19c46dac9cfc499547512ec629f6729bc0f4ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?=
Date: Thu, 3 Dec 2015 20:09:27 +0100
Subject: [PATCH 69/83] Implement PropertyCollapsible's functionality into
Property
---
js/components/ascribe_forms/property.js | 206 +++++++++++++++---------
1 file changed, 129 insertions(+), 77 deletions(-)
diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js
index dce6b49b..d0b6ef37 100644
--- a/js/components/ascribe_forms/property.js
+++ b/js/components/ascribe_forms/property.js
@@ -2,61 +2,69 @@
import React from 'react';
import ReactAddons from 'react/addons';
-import classNames from 'classnames';
-import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
-import Tooltip from 'react-bootstrap/lib/Tooltip';
+import Panel from 'react-bootstrap/lib/Panel';
import AppConstants from '../../constants/application_constants';
-let Property = React.createClass({
- propTypes: {
- hidden: React.PropTypes.bool,
+import { mergeOptions } from '../../utils/general_utils';
- autoFocus: React.PropTypes.bool,
- editable: React.PropTypes.bool,
+
+const { bool, element, string, oneOfType, func, object, arrayOf } = React.PropTypes;
+
+const Property = React.createClass({
+ propTypes: {
+ editable: bool,
// If we want Form to have a different value for disabled as Property has one for
// editable, we need to set overrideForm to true, as it will then override Form's
// disabled value for individual Properties
- overrideForm: React.PropTypes.bool,
+ overrideForm: bool,
- tooltip: React.PropTypes.element,
- label: React.PropTypes.string,
- value: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
+ label: string,
+ value: oneOfType([
+ string,
+ element
]),
- footer: React.PropTypes.element,
- handleChange: React.PropTypes.func,
- ignoreFocus: React.PropTypes.bool,
- name: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.number
- ]).isRequired,
- className: React.PropTypes.string,
+ footer: element,
+ handleChange: func,
+ ignoreFocus: bool,
+ name: string.isRequired,
+ className: string,
- onClick: React.PropTypes.func,
- onChange: React.PropTypes.func,
- onBlur: React.PropTypes.func,
+ onClick: func,
+ onChange: func,
+ onBlur: func,
- children: React.PropTypes.oneOfType([
- React.PropTypes.arrayOf(React.PropTypes.element),
- React.PropTypes.element
+ children: oneOfType([
+ arrayOf(element),
+ element
]),
- style: React.PropTypes.object
+ style: object,
+ expanded: bool,
+ checkboxLabel: string
},
getDefaultProps() {
return {
editable: true,
- hidden: false,
+ expanded: true,
className: ''
};
},
getInitialState() {
+ const { expanded, ignoreFocus, checkboxLabel } = this.props;
+
return {
+ // We're mirroring expanded here as a state
+ // React's docs do NOT consider this an antipattern as long as it's
+ // not a "source of truth"-duplication
+ expanded,
+
+ // When a checkboxLabel is defined in the props, we want to set
+ // `ignoreFocus` to true
+ ignoreFocus: ignoreFocus || checkboxLabel,
// Please don't confuse initialValue with react's defaultValue.
// initialValue is set by us to ensure that a user can reset a specific
// property (after editing) to its initial value
@@ -67,15 +75,19 @@ let Property = React.createClass({
};
},
- componentDidMount() {
- if (this.props.autoFocus) {
- this.handleFocus();
- }
- },
-
- componentWillReceiveProps() {
+ componentWillReceiveProps(nextProps) {
let childInput = this.refs.input;
+ // For expanded there are actually two use cases:
+ //
+ // 1. Control its value from the outside completely (do not define `checkboxLabel`)
+ // 2. Let it be controlled from the inside (default value can be set though via `expanded`)
+ //
+ // This handles case 1.
+ if(nextProps.expanded !== this.state.expanded && !this.props.checkboxLabel) {
+ this.setState({ expanded: nextProps.expanded });
+ }
+
// In order to set this.state.value from another component
// the state of value should only be set if its not undefined and
// actually references something
@@ -88,13 +100,13 @@ let Property = React.createClass({
// from native HTML elements.
// To enable developers to create input elements, they can expose a property called value
// in their state that will be picked up by property.js
- } else if(childInput.state && typeof childInput.state.value !== 'undefined') {
+ } else if(childInput && childInput.state && typeof childInput.state.value !== 'undefined') {
this.setState({
value: childInput.state.value
});
}
- if(!this.state.initialValue && childInput.props.defaultValue) {
+ if(!this.state.initialValue && childInput && childInput.props.defaultValue) {
this.setState({
initialValue: childInput.props.defaultValue
});
@@ -146,7 +158,7 @@ let Property = React.createClass({
handleFocus() {
// if ignoreFocus (bool) is defined, then just ignore focusing on
// the property and input
- if(this.props.ignoreFocus) {
+ if(this.state.ignoreFocus) {
return;
}
@@ -198,7 +210,7 @@ let Property = React.createClass({
},
getClassName() {
- if(this.props.hidden){
+ if(!this.state.expanded && !this.props.checkboxLabel){
return 'is-hidden';
}
if(!this.props.editable){
@@ -215,63 +227,103 @@ let Property = React.createClass({
},
renderChildren(style) {
- return ReactAddons.Children.map(this.props.children, (child) => {
- return ReactAddons.addons.cloneWithProps(child, {
- style,
- onChange: this.handleChange,
- onFocus: this.handleFocus,
- onBlur: this.handleBlur,
- disabled: !this.props.editable,
- ref: 'input',
- name: this.props.name
+ // Input's props should only be cloned and propagated down the tree,
+ // if the component is actually being shown (!== 'expanded === false')
+ if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) {
+ return ReactAddons.Children.map(this.props.children, (child) => {
+
+ // Since refs will be overriden by this functions return statement,
+ // we still want to be able to define refs for nested `Form` or `Property`
+ // children, which is why we're upfront simply invoking the callback-ref-
+ // function before overriding it.
+ if(typeof child.ref === 'function' && this.refs.input) {
+ child.ref(this.refs.input);
+ }
+
+ return React.cloneElement(child, {
+ style,
+ onChange: this.handleChange,
+ onFocus: this.handleFocus,
+ onBlur: this.handleBlur,
+ disabled: !this.props.editable,
+ ref: 'input',
+ name: this.props.name
+ });
});
- });
+ }
},
- render() {
- const { className, editable, footer, label, tooltip } = this.props;
- const style = Object.assign({}, this.props.style, { cursor: !editable ? 'not-allowed' : null });
-
- let tooltipEl = tooltip ? {tooltip}
- : ;
-
- let labelEl;
- if (label || this.state.errors) {
- labelEl = (
+ getLabelAndErrors() {
+ if(this.props.label || this.state.errors) {
+ return (
);
}
diff --git a/js/components/whitelabel/wallet/components/market/market_register_piece.js b/js/components/whitelabel/wallet/components/market/market_register_piece.js
index e02df80c..eed17477 100644
--- a/js/components/whitelabel/wallet/components/market/market_register_piece.js
+++ b/js/components/whitelabel/wallet/components/market/market_register_piece.js
@@ -82,7 +82,7 @@ let MarketRegisterPiece = React.createClass({
handleAdditionalDataSuccess() {
this.refreshPieceList();
- this.history.pushState(null, `/collection`);
+ this.history.pushState(null, '/collection');
},
// We need to increase the step to lock the forms that are already filled out
From cb972eb448e3d8eb2643a3a3b12f219644c7479f Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Fri, 4 Dec 2015 18:30:45 +0100
Subject: [PATCH 82/83] Add consign error to market white labels
Can you beliebe? Magical 100th commit.
---
.../market_buttons/market_submit_button.js | 62 +++++++++++++------
.../components/market/market_consign_error.js | 51 +++++++++++++++
sass/ascribe_form.scss | 9 ++-
3 files changed, 103 insertions(+), 19 deletions(-)
create mode 100644 js/components/whitelabel/wallet/components/market/market_consign_error.js
diff --git a/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js b/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js
index d8ef4c41..bd333850 100644
--- a/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js
+++ b/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js
@@ -3,6 +3,8 @@
import React from 'react';
import classNames from 'classnames';
+import MarketConsignError from '../market_consign_error';
+
import MarketAdditionalDataForm from '../market_forms/market_additional_data_form';
import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory';
@@ -12,6 +14,7 @@ import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import AclProxy from '../../../../../acl_proxy';
+import EditionListActions from '../../../../../../actions/edition_list_actions';
import PieceActions from '../../../../../../actions/piece_actions';
import WhitelabelActions from '../../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../../stores/whitelabel_store';
@@ -89,6 +92,11 @@ let MarketSubmitButton = React.createClass({
this.refs.consignModal.show();
},
+ handleConsignError() {
+ // Unselect failed editions
+ EditionListActions.clearAllEditionSelections();
+ },
+
render() {
const { availableAcls, currentUser, className, editions, handleSuccess } = this.props;
const { whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail } } = this.state;
@@ -120,27 +128,45 @@ let MarketSubmitButton = React.createClass({
);
if (solePieceId && !canSubmit) {
- return (
-
+ if (availableAcls.acl_edit) {
+ return (
+
+
+
+
+
+
+ {consignForm}
+
+
+ );
+ } else {
+ // Oops, well this is a difficult situation...
+ // The user's likely already transferred another edition from the piece so
+ // they can't update the missing fields that are necessary for consignment
+ // to marketplaces.
+ // Let's show an error in response to explain the problem and let them
+ // contact the whitelabel themselves.
+ return (
-
+ handleSuccess={this.handleConsignError}
+ title={getLangText("Oops, we can't consign this piece to %s", whitelabelName)}>
+
-
-
- {consignForm}
-
-
- );
+ );
+ }
} else {
return (
+
+
+ {getLangText('Unfortunately, %s requires you to provide more information ' +
+ "about this edition, but it appears that you don't have the " +
+ 'permissions to edit the piece associated with this edition.', whitelabelName)}
+
+
+ {getLangText('Please contact us if you would still like to consign this piece to %s.', whitelabelName)}
+
+
+
+
+ );
+ }
+});
+
+export default MarketConsignError;
diff --git a/sass/ascribe_form.scss b/sass/ascribe_form.scss
index 1b265e91..84be8be9 100644
--- a/sass/ascribe_form.scss
+++ b/sass/ascribe_form.scss
@@ -23,4 +23,11 @@
@media (max-width: 550px) {
width: 100%;
}
-}
\ No newline at end of file
+}
+
+.ascribe-form-error-popup {
+ .error-popup-content {
+ margin-bottom: 30px;
+ margin-top: 20px;
+ }
+}
From 41ef51c4782615e53d5b9b0509af378743107503 Mon Sep 17 00:00:00 2001
From: Brett Sun
Date: Mon, 7 Dec 2015 10:17:42 +0100
Subject: [PATCH 83/83] Revert "Add consign error to market white labels"
This reverts commit cb972eb448e3d8eb2643a3a3b12f219644c7479f.
Unfortunately, the edition's `acl_edit` can still be true when
the piece's `acl_edit` is false, so there is no good way on
the frontend right now to tell when to show the error message.
For now we'll assume submissions to marketplaces are all new
and ignore this edge case.
---
.../market_buttons/market_submit_button.js | 62 ++++++-------------
.../components/market/market_consign_error.js | 51 ---------------
sass/ascribe_form.scss | 9 +--
3 files changed, 19 insertions(+), 103 deletions(-)
delete mode 100644 js/components/whitelabel/wallet/components/market/market_consign_error.js
diff --git a/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js b/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js
index bd333850..d8ef4c41 100644
--- a/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js
+++ b/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js
@@ -3,8 +3,6 @@
import React from 'react';
import classNames from 'classnames';
-import MarketConsignError from '../market_consign_error';
-
import MarketAdditionalDataForm from '../market_forms/market_additional_data_form';
import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory';
@@ -14,7 +12,6 @@ import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import AclProxy from '../../../../../acl_proxy';
-import EditionListActions from '../../../../../../actions/edition_list_actions';
import PieceActions from '../../../../../../actions/piece_actions';
import WhitelabelActions from '../../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../../stores/whitelabel_store';
@@ -92,11 +89,6 @@ let MarketSubmitButton = React.createClass({
this.refs.consignModal.show();
},
- handleConsignError() {
- // Unselect failed editions
- EditionListActions.clearAllEditionSelections();
- },
-
render() {
const { availableAcls, currentUser, className, editions, handleSuccess } = this.props;
const { whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail } } = this.state;
@@ -128,45 +120,27 @@ let MarketSubmitButton = React.createClass({
);
if (solePieceId && !canSubmit) {
- if (availableAcls.acl_edit) {
- return (
-
-
-
-
-
-
- {consignForm}
-
-
- );
- } else {
- // Oops, well this is a difficult situation...
- // The user's likely already transferred another edition from the piece so
- // they can't update the missing fields that are necessary for consignment
- // to marketplaces.
- // Let's show an error in response to explain the problem and let them
- // contact the whitelabel themselves.
- return (
+ return (
+
-
+ handleSuccess={this.handleAdditionalDataSuccess.bind(this, solePieceId)}
+ title={getLangText('Add additional information')}>
+
- );
- }
+
+
+ {consignForm}
+
+
+ );
} else {
return (
-
-
- {getLangText('Unfortunately, %s requires you to provide more information ' +
- "about this edition, but it appears that you don't have the " +
- 'permissions to edit the piece associated with this edition.', whitelabelName)}
-
-
- {getLangText('Please contact us if you would still like to consign this piece to %s.', whitelabelName)}
-