From c242cffdbda47a9e5ae4706c5b0b4604a13d668b Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Thu, 29 Oct 2015 17:04:31 +0100 Subject: [PATCH 01/58] Bring over changes for acl_button from Lumenus --- js/components/ascribe_buttons/acl_button.js | 81 +++++++++------------ js/utils/form_utils.js | 62 +++++++++++++--- 2 files changed, 85 insertions(+), 58 deletions(-) diff --git a/js/components/ascribe_buttons/acl_button.js b/js/components/ascribe_buttons/acl_button.js index 67f4a430..5197a744 100644 --- a/js/components/ascribe_buttons/acl_button.js +++ b/js/components/ascribe_buttons/acl_button.js @@ -16,7 +16,7 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import ApiUrls from '../../constants/api_urls'; -import { getAclFormMessage } from '../../utils/form_utils'; +import { getAclFormMessage, getAclFormDataId } from '../../utils/form_utils'; import { getLangText } from '../../utils/lang_utils'; let AclButton = React.createClass({ @@ -30,32 +30,37 @@ let AclButton = React.createClass({ currentUser: React.PropTypes.object, buttonAcceptName: React.PropTypes.string, buttonAcceptClassName: React.PropTypes.string, + email: React.PropTypes.string, handleSuccess: React.PropTypes.func.isRequired, 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'), form: ( - ), + ), 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 +69,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 +84,33 @@ 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 +118,9 @@ let AclButton = React.createClass({ - ), + url={this.isPiece() ? ApiUrls.ownership_shares_pieces + : ApiUrls.ownership_shares_editions}/> + ), handleSuccess: this.showNotification }; } else { @@ -121,36 +128,16 @@ 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 { - return {bitcoin_id: this.props.pieceOrEditions.map(function(edition){ - return edition.bitcoin_id; - }).join()}; - } + return getAclFormDataId(this.isPiece(), this.props.pieceOrEditions); }, // Removes the acl_ prefix and converts to upper case @@ -162,7 +149,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 : ''; @@ -184,4 +171,4 @@ let AclButton = React.createClass({ } }); -export default AclButton; \ No newline at end of file +export default AclButton; diff --git a/js/utils/form_utils.js b/js/utils/form_utils.js index 7f9cfb07..d2d2cd29 100644 --- a/js/utils/form_utils.js +++ b/js/utils/form_utils.js @@ -2,14 +2,42 @@ import { getLangText } from './lang_utils'; +import AppConstants from '../constants/application_constants'; + +/** + * Get the data ids of the given piece or editions. + * @param {boolean} isPiece Is the given entities parameter a piece? (False: array of editions) + * @param {(object|object[])} pieceOrEditions Piece or array of editions + * @return {(object|object[])} Data IDs of the pieceOrEditions for the form + */ +export function getAclFormDataId(isPiece, pieceOrEditions) { + if (isPiece) { + return {piece_id: pieceOrEditions.id}; + } else { + return {bitcoin_id: pieceOrEditions.map(function(edition){ + return edition.bitcoin_id; + }).join()}; + } +} + /** * Generates a message for submitting a form - * @param {string} aclName Enum name of a acl - * @param {string} entities Already computed name of entities - * @param {string} senderName Name of the sender - * @return {string} Completed message + * @param {object} options Options object for creating the message: + * @param {string} options.aclName Enum name of an acl + * @param {(object|object[])} options.entities Piece or array of Editions + * @param {boolean} options.isPiece Is the given entities parameter a piece? (False: array of editions) + * @param {string} [options.senderName] Name of the sender + * @return {string} Completed message */ -export function getAclFormMessage(aclName, entities, senderName) { +export function getAclFormMessage(options) { + if (!options || options.aclName === undefined || options.isPiece === undefined || + !(typeof options.entities === 'object' || options.entities.constructor === Array)) { + throw new Error('You must specify an acl class, entities in the correct format, and entity type'); + } + + let aclName = options.aclName; + let entityTitles = options.isPiece ? getTitlesStringOfPiece(options.entities) + : getTitlesStringOfEditions(options.entities); let message = ''; message += getLangText('Hi'); @@ -32,7 +60,7 @@ export function getAclFormMessage(aclName, entities, senderName) { } message += ':\n'; - message += entities; + message += entityTitles; if(aclName === 'acl_transfer' || aclName === 'acl_loan' || aclName === 'acl_consign') { message += getLangText('to you'); @@ -44,10 +72,22 @@ export function getAclFormMessage(aclName, entities, senderName) { throw new Error('Your specified aclName did not match a an acl class.'); } - message += '\n\n'; - message += getLangText('Truly yours,'); - message += '\n'; - message += senderName; + if (options.senderName) { + message += '\n\n'; + message += getLangText('Truly yours,'); + message += '\n'; + message += options.senderName; + } return message; -} \ No newline at end of file +} + +function getTitlesStringOfPiece(piece){ + return '\"' + piece.title + '\"'; +} + +function getTitlesStringOfEditions(editions) { + return editions.map(function(edition) { + return '- \"' + edition.title + ', ' + getLangText('edition') + ' ' + edition.edition_number + '\"\n'; + }).join(''); +} From 03e0bbd024115b545480f3452d6337ba7a5e3f06 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 30 Oct 2015 11:10:31 +0100 Subject: [PATCH 02/58] Separate form building concerns from AclButton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AclButton’s form building is now delegated to AclFormFactory so other components can use the same forms with ease. Its show/hide behaviour is also now controlled with AclProxy. --- js/components/ascribe_buttons/acl_button.js | 174 ------------------ .../ascribe_buttons/acl_button_list.js | 31 ++-- .../ascribe_buttons/acls/acl_button.js | 89 +++++++++ .../ascribe_buttons/acls/consign_button.js | 21 +++ .../ascribe_buttons/acls/loan_button.js | 21 +++ .../acls/loan_request_button.js | 21 +++ .../ascribe_buttons/acls/share_button.js | 21 +++ .../ascribe_buttons/acls/transfer_button.js | 21 +++ .../ascribe_buttons/acls/unconsign_button.js | 21 +++ .../ascribe_forms/acl_form_factory.js | 134 ++++++++++++++ .../ascribe_forms/form_request_action.js | 15 +- 11 files changed, 372 insertions(+), 197 deletions(-) delete mode 100644 js/components/ascribe_buttons/acl_button.js create mode 100644 js/components/ascribe_buttons/acls/acl_button.js create mode 100644 js/components/ascribe_buttons/acls/consign_button.js create mode 100644 js/components/ascribe_buttons/acls/loan_button.js create mode 100644 js/components/ascribe_buttons/acls/loan_request_button.js create mode 100644 js/components/ascribe_buttons/acls/share_button.js create mode 100644 js/components/ascribe_buttons/acls/transfer_button.js create mode 100644 js/components/ascribe_buttons/acls/unconsign_button.js create mode 100644 js/components/ascribe_forms/acl_form_factory.js diff --git a/js/components/ascribe_buttons/acl_button.js b/js/components/ascribe_buttons/acl_button.js deleted file mode 100644 index 5197a744..00000000 --- a/js/components/ascribe_buttons/acl_button.js +++ /dev/null @@ -1,174 +0,0 @@ -'use strict'; - -import React from 'react'; - -import ConsignForm from '../ascribe_forms/form_consign'; -import UnConsignForm from '../ascribe_forms/form_unconsign'; -import TransferForm from '../ascribe_forms/form_transfer'; -import LoanForm from '../ascribe_forms/form_loan'; -import LoanRequestAnswerForm from '../ascribe_forms/form_loan_request_answer'; -import ShareForm from '../ascribe_forms/form_share_email'; -import ModalWrapper from '../ascribe_modal/modal_wrapper'; -import AppConstants from '../../constants/application_constants'; - -import GlobalNotificationModel from '../../models/global_notification_model'; -import GlobalNotificationActions from '../../actions/global_notification_actions'; - -import ApiUrls from '../../constants/api_urls'; - -import { getAclFormMessage, getAclFormDataId } from '../../utils/form_utils'; -import { getLangText } from '../../utils/lang_utils'; - -let AclButton = React.createClass({ - propTypes: { - action: React.PropTypes.oneOf(AppConstants.aclList).isRequired, - availableAcls: React.PropTypes.object.isRequired, - pieceOrEditions: React.PropTypes.oneOfType([ - React.PropTypes.object, - React.PropTypes.array - ]).isRequired, - currentUser: React.PropTypes.object, - buttonAcceptName: React.PropTypes.string, - buttonAcceptClassName: React.PropTypes.string, - email: React.PropTypes.string, - handleSuccess: React.PropTypes.func.isRequired, - className: React.PropTypes.string - }, - - isPiece() { - return this.props.pieceOrEditions.constructor !== Array; - }, - - actionProperties() { - let message = getAclFormMessage({ - aclName: this.props.action, - entities: this.props.pieceOrEditions, - isPiece: this.isPiece(), - senderName: this.props.currentUser.username - }); - - if (this.props.action === 'acl_consign') { - return { - title: getLangText('Consign artwork'), - tooltip: getLangText('Have someone else sell the artwork'), - form: ( - - ), - handleSuccess: this.showNotification - }; - } else if (this.props.action === 'acl_unconsign') { - return { - title: getLangText('Unconsign artwork'), - tooltip: getLangText('Have the owner manage his sales again'), - form: ( - - ), - handleSuccess: this.showNotification - }; - } else if (this.props.action === 'acl_transfer') { - return { - title: getLangText('Transfer artwork'), - tooltip: getLangText('Transfer the ownership of the artwork'), - form: ( - - ), - handleSuccess: this.showNotification - }; - } else if (this.props.action === 'acl_loan') { - return { - title: getLangText('Loan artwork'), - tooltip: getLangText('Loan your artwork for a limited period of time'), - form: ( - - ), - handleSuccess: this.showNotification - }; - } 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') { - return { - title: getLangText('Share artwork'), - tooltip: getLangText('Share the artwork'), - form: ( - - ), - handleSuccess: this.showNotification - }; - } else { - throw new Error('Your specified action did not match a form.'); - } - }, - - showNotification(response) { - this.props.handleSuccess(); - if (response.notification) { - let notification = new GlobalNotificationModel(response.notification, 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - } - }, - - getFormDataId(){ - return getAclFormDataId(this.isPiece(), this.props.pieceOrEditions); - }, - - // Removes the acl_ prefix and converts to upper case - sanitizeAction() { - if (this.props.buttonAcceptName) { - return this.props.buttonAcceptName; - } - return this.props.action.split('acl_')[1].toUpperCase(); - }, - - render() { - if (this.props.availableAcls) { - let shouldDisplay = this.props.availableAcls[this.props.action]; - let aclProps = this.actionProperties(); - let buttonClassName = this.props.buttonAcceptClassName ? this.props.buttonAcceptClassName : ''; - return ( - - {this.sanitizeAction()} - - } - handleSuccess={aclProps.handleSuccess} - title={aclProps.title}> - {aclProps.form} - - ); - } - return null; - } -}); - -export default AclButton; diff --git a/js/components/ascribe_buttons/acl_button_list.js b/js/components/ascribe_buttons/acl_button_list.js index e87a6407..6aec6c77 100644 --- a/js/components/ascribe_buttons/acl_button_list.js +++ b/js/components/ascribe_buttons/acl_button_list.js @@ -5,21 +5,25 @@ import React from 'react/addons'; import UserActions from '../../actions/user_actions'; import UserStore from '../../stores/user_store'; -import AclButton from '../ascribe_buttons/acl_button'; +import ConsignButton from './acls/consign_button'; +import LoanButton from './acls/loan_button'; +import LoanRequestButton from './acls/loan_request_button'; +import ShareButton from './acls/share_button'; +import TransferButton from './acls/transfer_button'; +import UnconsignButton from './acls/unconsign_button'; import { mergeOptions } from '../../utils/general_utils'; - let AclButtonList = React.createClass({ propTypes: { className: React.PropTypes.string, editions: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.array - ]), - availableAcls: React.PropTypes.object, + ]).isRequired, + availableAcls: React.PropTypes.object.isRequired, buttonsStyle: React.PropTypes.object, - handleSuccess: React.PropTypes.func, + handleSuccess: React.PropTypes.func.isRequired, children: React.PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.element @@ -86,33 +90,28 @@ let AclButtonList = React.createClass({ return (
- - - - - @@ -123,4 +122,4 @@ let AclButtonList = React.createClass({ } }); -export default AclButtonList; \ No newline at end of file +export default AclButtonList; diff --git a/js/components/ascribe_buttons/acls/acl_button.js b/js/components/ascribe_buttons/acls/acl_button.js new file mode 100644 index 00000000..f7149e79 --- /dev/null +++ b/js/components/ascribe_buttons/acls/acl_button.js @@ -0,0 +1,89 @@ +'use strict'; + +import React from 'react'; +import classNames from 'classnames'; + +import AclProxy from '../../acl_proxy'; + +import AclFormFactory from '../../ascribe_forms/acl_form_factory'; + +import ModalWrapper from '../../ascribe_modal/modal_wrapper'; + +import AppConstants from '../../../constants/application_constants'; + +import GlobalNotificationModel from '../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../actions/global_notification_actions'; + +import ApiUrls from '../../../constants/api_urls'; + +import { getAclFormMessage, getAclFormDataId } from '../../../utils/form_utils'; +import { getLangText } from '../../../utils/lang_utils'; + +let AclButton = React.createClass({ + propTypes: { + action: React.PropTypes.oneOf(AppConstants.aclList).isRequired, + availableAcls: React.PropTypes.object.isRequired, + buttonAcceptName: React.PropTypes.string, + buttonAcceptClassName: React.PropTypes.string, + currentUser: React.PropTypes.object.isRequired, + email: React.PropTypes.string, + pieceOrEditions: React.PropTypes.oneOfType([ + React.PropTypes.object, + React.PropTypes.array + ]).isRequired, + title: React.PropTypes.string, + handleSuccess: React.PropTypes.func.isRequired, + className: React.PropTypes.string + }, + + getDefaultProps() { + return { + buttonAcceptClassName: '' + }; + }, + + // Removes the acl_ prefix and converts to upper case + sanitizeAction() { + if (this.props.buttonAcceptName) { + return this.props.buttonAcceptName; + } + return this.props.action.split('acl_')[1].toUpperCase(); + }, + + render() { + const { + action, + availableAcls, + buttonAcceptClassName, + currentUser, + email, + pieceOrEditions, + handleSuccess, + title } = this.props; + + return ( + + + {this.sanitizeAction()} + + } + handleSuccess={handleSuccess} + title={title}> + + + + ); + } +}); + +export default AclButton; diff --git a/js/components/ascribe_buttons/acls/consign_button.js b/js/components/ascribe_buttons/acls/consign_button.js new file mode 100644 index 00000000..3eb88fc4 --- /dev/null +++ b/js/components/ascribe_buttons/acls/consign_button.js @@ -0,0 +1,21 @@ +'use strict'; + +import React from 'react'; + +import AclButton from './acl_button'; + +import { getLangText } from '../../../utils/lang_utils'; + +let ConsignButton = React.createClass({ + render() { + return ( + + ); + } +}); + +export default ConsignButton; diff --git a/js/components/ascribe_buttons/acls/loan_button.js b/js/components/ascribe_buttons/acls/loan_button.js new file mode 100644 index 00000000..aa0036c4 --- /dev/null +++ b/js/components/ascribe_buttons/acls/loan_button.js @@ -0,0 +1,21 @@ +'use strict'; + +import React from 'react'; + +import AclButton from './acl_button'; + +import { getLangText } from '../../../utils/lang_utils'; + +let LoanButton = React.createClass({ + render() { + return ( + + ); + } +}); + +export default LoanButton; diff --git a/js/components/ascribe_buttons/acls/loan_request_button.js b/js/components/ascribe_buttons/acls/loan_request_button.js new file mode 100644 index 00000000..85483ab1 --- /dev/null +++ b/js/components/ascribe_buttons/acls/loan_request_button.js @@ -0,0 +1,21 @@ +'use strict'; + +import React from 'react'; + +import AclButton from './acl_button'; + +import { getLangText } from '../../../utils/lang_utils'; + +let LoanButton = React.createClass({ + render() { + return ( + + ); + } +}); + +export default LoanButton; diff --git a/js/components/ascribe_buttons/acls/share_button.js b/js/components/ascribe_buttons/acls/share_button.js new file mode 100644 index 00000000..30792ef1 --- /dev/null +++ b/js/components/ascribe_buttons/acls/share_button.js @@ -0,0 +1,21 @@ +'use strict'; + +import React from 'react'; + +import AclButton from './acl_button'; + +import { getLangText } from '../../../utils/lang_utils'; + +let ShareButton = React.createClass({ + render() { + return ( + + ); + } +}); + +export default ShareButton; diff --git a/js/components/ascribe_buttons/acls/transfer_button.js b/js/components/ascribe_buttons/acls/transfer_button.js new file mode 100644 index 00000000..da8728f6 --- /dev/null +++ b/js/components/ascribe_buttons/acls/transfer_button.js @@ -0,0 +1,21 @@ +'use strict'; + +import React from 'react'; + +import AclButton from './acl_button'; + +import { getLangText } from '../../../utils/lang_utils'; + +let TransferButton = React.createClass({ + render() { + return ( + + ); + } +}); + +export default TransferButton; diff --git a/js/components/ascribe_buttons/acls/unconsign_button.js b/js/components/ascribe_buttons/acls/unconsign_button.js new file mode 100644 index 00000000..daaf488b --- /dev/null +++ b/js/components/ascribe_buttons/acls/unconsign_button.js @@ -0,0 +1,21 @@ +'use strict'; + +import React from 'react'; + +import AclButton from './acl_button'; + +import { getLangText } from '../../../utils/lang_utils'; + +let UnconsignButton = React.createClass({ + render() { + return ( + + ); + } +}); + +export default UnconsignButton; diff --git a/js/components/ascribe_forms/acl_form_factory.js b/js/components/ascribe_forms/acl_form_factory.js new file mode 100644 index 00000000..dc5ebd4e --- /dev/null +++ b/js/components/ascribe_forms/acl_form_factory.js @@ -0,0 +1,134 @@ +'use strict'; + +import React from 'react'; + +import ConsignForm from '../ascribe_forms/form_consign'; +import UnConsignForm from '../ascribe_forms/form_unconsign'; +import TransferForm from '../ascribe_forms/form_transfer'; +import LoanForm from '../ascribe_forms/form_loan'; +import LoanRequestAnswerForm from '../ascribe_forms/form_loan_request_answer'; +import ShareForm from '../ascribe_forms/form_share_email'; + +import AppConstants from '../../constants/application_constants'; +import ApiUrls from '../../constants/api_urls'; + +import GlobalNotificationModel from '../../models/global_notification_model'; +import GlobalNotificationActions from '../../actions/global_notification_actions'; + +import { getAclFormMessage, getAclFormDataId } from '../../utils/form_utils'; + +let AclFormFactory = React.createClass({ + propTypes: { + action: React.PropTypes.oneOf(AppConstants.aclList).isRequired, + currentUser: React.PropTypes.object.isRequired, + email: React.PropTypes.string, + message: React.PropTypes.string, + pieceOrEditions: React.PropTypes.oneOfType([ + React.PropTypes.object, + React.PropTypes.array + ]).isRequired, + handleSuccess: React.PropTypes.func, + showNotification: React.PropTypes.bool + }, + + getDefaultProps() { + return { + showNotification: false + }; + }, + + isPiece() { + return this.props.pieceOrEditions.constructor !== Array; + }, + + getFormDataId() { + return getAclFormDataId(this.isPiece(), this.props.pieceOrEditions); + }, + + showSuccessNotification(response) { + if (typeof this.props.handleSuccess === 'function') { + this.props.handleSuccess(); + } + + if (response.notification) { + const notification = new GlobalNotificationModel(response.notification, 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + } + }, + + render() { + const { + action, + pieceOrEditions, + currentUser, + email, + message, + handleSuccess, + showNotification } = this.props; + + const formMessage = message || getAclFormMessage({ + aclName: action, + entities: pieceOrEditions, + isPiece: this.isPiece(), + senderName: currentUser.username + }); + + if (action === 'acl_consign') { + return ( + + ); + } else if (action === 'acl_unconsign') { + return ( + + ); + } else if (action === 'acl_transfer') { + return ( + + ); + } else if (action === 'acl_loan') { + return ( + + ); + } else if (action === 'acl_loan_request') { + return ( + + ); + } else if (action === 'acl_share') { + return ( + + ); + } else { + throw new Error('Your specified action did not match a form.'); + } + } +}); + +export default AclFormFactory; diff --git a/js/components/ascribe_forms/form_request_action.js b/js/components/ascribe_forms/form_request_action.js index b0f3b6c6..b3b6e3a4 100644 --- a/js/components/ascribe_forms/form_request_action.js +++ b/js/components/ascribe_forms/form_request_action.js @@ -2,10 +2,13 @@ import React from 'react'; -import AclButton from './../ascribe_buttons/acl_button'; -import ActionPanel from '../ascribe_panel/action_panel'; import Form from './form'; +import LoanRequestButton from '../ascribe_buttons/acls/loan_request_button'; +import UnconsignButton from '../ascribe_buttons/acls/unconsign_button'; + +import ActionPanel from '../ascribe_panel/action_panel'; + import NotificationActions from '../../actions/notification_actions'; import GlobalNotificationModel from '../../models/global_notification_model'; @@ -100,9 +103,8 @@ let RequestActionForm = React.createClass({ getAcceptButtonForm(urls) { if(this.props.notifications.action === 'unconsign') { return ( - Date: Fri, 30 Oct 2015 11:16:44 +0100 Subject: [PATCH 03/58] Remove unnecessary import from form utils --- js/utils/form_utils.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/utils/form_utils.js b/js/utils/form_utils.js index d2d2cd29..c15eb067 100644 --- a/js/utils/form_utils.js +++ b/js/utils/form_utils.js @@ -2,8 +2,6 @@ import { getLangText } from './lang_utils'; -import AppConstants from '../constants/application_constants'; - /** * Get the data ids of the given piece or editions. * @param {boolean} isPiece Is the given entities parameter a piece? (False: array of editions) From 67fbfbd470392ed82cbf76785a089f8f4880fe12 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 30 Oct 2015 11:46:01 +0100 Subject: [PATCH 04/58] Update RequestActionForm to use form utils --- .../ascribe_forms/form_request_action.js | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/js/components/ascribe_forms/form_request_action.js b/js/components/ascribe_forms/form_request_action.js index b3b6e3a4..ddf832b4 100644 --- a/js/components/ascribe_forms/form_request_action.js +++ b/js/components/ascribe_forms/form_request_action.js @@ -16,9 +16,9 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import ApiUrls from '../../constants/api_urls'; +import { getAclFormDataId } from '../../utils/form_utils'; import { getLangText } from '../../utils/lang_utils.js'; - let RequestActionForm = React.createClass({ propTypes: { pieceOrEditions: React.PropTypes.oneOfType([ @@ -30,26 +30,26 @@ let RequestActionForm = React.createClass({ handleSuccess: React.PropTypes.func }, - isPiece(){ + isPiece() { return this.props.pieceOrEditions.constructor !== Array; }, getUrls() { let urls = {}; - if (this.props.notifications.action === 'consign'){ + if (this.props.notifications.action === 'consign') { urls.accept = ApiUrls.ownership_consigns_confirm; urls.deny = ApiUrls.ownership_consigns_deny; - } else if (this.props.notifications.action === 'unconsign'){ + } else if (this.props.notifications.action === 'unconsign') { urls.accept = ApiUrls.ownership_unconsigns; urls.deny = ApiUrls.ownership_unconsigns_deny; - } else if (this.props.notifications.action === 'loan' && !this.isPiece()){ + } else if (this.props.notifications.action === 'loan' && !this.isPiece()) { urls.accept = ApiUrls.ownership_loans_confirm; urls.deny = ApiUrls.ownership_loans_deny; - } else if (this.props.notifications.action === 'loan' && this.isPiece()){ + } else if (this.props.notifications.action === 'loan' && this.isPiece()) { urls.accept = ApiUrls.ownership_loans_pieces_confirm; urls.deny = ApiUrls.ownership_loans_pieces_deny; - } else if (this.props.notifications.action === 'loan_request' && this.isPiece()){ + } else if (this.props.notifications.action === 'loan_request' && this.isPiece()) { urls.accept = ApiUrls.ownership_loans_pieces_request_confirm; urls.deny = ApiUrls.ownership_loans_pieces_request_deny; } @@ -57,37 +57,29 @@ let RequestActionForm = React.createClass({ return urls; }, - getFormData(){ - if (this.isPiece()) { - return {piece_id: this.props.pieceOrEditions.id}; - } - else { - return {bitcoin_id: this.props.pieceOrEditions.map(function(edition){ - return edition.bitcoin_id; - }).join()}; - } + getFormData() { + return getAclFormDataId(this.isPiece(), this.props.pieceOrEditions); }, showNotification(option, action, owner) { return () => { - let message = getLangText('You have successfully') + ' ' + option + ' the ' + action + ' request ' + getLangText('from') + ' ' + owner; + const message = getLangText('You have successfully') + ' ' + option + ' the ' + action + ' request ' + getLangText('from') + ' ' + owner; - let notifications = new GlobalNotificationModel(message, 'success'); + const notifications = new GlobalNotificationModel(message, 'success'); GlobalNotificationActions.appendGlobalNotification(notifications); this.handleSuccess(); - }; }, handleSuccess() { - if (this.isPiece()){ + if (this.isPiece()) { NotificationActions.fetchPieceListNotifications(); - } - else { + } else { NotificationActions.fetchEditionListNotifications(); } - if(this.props.handleSuccess) { + + if (typeof this.props.handleSuccess === 'function') { this.props.handleSuccess(); } }, @@ -101,7 +93,7 @@ let RequestActionForm = React.createClass({ }, getAcceptButtonForm(urls) { - if(this.props.notifications.action === 'unconsign') { + if (this.props.notifications.action === 'unconsign') { return ( ); - } else if(this.props.notifications.action === 'loan_request') { + } else if (this.props.notifications.action === 'loan_request') { return ( @@ -157,7 +149,7 @@ let RequestActionForm = React.createClass({ {acceptButtonForm} @@ -169,7 +161,7 @@ let RequestActionForm = React.createClass({ return ( + buttons={this.getButtonForm()} /> ); } }); From d23331d9b9e46f6862d0212ef1c0278001ac885f Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 30 Oct 2015 17:43:20 +0100 Subject: [PATCH 05/58] Remove ReactS3FineUploader's dependency on react-router's location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ReactS3FineUploader used to check the current url’s query params to determine which method it should use to upload, but this decision means the component is tightly coupled with react-router and history.js. A major pain point is having to propagate the location prop all the way down to this component even when it’s not necessary. Now, ReactS3FineUploader’s parent elements can either parse the current query params themselves or, if they have a location from react-router, simply use the location. Added a few utils to help parse url params. --- .../further_details_fileuploader.js | 8 +- .../ascribe_forms/form_create_contract.js | 8 +- .../ascribe_forms/form_register_piece.js | 9 +- .../ascribe_forms/input_fineuploader.js | 10 +-- .../contract_settings_update_button.js | 8 +- .../file_drag_and_drop.js | 34 ++++---- .../file_drag_and_drop_dialog.js | 56 +++++++------ .../react_s3_fine_uploader.js | 74 +++++++---------- js/fetchers/edition_list_fetcher.js | 2 +- js/fetchers/piece_list_fetcher.js | 2 +- js/utils/fetch_api_utils.js | 57 +------------ js/utils/requests.js | 6 +- js/utils/url_utils.js | 83 +++++++++++++++++++ package.json | 3 + 14 files changed, 190 insertions(+), 170 deletions(-) create mode 100644 js/utils/url_utils.js diff --git a/js/components/ascribe_detail/further_details_fileuploader.js b/js/components/ascribe_detail/further_details_fileuploader.js index c5ef8a1c..33caf9b0 100644 --- a/js/components/ascribe_detail/further_details_fileuploader.js +++ b/js/components/ascribe_detail/further_details_fileuploader.js @@ -20,8 +20,7 @@ let FurtherDetailsFileuploader = React.createClass({ submitFile: React.PropTypes.func, isReadyForFormSubmission: React.PropTypes.func, editable: React.PropTypes.bool, - multiple: React.PropTypes.bool, - location: React.PropTypes.object + multiple: React.PropTypes.bool }, getDefaultProps() { @@ -89,11 +88,10 @@ let FurtherDetailsFileuploader = React.createClass({ }} areAssetsDownloadable={true} areAssetsEditable={this.props.editable} - multiple={this.props.multiple} - location={this.props.location}/> + multiple={this.props.multiple} /> ); } }); -export default FurtherDetailsFileuploader; \ No newline at end of file +export default FurtherDetailsFileuploader; diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index fe00cebc..aac4c5ea 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -28,8 +28,7 @@ let CreateContractForm = React.createClass({ fileClassToUpload: React.PropTypes.shape({ singular: React.PropTypes.string, plural: React.PropTypes.string - }), - location: React.PropTypes.object + }) }, getInitialState() { @@ -87,8 +86,7 @@ let CreateContractForm = React.createClass({ areAssetsEditable={true} setIsUploadReady={this.setIsUploadReady} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} - fileClassToUpload={this.props.fileClassToUpload} - location={this.props.location}/> + fileClassToUpload={this.props.fileClassToUpload} /> + uploadMethod={this.props.location.query.method} /> + uploadMethod={this.props.uploadMethod} + fileClassToUpload={this.props.fileClassToUpload} /> ); } }); -export default InputFineUploader; \ No newline at end of file +export default InputFineUploader; diff --git a/js/components/ascribe_settings/contract_settings_update_button.js b/js/components/ascribe_settings/contract_settings_update_button.js index f3bab156..ffd5ef4b 100644 --- a/js/components/ascribe_settings/contract_settings_update_button.js +++ b/js/components/ascribe_settings/contract_settings_update_button.js @@ -20,8 +20,7 @@ import { getLangText } from '../../utils/lang_utils'; let ContractSettingsUpdateButton = React.createClass({ propTypes: { - contract: React.PropTypes.object, - location: React.PropTypes.object + contract: React.PropTypes.object }, submitFile(file) { @@ -90,10 +89,9 @@ let ContractSettingsUpdateButton = React.createClass({ plural: getLangText('UPDATE') }} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} - submitFile={this.submitFile} - location={this.props.location}/> + submitFile={this.submitFile} /> ); } }); -export default ContractSettingsUpdateButton; \ No newline at end of file +export default ContractSettingsUpdateButton; diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js index 38ec459a..430dcab3 100644 --- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js @@ -27,6 +27,7 @@ let FileDragAndDrop = React.createClass({ areAssetsEditable: React.PropTypes.bool, enableLocalHashing: React.PropTypes.bool, + uploadMethod: React.PropTypes.string, // triggers a FileDragAndDrop-global spinner hashingProgress: React.PropTypes.number, @@ -41,8 +42,7 @@ let FileDragAndDrop = React.createClass({ plural: React.PropTypes.string }), - allowedExtensions: React.PropTypes.string, - location: React.PropTypes.object + allowedExtensions: React.PropTypes.string }, handleDragOver(event) { @@ -137,19 +137,19 @@ let FileDragAndDrop = React.createClass({ }, render: function () { - let { filesToUpload, - dropzoneInactive, - className, - hashingProgress, - handleCancelHashing, - multiple, - enableLocalHashing, - fileClassToUpload, - areAssetsDownloadable, - areAssetsEditable, - allowedExtensions, - location - } = this.props; + const { + filesToUpload, + dropzoneInactive, + className, + hashingProgress, + handleCancelHashing, + multiple, + enableLocalHashing, + uploadMethod, + fileClassToUpload, + areAssetsDownloadable, + areAssetsEditable, + allowedExtensions } = this.props; // has files only is true if there are files that do not have the status deleted or canceled let hasFiles = filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0; @@ -185,8 +185,8 @@ let FileDragAndDrop = React.createClass({ hasFiles={hasFiles} onClick={this.handleOnClick} enableLocalHashing={enableLocalHashing} - fileClassToUpload={fileClassToUpload} - location={location}/> + uploadMethod={uploadMethod} + fileClassToUpload={fileClassToUpload} /> {getLangText('Drag %s here', fileClass)}

,

{getLangText('or')}

@@ -37,26 +35,31 @@ let FileDragAndDropDialog = React.createClass({ }, render() { - const queryParams = this.props.location.query; + const { + hasFiles, + multipleFiles, + enableLocalHashing, + uploadMethod, + fileClassToUpload, + onClick } = this.props; - if(this.props.hasFiles) { + if (hasFiles) { return null; } else { - if(this.props.enableLocalHashing && !queryParams.method) { + if (enableLocalHashing && !uploadMethod) { + const currentQueryParams = getCurrentQueryParams(); - let queryParamsHash = Object.assign({}, queryParams); + const queryParamsHash = Object.assign({}, currentQueryParams); queryParamsHash.method = 'hash'; - let queryParamsUpload = Object.assign({}, queryParams); + const queryParamsUpload = Object.assign({}, currentQueryParams); queryParamsUpload.method = 'upload'; - let { location } = this.props; - return (

{getLangText('Would you rather')}

{getLangText('Hash your work')} @@ -64,9 +67,9 @@ let FileDragAndDropDialog = React.createClass({ or - + {getLangText('Upload and hash your work')} @@ -75,26 +78,27 @@ let FileDragAndDropDialog = React.createClass({
); } else { - if(this.props.multipleFiles) { + if (multipleFiles) { return ( - {this.getDragDialog(this.props.fileClassToUpload.plural)} + {this.getDragDialog(fileClassToUpload.plural)} - {getLangText('choose %s to upload', this.props.fileClassToUpload.plural)} + onClick={onClick}> + {getLangText('choose %s to upload', fileClassToUpload.plural)} ); } else { - let dialog = queryParams.method === 'hash' ? getLangText('choose a %s to hash', this.props.fileClassToUpload.singular) : getLangText('choose a %s to upload', this.props.fileClassToUpload.singular); + const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular) + : getLangText('choose a %s to upload', fileClassToUpload.singular); return ( - {this.getDragDialog(this.props.fileClassToUpload.singular)} + {this.getDragDialog(fileClassToUpload.singular)} + onClick={onClick}> {dialog} @@ -105,4 +109,4 @@ let FileDragAndDropDialog = React.createClass({ } }); -export default FileDragAndDropDialog; \ No newline at end of file +export default FileDragAndDropDialog; diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index e8cc8bfa..61dbcbcc 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -18,7 +18,6 @@ import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp } import { getCookie } from '../../utils/fetch_api_utils'; import { getLangText } from '../../utils/lang_utils'; - let ReactS3FineUploader = React.createClass({ propTypes: { keyRoutine: React.PropTypes.shape({ @@ -107,11 +106,14 @@ let ReactS3FineUploader = React.createClass({ // One solution we found in the process of tackling this problem was to hash // the file in the browser using md5 and then uploading the resulting text document instead // of the actual file. - // This boolean essentially enables that behavior + // + // This boolean and string essentially enable that behavior. + // Right now, we determine which upload method to use by appending a query parameter, + // which should be passed into 'uploadMethod': + // 'hash': upload using the hash + // 'upload': upload full file (default if not specified) enableLocalHashing: React.PropTypes.bool, - - // automatically injected by React-Router - query: React.PropTypes.object, + uploadMethod: React.PropTypes.string, // A class of a file the user has to upload // Needs to be defined both in singular as well as in plural @@ -126,9 +128,7 @@ let ReactS3FineUploader = React.createClass({ fileInputElement: React.PropTypes.oneOfType([ React.PropTypes.func, React.PropTypes.element - ]), - - location: React.PropTypes.object + ]) }, getDefaultProps() { @@ -192,11 +192,11 @@ let ReactS3FineUploader = React.createClass({ filesToUpload: [], uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()), csrfToken: getCookie(AppConstants.csrftoken), - + // -1: aborted // -2: uninitialized hashingProgress: -2, - + // this is for logging chunks: {} }; @@ -354,7 +354,6 @@ let ReactS3FineUploader = React.createClass({ /* FineUploader specific callback function handlers */ onUploadChunk(id, name, chunkData) { - let chunks = this.state.chunks; chunks[id + '-' + chunkData.startByte + '-' + chunkData.endByte] = { @@ -370,10 +369,9 @@ let ReactS3FineUploader = React.createClass({ }, onUploadChunkSuccess(id, chunkData, responseJson, xhr) { - let chunks = this.state.chunks; let chunkKey = id + '-' + chunkData.startByte + '-' + chunkData.endByte; - + if(chunks[chunkKey]) { chunks[chunkKey].completed = true; chunks[chunkKey].responseJson = responseJson; @@ -414,7 +412,7 @@ let ReactS3FineUploader = React.createClass({ } else { console.warn('You didn\'t define submitFile in as a prop in react-s3-fine-uploader'); } - + // for explanation, check comment of if statement above if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) { // also, lets check if after the completion of this upload, @@ -597,7 +595,6 @@ let ReactS3FineUploader = React.createClass({ } else { throw new Error(getLangText('File upload could not be paused.')); } - }, handleResumeFile(fileId) { @@ -647,16 +644,14 @@ let ReactS3FineUploader = React.createClass({ // md5 hash of a file locally and just upload a txt file containing that hash. // // In the view this only happens when the user is allowed to do local hashing as well - // as when the correct query parameter is present in the url ('hash' and not 'upload') - let queryParams = this.props.location.query; - if(this.props.enableLocalHashing && queryParams && queryParams.method === 'hash') { - - let convertedFilePromises = []; + // as when the correct method prop is present ('hash' and not 'upload') + if (this.props.enableLocalHashing && this.props.uploadMethod === 'hash') { + const convertedFilePromises = []; let overallFileSize = 0; + // "files" is not a classical Javascript array but a Javascript FileList, therefore // we can not use map to convert values for(let i = 0; i < files.length; i++) { - // for calculating the overall progress of all submitted files // we'll need to calculate the overall sum of all files' sizes overallFileSize += files[i].size; @@ -668,7 +663,6 @@ let ReactS3FineUploader = React.createClass({ // we're using promises to handle that let hashedFilePromise = computeHashOfFile(files[i]); convertedFilePromises.push(hashedFilePromise); - } // To react after the computation of all files, we define the resolvement @@ -676,7 +670,6 @@ let ReactS3FineUploader = React.createClass({ // with their txt representative Q.all(convertedFilePromises) .progress(({index, value: {progress, reject}}) => { - // hashing progress has been aborted from outside // To get out of the executing, we need to call reject from the // inside of the promise's execution. @@ -696,18 +689,14 @@ let ReactS3FineUploader = React.createClass({ // currently hashing files let overallHashingProgress = 0; for(let i = 0; i < files.length; i++) { - let filesSliceOfOverall = files[i].size / overallFileSize; overallHashingProgress += filesSliceOfOverall * files[i].progress; - } // Multiply by 100, since react-progressbar expects decimal numbers this.setState({ hashingProgress: overallHashingProgress * 100}); - }) .then((convertedFiles) => { - // clear hashing progress, since its done this.setState({ hashingProgress: -2}); @@ -823,20 +812,18 @@ let ReactS3FineUploader = React.createClass({ changeSet.status = { $set: status }; let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet }); - + this.setState({ filesToUpload }); }, isDropzoneInactive() { - let filesToDisplay = this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1); - let queryParams = this.props.location.query; + const filesToDisplay = this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1); - if((this.props.enableLocalHashing && !queryParams.method) || !this.props.areAssetsEditable || !this.props.multiple && filesToDisplay.length > 0) { + if ((this.props.enableLocalHashing && !this.props.uploadMethod) || !this.props.areAssetsEditable || !this.props.multiple && filesToDisplay.length > 0) { return true; } else { return false; } - }, getAllowedExtensions() { @@ -850,17 +837,16 @@ let ReactS3FineUploader = React.createClass({ }, render() { - let { - multiple, - areAssetsDownloadable, - areAssetsEditable, - onInactive, - enableLocalHashing, - fileClassToUpload, - validation, - fileInputElement, - location - } = this.props; + const { + multiple, + areAssetsDownloadable, + areAssetsEditable, + onInactive, + enableLocalHashing, + uploadMethod, + fileClassToUpload, + validation, + fileInputElement } = this.props; // Here we initialize the template that has been either provided from the outside // or the default input that is FileDragAndDrop. @@ -870,8 +856,8 @@ let ReactS3FineUploader = React.createClass({ areAssetsEditable, onInactive, enableLocalHashing, + uploadMethod, fileClassToUpload, - location, onDrop: this.handleUploadFile, filesToUpload: this.state.filesToUpload, handleDeleteFile: this.handleDeleteFile, diff --git a/js/fetchers/edition_list_fetcher.js b/js/fetchers/edition_list_fetcher.js index b416c595..93e4553d 100644 --- a/js/fetchers/edition_list_fetcher.js +++ b/js/fetchers/edition_list_fetcher.js @@ -2,8 +2,8 @@ import requests from '../utils/requests'; -import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; import { mergeOptions } from '../utils/general_utils'; +import { generateOrderingQueryParams } from '../utils/url_utils'; let EditionListFetcher = { /** diff --git a/js/fetchers/piece_list_fetcher.js b/js/fetchers/piece_list_fetcher.js index 8e58402a..6bd4eb3a 100644 --- a/js/fetchers/piece_list_fetcher.js +++ b/js/fetchers/piece_list_fetcher.js @@ -3,7 +3,7 @@ import requests from '../utils/requests'; import { mergeOptions } from '../utils/general_utils'; -import { generateOrderingQueryParams } from '../utils/fetch_api_utils'; +import { generateOrderingQueryParams } from '../utils/url_utils'; let PieceListFetcher = { /** diff --git a/js/utils/fetch_api_utils.js b/js/utils/fetch_api_utils.js index 3ed964ba..cb676fce 100644 --- a/js/utils/fetch_api_utils.js +++ b/js/utils/fetch_api_utils.js @@ -2,63 +2,10 @@ import Q from 'q'; -import { sanitize } from './general_utils'; import AppConstants from '../constants/application_constants'; // TODO: Create Unittests that test all functions - /** - * Takes a key-value object of this form: - * - * { - * 'page': 1, - * 'pageSize': 10 - * } - * - * and converts it to a query-parameter, which you can append to your URL. - * The return looks like this: - * - * ?page=1&page_size=10 - * - * CamelCase gets converted to snake_case! - * - */ -export function argsToQueryParams(obj) { - - obj = sanitize(obj); - - return Object - .keys(obj) - .map((key, i) => { - let s = ''; - - if(i === 0) { - s += '?'; - } else { - s += '&'; - } - - let snakeCaseKey = key.replace(/[A-Z]/, (match) => '_' + match.toLowerCase()); - - return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]); - }) - .join(''); -} - -/** - * Takes a string and a boolean and generates a string query parameter for - * an API call. - */ -export function generateOrderingQueryParams(orderBy, orderAsc) { - let interpolation = ''; - - if(!orderAsc) { - interpolation += '-'; - } - - return interpolation + orderBy; -} - export function status(response) { if (response.status >= 200 && response.status < 300) { return response; @@ -68,7 +15,7 @@ export function status(response) { export function getCookie(name) { let parts = document.cookie.split(';'); - + for(let i = 0; i < parts.length; i++) { if(parts[i].indexOf(AppConstants.csrftoken + '=') > -1) { return parts[i].split('=').pop(); @@ -111,4 +58,4 @@ export function fetchImageAsBlob(url) { xhr.send(); }); -} \ No newline at end of file +} diff --git a/js/utils/requests.js b/js/utils/requests.js index 7e9c9a58..bf203751 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -2,11 +2,11 @@ import Q from 'q'; -import { argsToQueryParams, getCookie } from '../utils/fetch_api_utils'; - import AppConstants from '../constants/application_constants'; -import {excludePropFromObject} from '../utils/general_utils'; +import { getCookie } from '../utils/fetch_api_utils'; +import { excludePropFromObject } from '../utils/general_utils'; +import { argsToQueryParams } from '../utils/url_utils'; class Requests { _merge(defaults, options) { diff --git a/js/utils/url_utils.js b/js/utils/url_utils.js new file mode 100644 index 00000000..cc875981 --- /dev/null +++ b/js/utils/url_utils.js @@ -0,0 +1,83 @@ +'use strict' + +import camelCase from 'camelcase'; +import snakeCase from 'snake-case'; +import qs from 'qs'; + +import { sanitize } from './general_utils'; + +// TODO: Create Unittests that test all functions + +/** + * Takes a key-value dictionary of this form: + * + * { + * 'page': 1, + * 'pageSize': 10 + * } + * + * and converts it to a query-parameter, which you can append to your URL. + * The return looks like this: + * + * ?page=1&page_size=10 + * + * CamelCase gets converted to snake_case! + * + * @param {object} obj Query params dictionary + * @return {string} Query params string + */ +export function argsToQueryParams(obj) { + const sanitizedObj = sanitize(obj); + const queryParamObj = {}; + + Object + .keys(sanitizedObj) + .forEach((key) => { + queryParamObj[snakeCase(key)] = sanitizedObj[key]; + }); + + // Use bracket arrayFormat as history.js and react-router use it + return '?' + qs.stringify(queryParamObj, { arrayFormat: 'brackets' }); +} + +/** + * Get the current url's query params as an key-val dictionary. + * snake_case gets converted to CamelCase! + * @return {object} Query params dictionary + */ +export function getCurrentQueryParams() { + return queryParamsToArgs(window.location.search.substring(1)); +} + +/** + * Convert the given query param string into a key-val dictionary. + * snake_case gets converted to CamelCase! + * @param {string} queryParamString Query params string + * @return {object} Query params dictionary + */ +export function queryParamsToArgs(queryParamString) { + const qsQueryParamObj = qs.parse(queryParamString); + const camelCaseParamObj = {}; + + Object + .keys(qsQueryParamObj) + .forEach((key) => { + camelCaseParamObj[camelCase(key)] = qsQueryParamObj[key]; + }); + + return camelCaseParamObj; +} + +/** + * Takes a string and a boolean and generates a string query parameter for + * an API call. + */ +export function generateOrderingQueryParams(orderBy, orderAsc) { + let interpolation = ''; + + if(!orderAsc) { + interpolation += '-'; + } + + return interpolation + orderBy; +} diff --git a/package.json b/package.json index 2b770fd3..4e7cd6a9 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "browser-sync": "^2.7.5", "browserify": "^9.0.8", "browserify-shim": "^3.8.10", + "camelcase": "^1.2.1", "classnames": "^1.2.2", "compression": "^1.4.4", "envify": "^3.4.0", @@ -73,6 +74,7 @@ "object-assign": "^2.0.0", "opn": "^3.0.2", "q": "^1.4.1", + "qs": "^5.2.0", "raven-js": "^1.1.19", "react": "0.13.2", "react-bootstrap": "0.25.1", @@ -83,6 +85,7 @@ "react-textarea-autosize": "^2.5.2", "reactify": "^1.1.0", "shmui": "^0.1.0", + "snake-case": "^1.1.1", "spark-md5": "~1.0.0", "uglifyjs": "^2.4.10", "vinyl-buffer": "^1.0.0", From 1e328b722b8009a6ec3e9c8a2f4e7998041d5ac4 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Fri, 30 Oct 2015 17:46:51 +0100 Subject: [PATCH 06/58] Sanitize utility should not modify given object Mutating arguments and then returning them is redundant and confusing behaviour (why pass it back if they already have it? Am I getting a new copy since it returns something?). --- js/utils/general_utils.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/js/utils/general_utils.js b/js/utils/general_utils.js index 7c13f9b5..cd73ba45 100644 --- a/js/utils/general_utils.js +++ b/js/utils/general_utils.js @@ -1,29 +1,23 @@ 'use strict'; +import _ from 'lodash'; + /** - * Takes an object and deletes all keys that are - * - * tagged as false by the passed in filter function + * Takes an object and returns a shallow copy without any keys + * that fail the passed in filter function. + * Does not modify the passed in object. * * @param {object} obj regular javascript object * @return {object} regular javascript object without null values or empty strings */ export function sanitize(obj, filterFn) { - if(!filterFn) { + if (!filterFn) { // By matching null with a double equal, we can match undefined and null // http://stackoverflow.com/a/15992131 filterFn = (val) => val == null || val === ''; } - Object - .keys(obj) - .map((key) => { - if(filterFn(obj[key])) { - delete obj[key]; - } - }); - - return obj; + return _.omit(obj, filterFn); } /** From 147c852b022adb0a5214d8a28924c7e12c4bb3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 2 Nov 2015 10:32:48 +0100 Subject: [PATCH 07/58] Replace getFullYear() with getUTCFullYear() --- .../ascribe_accordion_list/accordion_list_item_wallet.js | 2 +- js/components/ascribe_detail/edition.js | 2 +- js/components/ascribe_detail/piece_container.js | 2 +- .../ascribe_accordion_list/accordion_list_item_prize.js | 2 +- .../prize/components/ascribe_detail/prize_piece_container.js | 2 +- .../wallet/components/ascribe_detail/wallet_piece_container.js | 2 +- .../cyland/cyland_accordion_list/cyland_accordion_list_item.js | 2 +- .../ikonotv_accordion_list/ikonotv_accordion_list_item.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/js/components/ascribe_accordion_list/accordion_list_item_wallet.js b/js/components/ascribe_accordion_list/accordion_list_item_wallet.js index 185f6e05..8899c67e 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item_wallet.js +++ b/js/components/ascribe_accordion_list/accordion_list_item_wallet.js @@ -129,7 +129,7 @@ let AccordionListItemWallet = React.createClass({ piece={this.props.content} subsubheading={
- {new Date(this.props.content.date_created).getFullYear()} + {new Date(this.props.content.date_created).getUTCFullYear()} {this.getLicences()}
} buttons={ diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index 64cbf714..ae53728f 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -85,7 +85,7 @@ let Edition = React.createClass({

{this.props.edition.title}

- +

{this.state.piece.title}

- + {this.state.piece.num_editions > 0 ? : null}
diff --git a/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js b/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js index caef504b..8eac81d1 100644 --- a/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js +++ b/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js @@ -182,7 +182,7 @@ let AccordionListItemPrize = React.createClass({ artistName={artistName} subsubheading={
- {new Date(this.props.content.date_created).getFullYear()} + {new Date(this.props.content.date_created).getUTCFullYear()}
} buttons={this.getPrizeButtons()} badge={this.getPrizeBadge()}> diff --git a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js b/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js index 07e84b0e..6bd47c18 100644 --- a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js +++ b/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js @@ -141,7 +141,7 @@ let PieceContainer = React.createClass({

{this.state.piece.title}

- + {artistEmail} {this.getActions()}
diff --git a/js/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js b/js/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js index 5644b5b0..e765bd7b 100644 --- a/js/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js +++ b/js/components/whitelabel/wallet/components/ascribe_detail/wallet_piece_container.js @@ -39,7 +39,7 @@ let WalletPieceContainer = React.createClass({

{this.props.piece.title}

- +
} diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js b/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js index 755e550b..0b4ec543 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_accordion_list/cyland_accordion_list_item.js @@ -100,7 +100,7 @@ let CylandAccordionListItem = React.createClass({ piece={this.props.content} subsubheading={
- {new Date(this.props.content.date_created).getFullYear()} + {new Date(this.props.content.date_created).getUTCFullYear()}
} buttons={this.getSubmitButtons()}> {this.props.children} diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_accordion_list/ikonotv_accordion_list_item.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_accordion_list/ikonotv_accordion_list_item.js index 7445eb36..e1187b7c 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_accordion_list/ikonotv_accordion_list_item.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_accordion_list/ikonotv_accordion_list_item.js @@ -106,7 +106,7 @@ let IkonotvAccordionListItem = React.createClass({ piece={this.props.content} subsubheading={
- {new Date(this.props.content.date_created).getFullYear()} + {new Date(this.props.content.date_created).getUTCFullYear()}
} buttons={this.getSubmitButtons()}> {this.props.children} From a513af984d0d7e98d419b8ff376e3411d2d01360 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Mon, 2 Nov 2015 10:41:59 +0100 Subject: [PATCH 08/58] Update cyland for FineUploader changes --- .../cyland/cyland_detail/cyland_piece_container.js | 4 +--- .../cyland/cyland_forms/cyland_additional_data_form.js | 8 +++----- .../wallet/components/cyland/cyland_register_piece.js | 3 +-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js b/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js index 79d63abf..7e784981 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_detail/cyland_piece_container.js @@ -33,7 +33,6 @@ import { mergeOptions } from '../../../../../../utils/general_utils'; let CylandPieceContainer = React.createClass({ propTypes: { - location: React.PropTypes.object, params: React.PropTypes.object }, @@ -107,8 +106,7 @@ let CylandPieceContainer = React.createClass({ + isInline={true} /> ); diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js b/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js index 63863b2d..0adfcb40 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js @@ -26,8 +26,7 @@ let CylandAdditionalDataForm = React.createClass({ handleSuccess: React.PropTypes.func, piece: React.PropTypes.object.isRequired, disabled: React.PropTypes.bool, - isInline: React.PropTypes.bool, - location: React.PropTypes.object + isInline: React.PropTypes.bool }, getDefaultProps() { @@ -143,8 +142,7 @@ let CylandAdditionalDataForm = React.createClass({ isReadyForFormSubmission={formSubmissionValidation.fileOptional} pieceId={piece.id} otherData={piece.other_data} - multiple={true} - location={this.props.location}/> + multiple={true} /> ); } else { @@ -157,4 +155,4 @@ let CylandAdditionalDataForm = React.createClass({ } }); -export default CylandAdditionalDataForm; \ No newline at end of file +export default CylandAdditionalDataForm; diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js b/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js index ca755cf4..1903c7a2 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js @@ -210,8 +210,7 @@ let CylandRegisterPiece = React.createClass({ 1} handleSuccess={this.handleAdditionalDataSuccess} - piece={this.state.piece} - location={this.props.location}/> + piece={this.state.piece} /> From 5f5461c10ddd11362a79708913e3d937cdd74970 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Mon, 2 Nov 2015 10:42:15 +0100 Subject: [PATCH 09/58] Remove warning for missing prop from FurtherDetailsFileUploader --- js/components/ascribe_detail/further_details_fileuploader.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/components/ascribe_detail/further_details_fileuploader.js b/js/components/ascribe_detail/further_details_fileuploader.js index 33caf9b0..9a1f091c 100644 --- a/js/components/ascribe_detail/further_details_fileuploader.js +++ b/js/components/ascribe_detail/further_details_fileuploader.js @@ -43,6 +43,7 @@ let FurtherDetailsFileuploader = React.createClass({ return ( Date: Mon, 2 Nov 2015 12:10:41 +0100 Subject: [PATCH 10/58] Check for a new csrf token on componentWillReceiveProps instead of componentWillUpdate this.setState() should not be used in componentWillUpdate(): https://facebook.github.io/react/docs/component-specs.html#updating-comp onentwillupdate --- js/components/ascribe_uploader/react_s3_fine_uploader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 61dbcbcc..685c2b2f 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -202,7 +202,7 @@ let ReactS3FineUploader = React.createClass({ }; }, - componentWillUpdate() { + componentWillReceiveProps() { // since the csrf header is defined in this component's props, // everytime the csrf cookie is changed we'll need to reinitalize // fineuploader and update the actual csrf token From 6c8016e094138db91f7e602ea2381eb7a1e762ac Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Mon, 2 Nov 2015 15:19:52 +0100 Subject: [PATCH 11/58] Remove misleading editions prop to pieceOrEditions --- js/components/ascribe_buttons/acl_button_list.js | 14 +++++++------- .../ascribe_detail/edition_action_panel.js | 4 ++-- js/components/ascribe_detail/piece_container.js | 2 +- .../piece_list_bulk_modal.js | 4 ++-- .../ascribe_detail/wallet_action_panel.js | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/js/components/ascribe_buttons/acl_button_list.js b/js/components/ascribe_buttons/acl_button_list.js index 6aec6c77..83495363 100644 --- a/js/components/ascribe_buttons/acl_button_list.js +++ b/js/components/ascribe_buttons/acl_button_list.js @@ -17,7 +17,7 @@ import { mergeOptions } from '../../utils/general_utils'; let AclButtonList = React.createClass({ propTypes: { className: React.PropTypes.string, - editions: React.PropTypes.oneOfType([ + pieceOrEditions: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.array ]).isRequired, @@ -82,7 +82,7 @@ let AclButtonList = React.createClass({ const { className, buttonsStyle, availableAcls, - editions, + pieceOrEditions, handleSuccess } = this.props; const { currentUser } = this.state; @@ -92,27 +92,27 @@ let AclButtonList = React.createClass({ {this.renderChildren()} diff --git a/js/components/ascribe_detail/edition_action_panel.js b/js/components/ascribe_detail/edition_action_panel.js index 7b075ce0..a2ad1e58 100644 --- a/js/components/ascribe_detail/edition_action_panel.js +++ b/js/components/ascribe_detail/edition_action_panel.js @@ -107,7 +107,7 @@ let EditionActionPanel = React.createClass({ Date: Mon, 2 Nov 2015 15:20:02 +0100 Subject: [PATCH 12/58] Remove unnecessary default props --- js/components/ascribe_buttons/acls/acl_button.js | 6 ------ js/components/ascribe_forms/acl_form_factory.js | 6 ------ 2 files changed, 12 deletions(-) diff --git a/js/components/ascribe_buttons/acls/acl_button.js b/js/components/ascribe_buttons/acls/acl_button.js index f7149e79..d81a19f4 100644 --- a/js/components/ascribe_buttons/acls/acl_button.js +++ b/js/components/ascribe_buttons/acls/acl_button.js @@ -36,12 +36,6 @@ let AclButton = React.createClass({ className: React.PropTypes.string }, - getDefaultProps() { - return { - buttonAcceptClassName: '' - }; - }, - // Removes the acl_ prefix and converts to upper case sanitizeAction() { if (this.props.buttonAcceptName) { diff --git a/js/components/ascribe_forms/acl_form_factory.js b/js/components/ascribe_forms/acl_form_factory.js index dc5ebd4e..d5494c2d 100644 --- a/js/components/ascribe_forms/acl_form_factory.js +++ b/js/components/ascribe_forms/acl_form_factory.js @@ -31,12 +31,6 @@ let AclFormFactory = React.createClass({ showNotification: React.PropTypes.bool }, - getDefaultProps() { - return { - showNotification: false - }; - }, - isPiece() { return this.props.pieceOrEditions.constructor !== Array; }, From 7746241a592d9f0876e21c2e2167fd4288870a5e Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Mon, 2 Nov 2015 15:21:27 +0100 Subject: [PATCH 13/58] Fix getLangText() when using multiple placeholders --- js/utils/lang_utils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/utils/lang_utils.js b/js/utils/lang_utils.js index f2e2fa14..ee2d292c 100644 --- a/js/utils/lang_utils.js +++ b/js/utils/lang_utils.js @@ -22,15 +22,15 @@ export function getLangText(s, ...args) { let lang = getLang(); try { if(lang in languages) { - return formatText(languages[lang][s], args); + return formatText(languages[lang][s], ...args); } else { // just use the english language - return formatText(languages['en-US'][s], args); + return formatText(languages['en-US'][s], ...args); } } catch(err) { //if(!(s in languages[lang])) { //console.warn('Language-string is not in constants file. Add: "' + s + '" to the "' + lang + '" language file. Defaulting to keyname'); - return formatText(s, args); + return formatText(s, ...args); //} else { // console.error(err); //} From a0ebc7dc58ca948f477b6d6e3e1323f00f43813f Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Mon, 2 Nov 2015 15:21:33 +0100 Subject: [PATCH 14/58] Use string formatting for RequestActionForm's notification message --- js/components/ascribe_forms/form_request_action.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/js/components/ascribe_forms/form_request_action.js b/js/components/ascribe_forms/form_request_action.js index ddf832b4..d3d1bc71 100644 --- a/js/components/ascribe_forms/form_request_action.js +++ b/js/components/ascribe_forms/form_request_action.js @@ -63,8 +63,7 @@ let RequestActionForm = React.createClass({ showNotification(option, action, owner) { return () => { - const message = getLangText('You have successfully') + ' ' + option + ' the ' + action + ' request ' + getLangText('from') + ' ' + owner; - + const message = getLangText('You have successfully %s the %s request from %s', getLangText(option), getLangText(action), owner); const notifications = new GlobalNotificationModel(message, 'success'); GlobalNotificationActions.appendGlobalNotification(notifications); @@ -118,7 +117,7 @@ let RequestActionForm = React.createClass({ url={urls.accept} getFormData={this.getFormData} handleSuccess={ - this.showNotification(getLangText('accepted'), this.props.notifications.action, this.props.notifications.by) + this.showNotification('accepted', this.props.notifications.action, this.props.notifications.by) } isInline={true} className='inline pull-right'> @@ -143,7 +142,7 @@ let RequestActionForm = React.createClass({ isInline={true} getFormData={this.getFormData} handleSuccess={ - this.showNotification(getLangText('denied'), this.props.notifications.action, this.props.notifications.by) + this.showNotification('denied', this.props.notifications.action, this.props.notifications.by) } className='inline pull-right'> - } - handleSuccess={handleSuccess} - title={title}> - - - - ); +export default function ({ action, displayName, title, tooltip }) { + if (AppConstants.aclList.indexOf(action) < 0) { + console.warn('Your specified aclName did not match a an acl class.'); } -}); -export default AclButton; + return React.createClass({ + displayName: displayName, + + propTypes: { + availableAcls: React.PropTypes.object.isRequired, + buttonAcceptName: React.PropTypes.string, + buttonAcceptClassName: React.PropTypes.string, + currentUser: React.PropTypes.object.isRequired, + email: React.PropTypes.string, + pieceOrEditions: React.PropTypes.oneOfType([ + React.PropTypes.object, + React.PropTypes.array + ]).isRequired, + handleSuccess: React.PropTypes.func.isRequired, + className: React.PropTypes.string + }, + + // Removes the acl_ prefix and converts to upper case + sanitizeAction() { + if (this.props.buttonAcceptName) { + return this.props.buttonAcceptName; + } + return action.split('acl_')[1].toUpperCase(); + }, + + render() { + const { + availableAcls, + buttonAcceptClassName, + currentUser, + email, + pieceOrEditions, + handleSuccess } = this.props; + + return ( + + + {this.sanitizeAction()} + + } + handleSuccess={handleSuccess} + title={title}> + + + + ); + } + }); +} diff --git a/js/components/ascribe_buttons/acls/consign_button.js b/js/components/ascribe_buttons/acls/consign_button.js index 6a3759db..88c86097 100644 --- a/js/components/ascribe_buttons/acls/consign_button.js +++ b/js/components/ascribe_buttons/acls/consign_button.js @@ -4,24 +4,11 @@ import React from 'react'; import AclButton from './acl_button'; -import { omitFromObject } from '../../../utils/general_utils'; import { getLangText } from '../../../utils/lang_utils'; -let ConsignButton = React.createClass({ - propTypes: { - ...omitFromObject(AclButton.propTypes, ['action']), - email: React.PropTypes.string - }, - - render() { - return ( - - ); - } +export default AclButton({ + action: 'acl_consign', + displayName: 'ConsignButton', + title: getLangText('Consign artwork'), + tooltip: getLangText('Have someone else sell the artwork') }); - -export default ConsignButton; diff --git a/js/components/ascribe_buttons/acls/loan_button.js b/js/components/ascribe_buttons/acls/loan_button.js index 283b43eb..4b803ceb 100644 --- a/js/components/ascribe_buttons/acls/loan_button.js +++ b/js/components/ascribe_buttons/acls/loan_button.js @@ -4,24 +4,11 @@ import React from 'react'; import AclButton from './acl_button'; -import { omitFromObject } from '../../../utils/general_utils'; import { getLangText } from '../../../utils/lang_utils'; -let LoanButton = React.createClass({ - propTypes: { - ...omitFromObject(AclButton.propTypes, ['action']), - email: React.PropTypes.string - }, - - render() { - return ( - - ); - } +export default AclButton({ + action: 'acl_loan', + displayName: 'LoanButton', + title: getLangText('Loan artwork'), + tooltip: getLangText('Loan your artwork for a limited period of time') }); - -export default LoanButton; diff --git a/js/components/ascribe_buttons/acls/loan_request_button.js b/js/components/ascribe_buttons/acls/loan_request_button.js index f4ffe9a4..a1ec5f3b 100644 --- a/js/components/ascribe_buttons/acls/loan_request_button.js +++ b/js/components/ascribe_buttons/acls/loan_request_button.js @@ -4,21 +4,11 @@ import React from 'react'; import AclButton from './acl_button'; -import { omitFromObject } from '../../../utils/general_utils'; import { getLangText } from '../../../utils/lang_utils'; -let LoanButton = React.createClass({ - propTypes: omitFromObject(AclButton.propTypes, ['action']), - - render() { - return ( - - ); - } +export default AclButton({ + action: 'acl_loan_request', + displayName: 'LoanRequestButton', + title: getLangText('Loan artwork'), + tooltip: getLangText('Someone requested you to loan your artwork for a limited period of time') }); - -export default LoanButton; diff --git a/js/components/ascribe_buttons/acls/share_button.js b/js/components/ascribe_buttons/acls/share_button.js index 4fb914a0..83781aed 100644 --- a/js/components/ascribe_buttons/acls/share_button.js +++ b/js/components/ascribe_buttons/acls/share_button.js @@ -4,21 +4,11 @@ import React from 'react'; import AclButton from './acl_button'; -import { omitFromObject } from '../../../utils/general_utils'; import { getLangText } from '../../../utils/lang_utils'; -let ShareButton = React.createClass({ - propTypes: omitFromObject(AclButton.propTypes, ['action']), - - render() { - return ( - - ); - } +export default AclButton({ + action: 'acl_share', + displayName: 'ShareButton', + title: getLangText('Share artwork'), + tooltip: getLangText('Share the artwork') }); - -export default ShareButton; diff --git a/js/components/ascribe_buttons/acls/transfer_button.js b/js/components/ascribe_buttons/acls/transfer_button.js index e85a81d1..346907ca 100644 --- a/js/components/ascribe_buttons/acls/transfer_button.js +++ b/js/components/ascribe_buttons/acls/transfer_button.js @@ -4,21 +4,11 @@ import React from 'react'; import AclButton from './acl_button'; -import { omitFromObject } from '../../../utils/general_utils'; import { getLangText } from '../../../utils/lang_utils'; -let TransferButton = React.createClass({ - propTypes: omitFromObject(AclButton.propTypes, ['action']), - - render() { - return ( - - ); - } +export default AclButton({ + action: 'acl_transfer', + displayName: 'TransferButton', + title: getLangText('Transfer artwork'), + tooltip: getLangText('Transfer the ownership of the artwork') }); - -export default TransferButton; diff --git a/js/components/ascribe_buttons/acls/unconsign_button.js b/js/components/ascribe_buttons/acls/unconsign_button.js index 39029c18..ce1ed0bc 100644 --- a/js/components/ascribe_buttons/acls/unconsign_button.js +++ b/js/components/ascribe_buttons/acls/unconsign_button.js @@ -4,21 +4,11 @@ import React from 'react'; import AclButton from './acl_button'; -import { omitFromObject } from '../../../utils/general_utils'; import { getLangText } from '../../../utils/lang_utils'; -let UnconsignButton = React.createClass({ - propTypes: omitFromObject(AclButton.propTypes, ['action']), - - render() { - return ( - - ); - } +export default AclButton({ + action: 'acl_unconsign', + displayName: 'UnconsignButton', + title: getLangText('Unconsign artwork'), + tooltip: getLangText('Have the owner manage his sales again') }); - -export default UnconsignButton; diff --git a/js/components/ascribe_routes/proxy_routes/auth_proxy_handler.js b/js/components/ascribe_routes/proxy_routes/auth_proxy_handler.js index cdfc129b..4f0fe307 100644 --- a/js/components/ascribe_routes/proxy_routes/auth_proxy_handler.js +++ b/js/components/ascribe_routes/proxy_routes/auth_proxy_handler.js @@ -31,6 +31,8 @@ export default function AuthProxyHandler({to, when}) { return (Component) => { return React.createClass({ + displayName: 'AuthProxyHandler', + propTypes: { location: object }, diff --git a/js/constants/application_constants.js b/js/constants/application_constants.js index 0fe5e210..84e8fd2f 100644 --- a/js/constants/application_constants.js +++ b/js/constants/application_constants.js @@ -11,7 +11,7 @@ let constants = { 'serverUrl': window.SERVER_URL, 'baseUrl': window.BASE_URL, 'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions', - 'acl_loan', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view', + 'acl_loan', 'acl_loan_request', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view', 'acl_withdraw_transfer', 'acl_wallet_submit'], 'version': 0.1, From 5adc34faebb23051c56b1d054d58c82457dc0617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 5 Nov 2015 16:45:12 +0100 Subject: [PATCH 27/58] Move and rename sluice prize app to 'simple_prize' --- .../prize/constants/prize_api_urls.js | 2 +- .../whitelabel/prize/prize_routes.js | 57 +++++++++++-------- .../actions/prize_actions.js | 2 +- .../actions/prize_jury_actions.js | 2 +- .../actions/prize_rating_actions.js | 2 +- .../accordion_list_item_prize.js | 20 +++---- .../ascribe_buttons/submit_to_prize_button.js | 6 +- .../ascribe_detail/prize_piece_container.js | 46 +++++++-------- .../components/prize_hero.js | 2 +- .../components/prize_landing.js | 8 +-- .../components/prize_login_container.js | 6 +- .../components/prize_piece_list.js | 12 ++-- .../components/prize_register_piece.js | 12 ++-- .../components/prize_settings_container.js | 26 ++++----- .../components/prize_signup_container.js | 6 +- .../fetchers/prize_fetcher.js | 2 +- .../fetchers/prize_jury_fetcher.js | 2 +- .../fetchers/prize_rating_fetcher.js | 2 +- .../prize/{ => simple_prize}/prize_app.js | 8 +-- .../stores/prize_jury_store.js | 2 +- .../stores/prize_rating_store.js | 2 +- .../{ => simple_prize}/stores/prize_store.js | 2 +- 22 files changed, 119 insertions(+), 110 deletions(-) rename js/components/whitelabel/prize/{ => simple_prize}/actions/prize_actions.js (94%) rename js/components/whitelabel/prize/{ => simple_prize}/actions/prize_jury_actions.js (97%) rename js/components/whitelabel/prize/{ => simple_prize}/actions/prize_rating_actions.js (98%) rename js/components/whitelabel/prize/{ => simple_prize}/components/ascribe_accordion_list/accordion_list_item_prize.js (89%) rename js/components/whitelabel/prize/{ => simple_prize}/components/ascribe_buttons/submit_to_prize_button.js (86%) rename js/components/whitelabel/prize/{ => simple_prize}/components/ascribe_detail/prize_piece_container.js (90%) rename js/components/whitelabel/prize/{ => simple_prize}/components/prize_hero.js (83%) rename js/components/whitelabel/prize/{ => simple_prize}/components/prize_landing.js (93%) rename js/components/whitelabel/prize/{ => simple_prize}/components/prize_login_container.js (83%) rename js/components/whitelabel/prize/{ => simple_prize}/components/prize_piece_list.js (86%) rename js/components/whitelabel/prize/{ => simple_prize}/components/prize_register_piece.js (87%) rename js/components/whitelabel/prize/{ => simple_prize}/components/prize_settings_container.js (91%) rename js/components/whitelabel/prize/{ => simple_prize}/components/prize_signup_container.js (86%) rename js/components/whitelabel/prize/{ => simple_prize}/fetchers/prize_fetcher.js (70%) rename js/components/whitelabel/prize/{ => simple_prize}/fetchers/prize_jury_fetcher.js (88%) rename js/components/whitelabel/prize/{ => simple_prize}/fetchers/prize_rating_fetcher.js (90%) rename js/components/whitelabel/prize/{ => simple_prize}/prize_app.js (87%) rename js/components/whitelabel/prize/{ => simple_prize}/stores/prize_jury_store.js (96%) rename js/components/whitelabel/prize/{ => simple_prize}/stores/prize_rating_store.js (93%) rename js/components/whitelabel/prize/{ => simple_prize}/stores/prize_store.js (87%) diff --git a/js/components/whitelabel/prize/constants/prize_api_urls.js b/js/components/whitelabel/prize/constants/prize_api_urls.js index 2d35cf19..e493cffd 100644 --- a/js/components/whitelabel/prize/constants/prize_api_urls.js +++ b/js/components/whitelabel/prize/constants/prize_api_urls.js @@ -18,7 +18,7 @@ function getPrizeApiUrls(subdomain) { 'ratings': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/', 'rating': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/', 'rating_average': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/average/', - 'select_piece' : AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/select/', + 'select_piece': AppPrizeConstants.prizeApiEndpoint + subdomain + '/ratings/${piece_id}/select/', 'notes': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/', 'note': AppPrizeConstants.prizeApiEndpoint + subdomain + '/notes/${piece_id}/' diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index 14d8b4d9..a9004927 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -3,58 +3,67 @@ import React from 'react'; import { Route, IndexRoute } from 'react-router'; -import Landing from './components/prize_landing'; -import LoginContainer from './components/prize_login_container'; -import LogoutContainer from '../../../components/logout_container'; -import SignupContainer from './components/prize_signup_container'; -import PasswordResetContainer from '../../../components/password_reset_container'; -import PrizeRegisterPiece from './components/prize_register_piece'; -import PrizePieceList from './components/prize_piece_list'; -import PrizePieceContainer from './components/ascribe_detail/prize_piece_container'; -import EditionContainer from '../../ascribe_detail/edition_container'; -import SettingsContainer from './components/prize_settings_container'; -import CoaVerifyContainer from '../../../components/coa_verify_container'; -import ErrorNotFoundPage from '../../../components/error_not_found_page'; +import SPLanding from './simple_prize/components/prize_landing'; +import SPLoginContainer from './simple_prize/components/prize_login_container'; +import SPSignupContainer from './simple_prize/components/prize_signup_container'; +import SPRegisterPiece from './simple_prize/components/prize_register_piece'; +import SPPieceList from './simple_prize/components/prize_piece_list'; +import SPPieceContainer from './simple_prize/components/ascribe_detail/prize_piece_container'; +import SPSettingsContainer from './simple_prize/components/prize_settings_container'; +import SPApp from './simple_prize/prize_app'; -import App from './prize_app'; +import EditionContainer from '../../ascribe_detail/edition_container'; +import LogoutContainer from '../../logout_container'; +import PasswordResetContainer from '../../password_reset_container'; +import CoaVerifyContainer from '../../coa_verify_container'; +import ErrorNotFoundPage from '../../error_not_found_page'; import AuthProxyHandler from '../../../components/ascribe_routes/proxy_routes/auth_proxy_handler'; -function getRoutes() { - return ( - - +const ROUTES = { + 'sluice': ( + + + component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SPLoginContainer)} /> + component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SPSignupContainer)} /> + component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(SPSettingsContainer)}/> - + - ); + ) +}; + + +function getRoutes(commonRoutes, subdomain) { + if(subdomain in ROUTES) { + return ROUTES[subdomain]; + } else { + throw new Error('Subdomain wasn\'t specified in the wallet app.'); + } } diff --git a/js/components/whitelabel/prize/actions/prize_actions.js b/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js similarity index 94% rename from js/components/whitelabel/prize/actions/prize_actions.js rename to js/components/whitelabel/prize/simple_prize/actions/prize_actions.js index fcd9e91e..27127035 100644 --- a/js/components/whitelabel/prize/actions/prize_actions.js +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import Q from 'q'; import PrizeFetcher from '../fetchers/prize_fetcher'; diff --git a/js/components/whitelabel/prize/actions/prize_jury_actions.js b/js/components/whitelabel/prize/simple_prize/actions/prize_jury_actions.js similarity index 97% rename from js/components/whitelabel/prize/actions/prize_jury_actions.js rename to js/components/whitelabel/prize/simple_prize/actions/prize_jury_actions.js index 9bd03f59..24cf08e8 100644 --- a/js/components/whitelabel/prize/actions/prize_jury_actions.js +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_jury_actions.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import Q from 'q'; import PrizeJuryFetcher from '../fetchers/prize_jury_fetcher'; diff --git a/js/components/whitelabel/prize/actions/prize_rating_actions.js b/js/components/whitelabel/prize/simple_prize/actions/prize_rating_actions.js similarity index 98% rename from js/components/whitelabel/prize/actions/prize_rating_actions.js rename to js/components/whitelabel/prize/simple_prize/actions/prize_rating_actions.js index 184d84e7..68b5334b 100644 --- a/js/components/whitelabel/prize/actions/prize_rating_actions.js +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_rating_actions.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import Q from 'q'; import PrizeRatingFetcher from '../fetchers/prize_rating_fetcher'; diff --git a/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js b/js/components/whitelabel/prize/simple_prize/components/ascribe_accordion_list/accordion_list_item_prize.js similarity index 89% rename from js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js rename to js/components/whitelabel/prize/simple_prize/components/ascribe_accordion_list/accordion_list_item_prize.js index caef504b..20184ff1 100644 --- a/js/components/whitelabel/prize/components/ascribe_accordion_list/accordion_list_item_prize.js +++ b/js/components/whitelabel/prize/simple_prize/components/ascribe_accordion_list/accordion_list_item_prize.js @@ -4,25 +4,25 @@ import React from 'react'; import { Link } from 'react-router'; import StarRating from 'react-star-rating'; -import PieceListActions from '../../../../../actions/piece_list_actions'; -import PieceListStore from '../../../../../stores/piece_list_store'; +import PieceListActions from '../../../../../../actions/piece_list_actions'; +import PieceListStore from '../../../../../../stores/piece_list_store'; import PrizeRatingActions from '../../actions/prize_rating_actions'; -import UserStore from '../../../../../stores/user_store'; +import UserStore from '../../../../../../stores/user_store'; -import InputCheckbox from '../../../../ascribe_forms/input_checkbox'; +import InputCheckbox from '../../../../../ascribe_forms/input_checkbox'; -import AccordionListItemPiece from '../../../../ascribe_accordion_list/accordion_list_item_piece'; +import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece'; -import GlobalNotificationModel from '../../../../../models/global_notification_model'; -import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; +import GlobalNotificationModel from '../../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../../actions/global_notification_actions'; -import AclProxy from '../../../../acl_proxy'; +import AclProxy from '../../../../../acl_proxy'; import SubmitToPrizeButton from './../ascribe_buttons/submit_to_prize_button'; -import { getLangText } from '../../../../../utils/lang_utils'; -import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../../utils/lang_utils'; +import { mergeOptions } from '../../../../../../utils/general_utils'; let AccordionListItemPrize = React.createClass({ diff --git a/js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js b/js/components/whitelabel/prize/simple_prize/components/ascribe_buttons/submit_to_prize_button.js similarity index 86% rename from js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js rename to js/components/whitelabel/prize/simple_prize/components/ascribe_buttons/submit_to_prize_button.js index 409b8aa1..8ceb87ea 100644 --- a/js/components/whitelabel/prize/components/ascribe_buttons/submit_to_prize_button.js +++ b/js/components/whitelabel/prize/simple_prize/components/ascribe_buttons/submit_to_prize_button.js @@ -3,10 +3,10 @@ import React from 'react'; import classNames from 'classnames'; -import ModalWrapper from '../../../../ascribe_modal/modal_wrapper'; -import PieceSubmitToPrizeForm from '../../../../ascribe_forms/form_submit_to_prize'; +import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper'; +import PieceSubmitToPrizeForm from '../../../../../ascribe_forms/form_submit_to_prize'; -import { getLangText } from '../../../../../utils/lang_utils'; +import { getLangText } from '../../../../../../utils/lang_utils'; let SubmitToPrizeButton = React.createClass({ propTypes: { diff --git a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js b/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js similarity index 90% rename from js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js rename to js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js index f2e22412..25354dc1 100644 --- a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/ascribe_detail/prize_piece_container.js @@ -6,41 +6,41 @@ import Moment from 'moment'; import StarRating from 'react-star-rating'; -import PieceActions from '../../../../../actions/piece_actions'; -import PieceStore from '../../../../../stores/piece_store'; +import PieceActions from '../../../../../../actions/piece_actions'; +import PieceStore from '../../../../../../stores/piece_store'; -import PieceListStore from '../../../../../stores/piece_list_store'; -import PieceListActions from '../../../../../actions/piece_list_actions'; +import PieceListStore from '../../../../../../stores/piece_list_store'; +import PieceListActions from '../../../../../../actions/piece_list_actions'; import PrizeRatingActions from '../../actions/prize_rating_actions'; import PrizeRatingStore from '../../stores/prize_rating_store'; -import UserStore from '../../../../../stores/user_store'; +import UserStore from '../../../../../../stores/user_store'; -import Piece from '../../../../../components/ascribe_detail/piece'; -import Note from '../../../../../components/ascribe_detail/note'; +import Piece from '../../../../../../components/ascribe_detail/piece'; +import Note from '../../../../../../components/ascribe_detail/note'; -import AscribeSpinner from '../../../../ascribe_spinner'; +import AscribeSpinner from '../../../../../ascribe_spinner'; -import Form from '../../../../../components/ascribe_forms/form'; -import Property from '../../../../../components/ascribe_forms/property'; -import InputTextAreaToggable from '../../../../../components/ascribe_forms/input_textarea_toggable'; -import CollapsibleParagraph from '../../../../../components/ascribe_collapsible/collapsible_paragraph'; +import Form from '../../../../../../components/ascribe_forms/form'; +import Property from '../../../../../../components/ascribe_forms/property'; +import InputTextAreaToggable from '../../../../../../components/ascribe_forms/input_textarea_toggable'; +import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph'; -import InputCheckbox from '../../../../ascribe_forms/input_checkbox'; -import LoanForm from '../../../../ascribe_forms/form_loan'; -import ListRequestActions from '../../../../ascribe_forms/list_form_request_actions'; -import ModalWrapper from '../../../../ascribe_modal/modal_wrapper'; +import InputCheckbox from '../../../../../ascribe_forms/input_checkbox'; +import LoanForm from '../../../../../ascribe_forms/form_loan'; +import ListRequestActions from '../../../../../ascribe_forms/list_form_request_actions'; +import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper'; -import GlobalNotificationModel from '../../../../../models/global_notification_model'; -import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; +import GlobalNotificationModel from '../../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../../actions/global_notification_actions'; -import DetailProperty from '../../../../ascribe_detail/detail_property'; +import DetailProperty from '../../../../../ascribe_detail/detail_property'; -import ApiUrls from '../../../../../constants/api_urls'; -import { mergeOptions } from '../../../../../utils/general_utils'; -import { getLangText } from '../../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../../utils/dom_utils'; +import ApiUrls from '../../../../../../constants/api_urls'; +import { mergeOptions } from '../../../../../../utils/general_utils'; +import { getLangText } from '../../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../../utils/dom_utils'; /** diff --git a/js/components/whitelabel/prize/components/prize_hero.js b/js/components/whitelabel/prize/simple_prize/components/prize_hero.js similarity index 83% rename from js/components/whitelabel/prize/components/prize_hero.js rename to js/components/whitelabel/prize/simple_prize/components/prize_hero.js index b98f407e..8842acf9 100644 --- a/js/components/whitelabel/prize/components/prize_hero.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_hero.js @@ -1,7 +1,7 @@ 'use strict'; import React from 'react'; -import constants from '../../../../constants/application_constants'; +import constants from '../../../../../constants/application_constants'; let Hero = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_landing.js b/js/components/whitelabel/prize/simple_prize/components/prize_landing.js similarity index 93% rename from js/components/whitelabel/prize/components/prize_landing.js rename to js/components/whitelabel/prize/simple_prize/components/prize_landing.js index 355b3786..e26a05b5 100644 --- a/js/components/whitelabel/prize/components/prize_landing.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_landing.js @@ -11,11 +11,11 @@ import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; -import UserStore from '../../../../stores/user_store'; -import UserActions from '../../../../actions/user_actions'; +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; -import { mergeOptions } from '../../../../utils/general_utils'; -import { getLangText } from '../../../../utils/lang_utils'; +import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; let Landing = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_login_container.js b/js/components/whitelabel/prize/simple_prize/components/prize_login_container.js similarity index 83% rename from js/components/whitelabel/prize/components/prize_login_container.js rename to js/components/whitelabel/prize/simple_prize/components/prize_login_container.js index 9a0de06d..e168ca68 100644 --- a/js/components/whitelabel/prize/components/prize_login_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_login_container.js @@ -3,10 +3,10 @@ import React from 'react'; import { Link } from 'react-router'; -import LoginForm from '../../../ascribe_forms/form_login'; +import LoginForm from '../../../../ascribe_forms/form_login'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let LoginContainer = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_piece_list.js b/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js similarity index 86% rename from js/components/whitelabel/prize/components/prize_piece_list.js rename to js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js index 7a6a90ac..8e602012 100644 --- a/js/components/whitelabel/prize/components/prize_piece_list.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js @@ -1,10 +1,10 @@ 'use strict'; import React from 'react'; -import PieceList from '../../../piece_list'; +import PieceList from '../../../../piece_list'; -import UserActions from '../../../../actions/user_actions'; -import UserStore from '../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; +import UserStore from '../../../../../stores/user_store'; import PrizeActions from '../actions/prize_actions'; import PrizeStore from '../stores/prize_store'; @@ -15,9 +15,9 @@ import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import AccordionListItemPrize from './ascribe_accordion_list/accordion_list_item_prize'; -import { mergeOptions } from '../../../../utils/general_utils'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let PrizePieceList = React.createClass({ propTypes: { diff --git a/js/components/whitelabel/prize/components/prize_register_piece.js b/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js similarity index 87% rename from js/components/whitelabel/prize/components/prize_register_piece.js rename to js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js index 87a23591..a35893ed 100644 --- a/js/components/whitelabel/prize/components/prize_register_piece.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js @@ -5,13 +5,13 @@ import React from 'react'; import PrizeActions from '../actions/prize_actions'; import PrizeStore from '../stores/prize_store'; -import RegisterPiece from '../../../register_piece'; -import Property from '../../../ascribe_forms/property'; -import InputTextAreaToggable from '../../../ascribe_forms/input_textarea_toggable'; -import InputCheckbox from '../../../ascribe_forms/input_checkbox'; +import RegisterPiece from '../../../../register_piece'; +import Property from '../../../../ascribe_forms/property'; +import InputTextAreaToggable from '../../../../ascribe_forms/input_textarea_toggable'; +import InputCheckbox from '../../../../ascribe_forms/input_checkbox'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let PrizeRegisterPiece = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_settings_container.js b/js/components/whitelabel/prize/simple_prize/components/prize_settings_container.js similarity index 91% rename from js/components/whitelabel/prize/components/prize_settings_container.js rename to js/components/whitelabel/prize/simple_prize/components/prize_settings_container.js index 81d62380..145a9d24 100644 --- a/js/components/whitelabel/prize/components/prize_settings_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_settings_container.js @@ -2,29 +2,29 @@ import React from 'react'; -import UserStore from '../../../../stores/user_store'; -import UserActions from '../../../../actions/user_actions'; +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; import PrizeActions from '../actions/prize_actions'; import PrizeStore from '../stores/prize_store'; import PrizeJuryActions from '../actions/prize_jury_actions'; import PrizeJuryStore from '../stores/prize_jury_store'; -import SettingsContainer from '../../../ascribe_settings/settings_container'; -import CollapsibleParagraph from '../../../ascribe_collapsible/collapsible_paragraph'; +import SettingsContainer from '../../../../ascribe_settings/settings_container'; +import CollapsibleParagraph from '../../../../ascribe_collapsible/collapsible_paragraph'; -import Form from '../../../ascribe_forms/form'; -import Property from '../../../ascribe_forms/property'; +import Form from '../../../../ascribe_forms/form'; +import Property from '../../../../ascribe_forms/property'; -import ActionPanel from '../../../ascribe_panel/action_panel'; +import ActionPanel from '../../../../ascribe_panel/action_panel'; -import GlobalNotificationModel from '../../../../models/global_notification_model'; -import GlobalNotificationActions from '../../../../actions/global_notification_actions'; +import GlobalNotificationModel from '../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; -import AscribeSpinner from '../../../ascribe_spinner'; -import ApiUrls from '../../../../constants/api_urls'; +import AscribeSpinner from '../../../../ascribe_spinner'; +import ApiUrls from '../../../../../constants/api_urls'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let Settings = React.createClass({ diff --git a/js/components/whitelabel/prize/components/prize_signup_container.js b/js/components/whitelabel/prize/simple_prize/components/prize_signup_container.js similarity index 86% rename from js/components/whitelabel/prize/components/prize_signup_container.js rename to js/components/whitelabel/prize/simple_prize/components/prize_signup_container.js index 884062da..7a44d521 100644 --- a/js/components/whitelabel/prize/components/prize_signup_container.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_signup_container.js @@ -1,10 +1,10 @@ 'use strict'; import React from 'react'; -import SignupForm from '../../../ascribe_forms/form_signup'; +import SignupForm from '../../../../ascribe_forms/form_signup'; -import { getLangText } from '../../../../utils/lang_utils'; -import { setDocumentTitle } from '../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; let SignupContainer = React.createClass({ propTypes: { diff --git a/js/components/whitelabel/prize/fetchers/prize_fetcher.js b/js/components/whitelabel/prize/simple_prize/fetchers/prize_fetcher.js similarity index 70% rename from js/components/whitelabel/prize/fetchers/prize_fetcher.js rename to js/components/whitelabel/prize/simple_prize/fetchers/prize_fetcher.js index 0bf9fc55..410d63a2 100644 --- a/js/components/whitelabel/prize/fetchers/prize_fetcher.js +++ b/js/components/whitelabel/prize/simple_prize/fetchers/prize_fetcher.js @@ -1,6 +1,6 @@ 'use strict'; -import requests from '../../../../utils/requests'; +import requests from '../../../../../utils/requests'; let PrizeFetcher = { diff --git a/js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js b/js/components/whitelabel/prize/simple_prize/fetchers/prize_jury_fetcher.js similarity index 88% rename from js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js rename to js/components/whitelabel/prize/simple_prize/fetchers/prize_jury_fetcher.js index 1c5b0a0d..973107b4 100644 --- a/js/components/whitelabel/prize/fetchers/prize_jury_fetcher.js +++ b/js/components/whitelabel/prize/simple_prize/fetchers/prize_jury_fetcher.js @@ -1,6 +1,6 @@ 'use strict'; -import requests from '../../../../utils/requests'; +import requests from '../../../../../utils/requests'; let PrizeJuryFetcher = { diff --git a/js/components/whitelabel/prize/fetchers/prize_rating_fetcher.js b/js/components/whitelabel/prize/simple_prize/fetchers/prize_rating_fetcher.js similarity index 90% rename from js/components/whitelabel/prize/fetchers/prize_rating_fetcher.js rename to js/components/whitelabel/prize/simple_prize/fetchers/prize_rating_fetcher.js index 33450dd6..38d0576e 100644 --- a/js/components/whitelabel/prize/fetchers/prize_rating_fetcher.js +++ b/js/components/whitelabel/prize/simple_prize/fetchers/prize_rating_fetcher.js @@ -1,6 +1,6 @@ 'use strict'; -import requests from '../../../../utils/requests'; +import requests from '../../../../../utils/requests'; let PrizeRatingFetcher = { diff --git a/js/components/whitelabel/prize/prize_app.js b/js/components/whitelabel/prize/simple_prize/prize_app.js similarity index 87% rename from js/components/whitelabel/prize/prize_app.js rename to js/components/whitelabel/prize/simple_prize/prize_app.js index aadb0b05..d95d7772 100644 --- a/js/components/whitelabel/prize/prize_app.js +++ b/js/components/whitelabel/prize/simple_prize/prize_app.js @@ -2,11 +2,11 @@ import React from 'react'; import Hero from './components/prize_hero'; -import Header from '../../header'; -import Footer from '../../footer'; -import GlobalNotification from '../../global_notification'; +import Header from '../../../header'; +import Footer from '../../../footer'; +import GlobalNotification from '../../../global_notification'; -import { getSubdomain } from '../../../utils/general_utils'; +import { getSubdomain } from '../../../../utils/general_utils'; let PrizeApp = React.createClass({ diff --git a/js/components/whitelabel/prize/stores/prize_jury_store.js b/js/components/whitelabel/prize/simple_prize/stores/prize_jury_store.js similarity index 96% rename from js/components/whitelabel/prize/stores/prize_jury_store.js rename to js/components/whitelabel/prize/simple_prize/stores/prize_jury_store.js index 69d73e3a..536b8633 100644 --- a/js/components/whitelabel/prize/stores/prize_jury_store.js +++ b/js/components/whitelabel/prize/simple_prize/stores/prize_jury_store.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import PrizeJuryActions from '../actions/prize_jury_actions'; diff --git a/js/components/whitelabel/prize/stores/prize_rating_store.js b/js/components/whitelabel/prize/simple_prize/stores/prize_rating_store.js similarity index 93% rename from js/components/whitelabel/prize/stores/prize_rating_store.js rename to js/components/whitelabel/prize/simple_prize/stores/prize_rating_store.js index d67fa603..9f1552bb 100644 --- a/js/components/whitelabel/prize/stores/prize_rating_store.js +++ b/js/components/whitelabel/prize/simple_prize/stores/prize_rating_store.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import PrizeRatingActions from '../actions/prize_rating_actions'; diff --git a/js/components/whitelabel/prize/stores/prize_store.js b/js/components/whitelabel/prize/simple_prize/stores/prize_store.js similarity index 87% rename from js/components/whitelabel/prize/stores/prize_store.js rename to js/components/whitelabel/prize/simple_prize/stores/prize_store.js index 68cc9264..8d9c4bbe 100644 --- a/js/components/whitelabel/prize/stores/prize_store.js +++ b/js/components/whitelabel/prize/simple_prize/stores/prize_store.js @@ -1,6 +1,6 @@ 'use strict'; -import { alt } from '../../../../alt'; +import { alt } from '../../../../../alt'; import PrizeActions from '../actions/prize_actions'; From f5a5e045a6b1d3e63a2242408e146102ac97d079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Fri, 6 Nov 2015 12:09:31 +0100 Subject: [PATCH 28/58] Finalize boilerplate for portfolioreview subdomain --- README.md | 2 + .../portfolioreview/components/pr_landing.js | 117 ++++++++++++++++++ .../prize/portfolioreview/pr_app.js | 35 ++++++ .../whitelabel/prize/prize_routes.js | 27 +++- js/constants/application_constants.js | 7 ++ 5 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 js/components/whitelabel/prize/portfolioreview/components/pr_landing.js create mode 100644 js/components/whitelabel/prize/portfolioreview/pr_app.js diff --git a/README.md b/README.md index 1dc4492b..16fba17d 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ Additionally, to work on the white labeling functionality, you need to edit your 127.0.0.1 cyland.localhost.com 127.0.0.1 ikonotv.localhost.com 127.0.0.1 sluice.localhost.com +127.0.0.1 lumenus.localhost.com +127.0.0.1 portfolioreview.localhost.com ``` diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_landing.js b/js/components/whitelabel/prize/portfolioreview/components/pr_landing.js new file mode 100644 index 00000000..e88efff2 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_landing.js @@ -0,0 +1,117 @@ +'use strict'; + +import React from 'react'; +import { History } from 'react-router'; + +import PrizeActions from '../../simple_prize/actions/prize_actions'; +import PrizeStore from '../../simple_prize/stores/prize_store'; + +import Button from 'react-bootstrap/lib/Button'; +import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; + +import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; + +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; + +import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; + +const PRLanding = React.createClass({ + + mixins: [History], + + getInitialState() { + return mergeOptions( + PrizeStore.getState(), + UserStore.getState() + ); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + PrizeStore.listen(this.onChange); + PrizeActions.fetchPrize(); + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + PrizeStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + getButtons() { + if (this.state.prize && this.state.prize.active){ + return ( + + + + + +

+ {getLangText('or, already an ascribe user?')} +

+ + + +
+ ); + } + return ( + + + {getLangText('Sign up to ascribe')} + + +

+ {getLangText('or, already an ascribe user?')} +

+ + + +
+ ); + }, + + getTitle() { + if (this.state.prize && this.state.prize.active){ + return ( +

+ {getLangText('This is the submission page for Portfolio Review 2015.')} +

+ ); + } + return ( +

+ {getLangText('Submissions for Portfolio Review 2015 are now closed.')} +

+ ); + }, + render() { + return ( +
+
+
+

+ {getLangText('Welcome to Portfolio Review 2015')} +

+ {this.getTitle()} + {this.getButtons()} +
+
+
+ ); + } +}); + +export default PRLanding; \ No newline at end of file diff --git a/js/components/whitelabel/prize/portfolioreview/pr_app.js b/js/components/whitelabel/prize/portfolioreview/pr_app.js new file mode 100644 index 00000000..072542f9 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/pr_app.js @@ -0,0 +1,35 @@ +'use strict'; + +import React from 'react'; +import Footer from '../../../footer'; +import GlobalNotification from '../../../global_notification'; + +import { getSubdomain } from '../../../../utils/general_utils'; + + +let PrizeApp = React.createClass({ + propTypes: { + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.element), + React.PropTypes.element + ]), + history: React.PropTypes.object, + routes: React.PropTypes.arrayOf(React.PropTypes.object) + }, + + render() { + const { children } = this.props; + let subdomain = getSubdomain(); + + return ( +
+ {children} + + +
+
+ ); + } +}); + +export default PrizeApp; diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index a9004927..9396a354 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -12,6 +12,9 @@ import SPPieceContainer from './simple_prize/components/ascribe_detail/prize_pie import SPSettingsContainer from './simple_prize/components/prize_settings_container'; import SPApp from './simple_prize/prize_app'; +import PRApp from './portfolioreview/pr_app'; +import PRLanding from './portfolioreview/components/pr_landing'; + import EditionContainer from '../../ascribe_detail/edition_container'; import LogoutContainer from '../../logout_container'; import PasswordResetContainer from '../../password_reset_container'; @@ -22,7 +25,7 @@ import AuthProxyHandler from '../../../components/ascribe_routes/proxy_routes/au const ROUTES = { - 'sluice': ( + sluice: ( + ), + portfolioreview: ( + + + + + + + + + ) }; diff --git a/js/constants/application_constants.js b/js/constants/application_constants.js index 0fe5e210..b175c158 100644 --- a/js/constants/application_constants.js +++ b/js/constants/application_constants.js @@ -46,6 +46,13 @@ let constants = { 'logo': 'https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/ikonotv/ikono-logo-black.png', 'permissions': ['register', 'edit', 'share', 'del_from_collection'], 'type': 'wallet' + }, + { + 'subdomain': 'portfolioreview', + 'name': 'Portfolio Review', + 'logo': 'http://notfoundlogo.de', + 'permissions': ['register', 'edit', 'share', 'del_from_collection'], + 'type': 'prize' } ], 'defaultDomain': { From b78bc66fe9e3e95ee8b67e52b4ce4101c1cbf2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Fri, 6 Nov 2015 15:47:55 +0100 Subject: [PATCH 29/58] Fix minor bugs for simple_prize app --- c | 12 +++ js/components/register_piece.js | 6 -- .../whitelabel/prize/prize_routes.js | 6 +- .../simple_prize/actions/prize_actions.js | 23 ++--- .../components/prize_register_piece.js | 84 ++++++++++--------- 5 files changed, 70 insertions(+), 61 deletions(-) create mode 100644 c diff --git a/c b/c new file mode 100644 index 00000000..d3923303 --- /dev/null +++ b/c @@ -0,0 +1,12 @@ +commit 22fd8656e9e93a5d8d2a412dbed33fce0db57af8 +Author: Tim Daubenschütz +Date: Fri Nov 6 12:09:31 2015 +0100 + + Finalize boilerplate for portfolioreview subdomain + +commit d1dd33c094194bb6271e2ae11817837b4c39caa5 +Author: Tim Daubenschütz +Date: Thu Nov 5 16:45:12 2015 +0100 + + Move and rename sluice prize app to 'simple_prize' + diff --git a/js/components/register_piece.js b/js/components/register_piece.js index f127c149..43ac7bb7 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -40,12 +40,6 @@ let RegisterPiece = React.createClass( { mixins: [History], - getDefaultProps() { - return { - canSpecifyEditions: true - }; - }, - getInitialState(){ return mergeOptions( UserStore.getState(), diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index 9396a354..343de03a 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -67,16 +67,16 @@ const ROUTES = { headerTitle='+ NEW WORK'/> + component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPLoginContainer)} /> + component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPSignupContainer)} /> + component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(PasswordResetContainer)} /> ) diff --git a/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js b/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js index 27127035..dbca1b5d 100644 --- a/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js @@ -1,7 +1,6 @@ 'use strict'; import { alt } from '../../../../../alt'; -import Q from 'q'; import PrizeFetcher from '../fetchers/prize_fetcher'; @@ -13,20 +12,16 @@ class PrizeActions { } fetchPrize() { - return Q.Promise((resolve, reject) => { - PrizeFetcher - .fetch() - .then((res) => { - this.actions.updatePrize({ - prize: res.prize - }); - resolve(res); - }) - .catch((err) => { - console.logGlobal(err); - reject(err); + PrizeFetcher + .fetch() + .then((res) => { + this.actions.updatePrize({ + prize: res.prize }); - }); + }) + .catch((err) => { + console.logGlobal(err); + }); } } diff --git a/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js b/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js index a35893ed..424a2fdd 100644 --- a/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_register_piece.js @@ -15,6 +15,10 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils'; let PrizeRegisterPiece = React.createClass({ + propTypes: { + location: React.PropTypes.object + }, + getInitialState() { return PrizeStore.getState(); }, @@ -33,48 +37,52 @@ let PrizeRegisterPiece = React.createClass({ }, render() { + const { location } = this.props; + setDocumentTitle(getLangText('Submit to the prize')); if(this.state.prize && this.state.prize.active){ return ( - - - - - - - - - - - {' ' + getLangText('I agree to the Terms of Service the art price') + ' '} - ( - {getLangText('read')} - ) - - - - ); + + + + + + + + + + + {' ' + getLangText('I agree to the Terms of Service the art price') + ' '} + ( + {getLangText('read')} + ) + + + + + ); } else { return ( From 7721d623192368f1a89c54c11f90a8c30242466e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Fri, 6 Nov 2015 16:52:08 +0100 Subject: [PATCH 30/58] Create boilerplate code for portfolioreview --- .../pr_forms/pr_register_piece_form.js | 85 +++++++++++++++++++ .../components/pr_register_piece.js | 34 ++++++++ .../whitelabel/prize/prize_routes.js | 3 +- .../components/prize_register_piece.js | 80 ++++++++--------- sass/whitelabel/prize/index.scss | 3 +- .../portfolioreview_custom_style.scss | 33 +++++++ .../simple_prize_custom_style.scss} | 0 7 files changed, 197 insertions(+), 41 deletions(-) create mode 100644 js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js create mode 100644 js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js create mode 100644 sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss rename sass/whitelabel/prize/{sluice/sluice_custom_style.scss => simple_prize/simple_prize_custom_style.scss} (100%) diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js b/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js new file mode 100644 index 00000000..df42b800 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js @@ -0,0 +1,85 @@ +'use strict'; + +import React from 'react'; + +import Form from '../../../../../ascribe_forms/form'; +import Property from '../../../../../ascribe_forms/property'; +import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable'; + +import { getLangText } from '../../../../../../utils/lang_utils'; + + +const PRRegisterPieceForm = React.createClass({ + getInitialState(){ + return { + isUploadReady: false + }; + }, + + handleSuccess() { + + }, + + render() { + return ( +
+
+ + + + + + + + + +
+
+ + + + + + + + + +
+
+ ); + } +}); + +export default PRRegisterPieceForm; \ No newline at end of file diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js new file mode 100644 index 00000000..ab705b61 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js @@ -0,0 +1,34 @@ +'use strict'; + +import React from 'react'; + +import Col from 'react-bootstrap/lib/Col'; +import Row from 'react-bootstrap/lib/Row'; + +import PRRegisterPieceForm from './pr_forms/pr_register_piece_form'; + +import { getLangText } from '../../../../../utils/lang_utils'; + + +const PRRegisterPiece = React.createClass({ + render() { + return ( + + +
+

Portfolio Review

+

{getLangText('Submission closing on %s', ' 21 Dec 2015')}

+

{getLangText('Submissions are open to everyone, we accept only PDFs.')}

+

{getLangText('We accept only one PDF with up to 20 images from every participant.')}

+

{getLangText('You need to pay 50€ in order to apply. We only accept PayPal.')}

+
+ + + + +
+ ); + } +}); + +export default PRRegisterPiece; \ No newline at end of file diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index 343de03a..7c4b40a3 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -14,6 +14,7 @@ import SPApp from './simple_prize/prize_app'; import PRApp from './portfolioreview/pr_app'; import PRLanding from './portfolioreview/components/pr_landing'; +import PRRegisterPiece from './portfolioreview/components/pr_register_piece'; import EditionContainer from '../../ascribe_detail/edition_container'; import LogoutContainer from '../../logout_container'; @@ -63,7 +64,7 @@ const ROUTES = { - - - - - - - - - - {' ' + getLangText('I agree to the Terms of Service the art price') + ' '} - ( - {getLangText('read')} - ) - - - - +
+ + + + + + + + + + + {' ' + getLangText('I agree to the Terms of Service the art price') + ' '} + ( + {getLangText('read')} + ) + + + + +
); } else { diff --git a/sass/whitelabel/prize/index.scss b/sass/whitelabel/prize/index.scss index 5bc75746..24645c8a 100644 --- a/sass/whitelabel/prize/index.scss +++ b/sass/whitelabel/prize/index.scss @@ -1,4 +1,5 @@ -@import 'sluice/sluice_custom_style'; +@import 'simple_prize/simple_prize_custom_style'; +@import 'portfolioreview/portfolioreview_custom_style'; .ascribe-prize-app { border-radius: 0; diff --git a/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss b/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss new file mode 100644 index 00000000..a0eb64ec --- /dev/null +++ b/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss @@ -0,0 +1,33 @@ +.client--portfolioreview { + padding-top: 20px !important; + + .register-piece--hero { + text-align: center; + + h1, h2 { + font-variant: small-caps; + } + + h1 { + font-size: 5em; + color: #757575; + } + + h2 { + font-size: 1.25em; + } + + p { + margin-bottom: 0; + } + + p + p { + margin-top: 0; + } + + p:last-child { + margin-bottom: 1.5em; + } + + } +} \ No newline at end of file diff --git a/sass/whitelabel/prize/sluice/sluice_custom_style.scss b/sass/whitelabel/prize/simple_prize/simple_prize_custom_style.scss similarity index 100% rename from sass/whitelabel/prize/sluice/sluice_custom_style.scss rename to sass/whitelabel/prize/simple_prize/simple_prize_custom_style.scss From 3e22ad1d9d3282d4a680b662d1a33dfbde2e2117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 9 Nov 2015 10:35:23 +0100 Subject: [PATCH 31/58] Finalize layouting submission form --- .../ascribe_buttons/upload_file_button.js | 74 ++++++++++++ .../contract_settings_update_button.js | 1 - .../pr_forms/pr_register_piece_form.js | 105 +++++++++++++++++- .../components/pr_register_piece.js | 17 ++- sass/ascribe_form.scss | 2 +- sass/ascribe_property.scss | 15 +++ .../portfolioreview_custom_style.scss | 14 +++ 7 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 js/components/ascribe_buttons/upload_file_button.js diff --git a/js/components/ascribe_buttons/upload_file_button.js b/js/components/ascribe_buttons/upload_file_button.js new file mode 100644 index 00000000..7f76f2ef --- /dev/null +++ b/js/components/ascribe_buttons/upload_file_button.js @@ -0,0 +1,74 @@ +'use strict'; + +import React from 'react'; + +import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader'; +import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button'; + +import AppConstants from '../../constants/application_constants'; +import ApiUrls from '../../constants/api_urls'; + +import { getCookie } from '../../utils/fetch_api_utils'; +import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; + + +const { shape, arrayOf, string, object, number, oneOfType } = React.PropTypes; + +const UploadFileButton = React.createClass({ + propTypes: { + keyRoutine: shape({ + url: string, + fileClass: string + }).isRequired, + validation: shape({ + itemLimit: number, + sizeLimit: oneOfType([string, number]), + allowedExtensions: arrayOf(string) + }), + location: object, + fileClassToUpload: shape({ + singular: string, + plural: string + }).isRequired + }, + + submitFile(file) { + console.log(file); + }, + + render() { + + const { fileClassToUpload, validation, keyRoutine } = this.props; + + return ( + {/* So that ReactS3FineUploader is not complaining */}} + signature={{ + endpoint: AppConstants.serverUrl + 's3/signature/', + customHeaders: { + 'X-CSRFToken': getCookie(AppConstants.csrftoken) + } + }} + deleteFile={{ + enabled: true, + method: 'DELETE', + endpoint: AppConstants.serverUrl + 's3/delete', + customHeaders: { + 'X-CSRFToken': getCookie(AppConstants.csrftoken) + } + }} + fileClassToUpload={fileClassToUpload} + isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} + submitFile={this.submitFile} + location={this.props.location}/> + ); + } +}); + +export default UploadFileButton; \ No newline at end of file diff --git a/js/components/ascribe_settings/contract_settings_update_button.js b/js/components/ascribe_settings/contract_settings_update_button.js index f3bab156..265e6548 100644 --- a/js/components/ascribe_settings/contract_settings_update_button.js +++ b/js/components/ascribe_settings/contract_settings_update_button.js @@ -56,7 +56,6 @@ let ContractSettingsUpdateButton = React.createClass({ render() { return ( +
@@ -77,6 +90,96 @@ const PRRegisterPieceForm = React.createClass({ placeholder={getLangText('THIS NEEDS TEXT')}/>
+
+ {getLangText('Select the PDF with your work')} + +
+
+ {getLangText('Featured Cover photo')} + +
+
+ {getLangText('Supporting Materials (Optional)')} + +
+
+ {getLangText('Proof of payment')} + +
+
+ + +  {getLangText('I agree to the Terms and Conditions of the Portfolio Review')} + + +
+
); } diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js index ab705b61..5d1fd728 100644 --- a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js @@ -8,13 +8,23 @@ import Row from 'react-bootstrap/lib/Row'; import PRRegisterPieceForm from './pr_forms/pr_register_piece_form'; import { getLangText } from '../../../../../utils/lang_utils'; +import { setDocumentTitle } from '../../../../../utils/dom_utils'; +const { object } = React.PropTypes; + const PRRegisterPiece = React.createClass({ + propTypes: { + location: object + }, + render() { + const { location } = this.props; + + setDocumentTitle(getLangText('Submission form')); return ( - +

Portfolio Review

{getLangText('Submission closing on %s', ' 21 Dec 2015')}

@@ -23,8 +33,9 @@ const PRRegisterPiece = React.createClass({

{getLangText('You need to pay 50€ in order to apply. We only accept PayPal.')}

- - + +
); diff --git a/sass/ascribe_form.scss b/sass/ascribe_form.scss index 41e17de1..1b265e91 100644 --- a/sass/ascribe_form.scss +++ b/sass/ascribe_form.scss @@ -23,4 +23,4 @@ @media (max-width: 550px) { width: 100%; } -} +} \ No newline at end of file diff --git a/sass/ascribe_property.scss b/sass/ascribe_property.scss index e938a7c3..cb7662e8 100644 --- a/sass/ascribe_property.scss +++ b/sass/ascribe_property.scss @@ -219,3 +219,18 @@ $ascribe-red-error: rgb(169, 68, 66); margin-top: 0; } } + +.input-upload-file-button-property { + background-color: white; + padding: 1.5em 0 1.5em 0; + text-align: right; + + button { + font-size: 1em; + margin-right: 1em; + } + + span + button { + margin-left: 1em; + } +} \ No newline at end of file diff --git a/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss b/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss index a0eb64ec..6c0d5d66 100644 --- a/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss +++ b/sass/whitelabel/prize/portfolioreview/portfolioreview_custom_style.scss @@ -28,6 +28,20 @@ p:last-child { margin-bottom: 1.5em; } + } + .register-piece--form { + + form { + border-top: none; + } + + form:first-child { + border-bottom: none; + } + + form + form, form:last-child { + border-bottom: 1px solid rgba(0, 0, 0, .05); + } } } \ No newline at end of file From deceb61c60778781fce3aa96bd1bb96cf9e83e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 9 Nov 2015 16:58:35 +0100 Subject: [PATCH 32/58] Implement lazy blob creation routine and increase robustness of upload button template for react-fineuploader --- .../ascribe_buttons/upload_file_button.js | 39 +++-- .../ascribe_upload_button/upload_button.js | 24 ++- .../react_s3_fine_uploader.js | 155 ++++++++++-------- 3 files changed, 135 insertions(+), 83 deletions(-) diff --git a/js/components/ascribe_buttons/upload_file_button.js b/js/components/ascribe_buttons/upload_file_button.js index 7f76f2ef..1f576064 100644 --- a/js/components/ascribe_buttons/upload_file_button.js +++ b/js/components/ascribe_buttons/upload_file_button.js @@ -2,6 +2,8 @@ import React from 'react'; +import Glyphicon from 'react-bootstrap/lib/Glyphicon'; + import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader'; import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button'; @@ -29,24 +31,41 @@ const UploadFileButton = React.createClass({ fileClassToUpload: shape({ singular: string, plural: string - }).isRequired + }).isRequired, + createBlobRoutine: shape({ + url: string, + pieceId: number + }) }, - submitFile(file) { - console.log(file); + getInitialState() { + return { + file: null + }; + }, + + handleSubmitFile(file) { + this.setState({ + file + }); + }, + + createBlobRoutine() { + const { fineuploader } = this.refs; + const { file } = this.state; + + fineuploader.createBlob(file); }, render() { - - const { fileClassToUpload, validation, keyRoutine } = this.props; + const { fileClassToUpload, validation, keyRoutine, createBlobRoutine } = this.props; return ( {/* So that ReactS3FineUploader is not complaining */}} signature={{ @@ -65,9 +84,9 @@ const UploadFileButton = React.createClass({ }} fileClassToUpload={fileClassToUpload} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} - submitFile={this.submitFile} + submitFile={this.handleSubmitFile} location={this.props.location}/> - ); + ); } }); diff --git a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js index 1547272e..5b08636c 100644 --- a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js +++ b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js @@ -2,8 +2,11 @@ import React from 'react'; +import Glyphicon from 'react-bootstrap/lib/Glyphicon'; + import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils'; import { getLangText } from '../../../utils/lang_utils'; +import { truncateTextAtCharIndex } from '../../../utils/general_utils'; let UploadButton = React.createClass({ @@ -37,6 +40,10 @@ let UploadButton = React.createClass({ return this.props.filesToUpload.filter((file) => file.status === 'uploading'); }, + getUploadedFile() { + return this.props.filesToUpload.filter((file) => file.status === 'upload successful')[0]; + }, + handleOnClick() { let uploadingFiles = this.getUploadingFiles(); @@ -57,14 +64,22 @@ let UploadButton = React.createClass({ }, getButtonLabel() { + const uploadedFile = this.getUploadedFile(); let { filesToUpload, fileClassToUpload } = this.props; // filter invalid files that might have been deleted or canceled... filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter); - // Depending on wether there is an upload going on or not we - // display the progress - if(filesToUpload.length > 0) { + // Depending on whether there is an upload going on or not we + // display the progress or the successfully uploaded file's name + if(uploadedFile) { + return ( + + + {' ' + truncateTextAtCharIndex(uploadedFile.name, 20)} + + ); + } else if(filesToUpload.length > 0) { return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%'; } else { return fileClassToUpload.singular; @@ -74,7 +89,6 @@ let UploadButton = React.createClass({ render() { let { multiple, - fileClassToUpload, allowedExtensions } = this.props; @@ -82,7 +96,7 @@ let UploadButton = React.createClass({ diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js index 5d1fd728..2d141866 100644 --- a/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_register_piece.js @@ -1,14 +1,19 @@ 'use strict'; import React from 'react'; +import { Link, History } from 'react-router'; import Col from 'react-bootstrap/lib/Col'; import Row from 'react-bootstrap/lib/Row'; +import UserStore from '../../../../../stores/user_store'; +import UserActions from '../../../../../actions/user_actions'; + import PRRegisterPieceForm from './pr_forms/pr_register_piece_form'; import { getLangText } from '../../../../../utils/lang_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils'; +import { getCookie } from '../../../../../utils/fetch_api_utils'; const { object } = React.PropTypes; @@ -18,7 +23,37 @@ const PRRegisterPiece = React.createClass({ location: object }, + mixins: [History], + + getInitialState() { + return UserStore.getState(); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + }, + + componentDidUpdate() { + const { currentUser } = this.state; + if(currentUser && currentUser.email) { + const submittedPieceId = getCookie(currentUser.email); + if(submittedPieceId) { + this.history.pushState(null, `/pieces/${submittedPieceId}`); + } + } + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + render() { + const { currentUser } = this.state; const { location } = this.props; setDocumentTitle(getLangText('Submission form')); @@ -28,14 +63,25 @@ const PRRegisterPiece = React.createClass({

Portfolio Review

{getLangText('Submission closing on %s', ' 21 Dec 2015')}

-

{getLangText('Submissions are open to everyone, we accept only PDFs.')}

-

{getLangText('We accept only one PDF with up to 20 images from every participant.')}

-

{getLangText('You need to pay 50€ in order to apply. We only accept PayPal.')}

+

+ {getLangText('Submissions are open to everyone, we accept only PDFs.')} +

+

+ {getLangText('We accept only one PDF with up to 20 images from every participant.')} +

+

+ {getLangText('You need to pay 50€ in order to apply. We only accept PayPal.')} +

+

+ {getLangText("You're submitting as %s. ", currentUser.email)} + {getLangText('Change account?')} +

+ location={location} + currentUser={currentUser}/> ); diff --git a/js/components/whitelabel/prize/portfolioreview/pr_app.js b/js/components/whitelabel/prize/portfolioreview/pr_app.js index 072542f9..22a837be 100644 --- a/js/components/whitelabel/prize/portfolioreview/pr_app.js +++ b/js/components/whitelabel/prize/portfolioreview/pr_app.js @@ -1,7 +1,6 @@ 'use strict'; import React from 'react'; -import Footer from '../../../footer'; import GlobalNotification from '../../../global_notification'; import { getSubdomain } from '../../../../utils/general_utils'; @@ -26,7 +25,6 @@ let PrizeApp = React.createClass({ {children} -