1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-30 21:52:08 +02:00

Merge pull request #202 from ascribe/upgrade-react

Upgrade react and react-router
This commit is contained in:
Brett Sun 2016-06-10 10:30:33 +02:00 committed by GitHub
commit 6eeac85012
100 changed files with 990 additions and 1021 deletions

View File

@ -4,16 +4,20 @@ import 'classlist-polyfill';
import 'isomorphic-fetch'; import 'isomorphic-fetch';
import React from 'react'; import React from 'react';
import { Router } from 'react-router'; import ReactDOM from 'react-dom';
import Router from 'react-router/es6/Router';
import AppResolver from './app_resolver'; import AppResolver from './app_resolver';
import history from './history'; import history from './history';
import AppConstants from './constants/application_constants';
import { getDefaultSubdomainSettings, getSubdomainSettings } from './utils/constants_utils'; import { getDefaultSubdomainSettings, getSubdomainSettings } from './utils/constants_utils';
import { initLogging } from './utils/error_utils'; import { initLogging } from './utils/error_utils';
import { getSubdomain } from './utils/general_utils'; import { getSubdomain } from './utils/general_utils';
import requests from './utils/requests'; import requests from './utils/requests';
// FIXME: rename these event actions // FIXME: rename these event actions
import EventActions from './actions/event_actions'; import EventActions from './actions/event_actions';
@ -50,10 +54,22 @@ const AppGateway = {
// `history.listen` is called on every route change, which is perfect for routeDidChange // `history.listen` is called on every route change, which is perfect for routeDidChange
// events. // events.
history.listen(EventActions.routeDidChange); // For history <= 3.0, history.listen will synchronously invoke the callback once
// immediately after registration.
history.listen((location) => {
const { locationQueue } = history;
locationQueue.unshift(location);
// Limit the number of locations to keep in memory to avoid too much memory usage
if (locationQueue.length > AppConstants.locationThreshold) {
locationQueue.length = AppConstants.locationThreshold;
}
EventActions.routeDidChange(location);
});
// Adds a client specific class to the body for whitelabel styling // Adds a client specific class to the body for whitelabel styling
window.document.body.classList.add(`client--${settings.subdomain}`); window.document.body.classList.add(`client--${settings.subdomain || 'ascribe'}`);
AppResolver AppResolver
.resolve(settings) .resolve(settings)
@ -70,7 +86,7 @@ const AppGateway = {
} }
}); });
React.render(( ReactDOM.render((
<Router history={history}> <Router history={history}>
{redirectRoute} {redirectRoute}
{routes} {routes}

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Redirect } from 'react-router'; import Redirect from 'react-router/es6/Redirect';
import Routes from './routes'; import Routes from './routes';

View File

@ -1,8 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import classNames from 'classnames';
import { History } from 'react-router';
import UserActions from '../actions/user_actions'; import UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store'; import UserStore from '../stores/user_store';
@ -11,8 +7,7 @@ import WhitelabelActions from '../actions/whitelabel_actions';
import WhitelabelStore from '../stores/whitelabel_store'; import WhitelabelStore from '../stores/whitelabel_store';
import GlobalNotification from './global_notification'; import GlobalNotification from './global_notification';
import { currentUserShape, locationShape, whitelabelShape } from './prop_types';
import AppConstants from '../constants/application_constants';
import { mergeOptions } from '../utils/general_utils'; import { mergeOptions } from '../utils/general_utils';
@ -23,11 +18,16 @@ export default function AppBase(App) {
propTypes: { propTypes: {
children: React.PropTypes.element.isRequired, children: React.PropTypes.element.isRequired,
history: React.PropTypes.object.isRequired, location: locationShape.isRequired,
location: React.PropTypes.object.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
}, },
childContextTypes: {
currentUser: currentUserShape,
location: locationShape,
whitelabel: whitelabelShape
},
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
UserStore.getState(), UserStore.getState(),
@ -35,7 +35,15 @@ export default function AppBase(App) {
); );
}, },
mixins: [History], getChildContext() {
const { currentUser, whitelabel } = this.state;
return {
currentUser,
whitelabel,
location: this.props.location
};
},
componentDidMount() { componentDidMount() {
UserStore.listen(this.onChange); UserStore.listen(this.onChange);
@ -43,18 +51,6 @@ export default function AppBase(App) {
UserActions.fetchCurrentUser(); UserActions.fetchCurrentUser();
WhitelabelActions.fetchWhitelabel(); WhitelabelActions.fetchWhitelabel();
this.history.locationQueue.push(this.props.location);
},
componentWillReceiveProps(nextProps) {
const { locationQueue } = this.history;
locationQueue.unshift(nextProps.location);
// Limit the number of locations to keep in memory to avoid too much memory usage
if (locationQueue.length > AppConstants.locationThreshold) {
locationQueue.length = AppConstants.locationThreshold;
}
}, },
componentWillUnmount() { componentWillUnmount() {
@ -68,7 +64,6 @@ export default function AppBase(App) {
render() { render() {
const { routes } = this.props; const { routes } = this.props;
const { currentUser, whitelabel } = this.state;
// The second element of the routes prop given to us by react-router is always the // The second element of the routes prop given to us by react-router is always the
// active second-level component object (ie. after App). // active second-level component object (ie. after App).
@ -78,13 +73,11 @@ export default function AppBase(App) {
<div> <div>
<App <App
{...this.props} {...this.props}
activeRoute={activeRoute} activeRoute={activeRoute} />
currentUser={currentUser}
whitelabel={whitelabel} />
<GlobalNotification /> <GlobalNotification />
<div id="modal" className="container" /> <div id="modal" className="container" />
</div> </div>
); );
} }
}); });
}; }

View File

@ -1,34 +0,0 @@
'use strict';
import React from 'react';
import { omitFromObject } from '../utils/general_utils';
const AppRouteWrapper = React.createClass({
propTypes: {
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]).isRequired
},
render() {
const propsToPropagate = omitFromObject(this.props, ['children']);
let childrenWithProps = this.props.children;
// If there are more props given, propagate them into the child routes by cloning the routes
if (Object.keys(propsToPropagate).length) {
childrenWithProps = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, propsToPropagate);
});
}
return (
<div className="container ascribe-body">
{childrenWithProps}
</div>
);
}
});
export default AppRouteWrapper;

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import Link from 'react-router/es6/Link';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import Link from 'react-router/es6/Link';
let AccordionListItem = React.createClass({ let AccordionListItem = React.createClass({

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router';
import AccordionListItem from './accordion_list_item'; import AccordionListItem from './accordion_list_item';
import AccordionListItemThumbnailPlacholder from './accordion_list_item_thumbnail_placeholder'; import AccordionListItemThumbnailPlacholder from './accordion_list_item_thumbnail_placeholder';

View File

@ -19,24 +19,27 @@ import AccordionListItemPiece from './accordion_list_item_piece';
import AccordionListItemEditionWidget from './accordion_list_item_edition_widget'; import AccordionListItemEditionWidget from './accordion_list_item_edition_widget';
import CreateEditionsForm from '../ascribe_forms/create_editions_form'; import CreateEditionsForm from '../ascribe_forms/create_editions_form';
import AclProxy from '../acl_proxy'; import AclProxy from '../acl_proxy';
import withContext from '../context/with_context';
import { whitelabelShape } from '../prop_types';
import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
let AccordionListItemWallet = React.createClass({ let AccordionListItemWallet = React.createClass({
propTypes: { propTypes: {
content: React.PropTypes.object.isRequired, content: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
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
]), ]),
className: React.PropTypes.string, className: React.PropTypes.string,
thumbnailPlaceholder: React.PropTypes.func thumbnailPlaceholder: React.PropTypes.func,
// Injected through HOCs
whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
getInitialState() { getInitialState() {
@ -162,4 +165,4 @@ let AccordionListItemWallet = React.createClass({
} }
}); });
export default AccordionListItemWallet; export default withContext(AccordionListItemWallet, 'whitelabel');

View File

@ -1,41 +1,28 @@
'use strict';
import React from 'react'; import React from 'react';
import AppBase from './app_base'; import AppBase from './app_base';
import AppRouteWrapper from './app_route_wrapper';
import Footer from './footer';
import Header from './header'; import Header from './header';
let AscribeApp = React.createClass({ const AscribeApp = React.createClass({
propTypes: { propTypes: {
activeRoute: React.PropTypes.object.isRequired, activeRoute: React.PropTypes.object.isRequired,
children: React.PropTypes.element.isRequired, children: React.PropTypes.element.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
// Provided from AppBase
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object
}, },
render() { render() {
const { activeRoute, children, currentUser, routes, whitelabel } = this.props; const { activeRoute, children, routes } = this.props;
const Footer = activeRoute && activeRoute.footer; const RouteFooterType = activeRoute && activeRoute.footer;
return ( return (
<div className="ascribe-app ascribe-default-app"> <div className="ascribe-app ascribe-default-app">
<Header <Header routes={routes} />
currentUser={currentUser} <div className="container ascribe-body">
routes={routes}
whitelabel={whitelabel} />
<AppRouteWrapper
currentUser={currentUser}
whitelabel={whitelabel}>
{/* Routes are injected here */} {/* Routes are injected here */}
{children} {children}
</AppRouteWrapper> </div>
{Footer ? <Footer /> : null} {RouteFooterType ? <RouteFooterType /> : null}
</div> </div>
); );
} }

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import React from 'react/addons'; import React from 'react';
import ConsignButton from './acls/consign_button'; import ConsignButton from './acls/consign_button';
import EmailButton from './acls/email_button'; import EmailButton from './acls/email_button';
@ -14,7 +14,6 @@ import { selectFromObject } from '../../utils/general_utils';
let AclButtonList = React.createClass({ let AclButtonList = React.createClass({
propTypes: { propTypes: {
availableAcls: React.PropTypes.object.isRequired, availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleSuccess: React.PropTypes.func.isRequired, handleSuccess: React.PropTypes.func.isRequired,
pieceOrEditions: React.PropTypes.oneOfType([ pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
@ -52,7 +51,7 @@ let AclButtonList = React.createClass({
handleResize() { handleResize() {
this.setState({ this.setState({
buttonListSize: this.refs.buttonList.getDOMNode().offsetWidth buttonListSize: this.refs.buttonList.offsetWidth
}); });
}, },
@ -61,7 +60,7 @@ let AclButtonList = React.createClass({
const { buttonListSize } = this.state; const { buttonListSize } = this.state;
return React.Children.map(children, (child) => { return React.Children.map(children, (child) => {
return React.addons.cloneWithProps(child, { buttonListSize }); return React.cloneElement(child, { buttonListSize });
}); });
}, },
@ -69,13 +68,11 @@ let AclButtonList = React.createClass({
const { availableAcls, const { availableAcls,
buttonsStyle, buttonsStyle,
className, className,
currentUser,
handleSuccess, handleSuccess,
pieceOrEditions } = this.props; pieceOrEditions } = this.props;
const buttonProps = selectFromObject(this.props, [ const buttonProps = selectFromObject(this.props, [
'availableAcls', 'availableAcls',
'currentUser',
'handleSuccess', 'handleSuccess',
'pieceOrEditions' 'pieceOrEditions'
]); ]);

View File

@ -43,7 +43,7 @@ let AclInformation = React.createClass({
if(aim) { if(aim) {
if(aim === 'form') { if(aim === 'form') {
return ( return (
<p> <p key={title}>
<span className="info"> <span className="info">
{replaceSubstringAtIndex(info.slice(2), 's ', ' ')} {replaceSubstringAtIndex(info.slice(2), 's ', ' ')}
</span> </span>
@ -55,7 +55,7 @@ let AclInformation = React.createClass({
} }
else if(aim === 'button') { else if(aim === 'button') {
return ( return (
<p> <p key={title}>
<span className="title"> <span className="title">
{title} {title}
</span> </span>

View File

@ -31,7 +31,6 @@ export default function AclButton({ action, displayName, title, tooltip }) {
buttonAcceptName: React.PropTypes.string, buttonAcceptName: React.PropTypes.string,
buttonAcceptClassName: React.PropTypes.string, buttonAcceptClassName: React.PropTypes.string,
currentUser: React.PropTypes.object,
email: React.PropTypes.string, email: React.PropTypes.string,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },
@ -43,7 +42,6 @@ export default function AclButton({ action, displayName, title, tooltip }) {
render() { render() {
const { availableAcls, const { availableAcls,
buttonAcceptClassName, buttonAcceptClassName,
currentUser,
email, email,
pieceOrEditions, pieceOrEditions,
handleSuccess } = this.props; handleSuccess } = this.props;
@ -63,7 +61,6 @@ export default function AclButton({ action, displayName, title, tooltip }) {
title={title}> title={title}>
<AclFormFactory <AclFormFactory
action={action} action={action}
currentUser={currentUser}
email={email} email={email}
pieceOrEditions={pieceOrEditions} pieceOrEditions={pieceOrEditions}
showNotification /> showNotification />

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import React from 'react/addons'; import React from 'react';
import update from 'react-addons-update';
const { string, object } = React.PropTypes; const { string, object } = React.PropTypes;
@ -30,7 +31,7 @@ const FormSubmitButton = React.createClass({
}, },
setReadyStateForKey(key, state) { setReadyStateForKey(key, state) {
const readyStates = React.addons.update(this.state.readyStates, { [key]: { $set: state } }); const readyStates = update(this.state.readyStates, { [key]: { $set: state } });
this.setState({ readyStates }); this.setState({ readyStates });
}, },

View File

@ -5,21 +5,26 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import ModalWrapper from '../ascribe_modal/modal_wrapper'; import ModalWrapper from '../ascribe_modal/modal_wrapper';
import UnConsignRequestForm from './../ascribe_forms/form_unconsign_request'; import UnConsignRequestForm from '../ascribe_forms/form_unconsign_request';
import { getLangText } from '../../utils/lang_utils.js'; import withContext from '../context/with_context';
import { currentUserShape } from '../prop_types';
import { getLangText } from '../../utils/lang_utils';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
let UnConsignRequestButton = React.createClass({ const UnConsignRequestButton = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired, edition: React.PropTypes.object.isRequired,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func,
// Injected through HOCs
currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
render: function () { render() {
const { currentUser, edition, handleSuccess } = this.props; const { currentUser, edition, handleSuccess } = this.props;
return ( return (
<ModalWrapper <ModalWrapper
@ -45,5 +50,4 @@ ${currentUser.username}`
} }
}); });
export default UnConsignRequestButton; export default withContext(UnConsignRequestButton, 'currentUser');

View File

@ -1,15 +1,13 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import Link from 'react-router/es6/Link';
import Moment from 'moment'; import Moment from 'moment';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import EditionActions from '../../actions/edition_actions';
import DetailProperty from './detail_property'; import DetailProperty from './detail_property';
import EditionActionPanel from './edition_action_panel'; import EditionActionPanel from './edition_action_panel';
import FurtherDetails from './further_details'; import FurtherDetails from './further_details';
@ -24,6 +22,7 @@ import Form from '../ascribe_forms/form';
import Property from '../ascribe_forms/property'; import Property from '../ascribe_forms/property';
import AclProxy from '../acl_proxy'; import AclProxy from '../acl_proxy';
import withContext from '../context/with_context';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
@ -34,16 +33,17 @@ import { getLangText } from '../../utils/lang_utils';
/** /**
* This is the component that implements display-specific functionality * This is the component that implements display-specific functionality
*/ */
let Edition = React.createClass({ const Edition = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired, edition: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
coaError: React.PropTypes.object, coaError: React.PropTypes.object,
furtherDetailsType: React.PropTypes.func, furtherDetailsType: React.PropTypes.func,
loadEdition: React.PropTypes.func loadEdition: React.PropTypes.func,
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired // eslint-disable-line react/sort-prop-types
}, },
getDefaultProps() { getDefaultProps() {
@ -53,20 +53,20 @@ let Edition = React.createClass({
}, },
render() { render() {
const { actionPanelButtonListType, const {
actionPanelButtonListType,
coaError, coaError,
currentUser,
edition, edition,
furtherDetailsType: FurtherDetailsType, isLoggedIn,
loadEdition, loadEdition,
whitelabel } = this.props; furtherDetailsType: FurtherDetailsType
} = this.props;
return ( return (
<Row> <Row>
<Col md={6} className="ascribe-print-col-left"> <Col md={6} className="ascribe-print-col-left">
<MediaContainer <MediaContainer
content={edition} content={edition}
currentUser={currentUser}
refreshObject={loadEdition} /> refreshObject={loadEdition} />
</Col> </Col>
<Col md={6} className="ascribe-edition-details ascribe-print-col-right"> <Col md={6} className="ascribe-edition-details ascribe-print-col-right">
@ -82,9 +82,7 @@ let Edition = React.createClass({
<EditionSummary <EditionSummary
actionPanelButtonListType={actionPanelButtonListType} actionPanelButtonListType={actionPanelButtonListType}
edition={edition} edition={edition}
currentUser={currentUser} handleSuccess={loadEdition} />
handleSuccess={loadEdition}
whitelabel={whitelabel} />
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Certificate of Authenticity')} title={getLangText('Certificate of Authenticity')}
show={edition.acl.acl_coa === true}> show={edition.acl.acl_coa === true}>
@ -114,7 +112,7 @@ let Edition = React.createClass({
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Notes')} title={getLangText('Notes')}
show={!!(currentUser.username || edition.acl.acl_edit || edition.public_note)}> show={!!(isLoggedIn || edition.acl.acl_edit || edition.public_note)}>
<Note <Note
id={() => {return {'bitcoin_id': edition.bitcoin_id}; }} id={() => {return {'bitcoin_id': edition.bitcoin_id}; }}
label={getLangText('Personal note (private)')} label={getLangText('Personal note (private)')}
@ -122,8 +120,7 @@ let Edition = React.createClass({
placeholder={getLangText('Enter your comments ...')} placeholder={getLangText('Enter your comments ...')}
editable={true} editable={true}
successMessage={getLangText('Private note saved')} successMessage={getLangText('Private note saved')}
url={ApiUrls.note_private_edition} url={ApiUrls.note_private_edition} />
currentUser={currentUser} />
<Note <Note
id={() => {return {'bitcoin_id': edition.bitcoin_id}; }} id={() => {return {'bitcoin_id': edition.bitcoin_id}; }}
label={getLangText('Personal note (public)')} label={getLangText('Personal note (public)')}
@ -132,8 +129,7 @@ let Edition = React.createClass({
editable={!!edition.acl.acl_edit} editable={!!edition.acl.acl_edit}
show={!!(edition.public_note || edition.acl.acl_edit)} show={!!(edition.public_note || edition.acl.acl_edit)}
successMessage={getLangText('Public edition note saved')} successMessage={getLangText('Public edition note saved')}
url={ApiUrls.note_public_edition} url={ApiUrls.note_public_edition} />
currentUser={currentUser} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Further Details')} title={getLangText('Further Details')}
@ -155,14 +151,15 @@ let Edition = React.createClass({
}); });
let EditionSummary = React.createClass({ let EditionSummary = withContext(React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired, edition: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func,
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired, // eslint-disable-line react/sort-prop-types
}, },
getStatus() { getStatus() {
@ -176,7 +173,12 @@ let EditionSummary = React.createClass({
}, },
render() { render() {
const { actionPanelButtonListType, currentUser, edition, handleSuccess, whitelabel } = this.props; const {
actionPanelButtonListType,
edition,
handleSuccess,
isLoggedIn,
} = this.props;
return ( return (
<div className="ascribe-detail-header"> <div className="ascribe-detail-header">
@ -200,23 +202,21 @@ let EditionSummary = React.createClass({
no more than 1 key, we're hiding the `DetailProperty` actions as otherwise no more than 1 key, we're hiding the `DetailProperty` actions as otherwise
`AclInformation` would show up `AclInformation` would show up
*/} */}
<AclProxy show={currentUser.email && Object.keys(edition.acl).length > 1}> <AclProxy show={isLoggedIn && Object.keys(edition.acl).length > 1}>
<DetailProperty <DetailProperty
label={getLangText('ACTIONS')} label={getLangText('ACTIONS')}
className="hidden-print"> className="hidden-print">
<EditionActionPanel <EditionActionPanel
actionPanelButtonListType={actionPanelButtonListType} actionPanelButtonListType={actionPanelButtonListType}
currentUser={currentUser}
edition={edition} edition={edition}
handleSuccess={handleSuccess} handleSuccess={handleSuccess} />
whitelabel={whitelabel} />
</DetailProperty> </DetailProperty>
</AclProxy> </AclProxy>
<hr/> <hr/>
</div> </div>
); );
} }
}); }), 'isLoggedIn');
let CoaDetails = React.createClass({ let CoaDetails = React.createClass({
@ -354,9 +354,9 @@ let SpoolDetails = React.createClass({
<pre className="ascribe-pre">{ownerAddress}</pre> <pre className="ascribe-pre">{ownerAddress}</pre>
</Property> </Property>
<hr /> <hr />
</Form>); </Form>
);
} }
}); });
export default Edition; export default withContext(Edition, 'isLoggedIn');

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
@ -11,20 +10,21 @@ import EditionListActions from '../../actions/edition_list_actions';
import PieceListActions from '../../actions/piece_list_actions'; import PieceListActions from '../../actions/piece_list_actions';
import PieceListStore from '../../stores/piece_list_store'; import PieceListStore from '../../stores/piece_list_store';
import Form from './../ascribe_forms/form';
import Property from './../ascribe_forms/property';
import ListRequestActions from './../ascribe_forms/list_form_request_actions';
import AclButtonList from './../ascribe_buttons/acl_button_list';
import UnConsignRequestButton from './../ascribe_buttons/unconsign_request_button';
import DeleteButton from '../ascribe_buttons/delete_button';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import Form from './../ascribe_forms/form';
import ListRequestActions from './../ascribe_forms/list_form_request_actions';
import Property from './../ascribe_forms/property';
import AclButtonList from './../ascribe_buttons/acl_button_list';
import AclInformation from '../ascribe_buttons/acl_information'; import AclInformation from '../ascribe_buttons/acl_information';
import DeleteButton from '../ascribe_buttons/delete_button';
import UnConsignRequestButton from './../ascribe_buttons/unconsign_request_button';
import AclProxy from '../acl_proxy'; import AclProxy from '../acl_proxy';
import withContext from '../context/with_context';
import { routerShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
@ -34,17 +34,16 @@ import { getLangText } from '../../utils/lang_utils';
A component that handles all the actions inside of the edition detail A component that handles all the actions inside of the edition detail
handleSuccess requires a loadEdition action (could be refactored) handleSuccess requires a loadEdition action (could be refactored)
*/ */
let EditionActionPanel = React.createClass({ const EditionActionPanel = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired, edition: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func,
},
mixins: [History], // Injected through HOCs
router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
},
getDefaultProps() { getDefaultProps() {
return { return {
@ -77,7 +76,7 @@ let EditionActionPanel = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success'); const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection'); this.props.router.push('/collection');
}, },
refreshCollection() { refreshCollection() {
@ -101,15 +100,11 @@ let EditionActionPanel = React.createClass({
}, },
render() { render() {
const { actionPanelButtonListType: ActionPanelButtonListType, const { edition, actionPanelButtonListType: ActionPanelButtonListType } = this.props;
currentUser,
edition,
whitelabel } = this.props;
if (edition.notifications && edition.notifications.length) { if (edition.notifications && edition.notifications.length) {
return ( return (
<ListRequestActions <ListRequestActions
currentUser={currentUser}
notifications={edition.notifications} notifications={edition.notifications}
pieceOrEditions={[edition]} pieceOrEditions={[edition]}
handleSuccess={this.handleSuccess} />); handleSuccess={this.handleSuccess} />);
@ -120,10 +115,8 @@ let EditionActionPanel = React.createClass({
<ActionPanelButtonListType <ActionPanelButtonListType
availableAcls={edition.acl} availableAcls={edition.acl}
className="ascribe-button-list" className="ascribe-button-list"
currentUser={currentUser}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
pieceOrEditions={[edition]} pieceOrEditions={[edition]} >
whitelabel={whitelabel}>
<AclProxy <AclProxy
aclObject={edition.acl} aclObject={edition.acl}
aclName="acl_withdraw_transfer"> aclName="acl_withdraw_transfer">
@ -170,7 +163,6 @@ let EditionActionPanel = React.createClass({
aclObject={edition.acl} aclObject={edition.acl}
aclName="acl_request_unconsign"> aclName="acl_request_unconsign">
<UnConsignRequestButton <UnConsignRequestButton
currentUser={currentUser}
edition={edition} edition={edition}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
</AclProxy> </AclProxy>
@ -189,4 +181,4 @@ let EditionActionPanel = React.createClass({
} }
}); });
export default EditionActionPanel; export default withContext(EditionActionPanel, 'router');

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import ReactError from '../../mixins/react_error'; import ReactError from '../../mixins/react_error';
import { ResourceNotFoundError } from '../../models/errors'; import { ResourceNotFoundError } from '../../models/errors';
@ -25,16 +24,11 @@ let EditionContainer = React.createClass({
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
furtherDetailsType: React.PropTypes.func, furtherDetailsType: React.PropTypes.func,
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router // Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
mixins: [History, ReactError], mixins: [ReactError],
getInitialState() { getInitialState() {
return EditionStore.getInitialState(); return EditionStore.getInitialState();
@ -77,7 +71,7 @@ let EditionContainer = React.createClass({
}, },
render() { render() {
const { actionPanelButtonListType, currentUser, furtherDetailsType, whitelabel } = this.props; const { actionPanelButtonListType, furtherDetailsType } = this.props;
const { edition, coaMeta } = this.state; const { edition, coaMeta } = this.state;
if (edition.id) { if (edition.id) {
@ -87,11 +81,9 @@ let EditionContainer = React.createClass({
<Edition <Edition
actionPanelButtonListType={actionPanelButtonListType} actionPanelButtonListType={actionPanelButtonListType}
coaError={coaMeta.err} coaError={coaMeta.err}
currentUser={currentUser}
edition={edition} edition={edition}
furtherDetailsType={furtherDetailsType} furtherDetailsType={furtherDetailsType}
loadEdition={this.loadEdition} loadEdition={this.loadEdition} />
whitelabel={whitelabel} />
); );
} else { } else {
return ( return (

View File

@ -17,12 +17,13 @@ import CollapsibleButton from './../ascribe_collapsible/collapsible_button';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import AclProxy from '../acl_proxy'; import AclProxy from '../acl_proxy';
import withContext from '../context/with_context';
import { currentUserShape } from '../prop_types';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils';
import { extractFileExtensionFromUrl } from '../../utils/file_utils'; import { extractFileExtensionFromUrl } from '../../utils/file_utils';
import { getLangText } from '../../utils/lang_utils';
const EMBED_IFRAME_HEIGHT = { const EMBED_IFRAME_HEIGHT = {
@ -36,7 +37,8 @@ let MediaContainer = React.createClass({
content: React.PropTypes.object.isRequired, content: React.PropTypes.object.isRequired,
refreshObject: React.PropTypes.func.isRequired, refreshObject: React.PropTypes.func.isRequired,
currentUser: React.PropTypes.object // Injected through HOCs
currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
getInitialState() { getInitialState() {
@ -118,7 +120,7 @@ let MediaContainer = React.createClass({
// the information in content will be updated if a user updates their username. // the information in content will be updated if a user updates their username.
// We also force uniqueness of usernames, so this check is safe to dtermine if the // We also force uniqueness of usernames, so this check is safe to dtermine if the
// content was registered by the current user. // content was registered by the current user.
const didUserRegisterContent = currentUser && (currentUser.username === content.user_registered); const didUserRegisterContent = currentUser.username === content.user_registered;
const thumbnail = content.thumbnail.thumbnail_sizes && content.thumbnail.thumbnail_sizes['600x600'] ? content.thumbnail.thumbnail_sizes['600x600'] const thumbnail = content.thumbnail.thumbnail_sizes && content.thumbnail.thumbnail_sizes['600x600'] ? content.thumbnail.thumbnail_sizes['600x600']
: content.thumbnail.url_safe; : content.thumbnail.url_safe;
@ -181,4 +183,4 @@ let MediaContainer = React.createClass({
} }
}); });
export default MediaContainer; export default withContext(MediaContainer, 'currentUser');

View File

@ -5,15 +5,16 @@ import React from 'react';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import Form from './../ascribe_forms/form'; import Form from '../ascribe_forms/form';
import Property from './../ascribe_forms/property'; import Property from '../ascribe_forms/property';
import InputTextAreaToggable from './../ascribe_forms/input_textarea_toggable'; import InputTextAreaToggable from '../ascribe_forms/input_textarea_toggable';
import withContext from '../context/with_context';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let Note = React.createClass({ let Note = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
id: React.PropTypes.func.isRequired, id: React.PropTypes.func.isRequired,
url: React.PropTypes.string.isRequired, url: React.PropTypes.string.isRequired,
@ -22,7 +23,10 @@ let Note = React.createClass({
label: React.PropTypes.string, label: React.PropTypes.string,
placeholder: React.PropTypes.string, placeholder: React.PropTypes.string,
show: React.PropTypes.bool, show: React.PropTypes.bool,
successMessage: React.PropTypes.string successMessage: React.PropTypes.string,
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired // eslint-disable-line react/sort-prop-types
}, },
getDefaultProps() { getDefaultProps() {
@ -40,9 +44,18 @@ let Note = React.createClass({
}, },
render() { render() {
const { currentUser, defaultValue, editable, id, label, placeholder, show, url } = this.props; const {
defaultValue,
editable,
id,
isLoggedIn,
label,
placeholder,
show,
url
} = this.props;
if ((currentUser.username && editable || !editable) && show) { if ((isLoggedIn && editable || !editable) && show) {
return ( return (
<Form <Form
url={url} url={url}
@ -66,4 +79,4 @@ let Note = React.createClass({
} }
}); });
export default Note; export default withContext(Note, 'isLoggedIn');

View File

@ -18,7 +18,6 @@ let Piece = React.createClass({
piece: React.PropTypes.object.isRequired, piece: React.PropTypes.object.isRequired,
buttons: React.PropTypes.object, buttons: React.PropTypes.object,
currentUser: React.PropTypes.object,
header: React.PropTypes.object, header: React.PropTypes.object,
subheader: React.PropTypes.object, subheader: React.PropTypes.object,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
@ -32,14 +31,13 @@ let Piece = React.createClass({
}, },
render() { render() {
const { buttons, children, currentUser, header, piece, subheader } = this.props; const { buttons, children, header, piece, subheader } = this.props;
return ( return (
<Row> <Row>
<Col md={6} className="ascribe-print-col-left"> <Col md={6} className="ascribe-print-col-left">
<MediaContainer <MediaContainer
content={piece} content={piece}
currentUser={currentUser}
refreshObject={this.updatePiece} /> refreshObject={this.updatePiece} />
</Col> </Col>
<Col md={6} className="ascribe-edition-details ascribe-print-col-right"> <Col md={6} className="ascribe-edition-details ascribe-print-col-right">

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import Moment from 'moment'; import Moment from 'moment';
import ReactError from '../../mixins/react_error'; import ReactError from '../../mixins/react_error';
@ -25,7 +24,7 @@ import LicenseDetail from './license_detail';
import Note from './note'; import Note from './note';
import Piece from './piece'; 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 AclInformation from '../ascribe_buttons/acl_information';
import CreateEditionsButton from '../ascribe_buttons/create_editions_button'; import CreateEditionsButton from '../ascribe_buttons/create_editions_button';
import DeleteButton from '../ascribe_buttons/delete_button'; import DeleteButton from '../ascribe_buttons/delete_button';
@ -36,31 +35,32 @@ import CreateEditionsForm from '../ascribe_forms/create_editions_form';
import ListRequestActions from '../ascribe_forms/list_form_request_actions'; import ListRequestActions from '../ascribe_forms/list_form_request_actions';
import AclProxy from '../acl_proxy'; import AclProxy from '../acl_proxy';
import AscribeSpinner from '../ascribe_spinner';
import withContext from '../context/with_context';
import { routerShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner';
import { setDocumentTitle } from '../../utils/dom_utils';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { setDocumentTitle } from '../../utils/dom_utils';
/** /**
* This is the component that implements resource/data specific functionality * This is the component that implements resource/data specific functionality
*/ */
let PieceContainer = React.createClass({ const PieceContainer = React.createClass({
propTypes: { propTypes: {
furtherDetailsType: React.PropTypes.func, furtherDetailsType: React.PropTypes.func,
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, isLoggedIn: React.PropTypes.bool.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: React.PropTypes.object, router: routerShape.isRequired, // eslint-disable-line react/sort-prop-types
// Provided from router // Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
mixins: [History, ReactError], mixins: [ReactError],
getDefaultProps() { getDefaultProps() {
return { return {
@ -164,7 +164,7 @@ let PieceContainer = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success'); const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection'); this.props.router.push('/collection');
}, },
getCreateEditionsDialog() { getCreateEditionsDialog() {
@ -207,12 +207,11 @@ let PieceContainer = React.createClass({
getActions() { getActions() {
const { piece } = this.state; const { piece } = this.state;
const { currentUser } = this.props; const { isLoggedIn } = this.props;
if (piece.notifications && piece.notifications.length > 0) { if (piece.notifications && piece.notifications.length > 0) {
return ( return (
<ListRequestActions <ListRequestActions
currentUser={currentUser}
handleSuccess={this.loadPiece} handleSuccess={this.loadPiece}
notifications={piece.notifications} notifications={piece.notifications}
pieceOrEditions={piece} /> pieceOrEditions={piece} />
@ -220,7 +219,7 @@ let PieceContainer = React.createClass({
} else { } else {
return ( return (
<AclProxy <AclProxy
show={currentUser && currentUser.email && Object.keys(piece.acl).length > 1}> show={isLoggedIn && Object.keys(piece.acl).length > 1}>
{/* {/*
`acl_view` is always available in `edition.acl`, therefore if it has `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 no more than 1 key, we're hiding the `DetailProperty` actions as otherwise
@ -232,7 +231,6 @@ let PieceContainer = React.createClass({
<AclButtonList <AclButtonList
availableAcls={piece.acl} availableAcls={piece.acl}
className="ascribe-button-list" className="ascribe-button-list"
currentUser={currentUser}
pieceOrEditions={piece} pieceOrEditions={piece}
handleSuccess={this.loadPiece}> handleSuccess={this.loadPiece}>
<CreateEditionsButton <CreateEditionsButton
@ -257,7 +255,7 @@ let PieceContainer = React.createClass({
}, },
render() { render() {
const { currentUser, furtherDetailsType: FurtherDetailsType } = this.props; const { isLoggedIn, furtherDetailsType: FurtherDetailsType } = this.props;
const { piece } = this.state; const { piece } = this.state;
if (piece.id) { if (piece.id) {
@ -266,7 +264,6 @@ let PieceContainer = React.createClass({
return ( return (
<Piece <Piece
piece={piece} piece={piece}
currentUser={currentUser}
header={ header={
<div className="ascribe-detail-header"> <div className="ascribe-detail-header">
<hr className="hidden-print" style={{marginTop: 0}} /> <hr className="hidden-print" style={{marginTop: 0}} />
@ -297,7 +294,7 @@ let PieceContainer = React.createClass({
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Notes')} title={getLangText('Notes')}
show={!!(currentUser.username || piece.acl.acl_edit || piece.public_note)}> show={!!(isLoggedIn || piece.acl.acl_edit || piece.public_note)}>
<Note <Note
id={this.getId} id={this.getId}
label={getLangText('Personal note (private)')} label={getLangText('Personal note (private)')}
@ -305,8 +302,7 @@ let PieceContainer = React.createClass({
placeholder={getLangText('Enter your comments ...')} placeholder={getLangText('Enter your comments ...')}
editable={true} editable={true}
successMessage={getLangText('Private note saved')} successMessage={getLangText('Private note saved')}
url={ApiUrls.note_private_piece} url={ApiUrls.note_private_piece} />
currentUser={currentUser} />
<Note <Note
id={this.getId} id={this.getId}
label={getLangText('Personal note (public)')} label={getLangText('Personal note (public)')}
@ -315,8 +311,7 @@ let PieceContainer = React.createClass({
editable={!!piece.acl.acl_edit} editable={!!piece.acl.acl_edit}
show={!!(piece.public_note || piece.acl.acl_edit)} show={!!(piece.public_note || piece.acl.acl_edit)}
successMessage={getLangText('Public note saved')} successMessage={getLangText('Public note saved')}
url={ApiUrls.note_public_piece} url={ApiUrls.note_public_piece} />
currentUser={currentUser} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Further Details')} title={getLangText('Further Details')}
@ -344,4 +339,4 @@ let PieceContainer = React.createClass({
} }
}); });
export default PieceContainer; export default withContext(PieceContainer, 'isLoggedIn', 'router');

View File

@ -2,6 +2,9 @@
import React from 'react'; import React from 'react';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import ConsignForm from '../ascribe_forms/form_consign'; import ConsignForm from '../ascribe_forms/form_consign';
import UnConsignForm from '../ascribe_forms/form_unconsign'; import UnConsignForm from '../ascribe_forms/form_unconsign';
import TransferForm from '../ascribe_forms/form_transfer'; import TransferForm from '../ascribe_forms/form_transfer';
@ -9,12 +12,12 @@ import LoanForm from '../ascribe_forms/form_loan';
import LoanRequestAnswerForm from '../ascribe_forms/form_loan_request_answer'; import LoanRequestAnswerForm from '../ascribe_forms/form_loan_request_answer';
import ShareForm from '../ascribe_forms/form_share_email'; import ShareForm from '../ascribe_forms/form_share_email';
import withContext from '../context/with_context';
import { currentUserShape } from '../prop_types';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import { getAclFormMessage, getAclFormDataId } from '../../utils/form_utils'; import { getAclFormMessage, getAclFormDataId } from '../../utils/form_utils';
let AclFormFactory = React.createClass({ let AclFormFactory = React.createClass({
@ -26,12 +29,14 @@ let AclFormFactory = React.createClass({
]).isRequired, ]).isRequired,
autoFocusProperty: React.PropTypes.string, autoFocusProperty: React.PropTypes.string,
currentUser: React.PropTypes.object,
email: React.PropTypes.string, email: React.PropTypes.string,
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
message: React.PropTypes.string,
labels: React.PropTypes.object, labels: React.PropTypes.object,
showNotification: React.PropTypes.bool message: React.PropTypes.string,
showNotification: React.PropTypes.bool,
// Injected through HOCs
currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
isPiece() { isPiece() {
@ -54,21 +59,23 @@ let AclFormFactory = React.createClass({
}, },
render() { render() {
const { action, const {
action,
autoFocusProperty, autoFocusProperty,
pieceOrEditions, pieceOrEditions,
currentUser,
email, email,
message, message,
labels, labels,
handleSuccess, handleSuccess,
showNotification } = this.props; showNotification,
currentUser: { username: senderName }
} = this.props;
const formMessage = message || getAclFormMessage({ const formMessage = message || getAclFormMessage({
senderName,
aclName: action, aclName: action,
entities: pieceOrEditions, entities: pieceOrEditions,
isPiece: this.isPiece(), isPiece: this.isPiece()
senderName: currentUser && currentUser.username
}); });
if (action === 'acl_consign') { if (action === 'acl_consign') {
@ -131,4 +138,4 @@ let AclFormFactory = React.createClass({
} }
}); });
export default AclFormFactory; export default withContext(AclFormFactory, 'currentUser');

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import ReactAddons from 'react/addons'; import ReactDOM from 'react-dom';
import classNames from 'classnames'; import classNames from 'classnames';
@ -257,7 +257,7 @@ let Form = React.createClass({
}, },
renderChildren() { renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => { return React.Children.map(this.props.children, (child, i) => {
if (child) { if (child) {
// Since refs will be overwritten by this functions return statement, // Since refs will be overwritten by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property` // we still want to be able to define refs for nested `Form` or `Property`
@ -355,7 +355,7 @@ let Form = React.createClass({
let refToValidate = {}; let refToValidate = {};
const property = this.refs[refName]; const property = this.refs[refName];
const input = property.refs.input; const input = property.refs.input;
const value = input.getDOMNode().value || input.state.value; const value = ReactDOM.findDOMNode(input).value || input.state.value;
const { max, const { max,
min, min,
pattern, pattern,

View File

@ -8,14 +8,22 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import withContext from '../context/with_context';
import { currentUserShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let CopyrightAssociationForm = React.createClass({
const { bool } = React.PropTypes;
const CopyrightAssociationForm = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired // Injected through HOCs
currentUser: currentUserShape.isRequired,
isLoggedIn: bool.isRequired
}, },
handleSubmitSuccess() { handleSubmitSuccess() {
@ -28,7 +36,7 @@ let CopyrightAssociationForm = React.createClass({
}, },
render() { render() {
const { currentUser } = this.props; const { currentUser, isLoggedIn } = this.props;
const selectDefaultValue = ' -- ' + getLangText('select an association') + ' -- '; const selectDefaultValue = ' -- ' + getLangText('select an association') + ' -- ';
let selectedState = selectDefaultValue; let selectedState = selectDefaultValue;
@ -38,7 +46,7 @@ let CopyrightAssociationForm = React.createClass({
} }
} }
if (currentUser.email) { if (isLoggedIn) {
return ( return (
<Form <Form
ref='form' ref='form'
@ -77,4 +85,4 @@ let CopyrightAssociationForm = React.createClass({
} }
}); });
export default CopyrightAssociationForm; export default withContext(CopyrightAssociationForm, 'currentUser', 'isLoggedIn');

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
@ -11,9 +10,11 @@ import UserActions from '../../actions/user_actions';
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import withContext from '../context/with_context';
import { locationShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
@ -22,11 +23,11 @@ let LoginForm = React.createClass({
propTypes: { propTypes: {
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
location: React.PropTypes.object, whitelabelName: React.PropTypes.string,
whitelabelName: React.PropTypes.string
},
mixins: [History], // Injected through HOCs
location: locationShape.isRequired // eslint-disable-line react/sort-prop-types
},
getDefaultProps() { getDefaultProps() {
return { return {
@ -93,4 +94,4 @@ let LoginForm = React.createClass({
} }
}); });
export default LoginForm; export default withContext(LoginForm, 'location');

View File

@ -8,23 +8,22 @@ import InputFineUploader from './input_fineuploader';
import FormSubmitButton from '../ascribe_buttons/form_submit_button'; import FormSubmitButton from '../ascribe_buttons/form_submit_button';
import { FileStatus } from '../ascribe_uploader/react_s3_fine_uploader_utils';
import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button'; import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import withContext from '../context/with_context';
import { currentUserShape, locationShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { validationParts, validationTypes } from '../../constants/uploader_constants'; import { validationParts, validationTypes } from '../../constants/uploader_constants';
import { FileStatus, formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let RegisterPieceForm = React.createClass({ let RegisterPieceForm = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
enableLocalHashing: React.PropTypes.bool, enableLocalHashing: React.PropTypes.bool,
@ -36,11 +35,14 @@ let RegisterPieceForm = React.createClass({
// For this form to work with SlideContainer, we sometimes have to disable it // For this form to work with SlideContainer, we sometimes have to disable it
disabled: React.PropTypes.bool, disabled: React.PropTypes.bool,
location: React.PropTypes.object,
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
]) ]),
// Injected through HOCs
currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
location: locationShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
getDefaultProps() { getDefaultProps() {
@ -128,7 +130,7 @@ let RegisterPieceForm = React.createClass({
location, location,
submitMessage } = this.props; submitMessage } = this.props;
const profileHashLocally = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; const profileHashLocally = currentUser.profile ? currentUser.profile.hash_locally : false;
const hashLocally = profileHashLocally && enableLocalHashing; const hashLocally = profileHashLocally && enableLocalHashing;
return ( return (
@ -238,4 +240,4 @@ let RegisterPieceForm = React.createClass({
} }
}); });
export default RegisterPieceForm; export default withContext(RegisterPieceForm, 'currentUser', 'location');

View File

@ -27,7 +27,6 @@ let RequestActionForm = React.createClass({
React.PropTypes.array React.PropTypes.array
]).isRequired, ]).isRequired,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },
@ -99,7 +98,6 @@ let RequestActionForm = React.createClass({
availableAcls={{'acl_unconsign': true}} availableAcls={{'acl_unconsign': true}}
buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px' buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px'
pieceOrEditions={this.props.pieceOrEditions} pieceOrEditions={this.props.pieceOrEditions}
currentUser={this.props.currentUser}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
); );
} else if (this.props.notifications.action === 'loan_request') { } else if (this.props.notifications.action === 'loan_request') {
@ -109,7 +107,6 @@ let RequestActionForm = React.createClass({
buttonAcceptName="LOAN" buttonAcceptName="LOAN"
buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px' buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px'
pieceOrEditions={this.props.pieceOrEditions} pieceOrEditions={this.props.pieceOrEditions}
currentUser={this.props.currentUser}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
); );
} else { } else {

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import ContractListActions from '../../actions/contract_list_actions'; import ContractListActions from '../../actions/contract_list_actions';
import ContractListStore from '../../stores/contract_list_store'; import ContractListStore from '../../stores/contract_list_store';
@ -13,19 +12,23 @@ import Form from './form';
import Property from './property'; import Property from './property';
import InputTextAreaToggable from './input_textarea_toggable'; import InputTextAreaToggable from './input_textarea_toggable';
import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import withContext from '../context/with_context';
import { routerShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
let SendContractAgreementForm = React.createClass({ const SendContractAgreementForm = React.createClass({
propTypes: { propTypes: {
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func,
},
mixins: [History], // Injected through HOCs
router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
},
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
@ -57,7 +60,7 @@ let SendContractAgreementForm = React.createClass({
const notification = new GlobalNotificationModel(getLangText('Contract agreement sent'), 'success', 10000); const notification = new GlobalNotificationModel(getLangText('Contract agreement sent'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection'); this.props.router.push('/collection');
}, },
getFormData() { getFormData() {
@ -151,4 +154,4 @@ let SendContractAgreementForm = React.createClass({
} }
}); });
export default SendContractAgreementForm; export default withContext(SendContractAgreementForm, 'router');

View File

@ -21,7 +21,6 @@ let ShareForm = React.createClass({
id: React.PropTypes.object, id: React.PropTypes.object,
message: React.PropTypes.string, message: React.PropTypes.string,
editions: React.PropTypes.array, editions: React.PropTypes.array,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
@ -12,8 +11,11 @@ import Form from './form';
import Property from './property'; import Property from './property';
import InputCheckbox from './input_checkbox'; import InputCheckbox from './input_checkbox';
import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import withContext from '../context/with_context';
import { locationShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
@ -23,16 +25,16 @@ let SignupForm = React.createClass({
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
location: React.PropTypes.object,
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,
React.PropTypes.string React.PropTypes.string
]), ]),
whitelabelName: React.PropTypes.string whitelabelName: React.PropTypes.string,
},
mixins: [History], // Injected through HOCs
isLoggedIn: locationShape.isRequired, // eslint-disable-line react/sort-prop-types
},
getDefaultProps() { getDefaultProps() {
return { return {
@ -132,4 +134,4 @@ let SignupForm = React.createClass({
}); });
export default SignupForm; export default withContext(SignupForm, 'location');

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom';
/** /**
* This component can be used as a custom input element for form properties. * This component can be used as a custom input element for form properties.
@ -71,7 +72,7 @@ let InputCheckbox = React.createClass({
} }
// On every change, we're inversing the input's value // On every change, we're inversing the input's value
let inverseValue = !this.refs.checkbox.getDOMNode().checked; let inverseValue = !ReactDOM.findDOMNode(this.refs.checkbox).checked;
// pass it to the state // pass it to the state
this.setState({value: inverseValue}); this.setState({value: inverseValue});

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import React from 'react/addons'; import React from 'react';
import update from 'react-addons-update';
import InputCheckbox from './input_checkbox'; import InputCheckbox from './input_checkbox';
@ -101,7 +102,7 @@ const InputContractAgreementCheckbox = React.createClass({
// so the parent `Property` is able to get the correct value of this component // so the parent `Property` is able to get the correct value of this component
// when the `Form` queries it. // when the `Form` queries it.
this.setState({ this.setState({
value: React.addons.update(this.state.value, { value: update(this.state.value, {
terms: { $set: event.target.value } terms: { $set: event.target.value }
}) })
}); });

View File

@ -11,19 +11,17 @@ let ListRequestActions = React.createClass({
React.PropTypes.array React.PropTypes.array
]).isRequired, ]).isRequired,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },
render () { render () {
const { currentUser, handleSuccess, notifications, pieceOrEditions } = this.props; const { handleSuccess, notifications, pieceOrEditions } = this.props;
if (notifications.length) { if (notifications.length) {
return ( return (
<div> <div>
{notifications.map((notification) => {notifications.map((notification) =>
<RequestActionForm <RequestActionForm
currentUser={currentUser}
handleSuccess={handleSuccess} handleSuccess={handleSuccess}
notifications={notification} notifications={notification}
pieceOrEditions={pieceOrEditions} /> pieceOrEditions={pieceOrEditions} />

View File

@ -1,14 +1,12 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import ReactAddons from 'react/addons'; import ReactDOM from 'react-dom';
import Panel from 'react-bootstrap/lib/Panel'; import Panel from 'react-bootstrap/lib/Panel';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { mergeOptions } from '../../utils/general_utils';
const { bool, element, string, oneOfType, func, object, arrayOf } = React.PropTypes; const { bool, element, string, oneOfType, func, object, arrayOf } = React.PropTypes;
@ -101,9 +99,9 @@ const Property = React.createClass({
// In order to set this.state.value from another component // In order to set this.state.value from another component
// the state of value should only be set if its not undefined and // the state of value should only be set if its not undefined and
// actually references something // actually references something
if(childInput && typeof childInput.getDOMNode().value !== 'undefined') { if(childInput && typeof ReactDOM.findDOMNode(childInput).value !== 'undefined') {
this.setState({ this.setState({
value: childInput.getDOMNode().value value: ReactDOM.findDOMNode(childInput).value
}); });
// When implementing custom input components, their value isn't exposed like the one // When implementing custom input components, their value isn't exposed like the one
@ -116,9 +114,9 @@ const Property = React.createClass({
}); });
} }
if(!this.state.initialValue && childInput && childInput.props.defaultValue) { if(!this.state.initialValue && childInput && childInput.defaultValue) {
this.setState({ this.setState({
initialValue: childInput.props.defaultValue initialValue: childInput.defaultValue
}); });
} }
}, },
@ -140,7 +138,7 @@ const Property = React.createClass({
// Therefore we have to make sure only to reset the initial value // Therefore we have to make sure only to reset the initial value
// of HTML inputs (which we determine by checking if there 'type' attribute matches // of HTML inputs (which we determine by checking if there 'type' attribute matches
// the ones included in AppConstants.possibleInputTypes). // the ones included in AppConstants.possibleInputTypes).
let inputDOMNode = input.getDOMNode(); let inputDOMNode = ReactDOM.findDOMNode(input);
if(inputDOMNode.type && typeof inputDOMNode.type === 'string' && if(inputDOMNode.type && typeof inputDOMNode.type === 'string' &&
AppConstants.possibleInputTypes.indexOf(inputDOMNode.type.toLowerCase()) > -1) { AppConstants.possibleInputTypes.indexOf(inputDOMNode.type.toLowerCase()) > -1) {
inputDOMNode.value = this.state.initialValue; inputDOMNode.value = this.state.initialValue;
@ -180,10 +178,10 @@ const Property = React.createClass({
// skip the focus of non-input elements // skip the focus of non-input elements
let nonInputHTMLElements = ['pre', 'div']; let nonInputHTMLElements = ['pre', 'div'];
if (this.refs.input && if (this.refs.input &&
nonInputHTMLElements.indexOf(this.refs.input.getDOMNode().nodeName.toLowerCase()) > -1 ) { nonInputHTMLElements.indexOf(ReactDOM.findDOMNode(this.refs.input).nodeName.toLowerCase()) > -1 ) {
return; return;
} }
this.refs.input.getDOMNode().focus(); ReactDOM.findDOMNode(this.refs.input).focus();
this.setState({ this.setState({
isFocused: true isFocused: true
}); });
@ -205,7 +203,7 @@ const Property = React.createClass({
errors: null, errors: null,
// also update initialValue in case of the user updating and canceling its actions again // also update initialValue in case of the user updating and canceling its actions again
initialValue: this.refs.input.getDOMNode().value initialValue: ReactDOM.findDOMNode(this.refs.input).value
}); });
}, },
@ -261,7 +259,7 @@ const Property = React.createClass({
// Input's props should only be cloned and propagated down the tree, // Input's props should only be cloned and propagated down the tree,
// if the component is actually being shown (!== 'expanded === false') // if the component is actually being shown (!== 'expanded === false')
if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) { if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) {
return ReactAddons.Children.map(this.props.children, (child) => { return React.Children.map(this.props.children, (child) => {
// Since refs will be overriden by this functions return statement, // Since refs will be overriden by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property` // we still want to be able to define refs for nested `Form` or `Property`
// children, which is why we're upfront simply invoking the callback-ref- // children, which is why we're upfront simply invoking the callback-ref-

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import Link from 'react-router/es6/Link';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';

View File

@ -1,66 +1,39 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import classnames from 'classnames';
let ActionPanel = React.createClass({ let ActionPanel = React.createClass({
propTypes: { propTypes: {
title: React.PropTypes.string, buttons: React.PropTypes.element,
content: React.PropTypes.oneOfType([ content: React.PropTypes.oneOfType([
React.PropTypes.string, React.PropTypes.string,
React.PropTypes.element React.PropTypes.element
]), ]),
buttons: React.PropTypes.element,
onClick: React.PropTypes.func,
ignoreFocus: React.PropTypes.bool,
leftColumnWidth: React.PropTypes.string, leftColumnWidth: React.PropTypes.string,
onClick: React.PropTypes.func,
rightColumnWidth: React.PropTypes.string rightColumnWidth: React.PropTypes.string
}, },
getInitialState() {
return {
isFocused: false
};
},
handleFocus() {
// if ignoreFocus (bool) is defined, then just ignore focusing on
// the property and input
if(this.props.ignoreFocus) {
return;
}
// if onClick is defined from the outside,
// just call it
if(this.props.onClick) {
this.props.onClick();
}
this.refs.input.getDOMNode().focus();
this.setState({
isFocused: true
});
},
render() { render() {
const { buttons, content, leftColumnWidth, onClick, rightColumnWidth } = this.props;
let { leftColumnWidth, rightColumnWidth } = this.props;
return ( return (
<div className={classnames('ascribe-panel-wrapper', {'is-focused': this.state.isFocused})}> <div
className={'ascribe-panel-wrapper'}
onClick={onClick}>
<div <div
className="ascribe-panel-table" className="ascribe-panel-table"
style={{width: leftColumnWidth}}> style={{width: leftColumnWidth}}>
<div className="ascribe-panel-content"> <div className="ascribe-panel-content">
{this.props.content} {content}
</div> </div>
</div> </div>
<div <div
className="ascribe-panel-table" className="ascribe-panel-table"
style={{width: rightColumnWidth}}> style={{width: rightColumnWidth}}>
<div className="ascribe-panel-content"> <div className="ascribe-panel-content">
{this.props.buttons} {buttons}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,15 +1,16 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { RouteContext } from 'react-router';
import history from '../../history';
import UserStore from '../../stores/user_store'; import UserStore from '../../stores/user_store';
import withContext from '../context/with_context';
import { currentUserShape, locationShape, routerShape, whitelabelShape } from '../prop_types';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
const { object } = React.PropTypes; const { bool, object } = React.PropTypes;
const WHEN_ENUM = ['loggedIn', 'loggedOut']; const WHEN_ENUM = ['loggedIn', 'loggedOut'];
/** /**
@ -25,7 +26,7 @@ export function AuthRedirect({ to, when }) {
throw new Error(`"when" must be one of: [${whenValues}] got "${when}" instead`); throw new Error(`"when" must be one of: [${whenValues}] got "${when}" instead`);
} }
return function(currentUser, query) { return function redirectRoute(router, { query }, { isLoggedIn }) {
const { redirectAuthenticated, redirect } = query; const { redirectAuthenticated, redirect } = query;
// The user of this handler specifies with `when`, what kind of status // The user of this handler specifies with `when`, what kind of status
@ -34,19 +35,18 @@ export function AuthRedirect({ to, when }) {
// //
// So if when === 'loggedIn', we're checking if the user is logged in (and // So if when === 'loggedIn', we're checking if the user is logged in (and
// vice versa) // vice versa)
const isLoggedIn = Object.keys(currentUser).length && currentUser.email;
const exprToValidate = when === 'loggedIn' ? isLoggedIn : !isLoggedIn; const exprToValidate = when === 'loggedIn' ? isLoggedIn : !isLoggedIn;
// and redirect if `true`. // and redirect if `true`.
if (exprToValidate) { if (exprToValidate) {
window.setTimeout(() => history.replace({ query, pathname: to })); window.setTimeout(() => router.replace({ query, pathname: to }));
return true; return true;
// Otherwise there can also be the case that the backend // 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 // wants to redirect the user to a specific route when the user is logged out already
} else if (!exprToValidate && when === 'loggedIn' && redirect) { } else if (!exprToValidate && when === 'loggedIn' && redirect) {
delete query.redirect; delete query.redirect;
window.setTimeout(() => history.replace({ query, pathname: '/' + redirect })); window.setTimeout(() => router.replace({ query, pathname: `/${redirect}` }));
return true; return true;
} else if (!exprToValidate && when === 'loggedOut' && redirectAuthenticated) { } else if (!exprToValidate && when === 'loggedOut' && redirectAuthenticated) {
@ -77,22 +77,19 @@ export function AuthRedirect({ to, when }) {
*/ */
export function ProxyHandler(...redirectFunctions) { export function ProxyHandler(...redirectFunctions) {
return (Component) => { return (Component) => {
return React.createClass({ // Don't worry about shadowing the HOC here; using a declaration like this allows
displayName: 'ProxyHandler', // babel-plugin-react-display-name to automatically generate the displayName.
// eslint-disable-next-line no-shadow
const ProxyHandler = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp, after the routes have been initialized // Injected through HOCs
currentUser: React.PropTypes.object, currentUser: currentUserShape.isRequired,
whitelabel: React.PropTypes.object, isLoggedIn: bool.isRequired,
location: locationShape.isRequired,
// Provided from router router: routerShape.isRequired,
location: object whitelabel: whitelabelShape.isRequired
}, },
// We need insert `RouteContext` here in order to be able
// to use the `Lifecycle` widget in further down nested components
mixins: [RouteContext],
componentDidMount() { componentDidMount() {
this.evaluateRedirectFunctions(); this.evaluateRedirectFunctions();
}, },
@ -102,15 +99,15 @@ export function ProxyHandler(...redirectFunctions) {
}, },
evaluateRedirectFunctions(props = this.props) { evaluateRedirectFunctions(props = this.props) {
const { currentUser, location: { query } } = props; const { currentUser, isLoggedIn, location, router, whitelabel } = props;
if (UserStore.hasLoaded() && !UserStore.isLoading()) { if (UserStore.hasLoaded() && !UserStore.isLoading()) {
const context = { currentUser, isLoggedIn, whitelabel };
for (let i = 0; i < redirectFunctions.length; i++) { for (let i = 0; i < redirectFunctions.length; i++) {
// if a redirectFunction redirects the user, // if a redirectFunction redirects the user, it should return `true` and
// it should return `true` and therefore // therefore stop/avoid the execution of all functions that follow
// stop/avoid the execution of all functions if (redirectFunctions[i](router, location, context)) {
// that follow
if (redirectFunctions[i](currentUser, query)) {
break; break;
} }
} }
@ -123,5 +120,12 @@ export function ProxyHandler(...redirectFunctions) {
); );
} }
}); });
return withContext(ProxyHandler,
'currentUser',
'isLoggedIn',
'location',
'router',
'whitelabel');
}; };
} }

View File

@ -5,25 +5,29 @@ import React from 'react';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import CopyrightAssociationForm from '../ascribe_forms/form_copyright_association';
import Form from '../ascribe_forms/form'; import Form from '../ascribe_forms/form';
import Property from '../ascribe_forms/property';
import InputCheckbox from '../ascribe_forms/input_checkbox'; import InputCheckbox from '../ascribe_forms/input_checkbox';
import Property from '../ascribe_forms/property';
import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph'; import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph';
import AclProxy from '../acl_proxy'; import AclProxy from '../acl_proxy';
import AscribeSpinner from '../ascribe_spinner';
import CopyrightAssociationForm from '../ascribe_forms/form_copyright_association'; import withContext from '../context/with_context';
import { currentUserShape, whitelabelShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let AccountSettings = React.createClass({ let AccountSettings = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
loadUser: React.PropTypes.func.isRequired, loadUser: React.PropTypes.func.isRequired,
whitelabel: React.PropTypes.object.isRequired
// Injected through HOCs
currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
handleSuccess() { handleSuccess() {
@ -100,7 +104,7 @@ let AccountSettings = React.createClass({
<AclProxy <AclProxy
aclObject={whitelabel} aclObject={whitelabel}
aclName="acl_view_settings_copyright_association"> aclName="acl_view_settings_copyright_association">
<CopyrightAssociationForm currentUser={currentUser} /> <CopyrightAssociationForm />
</AclProxy> </AclProxy>
{profile} {profile}
</CollapsibleParagraph> </CollapsibleParagraph>
@ -108,4 +112,4 @@ let AccountSettings = React.createClass({
} }
}); });
export default AccountSettings; export default withContext(AccountSettings, 'currentUser', 'whitelabel');

View File

@ -2,33 +2,33 @@
import React from 'react'; 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 ContractListStore from '../../stores/contract_list_store';
import ContractListActions from '../../actions/contract_list_actions'; 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 GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; 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 withContext from '../context/with_context';
import { currentUserShape, whitelabelShape } from '../prop_types';
import { getLangText } from '../../utils/lang_utils';
import { setDocumentTitle } from '../../utils/dom_utils'; import { setDocumentTitle } from '../../utils/dom_utils';
import { truncateTextAtCharIndex } from '../../utils/general_utils'; import { truncateTextAtCharIndex } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
let ContractSettings = React.createClass({ let ContractSettings = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, currentUser: currentUserShape.isRequired,
whitelabel: React.PropTypes.object.isRequired, whitelabel: whitelabelShape.isRequired
// Provided from router
location: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
@ -72,7 +72,7 @@ let ContractSettings = React.createClass({
}, },
render() { render() {
const { currentUser, location, whitelabel } = this.props; const { currentUser, whitelabel } = this.props;
const publicContracts = this.getPublicContracts(); const publicContracts = this.getPublicContracts();
const privateContracts = this.getPrivateContracts(); const privateContracts = this.getPrivateContracts();
let createPublicContractForm = null; let createPublicContractForm = null;
@ -180,4 +180,4 @@ let ContractSettings = React.createClass({
} }
}); });
export default ContractSettings; export default withContext(ContractSettings, 'currentUser', 'whitelabel');

View File

@ -5,15 +5,16 @@ import React from 'react';
import UserActions from '../../actions/user_actions'; import UserActions from '../../actions/user_actions';
import AccountSettings from './account_settings'; import AccountSettings from './account_settings';
import ApiSettings from './api_settings';
import BitcoinWalletSettings from './bitcoin_wallet_settings'; import BitcoinWalletSettings from './bitcoin_wallet_settings';
import APISettings from './api_settings';
import WebhookSettings from './webhook_settings'; import WebhookSettings from './webhook_settings';
import AclProxy from '../acl_proxy'; import AclProxy from '../acl_proxy';
import withContext from '../context/with_context';
import { whitelabelShape } from '../prop_types';
import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
import { setDocumentTitle } from '../../utils/dom_utils'; import { setDocumentTitle } from '../../utils/dom_utils';
import { getLangText } from '../../utils/lang_utils';
let SettingsContainer = React.createClass({ let SettingsContainer = React.createClass({
@ -23,12 +24,9 @@ let SettingsContainer = React.createClass({
React.PropTypes.element React.PropTypes.element
]), ]),
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, isLoggedIn: React.PropTypes.bool.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: React.PropTypes.object.isRequired, whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
// Provided from router
location: React.PropTypes.object
}, },
loadUser(invalidateCache) { loadUser(invalidateCache) {
@ -36,22 +34,19 @@ let SettingsContainer = React.createClass({
}, },
render() { render() {
const { children, currentUser, whitelabel } = this.props; const { children, isLoggedIn, whitelabel } = this.props;
setDocumentTitle(getLangText('Account settings')); setDocumentTitle(getLangText('Account settings'));
if (currentUser.username) { if (isLoggedIn) {
return ( return (
<div className="settings-container"> <div className="settings-container">
<AccountSettings <AccountSettings loadUser={this.loadUser} />
currentUser={currentUser}
loadUser={this.loadUser}
whitelabel={whitelabel} />
{children} {children}
<AclProxy <AclProxy
aclObject={whitelabel} aclObject={whitelabel}
aclName="acl_view_settings_api"> aclName="acl_view_settings_api">
<APISettings /> <ApiSettings />
</AclProxy> </AclProxy>
<WebhookSettings /> <WebhookSettings />
<AclProxy <AclProxy
@ -66,4 +61,4 @@ let SettingsContainer = React.createClass({
} }
}); });
export default SettingsContainer; export default withContext(SettingsContainer, 'isLoggedIn', 'whitelabel');

View File

@ -1,32 +1,33 @@
'use strict'; 'use strict';
import React from 'react/addons'; import React from 'react';
import { History, Lifecycle } from 'react-router';
import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs'; import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs';
import withContext from '../context/with_context';
import { locationShape, routerShape } from '../prop_types';
const { arrayOf, element, bool, shape, string, object } = React.PropTypes; const { arrayOf, element, bool, shape, string, object } = React.PropTypes;
const SlidesContainer = React.createClass({ const SlidesContainer = React.createClass({
propTypes: { propTypes: {
children: arrayOf(element),
forwardProcess: bool.isRequired, forwardProcess: bool.isRequired,
children: arrayOf(element),
glyphiconClassNames: shape({ glyphiconClassNames: shape({
pending: string, pending: string,
complete: string complete: string
}), }),
location: object,
pageExitWarning: string
},
mixins: [History, Lifecycle], // Injected through HOCs
location: locationShape.isRequired, // eslint-disable-line react/sort-prop-types
router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
},
getInitialState() { getInitialState() {
return { return {
containerWidth: 0, containerWidth: 0
pageExitWarning: null
}; };
}, },
@ -37,20 +38,17 @@ const SlidesContainer = React.createClass({
// Initially, we need to dispatch 'resize' once to render correctly // Initially, we need to dispatch 'resize' once to render correctly
window.dispatchEvent(new Event('resize')); window.dispatchEvent(new Event('resize'));
}, },
componentWillUnmount() { componentWillUnmount() {
window.removeEventListener('resize', this.handleContainerResize); window.removeEventListener('resize', this.handleContainerResize);
}, },
routerWillLeave() {
return this.props.pageExitWarning;
},
handleContainerResize() { handleContainerResize() {
this.setState({ this.setState({
// +30 to get rid of the padding of the container which is 15px + 15px left and right // +30 to get rid of the padding of the container which is 15px + 15px left and right
containerWidth: this.refs.containerWrapper.getDOMNode().offsetWidth + 30 containerWidth: this.refs.containerWrapper.offsetWidth + 30
}); });
}, },
@ -61,10 +59,10 @@ const SlidesContainer = React.createClass({
}, },
setSlideNum(nextSlideNum, additionalQueryParams = {}) { setSlideNum(nextSlideNum, additionalQueryParams = {}) {
const { location: { pathname } } = this.props; const { location: { pathname, query }, router } = this.props;
const query = Object.assign({}, this.props.location.query, additionalQueryParams, { slide_num: nextSlideNum }); const slideQuery = Object.assign({}, query, additionalQueryParams, { slide_num: nextSlideNum });
this.history.push({ pathname, query }); router.push({ pathname, query: slideQuery });
}, },
// breadcrumbs are defined as attributes of the slides. // breadcrumbs are defined as attributes of the slides.
@ -120,7 +118,7 @@ const SlidesContainer = React.createClass({
} }
}, },
// Since we need to give the slides a width, we need to call ReactAddons.addons.cloneWithProps // Since we need to give the slides a width, we need to call React.cloneElement
// Also, a key is nice to have! // Also, a key is nice to have!
renderChildren() { renderChildren() {
const startFrom = parseInt(this.props.location.query.start_from, 10) || -1; const startFrom = parseInt(this.props.location.query.start_from, 10) || -1;
@ -129,7 +127,7 @@ const SlidesContainer = React.createClass({
// since the default parameter of startFrom is -1, we do not need to check // since the default parameter of startFrom is -1, we do not need to check
// if its actually present in the url bar, as it will just not match // if its actually present in the url bar, as it will just not match
if(child && i >= startFrom) { if(child && i >= startFrom) {
return React.addons.cloneWithProps(child, { return React.cloneElement(child, {
className: 'ascribe-slide', className: 'ascribe-slide',
style: { style: {
width: this.state.containerWidth width: this.state.containerWidth
@ -179,4 +177,4 @@ const SlidesContainer = React.createClass({
} }
}); });
export default SlidesContainer; export default withContext(SlidesContainer, 'location', 'router');

View File

@ -50,7 +50,7 @@ let FacebookShareButton = React.createClass({
.inject(AppConstants.facebook.sdkUrl) .inject(AppConstants.facebook.sdkUrl)
.then(() => { .then(() => {
if (this.state.loaded) { if (this.state.loaded) {
FB.XFBML.parse(this.refs.fbShareButton.getDOMNode().parent) window.FB.XFBML.parse(this.refs.fbShareButton.parent);
} }
}); });
}, },

View File

@ -31,7 +31,7 @@ let TwitterShareButton = React.createClass({
loadTwitterButton() { loadTwitterButton() {
const { count, counturl, hashtags, size, text, url, via } = this.props; const { count, counturl, hashtags, size, text, url, via } = this.props;
twttr.widgets.createShareButton(url, this.refs.twitterShareButton.getDOMNode(), { window.twttr.widgets.createShareButton(url, this.refs.twitterShareButton, {
count, count,
counturl, counturl,
hashtags, hashtags,

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import ReactAddons from 'react/addons';
import TableHeader from './table_header'; import TableHeader from './table_header';
import { ColumnModel } from './models/table_models'; import { ColumnModel } from './models/table_models';
@ -20,8 +19,8 @@ let Table = React.createClass({
}, },
renderChildren() { renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => { return React.Children.map(this.props.children, (child, i) => {
return ReactAddons.addons.cloneWithProps(child, { return React.cloneElement(child, {
columnList: this.props.columnList, columnList: this.props.columnList,
columnContent: this.props.itemList[i], columnContent: this.props.itemList[i],
key: i key: i

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import Link from 'react-router/es6/Link';
import { ColumnModel } from './models/table_models'; import { ColumnModel } from './models/table_models';

View File

@ -55,7 +55,7 @@ let FileDragAndDrop = React.createClass({
}, },
clearSelection() { clearSelection() {
this.refs.fileSelector.getDOMNode().value = ''; this.refs.fileSelector.value = '';
}, },
handleDragOver(event) { handleDragOver(event) {
@ -132,7 +132,7 @@ let FileDragAndDrop = React.createClass({
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
} }
this.refs.fileSelector.getDOMNode().dispatchEvent(evt); this.refs.fileSelector.dispatchEvent(evt);
} }
}, },

View File

@ -34,11 +34,13 @@ let FileDragAndDropDialog = React.createClass({
}, },
render() { render() {
const { enableLocalHashing, const {
enableLocalHashing,
fileClassToUpload, fileClassToUpload,
multipleFiles, multipleFiles,
onClick, onClick,
uploadMethod } = this.props; uploadMethod
} = this.props;
let dialogElement; let dialogElement;
if (enableLocalHashing && !uploadMethod) { if (enableLocalHashing && !uploadMethod) {
@ -66,9 +68,7 @@ let FileDragAndDropDialog = React.createClass({
{getLangText('Hash your work')} {getLangText('Hash your work')}
</span> </span>
</Link> </Link>
<span> {getLangText('or')} </span> <span> {getLangText('or')} </span>
<Link <Link
to={`/${window.location.pathname.split('/').pop()}`} to={`/${window.location.pathname.split('/').pop()}`}
query={queryParamsUpload}> query={queryParamsUpload}>

View File

@ -66,7 +66,7 @@ export default function UploadButton({ className = 'btn btn-default btn-sm', sho
}, },
clearSelection() { clearSelection() {
this.refs.fileSelector.getDOMNode().value = ''; this.refs.fileSelector.value = '';
}, },
handleOnClick() { handleOnClick() {
@ -88,7 +88,7 @@ export default function UploadButton({ className = 'btn btn-default btn-sm', sho
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
} }
evt.stopPropagation(); evt.stopPropagation();
this.refs.fileSelector.getDOMNode().dispatchEvent(evt); this.refs.fileSelector.dispatchEvent(evt);
} }
}, },

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import React from 'react/addons'; import React from 'react';
import update from 'react-addons-update';
// FIXME: remove once using react-components // FIXME: remove once using react-components
import fineUploader from 'exports?qq!./vendor/s3.fine-uploader'; import fineUploader from 'exports?qq!./vendor/s3.fine-uploader';
import Q from 'q'; import Q from 'q';
@ -536,7 +537,7 @@ const ReactS3FineUploader = React.createClass({
changeSet.status = { $set: status }; changeSet.status = { $set: status };
let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet }); let filesToUpload = update(this.state.filesToUpload, { [fileId]: changeSet });
this.setState({ filesToUpload }, resolve); this.setState({ filesToUpload }, resolve);
}); });
@ -547,7 +548,7 @@ const ReactS3FineUploader = React.createClass({
if(fileId < filesToUpload.length) { if(fileId < filesToUpload.length) {
const changeSet = { $set: url }; const changeSet = { $set: url };
const newFilesToUpload = React.addons.update(filesToUpload, { const newFilesToUpload = update(filesToUpload, {
[fileId]: { thumbnailUrl: changeSet } [fileId]: { thumbnailUrl: changeSet }
}); });
@ -574,7 +575,7 @@ const ReactS3FineUploader = React.createClass({
completed: false completed: false
}; };
let startedChunks = React.addons.update(this.state.startedChunks, { $set: chunks }); let startedChunks = update(this.state.startedChunks, { $set: chunks });
this.setState({ startedChunks }); this.setState({ startedChunks });
}, },
@ -588,7 +589,7 @@ const ReactS3FineUploader = React.createClass({
chunks[chunkKey].responseJson = responseJson; chunks[chunkKey].responseJson = responseJson;
chunks[chunkKey].xhr = xhr; chunks[chunkKey].xhr = xhr;
let startedChunks = React.addons.update(this.state.startedChunks, { $set: chunks }); let startedChunks = update(this.state.startedChunks, { $set: chunks });
this.setState({ startedChunks }); this.setState({ startedChunks });
} }
@ -619,7 +620,7 @@ const ReactS3FineUploader = React.createClass({
files[id].status = FileStatus.UPLOAD_SUCCESSFUL; files[id].status = FileStatus.UPLOAD_SUCCESSFUL;
files[id].key = this.state.uploader.getKey(id); files[id].key = this.state.uploader.getKey(id);
let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: files }); let filesToUpload = update(this.state.filesToUpload, { $set: files });
this.setState({ filesToUpload }); this.setState({ filesToUpload });
// Only after the blob has been created server-side, we can make the form submittable. // Only after the blob has been created server-side, we can make the form submittable.
@ -667,7 +668,7 @@ const ReactS3FineUploader = React.createClass({
if (!this.state.errorState.errorClass) { if (!this.state.errorState.errorClass) {
notificationMessage = errorNotificationMessage; notificationMessage = errorNotificationMessage;
const errorState = React.addons.update(this.state.errorState, { const errorState = update(this.state.errorState, {
errorClass: { errorClass: {
$set: this.getUploadErrorClass({ $set: this.getUploadErrorClass({
reason: errorReason, reason: errorReason,
@ -720,7 +721,7 @@ const ReactS3FineUploader = React.createClass({
}, },
onProgress(id, name, uploadedBytes, totalBytes) { onProgress(id, name, uploadedBytes, totalBytes) {
let filesToUpload = React.addons.update(this.state.filesToUpload, { let filesToUpload = update(this.state.filesToUpload, {
[id]: { [id]: {
progress: { $set: (uploadedBytes / totalBytes) * 100} progress: { $set: (uploadedBytes / totalBytes) * 100}
} }
@ -747,7 +748,7 @@ const ReactS3FineUploader = React.createClass({
return file; return file;
}); });
let filesToUpload = React.addons.update(this.state.filesToUpload, {$set: updatedFilesToUpload}); let filesToUpload = update(this.state.filesToUpload, {$set: updatedFilesToUpload});
this.setState({filesToUpload }); this.setState({filesToUpload });
} else { } else {
@ -846,7 +847,7 @@ const ReactS3FineUploader = React.createClass({
fileIds.forEach((fileId) => { fileIds.forEach((fileId) => {
this.state.uploader.retry(fileId); this.state.uploader.retry(fileId);
filesToUpload = React.addons.update(filesToUpload, { [fileId]: { status: { $set: FileStatus.UPLOADING } } }); filesToUpload = update(filesToUpload, { [fileId]: { status: { $set: FileStatus.UPLOADING } } });
}); });
this.setState({ this.setState({
@ -1041,7 +1042,7 @@ const ReactS3FineUploader = React.createClass({
} }
// set the new file array // set the new file array
let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: oldAndNewFiles }); let filesToUpload = update(this.state.filesToUpload, { $set: oldAndNewFiles });
this.setState({ filesToUpload }, () => { this.setState({ filesToUpload }, () => {
// when files have been dropped or selected by a user, we want to propagate that // when files have been dropped or selected by a user, we want to propagate that

View File

@ -6,24 +6,23 @@ import GlobalNotificationModel from '../models/global_notification_model';
import GlobalNotificationActions from '../actions/global_notification_actions'; import GlobalNotificationActions from '../actions/global_notification_actions';
import Form from './ascribe_forms/form'; import Form from './ascribe_forms/form';
import Property from './ascribe_forms/property';
import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable'; import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable';
import Property from './ascribe_forms/property';
import AscribeSpinner from './ascribe_spinner'; import AscribeSpinner from './ascribe_spinner';
import withContext from './context/with_context';
import { locationShape } from './prop_types';
import ApiUrls from '../constants/api_urls'; import ApiUrls from '../constants/api_urls';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
import { setDocumentTitle } from '../utils/dom_utils'; import { setDocumentTitle } from '../utils/dom_utils';
let CoaVerifyContainer = React.createClass({ let CoaVerifyContainer = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object, location: locationShape.isRequired
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
}, },
render() { render() {
@ -117,5 +116,4 @@ let CoaVerifyForm = React.createClass({
} }
}); });
export default withContext(CoaVerifyContainer, 'location');
export default CoaVerifyContainer;

View File

@ -0,0 +1,138 @@
import React from 'react';
import { currentUserShape, locationShape, routerShape, whitelabelShape } from '../prop_types';
import { selectFromObject } from '../../utils/general_utils';
import { getDisplayName } from '../../utils/react_utils';
/**
* ContextPropDefinitions
* ======================
* contextType definitions for `contextProp`s.
*
* Each definition is of the form:
*
* contextProp = {
* 'contextTypes': {
* 'contextType': contextType used by the contextProp,
* ...
* },
* transformToProps: (optional) function that will receive the contextTypes from the component's
* context and should return an object containing the props to inject.
* }
*
* In the common case where your `contextProp` maps directly to the `contextType` used
* (eg. using `currentUser` as a `contextProp` to get the `currentUser` from context), you can omit
* the object definition and simply assign the `contextType` to the `contextProp`:
*
* contextProp = contextType
*
* as opposed to:
*
* contextProp = {
* 'contextTypes': {
* contextProp: contextType
* }
* }
*/
const ContextPropDefinitions = {
currentUser: currentUserShape.isRequired,
isLoggedIn: {
contextTypes: {
currentUser: currentUserShape.isRequired
},
transformToProps: ({ currentUser }) => ({ isLoggedIn: !!currentUser.email })
},
location: locationShape.isRequired,
router: routerShape.isRequired,
whitelabel: whitelabelShape.isRequired
};
// Expand any shortform definitions in ContextPropDefinitions into a normalized form that's
// easier for withContext to work with
Object.entries(ContextPropDefinitions).forEach(([prop, def]) => {
if (!def.hasOwnProperty('contextTypes')) {
ContextPropDefinitions[prop] = {
contextTypes: {
[prop]: def
}
};
}
});
/**
* Generalized version of react-router's `withRouter`.
* This injects the given `contextProp`s from the WrappedComponent's context into the component as
* a prop.
*
* Given `contextProp`s must have a matching definition in `ContextPropDefinitions`.
*
* @param {Component} WrappedComponent Component to inject context into
* @param {...string} contextProps Argument list of `contextProp`s (that must be registered in
* `ContextPropDefinitions`) to be injected into component as
* props
* @return {Component} Wrapped component
*/
export default function withContext(WrappedComponent, ...contextProps) {
const wrappedComponentName = getDisplayName(WrappedComponent);
if (!contextProps.length) {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn(`Used withContext on ${wrappedComponentName} without supplying any ` +
"items to inject from the component's context. Ignoring...");
}
return WrappedComponent;
}
const contextTypes = contextProps.reduce((types, contextProp) => {
const contextDef = ContextPropDefinitions[contextProp];
if (contextDef) {
Object.assign(types, contextDef.contextTypes);
} else if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn(`No context types were found for '${contextProp}' when adding context ` +
`to ${wrappedComponentName}. Make sure to add the context information ` +
'with_context.js.');
}
return types;
}, {});
const WithContext = (props, context) => {
if (process.env.NODE_ENV !== 'production') {
// Check if the expected context was available
Object.keys(contextTypes).forEach((contextType) => {
if (!context[contextType]) {
// eslint-disable-next-line no-console
console.warn(`Expected '${contextType}' did not exist in ` +
`${wrappedComponentName}'s context during mounting`);
}
});
}
const injectedProps = Object.assign({}, ...contextProps.map((contextProp) => {
const contextDef = ContextPropDefinitions[contextProp];
if (contextDef) {
const contextForProp = selectFromObject(context, Object.keys(contextDef.contextTypes));
return typeof contextDef.transformToProps === 'function'
? contextDef.transformToProps(contextForProp)
: contextForProp;
} else {
// Will be ignored by Object.assign()
return undefined;
}
}));
return (
<WrappedComponent {...props} {...injectedProps} />
);
};
WithContext.displayName = `WithContext(${wrappedComponentName}): [${contextProps.join(', ')}]`;
WithContext.contextTypes = contextTypes;
return WithContext;
}

View File

@ -1,25 +1,15 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import history from '../history';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
let ErrorNotFoundPage = React.createClass({ const ErrorNotFoundPage = React.createClass({
propTypes: { propTypes: {
message: React.PropTypes.string, message: React.PropTypes.string,
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
}, },
mixins: [History],
getDefaultProps() { getDefaultProps() {
return { return {
message: getLangText("Oops, the page you are looking for doesn't exist.") message: getLangText("Oops, the page you are looking for doesn't exist.")
@ -28,7 +18,7 @@ let ErrorNotFoundPage = React.createClass({
componentDidMount() { componentDidMount() {
// The previous page, if any, is the second item in the locationQueue // The previous page, if any, is the second item in the locationQueue
const { locationQueue: [ , previousPage ] } = this.history; const { locationQueue: [, previousPage] } = history;
if (previousPage) { if (previousPage) {
console.logGlobal('Page not found', { console.logGlobal('Page not found', {

View File

@ -48,7 +48,7 @@ let GlobalNotification = React.createClass({
handleContainerResize() { handleContainerResize() {
this.setState({ this.setState({
containerWidth: this.refs.notificationWrapper.getDOMNode().offsetWidth containerWidth: this.refs.notificationWrapper.offsetWidth
}); });
}, },

View File

@ -3,35 +3,35 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
import history from '../history';
import Nav from 'react-bootstrap/lib/Nav'; import Nav from 'react-bootstrap/lib/Nav';
import Navbar from 'react-bootstrap/lib/Navbar'; import Navbar from 'react-bootstrap/lib/Navbar';
import CollapsibleNav from 'react-bootstrap/lib/CollapsibleNav';
import DropdownButton from 'react-bootstrap/lib/DropdownButton'; import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import MenuItem from 'react-bootstrap/lib/MenuItem'; import MenuItem from 'react-bootstrap/lib/MenuItem';
import NavItem from 'react-bootstrap/lib/NavItem'; import NavItem from 'react-bootstrap/lib/NavItem';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import EventActions from '../actions/event_actions';
import PieceListStore from '../stores/piece_list_store'; import PieceListStore from '../stores/piece_list_store';
import AclProxy from './acl_proxy'; import AclProxy from './acl_proxy';
import withContext from './context/with_context';
import HeaderNotifications from './header_notifications'; import HeaderNotifications from './header_notifications';
import HeaderNotificationDebug from './header_notification_debug'; import HeaderNotificationDebug from './header_notification_debug';
import NavRoutesLinks from './nav_routes_links'; import NavRoutesLinks from './nav_routes_links';
import { currentUserShape, whitelabelShape } from './prop_types';
import { getLangText } from '../utils/lang_utils';
import { constructHead } from '../utils/dom_utils'; import { constructHead } from '../utils/dom_utils';
import { getLangText } from '../utils/lang_utils';
let Header = React.createClass({ let Header = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
whitelabel: React.PropTypes.object.isRequired
// Injected through HOCs
currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
isLoggedIn: React.PropTypes.bool.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
getInitialState() { getInitialState() {
@ -42,16 +42,10 @@ let Header = React.createClass({
// Listen to the piece list store, but don't fetch immediately to avoid // 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 // conflicts with routes that may need to wait to load the piece list
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
// react-bootstrap 0.25.1 has a bug in which it doesn't
// close the mobile expanded navigation after a click by itself.
// To get rid of this, we set the state of the component ourselves.
history.listen(this.onRouteChange);
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
//history.unlisten(this.onRouteChange);
}, },
onChange(state) { onChange(state) {
@ -116,24 +110,15 @@ let Header = React.createClass({
this.refs.dropdownbutton.setDropdownState(false); this.refs.dropdownbutton.setDropdownState(false);
}, },
// On route change, close expanded navbar again since react-bootstrap doesn't close
// the collapsibleNav by itself on click. setState() isn't available on a ref so
// doing this explicitly is the only way for now.
onRouteChange() {
if (this.refs.navbar) {
this.refs.navbar.state.navExpanded = false;
}
},
render() { render() {
const { currentUser, routes, whitelabel } = this.props; const { currentUser, isLoggedIn, routes, whitelabel } = this.props;
const { unfilteredPieceListCount } = this.state; const { unfilteredPieceListCount } = this.state;
let account; let account;
let signup; let signup;
let navRoutesLinks; let navRoutesLinks;
if (currentUser.username) { if (isLoggedIn) {
account = ( account = (
<DropdownButton <DropdownButton
ref='dropdownbutton' ref='dropdownbutton'
@ -175,7 +160,7 @@ let Header = React.createClass({
navRoutesLinks = ( navRoutesLinks = (
<NavRoutesLinks <NavRoutesLinks
navbar navbar
right pullRight
hasPieces={!!unfilteredPieceListCount} hasPieces={!!unfilteredPieceListCount}
routes={routes} routes={routes}
userAcl={currentUser.acl} /> userAcl={currentUser.acl} />
@ -201,26 +186,30 @@ let Header = React.createClass({
<div> <div>
<Navbar <Navbar
ref="navbar" ref="navbar"
brand={this.getLogo()}
toggleNavKey={0}
fixedTop={true} fixedTop={true}
className="hidden-print"> className="hidden-print">
<CollapsibleNav eventKey={0}> <Navbar.Header>
<Nav navbar left> <Navbar.Brand>
{this.getLogo()}
</Navbar.Brand>
</Navbar.Header>
<Navbar.Collapse
eventKey={0}>
<Nav navbar pullLeft>
<AclProxy <AclProxy
aclObject={whitelabel} aclObject={whitelabel}
aclName="acl_view_powered_by"> aclName="acl_view_powered_by">
{this.getPoweredBy()} {this.getPoweredBy()}
</AclProxy> </AclProxy>
</Nav> </Nav>
<Nav navbar right> <Nav navbar pullRight>
<HeaderNotificationDebug show={false}/> <HeaderNotificationDebug show={false}/>
{account} {account}
{signup} {signup}
</Nav> </Nav>
<HeaderNotifications currentUser={currentUser} /> <HeaderNotifications />
{navRoutesLinks} {navRoutesLinks}
</CollapsibleNav> </Navbar.Collapse>
</Navbar> </Navbar>
<p className="ascribe-print-header visible-print"> <p className="ascribe-print-header visible-print">
<span className="icon-ascribe-logo" /> <span className="icon-ascribe-logo" />
@ -230,4 +219,4 @@ let Header = React.createClass({
} }
}); });
export default Header; export default withContext(Header, 'currentUser', 'isLoggedIn', 'whitelabel');

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router';
import DropdownButton from 'react-bootstrap/lib/DropdownButton'; import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import MenuItem from 'react-bootstrap/lib/MenuItem'; import MenuItem from 'react-bootstrap/lib/MenuItem';
@ -11,12 +10,17 @@ import Nav from 'react-bootstrap/lib/Nav';
import NotificationActions from '../actions/notification_actions'; import NotificationActions from '../actions/notification_actions';
import NotificationStore from '../stores/notification_store'; import NotificationStore from '../stores/notification_store';
import withContext from './context/with_context';
import { currentUserShape } from './prop_types';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
let HeaderNotifications = React.createClass({ let HeaderNotifications = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired // Injected through HOCs
currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
isLoggedIn: React.PropTypes.bool.isRequired // eslint-disable-line react/sort-prop-types
}, },
getInitialState() { getInitialState() {
@ -26,7 +30,7 @@ let HeaderNotifications = React.createClass({
componentDidMount() { componentDidMount() {
NotificationStore.listen(this.onChange); NotificationStore.listen(this.onChange);
if (this.props.currentUser.email) { if (this.props.isLoggedIn) {
this.refreshNotifications(); this.refreshNotifications();
} }
}, },
@ -45,31 +49,6 @@ let HeaderNotifications = React.createClass({
this.setState(state); this.setState(state);
}, },
onMenuItemClick() {
/*
This is a hack to make the dropdown close after clicking on an item
The function just need to be defined
from https://github.com/react-bootstrap/react-bootstrap/issues/368:
@jvillasante - Have you tried to use onSelect with the DropdownButton?
I don't have a working example that is exactly like yours,
but I just noticed that the Dropdown closes when I've attached an event handler to OnSelect:
<DropdownButton eventKey={3} title="Admin" onSelect={ this.OnSelected } >
onSelected: function(e) {
// doesn't need to have functionality (necessarily) ... just wired up
}
Internally, a call to DropdownButton.setDropDownState(false) is made which will hide the dropdown menu.
So, you should be able to call that directly on the DropdownButton instance as well if needed.
NOW, THAT DIDN'T WORK - the onSelect routine isnt triggered in all cases
Hence, we do this manually
*/
this.refs.dropdownbutton.setDropdownState(false);
},
refreshNotifications() { refreshNotifications() {
NotificationActions.fetchPieceListNotifications(); NotificationActions.fetchPieceListNotifications();
NotificationActions.fetchEditionListNotifications(); NotificationActions.fetchEditionListNotifications();
@ -83,12 +62,18 @@ let HeaderNotifications = React.createClass({
{`${(isPiece ? 'Artworks' : 'Editions')} (${notifications.length})`} {`${(isPiece ? 'Artworks' : 'Editions')} (${notifications.length})`}
</div> </div>
{notifications.map((notification, i) => { {notifications.map((notification, i) => {
const pieceOrEdition = isPiece ? notification.piece : notification.edition;
const href = isPiece ? `/pieces/${pieceOrEdition.id}`
: `/editions/${pieceOrEdition.bitcoin_id}`;
return ( return (
<MenuItem eventKey={i + 2}> <MenuItem
key={href}
eventKey={i + 2}
href={href}>
<NotificationListItem <NotificationListItem
notification={notification.notification} notification={notification.notification}
pieceOrEdition={isPiece ? notification.piece : notification.edition} pieceOrEdition={pieceOrEdition} />
onClick={this.onMenuItemClick}/>
</MenuItem> </MenuItem>
); );
})} })}
@ -112,7 +97,7 @@ let HeaderNotifications = React.createClass({
} }
return ( return (
<Nav navbar right> <Nav navbar pullRight>
<DropdownButton <DropdownButton
ref='dropdownButton' ref='dropdownButton'
id="header-notification-dropdown" id="header-notification-dropdown"
@ -138,25 +123,6 @@ let NotificationListItem = React.createClass({
propTypes: { propTypes: {
notification: React.PropTypes.array, notification: React.PropTypes.array,
pieceOrEdition: React.PropTypes.object, pieceOrEdition: React.PropTypes.object,
onClick: React.PropTypes.func
},
isPiece() {
return !(this.props.pieceOrEdition && this.props.pieceOrEdition.parent);
},
getLinkData() {
let { pieceOrEdition } = this.props;
if (this.isPiece()) {
return `/pieces/${pieceOrEdition.id}`;
} else {
return `/editions/${pieceOrEdition.bitcoin_id}`;
}
},
onClick(event){
this.props.onClick(event);
}, },
getNotificationText(){ getNotificationText(){
@ -174,7 +140,6 @@ let NotificationListItem = React.createClass({
render() { render() {
if (this.props.pieceOrEdition) { if (this.props.pieceOrEdition) {
return ( return (
<Link to={this.getLinkData()} onClick={this.onClick}>
<div className="row notification-wrapper"> <div className="row notification-wrapper">
<div className="col-xs-4 clear-paddings"> <div className="col-xs-4 clear-paddings">
<div className="thumbnail-wrapper"> <div className="thumbnail-wrapper">
@ -187,10 +152,10 @@ let NotificationListItem = React.createClass({
{this.getNotificationText()} {this.getNotificationText()}
</div> </div>
</div> </div>
</Link>); );
} }
return null; return null;
} }
}); });
export default HeaderNotifications; export default withContext(HeaderNotifications, 'currentUser', 'isLoggedIn');

View File

@ -1,35 +1,31 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import Link from 'react-router/es6/Link';
import LoginForm from './ascribe_forms/form_login'; import LoginForm from './ascribe_forms/form_login';
import { getLangText } from '../utils/lang_utils'; import withContext from './context/with_context';
import { whitelabelShape } from './prop_types';
import { setDocumentTitle } from '../utils/dom_utils'; import { setDocumentTitle } from '../utils/dom_utils';
import { getLangText } from '../utils/lang_utils';
let LoginContainer = React.createClass({ let LoginContainer = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
}, },
render() { render() {
const { whitelabel: { name: whitelabelName }, const { whitelabel: { name: whitelabelName } } = this.props;
location } = this.props;
setDocumentTitle(getLangText('Log in')); setDocumentTitle(getLangText('Log in'));
return ( return (
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<LoginForm <LoginForm whitelabelName={whitelabelName} />
location={location}
whitelabelName={whitelabelName} />
<div className="ascribe-login-text"> <div className="ascribe-login-text">
{getLangText(`Not a ${whitelabelName || 'ascribe'} user`)}&#63; <Link to="/signup">{getLangText('Sign up')}...</Link><br/> {getLangText(`Not a ${whitelabelName || 'ascribe'} user`)}&#63; <Link to="/signup">{getLangText('Sign up')}...</Link><br/>
{getLangText('Forgot my password')}&#63; <Link to="/password_reset">{getLangText('Rescue me')}...</Link> {getLangText('Forgot my password')}&#63; <Link to="/password_reset">{getLangText('Rescue me')}...</Link>
@ -39,6 +35,4 @@ let LoginContainer = React.createClass({
} }
}); });
export default withContext(LoginContainer, 'whitelabel');
export default LoginContainer;

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import AscribeSpinner from './ascribe_spinner'; import AscribeSpinner from './ascribe_spinner';
@ -12,17 +11,6 @@ import { setDocumentTitle } from '../utils/dom_utils';
let LogoutContainer = React.createClass({ let LogoutContainer = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
},
mixins: [History],
componentDidMount() { componentDidMount() {
UserActions.logoutCurrentUser(); UserActions.logoutCurrentUser();
}, },

View File

@ -1,27 +1,27 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import Form from './ascribe_forms/form';
import Property from './ascribe_forms/property';
import ApiUrls from '../constants/api_urls';
import AscribeSpinner from './ascribe_spinner';
import GlobalNotificationModel from '../models/global_notification_model'; import GlobalNotificationModel from '../models/global_notification_model';
import GlobalNotificationActions from '../actions/global_notification_actions'; import GlobalNotificationActions from '../actions/global_notification_actions';
import { getLangText } from '../utils/lang_utils';
import Form from './ascribe_forms/form';
import Property from './ascribe_forms/property';
import AscribeSpinner from './ascribe_spinner';
import withContext from './context/with_context';
import { locationShape, routerShape } from './prop_types';
import ApiUrls from '../constants/api_urls';
import { setDocumentTitle } from '../utils/dom_utils'; import { setDocumentTitle } from '../utils/dom_utils';
import { getLangText } from '../utils/lang_utils';
let PasswordResetContainer = React.createClass({ let PasswordResetContainer = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object, location: locationShape.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
@ -108,13 +108,14 @@ let PasswordRequestResetForm = React.createClass({
} }
}); });
let PasswordResetForm = React.createClass({ let PasswordResetForm = withContext(React.createClass({
propTypes: { propTypes: {
email: React.PropTypes.string, email: React.PropTypes.string,
token: React.PropTypes.string token: React.PropTypes.string,
},
mixins: [History], // Injected through HOCs
router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
},
getFormData() { getFormData() {
return { return {
@ -124,7 +125,7 @@ let PasswordResetForm = React.createClass({
}, },
handleSuccess() { handleSuccess() {
this.history.push('/collection'); this.props.router.push('/collection');
const notification = new GlobalNotificationModel(getLangText('Password successfully updated'), 'success', 10000); const notification = new GlobalNotificationModel(getLangText('Password successfully updated'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
@ -175,6 +176,6 @@ let PasswordResetForm = React.createClass({
</Form> </Form>
); );
} }
}); }), 'router');
export default PasswordResetContainer; export default withContext(PasswordResetContainer, 'location');

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import PieceListStore from '../stores/piece_list_store'; import PieceListStore from '../stores/piece_list_store';
import PieceListActions from '../actions/piece_list_actions'; import PieceListActions from '../actions/piece_list_actions';
@ -24,14 +21,16 @@ import PieceListBulkModal from './ascribe_piece_list_bulk_modal/piece_list_bulk_
import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar'; import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
import AscribeSpinner from './ascribe_spinner'; import AscribeSpinner from './ascribe_spinner';
import withContext from './context/with_context';
import { locationShape, routerShape } from './prop_types';
import { getAvailableAcls } from '../utils/acl_utils'; import { getAvailableAcls } from '../utils/acl_utils';
import { setDocumentTitle } from '../utils/dom_utils';
import { mergeOptions, isShallowEqual } from '../utils/general_utils'; import { mergeOptions, isShallowEqual } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
import { setDocumentTitle } from '../utils/dom_utils';
let PieceList = React.createClass({ const PieceList = React.createClass({
propTypes: { propTypes: {
accordionListItemType: React.PropTypes.func, accordionListItemType: React.PropTypes.func,
bulkModalButtonListType: React.PropTypes.func, bulkModalButtonListType: React.PropTypes.func,
@ -47,16 +46,11 @@ let PieceList = React.createClass({
orderParams: React.PropTypes.array, orderParams: React.PropTypes.array,
orderBy: React.PropTypes.string, orderBy: React.PropTypes.string,
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, location: locationShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: React.PropTypes.object.isRequired, router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
// Provided from router
location: React.PropTypes.object
}, },
mixins: [History],
getDefaultProps() { getDefaultProps() {
return { return {
accordionListItemType: AccordionListItemWallet, accordionListItemType: AccordionListItemWallet,
@ -129,18 +123,18 @@ let PieceList = React.createClass({
}, },
componentDidUpdate() { componentDidUpdate() {
const { location: { query }, redirectTo, shouldRedirect } = this.props; const { location: { query }, redirectTo, router, shouldRedirect } = this.props;
const { unfilteredPieceListCount } = this.state; const { unfilteredPieceListCount } = this.state;
if (redirectTo && redirectTo.pathname && if (redirectTo && redirectTo.pathname &&
(typeof shouldRedirect === 'function' && shouldRedirect(unfilteredPieceListCount))) { (typeof shouldRedirect === 'function' && shouldRedirect(unfilteredPieceListCount))) {
// FIXME: hack to redirect out of the dispatch cycle // FIXME: hack to redirect out of the dispatch cycle
window.setTimeout(() => this.history.push({ window.setTimeout(() => router.push({
// Occasionally, the back end also sets query parameters for Onion. // Occasionally, the back end also sets query parameters for Onion.
// We need to consider this by merging all passed query parameters, as we'll // We need to consider this by merging all passed query parameters, as we'll
// otherwise end up in a 404 screen // otherwise end up in a 404 screen
query: Object.assign({}, query, redirectTo.query), pathname: redirectTo.pathname,
pathname: redirectTo.pathname query: Object.assign({}, query, redirectTo.query)
}), 0); }), 0);
} }
}, },
@ -195,14 +189,14 @@ let PieceList = React.createClass({
}, },
searchFor(search) { searchFor(search) {
const { location: { pathname } } = this.props; const { location: { pathname }, router } = this.props;
this.loadPieceList({ search, page: 1 }); this.loadPieceList({ search, page: 1 });
this.history.push({ pathname, query: { page: 1 } }); router.push({ pathname, query: { page: 1 } });
}, },
applyFilterBy(filterBy) { applyFilterBy(filterBy) {
const { location: { pathname } } = this.props; const { location: { pathname }, router } = this.props;
this.setState({ this.setState({
isFilterDirty: true isFilterDirty: true
@ -228,7 +222,7 @@ let PieceList = React.createClass({
// we have to redirect the user always to page one as it could be that there is no page two // we have to redirect the user always to page one as it could be that there is no page two
// for filtered pieces // for filtered pieces
this.history.push({ pathname, query: { page: 1 } }); router.push({ pathname, query: { page: 1 } });
}, },
applyOrderBy(orderBy) { applyOrderBy(orderBy) {
@ -277,14 +271,14 @@ let PieceList = React.createClass({
}, },
render() { render() {
const { accordionListItemType: AccordionListItemType, const {
bulkModalButtonListType: BulkModalButtonListType,
currentUser,
customSubmitButton, customSubmitButton,
customThumbnailPlaceholder, customThumbnailPlaceholder,
filterParams, filterParams,
orderParams, orderParams,
whitelabel } = this.props; accordionListItemType: AccordionListItemType,
bulkModalButtonListType: BulkModalButtonListType
} = this.props;
const loadingElement = <AscribeSpinner color='dark-blue' size='lg'/>; const loadingElement = <AscribeSpinner color='dark-blue' size='lg'/>;
@ -313,10 +307,8 @@ let PieceList = React.createClass({
className="ascribe-piece-list-bulk-modal"> className="ascribe-piece-list-bulk-modal">
<BulkModalButtonListType <BulkModalButtonListType
availableAcls={availableAcls} availableAcls={availableAcls}
currentUser={currentUser}
handleSuccess={this.handleAclSuccess} handleSuccess={this.handleAclSuccess}
pieceOrEditions={selectedEditions} pieceOrEditions={selectedEditions}
whitelabel={whitelabel}
className="text-center ascribe-button-list collapse-group"> className="text-center ascribe-button-list collapse-group">
<DeleteButton <DeleteButton
handleSuccess={this.handleAclSuccess} handleSuccess={this.handleAclSuccess}
@ -344,9 +336,7 @@ let PieceList = React.createClass({
key={piece.id} key={piece.id}
className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item" className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item"
content={piece} content={piece}
currentUser={currentUser} thumbnailPlaceholder={customThumbnailPlaceholder}>
thumbnailPlaceholder={customThumbnailPlaceholder}
whitelabel={whitelabel}>
<AccordionListItemTableEditions <AccordionListItemTableEditions
className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2" className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2"
parentId={piece.id} /> parentId={piece.id} />
@ -360,4 +350,4 @@ let PieceList = React.createClass({
} }
}); });
export default PieceList; export default withContext(PieceList, 'location', 'router');

View File

@ -0,0 +1,12 @@
import React from 'react';
const { number, object, shape, string } = React.PropTypes;
export default shape({
acl: object,
email: string,
id: number,
profile: object,
username: string
});

View File

@ -0,0 +1,5 @@
export { default as currentUserShape } from './current_user_shape';
export { default as whitelabelShape } from './whitelabel_shape';
// Re-export PropTypes from react-router
export { locationShape, routerShape } from 'react-router/es6/PropTypes';

View File

@ -0,0 +1,11 @@
import React from 'react';
const { shape, string } = React.PropTypes;
export default shape({
name: string,
subdomain: string,
title: string,
user: string
});

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
@ -15,11 +12,14 @@ import GlobalNotificationActions from '../actions/global_notification_actions';
import Property from './ascribe_forms/property'; import Property from './ascribe_forms/property';
import RegisterPieceForm from './ascribe_forms/form_register_piece'; import RegisterPieceForm from './ascribe_forms/form_register_piece';
import { getLangText } from '../utils/lang_utils'; import withContext from './context/with_context';
import { routerShape, whitelabelShape } from './prop_types';
import { setDocumentTitle } from '../utils/dom_utils'; import { setDocumentTitle } from '../utils/dom_utils';
import { getLangText } from '../utils/lang_utils';
let RegisterPiece = React.createClass( { const RegisterPiece = React.createClass( {
propTypes: { propTypes: {
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
@ -29,16 +29,11 @@ let RegisterPiece = React.createClass( {
React.PropTypes.string React.PropTypes.string
]), ]),
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object, router: routerShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: React.PropTypes.object.isRequired, whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
// Provided from router
location: React.PropTypes.object
}, },
mixins: [History],
getInitialState(){ getInitialState(){
return PieceListStore.getState(); return PieceListStore.getState();
}, },
@ -65,7 +60,7 @@ let RegisterPiece = React.createClass( {
// the piece list up to date // the piece list up to date
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.history.push(`/pieces/${response.piece.id}`); this.props.router.push(`/pieces/${response.piece.id}`);
}, },
getSpecifyEditions() { getSpecifyEditions() {
@ -107,5 +102,4 @@ let RegisterPiece = React.createClass( {
} }
}); });
export default withContext(RegisterPiece, 'router', 'whitelabel');
export default RegisterPiece;

View File

@ -2,6 +2,7 @@
import React from 'react'; import React from 'react';
// FIXME: Input is deprecated
import Input from 'react-bootstrap/lib/Input'; import Input from 'react-bootstrap/lib/Input';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';

View File

@ -1,22 +1,19 @@
'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import Link from 'react-router/es6/Link';
import SignupForm from './ascribe_forms/form_signup'; import SignupForm from './ascribe_forms/form_signup';
import { getLangText } from '../utils/lang_utils'; import withContext from './context/with_context';
import { whitelabelShape } from './prop_types';
import { setDocumentTitle } from '../utils/dom_utils'; import { setDocumentTitle } from '../utils/dom_utils';
import { getLangText } from '../utils/lang_utils';
let SignupContainer = React.createClass({ let SignupContainer = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp // Injected through HOCs
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
@ -34,8 +31,7 @@ let SignupContainer = React.createClass({
}, },
render() { render() {
const { location, const { whitelabel: { name: whitelabelName } } = this.props;
whitelabel: { name: whitelabelName } } = this.props;
const { message, submitted } = this.state; const { message, submitted } = this.state;
setDocumentTitle(getLangText('Sign up')); setDocumentTitle(getLangText('Sign up'));
@ -54,8 +50,7 @@ let SignupContainer = React.createClass({
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<SignupForm <SignupForm
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
whitelabelName={whitelabelName} whitelabelName={whitelabelName} />
location={location}/>
<div className="ascribe-login-text"> <div className="ascribe-login-text">
{getLangText(`Already a ${whitelabelName || 'ascribe'} user`)}&#63; <Link to="/login">{getLangText('Log in')}...</Link><br/> {getLangText(`Already a ${whitelabelName || 'ascribe'} user`)}&#63; <Link to="/login">{getLangText('Log in')}...</Link><br/>
</div> </div>
@ -65,5 +60,4 @@ let SignupContainer = React.createClass({
} }
}); });
export default withContext(SignupContainer, 'whitelabel');
export default SignupContainer;

View File

@ -5,20 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import { getLangText } from '../../../../../utils/lang_utils'; import withContext from '../../../../context/with_context';
import { whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let Vivi23Landing = React.createClass({ let Vivi23Landing = React.createClass({
propTypes: { propTypes: {
customThumbnailPlaceholder: React.PropTypes.func, // Injected through HOCs
whitelabel: whitelabelShape.isRequired
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
}, },
componentWillMount() { componentWillMount() {
@ -69,4 +66,4 @@ let Vivi23Landing = React.createClass({
} }
}); });
export default Vivi23Landing; export default withContext(Vivi23Landing, 'whitelabel');

View File

@ -7,15 +7,6 @@ import Vivi23AccordionListItemThumbnailPlaceholder from './23vivi_accordion_list
import MarketPieceList from '../market/market_piece_list'; import MarketPieceList from '../market/market_piece_list';
let Vivi23PieceList = React.createClass({ let Vivi23PieceList = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
},
render() { render() {
return ( return (
<MarketPieceList <MarketPieceList

View File

@ -5,15 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import withContext from '../../../../context/with_context';
import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
let ArtcityLanding = React.createClass({ let ArtcityLanding = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired
whitelabel: React.PropTypes.object.isRequired
}, },
componentWillMount() { componentWillMount() {
@ -61,4 +63,4 @@ let ArtcityLanding = React.createClass({
} }
}); });
export default ArtcityLanding; export default withContext(ArtcityLanding, 'whitelabel');

View File

@ -7,17 +7,21 @@ import AclButtonList from '../../../../ascribe_buttons/acl_button_list';
import DeleteButton from '../../../../ascribe_buttons/delete_button'; import DeleteButton from '../../../../ascribe_buttons/delete_button';
import AclProxy from '../../../../acl_proxy'; import AclProxy from '../../../../acl_proxy';
import withContext from '../../../../context/with_context';
import { currentUserShape } from '../../../../prop_types';
import { mergeOptions } from '../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../utils/general_utils';
let WalletActionPanel = React.createClass({ let WalletActionPanel = React.createClass({
propTypes: { propTypes: {
piece: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleDeleteSuccess: React.PropTypes.func.isRequired, handleDeleteSuccess: React.PropTypes.func.isRequired,
loadPiece: React.PropTypes.func.isRequired, loadPiece: React.PropTypes.func.isRequired,
submitButtonType: React.PropTypes.func.isRequired piece: React.PropTypes.object.isRequired,
submitButtonType: React.PropTypes.func.isRequired,
// Injected through HOCs
currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
render() { render() {
@ -27,7 +31,6 @@ let WalletActionPanel = React.createClass({
return ( return (
<ListRequestActions <ListRequestActions
pieceOrEditions={piece} pieceOrEditions={piece}
currentUser={currentUser}
handleSuccess={loadPiece} handleSuccess={loadPiece}
notifications={piece.notifications}/>); notifications={piece.notifications}/>);
} else { } else {
@ -46,7 +49,6 @@ let WalletActionPanel = React.createClass({
<AclButtonList <AclButtonList
availableAcls={availableAcls} availableAcls={availableAcls}
className="text-center ascribe-button-list" className="text-center ascribe-button-list"
currentUser={currentUser}
pieceOrEditions={piece} pieceOrEditions={piece}
handleSuccess={loadPiece}> handleSuccess={loadPiece}>
<AclProxy <AclProxy
@ -69,4 +71,4 @@ let WalletActionPanel = React.createClass({
} }
}); });
export default WalletActionPanel; export default withContext(WalletActionPanel, 'currentUser');

View File

@ -13,6 +13,7 @@ import Note from '../../../../ascribe_detail/note';
import Piece from '../../../../../components/ascribe_detail/piece'; import Piece from '../../../../../components/ascribe_detail/piece';
import AscribeSpinner from '../../../../ascribe_spinner'; import AscribeSpinner from '../../../../ascribe_spinner';
import withContext from '../../../../context/with_context';
import ApiUrls from '../../../../../constants/api_urls'; import ApiUrls from '../../../../../constants/api_urls';
@ -22,7 +23,6 @@ import { getLangText } from '../../../../../utils/lang_utils';
let WalletPieceContainer = React.createClass({ let WalletPieceContainer = React.createClass({
propTypes: { propTypes: {
piece: React.PropTypes.object.isRequired, piece: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleDeleteSuccess: React.PropTypes.func.isRequired, handleDeleteSuccess: React.PropTypes.func.isRequired,
loadPiece: React.PropTypes.func.isRequired, loadPiece: React.PropTypes.func.isRequired,
submitButtonType: React.PropTypes.func.isRequired, submitButtonType: React.PropTypes.func.isRequired,
@ -30,13 +30,16 @@ let WalletPieceContainer = React.createClass({
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.array React.PropTypes.array
]) ]),
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired // eslint-disable-line react/sort-prop-types
}, },
render() { render() {
const { children, const { children,
currentUser,
handleDeleteSuccess, handleDeleteSuccess,
isLoggedIn,
loadPiece, loadPiece,
piece, piece,
submitButtonType } = this.props; submitButtonType } = this.props;
@ -45,7 +48,6 @@ let WalletPieceContainer = React.createClass({
return ( return (
<Piece <Piece
piece={piece} piece={piece}
currentUser={currentUser}
header={ header={
<div className="ascribe-detail-header"> <div className="ascribe-detail-header">
<hr style={{marginTop: 0}}/> <hr style={{marginTop: 0}}/>
@ -64,7 +66,6 @@ let WalletPieceContainer = React.createClass({
}> }>
<WalletActionPanel <WalletActionPanel
piece={piece} piece={piece}
currentUser={currentUser}
loadPiece={loadPiece} loadPiece={loadPiece}
handleDeleteSuccess={handleDeleteSuccess} handleDeleteSuccess={handleDeleteSuccess}
submitButtonType={submitButtonType}/> submitButtonType={submitButtonType}/>
@ -76,7 +77,7 @@ let WalletPieceContainer = React.createClass({
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Notes')} title={getLangText('Notes')}
show={!!(currentUser.username || piece.public_note)}> show={!!(isLoggedIn || piece.public_note)}>
<Note <Note
id={() => {return {'id': piece.id}; }} id={() => {return {'id': piece.id}; }}
label={getLangText('Personal note (private)')} label={getLangText('Personal note (private)')}
@ -84,8 +85,7 @@ let WalletPieceContainer = React.createClass({
placeholder={getLangText('Enter your comments ...')} placeholder={getLangText('Enter your comments ...')}
editable={true} editable={true}
successMessage={getLangText('Private note saved')} successMessage={getLangText('Private note saved')}
url={ApiUrls.note_private_piece} url={ApiUrls.note_private_piece} />
currentUser={currentUser}/>
</CollapsibleParagraph> </CollapsibleParagraph>
{children} {children}
</Piece> </Piece>
@ -101,4 +101,4 @@ let WalletPieceContainer = React.createClass({
} }
}); });
export default WalletPieceContainer; export default withContext(WalletPieceContainer, 'isLoggedIn');

View File

@ -12,15 +12,6 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../utils/general_utils';
let CCRegisterPiece = React.createClass({ let CCRegisterPiece = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
},
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
LicenseStore.getState(), LicenseStore.getState(),
@ -93,8 +84,7 @@ let CCRegisterPiece = React.createClass({
{...this.props} {...this.props}
enableLocalHashing={false} enableLocalHashing={false}
headerMessage={getLangText('Register under a Creative Commons license')} headerMessage={getLangText('Register under a Creative Commons license')}
submitMessage={getLangText('Submit')} submitMessage={getLangText('Submit')}>
location={this.props.location}>
{this.getLicenses()} {this.getLicenses()}
</RegisterPiece> </RegisterPiece>
); );

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import EditionListActions from '../../../../../../actions/edition_list_actions'; import EditionListActions from '../../../../../../actions/edition_list_actions';
@ -23,25 +20,23 @@ import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container';
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph'; import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
import AscribeSpinner from '../../../../../ascribe_spinner'; import AscribeSpinner from '../../../../../ascribe_spinner';
import withContext from '../../../../../context/with_context';
import { routerShape } from '../../../../../prop_types';
import { getLangText } from '../../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../../utils/general_utils';
import { getLangText } from '../../../../../../utils/lang_utils';
let CylandPieceContainer = React.createClass({ const CylandPieceContainer = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, router: routerShape.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router // Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
mixins: [History],
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
PieceStore.getInitialState(), PieceStore.getInitialState(),
@ -90,22 +85,19 @@ let CylandPieceContainer = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success'); const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection'); this.props.router.push('/collection');
}, },
render() { render() {
const { piece } = this.state; const { piece } = this.state;
if (piece.id) { if (piece.id) {
const { currentUser } = this.props;
setDocumentTitle(`${piece.artist_name}, ${piece.title}`); setDocumentTitle(`${piece.artist_name}, ${piece.title}`);
return ( return (
<WalletPieceContainer <WalletPieceContainer
{...this.props} {...this.props}
piece={this.state.piece} piece={this.state.piece}
currentUser={currentUser}
loadPiece={this.loadPiece} loadPiece={this.loadPiece}
handleDeleteSuccess={this.handleDeleteSuccess} handleDeleteSuccess={this.handleDeleteSuccess}
submitButtonType={CylandSubmitButton}> submitButtonType={CylandSubmitButton}>
@ -129,4 +121,4 @@ let CylandPieceContainer = React.createClass({
} }
}); });
export default CylandPieceContainer; export default withContext(CylandPieceContainer, 'router');

View File

@ -6,20 +6,17 @@ import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import AscribeSpinner from '../../../../ascribe_spinner'; import withContext from '../../../../context/with_context';
import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let CylandLanding = React.createClass({ let CylandLanding = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
}, },
render() { render() {
@ -67,4 +64,4 @@ let CylandLanding = React.createClass({
} }
}); });
export default CylandLanding; export default withContext(CylandLanding, 'whitelabel');

View File

@ -5,24 +5,26 @@ import PieceList from '../../../../piece_list';
import CylandAccordionListItem from './cyland_accordion_list/cyland_accordion_list_item'; import CylandAccordionListItem from './cyland_accordion_list/cyland_accordion_list_item';
import { getLangText } from '../../../../../utils/lang_utils'; import withContext from '../../../../context/with_context';
import { currentUserShape, whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let CylandPieceList = React.createClass({ let CylandPieceList = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, currentUser: currentUserShape.isRequired,
whitelabel: React.PropTypes.object.isRequired, whitelabel: whitelabelShape.isRequired
// Provided from router
location: React.PropTypes.object
}, },
shouldRedirect(pieceCount) { shouldRedirect(pieceCount) {
const { currentUser: { email: userEmail }, const {
currentUser: { email: userEmail },
whitelabel: { whitelabel: {
user: whitelabelAdminEmail user: whitelabelAdminEmail
} } = this.props; }
} = this.props;
return userEmail !== whitelabelAdminEmail && !pieceCount; return userEmail !== whitelabelAdminEmail && !pieceCount;
}, },
@ -52,4 +54,4 @@ let CylandPieceList = React.createClass({
} }
}); });
export default CylandPieceList; export default withContext(CylandPieceList, 'currentUser', 'whitelabel');

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import Moment from 'moment'; import Moment from 'moment';
@ -26,26 +23,26 @@ import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import SlidesContainer from '../../../../ascribe_slides_container/slides_container'; import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
import withContext from '../../../../context/with_context';
import { currentUserShape, locationShape, routerShape, whitelabelShape } from '../../../../prop_types';
import ApiUrls from '../../../../../constants/api_urls'; import ApiUrls from '../../../../../constants/api_urls';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getAclFormMessage } from '../../../../../utils/form_utils'; import { getAclFormMessage } from '../../../../../utils/form_utils';
import { getLangText } from '../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
let CylandRegisterPiece = React.createClass({ const CylandRegisterPiece = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, currentUser: currentUserShape.isRequired,
whitelabel: React.PropTypes.object.isRequired, location: locationShape.isRequired,
router: routerShape.isRequired,
// Provided from router whitelabel: whitelabelShape.isRequired,
location: React.PropTypes.object
}, },
mixins: [History],
getInitialState(){ getInitialState(){
return mergeOptions( return mergeOptions(
PieceListStore.getState(), PieceListStore.getState(),
@ -110,7 +107,7 @@ let CylandRegisterPiece = React.createClass({
this.refreshPieceList(); this.refreshPieceList();
this.history.push(`/pieces/${this.state.piece.id}`); this.props.router.push(`/pieces/${this.state.piece.id}`);
}, },
nextSlide(queryParams) { nextSlide(queryParams) {
@ -129,7 +126,7 @@ let CylandRegisterPiece = React.createClass({
}, },
render() { render() {
const { currentUser, location, whitelabel } = this.props; const { currentUser, whitelabel } = this.props;
const { piece, step } = this.state; const { piece, step } = this.state;
const today = new Moment(); const today = new Moment();
@ -164,8 +161,7 @@ let CylandRegisterPiece = React.createClass({
glyphiconClassNames={{ glyphiconClassNames={{
pending: 'glyphicon glyphicon-chevron-right', pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock' completed: 'glyphicon glyphicon-lock'
}} }}>
location={location}>
<div data-slide-title={getLangText('Register work')}> <div data-slide-title={getLangText('Register work')}>
<Row className="no-margin"> <Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}> <Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
@ -220,4 +216,4 @@ let CylandRegisterPiece = React.createClass({
} }
}); });
export default CylandRegisterPiece; export default withContext(CylandRegisterPiece, 'currentUser', 'location', 'router', 'whitelabel');

View File

@ -5,18 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions'; import withContext from '../../../../context/with_context';
import WhitelabelStore from '../../../../../stores/whitelabel_store'; import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let DemoLanding = React.createClass({ let DemoLanding = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Provided from WalletApp
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired,
whitelabel: React.PropTypes.object.isRequired
}, },
componentWillMount() { componentWillMount() {
@ -64,4 +63,4 @@ let DemoLanding = React.createClass({
} }
}); });
export default DemoLanding; export default withContext(DemoLanding, 'whitelabel');

View File

@ -14,21 +14,24 @@ import IkonotvSubmitButton from '../ikonotv_buttons/ikonotv_submit_button';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece'; import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
import AclProxy from '../../../../../acl_proxy'; import AclProxy from '../../../../../acl_proxy';
import withContext from '../../../../../context/with_context';
import { currentUserShape } from '../../../../../prop_types';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
let IkonotvAccordionListItem = React.createClass({ let IkonotvAccordionListItem = React.createClass({
propTypes: { propTypes: {
content: React.PropTypes.object.isRequired, content: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
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
]), ]),
className: React.PropTypes.string className: React.PropTypes.string,
// Injected through HOCs
currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
getInitialState() { getInitialState() {
@ -116,4 +119,4 @@ let IkonotvAccordionListItem = React.createClass({
} }
}); });
export default IkonotvAccordionListItem; export default withContext(IkonotvAccordionListItem, 'currentUser');

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
@ -17,24 +14,23 @@ import OwnershipFetcher from '../../../../../fetchers/ownership_fetcher';
import CopyrightAssociationForm from '../../../../ascribe_forms/form_copyright_association'; import CopyrightAssociationForm from '../../../../ascribe_forms/form_copyright_association';
import Property from '../../../../ascribe_forms/property'; import Property from '../../../../ascribe_forms/property';
import withContext from '../../../../context/with_context';
import { currentUserShape, routerShape, whitelabelShape } from '../../../../prop_types';
import AppConstants from '../../../../../constants/application_constants'; import AppConstants from '../../../../../constants/application_constants';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvContractNotifications = React.createClass({ const IkonotvContractNotifications = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, currentUser: currentUserShape.isRequired,
whitelabel: React.PropTypes.object.isRequired, router: routerShape.isRequired,
whitelabel: whitelabelShape.isRequired
// Provided from router
location: React.PropTypes.object
}, },
mixins: [History],
getInitialState() { getInitialState() {
return NotificationStore.getState(); return NotificationStore.getState();
}, },
@ -115,7 +111,7 @@ let IkonotvContractNotifications = React.createClass({
NotificationActions.flushContractAgreementListNotifications(); NotificationActions.flushContractAgreementListNotifications();
NotificationActions.fetchContractAgreementListNotifications(); NotificationActions.fetchContractAgreementListNotifications();
this.history.push('/collection'); this.props.router.push('/collection');
}, },
handleDeny() { handleDeny() {
@ -129,13 +125,13 @@ let IkonotvContractNotifications = React.createClass({
const notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 5000); const notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection'); this.props.router.push('/collection');
}, },
getCopyrightAssociationForm() { getCopyrightAssociationForm() {
const { currentUser } = this.props; const { profile } = this.props.currentUser;
if (currentUser.profile && !currentUser.profile.copyright_association) { if (profile && !profile.copyright_association) {
return ( return (
<div className='notification-contract-footer'> <div className='notification-contract-footer'>
<h1>{getLangText('Are you a member of any copyright societies?')}</h1> <h1>{getLangText('Are you a member of any copyright societies?')}</h1>
@ -143,7 +139,7 @@ let IkonotvContractNotifications = React.createClass({
<p> <p>
{AppConstants.copyrightAssociations.join(', ')} {AppConstants.copyrightAssociations.join(', ')}
</p> </p>
<CopyrightAssociationForm currentUser={currentUser}/> <CopyrightAssociationForm />
</div> </div>
); );
} else { } else {
@ -200,4 +196,4 @@ let IkonotvContractNotifications = React.createClass({
} }
}); });
export default IkonotvContractNotifications; export default withContext(IkonotvContractNotifications, 'currentUser', 'router', 'whitelabel');

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import EditionListActions from '../../../../../../actions/edition_list_actions'; import EditionListActions from '../../../../../../actions/edition_list_actions';
@ -24,25 +21,23 @@ import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container';
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph'; import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
import AscribeSpinner from '../../../../../ascribe_spinner'; import AscribeSpinner from '../../../../../ascribe_spinner';
import withContext from '../../../../../context/with_context';
import { routerShape } from '../../../../../prop_types';
import { getLangText } from '../../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../../utils/general_utils';
import { getLangText } from '../../../../../../utils/lang_utils';
let IkonotvPieceContainer = React.createClass({ const IkonotvPieceContainer = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object, router: routerShape.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router // Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
mixins: [History],
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
PieceListStore.getState(), PieceListStore.getState(),
@ -91,11 +86,10 @@ let IkonotvPieceContainer = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success'); const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection'); this.props.router.push('/collection');
}, },
render() { render() {
const { currentUser } = this.props;
const { piece } = this.state; const { piece } = this.state;
let furtherDetails = ( let furtherDetails = (
@ -129,7 +123,6 @@ let IkonotvPieceContainer = React.createClass({
return ( return (
<WalletPieceContainer <WalletPieceContainer
piece={piece} piece={piece}
currentUser={currentUser}
loadPiece={this.loadPiece} loadPiece={this.loadPiece}
handleDeleteSuccess={this.handleDeleteSuccess} handleDeleteSuccess={this.handleDeleteSuccess}
submitButtonType={IkonotvSubmitButton}> submitButtonType={IkonotvSubmitButton}>
@ -146,4 +139,4 @@ let IkonotvPieceContainer = React.createClass({
} }
}); });
export default IkonotvPieceContainer; export default withContext(IkonotvPieceContainer, 'router');

View File

@ -6,32 +6,32 @@ import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import { getLangText } from '../../../../../utils/lang_utils'; import withContext from '../../../../context/with_context';
import { locationShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvLanding = React.createClass({ let IkonotvLanding = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, isLoggedIn: React.PropTypes.bool.isRequired,
whitelabel: React.PropTypes.object, location: locationShape.isRequired
// Provided from router
location: React.PropTypes.object
}, },
getEnterButton() { getEnterButton() {
const { currentUser, location } = this.props; const { isLoggedIn, location: { query } } = this.props;
let redirect = '/login'; let redirect = '/login';
if (currentUser.email) { if (isLoggedIn) {
redirect = '/collection'; redirect = '/collection';
} else if (location.query.redirect) { } else if (query.redirect) {
redirect = '/' + location.query.redirect; redirect = `/${query.redirect}`;
} }
return ( return (
<LinkContainer to={redirect} query={location.query}> <LinkContainer to={redirect} query={query}>
<Button> <Button>
{getLangText('ENTER TO START')} {getLangText('ENTER TO START')}
</Button> </Button>
@ -101,4 +101,4 @@ let IkonotvLanding = React.createClass({
} }
}); });
export default IkonotvLanding; export default withContext(IkonotvLanding, 'isLoggedIn', 'location');

View File

@ -8,18 +8,18 @@ import NotificationStore from '../../../../../stores/notification_store';
import IkonotvAccordionListItem from './ikonotv_accordion_list/ikonotv_accordion_list_item'; import IkonotvAccordionListItem from './ikonotv_accordion_list/ikonotv_accordion_list_item';
import withContext from '../../../../context/with_context';
import { currentUserShape, whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvPieceList = React.createClass({ let IkonotvPieceList = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, currentUser: currentUserShape.isRequired,
whitelabel: React.PropTypes.object.isRequired, whitelabel: whitelabelShape.isRequired
// Provided from router
location: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
@ -39,10 +39,12 @@ let IkonotvPieceList = React.createClass({
}, },
shouldRedirect(pieceCount) { shouldRedirect(pieceCount) {
const { currentUser: { email: userEmail }, const {
currentUser: { email: userEmail },
whitelabel: { whitelabel: {
user: whitelabelAdminEmail user: whitelabelAdminEmail
} } = this.props; }
} = this.props;
const { contractAgreementListNotifications } = this.state; const { contractAgreementListNotifications } = this.state;
return contractAgreementListNotifications && return contractAgreementListNotifications &&
@ -84,4 +86,4 @@ let IkonotvPieceList = React.createClass({
} }
}); });
export default IkonotvPieceList; export default withContext(IkonotvPieceList, 'currentUser', 'whitelabel');

View File

@ -1,8 +1,5 @@
'use strict';
import React from 'react'; import React from 'react';
import Moment from 'moment'; import Moment from 'moment';
import { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
@ -24,43 +21,46 @@ import LoanForm from '../../../../ascribe_forms/form_loan';
import SlidesContainer from '../../../../ascribe_slides_container/slides_container'; import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
import withContext from '../../../../context/with_context';
import { currentUserShape, locationShape, routerShape, whitelabelShape } from '../../../../prop_types';
import ApiUrls from '../../../../../constants/api_urls'; import ApiUrls from '../../../../../constants/api_urls';
import { mergeOptions } from '../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvRegisterPiece = React.createClass({ const IkonotvRegisterPiece = React.createClass({
propTypes: { propTypes: {
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: React.PropTypes.object.isRequired, location: locationShape.isRequired, // eslint-disable-line react/sort-prop-types
router: routerShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: whitelabelShape.isRequired, // eslint-disable-line react/sort-prop-types
// Provided from router // Provided from router
location: React.PropTypes.object route: React.PropTypes.object.isRequired // eslint-disable-line react/sort-prop-types
}, },
mixins: [History],
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
PieceListStore.getState(), PieceListStore.getState(),
PieceStore.getInitialState(), PieceStore.getInitialState(),
{ {
step: 0, step: 0,
pageExitWarning: getLangText("If you leave this form now, your work will not be loaned to Ikono TV.") pageExitWarning: getLangText('If you leave this form now, your work will not be loaned to Ikono TV.')
} }
); );
}, },
componentDidMount() { componentDidMount() {
const { location: { query }, route, router } = this.props;
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
const queryParams = this.props.location.query;
// Since every step of this register process is atomic, // Since every step of this register process is atomic,
// we may need to enter the process at step 1 or 2. // we may need to enter the process at step 1 or 2.
// If this is the case, we'll need the piece number to complete submission. // If this is the case, we'll need the piece number to complete submission.
@ -68,9 +68,12 @@ let IkonotvRegisterPiece = React.createClass({
// //
// We're using 'in' here as we want to know if 'piece_id' is present in the url, // We're using 'in' here as we want to know if 'piece_id' is present in the url,
// we don't care about the value. // we don't care about the value.
if ('piece_id' in queryParams) { if ('piece_id' in query) {
PieceActions.fetchPiece(queryParams.piece_id); PieceActions.fetchPiece(query.piece_id);
} }
// Warn the user if they try to leave before completing registration
router.setRouteLeaveHook(route, () => this.state.pageExitWarning);
}, },
componentWillUnmount() { componentWillUnmount() {
@ -92,7 +95,7 @@ let IkonotvRegisterPiece = React.createClass({
} }
if (!this.canSubmit()) { if (!this.canSubmit()) {
this.history.push('/collection'); this.props.router.push('/collection');
} else { } else {
this.nextSlide({ piece_id: response.piece.id }); this.nextSlide({ piece_id: response.piece.id });
} }
@ -117,7 +120,7 @@ let IkonotvRegisterPiece = React.createClass({
this.refreshPieceList(); this.refreshPieceList();
this.history.push(`/pieces/${this.state.piece.id}`); this.props.router.push(`/pieces/${this.state.piece.id}`);
}, },
nextSlide(queryParams) { nextSlide(queryParams) {
@ -210,7 +213,6 @@ let IkonotvRegisterPiece = React.createClass({
}, },
render() { render() {
const { location } = this.props;
const { pageExitWarning, step } = this.state; const { pageExitWarning, step } = this.state;
return ( return (
@ -221,7 +223,6 @@ let IkonotvRegisterPiece = React.createClass({
pending: 'glyphicon glyphicon-chevron-right', pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock' completed: 'glyphicon glyphicon-lock'
}} }}
location={location}
pageExitWarning={pageExitWarning}> pageExitWarning={pageExitWarning}>
<div data-slide-title={getLangText('Register work')}> <div data-slide-title={getLangText('Register work')}>
<Row className="no-margin"> <Row className="no-margin">
@ -245,4 +246,4 @@ let IkonotvRegisterPiece = React.createClass({
} }
}); });
export default IkonotvRegisterPiece; export default withContext(IkonotvRegisterPiece, 'currentUser', 'location', 'router', 'whitelabel');

View File

@ -5,18 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import { getLangText } from '../../../../../utils/lang_utils'; import withContext from '../../../../context/with_context';
import { whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let LumenusLanding = React.createClass({ let LumenusLanding = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
}, },
componentWillMount() { componentWillMount() {
@ -66,4 +65,4 @@ let LumenusLanding = React.createClass({
} }
}); });
export default LumenusLanding; export default withContext(LumenusLanding, 'whitelabel');

View File

@ -14,10 +14,8 @@ import { selectFromObject } from '../../../../../../utils/general_utils';
let MarketAclButtonList = React.createClass({ let MarketAclButtonList = React.createClass({
propTypes: { propTypes: {
availableAcls: React.PropTypes.object.isRequired, availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleSuccess: React.PropTypes.func.isRequired, handleSuccess: React.PropTypes.func.isRequired,
pieceOrEditions: React.PropTypes.array.isRequired, pieceOrEditions: React.PropTypes.array.isRequired,
whitelabel: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
@ -27,17 +25,16 @@ let MarketAclButtonList = React.createClass({
}, },
render() { render() {
const { availableAcls, const {
availableAcls,
children, children,
className, className,
currentUser,
handleSuccess, handleSuccess,
pieceOrEditions, pieceOrEditions
whitelabel } = this.props; } = this.props;
const buttonProps = selectFromObject(this.props, [ const buttonProps = selectFromObject(this.props, [
'availableAcls', 'availableAcls',
'currentUser',
'handleSuccess', 'handleSuccess',
'pieceOrEditions' 'pieceOrEditions'
]); ]);
@ -46,10 +43,8 @@ let MarketAclButtonList = React.createClass({
<div className={className}> <div className={className}>
<MarketSubmitButton <MarketSubmitButton
availableAcls={availableAcls} availableAcls={availableAcls}
currentUser={currentUser}
editions={pieceOrEditions} editions={pieceOrEditions}
handleSuccess={handleSuccess} handleSuccess={handleSuccess} />
whitelabel={whitelabel} />
<EmailButton {...buttonProps} /> <EmailButton {...buttonProps} />
<TransferButton {...buttonProps} /> <TransferButton {...buttonProps} />
<UnconsignButton {...buttonProps} /> <UnconsignButton {...buttonProps} />

View File

@ -10,13 +10,12 @@ import MarketAdditionalDataForm from '../market_forms/market_additional_data_for
import MarketErrorConsignUnavailable from '../market_error_consign_unavailable'; import MarketErrorConsignUnavailable from '../market_error_consign_unavailable';
import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory'; import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory';
import ConsignForm from '../../../../../ascribe_forms/form_consign';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper'; import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import AclProxy from '../../../../../acl_proxy'; import AclProxy from '../../../../../acl_proxy';
import withContext from '../../../../../context/with_context';
import ApiUrls from '../../../../../../constants/api_urls'; import { currentUserShape, whitelabelShape } from '../../../../../prop_types';
import { getAclFormMessage, getAclFormDataId } from '../../../../../../utils/form_utils'; import { getAclFormMessage, getAclFormDataId } from '../../../../../../utils/form_utils';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
@ -24,12 +23,14 @@ import { getLangText } from '../../../../../../utils/lang_utils';
let MarketSubmitButton = React.createClass({ let MarketSubmitButton = React.createClass({
propTypes: { propTypes: {
availableAcls: React.PropTypes.object.isRequired, availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
editions: React.PropTypes.array.isRequired, editions: React.PropTypes.array.isRequired,
whitelabel: React.PropTypes.object.isRequired,
className: React.PropTypes.string, 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
whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
}, },
canEditionBeSubmitted(edition) { canEditionBeSubmitted(edition) {
@ -81,12 +82,14 @@ let MarketSubmitButton = React.createClass({
}, },
render() { render() {
const { availableAcls, const {
availableAcls,
currentUser, currentUser,
className, className,
editions, editions,
handleSuccess, handleSuccess,
whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail } } = this.props; whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail }
} = this.props;
const { solePieceId, canEdit, canSubmit } = this.getAggregateEditionDetails(); const { solePieceId, canEdit, canSubmit } = this.getAggregateEditionDetails();
const message = getAclFormMessage({ const message = getAclFormMessage({
@ -184,4 +187,4 @@ let MarketSubmitButton = React.createClass({
} }
}); });
export default MarketSubmitButton; export default withContext(MarketSubmitButton, 'currentUser', 'whitelabel');

View File

@ -5,18 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions'; import withContext from '../../../../context/with_context';
import WhitelabelStore from '../../../../../stores/whitelabel_store'; import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let MarketLanding = React.createClass({ let MarketLanding = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired,
whitelabel: React.PropTypes.object.isRequired
}, },
componentDidUpdate() { componentDidUpdate() {
@ -70,4 +69,4 @@ let MarketLanding = React.createClass({
} }
}); });
export default MarketLanding; export default withContext(MarketLanding, 'whitelabel');

View File

@ -4,22 +4,20 @@ import React from 'react';
import MarketAclButtonList from './market_buttons/market_acl_button_list'; import MarketAclButtonList from './market_buttons/market_acl_button_list';
import withContext from '../../../../context/with_context';
import PieceList from '../../../../piece_list'; import PieceList from '../../../../piece_list';
import { currentUserShape, whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
let MarketPieceList = React.createClass({ let MarketPieceList = React.createClass({
propTypes: { propTypes: {
customThumbnailPlaceholder: React.PropTypes.func, customThumbnailPlaceholder: React.PropTypes.func,
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object.isRequired, currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: React.PropTypes.object.isRequired, whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
// Provided from router
location: React.PropTypes.object
}, },
componentWillMount() { componentWillMount() {
@ -27,11 +25,13 @@ let MarketPieceList = React.createClass({
}, },
render() { render() {
const { currentUser: { email: userEmail }, const {
currentUser: { email: userEmail },
whitelabel: { whitelabel: {
name: whitelabelName = 'Market', name: whitelabelName = 'Market',
user: whitelabelAdminEmail user: whitelabelAdminEmail
} } = this.props; }
} = this.props;
let filterParams = null; let filterParams = null;
let isUserAdmin = null; let isUserAdmin = null;
@ -68,4 +68,4 @@ let MarketPieceList = React.createClass({
} }
}); });
export default MarketPieceList; export default withContext(MarketPieceList, 'currentUser', 'whitelabel');

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
@ -19,22 +16,21 @@ import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import SlidesContainer from '../../../../ascribe_slides_container/slides_container'; import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
import { getLangText } from '../../../../../utils/lang_utils'; import withContext from '../../../../context/with_context';
import { locationShape, routerShape, whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let MarketRegisterPiece = React.createClass({ let MarketRegisterPiece = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object, router: routerShape.isRequired,
whitelabel: React.PropTypes.object.isRequired, location: locationShape.isRequired,
whitelabel: whitelabelShape.isRequired
// Provided from router
location: React.PropTypes.object
}, },
mixins: [History],
getInitialState(){ getInitialState(){
return mergeOptions( return mergeOptions(
PieceListStore.getState(), PieceListStore.getState(),
@ -82,7 +78,7 @@ let MarketRegisterPiece = React.createClass({
handleAdditionalDataSuccess() { handleAdditionalDataSuccess() {
this.refreshPieceList(); this.refreshPieceList();
this.history.push('/collection'); this.props.router.push('/collection');
}, },
nextSlide(queryParams) { nextSlide(queryParams) {
@ -101,10 +97,7 @@ let MarketRegisterPiece = React.createClass({
}, },
render() { render() {
const { location, const { whitelabel: { name: whitelabelName = 'Market' } } = this.props;
whitelabel: {
name: whitelabelName = 'Market'
} } = this.props
const { piece, step } = this.state; const { piece, step } = this.state;
setDocumentTitle(getLangText('Register a new piece')); setDocumentTitle(getLangText('Register a new piece'));
@ -116,8 +109,7 @@ let MarketRegisterPiece = React.createClass({
glyphiconClassNames={{ glyphiconClassNames={{
pending: 'glyphicon glyphicon-chevron-right', pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock' completed: 'glyphicon glyphicon-lock'
}} }}>
location={location}>
<div data-slide-title={getLangText('Register work')}> <div data-slide-title={getLangText('Register work')}>
<Row className="no-margin"> <Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}> <Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
@ -160,4 +152,4 @@ let MarketRegisterPiece = React.createClass({
} }
}); });
export default MarketRegisterPiece; export default withContext(MarketRegisterPiece, 'location', 'router', 'whitelabel');

View File

@ -5,18 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions'; import withContext from '../../../../context/with_context';
import WhitelabelStore from '../../../../../stores/whitelabel_store'; import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let PollineLanding = React.createClass({ let PollineLanding = React.createClass({
propTypes: { propTypes: {
// Provided from WalletApp // Injected through HOCs
currentUser: React.PropTypes.object, whitelabel: whitelabelShape.isRequired,
whitelabel: React.PropTypes.object.isRequired
}, },
componentWillMount() { componentWillMount() {
@ -64,4 +63,4 @@ let PollineLanding = React.createClass({
} }
}); });
export default PollineLanding; export default withContext(PollineLanding, 'whitelabel');

View File

@ -1,11 +1,10 @@
'use strict';
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import AppBase from '../../app_base'; import AppBase from '../../app_base';
import AppRouteWrapper from '../../app_route_wrapper'; import withContext from '../../context/with_context';
import Header from '../../header'; import Header from '../../header';
import { routerShape } from '../../prop_types';
import { getSubdomain } from '../../../utils/general_utils'; import { getSubdomain } from '../../../utils/general_utils';
@ -14,31 +13,26 @@ let WalletApp = React.createClass({
propTypes: { propTypes: {
activeRoute: React.PropTypes.object.isRequired, activeRoute: React.PropTypes.object.isRequired,
children: React.PropTypes.element.isRequired, children: React.PropTypes.element.isRequired,
history: React.PropTypes.object.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
// Provided from AppBase // Injected through HOCs
currentUser: React.PropTypes.object, router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
whitelabel: React.PropTypes.object
}, },
render() { render() {
const { activeRoute, children, currentUser, history, routes, whitelabel } = this.props; const { activeRoute, children, router, routes } = this.props;
const subdomain = getSubdomain(); const subdomain = getSubdomain();
const path = activeRoute && activeRoute.path; const path = activeRoute && activeRoute.path;
const Footer = activeRoute && activeRoute.footer; const RouteFooterType = activeRoute && activeRoute.footer;
let header = null; let header = null;
// if the path of the current activeRoute is not defined, then this is the IndexRoute // if the path of the current activeRoute is not defined, then this is the IndexRoute
if ((!path || history.isActive('/login') || history.isActive('/signup') || history.isActive('/contract_notifications')) if ((!path || router.isActive('/login') || router.isActive('/signup') || router.isActive('/contract_notifications'))
&& (['cyland', 'ikonotv', 'lumenus', '23vivi', 'polline', 'artcity', 'demo', 'liquidgallery']).includes(subdomain)) { && (['cyland', 'ikonotv', 'lumenus', '23vivi', 'polline', 'artcity', 'demo', 'liquidgallery']).includes(subdomain)) {
header = (<div className="hero"/>); header = (<div className="hero"/>);
} else { } else {
header = ( header = (
<Header <Header routes={routes} />
currentUser={currentUser}
routes={routes}
whitelabel={whitelabel} />
); );
} }
@ -47,16 +41,14 @@ let WalletApp = React.createClass({
return ( return (
<div className={classNames('ascribe-app', 'ascribe-wallet-app', `route--${(path ? path.split('/')[0] : 'landing')}`)}> <div className={classNames('ascribe-app', 'ascribe-wallet-app', `route--${(path ? path.split('/')[0] : 'landing')}`)}>
{header} {header}
<AppRouteWrapper <div className="container ascribe-body">
currentUser={currentUser}
whitelabel={whitelabel}>
{/* Routes are injected here */} {/* Routes are injected here */}
{children} {children}
</AppRouteWrapper> </div>
{Footer ? <Footer /> : null} {RouteFooterType ? <RouteFooterType /> : null}
</div> </div>
); );
} }
}); });
export default AppBase(WalletApp); export default AppBase(withContext(WalletApp, 'router'));

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Redirect } from 'react-router'; import Redirect from 'react-router/es6/Redirect';
import getWalletApiUrls from './constants/wallet_api_urls'; import getWalletApiUrls from './constants/wallet_api_urls';
import getWalletRoutes from './wallet_routes'; import getWalletRoutes from './wallet_routes';

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Route, IndexRoute } from 'react-router'; import IndexRoute from 'react-router/es6/IndexRoute';
import Route from 'react-router/es6/Route';
import { ProxyHandler, AuthRedirect } from '../../../components/ascribe_routes/proxy_handler'; import { ProxyHandler, AuthRedirect } from '../../../components/ascribe_routes/proxy_handler';

View File

@ -1,12 +1,10 @@
'use strict'; import useRouterHistory from 'react-router/es6/useRouterHistory';
import useBasename from 'history/lib/useBasename';
import useQueries from 'history/lib/useQueries';
import createBrowserHistory from 'history/lib/createBrowserHistory'; import createBrowserHistory from 'history/lib/createBrowserHistory';
import AppConstants from './constants/application_constants'; import AppConstants from './constants/application_constants';
const history = useBasename(useQueries(createBrowserHistory))({ const history = useRouterHistory(createBrowserHistory)({
basename: AppConstants.baseUrl basename: AppConstants.baseUrl
}); });

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Route } from 'react-router'; import Route from 'react-router/es6/Route';
import AscribeApp from './components/ascribe_app'; import AscribeApp from './components/ascribe_app';

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import update from 'react-addons-update';
import { alt } from '../alt'; import { alt } from '../alt';
import EditionsListActions from '../actions/edition_list_actions'; import EditionsListActions from '../actions/edition_list_actions';
@ -30,7 +30,7 @@ class EditionListStore {
// if edition already exists, just merge // if edition already exists, just merge
if (pieceEditionList[storeEditionIndex]) { if (pieceEditionList[storeEditionIndex]) {
pieceEditionList[storeEditionIndex] = React.addons.update(pieceEditionList[storeEditionIndex], { $merge: updatedEdition }); pieceEditionList[storeEditionIndex] = update(pieceEditionList[storeEditionIndex], { $merge: updatedEdition });
} else { } else {
// if does not exist, assign // if does not exist, assign
pieceEditionList[storeEditionIndex] = updatedEdition; pieceEditionList[storeEditionIndex] = updatedEdition;

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import React from 'react'; import update from 'react-addons-update';
import { alt } from '../alt'; import { alt } from '../alt';
import PieceListActions from '../actions/piece_list_actions'; import PieceListActions from '../actions/piece_list_actions';
@ -62,7 +62,7 @@ class PieceListStore {
pieceList.forEach((piece, i) => { pieceList.forEach((piece, i) => {
const oldPiece = this.pieceList[i]; const oldPiece = this.pieceList[i];
if (oldPiece) { if (oldPiece) {
piece = React.addons.update(piece, { piece = update(piece, {
show: { $set: oldPiece.show } show: { $set: oldPiece.show }
}); });
} }

View File

@ -33,7 +33,7 @@ class WhitelabelStore {
this.whitelabel = whitelabel; this.whitelabel = whitelabel;
} }
onErrorCurrentUser(err) { onErrorWhitelabel(err) {
console.logGlobal(err); console.logGlobal(err);
this.whitelabelMeta.err = err; this.whitelabelMeta.err = err;
} }

7
js/utils/react_utils.js Normal file
View File

@ -0,0 +1,7 @@
/**
* 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';
}

View File

@ -88,9 +88,8 @@
"express": "^4.13.4", "express": "^4.13.4",
"extract-text-webpack-plugin": "^1.0.1", "extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5", "file-loader": "^0.8.5",
"history": "1.17.0",
"html-webpack-plugin": "^2.19.0", "html-webpack-plugin": "^2.19.0",
"invariant": "^2.1.1", "invariant": "^2.2.1",
"isomorphic-fetch": "^2.0.2", "isomorphic-fetch": "^2.0.2",
"moment": "^2.10.6", "moment": "^2.10.6",
"node-sass": "^3.7.0", "node-sass": "^3.7.0",
@ -98,13 +97,15 @@
"q": "^1.4.1", "q": "^1.4.1",
"query-string": "^3.0.0", "query-string": "^3.0.0",
"raven-js": "^1.1.19", "raven-js": "^1.1.19",
"react": "0.13.2", "react": "^15.1.0",
"react-bootstrap": "0.25.1", "react-addons-update": "^15.1.0",
"react-datepicker": "^0.12.0", "react-bootstrap": "^0.29.4",
"react-router": "1.0.3", "react-datepicker": "^0.27.0",
"react-router-bootstrap": "^0.19.0", "react-dom": "^15.1.0",
"react-star-rating": "~1.3.2", "react-router": "^2.4.1",
"react-textarea-autosize": "^2.5.2", "react-router-bootstrap": "^0.23.0",
"react-star-rating": "^1.4.2",
"react-textarea-autosize": "^4.0.2",
"react-transform-hmr": "^1.0.4", "react-transform-hmr": "^1.0.4",
"remove-trailing-slash": "^0.1.0", "remove-trailing-slash": "^0.1.0",
"resolve-url-loader": "^1.4.3", "resolve-url-loader": "^1.4.3",

View File

@ -20,6 +20,10 @@ config.entry.unshift(`webpack-dev-server/client?http://${HOST}:${PORT}/`,
'webpack/hot/dev-server'); 'webpack/hot/dev-server');
config.plugins.push(new webpack.HotModuleReplacementPlugin()); config.plugins.push(new webpack.HotModuleReplacementPlugin());
// Set absolute url for public path to avoid OTS parsing errors in Chrome
// See http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts
config.output.publicPath = `http://${HOST}:${PORT}${config.output.publicPath}`;
// Configure server // Configure server
const compiler = webpack(config); const compiler = webpack(config);