diff --git a/js/components/ascribe_routes/proxy_handler.js b/js/components/ascribe_routes/proxy_handler.js new file mode 100644 index 00000000..228f0f62 --- /dev/null +++ b/js/components/ascribe_routes/proxy_handler.js @@ -0,0 +1,132 @@ +'use strict'; + +import React from 'react'; +import { RouteContext } from 'react-router'; +import history from '../../history'; + +import UserStore from '../../stores/user_store'; +import UserActions from '../../actions/user_actions'; + +import AppConstants from '../../constants/application_constants'; + + +const { object } = React.PropTypes; +const WHEN_ENUM = ['loggedIn', 'loggedOut']; + +/** + * Redirects the user conditionally according to his authentication + * + * @param {enum/string} options.when ('loggedIn' || 'loggedOut') + */ +export function AuthRedirect({to, when}) { + // validate `when`, must be contained in `WHEN_ENUM`. + // Throw an error otherwise. + if(WHEN_ENUM.indexOf(when) === -1) { + let whenValues = WHEN_ENUM.join(', '); + throw new Error(`"when" must be one of: [${whenValues}] got "${when}" instead`); + } + + return function(currentUser, query) { + const { redirectAuthenticated, redirect } = query; + + // The user of this handler specifies with `when`, what kind of status + // needs to be checked to conditionally do - if that state is `true` - + // a redirect. + // + // So if when === 'loggedIn', we're checking if the user is logged in (and + // vice versa) + let exprToValidate = when === 'loggedIn' ? currentUser && currentUser.email + : currentUser && !currentUser.email; + + // and redirect if `true`. + if(exprToValidate) { + window.setTimeout(() => history.replaceState(null, to, query)); + return true; + + // Otherwise there can also be the case that the backend + // wants to redirect the user to a specific route when the user is logged out already + } else if(!exprToValidate && when === 'loggedIn' && redirect) { + + delete query.redirect; + window.setTimeout(() => history.replaceState(null, '/' + redirect, query)); + return true; + + } else if(!exprToValidate && when === 'loggedOut' && redirectAuthenticated) { + /* + * redirectAuthenticated contains an arbitrary path + * eg pieces/, editions/, collection, settings, ... + * hence transitionTo cannot be used directly. + * + * While we're getting rid of `query.redirect` explicitly in the + * above `else if` statement, here it's sufficient to just call + * `baseUrl` + `redirectAuthenticated`, as it gets rid of queries as well. + */ + window.location = AppConstants.baseUrl + redirectAuthenticated; + return true; + } + return false; + }; +} + +/** + * Can be used in combination with `Route` as an intermediate Handler + * between the actual component we want to display dependent on a certain state + * that is required to display that component. + * + * @param {[function]} redirectFn A function that conditionally redirects + */ +export function ProxyHandler(...redirectFunctions) { + return (Component) => { + return React.createClass({ + displayName: 'ProxyHandler', + + propTypes: { + location: object + }, + + // We need insert `RouteContext` here in order to be able + // to use the `Lifecycle` widget in further down nested components + mixins: [RouteContext], + + getInitialState() { + return UserStore.getState(); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + }, + + componentDidUpdate() { + if(!UserStore.isLoading()) { + const { currentUser } = this.state; + const { query } = this.props.location; + + for(let i = 0; i < redirectFunctions.length; i++) { + // if a redirectFunction redirects the user, + // it should return `true` and therefore + // stop/avoid the execution of all functions + // that follow + if(redirectFunctions[i](currentUser, query)) { + break; + } + } + } + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + return ( + + ); + } + }); + }; +} diff --git a/js/components/ascribe_routes/proxy_routes/auth_proxy_handler.js b/js/components/ascribe_routes/proxy_routes/auth_proxy_handler.js deleted file mode 100644 index 0eb4ad8f..00000000 --- a/js/components/ascribe_routes/proxy_routes/auth_proxy_handler.js +++ /dev/null @@ -1,115 +0,0 @@ -'use strict'; - -import React from 'react'; -import { History, RouteContext } from 'react-router'; - -import UserStore from '../../../stores/user_store'; -import UserActions from '../../../actions/user_actions'; - -import AppConstants from '../../../constants/application_constants'; - - -const { object } = React.PropTypes; -const WHEN_ENUM = ['loggedIn', 'loggedOut']; - -/** - * Can be used in combination with `Route` as an intermediate Handler - * between the actual component we want to display dependent on a certain state - * that is required to display that component. - * - * @param {string} options.to Any type of route path that is defined in routes.js - * @param {enum/string} options.when ('loggedIn' || 'loggedOut') - */ -export default function AuthProxyHandler({to, when}) { - - // validate `when`, must be contained in `WHEN_ENUM`. - // Throw an error otherwise. - if(WHEN_ENUM.indexOf(when) === -1) { - let whenValues = WHEN_ENUM.join(', '); - throw new Error(`"when" must be one of: [${whenValues}] got "${when}" instead`); - } - - return (Component) => { - return React.createClass({ - displayName: 'AuthProxyHandler', - - propTypes: { - location: object - }, - - // We need insert `RouteContext` here in order to be able - // to use the `Lifecycle` widget in further down nested components - mixins: [History, RouteContext], - - getInitialState() { - return UserStore.getState(); - }, - - componentDidMount() { - UserStore.listen(this.onChange); - UserActions.fetchCurrentUser(); - }, - - componentDidUpdate() { - // Only refresh this component, when UserSources are not loading - // data from the server - if(!UserStore.isLoading()) { - this.redirectConditionally(); - } - }, - - componentWillUnmount() { - UserStore.unlisten(this.onChange); - }, - - redirectConditionally() { - const { query } = this.props.location; - const { redirectAuthenticated, redirect } = query; - - // The user of this handler specifies with `when`, what kind of status - // needs to be checked to conditionally do - if that state is `true` - - // a redirect. - // - // So if when === 'loggedIn', we're checking if the user is logged in (and - // vice versa) - let exprToValidate = when === 'loggedIn' ? - this.state.currentUser && this.state.currentUser.email : - this.state.currentUser && !this.state.currentUser.email; - - // and redirect if `true`. - if(exprToValidate) { - window.setTimeout(() => this.history.replaceState(null, to, query)); - - // Otherwise there can also be the case that the backend - // wants to redirect the user to a specific route when the user is logged out already - } else if(!exprToValidate && when === 'loggedIn' && redirect) { - - delete query.redirect; - window.setTimeout(() => this.history.replaceState(null, '/' + redirect, query)); - - } else if(!exprToValidate && when === 'loggedOut' && redirectAuthenticated) { - /* - * redirectAuthenticated contains an arbirary path - * eg pieces/, editions/, collection, settings, ... - * hence transitionTo cannot be used directly. - * - * While we're getting rid of `query.redirect` explicitly in the - * above `else if` statement, here it's sufficient to just call - * `baseUrl` + `redirectAuthenticated`, as it gets rid of queries as well. - */ - window.location = AppConstants.baseUrl + redirectAuthenticated; - } - }, - - onChange(state) { - this.setState(state); - }, - - render() { - return ( - - ); - } - }); - }; -} diff --git a/js/components/header.js b/js/components/header.js index b3fd543e..285a1ed2 100644 --- a/js/components/header.js +++ b/js/components/header.js @@ -37,16 +37,9 @@ import { constructHead } from '../utils/dom_utils'; let Header = React.createClass({ propTypes: { - showAddWork: React.PropTypes.bool, routes: React.PropTypes.arrayOf(React.PropTypes.object) }, - getDefaultProps() { - return { - showAddWork: true - }; - }, - getInitialState() { return mergeOptions( WhitelabelStore.getState(), @@ -55,10 +48,11 @@ let Header = React.createClass({ }, componentDidMount() { - UserActions.fetchCurrentUser(); UserStore.listen(this.onChange); - WhitelabelActions.fetchWhitelabel(); + UserActions.fetchCurrentUser.defer(); + WhitelabelStore.listen(this.onChange); + WhitelabelActions.fetchWhitelabel.defer(); // react-bootstrap 0.25.1 has a bug in which it doesn't // close the mobile expanded navigation after a click by itself. @@ -223,7 +217,7 @@ let Header = React.createClass({ {this.getPoweredBy()} diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_hero.js b/js/components/whitelabel/prize/portfolioreview/components/pr_hero.js index a0fa0811..0ef99c37 100644 --- a/js/components/whitelabel/prize/portfolioreview/components/pr_hero.js +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_hero.js @@ -1,11 +1,14 @@ 'use strict'; import React from 'react'; +import { Link } from 'react-router'; + +import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import UserStore from '../../../../../stores/user_store'; import UserActions from '../../../../../actions/user_actions'; -import Glyphicon from 'react-bootstrap/lib/Glyphicon'; +import { getLangText } from '../../../../../utils/lang_utils'; const PRHero = React.createClass({ @@ -15,7 +18,7 @@ const PRHero = React.createClass({ componentDidMount() { UserStore.listen(this.onChange); - UserActions.fetchCurrentUser(); + UserActions.fetchCurrentUser.defer(); }, componentWillUnmount() { @@ -31,12 +34,14 @@ const PRHero = React.createClass({ return (
-

Congratulations {currentUser.email}!

-

You have successfully submitted to Portfolio Review 2016

-

See below, your uploaded portfolio:

+

+  {getLangText('Congratulations') + (currentUser.email ? ` ${currentUser.email}!` : '!')} +

+

{getLangText('You have successfully submitted to Portfolio Review 2016.')}

+

Not you? {getLangText('Change account.')}

); } }); -export default PRHero; \ No newline at end of file +export default PRHero; diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_login_container.js b/js/components/whitelabel/prize/portfolioreview/components/pr_login_container.js deleted file mode 100644 index e69de29b..00000000 diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_routes/pr_proxy_handler.js b/js/components/whitelabel/prize/portfolioreview/components/pr_routes/pr_proxy_handler.js new file mode 100644 index 00000000..04ff8ce6 --- /dev/null +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_routes/pr_proxy_handler.js @@ -0,0 +1,26 @@ +'use strict'; + +import history from '../../../../../../history'; + + +export function AuthPrizeRoleRedirect({ to, when }) { + if (when.constructor !== Array || !when.length) { + throw new Error('`when` of AuthPrizeRoleRedirect must be an array containing values'); + } + if (!to || to.indexOf('/') === -1) { + throw new Error('`to` of AuthPrizeRoleRedirect must be defined and contain a valid route'); + } + + return function(currentUser, query) { + const exprToValidate = when + .map(role => currentUser[role]) + .reduce((a, b) => a || b); + + if (exprToValidate) { + window.setTimeout(() => history.replaceState(null, to, query)); + return true; + } else { + return false; + } + }; +} diff --git a/js/components/whitelabel/prize/portfolioreview/pr_app.js b/js/components/whitelabel/prize/portfolioreview/pr_app.js index 7e66c43c..ef85bfd5 100644 --- a/js/components/whitelabel/prize/portfolioreview/pr_app.js +++ b/js/components/whitelabel/prize/portfolioreview/pr_app.js @@ -4,6 +4,7 @@ import React from 'react'; import GlobalNotification from '../../../global_notification'; import Hero from './components/pr_hero'; +import Header from '../../../header'; import UserStore from '../../../../stores/user_store'; import UserActions from '../../../../actions/user_actions'; @@ -40,19 +41,28 @@ let PRApp = React.createClass({ }, render() { - const { history, children } = this.props; + const { history, children, routes } = this.props; const { currentUser } = this.state; + let style = {}; let subdomain = getSubdomain(); let header; + if (currentUser && currentUser.email && history.isActive(`/pieces/${getCookie(currentUser.email)}`)) { header = ; + style = { paddingTop: '0 !important' }; + } else if(currentUser && (currentUser.is_admin || currentUser.is_jury || currentUser.is_judge)) { + header =
; + } else { + style = { paddingTop: '0 !important' }; } return (
{header} -
+
{children} diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index ec0269a5..7a72e5d9 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -22,7 +22,8 @@ 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'; +import { ProxyHandler, AuthRedirect } from '../../../components/ascribe_routes/proxy_handler'; +import { AuthPrizeRoleRedirect } from './portfolioreview/components/pr_routes/pr_proxy_handler'; const ROUTES = { @@ -31,28 +32,27 @@ const ROUTES = { + component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SPLoginContainer)} /> + component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}/> + component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SPSignupContainer)} /> + component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)} /> + component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPSettingsContainer)}/> - @@ -61,24 +61,41 @@ const ROUTES = { ), portfolioreview: ( - + + component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PRRegisterPiece)}/> + + component={ProxyHandler( + AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }), + AuthRedirect({to: '/register_piece', when: 'loggedIn'}) + )(SPLoginContainer)} /> + component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)} /> + component={ProxyHandler( + AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }), + AuthRedirect({to: '/register_piece', when: 'loggedIn'}) + )(SPSignupContainer)} /> + component={ProxyHandler( + AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }), + AuthRedirect({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 dbca1b5d..242bb846 100644 --- a/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js +++ b/js/components/whitelabel/prize/simple_prize/actions/prize_actions.js @@ -16,7 +16,7 @@ class PrizeActions { .fetch() .then((res) => { this.actions.updatePrize({ - prize: res.prize + prize: res }); }) .catch((err) => { diff --git a/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js b/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js index 8e602012..972b3fac 100644 --- a/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js +++ b/js/components/whitelabel/prize/simple_prize/components/prize_piece_list.js @@ -48,7 +48,9 @@ let PrizePieceList = React.createClass({ }, getButtonSubmit() { - if (this.state.prize && this.state.prize.active && !this.state.currentUser.is_jury){ + const { currentUser } = this.state; + if (this.state.prize && this.state.prize.active && + !currentUser.is_jury && !currentUser.is_admin && !currentUser.is_judge){ return (