From d1dba86b1ae5d3e2c369cd84ad763a4bb38c22ae Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Mon, 6 Jun 2016 18:52:05 +0200 Subject: [PATCH] Convert currentUser to be passed down through context and withCurrentUser HOC Similar to what react-router did with their router and withRouter HOC. --- js/components/app_base.js | 16 +++++-- js/components/ascribe_app.js | 13 ++---- .../ascribe_buttons/acl_button_list.js | 3 -- .../ascribe_buttons/acls/acl_button.js | 3 -- .../unconsign_request_button.js | 20 +++++---- js/components/ascribe_detail/edition.js | 30 ++++++------- .../ascribe_detail/edition_action_panel.js | 5 --- .../ascribe_detail/edition_container.js | 4 +- .../ascribe_detail/media_container.js | 13 +++--- js/components/ascribe_detail/note.js | 13 +++--- js/components/ascribe_detail/piece.js | 4 +- .../ascribe_detail/piece_container.js | 28 ++++++------ .../ascribe_forms/acl_form_factory.js | 43 +++++++++++-------- .../form_copyright_association.js | 13 ++++-- .../ascribe_forms/form_register_piece.js | 16 ++++--- .../ascribe_forms/form_request_action.js | 3 -- .../ascribe_forms/form_share_email.js | 1 - .../list_form_request_actions.js | 4 +- js/components/ascribe_routes/proxy_handler.js | 20 ++++++--- .../ascribe_settings/account_settings.js | 18 +++++--- .../ascribe_settings/contract_settings.js | 25 ++++++----- .../ascribe_settings/settings_container.js | 15 ++++--- js/components/coa_verify_container.js | 1 - js/components/header.js | 17 ++++---- js/components/header_notifications.js | 8 +++- js/components/login_container.js | 1 - js/components/password_reset_container.js | 1 - js/components/piece_list.js | 4 -- .../prop_types/current_user_shape.js | 12 ++++++ js/components/prop_types/index.js | 1 + js/components/register_piece.js | 1 - js/components/signup_container.js | 1 - .../components/23vivi/23vivi_landing.js | 1 - .../components/23vivi/23vivi_piece_list.js | 1 - .../components/artcity/artcity_landing.js | 1 - .../ascribe_detail/wallet_action_panel.js | 14 +++--- .../ascribe_detail/wallet_piece_container.js | 11 +++-- .../wallet/components/cc/cc_register_piece.js | 1 - .../cyland_detail/cyland_piece_container.js | 4 -- .../components/cyland/cyland_piece_list.js | 23 ++++++---- .../cyland/cyland_register_piece.js | 13 ++++-- .../wallet/components/demo/demo_landing.js | 1 - .../ikonotv_accordion_list_item.js | 10 +++-- .../ikonotv/ikonotv_contract_notifications.js | 19 +++++--- .../ikonotv_detail/ikonotv_piece_container.js | 3 -- .../components/ikonotv/ikonotv_landing.js | 9 ++-- .../components/ikonotv/ikonotv_piece_list.js | 19 +++++--- .../ikonotv/ikonotv_register_piece.js | 9 +++- .../components/lumenus/lumenus_landing.js | 1 - .../market_buttons/market_acl_button_list.js | 4 -- .../market_buttons/market_submit_button.js | 13 +++--- .../components/market/market_landing.js | 3 +- .../components/market/market_piece_list.js | 21 +++++---- .../market/market_register_piece.js | 1 - .../components/polline/polline_landing.js | 1 - js/components/whitelabel/wallet/wallet_app.js | 5 +-- js/utils/react_utils.js | 33 ++++++++++++++ 57 files changed, 330 insertions(+), 248 deletions(-) create mode 100644 js/components/prop_types/current_user_shape.js create mode 100644 js/components/prop_types/index.js create mode 100644 js/utils/react_utils.js diff --git a/js/components/app_base.js b/js/components/app_base.js index 34a48f55..735c4cc4 100644 --- a/js/components/app_base.js +++ b/js/components/app_base.js @@ -1,5 +1,3 @@ -'use strict'; - import React from 'react'; import UserActions from '../actions/user_actions'; @@ -9,6 +7,7 @@ import WhitelabelActions from '../actions/whitelabel_actions'; import WhitelabelStore from '../stores/whitelabel_store'; import GlobalNotification from './global_notification'; +import { currentUserShape } from './prop_types'; import { mergeOptions } from '../utils/general_utils'; @@ -23,6 +22,10 @@ export default function AppBase(App) { routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired }, + childContextTypes: { + currentUser: currentUserShape + }, + getInitialState() { return mergeOptions( UserStore.getState(), @@ -30,6 +33,12 @@ export default function AppBase(App) { ); }, + getChildContext() { + const { currentUser } = this.state; + + return { currentUser }; + }, + componentDidMount() { UserStore.listen(this.onChange); WhitelabelStore.listen(this.onChange); @@ -49,7 +58,7 @@ export default function AppBase(App) { render() { const { routes } = this.props; - const { currentUser, whitelabel } = this.state; + const { whitelabel } = this.state; // The second element of the routes prop given to us by react-router is always the // active second-level component object (ie. after App). @@ -60,7 +69,6 @@ export default function AppBase(App) { ); } -}); +})); let CoaDetails = React.createClass({ @@ -354,9 +350,9 @@ let SpoolDetails = React.createClass({
{ownerAddress}

- ); - + + ); } }); -export default Edition; +export default withCurrentUser(Edition); diff --git a/js/components/ascribe_detail/edition_action_panel.js b/js/components/ascribe_detail/edition_action_panel.js index d8832347..b97525cc 100644 --- a/js/components/ascribe_detail/edition_action_panel.js +++ b/js/components/ascribe_detail/edition_action_panel.js @@ -36,7 +36,6 @@ import { getLangText } from '../../utils/lang_utils'; */ const EditionActionPanel = React.createClass({ propTypes: { - currentUser: React.PropTypes.object.isRequired, edition: React.PropTypes.object.isRequired, router: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, @@ -101,14 +100,12 @@ const EditionActionPanel = React.createClass({ render() { const { actionPanelButtonListType: ActionPanelButtonListType, - currentUser, edition, whitelabel } = this.props; if (edition.notifications && edition.notifications.length) { return ( ); @@ -119,7 +116,6 @@ const EditionActionPanel = React.createClass({ @@ -169,7 +165,6 @@ const EditionActionPanel = React.createClass({ aclObject={edition.acl} aclName="acl_request_unconsign"> diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index af938a2f..6c6b669e 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -25,7 +25,6 @@ let EditionContainer = React.createClass({ furtherDetailsType: React.PropTypes.func, // Provided from AscribeApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -76,7 +75,7 @@ let EditionContainer = React.createClass({ }, render() { - const { actionPanelButtonListType, currentUser, furtherDetailsType, whitelabel } = this.props; + const { actionPanelButtonListType, furtherDetailsType, whitelabel } = this.props; const { edition, coaMeta } = this.state; if (edition.id) { @@ -86,7 +85,6 @@ let EditionContainer = React.createClass({ diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index 9053cdc9..6ae10f96 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -25,7 +25,7 @@ import LicenseDetail from './license_detail'; import Note from './note'; import Piece from './piece'; -import AclButtonList from './../ascribe_buttons/acl_button_list'; +import AclButtonList from '../ascribe_buttons/acl_button_list'; import AclInformation from '../ascribe_buttons/acl_information'; import CreateEditionsButton from '../ascribe_buttons/create_editions_button'; import DeleteButton from '../ascribe_buttons/delete_button'; @@ -36,25 +36,28 @@ import CreateEditionsForm from '../ascribe_forms/create_editions_form'; import ListRequestActions from '../ascribe_forms/list_form_request_actions'; import AclProxy from '../acl_proxy'; - -import ApiUrls from '../../constants/api_urls'; import AscribeSpinner from '../ascribe_spinner'; +import { currentUserShape } from '../prop_types'; + +import ApiUrls from '../../constants/api_urls'; + +import { setDocumentTitle } from '../../utils/dom_utils'; import { mergeOptions } from '../../utils/general_utils'; import { getLangText } from '../../utils/lang_utils'; -import { setDocumentTitle } from '../../utils/dom_utils'; +import { withCurrentUser } from '../../utils/react_utils'; /** * This is the component that implements resource/data specific functionality */ const PieceContainer = React.createClass({ propTypes: { + currentUser: currentUserShape.isRequired, router: React.PropTypes.object.isRequired, furtherDetailsType: React.PropTypes.func, // Provided from AscribeApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object, // Provided from router @@ -214,7 +217,6 @@ const PieceContainer = React.createClass({ if (piece.notifications && piece.notifications.length > 0) { return ( @@ -222,7 +224,7 @@ const PieceContainer = React.createClass({ } else { return ( 1}> + show={currentUser.email && Object.keys(piece.acl).length > 1}> {/* `acl_view` is always available in `edition.acl`, therefore if it has no more than 1 key, we're hiding the `DetailProperty` actions as otherwise @@ -234,7 +236,6 @@ const PieceContainer = React.createClass({
@@ -307,8 +307,7 @@ const PieceContainer = React.createClass({ placeholder={getLangText('Enter your comments ...')} editable={true} successMessage={getLangText('Private note saved')} - url={ApiUrls.note_private_piece} - currentUser={currentUser} /> + url={ApiUrls.note_private_piece} /> + url={ApiUrls.note_public_piece} /> ); } else if (this.props.notifications.action === 'loan_request') { @@ -109,7 +107,6 @@ let RequestActionForm = React.createClass({ buttonAcceptName="LOAN" buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px' pieceOrEditions={this.props.pieceOrEditions} - currentUser={this.props.currentUser} handleSuccess={this.handleSuccess} /> ); } else { diff --git a/js/components/ascribe_forms/form_share_email.js b/js/components/ascribe_forms/form_share_email.js index 39cf5ac7..eb3770d4 100644 --- a/js/components/ascribe_forms/form_share_email.js +++ b/js/components/ascribe_forms/form_share_email.js @@ -21,7 +21,6 @@ let ShareForm = React.createClass({ id: React.PropTypes.object, message: React.PropTypes.string, editions: React.PropTypes.array, - currentUser: React.PropTypes.object, handleSuccess: React.PropTypes.func }, diff --git a/js/components/ascribe_forms/list_form_request_actions.js b/js/components/ascribe_forms/list_form_request_actions.js index 3aa61359..461e1ce9 100644 --- a/js/components/ascribe_forms/list_form_request_actions.js +++ b/js/components/ascribe_forms/list_form_request_actions.js @@ -11,19 +11,17 @@ let ListRequestActions = React.createClass({ React.PropTypes.array ]).isRequired, - currentUser: React.PropTypes.object, handleSuccess: React.PropTypes.func }, render () { - const { currentUser, handleSuccess, notifications, pieceOrEditions } = this.props; + const { handleSuccess, notifications, pieceOrEditions } = this.props; if (notifications.length) { return (
{notifications.map((notification) => diff --git a/js/components/ascribe_routes/proxy_handler.js b/js/components/ascribe_routes/proxy_handler.js index 793b1745..4f412fe8 100644 --- a/js/components/ascribe_routes/proxy_handler.js +++ b/js/components/ascribe_routes/proxy_handler.js @@ -5,8 +5,12 @@ import withRouter from 'react-router/es6/withRouter'; import UserStore from '../../stores/user_store'; +import { currentUserShape } from '../prop_types'; + import AppConstants from '../../constants/application_constants'; +import { withCurrentUser } from '../../utils/react_utils'; + const { object } = React.PropTypes; const WHEN_ENUM = ['loggedIn', 'loggedOut']; @@ -33,7 +37,7 @@ export function AuthRedirect({ to, when }) { // // So if when === 'loggedIn', we're checking if the user is logged in (and // vice versa) - const isLoggedIn = Object.keys(currentUser).length && currentUser.email; + const isLoggedIn = !!currentUser.email; const exprToValidate = when === 'loggedIn' ? isLoggedIn : !isLoggedIn; // and redirect if `true`. @@ -75,15 +79,17 @@ export function AuthRedirect({ to, when }) { * @param {[function]} redirectFn A function that conditionally redirects */ export function ProxyHandler(...redirectFunctions) { - return (Component) => ( - withRouter(React.createClass({ + return (Component) => { + const ProxyHandlerComponent = React.createClass({ displayName: 'ProxyHandler', propTypes: { router: React.PropTypes.object.isRequired, + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from AscribeApp, after the routes have been initialized - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object, // Provided from router @@ -130,6 +136,8 @@ export function ProxyHandler(...redirectFunctions) { ); } - }) - )); + }); + + return withRouter(withCurrentUser(ProxyHandlerComponent)); + }; } diff --git a/js/components/ascribe_settings/account_settings.js b/js/components/ascribe_settings/account_settings.js index c8d4d64a..7f8ce388 100644 --- a/js/components/ascribe_settings/account_settings.js +++ b/js/components/ascribe_settings/account_settings.js @@ -5,25 +5,29 @@ import React from 'react'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; +import CopyrightAssociationForm from '../ascribe_forms/form_copyright_association'; import Form from '../ascribe_forms/form'; -import Property from '../ascribe_forms/property'; import InputCheckbox from '../ascribe_forms/input_checkbox'; +import Property from '../ascribe_forms/property'; + import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph'; import AclProxy from '../acl_proxy'; - -import CopyrightAssociationForm from '../ascribe_forms/form_copyright_association'; +import AscribeSpinner from '../ascribe_spinner'; +import { currentUserShape } from '../prop_types'; import ApiUrls from '../../constants/api_urls'; -import AscribeSpinner from '../ascribe_spinner'; import { getLangText } from '../../utils/lang_utils'; +import { withCurrentUser } from '../../utils/react_utils'; let AccountSettings = React.createClass({ propTypes: { - currentUser: React.PropTypes.object.isRequired, loadUser: React.PropTypes.func.isRequired, whitelabel: React.PropTypes.object.isRequired + + // Injected through HOCs + currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types }, handleSuccess() { @@ -100,7 +104,7 @@ let AccountSettings = React.createClass({ - + {profile} @@ -108,4 +112,4 @@ let AccountSettings = React.createClass({ } }); -export default AccountSettings; +export default withCurrentUser(AccountSettings); diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js index 37305381..c812f6df 100644 --- a/js/components/ascribe_settings/contract_settings.js +++ b/js/components/ascribe_settings/contract_settings.js @@ -2,29 +2,34 @@ import React from 'react'; -import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph'; -import CreateContractForm from '../ascribe_forms/form_create_contract'; - import ContractListStore from '../../stores/contract_list_store'; import ContractListActions from '../../actions/contract_list_actions'; -import ActionPanel from '../ascribe_panel/action_panel'; -import ContractSettingsUpdateButton from './contract_settings_update_button'; - import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; -import AclProxy from '../acl_proxy'; +import ContractSettingsUpdateButton from './contract_settings_update_button'; + +import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph'; +import CreateContractForm from '../ascribe_forms/form_create_contract'; + +import ActionPanel from '../ascribe_panel/action_panel'; + +import AclProxy from '../acl_proxy'; +import { currentUserShape } from '../prop_types'; -import { getLangText } from '../../utils/lang_utils'; import { setDocumentTitle } from '../../utils/dom_utils'; import { truncateTextAtCharIndex } from '../../utils/general_utils'; +import { getLangText } from '../../utils/lang_utils'; +import { withCurrentUser } from '../../utils/react_utils'; let ContractSettings = React.createClass({ propTypes: { + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from AscribeApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -72,7 +77,7 @@ let ContractSettings = React.createClass({ }, render() { - const { currentUser, location, whitelabel } = this.props; + const { currentUser, whitelabel } = this.props; const publicContracts = this.getPublicContracts(); const privateContracts = this.getPrivateContracts(); let createPublicContractForm = null; diff --git a/js/components/ascribe_settings/settings_container.js b/js/components/ascribe_settings/settings_container.js index 9e94d638..7fd23076 100644 --- a/js/components/ascribe_settings/settings_container.js +++ b/js/components/ascribe_settings/settings_container.js @@ -5,26 +5,28 @@ import React from 'react'; import UserActions from '../../actions/user_actions'; import AccountSettings from './account_settings'; +import ApiSettings from './api_settings'; import BitcoinWalletSettings from './bitcoin_wallet_settings'; -import APISettings from './api_settings'; import WebhookSettings from './webhook_settings'; import AclProxy from '../acl_proxy'; +import { currentUserShape } from '../prop_types'; -import { mergeOptions } from '../../utils/general_utils'; -import { getLangText } from '../../utils/lang_utils'; import { setDocumentTitle } from '../../utils/dom_utils'; +import { getLangText } from '../../utils/lang_utils'; +import { withCurrentUser } from '../../utils/react_utils'; let SettingsContainer = React.createClass({ propTypes: { + currentUser: currentUserShape.isRequired, + children: React.PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.element ]), // Provided from AscribeApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -44,14 +46,13 @@ let SettingsContainer = React.createClass({ return (
{children} - + - + {navRoutesLinks} @@ -217,4 +218,4 @@ let Header = React.createClass({ } }); -export default Header; +export default withCurrentUser(Header); diff --git a/js/components/header_notifications.js b/js/components/header_notifications.js index 727c37d1..28851c72 100644 --- a/js/components/header_notifications.js +++ b/js/components/header_notifications.js @@ -10,12 +10,16 @@ import Nav from 'react-bootstrap/lib/Nav'; import NotificationActions from '../actions/notification_actions'; import NotificationStore from '../stores/notification_store'; +import { currentUserShape } from './prop_types'; + import { getLangText } from '../utils/lang_utils'; +import { withCurrentUser } from '../utils/react_utils'; let HeaderNotifications = React.createClass({ propTypes: { - currentUser: React.PropTypes.object.isRequired + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types }, getInitialState() { @@ -153,4 +157,4 @@ let NotificationListItem = React.createClass({ } }); -export default HeaderNotifications; +export default withCurrentUser(HeaderNotifications); diff --git a/js/components/login_container.js b/js/components/login_container.js index 3e6ff088..e8289fa5 100644 --- a/js/components/login_container.js +++ b/js/components/login_container.js @@ -12,7 +12,6 @@ import { setDocumentTitle } from '../utils/dom_utils'; let LoginContainer = React.createClass({ propTypes: { // Provided from AscribeApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object, // Provided from router diff --git a/js/components/password_reset_container.js b/js/components/password_reset_container.js index 838040e7..f5d6f1c5 100644 --- a/js/components/password_reset_container.js +++ b/js/components/password_reset_container.js @@ -17,7 +17,6 @@ import { setDocumentTitle } from '../utils/dom_utils'; let PasswordResetContainer = React.createClass({ propTypes: { // Provided from AscribeApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object, // Provided from router diff --git a/js/components/piece_list.js b/js/components/piece_list.js index d18aeed7..e1d89af1 100644 --- a/js/components/piece_list.js +++ b/js/components/piece_list.js @@ -48,7 +48,6 @@ const PieceList = React.createClass({ orderBy: React.PropTypes.string, // Provided from AscribeApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -277,7 +276,6 @@ const PieceList = React.createClass({ render() { const { accordionListItemType: AccordionListItemType, bulkModalButtonListType: BulkModalButtonListType, - currentUser, customSubmitButton, customThumbnailPlaceholder, filterParams, @@ -311,7 +309,6 @@ const PieceList = React.createClass({ className="ascribe-piece-list-bulk-modal"> ); } else { @@ -46,7 +49,6 @@ let WalletActionPanel = React.createClass({
@@ -64,7 +65,6 @@ let WalletPieceContainer = React.createClass({ }> @@ -84,8 +84,7 @@ let WalletPieceContainer = React.createClass({ placeholder={getLangText('Enter your comments ...')} editable={true} successMessage={getLangText('Private note saved')} - url={ApiUrls.note_private_piece} - currentUser={currentUser}/> + url={ApiUrls.note_private_piece} /> {children} @@ -101,4 +100,4 @@ let WalletPieceContainer = React.createClass({ } }); -export default WalletPieceContainer; +export default withCurrentUser(WalletPieceContainer); diff --git a/js/components/whitelabel/wallet/components/cc/cc_register_piece.js b/js/components/whitelabel/wallet/components/cc/cc_register_piece.js index ef440972..a89d34c5 100644 --- a/js/components/whitelabel/wallet/components/cc/cc_register_piece.js +++ b/js/components/whitelabel/wallet/components/cc/cc_register_piece.js @@ -14,7 +14,6 @@ import { mergeOptions } from '../../../../../utils/general_utils'; let CCRegisterPiece = React.createClass({ propTypes: { // Provided from AscribeApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object, // Provided from router 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 b33c5d70..e1885356 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 @@ -32,7 +32,6 @@ const CylandPieceContainer = React.createClass({ router: React.PropTypes.object.isRequired, // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object, // Provided from router @@ -95,15 +94,12 @@ const CylandPieceContainer = React.createClass({ const { piece } = this.state; if (piece.id) { - const { currentUser } = this.props; - setDocumentTitle(`${piece.artist_name}, ${piece.title}`); return ( diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js b/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js index 329976bf..6de2571a 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js @@ -5,13 +5,18 @@ import PieceList from '../../../../piece_list'; import CylandAccordionListItem from './cyland_accordion_list/cyland_accordion_list_item'; -import { getLangText } from '../../../../../utils/lang_utils'; +import { currentUserShape } from '../../../../prop_types'; + import { setDocumentTitle } from '../../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { withCurrentUser } from '../../../../../utils/react_utils'; let CylandPieceList = React.createClass({ propTypes: { + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -19,12 +24,14 @@ let CylandPieceList = React.createClass({ }, shouldRedirect(pieceCount) { - const { currentUser: { email: userEmail }, - whitelabel: { - user: whitelabelAdminEmail - } } = this.props; + const { + currentUser: { email: userEmail }, + whitelabel: { + user: whitelabelAdminEmail + } + } = this.props; - return userEmail !== whitelabelAdminEmail && !pieceCount; + return userEmail !== whitelabelAdminEmail && !pieceCount; }, render() { @@ -52,4 +59,4 @@ let CylandPieceList = React.createClass({ } }); -export default CylandPieceList; +export default withCurrentUser(CylandPieceList); 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 1f513348..f98769c2 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js @@ -24,20 +24,25 @@ import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece'; import SlidesContainer from '../../../../ascribe_slides_container/slides_container'; +import { currentUserShape } from '../../../../prop_types'; + import ApiUrls from '../../../../../constants/api_urls'; -import { getLangText } from '../../../../../utils/lang_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils'; -import { mergeOptions } from '../../../../../utils/general_utils'; import { getAclFormMessage } from '../../../../../utils/form_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { mergeOptions } from '../../../../../utils/general_utils'; +import { withCurrentUser } from '../../../../../utils/react_utils'; const CylandRegisterPiece = React.createClass({ propTypes: { router: React.PropTypes.object.isRequired, + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -218,4 +223,4 @@ const CylandRegisterPiece = React.createClass({ } }); -export default withRouter(CylandRegisterPiece); +export default withRouter(withCurrentUser(CylandRegisterPiece)); diff --git a/js/components/whitelabel/wallet/components/demo/demo_landing.js b/js/components/whitelabel/wallet/components/demo/demo_landing.js index 05d5643f..5d6a816a 100644 --- a/js/components/whitelabel/wallet/components/demo/demo_landing.js +++ b/js/components/whitelabel/wallet/components/demo/demo_landing.js @@ -15,7 +15,6 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils'; let DemoLanding = React.createClass({ propTypes: { // Provided from WalletApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object.isRequired }, 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 bc20c367..d19f89f4 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 @@ -14,21 +14,25 @@ import IkonotvSubmitButton from '../ikonotv_buttons/ikonotv_submit_button'; import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece'; import AclProxy from '../../../../../acl_proxy'; +import { currentUserShape } from '../../../../../prop_types'; import { getLangText } from '../../../../../../utils/lang_utils'; import { mergeOptions } from '../../../../../../utils/general_utils'; +import { withCurrentUser } from '../../../../../../utils/react_utils'; let IkonotvAccordionListItem = React.createClass({ propTypes: { content: React.PropTypes.object.isRequired, - currentUser: React.PropTypes.object.isRequired, children: React.PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.element ]), - className: React.PropTypes.string + className: React.PropTypes.string, + + // Injected through HOCs + currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types }, getInitialState() { @@ -116,4 +120,4 @@ let IkonotvAccordionListItem = React.createClass({ } }); -export default IkonotvAccordionListItem; +export default withCurrentUser(IkonotvAccordionListItem); diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js index 94b15997..28157e97 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_contract_notifications.js @@ -15,18 +15,23 @@ import OwnershipFetcher from '../../../../../fetchers/ownership_fetcher'; import CopyrightAssociationForm from '../../../../ascribe_forms/form_copyright_association'; import Property from '../../../../ascribe_forms/property'; +import { currentUserShape } from '../../../../../prop_types'; + import AppConstants from '../../../../../constants/application_constants'; -import { getLangText } from '../../../../../utils/lang_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { withCurrentUser } from '../../../../../../utils/react_utils'; const IkonotvContractNotifications = React.createClass({ propTypes: { router: React.PropTypes.object.isRequired, + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -130,10 +135,10 @@ const IkonotvContractNotifications = React.createClass({ this.props.router.push('/collection'); }, - getCopyrightAssociationForm(){ - const { currentUser } = this.props; + getCopyrightAssociationForm() { + const { profile } = this.props.currentUser; - if (currentUser.profile && !currentUser.profile.copyright_association) { + if (profile && !profile.copyright_association) { return (

{getLangText('Are you a member of any copyright societies?')}

@@ -141,7 +146,7 @@ const IkonotvContractNotifications = React.createClass({

{AppConstants.copyrightAssociations.join(', ')}

- +
); } else { @@ -198,4 +203,4 @@ const IkonotvContractNotifications = React.createClass({ } }); -export default withRouter(IkonotvContractNotifications); +export default withRouter(withCurrentUser(IkonotvContractNotifications)); diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js index 3ec7198e..83ba3cd4 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js @@ -33,7 +33,6 @@ const IkonotvPieceContainer = React.createClass({ router: React.PropTypes.object.isRequired, // Provided from WalletApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object, // Provided from router @@ -93,7 +92,6 @@ const IkonotvPieceContainer = React.createClass({ }, render() { - const { currentUser } = this.props; const { piece } = this.state; let furtherDetails = ( @@ -127,7 +125,6 @@ const IkonotvPieceContainer = React.createClass({ return ( diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_landing.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_landing.js index df535710..d85903df 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_landing.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_landing.js @@ -6,14 +6,17 @@ import Button from 'react-bootstrap/lib/Button'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; -import { getLangText } from '../../../../../utils/lang_utils'; +import { currentUserShape } from '../../../../prop_types'; + import { setDocumentTitle } from '../../../../../utils/dom_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; +import { withCurrentUser } from '../../../../../utils/react_utils'; let IkonotvLanding = React.createClass({ propTypes: { // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, + currentUser: currentUserShape.isRequired, whitelabel: React.PropTypes.object, // Provided from router @@ -101,4 +104,4 @@ let IkonotvLanding = React.createClass({ } }); -export default IkonotvLanding; +export default withCurrentUser(IkonotvLanding); diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js index c7c7e804..4ff2cfce 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js @@ -8,14 +8,19 @@ import NotificationStore from '../../../../../stores/notification_store'; import IkonotvAccordionListItem from './ikonotv_accordion_list/ikonotv_accordion_list_item'; +import { currentUserShape } from '../../../../prop_types'; + import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { getLangText } from '../../../../../utils/lang_utils'; +import { withCurrentUser } from '../../../../../utils/react_utils'; let IkonotvPieceList = React.createClass({ propTypes: { + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -39,10 +44,12 @@ let IkonotvPieceList = React.createClass({ }, shouldRedirect(pieceCount) { - const { currentUser: { email: userEmail }, - whitelabel: { - user: whitelabelAdminEmail - } } = this.props; + const { + currentUser: { email: userEmail }, + whitelabel: { + user: whitelabelAdminEmail + } + } = this.props; const { contractAgreementListNotifications } = this.state; return contractAgreementListNotifications && @@ -84,4 +91,4 @@ let IkonotvPieceList = React.createClass({ } }); -export default IkonotvPieceList; +export default withCurrentUser(IkonotvPieceList); diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js index a9d99ed1..f18255cb 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js @@ -22,10 +22,13 @@ import LoanForm from '../../../../ascribe_forms/form_loan'; import SlidesContainer from '../../../../ascribe_slides_container/slides_container'; +import { currentUserShape } from '../../../../prop_types'; + import ApiUrls from '../../../../../constants/api_urls'; import { mergeOptions } from '../../../../../utils/general_utils'; import { getLangText } from '../../../../../utils/lang_utils'; +import { withCurrentUser } from '../../../../../utils/react_utils'; const IkonotvRegisterPiece = React.createClass({ @@ -34,8 +37,10 @@ const IkonotvRegisterPiece = React.createClass({ handleSuccess: React.PropTypes.func, + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -243,4 +248,4 @@ const IkonotvRegisterPiece = React.createClass({ } }); -export default withRouter(IkonotvRegisterPiece); +export default withRouter(withCurrentUser(IkonotvRegisterPiece)); diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js index f7d70f4c..7888d0ad 100644 --- a/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js +++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js @@ -12,7 +12,6 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils'; let LumenusLanding = React.createClass({ propTypes: { // Provided from WalletApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object.isRequired, // Provided from router diff --git a/js/components/whitelabel/wallet/components/market/market_buttons/market_acl_button_list.js b/js/components/whitelabel/wallet/components/market/market_buttons/market_acl_button_list.js index 0ad9132d..9bbc0834 100644 --- a/js/components/whitelabel/wallet/components/market/market_buttons/market_acl_button_list.js +++ b/js/components/whitelabel/wallet/components/market/market_buttons/market_acl_button_list.js @@ -14,7 +14,6 @@ import { selectFromObject } from '../../../../../../utils/general_utils'; let MarketAclButtonList = React.createClass({ propTypes: { availableAcls: React.PropTypes.object.isRequired, - currentUser: React.PropTypes.object.isRequired, handleSuccess: React.PropTypes.func.isRequired, pieceOrEditions: React.PropTypes.array.isRequired, whitelabel: React.PropTypes.object.isRequired, @@ -30,14 +29,12 @@ let MarketAclButtonList = React.createClass({ const { availableAcls, children, className, - currentUser, handleSuccess, pieceOrEditions, whitelabel } = this.props; const buttonProps = selectFromObject(this.props, [ 'availableAcls', - 'currentUser', 'handleSuccess', 'pieceOrEditions' ]); @@ -46,7 +43,6 @@ let MarketAclButtonList = React.createClass({
diff --git a/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js b/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js index 8940186b..7d4d77e6 100644 --- a/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js +++ b/js/components/whitelabel/wallet/components/market/market_buttons/market_submit_button.js @@ -10,26 +10,27 @@ import MarketAdditionalDataForm from '../market_forms/market_additional_data_for import MarketErrorConsignUnavailable from '../market_error_consign_unavailable'; import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory'; -import ConsignForm from '../../../../../ascribe_forms/form_consign'; import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper'; import AclProxy from '../../../../../acl_proxy'; - -import ApiUrls from '../../../../../../constants/api_urls'; +import { currentUserShape } from '../../../../../prop_types'; import { getAclFormMessage, getAclFormDataId } from '../../../../../../utils/form_utils'; import { getLangText } from '../../../../../../utils/lang_utils'; +import { withCurrentUser } from '../../../../../../utils/react_utils'; let MarketSubmitButton = React.createClass({ propTypes: { availableAcls: React.PropTypes.object.isRequired, - currentUser: React.PropTypes.object.isRequired, editions: React.PropTypes.array.isRequired, whitelabel: React.PropTypes.object.isRequired, className: React.PropTypes.string, - handleSuccess: React.PropTypes.func + handleSuccess: React.PropTypes.func, + + // Injected through HOCs + currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types }, canEditionBeSubmitted(edition) { @@ -184,4 +185,4 @@ let MarketSubmitButton = React.createClass({ } }); -export default MarketSubmitButton; +export default withCurrentUser(MarketSubmitButton); diff --git a/js/components/whitelabel/wallet/components/market/market_landing.js b/js/components/whitelabel/wallet/components/market/market_landing.js index 9b14804e..15ef4621 100644 --- a/js/components/whitelabel/wallet/components/market/market_landing.js +++ b/js/components/whitelabel/wallet/components/market/market_landing.js @@ -15,13 +15,12 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils'; let MarketLanding = React.createClass({ propTypes: { // Provided from WalletApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object.isRequired }, componentDidUpdate() { const { name } = this.props.whitelabel; - + if (name) { setDocumentTitle(`${name} Marketplace`); } diff --git a/js/components/whitelabel/wallet/components/market/market_piece_list.js b/js/components/whitelabel/wallet/components/market/market_piece_list.js index 188e4d8b..c0b1d843 100644 --- a/js/components/whitelabel/wallet/components/market/market_piece_list.js +++ b/js/components/whitelabel/wallet/components/market/market_piece_list.js @@ -5,17 +5,20 @@ import React from 'react'; import MarketAclButtonList from './market_buttons/market_acl_button_list'; import PieceList from '../../../../piece_list'; +import { currentUserShape } from '../../../../prop_types'; import { setDocumentTitle } from '../../../../../utils/dom_utils'; -import { mergeOptions } from '../../../../../utils/general_utils'; import { getLangText } from '../../../../../utils/lang_utils'; +import { withCurrentUser } from '../../../../../utils/react_utils'; let MarketPieceList = React.createClass({ propTypes: { customThumbnailPlaceholder: React.PropTypes.func, + // Injected through HOCs + currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types + // Provided from WalletApp - currentUser: React.PropTypes.object.isRequired, whitelabel: React.PropTypes.object.isRequired, // Provided from router @@ -27,11 +30,13 @@ let MarketPieceList = React.createClass({ }, render() { - const { currentUser: { email: userEmail }, - whitelabel: { - name: whitelabelName = 'Market', - user: whitelabelAdminEmail - } } = this.props; + const { + currentUser: { email: userEmail }, + whitelabel: { + name: whitelabelName = 'Market', + user: whitelabelAdminEmail + } + } = this.props; let filterParams = null; let isUserAdmin = null; @@ -68,4 +73,4 @@ let MarketPieceList = React.createClass({ } }); -export default MarketPieceList; +export default withCurrentUser(MarketPieceList); diff --git a/js/components/whitelabel/wallet/components/market/market_register_piece.js b/js/components/whitelabel/wallet/components/market/market_register_piece.js index 76185c35..b10ad699 100644 --- a/js/components/whitelabel/wallet/components/market/market_register_piece.js +++ b/js/components/whitelabel/wallet/components/market/market_register_piece.js @@ -26,7 +26,6 @@ let MarketRegisterPiece = React.createClass({ router: React.PropTypes.object.isRequired, // Provided from WalletApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object.isRequired, // Provided from router diff --git a/js/components/whitelabel/wallet/components/polline/polline_landing.js b/js/components/whitelabel/wallet/components/polline/polline_landing.js index c27d56c6..2fa9134e 100644 --- a/js/components/whitelabel/wallet/components/polline/polline_landing.js +++ b/js/components/whitelabel/wallet/components/polline/polline_landing.js @@ -15,7 +15,6 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils'; let PollineLanding = React.createClass({ propTypes: { // Provided from WalletApp - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object.isRequired }, diff --git a/js/components/whitelabel/wallet/wallet_app.js b/js/components/whitelabel/wallet/wallet_app.js index 944c47d4..1064b5ba 100644 --- a/js/components/whitelabel/wallet/wallet_app.js +++ b/js/components/whitelabel/wallet/wallet_app.js @@ -17,12 +17,11 @@ let WalletApp = React.createClass({ routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, // Provided from AppBase - currentUser: React.PropTypes.object, whitelabel: React.PropTypes.object }, render() { - const { activeRoute, children, currentUser, router, routes, whitelabel } = this.props; + const { activeRoute, children, router, routes, whitelabel } = this.props; const subdomain = getSubdomain(); const path = activeRoute && activeRoute.path; const Footer = activeRoute && activeRoute.footer; @@ -35,7 +34,6 @@ let WalletApp = React.createClass({ } else { header = (
); @@ -47,7 +45,6 @@ let WalletApp = React.createClass({
{header} {/* Routes are injected here */} {children} diff --git a/js/utils/react_utils.js b/js/utils/react_utils.js new file mode 100644 index 00000000..58c52a8d --- /dev/null +++ b/js/utils/react_utils.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { currentUserShape } from '../components/prop_types'; + + +/** + * Taken from react-router (https://github.com/reactjs/react-router/blob/master/modules/withRouter.js) + * FIXME: should be put into react-component's utils + */ +export function getDisplayName(WrappedComponent) { + return WrappedComponent.displayName || WrappedComponent.name || 'Component'; +} + +/** + * Similar to react-router's `withRouter`, this injects the `currentUser` from the Component's + * context into the Component as a prop. + * + * @param {Component} Component Component to inject `context.currentUser` into + * @return {Component} Wrapped component + */ +export function withCurrentUser(Component) { + const contextTypes = { + currentUser: currentUserShape.isRequired + }; + + const WithCurrentUser = (props, { currentUser }) => ( + + ); + + WithCurrentUser.displayName = `WithCurrentUser(${getDisplayName(Component)})`; + WithCurrentUser.contextTypes = contextTypes; + + return WithCurrentUser; +}