Merge pull request #95 from ascribe/add-default-should-redirect

Collection should redirect to Register Work when a user does not have pieces yet
This commit is contained in:
Tim Daubenschütz 2016-01-26 11:08:32 +01:00
commit d8f2423344
7 changed files with 120 additions and 65 deletions

View File

@ -18,6 +18,8 @@ import AclProxy from './acl_proxy';
import EventActions from '../actions/event_actions'; import EventActions from '../actions/event_actions';
import PieceListStore from '../stores/piece_list_store';
import UserActions from '../actions/user_actions'; import UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store'; import UserStore from '../stores/user_store';
@ -43,12 +45,17 @@ let Header = React.createClass({
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
PieceListStore.getState(),
WhitelabelStore.getState(), WhitelabelStore.getState(),
UserStore.getState() UserStore.getState()
); );
}, },
componentDidMount() { 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); UserStore.listen(this.onChange);
UserActions.fetchCurrentUser.defer(); UserActions.fetchCurrentUser.defer();
@ -75,11 +82,16 @@ let Header = React.createClass({
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange); UserStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange); WhitelabelStore.unlisten(this.onChange);
//history.unlisten(this.onRouteChange); //history.unlisten(this.onRouteChange);
}, },
onChange(state) {
this.setState(state);
},
getLogo() { getLogo() {
let { whitelabel } = this.state; let { whitelabel } = this.state;
@ -93,16 +105,16 @@ let Header = React.createClass({
<img className="img-brand" src={whitelabel.logo} alt="Whitelabel brand"/> <img className="img-brand" src={whitelabel.logo} alt="Whitelabel brand"/>
</Link> </Link>
); );
} else {
return (
<span>
<Link className="icon-ascribe-logo" to="/collection"/>
</span>
);
} }
return (
<span>
<Link className="icon-ascribe-logo" to="/collection"/>
</span>
);
}, },
getPoweredBy(){ getPoweredBy() {
return ( return (
<AclProxy <AclProxy
aclObject={this.state.whitelabel} aclObject={this.state.whitelabel}
@ -117,10 +129,6 @@ let Header = React.createClass({
); );
}, },
onChange(state) {
this.setState(state);
},
onMenuItemClick() { onMenuItemClick() {
/* /*
This is a hack to make the dropdown close after clicking on an item This is a hack to make the dropdown close after clicking on an item
@ -156,15 +164,17 @@ let Header = React.createClass({
}, },
render() { render() {
const { currentUser, unfilteredPieceListCount } = this.state;
let account; let account;
let signup; let signup;
let navRoutesLinks; let navRoutesLinks;
if (this.state.currentUser.username){
if (currentUser.username) {
account = ( account = (
<DropdownButton <DropdownButton
ref='dropdownbutton' ref='dropdownbutton'
eventKey="1" eventKey="1"
title={this.state.currentUser.username}> title={currentUser.username}>
<LinkContainer <LinkContainer
to="/settings" to="/settings"
onClick={this.onMenuItemClick}> onClick={this.onMenuItemClick}>
@ -174,7 +184,7 @@ let Header = React.createClass({
</MenuItem> </MenuItem>
</LinkContainer> </LinkContainer>
<AclProxy <AclProxy
aclObject={this.state.currentUser.acl} aclObject={currentUser.acl}
aclName="acl_view_settings_contract"> aclName="acl_view_settings_contract">
<LinkContainer <LinkContainer
to="/contract_settings" to="/contract_settings"
@ -195,9 +205,21 @@ let Header = React.createClass({
</LinkContainer> </LinkContainer>
</DropdownButton> </DropdownButton>
); );
navRoutesLinks = <NavRoutesLinks routes={this.props.routes} userAcl={this.state.currentUser.acl} navbar right/>;
} // Let's assume that if the piece list hasn't loaded yet (ie. when unfilteredPieceListCount === -1)
else { // 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 = (
<NavRoutesLinks
navbar
right
hasPieces={!!unfilteredPieceListCount}
routes={this.props.routes}
userAcl={currentUser.acl} />
);
} else {
account = ( account = (
<LinkContainer <LinkContainer
to="/login"> to="/login">

View File

@ -11,14 +11,33 @@ import AclProxy from './acl_proxy';
import { sanitizeList } from '../utils/general_utils'; import { sanitizeList } from '../utils/general_utils';
const DISABLE_ENUM = ['hasPieces', 'noPieces'];
let NavRoutesLinks = React.createClass({ let NavRoutesLinks = React.createClass({
propTypes: { propTypes: {
hasPieces: React.PropTypes.bool,
routes: React.PropTypes.arrayOf(React.PropTypes.object), routes: React.PropTypes.arrayOf(React.PropTypes.object),
userAcl: 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 * 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 * We can define a headerTitle as well as a aclName and according to that the
@ -29,48 +48,50 @@ let NavRoutesLinks = React.createClass({
* @return {Array} Array of ReactElements that can be displayed to the user * @return {Array} Array of ReactElements that can be displayed to the user
*/ */
extractLinksFromRoutes(node, userAcl, i) { extractLinksFromRoutes(node, userAcl, i) {
if(!node) { if (!node) {
return; return;
} }
let links = node.childRoutes.map((child, j) => { const links = node.childRoutes.map((child, j) => {
let childrenFn = null; const { aclName, disableOn, headerTitle, path, childRoutes } = child;
let { aclName, headerTitle, path, childRoutes } = child;
// 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 > 0) {
childrenFn = this.extractLinksFromRoutes(child, userAcl, i++);
}
// We validate if the user has set the title correctly, // We validate if the user has set the title correctly,
// otherwise we're not going to render his route // otherwise we're not going to render his route
if(headerTitle && typeof headerTitle === 'string') { 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, // if there is an aclName present on the route definition,
// we evaluate it against the user's acl // we evaluate it against the user's acl
if(aclName && typeof aclName !== 'undefined') { if (aclName && typeof aclName !== 'undefined') {
return ( return (
<AclProxy <AclProxy
key={j} key={j}
aclName={aclName} aclName={aclName}
aclObject={this.props.userAcl}> aclObject={this.props.userAcl}>
<NavRoutesLinksLink <NavRoutesLinksLink {...navLinkProps} />
headerTitle={headerTitle}
routePath={'/' + path}
depth={i}
children={childrenFn}/>
</AclProxy> </AclProxy>
); );
} else { } else {
return ( return (
<NavRoutesLinksLink <NavRoutesLinksLink
key={j} key={j}
headerTitle={headerTitle} {...navLinkProps} />
routePath={'/' + path}
depth={i}
children={childrenFn}/>
); );
} }
} else { } else {
@ -84,7 +105,7 @@ let NavRoutesLinks = React.createClass({
}, },
render() { render() {
let {routes, userAcl} = this.props; const {routes, userAcl} = this.props;
return ( return (
<Nav {...this.props}> <Nav {...this.props}>
@ -94,4 +115,4 @@ let NavRoutesLinks = React.createClass({
} }
}); });
export default NavRoutesLinks; export default NavRoutesLinks;

View File

@ -11,40 +11,45 @@ import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
let NavRoutesLinksLink = React.createClass({ let NavRoutesLinksLink = React.createClass({
propTypes: { propTypes: {
headerTitle: React.PropTypes.string,
routePath: React.PropTypes.string,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
]), ]),
disabled: React.PropTypes.bool,
depth: React.PropTypes.number depth: React.PropTypes.number,
headerTitle: React.PropTypes.string,
routePath: React.PropTypes.string
}, },
render() { render() {
let { 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 // if the route has children, we're returning a DropdownButton that will get filled
// with MenuItems // with MenuItems
if(children) { if (children) {
return ( return (
<DropdownButton title={headerTitle}> <DropdownButton
disabled={disabled}
title={headerTitle}>
{children} {children}
</DropdownButton> </DropdownButton>
); );
} else { } else {
if(depth === 1) { if (depth === 1) {
// if the node's child is actually a node of level one (a child of a node), we're // if the node's child is actually a node of level one (a child of a node), we're
// returning a DropdownButton matching MenuItem // returning a DropdownButton matching MenuItem
return ( return (
<LinkContainer to={routePath}> <LinkContainer
disabled={disabled}
to={routePath}>
<MenuItem>{headerTitle}</MenuItem> <MenuItem>{headerTitle}</MenuItem>
</LinkContainer> </LinkContainer>
); );
} else if(depth === 0) { } else if (depth === 0) {
return ( return (
<LinkContainer to={routePath}> <LinkContainer
disabled={disabled}
to={routePath}>
<NavItem>{headerTitle}</NavItem> <NavItem>{headerTitle}</NavItem>
</LinkContainer> </LinkContainer>
); );
@ -55,4 +60,4 @@ let NavRoutesLinksLink = React.createClass({
} }
}); });
export default NavRoutesLinksLink; export default NavRoutesLinksLink;

View File

@ -53,7 +53,6 @@ let PieceList = React.createClass({
accordionListItemType: AccordionListItemWallet, accordionListItemType: AccordionListItemWallet,
bulkModalButtonListType: AclButtonList, bulkModalButtonListType: AclButtonList,
canLoadPieceList: true, canLoadPieceList: true,
orderParams: ['artist_name', 'title'],
filterParams: [{ filterParams: [{
label: getLangText('Show works I can'), label: getLangText('Show works I can'),
items: [ items: [
@ -61,7 +60,10 @@ let PieceList = React.createClass({
'acl_consign', 'acl_consign',
'acl_create_editions' 'acl_create_editions'
] ]
}] }],
orderParams: ['artist_name', 'title'],
redirectTo: '/register_piece',
shouldRedirect: () => true
}; };
}, },

View File

@ -75,7 +75,6 @@ let PrizePieceList = React.createClass({
<div> <div>
<PieceList <PieceList
ref="list" ref="list"
redirectTo="/register_piece"
accordionListItemType={AccordionListItemPrize} accordionListItemType={AccordionListItemPrize}
orderParams={orderParams} orderParams={orderParams}
orderBy={this.state.currentUser.is_jury ? 'rating' : null} orderBy={this.state.currentUser.is_jury ? 'rating' : null}

View File

@ -75,7 +75,8 @@ let ROUTES = {
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CylandPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CylandPieceList)}
headerTitle='COLLECTION' /> headerTitle='COLLECTION'
disableOn='noPieces' />
<Route path='editions/:editionId' component={EditionContainer} /> <Route path='editions/:editionId' component={EditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path='verify' component={CoaVerifyContainer} />
<Route path='pieces/:pieceId' component={CylandPieceContainer} /> <Route path='pieces/:pieceId' component={CylandPieceContainer} />
@ -109,7 +110,8 @@ let ROUTES = {
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PieceList)}
headerTitle='COLLECTION' /> headerTitle='COLLECTION'
disableOn='noPieces' />
<Route path='pieces/:pieceId' component={PieceContainer} /> <Route path='pieces/:pieceId' component={PieceContainer} />
<Route path='editions/:editionId' component={EditionContainer} /> <Route path='editions/:editionId' component={EditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path='verify' component={CoaVerifyContainer} />
@ -150,7 +152,8 @@ let ROUTES = {
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvPieceList)}
headerTitle='COLLECTION' /> headerTitle='COLLECTION'
disableOn='noPieces' />
<Route <Route
path='contract_notifications' path='contract_notifications'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvContractNotifications)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvContractNotifications)} />
@ -189,7 +192,8 @@ let ROUTES = {
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketPieceList)}
headerTitle='COLLECTION' /> headerTitle='COLLECTION'
disableOn='noPieces' />
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> <Route path='pieces/:pieceId' component={MarketPieceContainer} />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route path='editions/:editionId' component={MarketEditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path='verify' component={CoaVerifyContainer} />
@ -225,7 +229,8 @@ let ROUTES = {
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(Vivi23PieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(Vivi23PieceList)}
headerTitle='COLLECTION' /> headerTitle='COLLECTION'
disableOn='noPieces' />
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> <Route path='pieces/:pieceId' component={MarketPieceContainer} />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route path='editions/:editionId' component={MarketEditionContainer} />
<Route path='verify' component={CoaVerifyContainer} /> <Route path='verify' component={CoaVerifyContainer} />

View File

@ -40,7 +40,8 @@ const COMMON_ROUTES = (
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PieceList)}
headerTitle='COLLECTION'/> headerTitle='COLLECTION'
disableOn='noPieces' />
<Route <Route
path='signup' path='signup'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)} />