1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-25 18:56:28 +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 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 history from './history';
import AppConstants from './constants/application_constants';
import { getDefaultSubdomainSettings, getSubdomainSettings } from './utils/constants_utils';
import { initLogging } from './utils/error_utils';
import { getSubdomain } from './utils/general_utils';
import requests from './utils/requests';
// FIXME: rename these 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
// 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
window.document.body.classList.add(`client--${settings.subdomain}`);
window.document.body.classList.add(`client--${settings.subdomain || 'ascribe'}`);
AppResolver
.resolve(settings)
@ -70,7 +86,7 @@ const AppGateway = {
}
});
React.render((
ReactDOM.render((
<Router history={history}>
{redirectRoute}
{routes}

View File

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

View File

@ -1,8 +1,4 @@
'use strict';
import React from 'react';
import classNames from 'classnames';
import { History } from 'react-router';
import UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store';
@ -11,8 +7,7 @@ import WhitelabelActions from '../actions/whitelabel_actions';
import WhitelabelStore from '../stores/whitelabel_store';
import GlobalNotification from './global_notification';
import AppConstants from '../constants/application_constants';
import { currentUserShape, locationShape, whitelabelShape } from './prop_types';
import { mergeOptions } from '../utils/general_utils';
@ -23,11 +18,16 @@ export default function AppBase(App) {
propTypes: {
children: React.PropTypes.element.isRequired,
history: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired,
location: locationShape.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
},
childContextTypes: {
currentUser: currentUserShape,
location: locationShape,
whitelabel: whitelabelShape
},
getInitialState() {
return mergeOptions(
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() {
UserStore.listen(this.onChange);
@ -43,18 +51,6 @@ export default function AppBase(App) {
UserActions.fetchCurrentUser();
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() {
@ -68,7 +64,6 @@ export default function AppBase(App) {
render() {
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
// active second-level component object (ie. after App).
@ -78,13 +73,11 @@ export default function AppBase(App) {
<div>
<App
{...this.props}
activeRoute={activeRoute}
currentUser={currentUser}
whitelabel={whitelabel} />
activeRoute={activeRoute} />
<GlobalNotification />
<div id="modal" className="container" />
</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';
import React from 'react';
import { Link } from 'react-router';
import Link from 'react-router/es6/Link';
import { getLangText } from '../../utils/lang_utils';

View File

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

View File

@ -1,7 +1,6 @@
'use strict';
import React from 'react';
import { Link } from 'react-router';
import AccordionListItem from './accordion_list_item';
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 CreateEditionsForm from '../ascribe_forms/create_editions_form';
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 { getLangText } from '../../utils/lang_utils';
let AccordionListItemWallet = React.createClass({
propTypes: {
content: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
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() {
@ -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 AppBase from './app_base';
import AppRouteWrapper from './app_route_wrapper';
import Footer from './footer';
import Header from './header';
let AscribeApp = React.createClass({
const AscribeApp = React.createClass({
propTypes: {
activeRoute: React.PropTypes.object.isRequired,
children: React.PropTypes.element.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
// Provided from AppBase
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
},
render() {
const { activeRoute, children, currentUser, routes, whitelabel } = this.props;
const Footer = activeRoute && activeRoute.footer;
const { activeRoute, children, routes } = this.props;
const RouteFooterType = activeRoute && activeRoute.footer;
return (
<div className="ascribe-app ascribe-default-app">
<Header
currentUser={currentUser}
routes={routes}
whitelabel={whitelabel} />
<AppRouteWrapper
currentUser={currentUser}
whitelabel={whitelabel}>
<Header routes={routes} />
<div className="container ascribe-body">
{/* Routes are injected here */}
{children}
</AppRouteWrapper>
{Footer ? <Footer /> : null}
</div>
{RouteFooterType ? <RouteFooterType /> : null}
</div>
);
}

View File

@ -1,6 +1,6 @@
'use strict';
import React from 'react/addons';
import React from 'react';
import ConsignButton from './acls/consign_button';
import EmailButton from './acls/email_button';
@ -14,7 +14,6 @@ import { selectFromObject } from '../../utils/general_utils';
let AclButtonList = React.createClass({
propTypes: {
availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleSuccess: React.PropTypes.func.isRequired,
pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object,
@ -52,7 +51,7 @@ let AclButtonList = React.createClass({
handleResize() {
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;
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,
buttonsStyle,
className,
currentUser,
handleSuccess,
pieceOrEditions } = this.props;
const buttonProps = selectFromObject(this.props, [
'availableAcls',
'currentUser',
'handleSuccess',
'pieceOrEditions'
]);

View File

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

View File

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

View File

@ -1,6 +1,7 @@
'use strict';
import React from 'react/addons';
import React from 'react';
import update from 'react-addons-update';
const { string, object } = React.PropTypes;
@ -30,7 +31,7 @@ const FormSubmitButton = React.createClass({
},
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 });
},

View File

@ -5,21 +5,26 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button';
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';
let UnConsignRequestButton = React.createClass({
const UnConsignRequestButton = React.createClass({
propTypes: {
currentUser: 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;
return (
<ModalWrapper
@ -45,5 +50,4 @@ ${currentUser.username}`
}
});
export default UnConsignRequestButton;
export default withContext(UnConsignRequestButton, 'currentUser');

View File

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

View File

@ -1,7 +1,6 @@
'use strict';
import React from 'react';
import { History } from 'react-router';
import Row from 'react-bootstrap/lib/Row';
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 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 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 DeleteButton from '../ascribe_buttons/delete_button';
import UnConsignRequestButton from './../ascribe_buttons/unconsign_request_button';
import AclProxy from '../acl_proxy';
import withContext from '../context/with_context';
import { routerShape } from '../prop_types';
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
handleSuccess requires a loadEdition action (could be refactored)
*/
let EditionActionPanel = React.createClass({
const EditionActionPanel = React.createClass({
propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
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() {
return {
@ -77,7 +76,7 @@ let EditionActionPanel = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection');
this.props.router.push('/collection');
},
refreshCollection() {
@ -101,15 +100,11 @@ let EditionActionPanel = React.createClass({
},
render() {
const { actionPanelButtonListType: ActionPanelButtonListType,
currentUser,
edition,
whitelabel } = this.props;
const { edition, actionPanelButtonListType: ActionPanelButtonListType } = this.props;
if (edition.notifications && edition.notifications.length) {
return (
<ListRequestActions
currentUser={currentUser}
notifications={edition.notifications}
pieceOrEditions={[edition]}
handleSuccess={this.handleSuccess} />);
@ -120,10 +115,8 @@ let EditionActionPanel = React.createClass({
<ActionPanelButtonListType
availableAcls={edition.acl}
className="ascribe-button-list"
currentUser={currentUser}
handleSuccess={this.handleSuccess}
pieceOrEditions={[edition]}
whitelabel={whitelabel}>
pieceOrEditions={[edition]} >
<AclProxy
aclObject={edition.acl}
aclName="acl_withdraw_transfer">
@ -170,7 +163,6 @@ let EditionActionPanel = React.createClass({
aclObject={edition.acl}
aclName="acl_request_unconsign">
<UnConsignRequestButton
currentUser={currentUser}
edition={edition}
handleSuccess={this.handleSuccess} />
</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';
import React from 'react';
import { History } from 'react-router';
import ReactError from '../../mixins/react_error';
import { ResourceNotFoundError } from '../../models/errors';
@ -25,16 +24,11 @@ let EditionContainer = React.createClass({
actionPanelButtonListType: React.PropTypes.func,
furtherDetailsType: React.PropTypes.func,
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object
},
mixins: [History, ReactError],
mixins: [ReactError],
getInitialState() {
return EditionStore.getInitialState();
@ -77,7 +71,7 @@ let EditionContainer = React.createClass({
},
render() {
const { actionPanelButtonListType, currentUser, furtherDetailsType, whitelabel } = this.props;
const { actionPanelButtonListType, furtherDetailsType } = this.props;
const { edition, coaMeta } = this.state;
if (edition.id) {
@ -87,11 +81,9 @@ let EditionContainer = React.createClass({
<Edition
actionPanelButtonListType={actionPanelButtonListType}
coaError={coaMeta.err}
currentUser={currentUser}
edition={edition}
furtherDetailsType={furtherDetailsType}
loadEdition={this.loadEdition}
whitelabel={whitelabel} />
loadEdition={this.loadEdition} />
);
} else {
return (

View File

@ -17,12 +17,13 @@ import CollapsibleButton from './../ascribe_collapsible/collapsible_button';
import AscribeSpinner from '../ascribe_spinner';
import AclProxy from '../acl_proxy';
import withContext from '../context/with_context';
import { currentUserShape } from '../prop_types';
import AppConstants from '../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils';
import { extractFileExtensionFromUrl } from '../../utils/file_utils';
import { getLangText } from '../../utils/lang_utils';
const EMBED_IFRAME_HEIGHT = {
@ -36,7 +37,8 @@ let MediaContainer = React.createClass({
content: React.PropTypes.object.isRequired,
refreshObject: React.PropTypes.func.isRequired,
currentUser: React.PropTypes.object
// Injected through HOCs
currentUser: currentUserShape.isRequired // eslint-disable-line react/sort-prop-types
},
getInitialState() {
@ -118,7 +120,7 @@ let MediaContainer = React.createClass({
// 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
// 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']
: 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 GlobalNotificationActions from '../../actions/global_notification_actions';
import Form from './../ascribe_forms/form';
import Property from './../ascribe_forms/property';
import InputTextAreaToggable from './../ascribe_forms/input_textarea_toggable';
import Form from '../ascribe_forms/form';
import Property from '../ascribe_forms/property';
import InputTextAreaToggable from '../ascribe_forms/input_textarea_toggable';
import withContext from '../context/with_context';
import { getLangText } from '../../utils/lang_utils';
let Note = React.createClass({
propTypes: {
currentUser: React.PropTypes.object.isRequired,
id: React.PropTypes.func.isRequired,
url: React.PropTypes.string.isRequired,
@ -22,7 +23,10 @@ let Note = React.createClass({
label: React.PropTypes.string,
placeholder: React.PropTypes.string,
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() {
@ -40,9 +44,18 @@ let Note = React.createClass({
},
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 (
<Form
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,
buttons: React.PropTypes.object,
currentUser: React.PropTypes.object,
header: React.PropTypes.object,
subheader: React.PropTypes.object,
children: React.PropTypes.oneOfType([
@ -32,14 +31,13 @@ let Piece = React.createClass({
},
render() {
const { buttons, children, currentUser, header, piece, subheader } = this.props;
const { buttons, children, header, piece, subheader } = this.props;
return (
<Row>
<Col md={6} className="ascribe-print-col-left">
<MediaContainer
content={piece}
currentUser={currentUser}
refreshObject={this.updatePiece} />
</Col>
<Col md={6} className="ascribe-edition-details ascribe-print-col-right">

View File

@ -1,7 +1,6 @@
'use strict';
import React from 'react';
import { History } from 'react-router';
import Moment from 'moment';
import ReactError from '../../mixins/react_error';
@ -25,7 +24,7 @@ import LicenseDetail from './license_detail';
import Note from './note';
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 CreateEditionsButton from '../ascribe_buttons/create_editions_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 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 AscribeSpinner from '../ascribe_spinner';
import { setDocumentTitle } from '../../utils/dom_utils';
import { mergeOptions } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
import { setDocumentTitle } from '../../utils/dom_utils';
/**
* This is the component that implements resource/data specific functionality
*/
let PieceContainer = React.createClass({
const PieceContainer = React.createClass({
propTypes: {
furtherDetailsType: React.PropTypes.func,
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired, // eslint-disable-line react/sort-prop-types
router: routerShape.isRequired, // eslint-disable-line react/sort-prop-types
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object
},
mixins: [History, ReactError],
mixins: [ReactError],
getDefaultProps() {
return {
@ -164,7 +164,7 @@ let PieceContainer = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection');
this.props.router.push('/collection');
},
getCreateEditionsDialog() {
@ -207,12 +207,11 @@ let PieceContainer = React.createClass({
getActions() {
const { piece } = this.state;
const { currentUser } = this.props;
const { isLoggedIn } = this.props;
if (piece.notifications && piece.notifications.length > 0) {
return (
<ListRequestActions
currentUser={currentUser}
handleSuccess={this.loadPiece}
notifications={piece.notifications}
pieceOrEditions={piece} />
@ -220,7 +219,7 @@ let PieceContainer = React.createClass({
} else {
return (
<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
no more than 1 key, we're hiding the `DetailProperty` actions as otherwise
@ -232,7 +231,6 @@ let PieceContainer = React.createClass({
<AclButtonList
availableAcls={piece.acl}
className="ascribe-button-list"
currentUser={currentUser}
pieceOrEditions={piece}
handleSuccess={this.loadPiece}>
<CreateEditionsButton
@ -257,7 +255,7 @@ let PieceContainer = React.createClass({
},
render() {
const { currentUser, furtherDetailsType: FurtherDetailsType } = this.props;
const { isLoggedIn, furtherDetailsType: FurtherDetailsType } = this.props;
const { piece } = this.state;
if (piece.id) {
@ -266,7 +264,6 @@ let PieceContainer = React.createClass({
return (
<Piece
piece={piece}
currentUser={currentUser}
header={
<div className="ascribe-detail-header">
<hr className="hidden-print" style={{marginTop: 0}} />
@ -297,7 +294,7 @@ let PieceContainer = React.createClass({
</CollapsibleParagraph>
<CollapsibleParagraph
title={getLangText('Notes')}
show={!!(currentUser.username || piece.acl.acl_edit || piece.public_note)}>
show={!!(isLoggedIn || piece.acl.acl_edit || piece.public_note)}>
<Note
id={this.getId}
label={getLangText('Personal note (private)')}
@ -305,8 +302,7 @@ let PieceContainer = React.createClass({
placeholder={getLangText('Enter your comments ...')}
editable={true}
successMessage={getLangText('Private note saved')}
url={ApiUrls.note_private_piece}
currentUser={currentUser} />
url={ApiUrls.note_private_piece} />
<Note
id={this.getId}
label={getLangText('Personal note (public)')}
@ -315,8 +311,7 @@ let PieceContainer = React.createClass({
editable={!!piece.acl.acl_edit}
show={!!(piece.public_note || piece.acl.acl_edit)}
successMessage={getLangText('Public note saved')}
url={ApiUrls.note_public_piece}
currentUser={currentUser} />
url={ApiUrls.note_public_piece} />
</CollapsibleParagraph>
<CollapsibleParagraph
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 GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import ConsignForm from '../ascribe_forms/form_consign';
import UnConsignForm from '../ascribe_forms/form_unconsign';
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 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 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';
let AclFormFactory = React.createClass({
@ -26,12 +29,14 @@ let AclFormFactory = React.createClass({
]).isRequired,
autoFocusProperty: React.PropTypes.string,
currentUser: React.PropTypes.object,
email: React.PropTypes.string,
handleSuccess: React.PropTypes.func,
message: React.PropTypes.string,
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() {
@ -54,21 +59,23 @@ let AclFormFactory = React.createClass({
},
render() {
const { action,
autoFocusProperty,
pieceOrEditions,
currentUser,
email,
message,
labels,
handleSuccess,
showNotification } = this.props;
const {
action,
autoFocusProperty,
pieceOrEditions,
email,
message,
labels,
handleSuccess,
showNotification,
currentUser: { username: senderName }
} = this.props;
const formMessage = message || getAclFormMessage({
senderName,
aclName: action,
entities: pieceOrEditions,
isPiece: this.isPiece(),
senderName: currentUser && currentUser.username
isPiece: this.isPiece()
});
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';
import React from 'react';
import ReactAddons from 'react/addons';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
@ -257,7 +257,7 @@ let Form = React.createClass({
},
renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => {
return React.Children.map(this.props.children, (child, i) => {
if (child) {
// Since refs will be overwritten by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property`
@ -355,7 +355,7 @@ let Form = React.createClass({
let refToValidate = {};
const property = this.refs[refName];
const input = property.refs.input;
const value = input.getDOMNode().value || input.state.value;
const value = ReactDOM.findDOMNode(input).value || input.state.value;
const { max,
min,
pattern,

View File

@ -8,14 +8,22 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
import Form from './form';
import Property from './property';
import withContext from '../context/with_context';
import { currentUserShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils';
let CopyrightAssociationForm = React.createClass({
const { bool } = React.PropTypes;
const CopyrightAssociationForm = React.createClass({
propTypes: {
currentUser: React.PropTypes.object.isRequired
// Injected through HOCs
currentUser: currentUserShape.isRequired,
isLoggedIn: bool.isRequired
},
handleSubmitSuccess() {
@ -28,7 +36,7 @@ let CopyrightAssociationForm = React.createClass({
},
render() {
const { currentUser } = this.props;
const { currentUser, isLoggedIn } = this.props;
const selectDefaultValue = ' -- ' + getLangText('select an association') + ' -- ';
let selectedState = selectDefaultValue;
@ -38,7 +46,7 @@ let CopyrightAssociationForm = React.createClass({
}
}
if (currentUser.email) {
if (isLoggedIn) {
return (
<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';
import React from 'react';
import { History } from 'react-router';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
@ -11,9 +10,11 @@ import UserActions from '../../actions/user_actions';
import Form from './form';
import Property from './property';
import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants';
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';
@ -22,11 +23,11 @@ let LoginForm = React.createClass({
propTypes: {
headerMessage: 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() {
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 { FileStatus } from '../ascribe_uploader/react_s3_fine_uploader_utils';
import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button';
import AscribeSpinner from '../ascribe_spinner';
import withContext from '../context/with_context';
import { currentUserShape, locationShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_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 { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let RegisterPieceForm = React.createClass({
propTypes: {
currentUser: React.PropTypes.object.isRequired,
headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string,
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
disabled: React.PropTypes.bool,
location: React.PropTypes.object,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(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() {
@ -128,7 +130,7 @@ let RegisterPieceForm = React.createClass({
location,
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;
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
]).isRequired,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func
},
@ -99,7 +98,6 @@ let RequestActionForm = React.createClass({
availableAcls={{'acl_unconsign': true}}
buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px'
pieceOrEditions={this.props.pieceOrEditions}
currentUser={this.props.currentUser}
handleSuccess={this.handleSuccess} />
);
} else if (this.props.notifications.action === 'loan_request') {
@ -109,7 +107,6 @@ let RequestActionForm = React.createClass({
buttonAcceptName="LOAN"
buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px'
pieceOrEditions={this.props.pieceOrEditions}
currentUser={this.props.currentUser}
handleSuccess={this.handleSuccess} />
);
} else {

View File

@ -1,7 +1,6 @@
'use strict';
import React from 'react';
import { History } from 'react-router';
import ContractListActions from '../../actions/contract_list_actions';
import ContractListStore from '../../stores/contract_list_store';
@ -13,19 +12,23 @@ import Form from './form';
import Property from './property';
import InputTextAreaToggable from './input_textarea_toggable';
import ApiUrls from '../../constants/api_urls';
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 { mergeOptions } from '../../utils/general_utils';
let SendContractAgreementForm = React.createClass({
const SendContractAgreementForm = React.createClass({
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() {
return mergeOptions(
@ -57,7 +60,7 @@ let SendContractAgreementForm = React.createClass({
const notification = new GlobalNotificationModel(getLangText('Contract agreement sent'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection');
this.props.router.push('/collection');
},
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,
message: React.PropTypes.string,
editions: React.PropTypes.array,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func
},

View File

@ -1,7 +1,6 @@
'use strict';
import React from 'react';
import { History } from 'react-router';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
@ -12,8 +11,11 @@ import Form from './form';
import Property from './property';
import InputCheckbox from './input_checkbox';
import ApiUrls from '../../constants/api_urls';
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';
@ -23,16 +25,16 @@ let SignupForm = React.createClass({
headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string,
handleSuccess: React.PropTypes.func,
location: React.PropTypes.object,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element,
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() {
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';
import React from 'react';
import ReactDOM from 'react-dom';
/**
* 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
let inverseValue = !this.refs.checkbox.getDOMNode().checked;
let inverseValue = !ReactDOM.findDOMNode(this.refs.checkbox).checked;
// pass it to the state
this.setState({value: inverseValue});

View File

@ -1,6 +1,7 @@
'use strict';
import React from 'react/addons';
import React from 'react';
import update from 'react-addons-update';
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
// when the `Form` queries it.
this.setState({
value: React.addons.update(this.state.value, {
value: update(this.state.value, {
terms: { $set: event.target.value }
})
});

View File

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

View File

@ -1,14 +1,12 @@
'use strict';
import React from 'react';
import ReactAddons from 'react/addons';
import ReactDOM from 'react-dom';
import Panel from 'react-bootstrap/lib/Panel';
import AppConstants from '../../constants/application_constants';
import { mergeOptions } from '../../utils/general_utils';
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
// the state of value should only be set if its not undefined and
// actually references something
if(childInput && typeof childInput.getDOMNode().value !== 'undefined') {
if(childInput && typeof ReactDOM.findDOMNode(childInput).value !== 'undefined') {
this.setState({
value: childInput.getDOMNode().value
value: ReactDOM.findDOMNode(childInput).value
});
// 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({
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
// of HTML inputs (which we determine by checking if there 'type' attribute matches
// the ones included in AppConstants.possibleInputTypes).
let inputDOMNode = input.getDOMNode();
let inputDOMNode = ReactDOM.findDOMNode(input);
if(inputDOMNode.type && typeof inputDOMNode.type === 'string' &&
AppConstants.possibleInputTypes.indexOf(inputDOMNode.type.toLowerCase()) > -1) {
inputDOMNode.value = this.state.initialValue;
@ -180,10 +178,10 @@ const Property = React.createClass({
// skip the focus of non-input elements
let nonInputHTMLElements = ['pre', 'div'];
if (this.refs.input &&
nonInputHTMLElements.indexOf(this.refs.input.getDOMNode().nodeName.toLowerCase()) > -1 ) {
nonInputHTMLElements.indexOf(ReactDOM.findDOMNode(this.refs.input).nodeName.toLowerCase()) > -1 ) {
return;
}
this.refs.input.getDOMNode().focus();
ReactDOM.findDOMNode(this.refs.input).focus();
this.setState({
isFocused: true
});
@ -205,7 +203,7 @@ const Property = React.createClass({
errors: null,
// 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,
// if the component is actually being shown (!== 'expanded === false')
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,
// 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-

View File

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

View File

@ -1,66 +1,39 @@
'use strict';
import React from 'react';
import classnames from 'classnames';
let ActionPanel = React.createClass({
propTypes: {
title: React.PropTypes.string,
buttons: React.PropTypes.element,
content: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.element
]),
buttons: React.PropTypes.element,
onClick: React.PropTypes.func,
ignoreFocus: React.PropTypes.bool,
leftColumnWidth: React.PropTypes.string,
onClick: React.PropTypes.func,
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() {
let { leftColumnWidth, rightColumnWidth } = this.props;
const { buttons, content, leftColumnWidth, onClick, rightColumnWidth } = this.props;
return (
<div className={classnames('ascribe-panel-wrapper', {'is-focused': this.state.isFocused})}>
<div
className={'ascribe-panel-wrapper'}
onClick={onClick}>
<div
className="ascribe-panel-table"
style={{width: leftColumnWidth}}>
<div className="ascribe-panel-content">
{this.props.content}
{content}
</div>
</div>
<div
className="ascribe-panel-table"
style={{width: rightColumnWidth}}>
<div className="ascribe-panel-content">
{this.props.buttons}
{buttons}
</div>
</div>
</div>
@ -68,4 +41,4 @@ let ActionPanel = React.createClass({
}
});
export default ActionPanel;
export default ActionPanel;

View File

@ -1,15 +1,16 @@
'use strict';
import React from 'react';
import { RouteContext } from 'react-router';
import history from '../../history';
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';
const { object } = React.PropTypes;
const { bool, object } = React.PropTypes;
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`);
}
return function(currentUser, query) {
return function redirectRoute(router, { query }, { isLoggedIn }) {
const { redirectAuthenticated, redirect } = query;
// 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
// vice versa)
const isLoggedIn = Object.keys(currentUser).length && currentUser.email;
const exprToValidate = when === 'loggedIn' ? isLoggedIn : !isLoggedIn;
// and redirect if `true`.
if (exprToValidate) {
window.setTimeout(() => history.replace({ query, pathname: to }));
window.setTimeout(() => router.replace({ query, pathname: to }));
return true;
// Otherwise there can also be the case that the backend
// wants to redirect the user to a specific route when the user is logged out already
} else if (!exprToValidate && when === 'loggedIn' && redirect) {
delete query.redirect;
window.setTimeout(() => history.replace({ query, pathname: '/' + redirect }));
window.setTimeout(() => router.replace({ query, pathname: `/${redirect}` }));
return true;
} else if (!exprToValidate && when === 'loggedOut' && redirectAuthenticated) {
@ -77,22 +77,19 @@ export function AuthRedirect({ to, when }) {
*/
export function ProxyHandler(...redirectFunctions) {
return (Component) => {
return React.createClass({
displayName: 'ProxyHandler',
// Don't worry about shadowing the HOC here; using a declaration like this allows
// babel-plugin-react-display-name to automatically generate the displayName.
// eslint-disable-next-line no-shadow
const ProxyHandler = React.createClass({
propTypes: {
// Provided from AscribeApp, after the routes have been initialized
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: object
// Injected through HOCs
currentUser: currentUserShape.isRequired,
isLoggedIn: bool.isRequired,
location: locationShape.isRequired,
router: routerShape.isRequired,
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() {
this.evaluateRedirectFunctions();
},
@ -102,15 +99,15 @@ export function ProxyHandler(...redirectFunctions) {
},
evaluateRedirectFunctions(props = this.props) {
const { currentUser, location: { query } } = props;
const { currentUser, isLoggedIn, location, router, whitelabel } = props;
if (UserStore.hasLoaded() && !UserStore.isLoading()) {
const context = { currentUser, isLoggedIn, whitelabel };
for (let i = 0; i < redirectFunctions.length; i++) {
// if a redirectFunction redirects the user,
// it should return `true` and therefore
// stop/avoid the execution of all functions
// that follow
if (redirectFunctions[i](currentUser, query)) {
// if a redirectFunction redirects the user, it should return `true` and
// therefore stop/avoid the execution of all functions that follow
if (redirectFunctions[i](router, location, context)) {
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 GlobalNotificationActions from '../../actions/global_notification_actions';
import CopyrightAssociationForm from '../ascribe_forms/form_copyright_association';
import Form from '../ascribe_forms/form';
import Property from '../ascribe_forms/property';
import InputCheckbox from '../ascribe_forms/input_checkbox';
import Property from '../ascribe_forms/property';
import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph';
import AclProxy from '../acl_proxy';
import CopyrightAssociationForm from '../ascribe_forms/form_copyright_association';
import AscribeSpinner from '../ascribe_spinner';
import withContext from '../context/with_context';
import { currentUserShape, whitelabelShape } from '../prop_types';
import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang_utils';
let AccountSettings = React.createClass({
propTypes: {
currentUser: React.PropTypes.object.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() {
@ -100,7 +104,7 @@ let AccountSettings = React.createClass({
<AclProxy
aclObject={whitelabel}
aclName="acl_view_settings_copyright_association">
<CopyrightAssociationForm currentUser={currentUser} />
<CopyrightAssociationForm />
</AclProxy>
{profile}
</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 CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph';
import CreateContractForm from '../ascribe_forms/form_create_contract';
import ContractListStore from '../../stores/contract_list_store';
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 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 { truncateTextAtCharIndex } from '../../utils/general_utils';
import { getLangText } from '../../utils/lang_utils';
let ContractSettings = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
currentUser: currentUserShape.isRequired,
whitelabel: whitelabelShape.isRequired
},
getInitialState() {
@ -72,7 +72,7 @@ let ContractSettings = React.createClass({
},
render() {
const { currentUser, location, whitelabel } = this.props;
const { currentUser, whitelabel } = this.props;
const publicContracts = this.getPublicContracts();
const privateContracts = this.getPrivateContracts();
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 AccountSettings from './account_settings';
import ApiSettings from './api_settings';
import BitcoinWalletSettings from './bitcoin_wallet_settings';
import APISettings from './api_settings';
import WebhookSettings from './webhook_settings';
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 { getLangText } from '../../utils/lang_utils';
let SettingsContainer = React.createClass({
@ -23,12 +24,9 @@ let SettingsContainer = React.createClass({
React.PropTypes.element
]),
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
},
loadUser(invalidateCache) {
@ -36,22 +34,19 @@ let SettingsContainer = React.createClass({
},
render() {
const { children, currentUser, whitelabel } = this.props;
const { children, isLoggedIn, whitelabel } = this.props;
setDocumentTitle(getLangText('Account settings'));
if (currentUser.username) {
if (isLoggedIn) {
return (
<div className="settings-container">
<AccountSettings
currentUser={currentUser}
loadUser={this.loadUser}
whitelabel={whitelabel} />
<AccountSettings loadUser={this.loadUser} />
{children}
<AclProxy
aclObject={whitelabel}
aclName="acl_view_settings_api">
<APISettings />
<ApiSettings />
</AclProxy>
<WebhookSettings />
<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';
import React from 'react/addons';
import { History, Lifecycle } from 'react-router';
import React from 'react';
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 SlidesContainer = React.createClass({
propTypes: {
children: arrayOf(element),
forwardProcess: bool.isRequired,
children: arrayOf(element),
glyphiconClassNames: shape({
pending: 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() {
return {
containerWidth: 0,
pageExitWarning: null
containerWidth: 0
};
},
@ -37,20 +38,17 @@ const SlidesContainer = React.createClass({
// Initially, we need to dispatch 'resize' once to render correctly
window.dispatchEvent(new Event('resize'));
},
componentWillUnmount() {
window.removeEventListener('resize', this.handleContainerResize);
},
routerWillLeave() {
return this.props.pageExitWarning;
},
handleContainerResize() {
this.setState({
// +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 = {}) {
const { location: { pathname } } = this.props;
const query = Object.assign({}, this.props.location.query, additionalQueryParams, { slide_num: nextSlideNum });
const { location: { pathname, query }, router } = this.props;
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.
@ -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!
renderChildren() {
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
// if its actually present in the url bar, as it will just not match
if(child && i >= startFrom) {
return React.addons.cloneWithProps(child, {
return React.cloneElement(child, {
className: 'ascribe-slide',
style: {
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)
.then(() => {
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() {
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,
counturl,
hashtags,

View File

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

View File

@ -1,7 +1,7 @@
'use strict';
import React from 'react';
import { Link } from 'react-router';
import Link from 'react-router/es6/Link';
import { ColumnModel } from './models/table_models';
@ -50,4 +50,4 @@ let TableItemWrapper = React.createClass({
}
});
export default TableItemWrapper;
export default TableItemWrapper;

View File

@ -55,7 +55,7 @@ let FileDragAndDrop = React.createClass({
},
clearSelection() {
this.refs.fileSelector.getDOMNode().value = '';
this.refs.fileSelector.value = '';
},
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);
}
this.refs.fileSelector.getDOMNode().dispatchEvent(evt);
this.refs.fileSelector.dispatchEvent(evt);
}
},

View File

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

View File

@ -66,7 +66,7 @@ export default function UploadButton({ className = 'btn btn-default btn-sm', sho
},
clearSelection() {
this.refs.fileSelector.getDOMNode().value = '';
this.refs.fileSelector.value = '';
},
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.stopPropagation();
this.refs.fileSelector.getDOMNode().dispatchEvent(evt);
this.refs.fileSelector.dispatchEvent(evt);
}
},

View File

@ -1,6 +1,7 @@
'use strict';
import React from 'react/addons';
import React from 'react';
import update from 'react-addons-update';
// FIXME: remove once using react-components
import fineUploader from 'exports?qq!./vendor/s3.fine-uploader';
import Q from 'q';
@ -536,7 +537,7 @@ const ReactS3FineUploader = React.createClass({
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);
});
@ -547,7 +548,7 @@ const ReactS3FineUploader = React.createClass({
if(fileId < filesToUpload.length) {
const changeSet = { $set: url };
const newFilesToUpload = React.addons.update(filesToUpload, {
const newFilesToUpload = update(filesToUpload, {
[fileId]: { thumbnailUrl: changeSet }
});
@ -574,7 +575,7 @@ const ReactS3FineUploader = React.createClass({
completed: false
};
let startedChunks = React.addons.update(this.state.startedChunks, { $set: chunks });
let startedChunks = update(this.state.startedChunks, { $set: chunks });
this.setState({ startedChunks });
},
@ -588,7 +589,7 @@ const ReactS3FineUploader = React.createClass({
chunks[chunkKey].responseJson = responseJson;
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 });
}
@ -619,7 +620,7 @@ const ReactS3FineUploader = React.createClass({
files[id].status = FileStatus.UPLOAD_SUCCESSFUL;
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 });
// 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) {
notificationMessage = errorNotificationMessage;
const errorState = React.addons.update(this.state.errorState, {
const errorState = update(this.state.errorState, {
errorClass: {
$set: this.getUploadErrorClass({
reason: errorReason,
@ -720,7 +721,7 @@ const ReactS3FineUploader = React.createClass({
},
onProgress(id, name, uploadedBytes, totalBytes) {
let filesToUpload = React.addons.update(this.state.filesToUpload, {
let filesToUpload = update(this.state.filesToUpload, {
[id]: {
progress: { $set: (uploadedBytes / totalBytes) * 100}
}
@ -747,7 +748,7 @@ const ReactS3FineUploader = React.createClass({
return file;
});
let filesToUpload = React.addons.update(this.state.filesToUpload, {$set: updatedFilesToUpload});
let filesToUpload = update(this.state.filesToUpload, {$set: updatedFilesToUpload});
this.setState({filesToUpload });
} else {
@ -846,7 +847,7 @@ const ReactS3FineUploader = React.createClass({
fileIds.forEach((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({
@ -1041,7 +1042,7 @@ const ReactS3FineUploader = React.createClass({
}
// 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 }, () => {
// 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 Form from './ascribe_forms/form';
import Property from './ascribe_forms/property';
import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable';
import Property from './ascribe_forms/property';
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 { setDocumentTitle } from '../utils/dom_utils';
let CoaVerifyContainer = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
location: locationShape.isRequired
},
render() {
@ -117,5 +116,4 @@ let CoaVerifyForm = React.createClass({
}
});
export default CoaVerifyContainer;
export default withContext(CoaVerifyContainer, 'location');

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 { History } from 'react-router';
import history from '../history';
import { getLangText } from '../utils/lang_utils';
let ErrorNotFoundPage = React.createClass({
const ErrorNotFoundPage = React.createClass({
propTypes: {
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() {
return {
message: getLangText("Oops, the page you are looking for doesn't exist.")
@ -28,7 +18,7 @@ let ErrorNotFoundPage = React.createClass({
componentDidMount() {
// The previous page, if any, is the second item in the locationQueue
const { locationQueue: [ , previousPage ] } = this.history;
const { locationQueue: [, previousPage] } = history;
if (previousPage) {
console.logGlobal('Page not found', {

View File

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

View File

@ -3,35 +3,35 @@
import React from 'react';
import { Link } from 'react-router';
import history from '../history';
import Nav from 'react-bootstrap/lib/Nav';
import Navbar from 'react-bootstrap/lib/Navbar';
import CollapsibleNav from 'react-bootstrap/lib/CollapsibleNav';
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import MenuItem from 'react-bootstrap/lib/MenuItem';
import NavItem from 'react-bootstrap/lib/NavItem';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import EventActions from '../actions/event_actions';
import PieceListStore from '../stores/piece_list_store';
import AclProxy from './acl_proxy';
import withContext from './context/with_context';
import HeaderNotifications from './header_notifications';
import HeaderNotificationDebug from './header_notification_debug';
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 { getLangText } from '../utils/lang_utils';
let Header = React.createClass({
propTypes: {
currentUser: 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() {
@ -42,16 +42,10 @@ let Header = React.createClass({
// Listen to the piece list store, but don't fetch immediately to avoid
// conflicts with routes that may need to wait to load the piece list
PieceListStore.listen(this.onChange);
// 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() {
PieceListStore.unlisten(this.onChange);
//history.unlisten(this.onRouteChange);
},
onChange(state) {
@ -116,24 +110,15 @@ let Header = React.createClass({
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() {
const { currentUser, routes, whitelabel } = this.props;
const { currentUser, isLoggedIn, routes, whitelabel } = this.props;
const { unfilteredPieceListCount } = this.state;
let account;
let signup;
let navRoutesLinks;
if (currentUser.username) {
if (isLoggedIn) {
account = (
<DropdownButton
ref='dropdownbutton'
@ -175,7 +160,7 @@ let Header = React.createClass({
navRoutesLinks = (
<NavRoutesLinks
navbar
right
pullRight
hasPieces={!!unfilteredPieceListCount}
routes={routes}
userAcl={currentUser.acl} />
@ -201,26 +186,30 @@ let Header = React.createClass({
<div>
<Navbar
ref="navbar"
brand={this.getLogo()}
toggleNavKey={0}
fixedTop={true}
className="hidden-print">
<CollapsibleNav eventKey={0}>
<Nav navbar left>
<Navbar.Header>
<Navbar.Brand>
{this.getLogo()}
</Navbar.Brand>
</Navbar.Header>
<Navbar.Collapse
eventKey={0}>
<Nav navbar pullLeft>
<AclProxy
aclObject={whitelabel}
aclName="acl_view_powered_by">
{this.getPoweredBy()}
</AclProxy>
</Nav>
<Nav navbar right>
<HeaderNotificationDebug show={false} />
<Nav navbar pullRight>
<HeaderNotificationDebug show={false}/>
{account}
{signup}
</Nav>
<HeaderNotifications currentUser={currentUser} />
<HeaderNotifications />
{navRoutesLinks}
</CollapsibleNav>
</Navbar.Collapse>
</Navbar>
<p className="ascribe-print-header visible-print">
<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';
import React from 'react';
import { Link } from 'react-router';
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
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 NotificationStore from '../stores/notification_store';
import withContext from './context/with_context';
import { currentUserShape } from './prop_types';
import { getLangText } from '../utils/lang_utils';
let HeaderNotifications = React.createClass({
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() {
@ -26,7 +30,7 @@ let HeaderNotifications = React.createClass({
componentDidMount() {
NotificationStore.listen(this.onChange);
if (this.props.currentUser.email) {
if (this.props.isLoggedIn) {
this.refreshNotifications();
}
},
@ -45,31 +49,6 @@ let HeaderNotifications = React.createClass({
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() {
NotificationActions.fetchPieceListNotifications();
NotificationActions.fetchEditionListNotifications();
@ -83,12 +62,18 @@ let HeaderNotifications = React.createClass({
{`${(isPiece ? 'Artworks' : 'Editions')} (${notifications.length})`}
</div>
{notifications.map((notification, i) => {
const pieceOrEdition = isPiece ? notification.piece : notification.edition;
const href = isPiece ? `/pieces/${pieceOrEdition.id}`
: `/editions/${pieceOrEdition.bitcoin_id}`;
return (
<MenuItem eventKey={i + 2}>
<MenuItem
key={href}
eventKey={i + 2}
href={href}>
<NotificationListItem
notification={notification.notification}
pieceOrEdition={isPiece ? notification.piece : notification.edition}
onClick={this.onMenuItemClick}/>
pieceOrEdition={pieceOrEdition} />
</MenuItem>
);
})}
@ -112,7 +97,7 @@ let HeaderNotifications = React.createClass({
}
return (
<Nav navbar right>
<Nav navbar pullRight>
<DropdownButton
ref='dropdownButton'
id="header-notification-dropdown"
@ -138,25 +123,6 @@ let NotificationListItem = React.createClass({
propTypes: {
notification: React.PropTypes.array,
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(){
@ -174,23 +140,22 @@ let NotificationListItem = React.createClass({
render() {
if (this.props.pieceOrEdition) {
return (
<Link to={this.getLinkData()} onClick={this.onClick}>
<div className="row notification-wrapper">
<div className="col-xs-4 clear-paddings">
<div className="thumbnail-wrapper">
<img src={this.props.pieceOrEdition.thumbnail.url_safe}/>
</div>
</div>
<div className="col-xs-8 notification-list-item-header">
<h1>{this.props.pieceOrEdition.title}</h1>
<div className="sub-header">by {this.props.pieceOrEdition.artist_name}</div>
{this.getNotificationText()}
<div className="row notification-wrapper">
<div className="col-xs-4 clear-paddings">
<div className="thumbnail-wrapper">
<img src={this.props.pieceOrEdition.thumbnail.url_safe}/>
</div>
</div>
</Link>);
<div className="col-xs-8 notification-list-item-header">
<h1>{this.props.pieceOrEdition.title}</h1>
<div className="sub-header">by {this.props.pieceOrEdition.artist_name}</div>
{this.getNotificationText()}
</div>
</div>
);
}
return null;
}
});
export default HeaderNotifications;
export default withContext(HeaderNotifications, 'currentUser', 'isLoggedIn');

View File

@ -1,35 +1,31 @@
'use strict';
import React from 'react';
import { Link } from 'react-router';
import Link from 'react-router/es6/Link';
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 { getLangText } from '../utils/lang_utils';
let LoginContainer = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
whitelabel: whitelabelShape.isRequired
},
render() {
const { whitelabel: { name: whitelabelName },
location } = this.props;
const { whitelabel: { name: whitelabelName } } = this.props;
setDocumentTitle(getLangText('Log in'));
return (
<div className="ascribe-login-wrapper">
<LoginForm
location={location}
whitelabelName={whitelabelName} />
<LoginForm whitelabelName={whitelabelName} />
<div className="ascribe-login-text">
{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>
@ -39,6 +35,4 @@ let LoginContainer = React.createClass({
}
});
export default LoginContainer;
export default withContext(LoginContainer, 'whitelabel');

View File

@ -1,7 +1,6 @@
'use strict';
import React from 'react';
import { History } from 'react-router';
import AscribeSpinner from './ascribe_spinner';
@ -12,17 +11,6 @@ import { setDocumentTitle } from '../utils/dom_utils';
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() {
UserActions.logoutCurrentUser();
},

View File

@ -1,27 +1,27 @@
'use strict';
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 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 { getLangText } from '../utils/lang_utils';
let PasswordResetContainer = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
location: locationShape.isRequired,
},
getInitialState() {
@ -108,13 +108,14 @@ let PasswordRequestResetForm = React.createClass({
}
});
let PasswordResetForm = React.createClass({
let PasswordResetForm = withContext(React.createClass({
propTypes: {
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() {
return {
@ -124,7 +125,7 @@ let PasswordResetForm = React.createClass({
},
handleSuccess() {
this.history.push('/collection');
this.props.router.push('/collection');
const notification = new GlobalNotificationModel(getLangText('Password successfully updated'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
@ -175,6 +176,6 @@ let PasswordResetForm = React.createClass({
</Form>
);
}
});
}), 'router');
export default PasswordResetContainer;
export default withContext(PasswordResetContainer, 'location');

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react';
import { History } from 'react-router';
import PieceListStore from '../stores/piece_list_store';
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 AscribeSpinner from './ascribe_spinner';
import withContext from './context/with_context';
import { locationShape, routerShape } from './prop_types';
import { getAvailableAcls } from '../utils/acl_utils';
import { setDocumentTitle } from '../utils/dom_utils';
import { mergeOptions, isShallowEqual } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils';
import { setDocumentTitle } from '../utils/dom_utils';
let PieceList = React.createClass({
const PieceList = React.createClass({
propTypes: {
accordionListItemType: React.PropTypes.func,
bulkModalButtonListType: React.PropTypes.func,
@ -47,16 +46,11 @@ let PieceList = React.createClass({
orderParams: React.PropTypes.array,
orderBy: React.PropTypes.string,
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
location: locationShape.isRequired, // eslint-disable-line react/sort-prop-types
router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
},
mixins: [History],
getDefaultProps() {
return {
accordionListItemType: AccordionListItemWallet,
@ -129,18 +123,18 @@ let PieceList = React.createClass({
},
componentDidUpdate() {
const { location: { query }, redirectTo, shouldRedirect } = this.props;
const { location: { query }, redirectTo, router, shouldRedirect } = this.props;
const { unfilteredPieceListCount } = this.state;
if (redirectTo && redirectTo.pathname &&
(typeof shouldRedirect === 'function' && shouldRedirect(unfilteredPieceListCount))) {
// 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.
// We need to consider this by merging all passed query parameters, as we'll
// 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);
}
},
@ -195,14 +189,14 @@ let PieceList = React.createClass({
},
searchFor(search) {
const { location: { pathname } } = this.props;
const { location: { pathname }, router } = this.props;
this.loadPieceList({ search, page: 1 });
this.history.push({ pathname, query: { page: 1 } });
router.push({ pathname, query: { page: 1 } });
},
applyFilterBy(filterBy) {
const { location: { pathname } } = this.props;
const { location: { pathname }, router } = this.props;
this.setState({
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
// for filtered pieces
this.history.push({ pathname, query: { page: 1 } });
router.push({ pathname, query: { page: 1 } });
},
applyOrderBy(orderBy) {
@ -277,14 +271,14 @@ let PieceList = React.createClass({
},
render() {
const { accordionListItemType: AccordionListItemType,
bulkModalButtonListType: BulkModalButtonListType,
currentUser,
customSubmitButton,
customThumbnailPlaceholder,
filterParams,
orderParams,
whitelabel } = this.props;
const {
customSubmitButton,
customThumbnailPlaceholder,
filterParams,
orderParams,
accordionListItemType: AccordionListItemType,
bulkModalButtonListType: BulkModalButtonListType
} = this.props;
const loadingElement = <AscribeSpinner color='dark-blue' size='lg'/>;
@ -313,10 +307,8 @@ let PieceList = React.createClass({
className="ascribe-piece-list-bulk-modal">
<BulkModalButtonListType
availableAcls={availableAcls}
currentUser={currentUser}
handleSuccess={this.handleAclSuccess}
pieceOrEditions={selectedEditions}
whitelabel={whitelabel}
className="text-center ascribe-button-list collapse-group">
<DeleteButton
handleSuccess={this.handleAclSuccess}
@ -344,9 +336,7 @@ let PieceList = React.createClass({
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"
content={piece}
currentUser={currentUser}
thumbnailPlaceholder={customThumbnailPlaceholder}
whitelabel={whitelabel}>
thumbnailPlaceholder={customThumbnailPlaceholder}>
<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"
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 { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col';
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 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 { getLangText } from '../utils/lang_utils';
let RegisterPiece = React.createClass( {
const RegisterPiece = React.createClass( {
propTypes: {
headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string,
@ -29,16 +29,11 @@ let RegisterPiece = React.createClass( {
React.PropTypes.string
]),
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
router: routerShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
},
mixins: [History],
getInitialState(){
return PieceListStore.getState();
},
@ -65,7 +60,7 @@ let RegisterPiece = React.createClass( {
// the piece list up to date
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.history.push(`/pieces/${response.piece.id}`);
this.props.router.push(`/pieces/${response.piece.id}`);
},
getSpecifyEditions() {
@ -107,5 +102,4 @@ let RegisterPiece = React.createClass( {
}
});
export default RegisterPiece;
export default withContext(RegisterPiece, 'router', 'whitelabel');

View File

@ -2,6 +2,7 @@
import React from 'react';
// FIXME: Input is deprecated
import Input from 'react-bootstrap/lib/Input';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
@ -140,4 +141,4 @@ const SearchBar = React.createClass({
});
export default SearchBar;
export default SearchBar;

View File

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

View File

@ -5,20 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button';
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 { getLangText } from '../../../../../utils/lang_utils';
let Vivi23Landing = React.createClass({
propTypes: {
customThumbnailPlaceholder: React.PropTypes.func,
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
whitelabel: whitelabelShape.isRequired
},
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';
let Vivi23PieceList = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
},
render() {
return (
<MarketPieceList

View File

@ -5,15 +5,17 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button';
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 { setDocumentTitle } from '../../../../../utils/dom_utils';
let ArtcityLanding = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired
// Injected through HOCs
whitelabel: whitelabelShape.isRequired
},
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 AclProxy from '../../../../acl_proxy';
import withContext from '../../../../context/with_context';
import { currentUserShape } from '../../../../prop_types';
import { mergeOptions } from '../../../../../utils/general_utils';
let WalletActionPanel = React.createClass({
propTypes: {
piece: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleDeleteSuccess: 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() {
@ -27,7 +31,6 @@ let WalletActionPanel = React.createClass({
return (
<ListRequestActions
pieceOrEditions={piece}
currentUser={currentUser}
handleSuccess={loadPiece}
notifications={piece.notifications}/>);
} else {
@ -46,7 +49,6 @@ let WalletActionPanel = React.createClass({
<AclButtonList
availableAcls={availableAcls}
className="text-center ascribe-button-list"
currentUser={currentUser}
pieceOrEditions={piece}
handleSuccess={loadPiece}>
<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 AscribeSpinner from '../../../../ascribe_spinner';
import withContext from '../../../../context/with_context';
import ApiUrls from '../../../../../constants/api_urls';
@ -22,7 +23,6 @@ import { getLangText } from '../../../../../utils/lang_utils';
let WalletPieceContainer = React.createClass({
propTypes: {
piece: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleDeleteSuccess: React.PropTypes.func.isRequired,
loadPiece: React.PropTypes.func.isRequired,
submitButtonType: React.PropTypes.func.isRequired,
@ -30,13 +30,16 @@ let WalletPieceContainer = React.createClass({
children: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.array
])
]),
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired // eslint-disable-line react/sort-prop-types
},
render() {
const { children,
currentUser,
handleDeleteSuccess,
isLoggedIn,
loadPiece,
piece,
submitButtonType } = this.props;
@ -45,7 +48,6 @@ let WalletPieceContainer = React.createClass({
return (
<Piece
piece={piece}
currentUser={currentUser}
header={
<div className="ascribe-detail-header">
<hr style={{marginTop: 0}}/>
@ -64,7 +66,6 @@ let WalletPieceContainer = React.createClass({
}>
<WalletActionPanel
piece={piece}
currentUser={currentUser}
loadPiece={loadPiece}
handleDeleteSuccess={handleDeleteSuccess}
submitButtonType={submitButtonType}/>
@ -76,7 +77,7 @@ let WalletPieceContainer = React.createClass({
</CollapsibleParagraph>
<CollapsibleParagraph
title={getLangText('Notes')}
show={!!(currentUser.username || piece.public_note)}>
show={!!(isLoggedIn || piece.public_note)}>
<Note
id={() => {return {'id': piece.id}; }}
label={getLangText('Personal note (private)')}
@ -84,8 +85,7 @@ let WalletPieceContainer = React.createClass({
placeholder={getLangText('Enter your comments ...')}
editable={true}
successMessage={getLangText('Private note saved')}
url={ApiUrls.note_private_piece}
currentUser={currentUser}/>
url={ApiUrls.note_private_piece} />
</CollapsibleParagraph>
{children}
</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';
let CCRegisterPiece = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
},
getInitialState() {
return mergeOptions(
LicenseStore.getState(),
@ -93,8 +84,7 @@ let CCRegisterPiece = React.createClass({
{...this.props}
enableLocalHashing={false}
headerMessage={getLangText('Register under a Creative Commons license')}
submitMessage={getLangText('Submit')}
location={this.props.location}>
submitMessage={getLangText('Submit')}>
{this.getLicenses()}
</RegisterPiece>
);

View File

@ -1,7 +1,4 @@
'use strict';
import React from 'react';
import { History } from 'react-router';
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 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 { mergeOptions } from '../../../../../../utils/general_utils';
import { getLangText } from '../../../../../../utils/lang_utils';
let CylandPieceContainer = React.createClass({
const CylandPieceContainer = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Injected through HOCs
router: routerShape.isRequired,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object
},
mixins: [History],
getInitialState() {
return mergeOptions(
PieceStore.getInitialState(),
@ -90,22 +85,19 @@ let CylandPieceContainer = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection');
this.props.router.push('/collection');
},
render() {
const { piece } = this.state;
if (piece.id) {
const { currentUser } = this.props;
setDocumentTitle(`${piece.artist_name}, ${piece.title}`);
return (
<WalletPieceContainer
{...this.props}
piece={this.state.piece}
currentUser={currentUser}
loadPiece={this.loadPiece}
handleDeleteSuccess={this.handleDeleteSuccess}
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 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 { getLangText } from '../../../../../utils/lang_utils';
let CylandLanding = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
whitelabel: whitelabelShape.isRequired,
},
render() {
@ -67,4 +64,4 @@ let CylandLanding = React.createClass({
}
});
export default CylandLanding;
export default withContext(CylandLanding, 'whitelabel');

View File

@ -5,26 +5,28 @@ import PieceList from '../../../../piece_list';
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 { getLangText } from '../../../../../utils/lang_utils';
let CylandPieceList = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
currentUser: currentUserShape.isRequired,
whitelabel: whitelabelShape.isRequired
},
shouldRedirect(pieceCount) {
const { currentUser: { email: userEmail },
whitelabel: {
user: whitelabelAdminEmail
} } = this.props;
const {
currentUser: { email: userEmail },
whitelabel: {
user: whitelabelAdminEmail
}
} = this.props;
return userEmail !== whitelabelAdminEmail && !pieceCount;
return userEmail !== whitelabelAdminEmail && !pieceCount;
},
render() {
@ -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 { History } from 'react-router';
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 withContext from '../../../../context/with_context';
import { currentUserShape, locationShape, routerShape, whitelabelShape } from '../../../../prop_types';
import ApiUrls from '../../../../../constants/api_urls';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_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: {
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
currentUser: currentUserShape.isRequired,
location: locationShape.isRequired,
router: routerShape.isRequired,
whitelabel: whitelabelShape.isRequired,
},
mixins: [History],
getInitialState(){
return mergeOptions(
PieceListStore.getState(),
@ -110,7 +107,7 @@ let CylandRegisterPiece = React.createClass({
this.refreshPieceList();
this.history.push(`/pieces/${this.state.piece.id}`);
this.props.router.push(`/pieces/${this.state.piece.id}`);
},
nextSlide(queryParams) {
@ -129,7 +126,7 @@ let CylandRegisterPiece = React.createClass({
},
render() {
const { currentUser, location, whitelabel } = this.props;
const { currentUser, whitelabel } = this.props;
const { piece, step } = this.state;
const today = new Moment();
@ -164,8 +161,7 @@ let CylandRegisterPiece = React.createClass({
glyphiconClassNames={{
pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock'
}}
location={location}>
}}>
<div data-slide-title={getLangText('Register work')}>
<Row className="no-margin">
<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 LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import withContext from '../../../../context/with_context';
import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let DemoLanding = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired
whitelabel: whitelabelShape.isRequired,
},
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 AclProxy from '../../../../../acl_proxy';
import withContext from '../../../../../context/with_context';
import { currentUserShape } from '../../../../../prop_types';
import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
let IkonotvAccordionListItem = React.createClass({
propTypes: {
content: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(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() {
@ -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 { History } from 'react-router';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
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 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 { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvContractNotifications = React.createClass({
const IkonotvContractNotifications = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
currentUser: currentUserShape.isRequired,
router: routerShape.isRequired,
whitelabel: whitelabelShape.isRequired
},
mixins: [History],
getInitialState() {
return NotificationStore.getState();
},
@ -115,7 +111,7 @@ let IkonotvContractNotifications = React.createClass({
NotificationActions.flushContractAgreementListNotifications();
NotificationActions.fetchContractAgreementListNotifications();
this.history.push('/collection');
this.props.router.push('/collection');
},
handleDeny() {
@ -129,13 +125,13 @@ let IkonotvContractNotifications = React.createClass({
const notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection');
this.props.router.push('/collection');
},
getCopyrightAssociationForm(){
const { currentUser } = this.props;
getCopyrightAssociationForm() {
const { profile } = this.props.currentUser;
if (currentUser.profile && !currentUser.profile.copyright_association) {
if (profile && !profile.copyright_association) {
return (
<div className='notification-contract-footer'>
<h1>{getLangText('Are you a member of any copyright societies?')}</h1>
@ -143,7 +139,7 @@ let IkonotvContractNotifications = React.createClass({
<p>
{AppConstants.copyrightAssociations.join(', ')}
</p>
<CopyrightAssociationForm currentUser={currentUser}/>
<CopyrightAssociationForm />
</div>
);
} 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 { History } from 'react-router';
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 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 { mergeOptions } from '../../../../../../utils/general_utils';
import { getLangText } from '../../../../../../utils/lang_utils';
let IkonotvPieceContainer = React.createClass({
const IkonotvPieceContainer = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Injected through HOCs
router: routerShape.isRequired,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object
},
mixins: [History],
getInitialState() {
return mergeOptions(
PieceListStore.getState(),
@ -91,11 +86,10 @@ let IkonotvPieceContainer = React.createClass({
const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection');
this.props.router.push('/collection');
},
render() {
const { currentUser } = this.props;
const { piece } = this.state;
let furtherDetails = (
@ -129,7 +123,6 @@ let IkonotvPieceContainer = React.createClass({
return (
<WalletPieceContainer
piece={piece}
currentUser={currentUser}
loadPiece={this.loadPiece}
handleDeleteSuccess={this.handleDeleteSuccess}
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 { getLangText } from '../../../../../utils/lang_utils';
import withContext from '../../../../context/with_context';
import { locationShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvLanding = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
isLoggedIn: React.PropTypes.bool.isRequired,
location: locationShape.isRequired
},
getEnterButton() {
const { currentUser, location } = this.props;
const { isLoggedIn, location: { query } } = this.props;
let redirect = '/login';
if (currentUser.email) {
if (isLoggedIn) {
redirect = '/collection';
} else if (location.query.redirect) {
redirect = '/' + location.query.redirect;
} else if (query.redirect) {
redirect = `/${query.redirect}`;
}
return (
<LinkContainer to={redirect} query={location.query}>
<LinkContainer to={redirect} query={query}>
<Button>
{getLangText('ENTER TO START')}
</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 withContext from '../../../../context/with_context';
import { currentUserShape, whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvPieceList = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
currentUser: currentUserShape.isRequired,
whitelabel: whitelabelShape.isRequired
},
getInitialState() {
@ -39,10 +39,12 @@ let IkonotvPieceList = React.createClass({
},
shouldRedirect(pieceCount) {
const { currentUser: { email: userEmail },
whitelabel: {
user: whitelabelAdminEmail
} } = this.props;
const {
currentUser: { email: userEmail },
whitelabel: {
user: whitelabelAdminEmail
}
} = this.props;
const { contractAgreementListNotifications } = this.state;
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 Moment from 'moment';
import { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col';
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 withContext from '../../../../context/with_context';
import { currentUserShape, locationShape, routerShape, whitelabelShape } from '../../../../prop_types';
import ApiUrls from '../../../../../constants/api_urls';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvRegisterPiece = React.createClass({
const IkonotvRegisterPiece = React.createClass({
propTypes: {
handleSuccess: React.PropTypes.func,
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Injected through HOCs
currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
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
location: React.PropTypes.object
route: React.PropTypes.object.isRequired // eslint-disable-line react/sort-prop-types
},
mixins: [History],
getInitialState() {
return mergeOptions(
PieceListStore.getState(),
PieceStore.getInitialState(),
{
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() {
const { location: { query }, route, router } = this.props;
PieceListStore.listen(this.onChange);
PieceStore.listen(this.onChange);
const queryParams = this.props.location.query;
// Since every step of this register process is atomic,
// 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.
@ -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 don't care about the value.
if ('piece_id' in queryParams) {
PieceActions.fetchPiece(queryParams.piece_id);
if ('piece_id' in query) {
PieceActions.fetchPiece(query.piece_id);
}
// Warn the user if they try to leave before completing registration
router.setRouteLeaveHook(route, () => this.state.pageExitWarning);
},
componentWillUnmount() {
@ -92,7 +95,7 @@ let IkonotvRegisterPiece = React.createClass({
}
if (!this.canSubmit()) {
this.history.push('/collection');
this.props.router.push('/collection');
} else {
this.nextSlide({ piece_id: response.piece.id });
}
@ -117,7 +120,7 @@ let IkonotvRegisterPiece = React.createClass({
this.refreshPieceList();
this.history.push(`/pieces/${this.state.piece.id}`);
this.props.router.push(`/pieces/${this.state.piece.id}`);
},
nextSlide(queryParams) {
@ -210,7 +213,6 @@ let IkonotvRegisterPiece = React.createClass({
},
render() {
const { location } = this.props;
const { pageExitWarning, step } = this.state;
return (
@ -221,7 +223,6 @@ let IkonotvRegisterPiece = React.createClass({
pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock'
}}
location={location}
pageExitWarning={pageExitWarning}>
<div data-slide-title={getLangText('Register work')}>
<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 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 { getLangText } from '../../../../../utils/lang_utils';
let LumenusLanding = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
whitelabel: whitelabelShape.isRequired,
},
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({
propTypes: {
availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleSuccess: React.PropTypes.func.isRequired,
pieceOrEditions: React.PropTypes.array.isRequired,
whitelabel: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
@ -27,17 +25,16 @@ let MarketAclButtonList = React.createClass({
},
render() {
const { availableAcls,
children,
className,
currentUser,
handleSuccess,
pieceOrEditions,
whitelabel } = this.props;
const {
availableAcls,
children,
className,
handleSuccess,
pieceOrEditions
} = this.props;
const buttonProps = selectFromObject(this.props, [
'availableAcls',
'currentUser',
'handleSuccess',
'pieceOrEditions'
]);
@ -46,10 +43,8 @@ let MarketAclButtonList = React.createClass({
<div className={className}>
<MarketSubmitButton
availableAcls={availableAcls}
currentUser={currentUser}
editions={pieceOrEditions}
handleSuccess={handleSuccess}
whitelabel={whitelabel} />
handleSuccess={handleSuccess} />
<EmailButton {...buttonProps} />
<TransferButton {...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 AclFormFactory from '../../../../../ascribe_forms/acl_form_factory';
import ConsignForm from '../../../../../ascribe_forms/form_consign';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import AclProxy from '../../../../../acl_proxy';
import ApiUrls from '../../../../../../constants/api_urls';
import withContext from '../../../../../context/with_context';
import { currentUserShape, whitelabelShape } from '../../../../../prop_types';
import { getAclFormMessage, getAclFormDataId } from '../../../../../../utils/form_utils';
import { getLangText } from '../../../../../../utils/lang_utils';
@ -24,12 +23,14 @@ import { getLangText } from '../../../../../../utils/lang_utils';
let MarketSubmitButton = React.createClass({
propTypes: {
availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
editions: React.PropTypes.array.isRequired,
whitelabel: React.PropTypes.object.isRequired,
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) {
@ -81,12 +82,14 @@ let MarketSubmitButton = React.createClass({
},
render() {
const { availableAcls,
currentUser,
className,
editions,
handleSuccess,
whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail } } = this.props;
const {
availableAcls,
currentUser,
className,
editions,
handleSuccess,
whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail }
} = this.props;
const { solePieceId, canEdit, canSubmit } = this.getAggregateEditionDetails();
const message = getAclFormMessage({
@ -184,4 +187,4 @@ let MarketSubmitButton = React.createClass({
}
});
export default MarketSubmitButton;
export default withContext(MarketSubmitButton, 'currentUser', 'whitelabel');

View File

@ -5,23 +5,22 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import withContext from '../../../../context/with_context';
import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let MarketLanding = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired
// Injected through HOCs
whitelabel: whitelabelShape.isRequired,
},
componentDidUpdate() {
const { name } = this.props.whitelabel;
if (name) {
setDocumentTitle(`${name} Marketplace`);
}
@ -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 withContext from '../../../../context/with_context';
import PieceList from '../../../../piece_list';
import { currentUserShape, whitelabelShape } from '../../../../prop_types';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let MarketPieceList = React.createClass({
propTypes: {
customThumbnailPlaceholder: React.PropTypes.func,
// Provided from WalletApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
currentUser: currentUserShape.isRequired, // eslint-disable-line react/sort-prop-types
whitelabel: whitelabelShape.isRequired // eslint-disable-line react/sort-prop-types
},
componentWillMount() {
@ -27,11 +25,13 @@ let MarketPieceList = React.createClass({
},
render() {
const { currentUser: { email: userEmail },
whitelabel: {
name: whitelabelName = 'Market',
user: whitelabelAdminEmail
} } = this.props;
const {
currentUser: { email: userEmail },
whitelabel: {
name: whitelabelName = 'Market',
user: whitelabelAdminEmail
}
} = this.props;
let filterParams = 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 { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col';
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 { 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 { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let MarketRegisterPiece = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
// Injected through HOCs
router: routerShape.isRequired,
location: locationShape.isRequired,
whitelabel: whitelabelShape.isRequired
},
mixins: [History],
getInitialState(){
return mergeOptions(
PieceListStore.getState(),
@ -82,7 +78,7 @@ let MarketRegisterPiece = React.createClass({
handleAdditionalDataSuccess() {
this.refreshPieceList();
this.history.push('/collection');
this.props.router.push('/collection');
},
nextSlide(queryParams) {
@ -101,10 +97,7 @@ let MarketRegisterPiece = React.createClass({
},
render() {
const { location,
whitelabel: {
name: whitelabelName = 'Market'
} } = this.props
const { whitelabel: { name: whitelabelName = 'Market' } } = this.props;
const { piece, step } = this.state;
setDocumentTitle(getLangText('Register a new piece'));
@ -116,8 +109,7 @@ let MarketRegisterPiece = React.createClass({
glyphiconClassNames={{
pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock'
}}
location={location}>
}}>
<div data-slide-title={getLangText('Register work')}>
<Row className="no-margin">
<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 LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import withContext from '../../../../context/with_context';
import { whitelabelShape } from '../../../../prop_types';
import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { getLangText } from '../../../../../utils/lang_utils';
let PollineLanding = React.createClass({
propTypes: {
// Provided from WalletApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired
// Injected through HOCs
whitelabel: whitelabelShape.isRequired,
},
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 classNames from 'classnames';
import AppBase from '../../app_base';
import AppRouteWrapper from '../../app_route_wrapper';
import withContext from '../../context/with_context';
import Header from '../../header';
import { routerShape } from '../../prop_types';
import { getSubdomain } from '../../../utils/general_utils';
@ -14,31 +13,26 @@ let WalletApp = React.createClass({
propTypes: {
activeRoute: React.PropTypes.object.isRequired,
children: React.PropTypes.element.isRequired,
history: React.PropTypes.object.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
// Provided from AppBase
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object
// Injected through HOCs
router: routerShape.isRequired // eslint-disable-line react/sort-prop-types
},
render() {
const { activeRoute, children, currentUser, history, routes, whitelabel } = this.props;
const { activeRoute, children, router, routes } = this.props;
const subdomain = getSubdomain();
const path = activeRoute && activeRoute.path;
const Footer = activeRoute && activeRoute.footer;
const RouteFooterType = activeRoute && activeRoute.footer;
let header = null;
// 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)) {
header = (<div className="hero"/>);
} else {
header = (
<Header
currentUser={currentUser}
routes={routes}
whitelabel={whitelabel} />
<Header routes={routes} />
);
}
@ -47,16 +41,14 @@ let WalletApp = React.createClass({
return (
<div className={classNames('ascribe-app', 'ascribe-wallet-app', `route--${(path ? path.split('/')[0] : 'landing')}`)}>
{header}
<AppRouteWrapper
currentUser={currentUser}
whitelabel={whitelabel}>
<div className="container ascribe-body">
{/* Routes are injected here */}
{children}
</AppRouteWrapper>
{Footer ? <Footer /> : null}
</div>
{RouteFooterType ? <RouteFooterType /> : null}
</div>
);
}
});
export default AppBase(WalletApp);
export default AppBase(withContext(WalletApp, 'router'));

View File

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

View File

@ -1,5 +1,6 @@
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';

View File

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

View File

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

View File

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

View File

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

View File

@ -33,7 +33,7 @@ class WhitelabelStore {
this.whitelabel = whitelabel;
}
onErrorCurrentUser(err) {
onErrorWhitelabel(err) {
console.logGlobal(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",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5",
"history": "1.17.0",
"html-webpack-plugin": "^2.19.0",
"invariant": "^2.1.1",
"invariant": "^2.2.1",
"isomorphic-fetch": "^2.0.2",
"moment": "^2.10.6",
"node-sass": "^3.7.0",
@ -98,13 +97,15 @@
"q": "^1.4.1",
"query-string": "^3.0.0",
"raven-js": "^1.1.19",
"react": "0.13.2",
"react-bootstrap": "0.25.1",
"react-datepicker": "^0.12.0",
"react-router": "1.0.3",
"react-router-bootstrap": "^0.19.0",
"react-star-rating": "~1.3.2",
"react-textarea-autosize": "^2.5.2",
"react": "^15.1.0",
"react-addons-update": "^15.1.0",
"react-bootstrap": "^0.29.4",
"react-datepicker": "^0.27.0",
"react-dom": "^15.1.0",
"react-router": "^2.4.1",
"react-router-bootstrap": "^0.23.0",
"react-star-rating": "^1.4.2",
"react-textarea-autosize": "^4.0.2",
"react-transform-hmr": "^1.0.4",
"remove-trailing-slash": "^0.1.0",
"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');
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
const compiler = webpack(config);