From 9559bc09b4dd99dce4afea56f108f60f43e7c648 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Thu, 21 Jan 2016 16:47:35 +0100 Subject: [PATCH] Disable 'Collection' header item when no pieces are found for the user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The disabling only happens when the piece list is fetched, which may not happen immediately in some cases (ie. logged in user goes straight to another person’s edition first). We’re not able to fetch the piece list immediately, as some white labels apply default filters on the piece list which forces them to wait until they can begin fetching their piece lists. --- js/components/header.js | 31 +++++++--- js/components/nav_routes_links.js | 61 +++++++++++++------ js/components/nav_routes_links_link.js | 23 ++++--- .../whitelabel/prize/prize_routes.js | 6 +- .../whitelabel/wallet/wallet_routes.js | 15 +++-- js/routes.js | 3 +- 6 files changed, 94 insertions(+), 45 deletions(-) diff --git a/js/components/header.js b/js/components/header.js index 68f50f7c..d2ecb76b 100644 --- a/js/components/header.js +++ b/js/components/header.js @@ -18,6 +18,8 @@ import AclProxy from './acl_proxy'; import EventActions from '../actions/event_actions'; +import PieceListStore from '../stores/piece_list_store'; + import UserActions from '../actions/user_actions'; import UserStore from '../stores/user_store'; @@ -43,12 +45,17 @@ let Header = React.createClass({ getInitialState() { return mergeOptions( + PieceListStore.getState(), WhitelabelStore.getState(), UserStore.getState() ); }, componentDidMount() { + // Listen to the piece list store, but don't fetch immediately to avoid + // conflicts with routes that may need to wait to load the piece list + PieceListStore.listen(this.onChange); + UserStore.listen(this.onChange); UserActions.fetchCurrentUser.defer(); @@ -75,11 +82,16 @@ let Header = React.createClass({ }, componentWillUnmount() { + PieceListStore.unlisten(this.onChange); UserStore.unlisten(this.onChange); WhitelabelStore.unlisten(this.onChange); //history.unlisten(this.onRouteChange); }, + onChange(state) { + this.setState(state); + }, + getLogo() { let { whitelabel } = this.state; @@ -117,10 +129,6 @@ let Header = React.createClass({ ); }, - onChange(state) { - this.setState(state); - }, - onMenuItemClick() { /* This is a hack to make the dropdown close after clicking on an item @@ -156,16 +164,17 @@ let Header = React.createClass({ }, render() { + const { currentUser, unfilteredPieceListCount } = this.state; let account; let signup; let navRoutesLinks; - if (this.state.currentUser.username) { + if (currentUser.username) { account = ( + title={currentUser.username}> @@ -175,7 +184,7 @@ let Header = React.createClass({ ); + // Let's assume that if the piece list hasn't loaded yet (ie. when unfilteredPieceListCount === -1) + // then the user has pieces + // FIXME: this doesn't work that well as the user may not load their piece list + // until much later, so we would show the 'Collection' header as available until + // they actually click on it and get redirected to piece registration. navRoutesLinks = ( + userAcl={currentUser.acl} /> ); } else { account = ( diff --git a/js/components/nav_routes_links.js b/js/components/nav_routes_links.js index 2a78a337..676a9896 100644 --- a/js/components/nav_routes_links.js +++ b/js/components/nav_routes_links.js @@ -11,14 +11,33 @@ import AclProxy from './acl_proxy'; import { sanitizeList } from '../utils/general_utils'; +const DISABLE_ENUM = ['hasPieces', 'noPieces']; + let NavRoutesLinks = React.createClass({ propTypes: { + hasPieces: React.PropTypes.bool, routes: React.PropTypes.arrayOf(React.PropTypes.object), userAcl: React.PropTypes.object }, + isRouteDisabled(disableOn) { + const { hasPieces } = this.props; + + if (disableOn) { + if (!DISABLE_ENUM.includes(disableOn)) { + throw new Error(`"disableOn" must be one of: [${DISABLE_ENUM.join(', ')}] got "${disableOn}" instead`); + } + + if (disableOn === 'hasPieces') { + return hasPieces; + } else if (disableOn === 'noPieces') { + return !hasPieces; + } + } + }, + /** - * This method generales a bunch of react-bootstrap specific links + * This method generates a bunch of react-bootstrap specific links * from the routes we defined in one of the specific routes.js file * * We can define a headerTitle as well as a aclName and according to that the @@ -34,20 +53,29 @@ let NavRoutesLinks = React.createClass({ } const links = node.childRoutes.map((child, j) => { - const { aclName, headerTitle, path, childRoutes } = child; - let childrenFn = null; - - // If the node has children that could be rendered, then we want - // to execute this function again with the child as the root - // - // Otherwise we'll just pass childrenFn as false - if (child.childRoutes && child.childRoutes.length) { - childrenFn = this.extractLinksFromRoutes(child, userAcl, i++); - } + const { aclName, disableOn, headerTitle, path, childRoutes } = child; // We validate if the user has set the title correctly, // otherwise we're not going to render his route if (headerTitle && typeof headerTitle === 'string') { + let nestedChildren = null; + + // If the node has children that could be rendered, then we want + // to execute this function again with the child as the root + // + // Otherwise we'll just pass nestedChildren as false + if (child.childRoutes && child.childRoutes.length) { + nestedChildren = this.extractLinksFromRoutes(child, userAcl, i++); + } + + const navLinkProps = { + headerTitle, + children: nestedChildren, + depth: i, + disabled: this.isRouteDisabled(disableOn), + routePath: `/${path}` + }; + // if there is an aclName present on the route definition, // we evaluate it against the user's acl if (aclName && typeof aclName !== 'undefined') { @@ -56,21 +84,14 @@ let NavRoutesLinks = React.createClass({ key={j} aclName={aclName} aclObject={this.props.userAcl}> - + ); } else { return ( + {...navLinkProps} /> ); } } else { diff --git a/js/components/nav_routes_links_link.js b/js/components/nav_routes_links_link.js index eef98e59..6daec0ea 100644 --- a/js/components/nav_routes_links_link.js +++ b/js/components/nav_routes_links_link.js @@ -11,25 +11,26 @@ import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; let NavRoutesLinksLink = React.createClass({ propTypes: { - headerTitle: React.PropTypes.string, - routePath: React.PropTypes.string, - children: React.PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.element ]), - - depth: React.PropTypes.number + disabled: React.PropTypes.bool, + depth: React.PropTypes.number, + headerTitle: React.PropTypes.string, + routePath: React.PropTypes.string }, render() { - const { children, headerTitle, depth, routePath } = this.props; + const { children, headerTitle, depth, disabled, routePath } = this.props; // if the route has children, we're returning a DropdownButton that will get filled // with MenuItems if (children) { return ( - + {children} ); @@ -38,13 +39,17 @@ let NavRoutesLinksLink = React.createClass({ // if the node's child is actually a node of level one (a child of a node), we're // returning a DropdownButton matching MenuItem return ( - + {headerTitle} ); } else if (depth === 0) { return ( - + {headerTitle} ); diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index 5f80b30c..b8502004 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -54,7 +54,8 @@ const ROUTES = { + headerTitle='COLLECTION' + disableOn='noPieces' /> @@ -70,7 +71,8 @@ const ROUTES = { + headerTitle='SUBMISSIONS' + disableOn='noPieces' /> + headerTitle='COLLECTION' + disableOn='noPieces' /> @@ -109,7 +110,8 @@ let ROUTES = { + headerTitle='COLLECTION' + disableOn='noPieces' /> @@ -150,7 +152,8 @@ let ROUTES = { + headerTitle='COLLECTION' + disableOn='noPieces' /> @@ -189,7 +192,8 @@ let ROUTES = { + headerTitle='COLLECTION' + disableOn='noPieces' /> @@ -225,7 +229,8 @@ let ROUTES = { + headerTitle='COLLECTION' + disableOn='noPieces' /> diff --git a/js/routes.js b/js/routes.js index 49a284af..7a4fce2f 100644 --- a/js/routes.js +++ b/js/routes.js @@ -40,7 +40,8 @@ let COMMON_ROUTES = ( + headerTitle='COLLECTION' + disableOn='noPieces' />