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 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;
@ -93,16 +105,16 @@ let Header = React.createClass({
<img className="img-brand" src={whitelabel.logo} alt="Whitelabel brand"/>
</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 (
<AclProxy
aclObject={this.state.whitelabel}
@ -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,15 +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 = (
<DropdownButton
ref='dropdownbutton'
eventKey="1"
title={this.state.currentUser.username}>
title={currentUser.username}>
<LinkContainer
to="/settings"
onClick={this.onMenuItemClick}>
@ -174,7 +184,7 @@ let Header = React.createClass({
</MenuItem>
</LinkContainer>
<AclProxy
aclObject={this.state.currentUser.acl}
aclObject={currentUser.acl}
aclName="acl_view_settings_contract">
<LinkContainer
to="/contract_settings"
@ -195,9 +205,21 @@ let Header = React.createClass({
</LinkContainer>
</DropdownButton>
);
navRoutesLinks = <NavRoutesLinks routes={this.props.routes} userAcl={this.state.currentUser.acl} navbar right/>;
}
else {
// 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 = (
<NavRoutesLinks
navbar
right
hasPieces={!!unfilteredPieceListCount}
routes={this.props.routes}
userAcl={currentUser.acl} />
);
} else {
account = (
<LinkContainer
to="/login">

View File

@ -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
@ -29,48 +48,50 @@ let NavRoutesLinks = React.createClass({
* @return {Array} Array of ReactElements that can be displayed to the user
*/
extractLinksFromRoutes(node, userAcl, i) {
if(!node) {
if (!node) {
return;
}
let links = node.childRoutes.map((child, j) => {
let childrenFn = null;
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++);
}
const links = node.childRoutes.map((child, j) => {
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') {
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') {
if (aclName && typeof aclName !== 'undefined') {
return (
<AclProxy
key={j}
aclName={aclName}
aclObject={this.props.userAcl}>
<NavRoutesLinksLink
headerTitle={headerTitle}
routePath={'/' + path}
depth={i}
children={childrenFn}/>
<NavRoutesLinksLink {...navLinkProps} />
</AclProxy>
);
} else {
return (
<NavRoutesLinksLink
key={j}
headerTitle={headerTitle}
routePath={'/' + path}
depth={i}
children={childrenFn}/>
{...navLinkProps} />
);
}
} else {
@ -84,7 +105,7 @@ let NavRoutesLinks = React.createClass({
},
render() {
let {routes, userAcl} = this.props;
const {routes, userAcl} = this.props;
return (
<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({
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() {
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
// with MenuItems
if(children) {
if (children) {
return (
<DropdownButton title={headerTitle}>
<DropdownButton
disabled={disabled}
title={headerTitle}>
{children}
</DropdownButton>
);
} 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
// returning a DropdownButton matching MenuItem
return (
<LinkContainer to={routePath}>
<LinkContainer
disabled={disabled}
to={routePath}>
<MenuItem>{headerTitle}</MenuItem>
</LinkContainer>
);
} else if(depth === 0) {
} else if (depth === 0) {
return (
<LinkContainer to={routePath}>
<LinkContainer
disabled={disabled}
to={routePath}>
<NavItem>{headerTitle}</NavItem>
</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,
bulkModalButtonListType: AclButtonList,
canLoadPieceList: true,
orderParams: ['artist_name', 'title'],
filterParams: [{
label: getLangText('Show works I can'),
items: [
@ -61,7 +60,10 @@ let PieceList = React.createClass({
'acl_consign',
'acl_create_editions'
]
}]
}],
orderParams: ['artist_name', 'title'],
redirectTo: '/register_piece',
shouldRedirect: () => true
};
},

View File

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

View File

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

View File

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