1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 09:23:13 +01:00

Merge branch 'master' into AP-7-artcity-consignment-wallet

Conflicts:
	js/components/whitelabel/wallet/components/23vivi/23vivi_landing.js
	js/components/whitelabel/wallet/wallet_app.js
This commit is contained in:
Tim Daubenschütz 2016-02-08 10:05:35 +01:00
commit 030a86e883
136 changed files with 3550 additions and 2255 deletions

10
.gitignore vendored
View File

@ -16,10 +16,14 @@ webapp-dependencies.txt
pids pids
logs logs
results results
node_modules/*
build build/*
gemini-coverage/*
gemini-report/*
test/gemini/screenshots/*
node_modules/*
.DS_Store .DS_Store
.env .env

View File

@ -1,18 +1,19 @@
Introduction Introduction
============ ============
Onion is the web client for Ascribe. The idea is to have a well documented, Onion is the web client for Ascribe. The idea is to have a well documented, modern, easy to test, easy to hack, JavaScript application.
easy to test, easy to hack, JavaScript application.
The code is JavaScript ECMA 6. The code is JavaScript 2015 / ECMAScript 6.
Getting started Getting started
=============== ===============
Install some nice extension for Chrom(e|ium): Install some nice extension for Chrom(e|ium):
- [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) - [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)
- [Alt Developer Tools](https://github.com/goatslacker/alt-devtool)
```bash ```bash
git clone git@github.com:ascribe/onion.git git clone git@github.com:ascribe/onion.git
cd onion cd onion
@ -39,17 +40,34 @@ Additionally, to work on the white labeling functionality, you need to edit your
JavaScript Code Conventions JavaScript Code Conventions
=========================== ===========================
For this project, we're using: For this project, we're using:
* 4 Spaces * 4 Spaces
* We use ES6 * ES6
* We don't use ES6's class declaration for React components because it does not support Mixins as well as Autobinding ([Blog post about it](http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding)) * We don't use ES6's class declaration for React components because it does not support Mixins as well as Autobinding ([Blog post about it](http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding))
* We don't use camel case for file naming but in everything Javascript related * We don't use camel case for file naming but in everything Javascript related
* We use `let` instead of `var`: [SA Post](http://stackoverflow.com/questions/762011/javascript-let-keyword-vs-var-keyword) * We use `momentjs` instead of Javascript's `Date` object, as the native `Date` interface previously introduced bugs and we're including `momentjs` for other dependencies anyway
* We don't use Javascript's `Date` object, as its interface introduced bugs previously and we're including `momentjs` for other dependencies anyways
Make sure to check out the [style guide](https://github.com/ascribe/javascript).
Linting
-------
We use [ESLint](https://github.com/eslint/eslint) with our own [custom ruleset](.eslintrc).
SCSS Code Conventions
=====================
Install [lint-scss](https://github.com/brigade/scss-lint), check the [editor integration docs](https://github.com/brigade/scss-lint#editor-integration) to integrate the lint in your editor.
Some interesting links:
* [Improving Sass code quality on theguardian.com](https://www.theguardian.com/info/developer-blog/2014/may/13/improving-sass-code-quality-on-theguardiancom)
Branch names Branch names
===================== ============
To allow Github and JIRA to track branches while still allowing us to switch branches quickly using a ticket's number (and keep our peace of mind), we have the following rules for naming branches: To allow Github and JIRA to track branches while still allowing us to switch branches quickly using a ticket's number (and keep our peace of mind), we have the following rules for naming branches:
@ -63,22 +81,21 @@ AD-<JIRA-ticket-id>-brief-and-sane-description-of-the-ticket
where `brief-and-sane-description-of-the-ticket` does not need to equal to the issue or ticket's title. where `brief-and-sane-description-of-the-ticket` does not need to equal to the issue or ticket's title.
Example Example
------------- -------
**JIRA ticket name:** `AD-1242 - Frontend caching for simple endpoints to measure perceived page load <more useless information>` **JIRA ticket name:** `AD-1242 - Frontend caching for simple endpoints to measure perceived page load <more useless information>`
**Github branch name:** `AD-1242-caching-solution-for-stores` **Github branch name:** `AD-1242-caching-solution-for-stores`
SCSS Code Conventions
=====================
Install [lint-scss](https://github.com/brigade/scss-lint), check the [editor integration docs](https://github.com/brigade/scss-lint#editor-integration) to integrate the lint in your editor.
Some interesting links:
* [Improving Sass code quality on theguardian.com](https://www.theguardian.com/info/developer-blog/2014/may/13/improving-sass-code-quality-on-theguardiancom)
Testing Testing
=============== =======
Unit Testing
------------
We're using Facebook's jest to do testing as it integrates nicely with react.js as well. We're using Facebook's jest to do testing as it integrates nicely with react.js as well.
Tests are always created per directory by creating a `__tests__` folder. To test a specific file, a `<file_name>_tests.js` file needs to be created. Tests are always created per directory by creating a `__tests__` folder. To test a specific file, a `<file_name>_tests.js` file needs to be created.
@ -88,7 +105,24 @@ This is due to the fact that jest's function mocking and ES6 module syntax are [
Therefore, to require a module in your test file, you need to use CommonJS's `require` syntax. Except for this, all tests can be written in ES6 syntax. Therefore, to require a module in your test file, you need to use CommonJS's `require` syntax. Except for this, all tests can be written in ES6 syntax.
## Workflow Visual Regression Testing
-------------------------
We're using [Gemini](https://github.com/gemini-testing/gemini) for visual regression tests because it supports both PhantomJS2 and SauceLabs.
See the [helper docs](test/gemini/README.md) for information on installing Gemini, its dependencies, and running and writing tests.
Integration Testing
-------------------
We're using [Sauce Labs](https://saucelabs.com/home) with [WD.js](https://github.com/admc/wd) for integration testing across browser grids with Selenium.
See the [helper docs](test/integration/README.md) for information on each part of the test stack and how to run and write tests.
Workflow
========
Generally, when you're runing `gulp serve`, all tests are being run. Generally, when you're runing `gulp serve`, all tests are being run.
If you want to test exclusively (without having the obnoxious ES6Linter warnings), you can just run `gulp jest:watch`. If you want to test exclusively (without having the obnoxious ES6Linter warnings), you can just run `gulp jest:watch`.
@ -139,9 +173,16 @@ A: Easily by starting the your gulp process with the following command:
ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' gulp serve ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' gulp serve
``` ```
Or, by adding these two your environment variables:
```
ONION_BASE_URL='/'
ONION_SERVER_URL='http://localhost.com:8000/'
```
Q: I want to know all dependencies that get bundled into the live build. Q: I want to know all dependencies that get bundled into the live build.
A: ```browserify -e js/app.js --list > webapp-dependencies.txt``` A: ```browserify -e js/app.js --list > webapp-dependencies.txt```
Reading list Reading list
============ ============
@ -154,7 +195,6 @@ Start here
- [alt.js](http://alt.js.org/) - [alt.js](http://alt.js.org/)
- [alt.js readme](https://github.com/goatslacker/alt) - [alt.js readme](https://github.com/goatslacker/alt)
Moar stuff Moar stuff
---------- ----------

View File

@ -97,7 +97,8 @@ gulp.task('browser-sync', function() {
proxy: 'http://localhost:4000', proxy: 'http://localhost:4000',
port: 3000, port: 3000,
open: false, // does not open the browser-window anymore (handled manually) open: false, // does not open the browser-window anymore (handled manually)
ghostMode: false ghostMode: false,
notify: false // stop showing the browsersync pop up
}); });
}); });

View File

@ -8,9 +8,8 @@ class EventActions {
this.generateActions( this.generateActions(
'applicationWillBoot', 'applicationWillBoot',
'applicationDidBoot', 'applicationDidBoot',
'profileDidLoad', 'userDidAuthenticate',
//'userDidLogin', 'userDidLogout',
//'userDidLogout',
'routeDidChange' 'routeDidChange'
); );
} }

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { altUser } from '../alt'; import { alt } from '../alt';
class UserActions { class UserActions {
@ -15,4 +15,4 @@ class UserActions {
} }
} }
export default altUser.createActions(UserActions); export default alt.createActions(UserActions);

View File

@ -4,5 +4,4 @@ import Alt from 'alt';
export let alt = new Alt(); export let alt = new Alt();
export let altThirdParty = new Alt(); export let altThirdParty = new Alt();
export let altUser = new Alt();
export let altWhitelabel = new Alt(); export let altWhitelabel = new Alt();

90
js/components/app_base.js Normal file
View File

@ -0,0 +1,90 @@
'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';
import WhitelabelActions from '../actions/whitelabel_actions';
import WhitelabelStore from '../stores/whitelabel_store';
import GlobalNotification from './global_notification';
import AppConstants from '../constants/application_constants';
import { mergeOptions } from '../utils/general_utils';
export default function AppBase(App) {
return React.createClass({
displayName: 'AppBase',
propTypes: {
children: React.PropTypes.element.isRequired,
history: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
},
getInitialState() {
return mergeOptions(
UserStore.getState(),
WhitelabelStore.getState()
);
},
mixins: [History],
componentDidMount() {
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
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() {
UserStore.unlisten(this.onChange);
WhitelabelActions.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
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).
const activeRoute = routes[1];
return (
<div>
<App
{...this.props}
activeRoute={activeRoute}
currentUser={currentUser}
whitelabel={whitelabel} />
<GlobalNotification />
<div id="modal" className="container" />
</div>
);
}
});
};

View File

@ -0,0 +1,34 @@
'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,14 +1,17 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Link } from 'react-router';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let AccordionList = React.createClass({ let AccordionList = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string,
children: React.PropTypes.arrayOf(React.PropTypes.element).isRequired, children: React.PropTypes.arrayOf(React.PropTypes.element).isRequired,
loadingElement: React.PropTypes.element, loadingElement: React.PropTypes.element.isRequired,
className: React.PropTypes.string,
count: React.PropTypes.number, count: React.PropTypes.number,
itemList: React.PropTypes.arrayOf(React.PropTypes.object), itemList: React.PropTypes.arrayOf(React.PropTypes.object),
search: React.PropTypes.string, search: React.PropTypes.string,
@ -22,7 +25,7 @@ let AccordionList = React.createClass({
render() { render() {
const { search } = this.props; const { search } = this.props;
if(this.props.itemList && this.props.itemList.length > 0) { if (this.props.itemList && this.props.itemList.length > 0) {
return ( return (
<div className={this.props.className}> <div className={this.props.className}>
{this.props.children} {this.props.children}
@ -36,7 +39,7 @@ let AccordionList = React.createClass({
</p> </p>
<p className="text-center"> <p className="text-center">
{getLangText('To register one, click')}&nbsp; {getLangText('To register one, click')}&nbsp;
<a href="register_piece">{getLangText('here')}</a>! <Link to="/register_piece">{getLangText('here')}</Link>!
</p> </p>
</div> </div>
); );

View File

@ -21,16 +21,16 @@ let AccordionListItem = React.createClass({
}, },
render() { render() {
const { linkData, const {
className, linkData,
thumbnail, className,
heading, thumbnail,
subheading, heading,
subsubheading, subheading,
buttons, subsubheading,
badge, buttons,
children } = this.props; badge,
children } = this.props;
return ( return (
<div className="row"> <div className="row">

View File

@ -34,11 +34,10 @@ let AccordionListItemPiece = React.createClass({
}, },
getLinkData() { getLinkData() {
let { piece } = this.props; const { piece } = this.props;
if(piece && piece.first_edition) { if (piece && piece.first_edition) {
return `/editions/${piece.first_edition.bitcoin_id}`; return `/editions/${piece.first_edition.bitcoin_id}`;
} else { } else {
return `/pieces/${piece.id}`; return `/pieces/${piece.id}`;
} }

View File

@ -7,20 +7,19 @@ import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip'; import Tooltip from 'react-bootstrap/lib/Tooltip';
import AccordionListItemPiece from './accordion_list_item_piece';
import AccordionListItemEditionWidget from './accordion_list_item_edition_widget';
import CreateEditionsForm from '../ascribe_forms/create_editions_form';
import PieceListActions from '../../actions/piece_list_actions';
import PieceListStore from '../../stores/piece_list_store';
import WhitelabelStore from '../../stores/whitelabel_store';
import EditionListActions from '../../actions/edition_list_actions'; import EditionListActions from '../../actions/edition_list_actions';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import PieceListActions from '../../actions/piece_list_actions';
import PieceListStore from '../../stores/piece_list_store';
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 AclProxy from '../acl_proxy';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
@ -29,50 +28,51 @@ import { mergeOptions } from '../../utils/general_utils';
let AccordionListItemWallet = React.createClass({ let AccordionListItemWallet = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, content: React.PropTypes.object.isRequired,
content: React.PropTypes.object, whitelabel: React.PropTypes.object.isRequired,
thumbnailPlaceholder: React.PropTypes.func,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
]) ]),
className: React.PropTypes.string,
thumbnailPlaceholder: React.PropTypes.func
}, },
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
PieceListStore.getState(),
{ {
showCreateEditionsDialog: false showCreateEditionsDialog: false
}, }
PieceListStore.getState(),
WhitelabelStore.getState()
); );
}, },
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
this.setState(state); this.setState(state);
}, },
getGlyphicon(){ getGlyphicon() {
if ((this.props.content.notifications && this.props.content.notifications.length > 0)){ if (this.props.content.notifications && this.props.content.notifications.length) {
return ( return (
<OverlayTrigger <OverlayTrigger
delay={500} delay={500}
placement="left" placement="left"
overlay={<Tooltip>{getLangText('You have actions pending')}</Tooltip>}> overlay={<Tooltip>{getLangText('You have actions pending')}</Tooltip>}>
<Glyphicon glyph='bell' color="green"/> <Glyphicon glyph='bell' color="green" />
</OverlayTrigger>); </OverlayTrigger>
);
} else {
return null;
} }
return null;
}, },
toggleCreateEditionsDialog() { toggleCreateEditionsDialog() {
@ -93,7 +93,7 @@ let AccordionListItemWallet = React.createClass({
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
EditionListActions.toggleEditionList(pieceId); EditionListActions.toggleEditionList(pieceId);
const notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000); const notification = new GlobalNotificationModel(getLangText('Editions successfully created'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
@ -111,13 +111,15 @@ let AccordionListItemWallet = React.createClass({
}, },
getLicences() { getLicences() {
const { content, whitelabel } = this.props;
// convert this to acl_view_licences later // convert this to acl_view_licences later
if (this.state.whitelabel && this.state.whitelabel.name === 'Creative Commons France') { if (whitelabel.name === 'Creative Commons France') {
return ( return (
<span> <span>
<span>, </span> <span>, </span>
<a href={this.props.content.license_type.url} target="_blank"> <a href={content.license_type.url} target="_blank">
{getLangText('%s license', this.props.content.license_type.code)} {getLangText('%s license', content.license_type.code)}
</a> </a>
</span> </span>
); );

View File

@ -2,36 +2,42 @@
import React from 'react'; import React from 'react';
import Header from '../components/header'; import AppBase from './app_base';
import Footer from '../components/footer'; import AppRouteWrapper from './app_route_wrapper';
import GlobalNotification from './global_notification'; import Footer from './footer';
import Header from './header';
let AscribeApp = React.createClass({ let AscribeApp = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.oneOfType([ activeRoute: React.PropTypes.object.isRequired,
React.PropTypes.arrayOf(React.PropTypes.element), children: React.PropTypes.element.isRequired,
React.PropTypes.element routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
]),
routes: React.PropTypes.arrayOf(React.PropTypes.object) // Provided from AppBase
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object
}, },
render() { render() {
let { children, routes } = this.props; const { activeRoute, children, currentUser, routes, whitelabel } = this.props;
return ( return (
<div className="container ascribe-default-app"> <div className="ascribe-app ascribe-default-app">
<Header routes={routes} /> <Header
{/* Routes are injected here */} currentUser={currentUser}
<div className="ascribe-body"> routes={routes}
whitelabel={whitelabel} />
<AppRouteWrapper
currentUser={currentUser}
whitelabel={whitelabel}>
{/* Routes are injected here */}
{children} {children}
</div> </AppRouteWrapper>
<Footer /> <Footer activeRoute={activeRoute} />
<GlobalNotification />
<div id="modal" className="container"></div>
</div> </div>
); );
} }
}); });
export default AscribeApp; export default AppBase(AscribeApp);

View File

@ -2,9 +2,6 @@
import React from 'react/addons'; import React from 'react/addons';
import UserActions from '../../actions/user_actions';
import UserStore from '../../stores/user_store';
import ConsignButton from './acls/consign_button'; import ConsignButton from './acls/consign_button';
import EmailButton from './acls/email_button'; import EmailButton from './acls/email_button';
import LoanButton from './acls/loan_button'; import LoanButton from './acls/loan_button';
@ -12,50 +9,44 @@ import LoanRequestButton from './acls/loan_request_button';
import TransferButton from './acls/transfer_button'; import TransferButton from './acls/transfer_button';
import UnconsignButton from './acls/unconsign_button'; import UnconsignButton from './acls/unconsign_button';
import { mergeOptions } from '../../utils/general_utils'; import { selectFromObject } from '../../utils/general_utils';
let AclButtonList = React.createClass({ let AclButtonList = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired,
handleSuccess: React.PropTypes.func.isRequired,
pieceOrEditions: React.PropTypes.oneOfType([ pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.array React.PropTypes.array
]).isRequired, ]).isRequired,
availableAcls: React.PropTypes.object.isRequired,
buttonsStyle: React.PropTypes.object, buttonsStyle: React.PropTypes.object,
handleSuccess: React.PropTypes.func.isRequired,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
]) ]),
className: React.PropTypes.string
}, },
getInitialState() { getInitialState() {
return mergeOptions( return {
UserStore.getState(), buttonListSize: 0
{ }
buttonListSize: 0
}
);
}, },
componentDidMount() { componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser.defer();
window.addEventListener('resize', this.handleResize); window.addEventListener('resize', this.handleResize);
window.dispatchEvent(new Event('resize')); window.dispatchEvent(new Event('resize'));
}, },
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if(prevProps.availableAcls && prevProps.availableAcls !== this.props.availableAcls) { if (prevProps.availableAcls && prevProps.availableAcls !== this.props.availableAcls) {
window.dispatchEvent(new Event('resize')); window.dispatchEvent(new Event('resize'));
} }
}, },
componentWillUnmount() { componentWillUnmount() {
UserStore.unlisten(this.onChange);
window.removeEventListener('resize', this.handleResize); window.removeEventListener('resize', this.handleResize);
}, },
@ -65,10 +56,6 @@ let AclButtonList = React.createClass({
}); });
}, },
onChange(state) {
this.setState(state);
},
renderChildren() { renderChildren() {
const { children } = this.props; const { children } = this.props;
const { buttonListSize } = this.state; const { buttonListSize } = this.state;
@ -79,42 +66,29 @@ let AclButtonList = React.createClass({
}, },
render() { render() {
const { className, const {
buttonsStyle, availableAcls,
availableAcls, buttonsStyle,
pieceOrEditions, className,
handleSuccess } = this.props; currentUser,
handleSuccess,
pieceOrEditions } = this.props;
const { currentUser } = this.state; const buttonProps = selectFromObject(this.props, [
'availableAcls',
'currentUser',
'handleSuccess',
'pieceOrEditions'
]);
return ( return (
<div className={className}> <div className={className}>
<span ref="buttonList" style={buttonsStyle}> <span ref="buttonList" style={buttonsStyle}>
<EmailButton <EmailButton {...buttonProps} />
availableAcls={availableAcls} <TransferButton {...buttonProps} />
pieceOrEditions={pieceOrEditions} <ConsignButton {...buttonProps} />
currentUser={currentUser} <UnconsignButton {...buttonProps} />
handleSuccess={handleSuccess} /> <LoanButton {...buttonProps} />
<TransferButton
availableAcls={availableAcls}
pieceOrEditions={pieceOrEditions}
currentUser={currentUser}
handleSuccess={handleSuccess}/>
<ConsignButton
availableAcls={availableAcls}
pieceOrEditions={pieceOrEditions}
currentUser={currentUser}
handleSuccess={handleSuccess} />
<UnconsignButton
availableAcls={availableAcls}
pieceOrEditions={pieceOrEditions}
currentUser={currentUser}
handleSuccess={handleSuccess} />
<LoanButton
availableAcls={availableAcls}
pieceOrEditions={pieceOrEditions}
currentUser={currentUser}
handleSuccess={handleSuccess} />
{this.renderChildren()} {this.renderChildren()}
</span> </span>
</div> </div>

View File

@ -14,7 +14,7 @@ import AppConstants from '../../../constants/application_constants';
import { AclInformationText } from '../../../constants/acl_information_text'; import { AclInformationText } from '../../../constants/acl_information_text';
export default function ({ action, displayName, title, tooltip }) { export default function AclButton({ action, displayName, title, tooltip }) {
if (AppConstants.aclList.indexOf(action) < 0) { if (AppConstants.aclList.indexOf(action) < 0) {
console.warn('Your specified aclName did not match a an acl class.'); console.warn('Your specified aclName did not match a an acl class.');
} }
@ -24,23 +24,20 @@ export default function ({ action, displayName, title, tooltip }) {
propTypes: { propTypes: {
availableAcls: React.PropTypes.object.isRequired, availableAcls: React.PropTypes.object.isRequired,
buttonAcceptName: React.PropTypes.string,
buttonAcceptClassName: React.PropTypes.string,
currentUser: React.PropTypes.object,
email: React.PropTypes.string,
pieceOrEditions: React.PropTypes.oneOfType([ pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.array React.PropTypes.array
]).isRequired, ]).isRequired,
handleSuccess: React.PropTypes.func.isRequired,
className: React.PropTypes.string buttonAcceptName: React.PropTypes.string,
buttonAcceptClassName: React.PropTypes.string,
currentUser: React.PropTypes.object,
email: React.PropTypes.string,
handleSuccess: React.PropTypes.func
}, },
sanitizeAction() { sanitizeAction() {
if (this.props.buttonAcceptName) { return this.props.buttonAcceptName || AclInformationText.titles[action];
return this.props.buttonAcceptName;
}
return AclInformationText.titles[action];
}, },
render() { render() {

View File

@ -15,10 +15,12 @@ let UnConsignRequestButton = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired, currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired, edition: React.PropTypes.object.isRequired,
handleSuccess: React.PropTypes.func.isRequired
handleSuccess: React.PropTypes.func
}, },
render: function () { render: function () {
const { currentUser, edition, handleSuccess } = this.props;
return ( return (
<ModalWrapper <ModalWrapper
trigger={ trigger={
@ -26,17 +28,18 @@ let UnConsignRequestButton = React.createClass({
REQUEST UNCONSIGN REQUEST UNCONSIGN
</Button> </Button>
} }
handleSuccess={this.props.handleSuccess} handleSuccess={handleSuccess}
title='Request to Un-Consign'> title='Request to Un-Consign'>
<UnConsignRequestForm <UnConsignRequestForm
url={ApiUrls.ownership_unconsigns_request} url={ApiUrls.ownership_unconsigns_request}
id={{'bitcoin_id': this.props.edition.bitcoin_id}} id={{'bitcoin_id': edition.bitcoin_id}}
message={`${getLangText('Hi')}, message={`${getLangText('Hi')},
${getLangText('I request you to un-consign')} \" ${this.props.edition.title} \". ${getLangText('I request you to un-consign')} \" ${edition.title} \".
${getLangText('Truly yours')}, ${getLangText('Truly yours')},
${this.props.currentUser.username}`}/> ${currentUser.username}`
} />
</ModalWrapper> </ModalWrapper>
); );
} }

View File

@ -8,23 +8,23 @@ import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import HistoryIterator from './history_iterator'; import EditionActions from '../../actions/edition_actions';
import MediaContainer from './media_container';
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
import Form from './../ascribe_forms/form';
import Property from './../ascribe_forms/property';
import DetailProperty from './detail_property'; import DetailProperty from './detail_property';
import LicenseDetail from './license_detail';
import FurtherDetails from './further_details';
import EditionActionPanel from './edition_action_panel'; import EditionActionPanel from './edition_action_panel';
import AclProxy from '../acl_proxy'; import FurtherDetails from './further_details';
import HistoryIterator from './history_iterator';
import LicenseDetail from './license_detail';
import MediaContainer from './media_container';
import Note from './note'; import Note from './note';
import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph';
import Form from '../ascribe_forms/form';
import Property from '../ascribe_forms/property';
import AclProxy from '../acl_proxy';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
@ -36,11 +36,13 @@ import { getLangText } from '../../utils/lang_utils';
*/ */
let Edition = React.createClass({ let Edition = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
furtherDetailsType: React.PropTypes.func,
edition: React.PropTypes.object,
coaError: React.PropTypes.object, coaError: React.PropTypes.object,
currentUser: React.PropTypes.object, furtherDetailsType: React.PropTypes.func,
loadEdition: React.PropTypes.func loadEdition: React.PropTypes.func
}, },
@ -57,56 +59,56 @@ let Edition = React.createClass({
currentUser, currentUser,
edition, edition,
furtherDetailsType: FurtherDetailsType, furtherDetailsType: FurtherDetailsType,
loadEdition } = this.props; loadEdition,
whitelabel } = this.props;
return ( return (
<Row> <Row>
<Col md={6} className="ascribe-print-col-left"> <Col md={6} className="ascribe-print-col-left">
<MediaContainer <MediaContainer
content={edition} content={edition}
currentUser={currentUser} /> currentUser={currentUser}
refreshObject={loadEdition} />
</Col> </Col>
<Col md={6} className="ascribe-edition-details ascribe-print-col-right"> <Col md={6} className="ascribe-edition-details ascribe-print-col-right">
<div className="ascribe-detail-header"> <div className="ascribe-detail-header">
<hr className="hidden-print" style={{marginTop: 0}}/> <hr className="hidden-print" style={{marginTop: 0}} />
<h1 className="ascribe-detail-title">{edition.title}</h1> <h1 className="ascribe-detail-title">{edition.title}</h1>
<DetailProperty label="CREATED BY" value={edition.artist_name} /> <DetailProperty label="CREATED BY" value={edition.artist_name} />
<DetailProperty label="DATE" value={Moment(edition.date_created, 'YYYY-MM-DD').year()} /> <DetailProperty label="DATE" value={Moment(edition.date_created, 'YYYY-MM-DD').year()} />
<hr/> <hr />
</div> </div>
<EditionSummary <EditionSummary
actionPanelButtonListType={actionPanelButtonListType} actionPanelButtonListType={actionPanelButtonListType}
edition={edition} edition={edition}
currentUser={currentUser} currentUser={currentUser}
handleSuccess={loadEdition}/> handleSuccess={loadEdition}
whitelabel={whitelabel} />
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Certificate of Authenticity')} title={getLangText('Certificate of Authenticity')}
show={edition.acl.acl_coa === true}> show={edition.acl.acl_coa === true}>
<CoaDetails <CoaDetails
coa={edition.coa} coa={edition.coa}
coaError={coaError} coaError={coaError}
editionId={edition.bitcoin_id}/> editionId={edition.bitcoin_id} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Provenance/Ownership History')} title={getLangText('Provenance/Ownership History')}
show={edition.ownership_history && edition.ownership_history.length > 0}> show={edition.ownership_history && edition.ownership_history.length}>
<HistoryIterator <HistoryIterator history={edition.ownership_history} />
history={edition.ownership_history} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Consignment History')} title={getLangText('Consignment History')}
show={edition.consign_history && edition.consign_history.length > 0}> show={edition.consign_history && edition.consign_history.length > 0}>
<HistoryIterator <HistoryIterator history={edition.consign_history} />
history={edition.consign_history} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Loan History')} title={getLangText('Loan History')}
show={edition.loan_history && edition.loan_history.length > 0}> show={edition.loan_history && edition.loan_history.length > 0}>
<HistoryIterator <HistoryIterator history={edition.loan_history} />
history={edition.loan_history} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
@ -120,7 +122,7 @@ let Edition = React.createClass({
editable={true} editable={true}
successMessage={getLangText('Private note saved')} successMessage={getLangText('Private note saved')}
url={ApiUrls.note_private_edition} url={ApiUrls.note_private_edition}
currentUser={currentUser}/> currentUser={currentUser} />
<Note <Note
id={() => {return {'bitcoin_id': edition.bitcoin_id}; }} id={() => {return {'bitcoin_id': edition.bitcoin_id}; }}
label={getLangText('Personal note (public)')} label={getLangText('Personal note (public)')}
@ -130,13 +132,11 @@ let Edition = React.createClass({
show={!!edition.public_note || !!edition.acl.acl_edit} show={!!edition.public_note || !!edition.acl.acl_edit}
successMessage={getLangText('Public edition note saved')} successMessage={getLangText('Public edition note saved')}
url={ApiUrls.note_public_edition} url={ApiUrls.note_public_edition}
currentUser={currentUser}/> currentUser={currentUser} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Further Details')} title={getLangText('Further Details')}
show={edition.acl.acl_edit || show={edition.acl.acl_edit || Object.keys(edition.extra_data).length || edition.other_data.length}>
Object.keys(edition.extra_data).length > 0 ||
edition.other_data.length > 0}>
<FurtherDetailsType <FurtherDetailsType
editable={edition.acl.acl_edit} editable={edition.acl.acl_edit}
pieceId={edition.parent} pieceId={edition.parent}
@ -144,10 +144,8 @@ let Edition = React.createClass({
otherData={edition.other_data} otherData={edition.other_data}
handleSuccess={loadEdition} /> handleSuccess={loadEdition} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph title={getLangText('SPOOL Details')}>
title={getLangText('SPOOL Details')}> <SpoolDetails edition={edition} />
<SpoolDetails
edition={edition} />
</CollapsibleParagraph> </CollapsibleParagraph>
</Col> </Col>
</Row> </Row>
@ -158,60 +156,56 @@ let Edition = React.createClass({
let EditionSummary = React.createClass({ let EditionSummary = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
edition: React.PropTypes.object,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },
handleSuccess() { getStatus() {
this.props.handleSuccess(); const { status } = this.props.edition;
},
getStatus(){ return status.length ? (
let status = null; <DetailProperty
if (this.props.edition.status.length > 0){ label="STATUS"
let statusStr = this.props.edition.status.join(', ').replace(/_/g, ' '); value={status.join(', ').replace(/_/g, ' ')} />
status = <DetailProperty label="STATUS" value={ statusStr }/>; ) : null;
if (this.props.edition.pending_new_owner && this.props.edition.acl.acl_withdraw_transfer){
status = (
<DetailProperty label="STATUS" value={ statusStr } />
);
}
}
return status;
}, },
render() { render() {
let { actionPanelButtonListType, edition, currentUser } = this.props; const { actionPanelButtonListType, currentUser, edition, handleSuccess, whitelabel } = this.props;
return ( return (
<div className="ascribe-detail-header"> <div className="ascribe-detail-header">
<DetailProperty <DetailProperty
label={getLangText('EDITION')} label={getLangText('EDITION')}
value={ edition.edition_number + ' ' + getLangText('of') + ' ' + edition.num_editions} /> value={edition.edition_number + ' ' + getLangText('of') + ' ' + edition.num_editions} />
<DetailProperty <DetailProperty
label={getLangText('ID')} label={getLangText('ID')}
value={ edition.bitcoin_id } value={edition.bitcoin_id}
ellipsis={true} /> ellipsis={true} />
<DetailProperty <DetailProperty
label={getLangText('OWNER')} label={getLangText('OWNER')}
value={ edition.owner } /> value={edition.owner} />
<LicenseDetail license={edition.license_type}/> <LicenseDetail license={edition.license_type} />
{this.getStatus()} {this.getStatus()}
{/* {/*
`acl_view` is always available in `edition.acl`, therefore if it has `acl_view` is always available in `edition.acl`, therefore if it has
no more than 1 key, we're hiding the `DetailProperty` actions as otherwise no more than 1 key, we're hiding the `DetailProperty` actions as otherwise
`AclInformation` would show up `AclInformation` would show up
*/} */}
<AclProxy show={currentUser && currentUser.email && Object.keys(edition.acl).length > 1}> <AclProxy show={currentUser.email && Object.keys(edition.acl).length > 1}>
<DetailProperty <DetailProperty
label={getLangText('ACTIONS')} label={getLangText('ACTIONS')}
className="hidden-print"> className="hidden-print">
<EditionActionPanel <EditionActionPanel
actionPanelButtonListType={actionPanelButtonListType} actionPanelButtonListType={actionPanelButtonListType}
edition={edition}
currentUser={currentUser} currentUser={currentUser}
handleSuccess={this.handleSuccess} /> edition={edition}
handleSuccess={handleSuccess}
whitelabel={whitelabel} />
</DetailProperty> </DetailProperty>
</AclProxy> </AclProxy>
<hr/> <hr/>
@ -360,4 +354,5 @@ let SpoolDetails = React.createClass({
} }
}); });
export default Edition; export default Edition;

View File

@ -36,9 +36,11 @@ import { getLangText } from '../../utils/lang_utils';
*/ */
let EditionActionPanel = React.createClass({ let EditionActionPanel = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
edition: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
edition: React.PropTypes.object,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },
@ -87,39 +89,42 @@ let EditionActionPanel = React.createClass({
handleSuccess(response) { handleSuccess(response) {
this.refreshCollection(); this.refreshCollection();
this.props.handleSuccess();
if (response){ if (response) {
let notification = new GlobalNotificationModel(response.notification, 'success'); const notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
} }
if (typeof this.props.handleSuccess === 'function') {
this.props.handleSuccess();
}
}, },
render() { render() {
const { const {
actionPanelButtonListType: ActionPanelButtonListType, actionPanelButtonListType: ActionPanelButtonListType,
currentUser,
edition, edition,
currentUser } = this.props; whitelabel } = this.props;
if (edition && if (edition.notifications && edition.notifications.length) {
edition.notifications &&
edition.notifications.length > 0){
return ( return (
<ListRequestActions <ListRequestActions
pieceOrEditions={[edition]}
currentUser={currentUser} currentUser={currentUser}
handleSuccess={this.handleSuccess} notifications={edition.notifications}
notifications={edition.notifications}/>); pieceOrEditions={[edition]}
} handleSuccess={this.handleSuccess} />);
} else {
else {
return ( return (
<Row> <Row>
<Col md={12}> <Col md={12}>
<ActionPanelButtonListType <ActionPanelButtonListType
className="ascribe-button-list"
availableAcls={edition.acl} availableAcls={edition.acl}
className="ascribe-button-list"
currentUser={currentUser}
handleSuccess={this.handleSuccess}
pieceOrEditions={[edition]} pieceOrEditions={[edition]}
handleSuccess={this.handleSuccess}> whitelabel={whitelabel}>
<AclProxy <AclProxy
aclObject={edition.acl} aclObject={edition.acl}
aclName="acl_withdraw_transfer"> aclName="acl_withdraw_transfer">

View File

@ -9,16 +9,12 @@ import { ResourceNotFoundError } from '../../models/errors';
import EditionActions from '../../actions/edition_actions'; import EditionActions from '../../actions/edition_actions';
import EditionStore from '../../stores/edition_store'; import EditionStore from '../../stores/edition_store';
import UserActions from '../../actions/user_actions';
import UserStore from '../../stores/user_store';
import Edition from './edition'; import Edition from './edition';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { setDocumentTitle } from '../../utils/dom_utils'; import { setDocumentTitle } from '../../utils/dom_utils';
import { mergeOptions } from '../../utils/general_utils';
/** /**
@ -28,24 +24,26 @@ let EditionContainer = React.createClass({
propTypes: { propTypes: {
actionPanelButtonListType: React.PropTypes.func, actionPanelButtonListType: React.PropTypes.func,
furtherDetailsType: React.PropTypes.func, furtherDetailsType: React.PropTypes.func,
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
mixins: [History, ReactError], mixins: [History, ReactError],
getInitialState() { getInitialState() {
return mergeOptions( return EditionStore.getInitialState();
EditionStore.getInitialState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
EditionStore.listen(this.onChange); EditionStore.listen(this.onChange);
UserStore.listen(this.onChange);
this.loadEdition(); this.loadEdition();
UserActions.fetchCurrentUser();
}, },
// This is done to update the container when the user clicks on the prev or next // This is done to update the container when the user clicks on the prev or next
@ -68,19 +66,10 @@ let EditionContainer = React.createClass({
componentWillUnmount() { componentWillUnmount() {
window.clearInterval(this.state.timerId); window.clearInterval(this.state.timerId);
EditionStore.unlisten(this.onChange); EditionStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
this.setState(state); this.setState(state);
if(state && state.edition && state.edition.digital_work) {
let isEncoding = state.edition.digital_work.isEncoding;
if (state.edition.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) {
let timerId = window.setInterval(() => EditionActions.fetchEdition(this.props.params.editionId), 10000);
this.setState({timerId: timerId});
}
}
}, },
loadEdition(editionId = this.props.params.editionId) { loadEdition(editionId = this.props.params.editionId) {
@ -88,8 +77,8 @@ let EditionContainer = React.createClass({
}, },
render() { render() {
const { edition, currentUser, coaMeta } = this.state; const { actionPanelButtonListType, currentUser, furtherDetailsType, whitelabel } = this.props;
const { actionPanelButtonListType, furtherDetailsType } = this.props; const { edition, coaMeta } = this.state;
if (edition.id) { if (edition.id) {
setDocumentTitle(`${edition.artist_name}, ${edition.title}`); setDocumentTitle(`${edition.artist_name}, ${edition.title}`);
@ -97,11 +86,12 @@ let EditionContainer = React.createClass({
return ( return (
<Edition <Edition
actionPanelButtonListType={actionPanelButtonListType} actionPanelButtonListType={actionPanelButtonListType}
furtherDetailsType={furtherDetailsType}
edition={edition}
coaError={coaMeta.err} coaError={coaMeta.err}
currentUser={currentUser} currentUser={currentUser}
loadEdition={this.loadEdition} /> edition={edition}
furtherDetailsType={furtherDetailsType}
loadEdition={this.loadEdition}
whitelabel={whitelabel} />
); );
} else { } else {
return ( return (

View File

@ -22,12 +22,14 @@ const EMBED_IFRAME_HEIGHT = {
video: 315, video: 315,
audio: 62 audio: 62
}; };
const ENCODE_UPDATE_TIME = 5000;
let MediaContainer = React.createClass({ let MediaContainer = React.createClass({
propTypes: { propTypes: {
content: React.PropTypes.object, content: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object, refreshObject: React.PropTypes.func.isRequired,
refreshObject: React.PropTypes.func
currentUser: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
@ -37,14 +39,16 @@ let MediaContainer = React.createClass({
}, },
componentDidMount() { componentDidMount() {
if (!this.props.content.digital_work) { const { content: { digital_work: digitalWork }, refreshObject } = this.props;
return;
}
const isEncoding = this.props.content.digital_work.isEncoding; if (digitalWork) {
if (this.props.content.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) { const isEncoding = digitalWork.isEncoding;
let timerId = window.setInterval(this.props.refreshObject, 10000);
this.setState({timerId: timerId}); if (digitalWork.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) {
this.setState({
timerId: window.setInterval(refreshObject, ENCODE_UPDATE_TIME)
});
}
} }
}, },
@ -105,7 +109,7 @@ let MediaContainer = React.createClass({
{'<iframe width="560" height="' + height + '" src="https://embed.ascribe.io/content/' {'<iframe width="560" height="' + height + '" src="https://embed.ascribe.io/content/'
+ content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'} + content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'}
</pre> </pre>
}/> } />
); );
} }
return ( return (
@ -136,7 +140,7 @@ let MediaContainer = React.createClass({
If it turns out that `fileExtension` is an empty string, we're just If it turns out that `fileExtension` is an empty string, we're just
using the label 'file'. using the label 'file'.
*/} */}
{getLangText('Download')} .{fileExtension || 'file'} <Glyphicon glyph="cloud-download"/> {getLangText('Download')} .{fileExtension || 'file'} <Glyphicon glyph="cloud-download" />
</Button> </Button>
</AclProxy> </AclProxy>
{embed} {embed}

View File

@ -2,64 +2,68 @@
import React from 'react'; 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 Form from './../ascribe_forms/form';
import Property from './../ascribe_forms/property'; import Property from './../ascribe_forms/property';
import InputTextAreaToggable from './../ascribe_forms/input_textarea_toggable'; import InputTextAreaToggable from './../ascribe_forms/input_textarea_toggable';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let Note = React.createClass({ let Note = React.createClass({
propTypes: { propTypes: {
url: React.PropTypes.string, currentUser: React.PropTypes.object.isRequired,
id: React.PropTypes.func, id: React.PropTypes.func.isRequired,
label: React.PropTypes.string, url: React.PropTypes.string.isRequired,
currentUser: React.PropTypes.object,
defaultValue: React.PropTypes.string, defaultValue: React.PropTypes.string,
editable: React.PropTypes.bool, editable: React.PropTypes.bool,
show: React.PropTypes.bool, label: React.PropTypes.string,
placeholder: React.PropTypes.string, placeholder: React.PropTypes.string,
show: React.PropTypes.bool,
successMessage: React.PropTypes.string successMessage: React.PropTypes.string
}, },
getDefaultProps() { getDefaultProps() {
return { return {
editable: true, editable: true,
show: true,
placeholder: getLangText('Enter a note'), placeholder: getLangText('Enter a note'),
show: true,
successMessage: getLangText('Note saved') successMessage: getLangText('Note saved')
}; };
}, },
showNotification(){ showNotification() {
let notification = new GlobalNotificationModel(this.props.successMessage, 'success'); const notification = new GlobalNotificationModel(this.props.successMessage, 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
render() { render() {
if ((!!this.props.currentUser.username && this.props.editable || !this.props.editable ) && this.props.show) { const { currentUser, defaultValue, editable, id, label, placeholder, show, url } = this.props;
if ((!!currentUser.username && editable || !editable ) && show) {
return ( return (
<Form <Form
url={this.props.url} url={url}
getFormData={this.props.id} getFormData={id}
handleSuccess={this.showNotification} handleSuccess={this.showNotification}
disabled={!this.props.editable}> disabled={!editable}>
<Property <Property
name='note' name='note'
label={this.props.label}> label={label}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.defaultValue} defaultValue={defaultValue}
placeholder={this.props.placeholder}/> placeholder={placeholder} />
</Property> </Property>
<hr /> <hr />
</Form> </Form>
); );
} else {
return null;
} }
return null;
} }
}); });
export default Note; export default Note;

View File

@ -15,19 +15,19 @@ import MediaContainer from './media_container';
*/ */
let Piece = React.createClass({ let Piece = React.createClass({
propTypes: { propTypes: {
piece: React.PropTypes.object, piece: React.PropTypes.object.isRequired,
buttons: React.PropTypes.object,
currentUser: React.PropTypes.object, currentUser: React.PropTypes.object,
header: React.PropTypes.object, header: React.PropTypes.object,
subheader: React.PropTypes.object, subheader: React.PropTypes.object,
buttons: React.PropTypes.object,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
]) ])
}, },
updatePiece() {
updateObject() {
return PieceActions.fetchPiece(this.props.piece.id); return PieceActions.fetchPiece(this.props.piece.id);
}, },
@ -40,7 +40,7 @@ let Piece = React.createClass({
<MediaContainer <MediaContainer
content={piece} content={piece}
currentUser={currentUser} currentUser={currentUser}
refreshObject={this.updateObject} /> refreshObject={this.updatePiece} />
</Col> </Col>
<Col md={6} className="ascribe-edition-details ascribe-print-col-right"> <Col md={6} className="ascribe-edition-details ascribe-print-col-right">
{header} {header}

View File

@ -7,39 +7,35 @@ import Moment from 'moment';
import ReactError from '../../mixins/react_error'; import ReactError from '../../mixins/react_error';
import { ResourceNotFoundError } from '../../models/errors'; import { ResourceNotFoundError } from '../../models/errors';
import EditionListActions from '../../actions/edition_list_actions';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import PieceActions from '../../actions/piece_actions'; import PieceActions from '../../actions/piece_actions';
import PieceStore from '../../stores/piece_store'; import PieceStore from '../../stores/piece_store';
import PieceListActions from '../../actions/piece_list_actions'; import PieceListActions from '../../actions/piece_list_actions';
import PieceListStore from '../../stores/piece_list_store'; import PieceListStore from '../../stores/piece_list_store';
import UserActions from '../../actions/user_actions';
import UserStore from '../../stores/user_store';
import EditionListActions from '../../actions/edition_list_actions';
import Piece from './piece';
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
import FurtherDetails from './further_details'; import FurtherDetails from './further_details';
import DetailProperty from './detail_property'; import DetailProperty from './detail_property';
import LicenseDetail from './license_detail';
import HistoryIterator from './history_iterator'; import HistoryIterator from './history_iterator';
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 CreateEditionsForm from '../ascribe_forms/create_editions_form'; import AclInformation from '../ascribe_buttons/acl_information';
import CreateEditionsButton from '../ascribe_buttons/create_editions_button'; import CreateEditionsButton from '../ascribe_buttons/create_editions_button';
import DeleteButton from '../ascribe_buttons/delete_button'; import DeleteButton from '../ascribe_buttons/delete_button';
import AclInformation from '../ascribe_buttons/acl_information'; import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
import AclProxy from '../acl_proxy';
import CreateEditionsForm from '../ascribe_forms/create_editions_form';
import ListRequestActions from '../ascribe_forms/list_form_request_actions'; import ListRequestActions from '../ascribe_forms/list_form_request_actions';
import GlobalNotificationModel from '../../models/global_notification_model'; import AclProxy from '../acl_proxy';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import Note from './note';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AscribeSpinner from '../ascribe_spinner'; import AscribeSpinner from '../ascribe_spinner';
@ -54,6 +50,13 @@ import { setDocumentTitle } from '../../utils/dom_utils';
let PieceContainer = React.createClass({ let PieceContainer = React.createClass({
propTypes: { propTypes: {
furtherDetailsType: React.PropTypes.func, furtherDetailsType: React.PropTypes.func,
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
@ -67,7 +70,6 @@ let PieceContainer = React.createClass({
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
UserStore.getState(),
PieceListStore.getState(), PieceListStore.getState(),
PieceStore.getInitialState(), PieceStore.getInitialState(),
{ {
@ -77,12 +79,10 @@ let PieceContainer = React.createClass({
}, },
componentDidMount() { componentDidMount() {
UserStore.listen(this.onChange);
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
this.loadPiece(); this.loadPiece();
UserActions.fetchCurrentUser();
}, },
// This is done to update the container when the user clicks on the prev or next // This is done to update the container when the user clicks on the prev or next
@ -105,7 +105,6 @@ let PieceContainer = React.createClass({
componentWillUnmount() { componentWillUnmount() {
PieceStore.unlisten(this.onChange); PieceStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
}, },
@ -207,15 +206,17 @@ let PieceContainer = React.createClass({
}, },
getActions() { getActions() {
const { piece, currentUser } = this.state; const { piece } = this.state;
const { currentUser } = this.props;
if (piece.notifications && piece.notifications.length > 0) { if (piece.notifications && piece.notifications.length > 0) {
return ( return (
<ListRequestActions <ListRequestActions
pieceOrEditions={piece}
currentUser={currentUser} currentUser={currentUser}
handleSuccess={this.loadPiece} handleSuccess={this.loadPiece}
notifications={piece.notifications} />); notifications={piece.notifications}
pieceOrEditions={piece} />
);
} else { } else {
return ( return (
<AclProxy <AclProxy
@ -229,8 +230,9 @@ let PieceContainer = React.createClass({
label={getLangText('ACTIONS')} label={getLangText('ACTIONS')}
className="hidden-print"> className="hidden-print">
<AclButtonList <AclButtonList
className="ascribe-button-list"
availableAcls={piece.acl} availableAcls={piece.acl}
className="ascribe-button-list"
currentUser={currentUser}
pieceOrEditions={piece} pieceOrEditions={piece}
handleSuccess={this.loadPiece}> handleSuccess={this.loadPiece}>
<CreateEditionsButton <CreateEditionsButton
@ -255,8 +257,8 @@ let PieceContainer = React.createClass({
}, },
render() { render() {
const { furtherDetailsType: FurtherDetailsType } = this.props; const { currentUser, furtherDetailsType: FurtherDetailsType } = this.props;
const { currentUser, piece } = this.state; const { piece } = this.state;
if (piece.id) { if (piece.id) {
setDocumentTitle(`${piece.artist_name}, ${piece.title}`); setDocumentTitle(`${piece.artist_name}, ${piece.title}`);

View File

@ -20,16 +20,17 @@ import { getAclFormMessage, getAclFormDataId } from '../../utils/form_utils';
let AclFormFactory = React.createClass({ let AclFormFactory = React.createClass({
propTypes: { propTypes: {
action: React.PropTypes.oneOf(AppConstants.aclList).isRequired, action: React.PropTypes.oneOf(AppConstants.aclList).isRequired,
autoFocusProperty: React.PropTypes.string,
currentUser: React.PropTypes.object,
email: React.PropTypes.string,
message: React.PropTypes.string,
labels: React.PropTypes.object,
pieceOrEditions: React.PropTypes.oneOfType([ pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.array React.PropTypes.array
]).isRequired, ]).isRequired,
autoFocusProperty: React.PropTypes.string,
currentUser: React.PropTypes.object,
email: React.PropTypes.string,
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
message: React.PropTypes.string,
labels: React.PropTypes.object,
showNotification: React.PropTypes.bool showNotification: React.PropTypes.bool
}, },
@ -105,7 +106,7 @@ let AclFormFactory = React.createClass({
message={formMessage} message={formMessage}
id={this.getFormDataId()} id={this.getFormDataId()}
url={this.isPiece() ? ApiUrls.ownership_loans_pieces url={this.isPiece() ? ApiUrls.ownership_loans_pieces
: ApiUrls.ownership_loans_editions} : ApiUrls.ownership_loans_editions}
handleSuccess={showNotification ? this.showSuccessNotification : handleSuccess} /> handleSuccess={showNotification ? this.showSuccessNotification : handleSuccess} />
); );
} else if (action === 'acl_loan_request') { } else if (action === 'acl_loan_request') {
@ -122,7 +123,7 @@ let AclFormFactory = React.createClass({
message={formMessage} message={formMessage}
id={this.getFormDataId()} id={this.getFormDataId()}
url={this.isPiece() ? ApiUrls.ownership_shares_pieces url={this.isPiece() ? ApiUrls.ownership_shares_pieces
: ApiUrls.ownership_shares_editions} : ApiUrls.ownership_shares_editions}
handleSuccess={showNotification ? this.showSuccessNotification : handleSuccess} /> handleSuccess={showNotification ? this.showSuccessNotification : handleSuccess} />
); );
} else { } else {

View File

@ -123,8 +123,7 @@ let ConsignForm = React.createClass({
<Property <Property
name='contract_agreement' name='contract_agreement'
label={getLangText('Consign Contract')} label={getLangText('Consign Contract')}
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle">
style={{paddingBottom: 0}}>
<InputContractAgreementCheckbox <InputContractAgreementCheckbox
createPublicContractAgreement={createPublicContractAgreement} createPublicContractAgreement={createPublicContractAgreement}
email={email} /> email={email} />

View File

@ -15,30 +15,30 @@ import { getLangText } from '../../utils/lang_utils';
let CopyrightAssociationForm = React.createClass({ let CopyrightAssociationForm = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object currentUser: React.PropTypes.object.isRequired
}, },
handleSubmitSuccess(){ handleSubmitSuccess() {
let notification = getLangText('Copyright association updated'); const notification = new GlobalNotificationModel(getLangText('Copyright association updated'), 'success', 10000);
notification = new GlobalNotificationModel(notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
getProfileFormData(){ getProfileFormData() {
return {email: this.props.currentUser.email}; return { email: this.props.currentUser.email };
}, },
render() { render() {
let selectedState; const { currentUser } = this.props;
let selectDefaultValue = ' -- ' + getLangText('select an association') + ' -- '; const selectDefaultValue = ' -- ' + getLangText('select an association') + ' -- ';
if (this.props.currentUser && this.props.currentUser.profile let selectedState = selectDefaultValue;
&& this.props.currentUser.profile.copyright_association) { if (currentUser.profile && currentUser.profile.copyright_association) {
selectedState = AppConstants.copyrightAssociations.indexOf(this.props.currentUser.profile.copyright_association); if (AppConstants.copyrightAssociations.indexOf(currentUser.profile.copyright_association) !== -1) {
selectedState = selectedState !== -1 ? AppConstants.copyrightAssociations[selectedState] : selectDefaultValue; selectedState = AppConstants.copyrightAssociations[selectedState];
}
} }
if (this.props.currentUser && this.props.currentUser.email){ if (currentUser.email) {
return ( return (
<Form <Form
ref='form' ref='form'
@ -48,8 +48,7 @@ let CopyrightAssociationForm = React.createClass({
<Property <Property
name="copyright_association" name="copyright_association"
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle"
label={getLangText('Copyright Association')} label={getLangText('Copyright Association')}>
style={{paddingBottom: 0}}>
<select defaultValue={selectedState} name="contract"> <select defaultValue={selectedState} name="contract">
<option <option
name={0} name={0}
@ -72,9 +71,10 @@ let CopyrightAssociationForm = React.createClass({
<hr /> <hr />
</Form> </Form>
); );
} else {
return null;
} }
return null;
} }
}); });
export default CopyrightAssociationForm; export default CopyrightAssociationForm;

View File

@ -210,8 +210,7 @@ let LoanForm = React.createClass({
<Property <Property
name='contract_agreement' name='contract_agreement'
label={getLangText('Loan Contract')} label={getLangText('Loan Contract')}
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle">
style={{paddingBottom: 0}}>
<InputContractAgreementCheckbox <InputContractAgreementCheckbox
createPublicContractAgreement={createPublicContractAgreement} createPublicContractAgreement={createPublicContractAgreement}
email={email} /> email={email} />

View File

@ -6,7 +6,6 @@ import { History } from 'react-router';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import UserStore from '../../stores/user_store';
import UserActions from '../../actions/user_actions'; import UserActions from '../../actions/user_actions';
import Form from './form'; import Form from './form';
@ -23,8 +22,6 @@ let LoginForm = React.createClass({
propTypes: { propTypes: {
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
redirectOnLoggedIn: React.PropTypes.bool,
redirectOnLoginSuccess: React.PropTypes.bool,
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -32,40 +29,26 @@ let LoginForm = React.createClass({
getDefaultProps() { getDefaultProps() {
return { return {
headerMessage: getLangText('Enter ascribe'), headerMessage: getLangText('Enter') + ' ascribe',
submitMessage: getLangText('Log in'), submitMessage: getLangText('Log in')
redirectOnLoggedIn: true,
redirectOnLoginSuccess: true
}; };
}, },
getInitialState() { handleSuccess({ success }) {
return UserStore.getState(); const notification = new GlobalNotificationModel(getLangText('Login successful'), 'success', 10000);
},
componentDidMount() {
UserStore.listen(this.onChange);
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
handleSuccess({ success }){
let notification = new GlobalNotificationModel('Login successful', 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
if(success) { if (success) {
UserActions.fetchCurrentUser(true); UserActions.fetchCurrentUser(true);
} }
}, },
render() { render() {
let email = this.props.location.query.email || null; const {
headerMessage,
location: { query: { email: emailQuery } },
submitMessage } = this.props;
return ( return (
<Form <Form
className="ascribe-form-bordered" className="ascribe-form-bordered"
@ -77,7 +60,7 @@ let LoginForm = React.createClass({
<button <button
type="submit" type="submit"
className="btn btn-default btn-wide"> className="btn btn-default btn-wide">
{this.props.submitMessage} {submitMessage}
</button>} </button>}
spinner={ spinner={
<span className="btn btn-default btn-wide btn-spinner"> <span className="btn btn-default btn-wide btn-spinner">
@ -85,7 +68,7 @@ let LoginForm = React.createClass({
</span> </span>
}> }>
<div className="ascribe-form-header"> <div className="ascribe-form-header">
<h3>{this.props.headerMessage}</h3> <h3>{headerMessage}</h3>
</div> </div>
<Property <Property
name='email' name='email'
@ -93,7 +76,7 @@ let LoginForm = React.createClass({
<input <input
type="email" type="email"
placeholder={getLangText('Enter your email')} placeholder={getLangText('Enter your email')}
defaultValue={email} defaultValue={emailQuery}
required/> required/>
</Property> </Property>
<Property <Property

View File

@ -2,9 +2,6 @@
import React from 'react'; import React from 'react';
import UserStore from '../../stores/user_store';
import UserActions from '../../actions/user_actions';
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import InputFineUploader from './input_fineuploader'; import InputFineUploader from './input_fineuploader';
@ -20,22 +17,24 @@ import AppConstants from '../../constants/application_constants';
import { validationParts, validationTypes } from '../../constants/uploader_constants'; import { validationParts, validationTypes } from '../../constants/uploader_constants';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils';
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let RegisterPieceForm = React.createClass({ let RegisterPieceForm = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.object.isRequired,
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
handleSuccess: React.PropTypes.func,
isFineUploaderActive: React.PropTypes.bool,
isFineUploaderEditable: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool, enableLocalHashing: React.PropTypes.bool,
enableSeparateThumbnail: React.PropTypes.bool, enableSeparateThumbnail: React.PropTypes.bool,
isFineUploaderActive: React.PropTypes.bool,
isFineUploaderEditable: React.PropTypes.bool,
handleSuccess: React.PropTypes.func,
// For this form to work with SlideContainer, we sometimes have to disable it // For this form to work with SlideContainer, we sometimes have to disable it
disabled: React.PropTypes.bool, disabled: React.PropTypes.bool,
location: React.PropTypes.object, location: React.PropTypes.object,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
@ -52,26 +51,10 @@ let RegisterPieceForm = React.createClass({
}; };
}, },
getInitialState(){ getInitialState() {
return mergeOptions( return {
{ digitalWorkFile: null
digitalWorkFile: null }
},
UserStore.getState()
);
},
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
}, },
/** /**
@ -133,16 +116,17 @@ let RegisterPieceForm = React.createClass({
}, },
render() { render() {
const { disabled, const {
handleSuccess, children,
submitMessage, currentUser,
headerMessage, disabled,
isFineUploaderActive, enableLocalHashing,
isFineUploaderEditable, handleSuccess,
location, headerMessage,
children, isFineUploaderActive,
enableLocalHashing } = this.props; isFineUploaderEditable,
const { currentUser} = this.state; location,
submitMessage } = this.props;
const profileHashLocally = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; const profileHashLocally = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false;
const hashLocally = profileHashLocally && enableLocalHashing; const hashLocally = profileHashLocally && enableLocalHashing;

View File

@ -21,11 +21,12 @@ import { getLangText } from '../../utils/lang_utils.js';
let RequestActionForm = React.createClass({ let RequestActionForm = React.createClass({
propTypes: { propTypes: {
notifications: React.PropTypes.object.isRequired,
pieceOrEditions: React.PropTypes.oneOfType([ pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.array React.PropTypes.array
]).isRequired, ]).isRequired,
notifications: React.PropTypes.object,
currentUser: React.PropTypes.object, currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },

View File

@ -128,8 +128,7 @@ let SendContractAgreementForm = React.createClass({
<Property <Property
name='appendix' name='appendix'
checkboxLabel={getLangText('Add appendix to the contract')} checkboxLabel={getLangText('Add appendix to the contract')}
expanded={false} expanded={false}>
style={{paddingBottom: 0}}>
<span>{getLangText('Appendix')}</span> <span>{getLangText('Appendix')}</span>
{/* We're using disabled on a form here as PropertyCollapsible currently {/* We're using disabled on a form here as PropertyCollapsible currently
does not support the disabled + overrideForm functionality */} does not support the disabled + overrideForm functionality */}

View File

@ -3,12 +3,11 @@
import React from 'react'; import React from 'react';
import { History } from 'react-router'; import { History } from 'react-router';
import UserStore from '../../stores/user_store';
import UserActions from '../../actions/user_actions';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import UserActions from '../../actions/user_actions';
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import InputCheckbox from './input_checkbox'; import InputCheckbox from './input_checkbox';
@ -24,8 +23,12 @@ let SignupForm = React.createClass({
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
children: React.PropTypes.element, location: React.PropTypes.object,
location: React.PropTypes.object children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element,
React.PropTypes.string
])
}, },
mixins: [History], mixins: [History],
@ -37,27 +40,11 @@ let SignupForm = React.createClass({
}; };
}, },
getInitialState() {
return UserStore.getState();
},
componentDidMount() {
UserStore.listen(this.onChange);
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
handleSuccess(response) { handleSuccess(response) {
if (response.user) { if (response.user) {
let notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000); const notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
// Refactor this to its own component // Refactor this to its own component
this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.'); this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.');
} else { } else {
@ -66,18 +53,20 @@ let SignupForm = React.createClass({
}, },
getFormData() { getFormData() {
if (this.props.location.query.token){ const { token } = this.props.location.query;
return {token: this.props.location.query.token}; return token ? { token } : null;
}
return null;
}, },
render() { render() {
let tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' + const {
getLangText('This password is securing your digital property like a bank account') + '.\n ' + children,
getLangText('Store it in a safe place') + '!'; headerMessage,
location: { query: { email: emailQuery } },
submitMessage } = this.props;
let email = this.props.location.query.email || null; const tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' +
getLangText('This password is securing your digital property like a bank account') + '.\n ' +
getLangText('Store it in a safe place') + '!';
return ( return (
<Form <Form
@ -88,15 +77,16 @@ let SignupForm = React.createClass({
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
buttons={ buttons={
<button type="submit" className="btn btn-default btn-wide"> <button type="submit" className="btn btn-default btn-wide">
{this.props.submitMessage} {submitMessage}
</button>} </button>
}
spinner={ spinner={
<span className="btn btn-default btn-wide btn-spinner"> <span className="btn btn-default btn-wide btn-spinner">
<AscribeSpinner color="dark-blue" size="md" /> <AscribeSpinner color="dark-blue" size="md" />
</span> </span>
}> }>
<div className="ascribe-form-header"> <div className="ascribe-form-header">
<h3>{this.props.headerMessage}</h3> <h3>{headerMessage}</h3>
</div> </div>
<Property <Property
name='email' name='email'
@ -105,7 +95,7 @@ let SignupForm = React.createClass({
type="email" type="email"
placeholder={getLangText('(e.g. andy@warhol.co.uk)')} placeholder={getLangText('(e.g. andy@warhol.co.uk)')}
autoComplete="on" autoComplete="on"
defaultValue={email} defaultValue={emailQuery}
required/> required/>
</Property> </Property>
<Property <Property
@ -128,11 +118,10 @@ let SignupForm = React.createClass({
autoComplete="on" autoComplete="on"
required/> required/>
</Property> </Property>
{this.props.children} {children}
<Property <Property
name="terms" name="terms"
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle">
style={{paddingBottom: 0}}>
<InputCheckbox> <InputCheckbox>
<span> <span>
{' ' + getLangText('I agree to the Terms of Service of ascribe') + ' '} {' ' + getLangText('I agree to the Terms of Service of ascribe') + ' '}

View File

@ -66,8 +66,7 @@ let PieceSubmitToPrizeForm = React.createClass({
</Property> </Property>
<Property <Property
name="terms" name="terms"
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle">
style={{paddingBottom: 0}}>
<InputCheckbox> <InputCheckbox>
<span> <span>
{' ' + getLangText('I agree to the Terms of Service the art price') + ' '} {' ' + getLangText('I agree to the Terms of Service the art price') + ' '}

View File

@ -18,26 +18,26 @@ import { getLangText } from '../../utils/lang_utils.js';
let TransferForm = React.createClass({ let TransferForm = React.createClass({
propTypes: { propTypes: {
url: React.PropTypes.string, id: React.PropTypes.object.isRequired,
id: React.PropTypes.object, url: React.PropTypes.string.isRequired,
message: React.PropTypes.string,
editions: React.PropTypes.array, handleSuccess: React.PropTypes.func,
currentUser: React.PropTypes.object, message: React.PropTypes.string
handleSuccess: React.PropTypes.func
}, },
getFormData(){ getFormData() {
return this.props.id; return this.props.id;
}, },
render() { render() {
const { handleSuccess, message, url } = this.props;
return ( return (
<Form <Form
ref='form' ref='form'
url={this.props.url} url={url}
getFormData={this.getFormData} getFormData={this.getFormData}
handleSuccess={this.props.handleSuccess} handleSuccess={handleSuccess}
buttons={ buttons={
<div className="modal-footer"> <div className="modal-footer">
<p className="pull-right"> <p className="pull-right">
@ -70,7 +70,7 @@ let TransferForm = React.createClass({
overrideForm={true}> overrideForm={true}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.message} defaultValue={message}
placeholder={getLangText('Enter a message...')} placeholder={getLangText('Enter a message...')}
required /> required />
</Property> </Property>

View File

@ -156,7 +156,7 @@ const InputContractAgreementCheckbox = React.createClass({
return ( return (
<div <div
className="notification-contract-pdf" className="notification-contract-pdf"
style={{paddingBottom: '1em'}}> style={{paddingBottom: '0.25em'}}>
<embed <embed
className="embed-form" className="embed-form"
src={contractUrl} src={contractUrl}

View File

@ -4,32 +4,35 @@ import React from 'react';
import RequestActionForm from './form_request_action'; import RequestActionForm from './form_request_action';
let ListRequestActions = React.createClass({ let ListRequestActions = React.createClass({
propTypes: { propTypes: {
notifications: React.PropTypes.array.isRequired,
pieceOrEditions: React.PropTypes.oneOfType([ pieceOrEditions: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.array React.PropTypes.array
]).isRequired, ]).isRequired,
currentUser: React.PropTypes.object, currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func.isRequired, handleSuccess: React.PropTypes.func
notifications: React.PropTypes.array.isRequired
}, },
render () { render () {
if (this.props.notifications && const { currentUser, handleSuccess, notifications, pieceOrEditions } = this.props;
this.props.notifications.length > 0) {
if (notifications.length) {
return ( return (
<div> <div>
{this.props.notifications.map((notification) => {notifications.map((notification) =>
<RequestActionForm <RequestActionForm
currentUser={this.props.currentUser} currentUser={currentUser}
pieceOrEditions={ this.props.pieceOrEditions } handleSuccess={handleSuccess}
notifications={notification} notifications={notification}
handleSuccess={this.props.handleSuccess}/>)} pieceOrEditions={pieceOrEditions} />
)}
</div> </div>
); );
} else {
return null;
} }
return null;
} }
}); });

View File

@ -294,18 +294,18 @@ const Property = React.createClass({
}, },
getCheckbox() { getCheckbox() {
const { checkboxLabel } = this.props; const { checkboxLabel, name } = this.props;
if(checkboxLabel) { if (checkboxLabel) {
return ( return (
<div <div
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle"
onClick={this.handleCheckboxToggle}> onClick={this.handleCheckboxToggle}>
<input <input
onChange={this.handleCheckboxToggle} name={`${name}-checkbox`}
type="checkbox"
checked={this.state.expanded} checked={this.state.expanded}
ref="checkboxCollapsible"/> onChange={this.handleCheckboxToggle}
type="checkbox" />
<span className="checkbox">{' ' + checkboxLabel}</span> <span className="checkbox">{' ' + checkboxLabel}</span>
</div> </div>
); );

View File

@ -6,6 +6,10 @@ import Modal from 'react-bootstrap/lib/Modal';
let ModalWrapper = React.createClass({ let ModalWrapper = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]).isRequired,
title: React.PropTypes.oneOfType([ title: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element, React.PropTypes.element,
@ -14,11 +18,7 @@ let ModalWrapper = React.createClass({
handleCancel: React.PropTypes.func, handleCancel: React.PropTypes.func,
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
trigger: React.PropTypes.element, trigger: React.PropTypes.element
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
])
}, },
getInitialState() { getInitialState() {

View File

@ -84,6 +84,7 @@ let PieceListToolbarFilterWidget = React.createClass({
if (this.props.filterParams && this.props.filterParams.length) { if (this.props.filterParams && this.props.filterParams.length) {
return ( return (
<DropdownButton <DropdownButton
id="ascribe-piece-list-toolbar-filter-widget-dropdown"
pullRight={true} pullRight={true}
title={filterIcon} title={filterIcon}
className="ascribe-piece-list-toolbar-filter-widget"> className="ascribe-piece-list-toolbar-filter-widget">

View File

@ -45,7 +45,7 @@ let PieceListToolbarOrderWidget = React.createClass({
}, },
render() { render() {
let filterIcon = ( let orderIcon = (
<span> <span>
<span className="ascribe-icon icon-ascribe-sort" aria-hidden="true"></span> <span className="ascribe-icon icon-ascribe-sort" aria-hidden="true"></span>
<span style={this.isOrderActive()}>&middot;</span> <span style={this.isOrderActive()}>&middot;</span>
@ -55,9 +55,10 @@ let PieceListToolbarOrderWidget = React.createClass({
if (this.props.orderParams && this.props.orderParams.length) { if (this.props.orderParams && this.props.orderParams.length) {
return ( return (
<DropdownButton <DropdownButton
id="ascribe-piece-list-toolbar-order-widget-dropdown"
pullRight={true} pullRight={true}
title={filterIcon} className="ascribe-piece-list-toolbar-filter-widget"
className="ascribe-piece-list-toolbar-filter-widget"> title={orderIcon}>
<li style={{'textAlign': 'center'}}> <li style={{'textAlign': 'center'}}>
<em>{getLangText('Sort by')}:</em> <em>{getLangText('Sort by')}:</em>
</li> </li>

View File

@ -5,7 +5,6 @@ import { RouteContext } from 'react-router';
import history from '../../history'; import history from '../../history';
import UserStore from '../../stores/user_store'; import UserStore from '../../stores/user_store';
import UserActions from '../../actions/user_actions';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
@ -21,8 +20,8 @@ const WHEN_ENUM = ['loggedIn', 'loggedOut'];
export function AuthRedirect({to, when}) { export function AuthRedirect({to, when}) {
// validate `when`, must be contained in `WHEN_ENUM`. // validate `when`, must be contained in `WHEN_ENUM`.
// Throw an error otherwise. // Throw an error otherwise.
if(WHEN_ENUM.indexOf(when) === -1) { if (WHEN_ENUM.indexOf(when) === -1) {
let whenValues = WHEN_ENUM.join(', '); const whenValues = WHEN_ENUM.join(', ');
throw new Error(`"when" must be one of: [${whenValues}] got "${when}" instead`); throw new Error(`"when" must be one of: [${whenValues}] got "${when}" instead`);
} }
@ -35,23 +34,22 @@ export function AuthRedirect({to, when}) {
// //
// So if when === 'loggedIn', we're checking if the user is logged in (and // So if when === 'loggedIn', we're checking if the user is logged in (and
// vice versa) // vice versa)
let exprToValidate = when === 'loggedIn' ? currentUser && currentUser.email const isLoggedIn = Object.keys(currentUser).length && currentUser.email;
: currentUser && !currentUser.email; const exprToValidate = when === 'loggedIn' ? isLoggedIn : !isLoggedIn;
// and redirect if `true`. // and redirect if `true`.
if(exprToValidate) { if (exprToValidate) {
window.setTimeout(() => history.replace({ query, pathname: to })); window.setTimeout(() => history.replace({ query, pathname: to }));
return true; return true;
// Otherwise there can also be the case that the backend // Otherwise there can also be the case that the backend
// wants to redirect the user to a specific route when the user is logged out already // wants to redirect the user to a specific route when the user is logged out already
} else if(!exprToValidate && when === 'loggedIn' && redirect) { } else if (!exprToValidate && when === 'loggedIn' && redirect) {
delete query.redirect; delete query.redirect;
window.setTimeout(() => history.replace({ query, pathname: '/' + redirect })); window.setTimeout(() => history.replace({ query, pathname: '/' + redirect }));
return true; return true;
} else if(!exprToValidate && when === 'loggedOut' && redirectAuthenticated) { } else if (!exprToValidate && when === 'loggedOut' && redirectAuthenticated) {
/* /*
* redirectAuthenticated contains an arbitrary path * redirectAuthenticated contains an arbitrary path
* eg pieces/<id>, editions/<bitcoin_id>, collection, settings, ... * eg pieces/<id>, editions/<bitcoin_id>, collection, settings, ...
@ -64,6 +62,7 @@ export function AuthRedirect({to, when}) {
window.location = AppConstants.baseUrl + redirectAuthenticated; window.location = AppConstants.baseUrl + redirectAuthenticated;
return true; return true;
} }
return false; return false;
}; };
} }
@ -81,6 +80,11 @@ export function ProxyHandler(...redirectFunctions) {
displayName: 'ProxyHandler', displayName: 'ProxyHandler',
propTypes: { propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: object location: object
}, },
@ -88,43 +92,33 @@ export function ProxyHandler(...redirectFunctions) {
// to use the `Lifecycle` widget in further down nested components // to use the `Lifecycle` widget in further down nested components
mixins: [RouteContext], mixins: [RouteContext],
getInitialState() {
return UserStore.getState();
},
componentDidMount() { componentDidMount() {
UserStore.listen(this.onChange); this.evaluateRedirectFunctions();
UserActions.fetchCurrentUser();
}, },
componentDidUpdate() { componentWillReceiveProps(nextProps) {
if(!UserStore.isLoading()) { this.evaluateRedirectFunctions(nextProps);
const { currentUser } = this.state; },
const { query } = this.props.location;
for(let i = 0; i < redirectFunctions.length; i++) { evaluateRedirectFunctions(props = this.props) {
const { currentUser, location: { query } } = props;
if (UserStore.hasLoaded() && !UserStore.isLoading()) {
for (let i = 0; i < redirectFunctions.length; i++) {
// if a redirectFunction redirects the user, // if a redirectFunction redirects the user,
// it should return `true` and therefore // it should return `true` and therefore
// stop/avoid the execution of all functions // stop/avoid the execution of all functions
// that follow // that follow
if(redirectFunctions[i](currentUser, query)) { if (redirectFunctions[i](currentUser, query)) {
break; break;
} }
} }
} }
}, },
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() { render() {
return ( return (
<Component {...this.props}/> <Component {...this.props} />
); );
} }
}); });

View File

@ -26,21 +26,23 @@ let AccountSettings = React.createClass({
whitelabel: React.PropTypes.object.isRequired whitelabel: React.PropTypes.object.isRequired
}, },
handleSuccess(){ handleSuccess() {
this.props.loadUser(true); this.props.loadUser(true);
let notification = new GlobalNotificationModel(getLangText('Settings succesfully updated'), 'success', 5000);
const notification = new GlobalNotificationModel(getLangText('Settings succesfully updated'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
getFormDataProfile(){ getFormDataProfile() {
return {'email': this.props.currentUser.email}; return { 'email': this.props.currentUser.email };
}, },
render() { render() {
let content = <AscribeSpinner color='dark-blue' size='lg'/>; const { currentUser, whitelabel } = this.props;
let content = <AscribeSpinner color='dark-blue' size='lg' />;
let profile = null; let profile = null;
if (this.props.currentUser.username) { if (currentUser.username) {
content = ( content = (
<Form <Form
url={ApiUrls.users_username} url={ApiUrls.users_username}
@ -50,7 +52,7 @@ let AccountSettings = React.createClass({
label={getLangText('Username')}> label={getLangText('Username')}>
<input <input
type="text" type="text"
defaultValue={this.props.currentUser.username} defaultValue={currentUser.username}
placeholder={getLangText('Enter your username')} placeholder={getLangText('Enter your username')}
required/> required/>
</Property> </Property>
@ -61,7 +63,7 @@ let AccountSettings = React.createClass({
editable={false}> editable={false}>
<input <input
type="text" type="text"
defaultValue={this.props.currentUser.email} defaultValue={currentUser.email}
placeholder={getLangText('Enter your username')} placeholder={getLangText('Enter your username')}
required/> required/>
</Property> </Property>
@ -70,7 +72,7 @@ let AccountSettings = React.createClass({
); );
profile = ( profile = (
<AclProxy <AclProxy
aclObject={this.props.whitelabel} aclObject={whitelabel}
aclName="acl_view_settings_account_hash"> aclName="acl_view_settings_account_hash">
<Form <Form
url={ApiUrls.users_profile} url={ApiUrls.users_profile}
@ -78,10 +80,9 @@ let AccountSettings = React.createClass({
getFormData={this.getFormDataProfile}> getFormData={this.getFormDataProfile}>
<Property <Property
name="hash_locally" name="hash_locally"
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle">
style={{paddingBottom: 0}}>
<InputCheckbox <InputCheckbox
defaultChecked={this.props.currentUser.profile.hash_locally}> defaultChecked={currentUser.profile.hash_locally}>
<span> <span>
{' ' + getLangText('Enable hash option, e.g. slow connections or to keep piece private')} {' ' + getLangText('Enable hash option, e.g. slow connections or to keep piece private')}
</span> </span>
@ -97,9 +98,9 @@ let AccountSettings = React.createClass({
defaultExpanded={true}> defaultExpanded={true}>
{content} {content}
<AclProxy <AclProxy
aclObject={this.props.whitelabel} aclObject={whitelabel}
aclName="acl_view_settings_copyright_association"> aclName="acl_view_settings_copyright_association">
<CopyrightAssociationForm currentUser={this.props.currentUser}/> <CopyrightAssociationForm currentUser={currentUser} />
</AclProxy> </AclProxy>
{profile} {profile}
</CollapsibleParagraph> </CollapsibleParagraph>

View File

@ -8,12 +8,6 @@ import CreateContractForm from '../ascribe_forms/form_create_contract';
import ContractListStore from '../../stores/contract_list_store'; import ContractListStore from '../../stores/contract_list_store';
import ContractListActions from '../../actions/contract_list_actions'; import ContractListActions from '../../actions/contract_list_actions';
import UserStore from '../../stores/user_store';
import UserActions from '../../actions/user_actions';
import WhitelabelStore from '../../stores/whitelabel_store';
import WhitelabelActions from '../../actions/whitelabel_actions';
import ActionPanel from '../ascribe_panel/action_panel'; import ActionPanel from '../ascribe_panel/action_panel';
import ContractSettingsUpdateButton from './contract_settings_update_button'; import ContractSettingsUpdateButton from './contract_settings_update_button';
@ -24,30 +18,29 @@ import AclProxy from '../acl_proxy';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { setDocumentTitle } from '../../utils/dom_utils'; import { setDocumentTitle } from '../../utils/dom_utils';
import { mergeOptions, truncateTextAtCharIndex } from '../../utils/general_utils'; import { truncateTextAtCharIndex } from '../../utils/general_utils';
let ContractSettings = React.createClass({ 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
},
getInitialState() { getInitialState() {
return mergeOptions( return ContractListStore.getState();
ContractListStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
ContractListStore.listen(this.onChange); ContractListStore.listen(this.onChange);
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
UserActions.fetchCurrentUser();
ContractListActions.fetchContractList(true); ContractListActions.fetchContractList(true);
}, },
componentWillUnmount() { componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
ContractListStore.unlisten(this.onChange); ContractListStore.unlisten(this.onChange);
}, },
@ -79,6 +72,7 @@ let ContractSettings = React.createClass({
}, },
render() { render() {
const { currentUser, location, whitelabel } = this.props;
const publicContracts = this.getPublicContracts(); const publicContracts = this.getPublicContracts();
const privateContracts = this.getPrivateContracts(); const privateContracts = this.getPrivateContracts();
let createPublicContractForm = null; let createPublicContractForm = null;
@ -88,11 +82,11 @@ let ContractSettings = React.createClass({
if (publicContracts.length === 0) { if (publicContracts.length === 0) {
createPublicContractForm = ( createPublicContractForm = (
<CreateContractForm <CreateContractForm
isPublic={true}
fileClassToUpload={{ fileClassToUpload={{
singular: 'new contract', singular: 'new contract',
plural: 'new contracts' plural: 'new contracts'
}} /> }}
isPublic={true} />
); );
} }
@ -103,7 +97,7 @@ let ContractSettings = React.createClass({
defaultExpanded={true}> defaultExpanded={true}>
<AclProxy <AclProxy
aclName="acl_edit_public_contract" aclName="acl_edit_public_contract"
aclObject={this.state.currentUser.acl}> aclObject={currentUser.acl}>
<div> <div>
{createPublicContractForm} {createPublicContractForm}
{publicContracts.map((contract, i) => { {publicContracts.map((contract, i) => {
@ -115,10 +109,9 @@ let ContractSettings = React.createClass({
buttons={ buttons={
<div className="pull-right"> <div className="pull-right">
<AclProxy <AclProxy
aclObject={this.state.whitelabel} aclObject={whitelabel}
aclName="acl_update_public_contract"> aclName="acl_update_public_contract">
<ContractSettingsUpdateButton <ContractSettingsUpdateButton contract={contract} />
contract={contract} />
</AclProxy> </AclProxy>
<a <a
className="btn btn-default btn-sm margin-left-2px" className="btn btn-default btn-sm margin-left-2px"
@ -141,14 +134,14 @@ let ContractSettings = React.createClass({
</AclProxy> </AclProxy>
<AclProxy <AclProxy
aclName="acl_edit_private_contract" aclName="acl_edit_private_contract"
aclObject={this.state.currentUser.acl}> aclObject={currentUser.acl}>
<div> <div>
<CreateContractForm <CreateContractForm
isPublic={false} fileClassToUpload={{
fileClassToUpload={{ singular: getLangText('new contract'),
singular: getLangText('new contract'), plural: getLangText('new contracts')
plural: getLangText('new contracts') }}
}} /> isPublic={false} />
{privateContracts.map((contract, i) => { {privateContracts.map((contract, i) => {
return ( return (
<ActionPanel <ActionPanel
@ -158,10 +151,9 @@ let ContractSettings = React.createClass({
buttons={ buttons={
<div className="pull-right"> <div className="pull-right">
<AclProxy <AclProxy
aclObject={this.state.whitelabel} aclObject={whitelabel}
aclName="acl_update_private_contract"> aclName="acl_update_private_contract">
<ContractSettingsUpdateButton <ContractSettingsUpdateButton contract={contract} />
contract={contract} />
</AclProxy> </AclProxy>
<a <a
className="btn btn-default btn-sm margin-left-2px" className="btn btn-default btn-sm margin-left-2px"

View File

@ -2,12 +2,8 @@
import React from 'react'; import React from 'react';
import UserStore from '../../stores/user_store';
import UserActions from '../../actions/user_actions'; import UserActions from '../../actions/user_actions';
import WhitelabelStore from '../../stores/whitelabel_store';
import WhitelabelActions from '../../actions/whitelabel_actions';
import AccountSettings from './account_settings'; import AccountSettings from './account_settings';
import BitcoinWalletSettings from './bitcoin_wallet_settings'; import BitcoinWalletSettings from './bitcoin_wallet_settings';
import APISettings from './api_settings'; import APISettings from './api_settings';
@ -24,56 +20,42 @@ let SettingsContainer = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element]) React.PropTypes.element
]),
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
}, },
getInitialState() { loadUser(invalidateCache) {
return mergeOptions(
UserStore.getState(),
WhitelabelStore.getState()
);
},
componentDidMount() {
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
UserActions.fetchCurrentUser();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
},
loadUser(invalidateCache){
UserActions.fetchCurrentUser(invalidateCache); UserActions.fetchCurrentUser(invalidateCache);
}, },
onChange(state) {
this.setState(state);
},
render() { render() {
const { children, currentUser, whitelabel } = this.props;
setDocumentTitle(getLangText('Account settings')); setDocumentTitle(getLangText('Account settings'));
if (this.state.currentUser && this.state.currentUser.username) { if (currentUser.username) {
return ( return (
<div className="settings-container"> <div className="settings-container">
<AccountSettings <AccountSettings
currentUser={this.state.currentUser} currentUser={currentUser}
loadUser={this.loadUser} loadUser={this.loadUser}
whitelabel={this.state.whitelabel}/> whitelabel={whitelabel} />
{this.props.children} {children}
<AclProxy <AclProxy
aclObject={this.state.whitelabel} aclObject={whitelabel}
aclName="acl_view_settings_api"> aclName="acl_view_settings_api">
<APISettings /> <APISettings />
</AclProxy> </AclProxy>
<WebhookSettings /> <WebhookSettings />
<AclProxy <AclProxy
aclObject={this.state.whitelabel} aclObject={whitelabel}
aclName="acl_view_settings_bitcoin"> aclName="acl_view_settings_bitcoin">
<BitcoinWalletSettings /> <BitcoinWalletSettings />
</AclProxy> </AclProxy>

View File

@ -18,6 +18,11 @@ import { setDocumentTitle } from '../utils/dom_utils';
let CoaVerifyContainer = React.createClass({ let CoaVerifyContainer = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -27,7 +32,7 @@ let CoaVerifyContainer = React.createClass({
return ( return (
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<br/> <br />
<div className="ascribe-login-text ascribe-login-header"> <div className="ascribe-login-text ascribe-login-header">
{getLangText('Verify your Certificate of Authenticity')} {getLangText('Verify your Certificate of Authenticity')}
</div> </div>
@ -37,7 +42,7 @@ let CoaVerifyContainer = React.createClass({
signature={signature}/> signature={signature}/>
<br /> <br />
<br /> <br />
{getLangText('ascribe is using the following public key for verification')}: {getLangText('ascribe is using the following public key for verification')}:
<br /> <br />
<pre> <pre>
-----BEGIN PUBLIC KEY----- -----BEGIN PUBLIC KEY-----
@ -60,9 +65,8 @@ let CoaVerifyForm = React.createClass({
}, },
handleSuccess(response){ handleSuccess(response){
let notification = null;
if (response.verdict) { if (response.verdict) {
notification = new GlobalNotificationModel(getLangText('Certificate of Authenticity successfully verified'), 'success'); const notification = new GlobalNotificationModel(getLangText('Certificate of Authenticity successfully verified'), 'success');
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
} }
}, },
@ -71,46 +75,44 @@ let CoaVerifyForm = React.createClass({
const { message, signature } = this.props; const { message, signature } = this.props;
return ( return (
<div> <Form
<Form url={ApiUrls.coa_verify}
url={ApiUrls.coa_verify} handleSuccess={this.handleSuccess}
handleSuccess={this.handleSuccess} buttons={
buttons={ <button
<button type="submit"
type="submit" className="btn btn-default btn-wide">
className="btn btn-default btn-wide"> {getLangText('Verify your Certificate of Authenticity')}
{getLangText('Verify your Certificate of Authenticity')} </button>
</button>} }
spinner={ spinner={
<span className="btn btn-default btn-wide btn-spinner"> <span className="btn btn-default btn-wide btn-spinner">
<AscribeSpinner color="dark-blue" size="md" /> <AscribeSpinner color="dark-blue" size="md" />
</span> </span>
}> }>
<Property <Property
name='message' name='message'
label={getLangText('Message')}> label={getLangText('Message')}>
<input <input
type="text" type="text"
placeholder={getLangText('Copy paste the message on the bottom of your Certificate of Authenticity')} placeholder={getLangText('Copy paste the message on the bottom of your Certificate of Authenticity')}
autoComplete="on" autoComplete="on"
defaultValue={message} defaultValue={message}
name="username" required />
required/> </Property>
</Property> <Property
<Property name='signature'
name='signature' label="Signature"
label="Signature" editable={true}
editable={true} overrideForm={true}>
overrideForm={true}> <InputTextAreaToggable
<InputTextAreaToggable rows={3}
rows={3} placeholder={getLangText('Copy paste the signature on the bottom of your Certificate of Authenticity')}
placeholder={getLangText('Copy paste the signature on the bottom of your Certificate of Authenticity')} defaultValue={signature}
defaultValue={signature} required />
required/> </Property>
</Property> <hr />
<hr /> </Form>
</Form>
</div>
); );
} }
}); });

View File

@ -1,21 +1,42 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
let ErrorNotFoundPage = React.createClass({ let ErrorNotFoundPage = React.createClass({
propTypes: { propTypes: {
message: React.PropTypes.string message: React.PropTypes.string,
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
}, },
mixins: [History],
getDefaultProps() { getDefaultProps() {
return { return {
message: getLangText("Oops, the page you are looking for doesn't exist.") message: getLangText("Oops, the page you are looking for doesn't exist.")
}; };
}, },
componentDidMount() {
// The previous page, if any, is the second item in the locationQueue
const { locationQueue: [ , previousPage ] } = this.history;
if (previousPage) {
console.logGlobal('Page not found', {
previousPath: previousPage.pathname
});
}
},
render() { render() {
return ( return (
<div className="row"> <div className="row">
@ -32,4 +53,4 @@ let ErrorNotFoundPage = React.createClass({
} }
}); });
export default ErrorNotFoundPage; export default ErrorNotFoundPage;

View File

@ -5,8 +5,12 @@ import React from 'react';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
let Footer = React.createClass({ let Footer = React.createClass({
propTypes: {
activeRoute: React.PropTypes.object.isRequired
},
render() { render() {
return ( return !this.props.activeRoute.hideFooter ? (
<div className="ascribe-footer hidden-print"> <div className="ascribe-footer hidden-print">
<p className="ascribe-sub-sub-statement"> <p className="ascribe-sub-sub-statement">
<br /> <br />
@ -24,7 +28,7 @@ let Footer = React.createClass({
<a href="https://www.linkedin.com/company/4816284?trk=vsrp_companies_res_name&trkInfo=VSRPsearchId%3A122827941425632318075%2CVSRPtargetId%3A4816284%2CVSRPcmpt%3Aprimary" className="social social-linked-in" target="_blank"></a> <a href="https://www.linkedin.com/company/4816284?trk=vsrp_companies_res_name&trkInfo=VSRPsearchId%3A122827941425632318075%2CVSRPtargetId%3A4816284%2CVSRPcmpt%3Aprimary" className="social social-linked-in" target="_blank"></a>
</p> </p>
</div> </div>
); ) : null;
} }
}); });

View File

@ -14,41 +14,28 @@ import NavItem from 'react-bootstrap/lib/NavItem';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import AclProxy from './acl_proxy';
import EventActions from '../actions/event_actions'; import EventActions from '../actions/event_actions';
import PieceListStore from '../stores/piece_list_store'; import PieceListStore from '../stores/piece_list_store';
import UserActions from '../actions/user_actions'; import AclProxy from './acl_proxy';
import UserStore from '../stores/user_store';
import WhitelabelActions from '../actions/whitelabel_actions';
import WhitelabelStore from '../stores/whitelabel_store';
import HeaderNotifications from './header_notification'; import HeaderNotifications from './header_notification';
import HeaderNotificationDebug from './header_notification_debug'; import HeaderNotificationDebug from './header_notification_debug';
import NavRoutesLinks from './nav_routes_links'; import NavRoutesLinks from './nav_routes_links';
import { mergeOptions } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
import { constructHead } from '../utils/dom_utils'; import { constructHead } from '../utils/dom_utils';
let Header = React.createClass({ let Header = React.createClass({
propTypes: { propTypes: {
routes: React.PropTypes.arrayOf(React.PropTypes.object) currentUser: React.PropTypes.object.isRequired,
routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
whitelabel: React.PropTypes.object.isRequired
}, },
getInitialState() { getInitialState() {
return mergeOptions( return PieceListStore.getState();
PieceListStore.getState(),
WhitelabelStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
@ -56,35 +43,14 @@ let Header = React.createClass({
// conflicts with routes that may need to wait to load the piece list // conflicts with routes that may need to wait to load the piece list
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser.defer();
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel.defer();
// react-bootstrap 0.25.1 has a bug in which it doesn't // react-bootstrap 0.25.1 has a bug in which it doesn't
// close the mobile expanded navigation after a click by itself. // close the mobile expanded navigation after a click by itself.
// To get rid of this, we set the state of the component ourselves. // To get rid of this, we set the state of the component ourselves.
history.listen(this.onRouteChange); history.listen(this.onRouteChange);
if (this.state.currentUser && this.state.currentUser.email) {
EventActions.profileDidLoad.defer(this.state.currentUser);
}
},
componentWillUpdate(nextProps, nextState) {
const { currentUser: { email: curEmail } = {} } = this.state;
const { currentUser: { email: nextEmail } = {} } = nextState;
if (nextEmail && curEmail !== nextEmail) {
EventActions.profileDidLoad.defer(nextState.currentUser);
}
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
//history.unlisten(this.onRouteChange); //history.unlisten(this.onRouteChange);
}, },
@ -93,7 +59,7 @@ let Header = React.createClass({
}, },
getLogo() { getLogo() {
let { whitelabel } = this.state; const { whitelabel } = this.props;
if (whitelabel.head) { if (whitelabel.head) {
constructHead(whitelabel.head); constructHead(whitelabel.head);
@ -117,7 +83,7 @@ let Header = React.createClass({
getPoweredBy() { getPoweredBy() {
return ( return (
<AclProxy <AclProxy
aclObject={this.state.whitelabel} aclObject={this.props.whitelabel}
aclName="acl_view_powered_by"> aclName="acl_view_powered_by">
<li> <li>
<a className="pull-right ascribe-powered-by" href="https://www.ascribe.io/" target="_blank"> <a className="pull-right ascribe-powered-by" href="https://www.ascribe.io/" target="_blank">
@ -164,7 +130,9 @@ let Header = React.createClass({
}, },
render() { render() {
const { currentUser, unfilteredPieceListCount } = this.state; const { currentUser, routes } = this.props;
const { unfilteredPieceListCount } = this.state;
let account; let account;
let signup; let signup;
let navRoutesLinks; let navRoutesLinks;
@ -173,13 +141,13 @@ let Header = React.createClass({
account = ( account = (
<DropdownButton <DropdownButton
ref='dropdownbutton' ref='dropdownbutton'
id="nav-route-user-dropdown"
eventKey="1" eventKey="1"
title={currentUser.username}> title={currentUser.username}>
<LinkContainer <LinkContainer
to="/settings" to="/settings"
onClick={this.onMenuItemClick}> onClick={this.onMenuItemClick}>
<MenuItem <MenuItem eventKey="2">
eventKey="2">
{getLangText('Account Settings')} {getLangText('Account Settings')}
</MenuItem> </MenuItem>
</LinkContainer> </LinkContainer>
@ -189,17 +157,14 @@ let Header = React.createClass({
<LinkContainer <LinkContainer
to="/contract_settings" to="/contract_settings"
onClick={this.onMenuItemClick}> onClick={this.onMenuItemClick}>
<MenuItem <MenuItem eventKey="2">
eventKey="2">
{getLangText('Contract Settings')} {getLangText('Contract Settings')}
</MenuItem> </MenuItem>
</LinkContainer> </LinkContainer>
</AclProxy> </AclProxy>
<MenuItem divider /> <MenuItem divider />
<LinkContainer <LinkContainer to="/logout">
to="/logout"> <MenuItem eventKey="3">
<MenuItem
eventKey="3">
{getLangText('Log out')} {getLangText('Log out')}
</MenuItem> </MenuItem>
</LinkContainer> </LinkContainer>
@ -216,21 +181,19 @@ let Header = React.createClass({
navbar navbar
right right
hasPieces={!!unfilteredPieceListCount} hasPieces={!!unfilteredPieceListCount}
routes={this.props.routes} routes={routes}
userAcl={currentUser.acl} /> userAcl={currentUser.acl} />
); );
} else { } else {
account = ( account = (
<LinkContainer <LinkContainer to="/login">
to="/login">
<NavItem> <NavItem>
{getLangText('LOGIN')} {getLangText('LOGIN')}
</NavItem> </NavItem>
</LinkContainer> </LinkContainer>
); );
signup = ( signup = (
<LinkContainer <LinkContainer to="/signup">
to="/signup">
<NavItem> <NavItem>
{getLangText('SIGNUP')} {getLangText('SIGNUP')}
</NavItem> </NavItem>
@ -246,13 +209,12 @@ let Header = React.createClass({
toggleNavKey={0} toggleNavKey={0}
fixedTop={true} fixedTop={true}
className="hidden-print"> className="hidden-print">
<CollapsibleNav <CollapsibleNav eventKey={0}>
eventKey={0}>
<Nav navbar left> <Nav navbar left>
{this.getPoweredBy()} {this.getPoweredBy()}
</Nav> </Nav>
<Nav navbar right> <Nav navbar right>
<HeaderNotificationDebug show={false}/> <HeaderNotificationDebug show={false} />
{account} {account}
{signup} {signup}
</Nav> </Nav>

View File

@ -11,16 +11,12 @@ import Nav from 'react-bootstrap/lib/Nav';
import NotificationActions from '../actions/notification_actions'; import NotificationActions from '../actions/notification_actions';
import NotificationStore from '../stores/notification_store'; import NotificationStore from '../stores/notification_store';
import { mergeOptions } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
let HeaderNotifications = React.createClass({ let HeaderNotifications = React.createClass({
getInitialState() { getInitialState() {
return mergeOptions( return NotificationStore.getState();
NotificationStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
@ -62,7 +58,7 @@ let HeaderNotifications = React.createClass({
this.refs.dropdownbutton.setDropdownState(false); this.refs.dropdownbutton.setDropdownState(false);
}, },
getPieceNotifications(){ getPieceNotifications() {
if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) { if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) {
return ( return (
<div> <div>
@ -87,7 +83,7 @@ let HeaderNotifications = React.createClass({
return null; return null;
}, },
getEditionNotifications(){ getEditionNotifications() {
if (this.state.editionListNotifications && this.state.editionListNotifications.length > 0) { if (this.state.editionListNotifications && this.state.editionListNotifications.length > 0) {
return ( return (
<div> <div>
@ -114,7 +110,7 @@ let HeaderNotifications = React.createClass({
render() { render() {
if ((this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) || if ((this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) ||
(this.state.editionListNotifications && this.state.editionListNotifications.length > 0)){ (this.state.editionListNotifications && this.state.editionListNotifications.length > 0)) {
let numNotifications = 0; let numNotifications = 0;
if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) { if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) {
numNotifications += this.state.pieceListNotifications.length; numNotifications += this.state.pieceListNotifications.length;
@ -125,7 +121,8 @@ let HeaderNotifications = React.createClass({
return ( return (
<Nav navbar right> <Nav navbar right>
<DropdownButton <DropdownButton
ref='dropdownbutton' ref='dropdownButton'
id="header-notification-dropdown"
eventKey="1" eventKey="1"
title={ title={
<span> <span>

View File

@ -11,19 +11,12 @@ import { setDocumentTitle } from '../utils/dom_utils';
let LoginContainer = React.createClass({ let LoginContainer = React.createClass({
propTypes: { propTypes: {
message: React.PropTypes.string, // Provided from AscribeApp
redirectOnLoggedIn: React.PropTypes.bool, currentUser: React.PropTypes.object,
redirectOnLoginSuccess: React.PropTypes.bool, whitelabel: React.PropTypes.object,
onLogin: React.PropTypes.func,
location: React.PropTypes.object
},
getDefaultProps() { // Provided from router
return { location: React.PropTypes.object
message: getLangText('Enter') + ' ascribe',
redirectOnLoggedIn: true,
redirectOnLoginSuccess: true
};
}, },
render() { render() {
@ -31,12 +24,7 @@ let LoginContainer = React.createClass({
return ( return (
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<LoginForm <LoginForm location={this.props.location} />
redirectOnLoggedIn={this.props.redirectOnLoggedIn}
redirectOnLoginSuccess={this.props.redirectOnLoginSuccess}
message={this.props.message}
onLogin={this.props.onLogin}
location={this.props.location}/>
<div className="ascribe-login-text"> <div className="ascribe-login-text">
{getLangText('Not an ascribe user')}&#63; <Link to="/signup">{getLangText('Sign up')}...</Link><br/> {getLangText('Not an ascribe user')}&#63; <Link to="/signup">{getLangText('Sign up')}...</Link><br/>
{getLangText('Forgot my password')}&#63; <Link to="/password_reset">{getLangText('Rescue me')}...</Link> {getLangText('Forgot my password')}&#63; <Link to="/password_reset">{getLangText('Rescue me')}...</Link>

View File

@ -6,23 +6,25 @@ import { History } from 'react-router';
import AscribeSpinner from './ascribe_spinner'; import AscribeSpinner from './ascribe_spinner';
import UserActions from '../actions/user_actions'; import UserActions from '../actions/user_actions';
import { alt, altWhitelabel, altUser, altThirdParty } from '../alt';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
import { setDocumentTitle } from '../utils/dom_utils'; import { setDocumentTitle } from '../utils/dom_utils';
let LogoutContainer = React.createClass({ let LogoutContainer = React.createClass({
propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
},
mixins: [History], mixins: [History],
componentDidMount() { componentDidMount() {
UserActions.logoutCurrentUser(); UserActions.logoutCurrentUser();
alt.flush();
altWhitelabel.flush();
altUser.flush();
altThirdParty.flush();
// kill intercom (with fire)
window.Intercom('shutdown');
}, },
render() { render() {

View File

@ -30,6 +30,7 @@ let NavRoutesLinksLink = React.createClass({
return ( return (
<DropdownButton <DropdownButton
disabled={disabled} disabled={disabled}
id={`nav-route-${headerTitle.toLowerCase()}-dropdown`}
title={headerTitle}> title={headerTitle}>
{children} {children}
</DropdownButton> </DropdownButton>

View File

@ -16,52 +16,44 @@ import { setDocumentTitle } from '../utils/dom_utils';
let PasswordResetContainer = React.createClass({ let PasswordResetContainer = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
return {isRequested: false}; return { isRequested: false };
}, },
handleRequestSuccess(email) { handleRequestSuccess(email) {
this.setState({isRequested: email}); this.setState({ isRequested: !!email });
}, },
render() { render() {
let { location } = this.props; const { email: emailQuery, token: tokenQuery } = this.props.location.query;
const { isRequested } = this.state;
if (location.query.email && location.query.token) { if (emailQuery && tokenQuery) {
return ( return (
<div> <PasswordResetForm
<PasswordResetForm email={emailQuery}
email={location.query.email} token={tokenQuery} />
token={location.query.token}/> );
} else if (!isRequested) {
return (
<PasswordRequestResetForm handleRequestSuccess={this.handleRequestSuccess} />
);
} else {
return (
<div className="ascribe-login-text ascribe-login-header">
{getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.')}
</div> </div>
); );
} }
else { }
if (this.state.isRequested === false) {
return (
<div>
<PasswordRequestResetForm
handleRequestSuccess={this.handleRequestSuccess}/>
</div>
);
}
else if (this.state.isRequested) {
return (
<div>
<div className="ascribe-login-text ascribe-login-header">
{getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.')}
</div>
</div>
);
}
else {
return <span />;
}
}
}
}); });
let PasswordRequestResetForm = React.createClass({ let PasswordRequestResetForm = React.createClass({
@ -70,9 +62,10 @@ let PasswordRequestResetForm = React.createClass({
}, },
handleSuccess() { handleSuccess() {
let notificationText = getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.'); const notificationText = getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.');
let notification = new GlobalNotificationModel(notificationText, 'success', 50000); const notification = new GlobalNotificationModel(notificationText, 'success', 50000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.props.handleRequestSuccess(this.refs.form.refs.email.state.value); this.props.handleRequestSuccess(this.refs.form.refs.email.state.value);
}, },
@ -90,12 +83,13 @@ let PasswordRequestResetForm = React.createClass({
type="submit" type="submit"
className="btn btn-default btn-wide"> className="btn btn-default btn-wide">
{getLangText('Reset your password')} {getLangText('Reset your password')}
</button>} </button>
}
spinner={ spinner={
<span className="btn btn-default btn-wide btn-spinner"> <span className="btn btn-default btn-wide btn-spinner">
<AscribeSpinner color="dark-blue" size="md" /> <AscribeSpinner color="dark-blue" size="md" />
</span> </span>
}> }>
<div className="ascribe-form-header"> <div className="ascribe-form-header">
<h3>{getLangText('Reset your password')}</h3> <h3>{getLangText('Reset your password')}</h3>
</div> </div>
@ -149,12 +143,13 @@ let PasswordResetForm = React.createClass({
type="submit" type="submit"
className="btn btn-default btn-wide"> className="btn btn-default btn-wide">
{getLangText('Reset your password')} {getLangText('Reset your password')}
</button>} </button>
}
spinner={ spinner={
<span className="btn btn-default btn-wide btn-spinner"> <span className="btn btn-default btn-wide btn-spinner">
<AscribeSpinner color="dark-blue" size="md" /> <AscribeSpinner color="dark-blue" size="md" />
</span> </span>
}> }>
<div className="ascribe-form-header"> <div className="ascribe-form-header">
<h3>{getLangText('Reset the password for')} {this.props.email}</h3> <h3>{getLangText('Reset the password for')} {this.props.email}</h3>
</div> </div>

View File

@ -46,6 +46,12 @@ let PieceList = React.createClass({
filterParams: React.PropTypes.array, filterParams: React.PropTypes.array,
orderParams: React.PropTypes.array, orderParams: React.PropTypes.array,
orderBy: React.PropTypes.string, orderBy: React.PropTypes.string,
// Provided from AscribeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -93,7 +99,7 @@ let PieceList = React.createClass({
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
EditionListStore.listen(this.onChange); EditionListStore.listen(this.onChange);
let page = this.props.location.query.page || 1; const page = this.props.location.query.page || 1;
if (this.props.canLoadPieceList && (this.state.pieceList.length === 0 || this.state.page !== page)) { if (this.props.canLoadPieceList && (this.state.pieceList.length === 0 || this.state.page !== page)) {
this.loadPieceList({ page }); this.loadPieceList({ page });
} }
@ -175,8 +181,8 @@ let PieceList = React.createClass({
}, },
getPagination() { getPagination() {
let currentPage = parseInt(this.props.location.query.page, 10) || 1; const currentPage = parseInt(this.props.location.query.page, 10) || 1;
let totalPages = Math.ceil(this.state.pieceListCount / this.state.pageSize); const totalPages = Math.ceil(this.state.pieceListCount / this.state.pageSize);
if (this.state.pieceListCount > 20) { if (this.state.pieceListCount > 20) {
return ( return (
@ -203,8 +209,7 @@ let PieceList = React.createClass({
}); });
// first we need to apply the filter on the piece list // first we need to apply the filter on the piece list
this this.loadPieceList({ page: 1, filterBy })
.loadPieceList({ page: 1, filterBy })
.then(() => { .then(() => {
// but also, we need to filter all the open edition lists // but also, we need to filter all the open edition lists
this.state.pieceList this.state.pieceList
@ -239,23 +244,22 @@ let PieceList = React.createClass({
}, },
fetchSelectedPieceEditionList() { fetchSelectedPieceEditionList() {
let filteredPieceIdList = Object.keys(this.state.editionList) const filteredPieceIdList = Object.keys(this.state.editionList)
.filter((pieceId) => { .filter((pieceId) => {
return this.state.editionList[pieceId] return this.state.editionList[pieceId]
.filter((edition) => edition.selected).length > 0; .filter((edition) => edition.selected)
}); .length;
});
return filteredPieceIdList; return filteredPieceIdList;
}, },
fetchSelectedEditionList() { fetchSelectedEditionList() {
let selectedEditionList = []; const selectedEditionList = Object.keys(this.state.editionList)
.reduce((selectedList, pieceId) => {
Object const selectedEditionsForPiece = this.state.editionList[pieceId]
.keys(this.state.editionList) .filter((edition) => edition.selected);
.forEach((pieceId) => { return selectedList.concat(selectedEditionsForPiece);
let filteredEditionsForPiece = this.state.editionList[pieceId].filter((edition) => edition.selected); }, []);
selectedEditionList = selectedEditionList.concat(filteredEditionsForPiece);
});
return selectedEditionList; return selectedEditionList;
}, },
@ -267,7 +271,7 @@ let PieceList = React.createClass({
this.fetchSelectedPieceEditionList() this.fetchSelectedPieceEditionList()
.forEach((pieceId) => { .forEach((pieceId) => {
EditionListActions.refreshEditionList({pieceId}); EditionListActions.refreshEditionList({ pieceId });
}); });
EditionListActions.clearAllEditionSelections(); EditionListActions.clearAllEditionSelections();
}, },
@ -276,10 +280,12 @@ let PieceList = React.createClass({
const { const {
accordionListItemType: AccordionListItemType, accordionListItemType: AccordionListItemType,
bulkModalButtonListType: BulkModalButtonListType, bulkModalButtonListType: BulkModalButtonListType,
currentUser,
customSubmitButton, customSubmitButton,
customThumbnailPlaceholder, customThumbnailPlaceholder,
filterParams, filterParams,
orderParams } = this.props; orderParams,
whitelabel } = this.props;
const loadingElement = <AscribeSpinner color='dark-blue' size='lg'/>; const loadingElement = <AscribeSpinner color='dark-blue' size='lg'/>;
@ -287,6 +293,7 @@ let PieceList = React.createClass({
const availableAcls = getAvailableAcls(selectedEditions, (aclName) => aclName !== 'acl_view'); const availableAcls = getAvailableAcls(selectedEditions, (aclName) => aclName !== 'acl_view');
setDocumentTitle(getLangText('Collection')); setDocumentTitle(getLangText('Collection'));
return ( return (
<div> <div>
<PieceListToolbar <PieceListToolbar
@ -312,17 +319,19 @@ let PieceList = React.createClass({
className="ascribe-piece-list-bulk-modal"> className="ascribe-piece-list-bulk-modal">
<BulkModalButtonListType <BulkModalButtonListType
availableAcls={availableAcls} availableAcls={availableAcls}
pieceOrEditions={selectedEditions} currentUser={currentUser}
handleSuccess={this.handleAclSuccess} handleSuccess={this.handleAclSuccess}
pieceOrEditions={selectedEditions}
whitelabel={whitelabel}
className="text-center ascribe-button-list collapse-group"> className="text-center ascribe-button-list collapse-group">
<DeleteButton <DeleteButton
handleSuccess={this.handleAclSuccess} handleSuccess={this.handleAclSuccess}
editions={selectedEditions}/> editions={selectedEditions} />
</BulkModalButtonListType> </BulkModalButtonListType>
</PieceListBulkModal> </PieceListBulkModal>
<PieceListFilterDisplay <PieceListFilterDisplay
filterBy={this.state.filterBy} filterBy={this.state.filterBy}
filterParams={filterParams}/> filterParams={filterParams} />
<AccordionList <AccordionList
className="ascribe-accordion-list" className="ascribe-accordion-list"
changeOrder={this.accordionChangeOrder} changeOrder={this.accordionChangeOrder}
@ -335,13 +344,15 @@ let PieceList = React.createClass({
page={this.state.page} page={this.state.page}
pageSize={this.state.pageSize} pageSize={this.state.pageSize}
loadingElement={loadingElement}> loadingElement={loadingElement}>
{this.state.pieceList.map((piece, i) => { {this.state.pieceList.map((piece) => {
return ( return (
<AccordionListItemType <AccordionListItemType
key={piece.id}
className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item" className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item"
content={piece} content={piece}
currentUser={currentUser}
thumbnailPlaceholder={customThumbnailPlaceholder} thumbnailPlaceholder={customThumbnailPlaceholder}
key={i}> whitelabel={whitelabel}>
<AccordionListItemTableEditions <AccordionListItemTableEditions
className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2" className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2"
parentId={piece.id} /> parentId={piece.id} />

View File

@ -6,27 +6,20 @@ import { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
import WhitelabelActions from '../actions/whitelabel_actions';
import WhitelabelStore from '../stores/whitelabel_store';
import PieceListStore from '../stores/piece_list_store'; import PieceListStore from '../stores/piece_list_store';
import PieceListActions from '../actions/piece_list_actions'; import PieceListActions from '../actions/piece_list_actions';
import UserStore from '../stores/user_store';
import GlobalNotificationModel from '../models/global_notification_model'; import GlobalNotificationModel from '../models/global_notification_model';
import GlobalNotificationActions from '../actions/global_notification_actions'; import GlobalNotificationActions from '../actions/global_notification_actions';
import Property from './ascribe_forms/property'; import Property from './ascribe_forms/property';
import RegisterPieceForm from './ascribe_forms/form_register_piece'; import RegisterPieceForm from './ascribe_forms/form_register_piece';
import { mergeOptions } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils'; import { getLangText } from '../utils/lang_utils';
import { setDocumentTitle } from '../utils/dom_utils'; import { setDocumentTitle } from '../utils/dom_utils';
let RegisterPiece = React.createClass( { let RegisterPiece = React.createClass( {
propTypes: { propTypes: {
headerMessage: React.PropTypes.string, headerMessage: React.PropTypes.string,
submitMessage: React.PropTypes.string, submitMessage: React.PropTypes.string,
@ -35,30 +28,27 @@ let RegisterPiece = React.createClass( {
React.PropTypes.element, React.PropTypes.element,
React.PropTypes.string React.PropTypes.string
]), ]),
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
mixins: [History], mixins: [History],
getInitialState(){ getInitialState(){
return mergeOptions( return PieceListStore.getState();
UserStore.getState(),
WhitelabelStore.getState(),
PieceListStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -79,7 +69,9 @@ let RegisterPiece = React.createClass( {
}, },
getSpecifyEditions() { getSpecifyEditions() {
if (this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) { const { whitelabel } = this.props;
if (whitelabel.acl_create_editions || Object.keys(whitelabel).length) {
return ( return (
<Property <Property
name="num_editions" name="num_editions"
@ -105,8 +97,7 @@ let RegisterPiece = React.createClass( {
<RegisterPieceForm <RegisterPieceForm
{...this.props} {...this.props}
isFineUploaderActive={true} isFineUploaderActive={true}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}>
location={this.props.location}>
{this.props.children} {this.props.children}
{this.getSpecifyEditions()} {this.getSpecifyEditions()}
</RegisterPieceForm> </RegisterPieceForm>

View File

@ -11,6 +11,11 @@ import { setDocumentTitle } from '../utils/dom_utils';
let SignupContainer = React.createClass({ let SignupContainer = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -21,7 +26,7 @@ let SignupContainer = React.createClass({
}; };
}, },
handleSuccess(message){ handleSuccess(message) {
this.setState({ this.setState({
submitted: true, submitted: true,
message: message message: message
@ -29,14 +34,17 @@ let SignupContainer = React.createClass({
}, },
render() { render() {
const { location } = this.props;
const { message, submitted } = this.state;
setDocumentTitle(getLangText('Sign up')); setDocumentTitle(getLangText('Sign up'));
if (this.state.submitted){ if (submitted) {
return ( return (
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<br/> <br/>
<div className="ascribe-login-text ascribe-login-header"> <div className="ascribe-login-text ascribe-login-header">
{this.state.message} {message}
</div> </div>
</div> </div>
); );
@ -45,7 +53,7 @@ let SignupContainer = React.createClass({
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<SignupForm <SignupForm
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
location={this.props.location}/> location={location}/>
<div className="ascribe-login-text"> <div className="ascribe-login-text">
{getLangText('Already an ascribe user')}&#63; <Link to="/login">{getLangText('Log in')}...</Link><br/> {getLangText('Already an ascribe user')}&#63; <Link to="/login">{getLangText('Log in')}...</Link><br/>
</div> </div>

View File

@ -32,9 +32,8 @@ const { object } = React.PropTypes;
const PRRegisterPieceForm = React.createClass({ const PRRegisterPieceForm = React.createClass({
propTypes: { propTypes: {
location: object, currentUser: object.isRequired,
history: object, location: object
currentUser: object
}, },
mixins: [History], mixins: [History],
@ -397,8 +396,7 @@ const PRRegisterPieceForm = React.createClass({
className="ascribe-form-bordered"> className="ascribe-form-bordered">
<Property <Property
name="terms" name="terms"
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle">
style={{paddingBottom: 0}}>
<span> <span>
{getLangText('By submitting this form, you agree to the') + ' '} {getLangText('By submitting this form, you agree to the') + ' '}
<a <a

View File

@ -12,7 +12,7 @@ const PRHero = React.createClass({
propTypes: { propTypes: {
currentUser: React.PropTypes.shape({ currentUser: React.PropTypes.shape({
email: React.PropTypes.object email: React.PropTypes.object
}) }).isRequired
}, },
render() { render() {

View File

@ -3,45 +3,41 @@
import React from 'react'; import React from 'react';
import { History } from 'react-router'; import { History } from 'react-router';
import PrizeActions from '../../simple_prize/actions/prize_actions';
import PrizeStore from '../../simple_prize/stores/prize_store';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import UserStore from '../../../../../stores/user_store'; import PrizeActions from '../../simple_prize/actions/prize_actions';
import UserActions from '../../../../../actions/user_actions'; import PrizeStore from '../../simple_prize/stores/prize_store';
import { mergeOptions, omitFromObject } from '../../../../../utils/general_utils'; import { omitFromObject } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
const PRLanding = React.createClass({ const PRLanding = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
mixins: [History], mixins: [History],
getInitialState() { getInitialState() {
return mergeOptions( return PrizeStore.getState();
PrizeStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
const { location } = this.props; const { location } = this.props;
UserStore.listen(this.onChange);
PrizeStore.listen(this.onChange); PrizeStore.listen(this.onChange);
UserActions.fetchCurrentUser();
PrizeActions.fetchPrize(); PrizeActions.fetchPrize();
if (location && location.query && location.query.redirect) { if (location.query.redirect) {
window.setTimeout(() => this.history.replace({ window.setTimeout(() => this.history.replace({
pathname: `/${location.query.redirect}`, pathname: `/${location.query.redirect}`,
query: omitFromObject(location.query, ['redirect']) query: omitFromObject(location.query, ['redirect'])
@ -50,7 +46,6 @@ const PRLanding = React.createClass({
}, },
componentWillUnmount() { componentWillUnmount() {
UserStore.unlisten(this.onChange);
PrizeStore.unlisten(this.onChange); PrizeStore.unlisten(this.onChange);
}, },
@ -59,7 +54,7 @@ const PRLanding = React.createClass({
}, },
getButtons() { getButtons() {
if (this.state.prize && this.state.prize.active){ if (this.state.prize && this.state.prize.active) {
return ( return (
<ButtonGroup className="enter" bsSize="large" vertical> <ButtonGroup className="enter" bsSize="large" vertical>
<LinkContainer to="/signup"> <LinkContainer to="/signup">
@ -78,39 +73,37 @@ const PRLanding = React.createClass({
</LinkContainer> </LinkContainer>
</ButtonGroup> </ButtonGroup>
); );
} } else {
return ( return (
<ButtonGroup className="enter" bsSize="large" vertical> <ButtonGroup className="enter" bsSize="large" vertical>
<a className="btn btn-default" href="https://www.ascribe.io/app/signup"> <a className="btn btn-default" href="https://www.ascribe.io/app/signup">
{getLangText('Sign up to ascribe')} {getLangText('Sign up to ascribe')}
</a> </a>
<p> <p>
{getLangText('or, already an ascribe user?')} {getLangText('or, already an ascribe user?')}
</p> </p>
<LinkContainer to="/login"> <LinkContainer to="/login">
<Button> <Button>
{getLangText('Log in')} {getLangText('Log in')}
</Button> </Button>
</LinkContainer> </LinkContainer>
</ButtonGroup> </ButtonGroup>
); );
}
}, },
getTitle() { getTitle() {
if (this.state.prize && this.state.prize.active){ const { prize } = this.state;
return (
<p>
{getLangText('This is the submission page for Portfolio Review 2016.')}
</p>
);
}
return ( return (
<p> <p>
{getLangText('Submissions for Portfolio Review 2016 are now closed.')} {getLangText(prize && prize.active ? 'This is the submission page for Portfolio Review 2016.'
: 'Submissions for Portfolio Review 2016 are now closed.')}
</p> </p>
); );
}, },
render() { render() {
return ( return (
<div className="container"> <div className="container">

View File

@ -6,9 +6,6 @@ import { Link, History } from 'react-router';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import PRRegisterPieceForm from './pr_forms/pr_register_piece_form'; import PRRegisterPieceForm from './pr_forms/pr_register_piece_form';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
@ -20,43 +17,31 @@ const { object } = React.PropTypes;
const PRRegisterPiece = React.createClass({ const PRRegisterPiece = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: object location: object
}, },
mixins: [History], mixins: [History],
getInitialState() {
return UserStore.getState();
},
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
},
componentDidUpdate() { componentDidUpdate() {
const { currentUser } = this.state; const { currentUser } = this.props;
if(currentUser && currentUser.email) { if (currentUser.email) {
const submittedPieceId = getCookie(currentUser.email); const submittedPieceId = getCookie(currentUser.email);
if(submittedPieceId) { if (submittedPieceId) {
this.history.push(`/pieces/${submittedPieceId}`); this.history.push(`/pieces/${submittedPieceId}`);
} }
} }
}, },
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() { render() {
const { currentUser } = this.state; const { currentUser, location } = this.props;
const { location } = this.props;
setDocumentTitle(getLangText('Submit to Portfolio Review')); setDocumentTitle(getLangText('Submit to Portfolio Review'));
return ( return (
<Row> <Row>
<Col xs={6}> <Col xs={6}>
@ -77,7 +62,7 @@ const PRRegisterPiece = React.createClass({
<Col xs={6}> <Col xs={6}>
<PRRegisterPieceForm <PRRegisterPieceForm
location={location} location={location}
currentUser={currentUser}/> currentUser={currentUser} />
</Col> </Col>
</Row> </Row>
); );

View File

@ -1,90 +1,73 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import GlobalNotification from '../../../global_notification'; import classNames from 'classnames';
import Hero from './components/pr_hero';
import Header from '../../../header';
import EventActions from '../../../../actions/event_actions'; import EventActions from '../../../../actions/event_actions';
import UserStore from '../../../../stores/user_store'; import UserStore from '../../../../stores/user_store';
import UserActions from '../../../../actions/user_actions'; import UserActions from '../../../../actions/user_actions';
import Hero from './components/pr_hero';
import AppBase from '../../../app_base';
import AppRouteWrapper from '../../../app_route_wrapper';
import Footer from '../../../footer';
import Header from '../../../header';
import { getSubdomain } from '../../../../utils/general_utils'; import { getSubdomain } from '../../../../utils/general_utils';
import { getCookie } from '../../../../utils/fetch_api_utils'; import { getCookie } from '../../../../utils/fetch_api_utils';
let PRApp = React.createClass({ let PRApp = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.oneOfType([ activeRoute: React.PropTypes.object.isRequired,
React.PropTypes.arrayOf(React.PropTypes.element), children: React.PropTypes.element.isRequired,
React.PropTypes.element history: React.PropTypes.object.isRequired,
]), routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
history: React.PropTypes.object,
routes: React.PropTypes.arrayOf(React.PropTypes.object)
},
getInitialState() { // Provided from AppBase
return UserStore.getState(); currentUser: React.PropTypes.object,
}, whitelabel: React.PropTypes.object
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
if (this.state.currentUser && this.state.currentUser.email) {
EventActions.profileDidLoad.defer(this.state.currentUser);
}
},
componentWillUpdate(nextProps, nextState) {
const { currentUser: { email: curEmail } = {} } = this.state;
const { currentUser: { email: nextEmail } = {} } = nextState;
if (nextEmail && curEmail !== nextEmail) {
EventActions.profileDidLoad.defer(nextState.currentUser);
}
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
}, },
render() { render() {
const { history, children, routes } = this.props; const { activeRoute, children, currentUser, history, routes, whitelabel } = this.props;
const { currentUser } = this.state; const subdomain = getSubdomain();
const path = activeRoute && activeRoute.path;
let style = {}; let style = {};
let subdomain = getSubdomain();
let header; let header;
if (currentUser && currentUser.email && history.isActive(`/pieces/${getCookie(currentUser.email)}`)) { if (currentUser && currentUser.email && history.isActive(`/pieces/${getCookie(currentUser.email)}`)) {
header = <Hero currentUser={currentUser} />; header = (<Hero currentUser={currentUser} />);
style = { paddingTop: '0 !important' }; style = { paddingTop: '0 !important' };
} else if(currentUser && (currentUser.is_admin || currentUser.is_jury || currentUser.is_judge)) { } else if (currentUser && (currentUser.is_admin || currentUser.is_jury || currentUser.is_judge)) {
header = <Header routes={routes} />; header = (
<Header
currentUser={currentUser}
routes={routes}
whitelabel={whitelabel}
/>
);
} else { } else {
style = { paddingTop: '0 !important' }; style = { paddingTop: '0 !important' };
} }
return ( return (
<div> <div
style={style}
className={classNames('ascribe-app', 'ascribe-prize-app', `route--${(path ? path.split('/')[0] : 'landing')}`)}>
{header} {header}
<div <AppRouteWrapper
style={style} currentUser={currentUser}
className={'container ascribe-prize-app client--' + subdomain}> whitelabel={whitelabel}>
{/* Routes are injected here */}
{children} {children}
<GlobalNotification /> </AppRouteWrapper>
<div id="modal" className="container"></div> <Footer activeRoute={activeRoute} />
</div>
</div> </div>
); );
} }
}); });
export default PRApp; export default AppBase(PRApp);

View File

@ -31,74 +31,116 @@ import { AuthPrizeRoleRedirect } from './portfolioreview/components/pr_routes/pr
const ROUTES = { const ROUTES = {
sluice: ( sluice: (
<Route path='/' component={SPApp}> <Route path='/' component={SPApp}>
<IndexRoute component={SPLanding} /> <IndexRoute
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SPLanding)}
hideFooter />
<Route <Route
path='login' path='login'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SPLoginContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SPLoginContainer)}
hideFooter />
<Route <Route
path='logout' path='logout'
component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}/> component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}
hideFooter />
<Route <Route
path='signup' path='signup'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SPSignupContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SPSignupContainer)}
hideFooter />
<Route <Route
path='password_reset' path='password_reset'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)}
hideFooter />
<Route <Route
path='settings' path='settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPSettingsContainer)}/> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPSettingsContainer)}
hideFooter />
<Route <Route
path='register_piece' path='register_piece'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPRegisterPiece)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPRegisterPiece)}
headerTitle='+ NEW WORK'/> headerTitle='+ NEW WORK'
hideFooter />
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPPieceList)}
headerTitle='COLLECTION'/> headerTitle='COLLECTION'
<Route path='pieces/:pieceId' component={SluicePieceContainer} /> hideFooter />
<Route path='editions/:editionId' component={EditionContainer} /> <Route
<Route path='verify' component={CoaVerifyContainer} /> path='pieces/:pieceId'
<Route path='*' component={ErrorNotFoundPage} /> component={SluicePieceContainer}
hideFooter />
<Route
path='editions/:editionId'
component={EditionContainer}
hideFooter />
<Route
path='coa_verify'
component={CoaVerifyContainer}
hideFooter />
<Route
path='*'
component={ErrorNotFoundPage}
hideFooter />
</Route> </Route>
), ),
portfolioreview: ( portfolioreview: (
<Route path='/' component={PRApp}> <Route path='/' component={PRApp}>
<IndexRoute component={ProxyHandler(AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }))(PRLanding)} /> <IndexRoute
component={ProxyHandler(AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }))(PRLanding)}
hideFooter />
<Route <Route
path='register_piece' path='register_piece'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PRRegisterPiece)}/> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PRRegisterPiece)}
hideFooter />
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPPieceList)}
headerTitle='SUBMISSIONS'/> headerTitle='SUBMISSIONS'
hideFooter />
<Route <Route
path='login' path='login'
component={ProxyHandler( component={ProxyHandler(
AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }), AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }),
AuthRedirect({to: '/register_piece', when: 'loggedIn'}) AuthRedirect({to: '/register_piece', when: 'loggedIn'})
)(SPLoginContainer)} /> )(SPLoginContainer)}
hideFooter />
<Route <Route
path='logout' path='logout'
component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}
hideFooter />
<Route <Route
path='signup' path='signup'
component={ProxyHandler( component={ProxyHandler(
AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }), AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }),
AuthRedirect({to: '/register_piece', when: 'loggedIn'}) AuthRedirect({to: '/register_piece', when: 'loggedIn'})
)(SPSignupContainer)} /> )(SPSignupContainer)}
hideFooter />
<Route <Route
path='password_reset' path='password_reset'
component={ProxyHandler( component={ProxyHandler(
AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }), AuthPrizeRoleRedirect({ to: '/collection', when: ['is_admin', 'is_judge', 'is_jury'] }),
AuthRedirect({to: '/register_piece', when: 'loggedIn'}) AuthRedirect({to: '/register_piece', when: 'loggedIn'})
)(PasswordResetContainer)} /> )(PasswordResetContainer)}
hideFooter />
<Route <Route
path='settings' path='settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPSettingsContainer)}/> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SPSettingsContainer)}
<Route path='pieces/:pieceId' component={SPPieceContainer} /> hideFooter />
<Route path='editions/:editionId' component={EditionContainer} /> <Route
<Route path='verify' component={CoaVerifyContainer} /> path='pieces/:pieceId'
<Route path='*' component={ErrorNotFoundPage} /> component={SPPieceContainer}
hideFooter />
<Route
path='editions/:editionId'
component={EditionContainer}
hideFooter />
<Route
path='coa_verify'
component={CoaVerifyContainer}
hideFooter />
<Route
path='*'
component={ErrorNotFoundPage}
hideFooter />
</Route> </Route>
) )
}; };

View File

@ -10,8 +10,6 @@ import PieceListStore from '../../../../../../stores/piece_list_store';
import PrizeRatingActions from '../../actions/prize_rating_actions'; import PrizeRatingActions from '../../actions/prize_rating_actions';
import UserStore from '../../../../../../stores/user_store';
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox'; import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece'; import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
@ -23,34 +21,30 @@ import AclProxy from '../../../../../acl_proxy';
import SubmitToPrizeButton from './../ascribe_buttons/submit_to_prize_button'; import SubmitToPrizeButton from './../ascribe_buttons/submit_to_prize_button';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
let AccordionListItemPrize = React.createClass({ let AccordionListItemPrize = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, content: React.PropTypes.object.isRequired,
content: React.PropTypes.object, currentUser: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
]) ]),
className: React.PropTypes.string
}, },
getInitialState() { getInitialState() {
return mergeOptions( return PieceListStore.getState();
PieceListStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -62,29 +56,30 @@ let AccordionListItemPrize = React.createClass({
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
let notification = new GlobalNotificationModel(response.notification, 'success', 10000); const notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
getPrizeButtons() { getPrizeButtons() {
if (this.state.currentUser && this.state.currentUser.is_jury){ const { currentUser, content: { id, ratings } } = this.props;
if ((this.props.content.ratings) &&
(this.props.content.ratings.rating || this.props.content.ratings.average)){ if (currentUser && (currentUser.is_jury || currentUser.is_judge)) {
if (ratings && (ratings.rating || ratings.average)) {
// jury and rating available // jury and rating available
let rating = null, let rating = null;
caption = null; let caption = null;
if (this.props.content.ratings.rating){
rating = parseInt(this.props.content.ratings.rating, 10); if (ratings.rating) {
rating = parseInt(ratings.rating, 10);
caption = getLangText('Your rating'); caption = getLangText('Your rating');
} } else if (ratings.average) {
else if (this.props.content.ratings.average){ rating = ratings.average;
rating = this.props.content.ratings.average; caption = getLangText('Average of ' + ratings.num_ratings + ' rating(s)');
caption = getLangText('Average of ' + this.props.content.ratings.num_ratings + ' rating(s)');
} }
return ( return (
<div id="list-rating" className="pull-right"> <div id="list-rating" className="pull-right">
<Link to={`/pieces/${this.props.content.id}`}> <Link to={`/pieces/${id}`}>
<StarRating <StarRating
ref='rating' ref='rating'
name="prize-rating" name="prize-rating"
@ -94,47 +89,46 @@ let AccordionListItemPrize = React.createClass({
rating={rating} rating={rating}
ratingAmount={5} /> ratingAmount={5} />
</Link> </Link>
</div>); </div>
} );
else { } else {
if (this.state.currentUser.is_judge){ if (currentUser.is_judge) {
return ( return (
<div className="react-rating-caption pull-right"> <div className="react-rating-caption pull-right">
{getLangText('Not rated')} {getLangText('Not rated')}
</div> </div>
); );
} else {
// jury and no rating yet
return (
<div className="react-rating-caption pull-right">
<Link to={`/pieces/${id}`}>
{getLangText('Submit your rating')}
</Link>
</div>
);
} }
// jury and no rating yet
return (
<div className="react-rating-caption pull-right">
<Link to={`/pieces/${this.props.content.id}`}>
{getLangText('Submit your rating')}
</Link>
</div>
);
} }
} else {
return this.getPrizeButtonsParticipant();
} }
return this.getPrizeButtonsParticipant();
}, },
getPrizeButtonsParticipant() { getPrizeButtonsParticipant() {
return ( return (
<div> <AclProxy
<AclProxy aclObject={this.props.content.acl}
aclObject={this.props.content.acl} aclName="acl_wallet_submit">
aclName="acl_wallet_submit"> <SubmitToPrizeButton
<SubmitToPrizeButton className="pull-right"
className="pull-right" piece={this.props.content}
piece={this.props.content} handleSuccess={this.handleSubmitPrizeSuccess} />
handleSuccess={this.handleSubmitPrizeSuccess}/> </AclProxy>
</AclProxy>
</div>
); );
}, },
handleShortlistSuccess(message){ handleShortlistSuccess(message) {
let notification = new GlobalNotificationModel(message, 'success', 2000); const notification = new GlobalNotificationModel(message, 'success', 2000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
@ -144,56 +138,52 @@ let AccordionListItemPrize = React.createClass({
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
}, },
onSelectChange(){ onSelectChange() {
PrizeRatingActions.toggleShortlist(this.props.content.id) PrizeRatingActions.toggleShortlist(this.props.content.id)
.then( .then((res) => {
(res) => { this.refreshPieceData();
this.refreshPieceData(); this.handleShortlistSuccess(res.notification);
return res; });
})
.then(
(res) => {
this.handleShortlistSuccess(res.notification);
}
);
}, },
getPrizeBadge(){ getPrizeBadge() {
if (this.state.currentUser && this.state.currentUser.is_judge) { const { currentUser } = this.props;
if (currentUser && currentUser.is_judge) {
return ( return (
<span className="pull-right ascribe-checkbox-wrapper ascribe-checkbox-badge"> <span className="pull-right ascribe-checkbox-wrapper ascribe-checkbox-badge">
<InputCheckbox <InputCheckbox
defaultChecked={this.props.content.selected} defaultChecked={this.props.content.selected}
onChange={this.onSelectChange}/> onChange={this.onSelectChange} />
</span> </span>
); );
} else {
return null;
} }
return null;
}, },
render() { render() {
const { children, className, content } = this.props; const { children, className, content, currentUser } = this.props;
const { currentUser } = this.state;
// Only show the artist name if you are the participant or if you are a judge and the piece is shortlisted // Only show the artist name if you are the participant or if you are a judge and the piece is shortlisted
let artistName = ((currentUser.is_jury && !currentUser.is_judge) || (currentUser.is_judge && !content.selected )) ? const artistName = ((currentUser.is_jury && !currentUser.is_judge) || (currentUser.is_judge && !content.selected )) ?
<span className="glyphicon glyphicon-eye-close" aria-hidden="true"/> : content.artist_name; <span className="glyphicon glyphicon-eye-close" aria-hidden="true"/> : content.artist_name;
return ( return (
<div> <AccordionListItemPiece
<AccordionListItemPiece className={className}
className={className} piece={content}
piece={content} artistName={artistName}
artistName={artistName} subsubheading={
subsubheading={ <div>
<div> <span>{Moment(content.date_created, 'YYYY-MM-DD').year()}</span>
<span>{Moment(content.date_created, 'YYYY-MM-DD').year()}</span> </div>
</div>} }
buttons={this.getPrizeButtons()} buttons={this.getPrizeButtons()}
badge={this.getPrizeBadge()}> badge={this.getPrizeBadge()}>
{children} {children}
</AccordionListItemPiece> </AccordionListItemPiece>
</div>
); );
} }
}); });

View File

@ -14,13 +14,10 @@ import PrizeStore from '../../stores/prize_store';
import PrizeRatingActions from '../../actions/prize_rating_actions'; import PrizeRatingActions from '../../actions/prize_rating_actions';
import PrizeRatingStore from '../../stores/prize_rating_store'; import PrizeRatingStore from '../../stores/prize_rating_store';
import PieceActions from '../../../../../../actions/piece_actions';
import PieceStore from '../../../../../../stores/piece_store';
import PieceListStore from '../../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../../actions/piece_list_actions';
import PieceActions from '../../../../../../actions/piece_actions';
import UserStore from '../../../../../../stores/user_store'; import PieceStore from '../../../../../../stores/piece_store';
import UserActions from '../../../../../../actions/user_actions';
import Piece from '../../../../../../components/ascribe_detail/piece'; import Piece from '../../../../../../components/ascribe_detail/piece';
import Note from '../../../../../../components/ascribe_detail/note'; import Note from '../../../../../../components/ascribe_detail/note';
@ -53,24 +50,26 @@ import { setDocumentTitle } from '../../../../../../utils/dom_utils';
*/ */
let PrizePieceContainer = React.createClass({ let PrizePieceContainer = React.createClass({
propTypes: { propTypes: {
params: React.PropTypes.object, selectedPrizeActionButton: React.PropTypes.func,
selectedPrizeActionButton: React.PropTypes.func
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object
}, },
mixins: [ReactError], mixins: [ReactError],
getInitialState() { getInitialState() {
return mergeOptions( return PieceStore.getInitialState();
PieceStore.getInitialState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
this.loadPiece(); this.loadPiece();
}, },
@ -94,7 +93,6 @@ let PrizePieceContainer = React.createClass({
componentWillUnmount() { componentWillUnmount() {
PieceStore.unlisten(this.onChange); PieceStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -102,7 +100,8 @@ let PrizePieceContainer = React.createClass({
}, },
getActions() { getActions() {
const { currentUser, piece } = this.state; const { currentUser } = this.props;
const { piece } = this.state;
if (piece.notifications && piece.notifications.length > 0) { if (piece.notifications && piece.notifications.length > 0) {
return ( return (
@ -119,8 +118,8 @@ let PrizePieceContainer = React.createClass({
}, },
render() { render() {
const { selectedPrizeActionButton } = this.props; const { currentUser, selectedPrizeActionButton } = this.props;
const { currentUser, piece } = this.state; const { piece } = this.state;
if (piece.id) { if (piece.id) {
/* /*

View File

@ -1,57 +1,47 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import PrizeActions from '../actions/prize_actions';
import PrizeStore from '../stores/prize_store';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import UserStore from '../../../../../stores/user_store'; import PrizeActions from '../actions/prize_actions';
import UserActions from '../../../../../actions/user_actions'; import PrizeStore from '../stores/prize_store';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
let Landing = React.createClass({
mixins: [History], let Landing = React.createClass({
propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object
},
getInitialState() { getInitialState() {
return mergeOptions( return PrizeStore.getState();
PrizeStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
PrizeStore.listen(this.onChange); PrizeStore.listen(this.onChange);
PrizeActions.fetchPrize(); PrizeActions.fetchPrize();
}, },
componentWillUnmount() { componentWillUnmount() {
UserStore.unlisten(this.onChange);
PrizeStore.unlisten(this.onChange); PrizeStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
this.setState(state); this.setState(state);
// if user is already logged in, redirect him to piece list
if(this.state.currentUser && this.state.currentUser.email) {
// FIXME: hack to redirect out of the dispatch cycle
window.setTimeout(() => this.history.replace('/collection'), 0);
}
}, },
getButtons() { getButtons() {
if (this.state.prize && this.state.prize.active){ if (this.state.prize && this.state.prize.active) {
return ( return (
<ButtonGroup className="enter" bsSize="large" vertical> <ButtonGroup className="enter" bsSize="large" vertical>
<LinkContainer to="/signup"> <LinkContainer to="/signup">
@ -70,39 +60,37 @@ let Landing = React.createClass({
</LinkContainer> </LinkContainer>
</ButtonGroup> </ButtonGroup>
); );
} } else {
return ( return (
<ButtonGroup className="enter" bsSize="large" vertical> <ButtonGroup className="enter" bsSize="large" vertical>
<a className="btn btn-default" href="https://www.ascribe.io/app/signup"> <a className="btn btn-default" href="https://www.ascribe.io/app/signup">
{getLangText('Sign up to ascribe')} {getLangText('Sign up to ascribe')}
</a> </a>
<p> <p>
{getLangText('or, already an ascribe user?')} {getLangText('or, already an ascribe user?')}
</p> </p>
<LinkContainer to="/login"> <LinkContainer to="/login">
<Button> <Button>
{getLangText('Log in')} {getLangText('Log in')}
</Button> </Button>
</LinkContainer> </LinkContainer>
</ButtonGroup> </ButtonGroup>
); );
}
}, },
getTitle() { getTitle() {
if (this.state.prize && this.state.prize.active){ const { prize } = this.state;
return (
<p>
{getLangText('This is the submission page for Sluice_screens ↄc Prize 2015.')}
</p>
);
}
return ( return (
<p> <p>
{getLangText('Submissions for Sluice_screens ↄc Prize 2015 are now closed.')} {getLangText(prize && prize.active ? 'This is the submission page for Sluice_screens ↄc Prize 2015.'
: 'Submissions for Sluice_screens ↄc Prize 2015 are now closed.')}
</p> </p>
); );
}, },
render() { render() {
return ( return (
<div className="container"> <div className="container">

View File

@ -11,6 +11,11 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils';
let LoginContainer = React.createClass({ let LoginContainer = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -21,12 +26,11 @@ let LoginContainer = React.createClass({
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<LoginForm <LoginForm
headerMessage={getLangText('Log in with ascribe')} headerMessage={getLangText('Log in with ascribe')}
location={this.props.location}/> location={this.props.location} />
<div <div className="ascribe-login-text">
className="ascribe-login-text">
{getLangText('I\'m not a user') + ' '} {getLangText('I\'m not a user') + ' '}
<Link to="/signup">{getLangText('Sign up...')}</Link> <Link to="/signup">{getLangText('Sign up...')}</Link>
<br/> <br />
{getLangText('I forgot my password') + ' '} {getLangText('I forgot my password') + ' '}
<Link to="/password_reset">{getLangText('Rescue me...')}</Link> <Link to="/password_reset">{getLangText('Rescue me...')}</Link>

View File

@ -1,19 +1,15 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import PieceList from '../../../../piece_list';
import UserActions from '../../../../../actions/user_actions'; import Button from 'react-bootstrap/lib/Button';
import UserStore from '../../../../../stores/user_store'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import PrizeActions from '../actions/prize_actions'; import PrizeActions from '../actions/prize_actions';
import PrizeStore from '../stores/prize_store'; import PrizeStore from '../stores/prize_store';
import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import AccordionListItemPrize from './ascribe_accordion_list/accordion_list_item_prize'; import AccordionListItemPrize from './ascribe_accordion_list/accordion_list_item_prize';
import PieceList from '../../../../piece_list';
import { mergeOptions } from '../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
@ -21,25 +17,24 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils';
let PrizePieceList = React.createClass({ let PrizePieceList = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
return mergeOptions( return PrizeStore.getState();
PrizeStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
PrizeStore.listen(this.onChange); PrizeStore.listen(this.onChange);
PrizeActions.fetchPrize(); PrizeActions.fetchPrize();
}, },
componentWillUnmount() { componentWillUnmount() {
UserStore.unlisten(this.onChange);
PrizeStore.unlisten(this.onChange); PrizeStore.unlisten(this.onChange);
}, },
@ -48,7 +43,8 @@ let PrizePieceList = React.createClass({
}, },
getButtonSubmit() { getButtonSubmit() {
const { currentUser, prize } = this.state; const { currentUser } = this.props;
const { prize } = this.state;
if (prize && prize.active && !currentUser.is_jury && !currentUser.is_admin && !currentUser.is_judge) { if (prize && prize.active && !currentUser.is_jury && !currentUser.is_admin && !currentUser.is_judge) {
return ( return (
<LinkContainer to="/register_piece"> <LinkContainer to="/register_piece">
@ -57,34 +53,40 @@ let PrizePieceList = React.createClass({
</Button> </Button>
</LinkContainer> </LinkContainer>
); );
} else {
return null;
} }
return null; },
shouldRedirect(pieceCount) {
const { currentUser } = this.props;
return !currentUser.is_admin && !currentUser.is_jury && !currentUser.is_judge && !pieceCount;
}, },
render() { render() {
const { is_judge: isJudge, is_jury: isJury, is_admin: isAdmin } = this.state.currentUser; const { currentUser, location } = this.props;
setDocumentTitle(getLangText('Collection')); setDocumentTitle(getLangText('Collection'));
let orderParams = ['artist_name', 'title']; let orderParams = ['artist_name', 'title'];
if (isJury) { if (currentUser.is_jury) {
orderParams = ['rating', 'title']; orderParams = ['rating', 'title'];
} }
if (isJudge) { if (currentUser.is_judge) {
orderParams = ['rating', 'title', 'selected']; orderParams = ['rating', 'title', 'selected'];
} }
return ( return (
<div> <PieceList
<PieceList ref="list"
ref="list" {...this.props}
accordionListItemType={AccordionListItemPrize} accordionListItemType={AccordionListItemPrize}
orderParams={orderParams} customSubmitButton={this.getButtonSubmit()}
orderBy={this.state.currentUser.is_jury ? 'rating' : null} filterParams={[]}
filterParams={[]} orderParams={orderParams}
customSubmitButton={this.getButtonSubmit()} orderBy={currentUser.is_jury ? 'rating' : null}
location={this.props.location} shouldRedirect={this.shouldRedirect} />
shouldRedirect={() => !(isJury || isJudge || isAdmin)} />
</div>
); );
} }
}); });

View File

@ -16,6 +16,11 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils';
let PrizeRegisterPiece = React.createClass({ let PrizeRegisterPiece = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -37,63 +42,59 @@ let PrizeRegisterPiece = React.createClass({
}, },
render() { render() {
const { location } = this.props; const { prize } = this.state;
setDocumentTitle(getLangText('Submit to the prize')); setDocumentTitle(getLangText('Submit to the prize'));
if(this.state.prize && this.state.prize.active){ if (prize && prize.active) {
return ( return (
<div> <RegisterPiece
<RegisterPiece {...this.props}
enableLocalHashing={false} enableLocalHashing={false}
headerMessage={''} headerMessage={''}
submitMessage={getLangText('Submit')} submitMessage={getLangText('Submit')}>
location={location}> <Property
<Property name='artist_statement'
name='artist_statement' label={getLangText('Artist statement')}
label={getLangText('Artist statement')} editable={true}
editable={true} overrideForm={true}>
overrideForm={true}> <InputTextAreaToggable
<InputTextAreaToggable rows={1}
rows={1} placeholder={getLangText('Enter your statement')}
placeholder={getLangText('Enter your statement')} required />
required /> </Property>
</Property> <Property
<Property name='work_description'
name='work_description' label={getLangText('Work description')}
label={getLangText('Work description')} editable={true}
editable={true} overrideForm={true}>
overrideForm={true}> <InputTextAreaToggable
<InputTextAreaToggable rows={1}
rows={1} placeholder={getLangText('Enter the description for your work')}
placeholder={getLangText('Enter the description for your work')} required />
required /> </Property>
</Property> <Property
<Property name="terms"
name="terms" className="ascribe-property-collapsible-toggle">
className="ascribe-property-collapsible-toggle" <InputCheckbox>
style={{paddingBottom: 0}}> <span>
<InputCheckbox> {' ' + getLangText('I agree to the Terms of Service the art price') + ' '}
<span> (<a href="https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/sluice/terms.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
{' ' + getLangText('I agree to the Terms of Service the art price') + ' '} {getLangText('read')}
(<a href="https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/sluice/terms.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}> </a>)
{getLangText('read')} </span>
</a>) </InputCheckbox>
</span> </Property>
</InputCheckbox> </RegisterPiece>
</Property>
</RegisterPiece>
</div>
); );
} } else {
else {
return ( return (
<div className='row'> <div className='row'>
<div style={{textAlign: 'center'}}> <div style={{textAlign: 'center'}}>
{getLangText('The prize is no longer active')} {getLangText('The prize is no longer active')}
</div> </div>
</div> </div>
); );
} }
} }
}); });

View File

@ -2,8 +2,6 @@
import React from 'react'; import React from 'react';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import PrizeActions from '../actions/prize_actions'; import PrizeActions from '../actions/prize_actions';
import PrizeStore from '../stores/prize_store'; import PrizeStore from '../stores/prize_store';
import PrizeJuryActions from '../actions/prize_jury_actions'; import PrizeJuryActions from '../actions/prize_jury_actions';
@ -28,40 +26,27 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils';
let Settings = React.createClass({ let Settings = React.createClass({
getInitialState() { propTypes: {
return UserStore.getState(); // Provided from PrizeApp
}, currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
componentDidMount() { // Provided from router
UserStore.listen(this.onChange); location: React.PropTypes.object
UserActions.fetchCurrentUser();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
}, },
render() { render() {
setDocumentTitle(getLangText('Account settings')); setDocumentTitle(getLangText('Account settings'));
let prizeSettings = null;
if (this.state.currentUser.is_admin){
prizeSettings = <PrizeSettings />;
}
return ( return (
<SettingsContainer> <SettingsContainer {...this.props}>
{prizeSettings} {this.props.currentUser.is_admin ? <PrizeSettings /> : null}
</SettingsContainer> </SettingsContainer>
); );
} }
}); });
let PrizeSettings = React.createClass({ let PrizeSettings = React.createClass({
getInitialState() { getInitialState() {
return PrizeStore.getState(); return PrizeStore.getState();
}, },

View File

@ -8,6 +8,11 @@ import { setDocumentTitle } from '../../../../../utils/dom_utils';
let SignupContainer = React.createClass({ let SignupContainer = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -18,7 +23,7 @@ let SignupContainer = React.createClass({
}; };
}, },
handleSuccess(message){ handleSuccess(message) {
this.setState({ this.setState({
submitted: true, submitted: true,
message: message message: message
@ -26,13 +31,15 @@ let SignupContainer = React.createClass({
}, },
render() { render() {
const { location } = this.props;
const { message, submitted } = this.state;
setDocumentTitle(getLangText('Sign up')); setDocumentTitle(getLangText('Sign up'));
if (this.state.submitted){ if (submitted) {
return ( return (
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<div className="ascribe-login-text ascribe-login-header"> <div className="ascribe-login-text ascribe-login-header">
{this.state.message} {message}
</div> </div>
</div> </div>
); );
@ -43,7 +50,7 @@ let SignupContainer = React.createClass({
headerMessage={getLangText('Create account for submission')} headerMessage={getLangText('Create account for submission')}
submitMessage={getLangText('Sign up')} submitMessage={getLangText('Sign up')}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
location={this.props.location}/> location={location} />
</div> </div>
); );
} }

View File

@ -1,50 +1,61 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import classNames from 'classnames';
import Hero from './components/prize_hero'; import Hero from './components/prize_hero';
import Header from '../../../header';
import AppBase from '../../../app_base';
import AppRouteWrapper from '../../../app_route_wrapper';
import Footer from '../../../footer'; import Footer from '../../../footer';
import GlobalNotification from '../../../global_notification'; import Header from '../../../header';
import { getSubdomain } from '../../../../utils/general_utils'; import { getSubdomain } from '../../../../utils/general_utils';
let PrizeApp = React.createClass({ let PrizeApp = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.oneOfType([ activeRoute: React.PropTypes.object.isRequired,
React.PropTypes.arrayOf(React.PropTypes.element), children: React.PropTypes.element.isRequired,
React.PropTypes.element history: React.PropTypes.object.isRequired,
]), routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
history: React.PropTypes.object,
routes: React.PropTypes.arrayOf(React.PropTypes.object) // Provided from AppBase
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object
}, },
render() { render() {
const { history, routes } = this.props; const { activeRoute, children, currentUser, history, routes, whitelabel } = this.props;
const subdomain = getSubdomain();
const path = activeRoute && activeRoute.path;
let header = null; let header = null;
let subdomain = getSubdomain();
// The second element of routes is always the active component object, where we can
// extract the path.
let path = routes[1] ? routes[1].path : null;
// if the path of the current activeRoute is not defined, then this is the IndexRoute // if the path of the current activeRoute is not defined, then this is the IndexRoute
if (!path || history.isActive('/login') || history.isActive('/signup')) { if (!path || history.isActive('/login') || history.isActive('/signup')) {
header = <Hero />; header = (<Hero />);
} else { } else {
header = <Header routes={routes}/>; header = (
<Header
currentUser={currentUser}
routes={routes}
whitelabel={whitelabel} />
);
} }
return ( return (
<div className={'container ascribe-prize-app client--' + subdomain}> <div className={classNames('ascribe-app', 'ascribe-prize-app', `route--${(path ? path.split('/')[0] : 'landing')}`)}>
{header} {header}
{this.props.children} <AppRouteWrapper
<GlobalNotification /> currentUser={currentUser}
<div id="modal" className="container"></div> whitelabel={whitelabel}>
<Footer /> {/* Routes are injected here */}
{children}
</AppRouteWrapper>
<Footer activeRoute={activeRoute} />
</div> </div>
); );
} }
}); });
export default PrizeApp; export default AppBase(PrizeApp);

View File

@ -16,8 +16,9 @@ import { getLangText } from '../../../../../../utils/lang_utils';
const SluiceSelectedPrizeActionButton = React.createClass({ const SluiceSelectedPrizeActionButton = React.createClass({
propTypes: { propTypes: {
piece: React.PropTypes.object, currentUser: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object, piece: React.PropTypes.object.isRequired,
startLoanDate: React.PropTypes.object, startLoanDate: React.PropTypes.object,
endLoanDate: React.PropTypes.object, endLoanDate: React.PropTypes.object,
className: React.PropTypes.string, className: React.PropTypes.string,
@ -38,7 +39,7 @@ const SluiceSelectedPrizeActionButton = React.createClass({
// Can't use default props since those are only created once // Can't use default props since those are only created once
const startLoanDate = this.props.startLoanDate || new Moment(); const startLoanDate = this.props.startLoanDate || new Moment();
const endLoanDate = this.props.endLoanDate || (new Moment()).add(6, 'months'); const endLoanDate = this.props.endLoanDate || new Moment().add(6, 'months');
return ( return (
<ModalWrapper <ModalWrapper

View File

@ -8,6 +8,12 @@ import PrizePieceContainer from '../../../simple_prize/components/ascribe_detail
const SluicePieceContainer = React.createClass({ const SluicePieceContainer = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },

View File

@ -5,42 +5,36 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
let Vivi23Landing = React.createClass({ let Vivi23Landing = React.createClass({
getInitialState() { propTypes: {
return WhitelabelStore.getState(); customThumbnailPlaceholder: React.PropTypes.func,
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
}, },
componentWillMount() { componentWillMount() {
setDocumentTitle('23VIVI Marketplace'); setDocumentTitle('23VIVI Marketplace');
}, },
componentDidMount() {
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() { render() {
return ( return (
<div className="container ascribe-form-wrapper vivi23-landing"> <div className="ascribe-form-wrapper vivi23-landing">
<div className="row"> <div className="row">
<div className="col-xs-12"> <div className="col-xs-12">
<div className="row vivi23-landing--header"> <div className="row vivi23-landing--header">
<img className="vivi23-landing--header-logo" src={this.state.whitelabel.logo} /> <img
className="vivi23-landing--header-logo"
src={this.props.whitelabel.logo}
height="75" />
<div> <div>
{getLangText('23VIVI Marketplace is powered by') + ' '} {getLangText('23VIVI Marketplace is powered by') + ' '}
<span className="icon-ascribe-logo" /> <span className="icon-ascribe-logo" />

View File

@ -8,17 +8,21 @@ import MarketPieceList from '../market/market_piece_list';
let Vivi23PieceList = React.createClass({ let Vivi23PieceList = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
render() { render() {
return ( return (
<MarketPieceList <MarketPieceList
customThumbnailPlaceholder={Vivi23AccordionListItemThumbnailPlaceholder} {...this.props}
location={this.props.location} /> customThumbnailPlaceholder={Vivi23AccordionListItemThumbnailPlaceholder} />
); );
} }
}); });
export default Vivi23PieceList; export default Vivi23PieceList;

View File

@ -5,42 +5,28 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
let ArtcityLanding = React.createClass({ let ArtcityLanding = React.createClass({
getInitialState() { propTypes: {
return WhitelabelStore.getState(); // Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired
}, },
componentWillMount() { componentWillMount() {
setDocumentTitle('Artcity Marketplace'); setDocumentTitle('Artcity Marketplace');
}, },
componentDidMount() {
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() { render() {
return ( return (
<div className="container ascribe-form-wrapper artcity-landing"> <div className="container ascribe-form-wrapper artcity-landing">
<div className="row"> <div className="row">
<div className="col-xs-12"> <div className="col-xs-12">
<div className="row artcity-landing--header"> <div className="row artcity-landing--header">
<img className="artcity-landing--header-logo" src={this.state.whitelabel.logo} /> <img className="artcity-landing--header-logo" src={this.props.whitelabel.logo} />
<div> <div>
{getLangText('Artcity Marketplace is powered by') + ' '} {getLangText('Artcity Marketplace is powered by') + ' '}
<span className="icon-ascribe-logo" /> <span className="icon-ascribe-logo" />

View File

@ -15,54 +15,54 @@ let WalletActionPanel = React.createClass({
propTypes: { propTypes: {
piece: React.PropTypes.object.isRequired, piece: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired, currentUser: React.PropTypes.object.isRequired,
loadPiece: React.PropTypes.func.isRequired,
handleDeleteSuccess: React.PropTypes.func.isRequired, handleDeleteSuccess: React.PropTypes.func.isRequired,
loadPiece: React.PropTypes.func.isRequired,
submitButtonType: React.PropTypes.func.isRequired submitButtonType: React.PropTypes.func.isRequired
}, },
render(){ render() {
if (this.props.piece && const { currentUser, handleDeleteSuccess, loadPiece, piece, submitButtonType } = this.props;
this.props.piece.notifications &&
this.props.piece.notifications.length > 0) { if (piece && piece.notifications && piece.notifications.length) {
return ( return (
<ListRequestActions <ListRequestActions
pieceOrEditions={this.props.piece} pieceOrEditions={piece}
currentUser={this.props.currentUser} currentUser={currentUser}
handleSuccess={this.props.loadPiece} handleSuccess={loadPiece}
notifications={this.props.piece.notifications}/>); notifications={piece.notifications}/>);
} } else {
else {
//We need to disable the normal acl_loan because we're inserting a custom acl_loan button //We need to disable the normal acl_loan because we're inserting a custom acl_loan button
let availableAcls; let availableAcls;
if (this.props.piece && this.props.piece.acl && typeof this.props.piece.acl.acl_loan !== 'undefined') { if (piece && piece.acl && typeof piece.acl.acl_loan !== 'undefined') {
// make a copy to not have side effects // make a copy to not have side effects
availableAcls = mergeOptions({}, this.props.piece.acl); availableAcls = mergeOptions({}, piece.acl);
availableAcls.acl_loan = false; availableAcls.acl_loan = false;
} }
let SubmitButtonType = this.props.submitButtonType; let SubmitButtonType = submitButtonType;
return ( return (
<AclButtonList <AclButtonList
className="text-center ascribe-button-list"
availableAcls={availableAcls} availableAcls={availableAcls}
pieceOrEditions={this.props.piece} className="text-center ascribe-button-list"
handleSuccess={this.props.loadPiece}> currentUser={currentUser}
pieceOrEditions={piece}
handleSuccess={loadPiece}>
<AclProxy <AclProxy
aclObject={this.props.currentUser.acl} aclObject={currentUser.acl}
aclName="acl_wallet_submit"> aclName="acl_wallet_submit">
<AclProxy <AclProxy
aclObject={availableAcls} aclObject={availableAcls}
aclName="acl_wallet_submit"> aclName="acl_wallet_submit">
<SubmitButtonType <SubmitButtonType
className="btn-sm" className="btn-sm"
piece={this.props.piece}/> piece={piece}/>
</AclProxy> </AclProxy>
</AclProxy> </AclProxy>
<DeleteButton <DeleteButton
handleSuccess={this.props.handleDeleteSuccess} handleSuccess={handleDeleteSuccess}
piece={this.props.piece}/> piece={piece}/>
</AclButtonList> </AclButtonList>
); );
} }

View File

@ -3,18 +3,18 @@
import React from 'react'; import React from 'react';
import Moment from 'moment'; import Moment from 'moment';
import Piece from '../../../../../components/ascribe_detail/piece';
import WalletActionPanel from './wallet_action_panel'; import WalletActionPanel from './wallet_action_panel';
import CollapsibleParagraph from '../../../../../components/ascribe_collapsible/collapsible_paragraph'; import CollapsibleParagraph from '../../../../../components/ascribe_collapsible/collapsible_paragraph';
import DetailProperty from '../../../../ascribe_detail/detail_property';
import HistoryIterator from '../../../../ascribe_detail/history_iterator'; import HistoryIterator from '../../../../ascribe_detail/history_iterator';
import Note from '../../../../ascribe_detail/note'; import Note from '../../../../ascribe_detail/note';
import Piece from '../../../../../components/ascribe_detail/piece';
import DetailProperty from '../../../../ascribe_detail/detail_property'; import AscribeSpinner from '../../../../ascribe_spinner';
import ApiUrls from '../../../../../constants/api_urls'; import ApiUrls from '../../../../../constants/api_urls';
import AscribeSpinner from '../../../../ascribe_spinner';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
@ -23,9 +23,10 @@ let WalletPieceContainer = React.createClass({
propTypes: { propTypes: {
piece: React.PropTypes.object.isRequired, piece: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired, currentUser: React.PropTypes.object.isRequired,
loadPiece: React.PropTypes.func.isRequired,
handleDeleteSuccess: React.PropTypes.func.isRequired, handleDeleteSuccess: React.PropTypes.func.isRequired,
loadPiece: React.PropTypes.func.isRequired,
submitButtonType: React.PropTypes.func.isRequired, submitButtonType: React.PropTypes.func.isRequired,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.array React.PropTypes.array

View File

@ -13,6 +13,11 @@ import { mergeOptions } from '../../../../../utils/general_utils';
let CCRegisterPiece = React.createClass({ let CCRegisterPiece = React.createClass({
propTypes: { propTypes: {
// Provided from AscribeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -85,6 +90,7 @@ let CCRegisterPiece = React.createClass({
setDocumentTitle(getLangText('Register a new piece')); setDocumentTitle(getLangText('Register a new piece'));
return ( return (
<RegisterPiece <RegisterPiece
{...this.props}
enableLocalHashing={false} enableLocalHashing={false}
headerMessage={getLangText('Register under a Creative Commons license')} headerMessage={getLangText('Register under a Creative Commons license')}
submitMessage={getLangText('Submit')} submitMessage={getLangText('Submit')}

View File

@ -3,27 +3,26 @@
import React from 'react'; import React from 'react';
import Moment from 'moment'; import Moment from 'moment';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece'; import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import PieceListActions from '../../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../../actions/piece_list_actions';
import PieceListStore from '../../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../../stores/piece_list_store';
import UserStore from '../../../../../../stores/user_store';
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import CylandSubmitButton from '../cyland_buttons/cyland_submit_button'; import CylandSubmitButton from '../cyland_buttons/cyland_submit_button';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
import AclProxy from '../../../../../acl_proxy'; import AclProxy from '../../../../../acl_proxy';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
let CylandAccordionListItem = React.createClass({ let CylandAccordionListItem = React.createClass({
propTypes: { propTypes: {
content: React.PropTypes.object.isRequired,
className: React.PropTypes.string, className: React.PropTypes.string,
content: React.PropTypes.object,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
@ -31,20 +30,15 @@ let CylandAccordionListItem = React.createClass({
}, },
getInitialState() { getInitialState() {
return mergeOptions( return PieceListStore.getState();
PieceListStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -56,37 +50,39 @@ let CylandAccordionListItem = React.createClass({
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
let notification = new GlobalNotificationModel(response.notification, 'success', 10000); const notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
getSubmitButtons() { getSubmitButtons() {
const { content } = this.props;
return ( return (
<div> <div>
<AclProxy <AclProxy
aclObject={this.props.content.acl} aclObject={content.acl}
aclName="acl_wallet_submit"> aclName="acl_wallet_submit">
<CylandSubmitButton <CylandSubmitButton
className="pull-right" className="pull-right"
piece={this.props.content} piece={content}
handleSuccess={this.handleSubmitSuccess}/> handleSuccess={this.handleSubmitSuccess}/>
</AclProxy> </AclProxy>
<AclProxy <AclProxy
aclObject={this.props.content.acl} aclObject={content.acl}
aclName="acl_wallet_submitted"> aclName="acl_wallet_submitted">
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Submitted to Cyland') + ' '} {getLangText('Submitted to Cyland') + ' '}
<span className='ascribe-icon icon-ascribe-ok'/> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
</AclProxy> </AclProxy>
<AclProxy <AclProxy
aclObject={this.props.content.acl} aclObject={content.acl}
aclName="acl_wallet_accepted"> aclName="acl_wallet_accepted">
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Loaned to Cyland') + ' '} {getLangText('Loaned to Cyland') + ' '}
<span className='ascribe-icon icon-ascribe-ok'/> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
@ -96,16 +92,19 @@ let CylandAccordionListItem = React.createClass({
}, },
render() { render() {
const { children, className, content } = this.props;
return ( return (
<AccordionListItemPiece <AccordionListItemPiece
className={this.props.className} className={className}
piece={this.props.content} piece={content}
subsubheading={ subsubheading={
<div className="pull-left"> <div className="pull-left">
<span>{Moment(this.props.content.date_created, 'YYYY-MM-DD').year()}</span> <span>{Moment(content.date_created, 'YYYY-MM-DD').year()}</span>
</div>} </div>
}
buttons={this.getSubmitButtons()}> buttons={this.getSubmitButtons()}>
{this.props.children} {children}
</AccordionListItemPiece> </AccordionListItemPiece>
); );
} }

View File

@ -7,39 +7,19 @@ import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../../stores/whitelabel_store';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
let CylandSubmitButton = React.createClass({ let CylandSubmitButton = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string,
handleSuccess: React.PropTypes.func,
piece: React.PropTypes.object.isRequired, piece: React.PropTypes.object.isRequired,
username: React.PropTypes.string
},
getInitialState() { className: React.PropTypes.string,
return WhitelabelStore.getState(); handleSuccess: React.PropTypes.func
},
componentDidMount() {
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
}, },
render() { render() {
const { piece, className } = this.props; const { className, piece } = this.props;
return ( return (
<LinkContainer <LinkContainer
@ -58,4 +38,4 @@ let CylandSubmitButton = React.createClass({
} }
}); });
export default CylandSubmitButton; export default CylandSubmitButton;

View File

@ -3,28 +3,26 @@
import React from 'react'; import React from 'react';
import { History } from 'react-router'; import { History } from 'react-router';
import EditionListActions from '../../../../../../actions/edition_list_actions';
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import PieceActions from '../../../../../../actions/piece_actions'; import PieceActions from '../../../../../../actions/piece_actions';
import PieceStore from '../../../../../../stores/piece_store'; import PieceStore from '../../../../../../stores/piece_store';
import UserStore from '../../../../../../stores/user_store';
import PieceListStore from '../../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../../actions/piece_list_actions';
import EditionListActions from '../../../../../../actions/edition_list_actions'; import CylandAdditionalDataForm from '../cyland_forms/cyland_additional_data_form';
import CylandSubmitButton from '../cyland_buttons/cyland_submit_button'; import CylandSubmitButton from '../cyland_buttons/cyland_submit_button';
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
import CylandAdditionalDataForm from '../cyland_forms/cyland_additional_data_form';
import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container'; import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container';
import AscribeSpinner from '../../../../../ascribe_spinner'; import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
import GlobalNotificationModel from '../../../../../../models/global_notification_model'; import AscribeSpinner from '../../../../../ascribe_spinner';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../../utils/dom_utils';
@ -33,6 +31,12 @@ import { mergeOptions } from '../../../../../../utils/general_utils';
let CylandPieceContainer = React.createClass({ let CylandPieceContainer = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
@ -41,14 +45,12 @@ let CylandPieceContainer = React.createClass({
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
PieceStore.getInitialState(), PieceStore.getInitialState(),
UserStore.getState(),
PieceListStore.getState() PieceListStore.getState()
); );
}, },
componentDidMount() { componentDidMount() {
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
UserStore.listen(this.onChange);
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
this.loadPiece(); this.loadPiece();
@ -64,7 +66,6 @@ let CylandPieceContainer = React.createClass({
componentWillUnmount() { componentWillUnmount() {
PieceStore.unlisten(this.onChange); PieceStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
}, },
@ -96,12 +97,15 @@ let CylandPieceContainer = React.createClass({
const { piece } = this.state; const { piece } = this.state;
if (piece.id) { if (piece.id) {
const { currentUser } = this.props;
setDocumentTitle(`${piece.artist_name}, ${piece.title}`); setDocumentTitle(`${piece.artist_name}, ${piece.title}`);
return ( return (
<WalletPieceContainer <WalletPieceContainer
piece={piece} {...this.props}
currentUser={this.state.currentUser} piece={this.state.piece}
currentUser={currentUser}
loadPiece={this.loadPiece} loadPiece={this.loadPiece}
handleDeleteSuccess={this.handleDeleteSuccess} handleDeleteSuccess={this.handleDeleteSuccess}
submitButtonType={CylandSubmitButton}> submitButtonType={CylandSubmitButton}>

View File

@ -1,20 +0,0 @@
'use strict';
import React from 'react';
import AppConstants from '../../../../constants/application_constants';
let Hero = React.createClass({
render() {
return (
<div className="hero">
<img
className="logo" src={AppConstants.whitelabel.logo}
alt="Cyland Video Archive"
height="200px"/>
</div>
);
}
});
export default Hero;

View File

@ -1,67 +1,36 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { History } from 'react-router';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import AscribeSpinner from '../../../../ascribe_spinner'; import AscribeSpinner from '../../../../ascribe_spinner';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
let CylandLanding = React.createClass({ let CylandLanding = React.createClass({
propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
mixins: [History], // Provided from router
location: React.PropTypes.object
getInitialState() {
return mergeOptions(
UserStore.getState(),
WhitelabelStore.getState()
);
},
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
// if user is already logged in, redirect him to piece list
if(this.state.currentUser && this.state.currentUser.email) {
// FIXME: hack to redirect out of the dispatch cycle
window.setTimeout(() => this.history.replace('/collection'), 0);
}
}, },
render() { render() {
setDocumentTitle('CYLAND MediaArtLab'); setDocumentTitle('CYLAND MediaArtLab');
return ( return (
<div className="container ascribe-form-wrapper cyland-landing"> <div className="ascribe-form-wrapper cyland-landing">
<div className="row"> <div className="row">
<div className="col-xs-12"> <div className="col-xs-12">
<div className="row" style={{border: '1px solid #CCC', padding: '2em'}}> <div className="row" style={{border: '1px solid #CCC', padding: '2em'}}>
<img src={this.state.whitelabel.logo} width="400px"/> <img src={this.props.whitelabel.logo} height="115" />
<div style={{marginTop: '1em'}}> <div style={{marginTop: '1em'}}>
{getLangText('Submissions to Cyland Archive are powered by') + ' '} {getLangText('Submissions to Cyland Archive are powered by') + ' '}
<span> <span>

View File

@ -3,52 +3,26 @@
import React from 'react'; import React from 'react';
import PieceList from '../../../../piece_list'; import PieceList from '../../../../piece_list';
import UserActions from '../../../../../actions/user_actions';
import UserStore from '../../../../../stores/user_store';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import CylandAccordionListItem from './cyland_accordion_list/cyland_accordion_list_item'; import CylandAccordionListItem from './cyland_accordion_list/cyland_accordion_list_item';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
let CylandPieceList = React.createClass({ let CylandPieceList = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
getInitialState() {
return mergeOptions(
UserStore.getState(),
WhitelabelStore.getState()
);
},
componentDidMount() {
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
UserActions.fetchCurrentUser();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
shouldRedirect(pieceCount) { shouldRedirect(pieceCount) {
const { currentUser: { email: userEmail }, const { currentUser: { email: userEmail },
whitelabel: { whitelabel: {
user: whitelabelAdminEmail user: whitelabelAdminEmail
} } = this.state; } } = this.props;
return userEmail !== whitelabelAdminEmail && !pieceCount; return userEmail !== whitelabelAdminEmail && !pieceCount;
}, },
@ -57,25 +31,23 @@ let CylandPieceList = React.createClass({
setDocumentTitle(getLangText('Collection')); setDocumentTitle(getLangText('Collection'));
return ( return (
<div> <PieceList
<PieceList {...this.props}
redirectTo={{ accordionListItemType={CylandAccordionListItem}
pathname: '/register_piece', filterParams={[{
query: { label: getLangText('Show works I have'),
'slide_num': 0 items: [{
} key: 'acl_loaned',
}} label: getLangText('loaned to Cyland')
shouldRedirect={this.shouldRedirect} }]
accordionListItemType={CylandAccordionListItem} }]}
filterParams={[{ redirectTo={{
label: getLangText('Show works I have'), pathname: '/register_piece',
items: [{ query: {
key: 'acl_loaned', 'slide_num': 0
label: getLangText('loaned to Cyland') }
}] }}
}]} shouldRedirect={this.shouldRedirect} />
location={this.props.location}/>
</div>
); );
} }
}); });

View File

@ -10,17 +10,9 @@ import Row from 'react-bootstrap/lib/Row';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import PieceListStore from '../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../actions/piece_list_actions';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import PieceStore from '../../../../../stores/piece_store'; import PieceStore from '../../../../../stores/piece_store';
import PieceActions from '../../../../../actions/piece_actions'; import PieceActions from '../../../../../actions/piece_actions';
@ -30,6 +22,7 @@ import GlobalNotificationActions from '../../../../../actions/global_notificatio
import CylandAdditionalDataForm from './cyland_forms/cyland_additional_data_form'; import CylandAdditionalDataForm from './cyland_forms/cyland_additional_data_form';
import LoanForm from '../../../../ascribe_forms/form_loan'; import LoanForm from '../../../../ascribe_forms/form_loan';
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import SlidesContainer from '../../../../ascribe_slides_container/slides_container'; import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
@ -43,6 +36,11 @@ import { getAclFormMessage } from '../../../../../utils/form_utils';
let CylandRegisterPiece = React.createClass({ let CylandRegisterPiece = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -50,10 +48,8 @@ let CylandRegisterPiece = React.createClass({
getInitialState(){ getInitialState(){
return mergeOptions( return mergeOptions(
UserStore.getState(),
PieceListStore.getState(), PieceListStore.getState(),
PieceStore.getInitialState(), PieceStore.getInitialState(),
WhitelabelStore.getState(),
{ {
step: 0 step: 0
}); });
@ -61,11 +57,7 @@ let CylandRegisterPiece = React.createClass({
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
UserActions.fetchCurrentUser();
WhitelabelActions.fetchWhitelabel();
const queryParams = this.props.location.query; const queryParams = this.props.location.query;
@ -83,9 +75,7 @@ let CylandRegisterPiece = React.createClass({
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceStore.unlisten(this.onChange); PieceStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -139,8 +129,8 @@ let CylandRegisterPiece = React.createClass({
}, },
render() { render() {
const { location } = this.props; const { currentUser, location, whitelabel } = this.props;
const { currentUser, piece, step, whitelabel } = this.state; const { piece, step } = this.state;
const today = new Moment(); const today = new Moment();
const datetimeWhenWeAllWillBeFlyingCoolHoverboardsAndDinosaursWillLiveAgain = new Moment().add(1000, 'years'); const datetimeWhenWeAllWillBeFlyingCoolHoverboardsAndDinosaursWillLiveAgain = new Moment().add(1000, 'years');
@ -180,13 +170,13 @@ let CylandRegisterPiece = React.createClass({
<Row className="no-margin"> <Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}> <Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<RegisterPieceForm <RegisterPieceForm
{...this.props}
disabled={step > 0} disabled={step > 0}
enableLocalHashing={false} enableLocalHashing={false}
headerMessage={getLangText('Submit to Cyland Archive')}
submitMessage={getLangText('Submit')}
isFineUploaderActive={true}
handleSuccess={this.handleRegisterSuccess} handleSuccess={this.handleRegisterSuccess}
location={location} /> headerMessage={getLangText('Submit to Cyland Archive')}
isFineUploaderActive={true}
submitMessage={getLangText('Submit')} />
</Col> </Col>
</Row> </Row>
</div> </div>

View File

@ -3,18 +3,16 @@
import React from 'react'; import React from 'react';
import Moment from 'moment'; import Moment from 'moment';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece'; import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import PieceListActions from '../../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../../actions/piece_list_actions';
import PieceListStore from '../../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../../stores/piece_list_store';
import UserStore from '../../../../../../stores/user_store';
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import IkonotvSubmitButton from '../ikonotv_buttons/ikonotv_submit_button'; import IkonotvSubmitButton from '../ikonotv_buttons/ikonotv_submit_button';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
import AclProxy from '../../../../../acl_proxy'; import AclProxy from '../../../../../acl_proxy';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
@ -23,29 +21,26 @@ import { mergeOptions } from '../../../../../../utils/general_utils';
let IkonotvAccordionListItem = React.createClass({ let IkonotvAccordionListItem = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, content: React.PropTypes.object.isRequired,
content: React.PropTypes.object, currentUser: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
]) ]),
className: React.PropTypes.string
}, },
getInitialState() { getInitialState() {
return mergeOptions( return PieceListStore.getState();
PieceListStore.getState(),
UserStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
}, },
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -57,41 +52,43 @@ let IkonotvAccordionListItem = React.createClass({
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
let notification = new GlobalNotificationModel(response.notification, 'success', 10000); const notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
getSubmitButtons() { getSubmitButtons() {
const { content, currentUser } = this.props;
return ( return (
<div> <div>
<AclProxy <AclProxy
aclObject={this.state.currentUser.acl} aclObject={currentUser.acl}
aclName="acl_wallet_submit"> aclName="acl_wallet_submit">
<AclProxy <AclProxy
aclObject={this.props.content.acl} aclObject={content.acl}
aclName="acl_wallet_submit"> aclName="acl_wallet_submit">
<IkonotvSubmitButton <IkonotvSubmitButton
className="btn-xs pull-right" className="btn-xs pull-right"
handleSuccess={this.handleSubmitSuccess} handleSuccess={this.handleSubmitSuccess}
piece={this.props.content}/> piece={content}/>
</AclProxy> </AclProxy>
</AclProxy> </AclProxy>
<AclProxy <AclProxy
aclObject={this.props.content.acl} aclObject={content.acl}
aclName="acl_wallet_submitted"> aclName="acl_wallet_submitted">
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Submitted to IkonoTV') + ' '} {getLangText('Submitted to IkonoTV') + ' '}
<span className='ascribe-icon icon-ascribe-ok'/> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
</AclProxy> </AclProxy>
<AclProxy <AclProxy
aclObject={this.props.content.acl} aclObject={content.acl}
aclName="acl_wallet_accepted"> aclName="acl_wallet_accepted">
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Loaned to IkonoTV') + ' '} {getLangText('Loaned to IkonoTV') + ' '}
<span className='ascribe-icon icon-ascribe-ok'/> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
@ -101,22 +98,21 @@ let IkonotvAccordionListItem = React.createClass({
}, },
render() { render() {
if(this.props.content) { const { children, className, content } = this.props;
return (
<AccordionListItemPiece return (
className={this.props.className} <AccordionListItemPiece
piece={this.props.content} className={className}
subsubheading={ piece={content}
<div className="pull-left"> subsubheading={
<span>{Moment(this.props.content.date_created, 'YYYY-MM-DD').year()}</span> <div className="pull-left">
</div>} <span>{Moment(content.date_created, 'YYYY-MM-DD').year()}</span>
buttons={this.getSubmitButtons()}> </div>
{this.props.children} }
</AccordionListItemPiece> buttons={this.getSubmitButtons()}>
); {children}
} else { </AccordionListItemPiece>
return null; );
}
} }
}); });

View File

@ -6,49 +6,42 @@ import { History } from 'react-router';
import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import NotificationActions from '../../../../../actions/notification_actions';
import NotificationStore from '../../../../../stores/notification_store';
import UserActions from '../../../../../actions/user_actions';
import UserStore from '../../../../../stores/user_store';
import OwnershipFetcher from '../../../../../fetchers/ownership_fetcher';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import GlobalNotificationModel from '../../../../../models/global_notification_model'; import GlobalNotificationModel from '../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
import CopyrightAssociationForm from '../../../../ascribe_forms/form_copyright_association'; import NotificationActions from '../../../../../actions/notification_actions';
import NotificationStore from '../../../../../stores/notification_store';
import OwnershipFetcher from '../../../../../fetchers/ownership_fetcher';
import CopyrightAssociationForm from '../../../../ascribe_forms/form_copyright_association';
import Property from '../../../../ascribe_forms/property'; import Property from '../../../../ascribe_forms/property';
import AppConstants from '../../../../../constants/application_constants'; import AppConstants from '../../../../../constants/application_constants';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
let IkonotvContractNotifications = React.createClass({ let IkonotvContractNotifications = React.createClass({
propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object
},
mixins: [History], mixins: [History],
getInitialState() { getInitialState() {
return mergeOptions( return NotificationStore.getState();
NotificationStore.getState(),
UserStore.getState(),
WhitelabelStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
NotificationStore.listen(this.onChange); NotificationStore.listen(this.onChange);
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
if (this.state.contractAgreementListNotifications === null){ if (this.state.contractAgreementListNotifications === null){
NotificationActions.fetchContractAgreementListNotifications(); NotificationActions.fetchContractAgreementListNotifications();
} }
@ -56,7 +49,6 @@ let IkonotvContractNotifications = React.createClass({
componentWillUnmount() { componentWillUnmount() {
NotificationStore.unlisten(this.onChange); NotificationStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -64,8 +56,9 @@ let IkonotvContractNotifications = React.createClass({
}, },
getContract(){ getContract(){
let notifications = this.state.contractAgreementListNotifications[0]; const notifications = this.state.contractAgreementListNotifications[0];
let blob = notifications.contract_agreement.contract.blob; const blob = notifications.contract_agreement.contract.blob;
if (blob.mime === 'pdf') { if (blob.mime === 'pdf') {
return ( return (
<div className='notification-contract-pdf'> <div className='notification-contract-pdf'>
@ -76,22 +69,24 @@ let IkonotvContractNotifications = React.createClass({
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/> pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
</div> </div>
); );
} else {
return (
<div className='notification-contract-download'>
<a href={blob.url_safe} target="_blank">
<Glyphicon glyph='download-alt'/>
<span style={{padding: '0.3em'}}>
{getLangText('Download contract')}
</span>
</a>
</div>
);
} }
return (
<div className='notification-contract-download'>
<a href={blob.url_safe} target="_blank">
<Glyphicon glyph='download-alt'/>
<span style={{padding: '0.3em'}}>
Download contract
</span>
</a>
</div>
);
}, },
getAppendix() { getAppendix() {
let notifications = this.state.contractAgreementListNotifications[0]; const notifications = this.state.contractAgreementListNotifications[0];
let appendix = notifications.contract_agreement.appendix; const appendix = notifications.contract_agreement.appendix;
if (appendix && appendix.default) { if (appendix && appendix.default) {
return ( return (
<Property <Property
@ -100,19 +95,20 @@ let IkonotvContractNotifications = React.createClass({
<pre className="ascribe-pre">{appendix.default}</pre> <pre className="ascribe-pre">{appendix.default}</pre>
</Property> </Property>
); );
} else {
return null;
} }
return null;
}, },
handleConfirm() { handleConfirm() {
let contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement; const contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement;
OwnershipFetcher OwnershipFetcher
.confirmContractAgreement(contractAgreement) .confirmContractAgreement(contractAgreement)
.then(this.handleConfirmSuccess); .then(this.handleConfirmSuccess);
}, },
handleConfirmSuccess() { handleConfirmSuccess() {
let notification = new GlobalNotificationModel(getLangText('You have accepted the conditions'), 'success', 5000); const notification = new GlobalNotificationModel(getLangText('You have accepted the conditions'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
// Flush contract notifications and refetch // Flush contract notifications and refetch
@ -123,22 +119,23 @@ let IkonotvContractNotifications = React.createClass({
}, },
handleDeny() { handleDeny() {
let contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement; const contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement;
OwnershipFetcher OwnershipFetcher
.denyContractAgreement(contractAgreement) .denyContractAgreement(contractAgreement)
.then(this.handleDenySuccess); .then(this.handleDenySuccess);
}, },
handleDenySuccess() { handleDenySuccess() {
let notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 5000); const notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
this.history.push('/collection'); this.history.push('/collection');
}, },
getCopyrightAssociationForm(){ getCopyrightAssociationForm(){
let currentUser = this.state.currentUser; const { currentUser } = this.props;
if (currentUser && currentUser.profile && !currentUser.profile.copyright_association) { if (currentUser.profile && !currentUser.profile.copyright_association) {
return ( return (
<div className='notification-contract-footer'> <div className='notification-contract-footer'>
<h1>{getLangText('Are you a member of any copyright societies?')}</h1> <h1>{getLangText('Are you a member of any copyright societies?')}</h1>
@ -149,24 +146,26 @@ let IkonotvContractNotifications = React.createClass({
<CopyrightAssociationForm currentUser={currentUser}/> <CopyrightAssociationForm currentUser={currentUser}/>
</div> </div>
); );
} else {
return null;
} }
return null;
}, },
render() { render() {
const { whitelabel } = this.props;
const { contractAgreementListNotifications } = this.state;
setDocumentTitle(getLangText('Contacts notifications')); setDocumentTitle(getLangText('Contacts notifications'));
if (this.state.contractAgreementListNotifications && if (contractAgreementListNotifications && contractAgreementListNotifications.length) {
this.state.contractAgreementListNotifications.length > 0) { const notifications = contractAgreementListNotifications[0];
const blob = notifications.contract_agreement.contract.blob;
let notifications = this.state.contractAgreementListNotifications[0];
let blob = notifications.contract_agreement.contract.blob;
return ( return (
<div className='container'> <div className='container'>
<div className='notification-contract-wrapper'> <div className='notification-contract-wrapper'>
<div className='notification-contract-logo'> <div className='notification-contract-logo'>
<img src={this.state.whitelabel.logo}/> <img src={whitelabel.logo} />
<div className='notification-contract-header'> <div className='notification-contract-header'>
{getLangText('Contract')} {getLangText('Contract')}
</div> </div>
@ -178,7 +177,7 @@ let IkonotvContractNotifications = React.createClass({
<a href={blob.url_safe} target="_blank"> <a href={blob.url_safe} target="_blank">
<Glyphicon glyph='download-alt'/> <Glyphicon glyph='download-alt'/>
<span style={{padding: '0.3em'}}> <span style={{padding: '0.3em'}}>
Download PDF version {getLangText('Download PDF version')}
</span> </span>
</a> </a>
</div> </div>
@ -195,8 +194,9 @@ let IkonotvContractNotifications = React.createClass({
</div> </div>
</div> </div>
); );
} else {
return null;
} }
return null;
} }
}); });

View File

@ -3,29 +3,27 @@
import React from 'react'; import React from 'react';
import { History } from 'react-router'; import { History } from 'react-router';
import PieceActions from '../../../../../../actions/piece_actions'; import EditionListActions from '../../../../../../actions/edition_list_actions';
import PieceStore from '../../../../../../stores/piece_store';
import UserStore from '../../../../../../stores/user_store'; import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import PieceListStore from '../../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../../actions/piece_list_actions';
import EditionListActions from '../../../../../../actions/edition_list_actions'; import PieceActions from '../../../../../../actions/piece_actions';
import PieceStore from '../../../../../../stores/piece_store';
import IkonotvSubmitButton from '../ikonotv_buttons/ikonotv_submit_button'; import IkonotvSubmitButton from '../ikonotv_buttons/ikonotv_submit_button';
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
import IkonotvArtistDetailsForm from '../ikonotv_forms/ikonotv_artist_details_form'; import IkonotvArtistDetailsForm from '../ikonotv_forms/ikonotv_artist_details_form';
import IkonotvArtworkDetailsForm from '../ikonotv_forms/ikonotv_artwork_details_form'; import IkonotvArtworkDetailsForm from '../ikonotv_forms/ikonotv_artwork_details_form';
import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container'; import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container';
import AscribeSpinner from '../../../../../ascribe_spinner'; import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
import GlobalNotificationModel from '../../../../../../models/global_notification_model'; import AscribeSpinner from '../../../../../ascribe_spinner';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../../utils/dom_utils';
@ -34,6 +32,12 @@ import { mergeOptions } from '../../../../../../utils/general_utils';
let IkonotvPieceContainer = React.createClass({ let IkonotvPieceContainer = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object,
params: React.PropTypes.object params: React.PropTypes.object
}, },
@ -41,15 +45,13 @@ let IkonotvPieceContainer = React.createClass({
getInitialState() { getInitialState() {
return mergeOptions( return mergeOptions(
PieceStore.getInitialState(), PieceListStore.getState(),
UserStore.getState(), PieceStore.getInitialState()
PieceListStore.getState()
); );
}, },
componentDidMount() { componentDidMount() {
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
UserStore.listen(this.onChange);
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
this.loadPiece(); this.loadPiece();
@ -65,7 +67,6 @@ let IkonotvPieceContainer = React.createClass({
componentWillUnmount() { componentWillUnmount() {
PieceStore.unlisten(this.onChange); PieceStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
}, },
@ -94,6 +95,7 @@ let IkonotvPieceContainer = React.createClass({
}, },
render() { render() {
const { currentUser } = this.props;
const { piece } = this.state; const { piece } = this.state;
let furtherDetails = ( let furtherDetails = (
@ -127,7 +129,7 @@ let IkonotvPieceContainer = React.createClass({
return ( return (
<WalletPieceContainer <WalletPieceContainer
piece={piece} piece={piece}
currentUser={this.state.currentUser} currentUser={currentUser}
loadPiece={this.loadPiece} loadPiece={this.loadPiece}
handleDeleteSuccess={this.handleDeleteSuccess} handleDeleteSuccess={this.handleDeleteSuccess}
submitButtonType={IkonotvSubmitButton}> submitButtonType={IkonotvSubmitButton}>

View File

@ -6,47 +6,32 @@ import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
let IkonotvLanding = React.createClass({ let IkonotvLanding = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
getInitialState() {
return UserStore.getState();
},
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
getEnterButton() { getEnterButton() {
const { currentUser, location } = this.props;
let redirect = '/login'; let redirect = '/login';
if(this.state.currentUser && this.state.currentUser.email) { if (currentUser.email) {
redirect = '/collection'; redirect = '/collection';
} } else if (location.query.redirect) {
else if (this.props.location.query && this.props.location.query.redirect) { redirect = '/' + location.query.redirect;
redirect = '/' + this.props.location.query.redirect;
} }
return ( return (
<LinkContainer to={redirect} query={this.props.location.query}> <LinkContainer to={redirect} query={location.query}>
<Button> <Button>
{getLangText('ENTER TO START')} {getLangText('ENTER TO START')}
</Button> </Button>

View File

@ -4,47 +4,34 @@ import React from 'react';
import PieceList from '../../../../piece_list'; import PieceList from '../../../../piece_list';
import UserActions from '../../../../../actions/user_actions';
import UserStore from '../../../../../stores/user_store';
import NotificationStore from '../../../../../stores/notification_store'; import NotificationStore from '../../../../../stores/notification_store';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import IkonotvAccordionListItem from './ikonotv_accordion_list/ikonotv_accordion_list_item'; import IkonotvAccordionListItem from './ikonotv_accordion_list/ikonotv_accordion_list_item';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvPieceList = React.createClass({ let IkonotvPieceList = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
getInitialState() { getInitialState() {
return mergeOptions( return NotificationStore.getState();
NotificationStore.getState(),
UserStore.getState(),
WhitelabelStore.getState()
);
}, },
componentDidMount() { componentDidMount() {
NotificationStore.listen(this.onChange); NotificationStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
UserStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
UserActions.fetchCurrentUser();
}, },
componentWillUnmount() { componentWillUnmount() {
NotificationStore.unlisten(this.onChange); NotificationStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -52,11 +39,11 @@ let IkonotvPieceList = React.createClass({
}, },
shouldRedirect(pieceCount) { shouldRedirect(pieceCount) {
const { contractAgreementListNotifications, const { currentUser: { email: userEmail },
currentUser: { email: userEmail },
whitelabel: { whitelabel: {
user: whitelabelAdminEmail user: whitelabelAdminEmail
} } = this.state; } } = this.props;
const { contractAgreementListNotifications } = this.state;
return contractAgreementListNotifications && return contractAgreementListNotifications &&
!contractAgreementListNotifications.length && !contractAgreementListNotifications.length &&
@ -70,13 +57,7 @@ let IkonotvPieceList = React.createClass({
return ( return (
<div> <div>
<PieceList <PieceList
redirectTo={{ {...this.props}
pathname: '/register_piece',
query: {
'slide_num': 0
}
}}
shouldRedirect={this.shouldRedirect}
accordionListItemType={IkonotvAccordionListItem} accordionListItemType={IkonotvAccordionListItem}
filterParams={[{ filterParams={[{
label: getLangText('Show works I have'), label: getLangText('Show works I have'),
@ -91,7 +72,13 @@ let IkonotvPieceList = React.createClass({
} }
] ]
}]} }]}
location={this.props.location}/> redirectTo={{
pathname: '/register_piece',
query: {
'slide_num': 0
}
}}
shouldRedirect={this.shouldRedirect} />
</div> </div>
); );
} }

View File

@ -10,24 +10,18 @@ import Row from 'react-bootstrap/lib/Row';
import PieceListStore from '../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../actions/piece_list_actions';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import PieceStore from '../../../../../stores/piece_store'; import PieceStore from '../../../../../stores/piece_store';
import PieceActions from '../../../../../actions/piece_actions'; import PieceActions from '../../../../../actions/piece_actions';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import GlobalNotificationModel from '../../../../../models/global_notification_model'; import GlobalNotificationModel from '../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import LoanForm from '../../../../ascribe_forms/form_loan';
import IkonotvArtistDetailsForm from './ikonotv_forms/ikonotv_artist_details_form'; import IkonotvArtistDetailsForm from './ikonotv_forms/ikonotv_artist_details_form';
import IkonotvArtworkDetailsForm from './ikonotv_forms/ikonotv_artwork_details_form'; import IkonotvArtworkDetailsForm from './ikonotv_forms/ikonotv_artwork_details_form';
import RegisterPieceForm from '../../../../ascribe_forms/form_register_piece';
import LoanForm from '../../../../ascribe_forms/form_loan';
import SlidesContainer from '../../../../ascribe_slides_container/slides_container'; import SlidesContainer from '../../../../ascribe_slides_container/slides_container';
import ApiUrls from '../../../../../constants/api_urls'; import ApiUrls from '../../../../../constants/api_urls';
@ -39,31 +33,31 @@ import { getLangText } from '../../../../../utils/lang_utils';
let IkonotvRegisterPiece = React.createClass({ let IkonotvRegisterPiece = React.createClass({
propTypes: { propTypes: {
handleSuccess: React.PropTypes.func, handleSuccess: React.PropTypes.func,
piece: React.PropTypes.object.isRequired,
// Provided from PrizeApp
currentUser: React.PropTypes.object.isRequired,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
mixins: [History], mixins: [History],
getInitialState(){ getInitialState() {
return mergeOptions( return mergeOptions(
UserStore.getState(),
PieceListStore.getState(), PieceListStore.getState(),
PieceStore.getInitialState(), PieceStore.getInitialState(),
WhitelabelStore.getState(),
{ {
step: 0, step: 0,
pageExitWarning: getLangText("If you leave this form now, your work will not be loaned to Ikono TV.") pageExitWarning: getLangText("If you leave this form now, your work will not be loaned to Ikono TV.")
}); }
);
}, },
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
UserActions.fetchCurrentUser();
WhitelabelActions.fetchWhitelabel();
const queryParams = this.props.location.query; const queryParams = this.props.location.query;
@ -81,9 +75,7 @@ let IkonotvRegisterPiece = React.createClass({
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
PieceStore.unlisten(this.onChange); PieceStore.unlisten(this.onChange);
WhitelabelStore.listen(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -144,8 +136,7 @@ let IkonotvRegisterPiece = React.createClass({
}, },
canSubmit() { canSubmit() {
let currentUser = this.state.currentUser; const { currentUser, whitelabel } = this.props;
let whitelabel = this.state.whitelabel;
return currentUser.acl && currentUser.acl.acl_wallet_submit && whitelabel.user; return currentUser.acl && currentUser.acl.acl_wallet_submit && whitelabel.user;
}, },
@ -187,7 +178,8 @@ let IkonotvRegisterPiece = React.createClass({
getSlideLoan() { getSlideLoan() {
if (this.canSubmit()) { if (this.canSubmit()) {
const { piece, whitelabel } = this.state; const { whitelabel } = this.props;
const { piece } = this.state;
const today = new Moment(); const today = new Moment();
const endDate = new Moment().add(2, 'years'); const endDate = new Moment().add(2, 'years');
@ -218,7 +210,8 @@ let IkonotvRegisterPiece = React.createClass({
}, },
render() { render() {
const { pageExitWarning } = this.state; const { location } = this.props;
const { pageExitWarning, step } = this.state;
return ( return (
<SlidesContainer <SlidesContainer
@ -228,19 +221,19 @@ let IkonotvRegisterPiece = React.createClass({
pending: 'glyphicon glyphicon-chevron-right', pending: 'glyphicon glyphicon-chevron-right',
completed: 'glyphicon glyphicon-lock' completed: 'glyphicon glyphicon-lock'
}} }}
location={this.props.location} location={location}
pageExitWarning={pageExitWarning}> pageExitWarning={pageExitWarning}>
<div data-slide-title={getLangText('Register work')}> <div data-slide-title={getLangText('Register work')}>
<Row className="no-margin"> <Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}> <Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<RegisterPieceForm <RegisterPieceForm
disabled={this.state.step > 0} {...this.props}
disabled={step > 0}
enableLocalHashing={false} enableLocalHashing={false}
headerMessage={getLangText('Register work')}
submitMessage={getLangText('Register')}
isFineUploaderActive={true}
handleSuccess={this.handleRegisterSuccess} handleSuccess={this.handleRegisterSuccess}
location={this.props.location} /> headerMessage={getLangText('Register work')}
isFineUploaderActive={true}
submitMessage={getLangText('Register')} />
</Col> </Col>
</Row> </Row>
</div> </div>

View File

@ -5,46 +5,31 @@ import React from 'react';
import Button from 'react-bootstrap/lib/Button'; import Button from 'react-bootstrap/lib/Button';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer'; import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
let LumenusLanding = React.createClass({ let LumenusLanding = React.createClass({
propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
getInitialState() { // Provided from router
return mergeOptions( location: React.PropTypes.object
WhitelabelStore.getState()
);
}, },
componentWillMount() { componentWillMount() {
setDocumentTitle('Lumenus Marketplace'); setDocumentTitle('Lumenus Marketplace');
}, },
componentDidMount() {
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() { render() {
return ( return (
<div className="container ascribe-form-wrapper"> <div className="container ascribe-form-wrapper">
<div className="row"> <div className="row">
<div className="col-xs-12 wp-landing-wrapper"> <div className="col-xs-12 wp-landing-wrapper">
<div className="row" style={{border: '1px solid #CCC', padding: '2em'}}> <div className="row" style={{border: '1px solid #CCC', padding: '2em'}}>
<img src={this.state.whitelabel.logo} width="150px"/> <img src={this.props.whitelabel.logo} height="150" />
<div style={{marginTop: '1em'}}> <div style={{marginTop: '1em'}}>
{getLangText('Lumenus Marketplace is powered by') + ' '} {getLangText('Lumenus Marketplace is powered by') + ' '}
<span> <span>

View File

@ -9,63 +9,52 @@ import EmailButton from '../../../../../ascribe_buttons/acls/email_button';
import TransferButton from '../../../../../ascribe_buttons/acls/transfer_button'; import TransferButton from '../../../../../ascribe_buttons/acls/transfer_button';
import UnconsignButton from '../../../../../ascribe_buttons/acls/unconsign_button'; import UnconsignButton from '../../../../../ascribe_buttons/acls/unconsign_button';
import UserActions from '../../../../../../actions/user_actions'; import { selectFromObject } from '../../../../../../utils/general_utils';
import UserStore from '../../../../../../stores/user_store';
let MarketAclButtonList = React.createClass({ let MarketAclButtonList = React.createClass({
propTypes: { propTypes: {
availableAcls: React.PropTypes.object.isRequired, availableAcls: React.PropTypes.object.isRequired,
className: React.PropTypes.string, currentUser: React.PropTypes.object.isRequired,
pieceOrEditions: React.PropTypes.array, handleSuccess: React.PropTypes.func.isRequired,
handleSuccess: React.PropTypes.func, pieceOrEditions: React.PropTypes.array.isRequired,
whitelabel: React.PropTypes.object.isRequired,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
]) ]),
}, className: React.PropTypes.string
getInitialState() {
return UserStore.getState();
},
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser.defer();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
}, },
render() { render() {
let { availableAcls, className, pieceOrEditions, handleSuccess } = this.props; const {
availableAcls,
children,
className,
currentUser,
handleSuccess,
pieceOrEditions,
whitelabel } = this.props;
const buttonProps = selectFromObject(this.props, [
'availableAcls',
'currentUser',
'handleSuccess',
'pieceOrEditions'
]);
return ( return (
<div className={className}> <div className={className}>
<MarketSubmitButton <MarketSubmitButton
availableAcls={availableAcls} availableAcls={availableAcls}
currentUser={this.state.currentUser} currentUser={currentUser}
editions={pieceOrEditions} editions={pieceOrEditions}
handleSuccess={handleSuccess} /> handleSuccess={handleSuccess}
<EmailButton whitelabel={whitelabel} />
availableAcls={availableAcls} <EmailButton {...buttonProps} />
currentUser={this.state.currentUser} <TransferButton {...buttonProps} />
pieceOrEditions={pieceOrEditions} <UnconsignButton {...buttonProps} />
handleSuccess={handleSuccess} /> {children}
<TransferButton
availableAcls={availableAcls}
currentUser={this.state.currentUser}
pieceOrEditions={pieceOrEditions}
handleSuccess={handleSuccess} />
<UnconsignButton
availableAcls={availableAcls}
currentUser={this.state.currentUser}
pieceOrEditions={pieceOrEditions}
handleSuccess={handleSuccess} />
{this.props.children}
</div> </div>
); );
} }

View File

@ -5,9 +5,6 @@ import classNames from 'classnames';
import EditionActions from '../../../../../../actions/edition_actions'; import EditionActions from '../../../../../../actions/edition_actions';
import WhitelabelActions from '../../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../../stores/whitelabel_store';
import MarketAdditionalDataForm from '../market_forms/market_additional_data_form'; import MarketAdditionalDataForm from '../market_forms/market_additional_data_form';
import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory'; import AclFormFactory from '../../../../../ascribe_forms/acl_form_factory';
@ -16,7 +13,6 @@ import ConsignForm from '../../../../../ascribe_forms/form_consign';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper'; import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import AclProxy from '../../../../../acl_proxy'; import AclProxy from '../../../../../acl_proxy';
import AscribeSpinner from '../../../../../ascribe_spinner';
import ApiUrls from '../../../../../../constants/api_urls'; import ApiUrls from '../../../../../../constants/api_urls';
@ -26,31 +22,14 @@ import { getLangText } from '../../../../../../utils/lang_utils';
let MarketSubmitButton = React.createClass({ let MarketSubmitButton = React.createClass({
propTypes: { propTypes: {
availableAcls: React.PropTypes.object.isRequired, availableAcls: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object, currentUser: React.PropTypes.object.isRequired,
editions: React.PropTypes.array.isRequired, editions: React.PropTypes.array.isRequired,
whitelabel: React.PropTypes.object.isRequired,
className: React.PropTypes.string, className: React.PropTypes.string,
handleSuccess: React.PropTypes.func handleSuccess: React.PropTypes.func
}, },
getInitialState() {
return WhitelabelStore.getState();
},
componentDidMount() {
WhitelabelStore.listen(this.onChange);
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
canEditionBeSubmitted(edition) { canEditionBeSubmitted(edition) {
if (edition && edition.extra_data && edition.other_data) { if (edition && edition.extra_data && edition.other_data) {
const { const {
@ -99,8 +78,14 @@ let MarketSubmitButton = React.createClass({
}, },
render() { render() {
const { availableAcls, currentUser, className, editions, handleSuccess } = this.props; const {
const { whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail } } = this.state; availableAcls,
currentUser,
className,
editions,
handleSuccess,
whitelabel: { name: whitelabelName = 'Market', user: whitelabelAdminEmail } } = this.props;
const { solePieceId, canSubmit } = this.getAggregateEditionDetails(); const { solePieceId, canSubmit } = this.getAggregateEditionDetails();
const message = getAclFormMessage({ const message = getAclFormMessage({
aclName: 'acl_consign', aclName: 'acl_consign',
@ -119,6 +104,7 @@ let MarketSubmitButton = React.createClass({
{getLangText('CONSIGN TO %s', whitelabelName.toUpperCase())} {getLangText('CONSIGN TO %s', whitelabelName.toUpperCase())}
</button> </button>
); );
const consignForm = ( const consignForm = (
<AclFormFactory <AclFormFactory
action='acl_consign' action='acl_consign'

View File

@ -6,11 +6,6 @@ import MarketAclButtonList from './market_buttons/market_acl_button_list';
import PieceList from '../../../../piece_list'; import PieceList from '../../../../piece_list';
import UserActions from '../../../../../actions/user_actions';
import UserStore from '../../../../../stores/user_store';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import { setDocumentTitle } from '../../../../../utils/dom_utils'; import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils'; import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils'; import { getLangText } from '../../../../../utils/lang_utils';
@ -18,45 +13,26 @@ import { getLangText } from '../../../../../utils/lang_utils';
let MarketPieceList = React.createClass({ let MarketPieceList = React.createClass({
propTypes: { propTypes: {
customThumbnailPlaceholder: React.PropTypes.func, customThumbnailPlaceholder: React.PropTypes.func,
location: React.PropTypes.object
},
getInitialState() { // Provided from PrizeApp
return mergeOptions( currentUser: React.PropTypes.object.isRequired,
UserStore.getState(), whitelabel: React.PropTypes.object.isRequired,
WhitelabelStore.getState()
); // Provided from router
location: React.PropTypes.object
}, },
componentWillMount() { componentWillMount() {
setDocumentTitle(getLangText('Collection')); setDocumentTitle(getLangText('Collection'));
}, },
componentDidMount() {
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
UserActions.fetchCurrentUser();
WhitelabelActions.fetchWhitelabel();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() { render() {
const { customThumbnailPlaceholder, location } = this.props;
const { const {
currentUser: { email: userEmail }, currentUser: { email: userEmail },
whitelabel: { whitelabel: {
name: whitelabelName = 'Market', name: whitelabelName = 'Market',
user: whitelabelAdminEmail user: whitelabelAdminEmail
} } = this.state; } } = this.props;
let filterParams = null; let filterParams = null;
let isUserAdmin = null; let isUserAdmin = null;
@ -78,6 +54,7 @@ let MarketPieceList = React.createClass({
return ( return (
<PieceList <PieceList
{...this.props}
canLoadPieceList={canLoadPieceList} canLoadPieceList={canLoadPieceList}
redirectTo={{ redirectTo={{
pathname: '/register_piece', pathname: '/register_piece',
@ -87,9 +64,7 @@ let MarketPieceList = React.createClass({
}} }}
shouldRedirect={(pieceCount) => !isUserAdmin && !pieceCount} shouldRedirect={(pieceCount) => !isUserAdmin && !pieceCount}
bulkModalButtonListType={MarketAclButtonList} bulkModalButtonListType={MarketAclButtonList}
customThumbnailPlaceholder={customThumbnailPlaceholder} filterParams={filterParams} />
filterParams={filterParams}
location={location} />
); );
} }
}); });

View File

@ -6,17 +6,11 @@ import { History } from 'react-router';
import Col from 'react-bootstrap/lib/Col'; import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row'; import Row from 'react-bootstrap/lib/Row';
import PieceStore from '../../../../../stores/piece_store';
import PieceActions from '../../../../../actions/piece_actions';
import PieceListStore from '../../../../../stores/piece_list_store'; import PieceListStore from '../../../../../stores/piece_list_store';
import PieceListActions from '../../../../../actions/piece_list_actions'; import PieceListActions from '../../../../../actions/piece_list_actions';
import UserStore from '../../../../../stores/user_store'; import PieceStore from '../../../../../stores/piece_store';
import UserActions from '../../../../../actions/user_actions'; import PieceActions from '../../../../../actions/piece_actions';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import MarketAdditionalDataForm from './market_forms/market_additional_data_form'; import MarketAdditionalDataForm from './market_forms/market_additional_data_form';
@ -31,6 +25,11 @@ import { mergeOptions } from '../../../../../utils/general_utils';
let MarketRegisterPiece = React.createClass({ let MarketRegisterPiece = React.createClass({
propTypes: { propTypes: {
// Provided from PrizeApp
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object.isRequired,
// Provided from router
location: React.PropTypes.object location: React.PropTypes.object
}, },
@ -40,8 +39,6 @@ let MarketRegisterPiece = React.createClass({
return mergeOptions( return mergeOptions(
PieceListStore.getState(), PieceListStore.getState(),
PieceStore.getInitialState(), PieceStore.getInitialState(),
UserStore.getState(),
WhitelabelStore.getState(),
{ {
step: 0 step: 0
}); });
@ -50,11 +47,6 @@ let MarketRegisterPiece = React.createClass({
componentDidMount() { componentDidMount() {
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
PieceStore.listen(this.onChange); PieceStore.listen(this.onChange);
UserStore.listen(this.onChange);
WhitelabelStore.listen(this.onChange);
UserActions.fetchCurrentUser();
WhitelabelActions.fetchWhitelabel();
const queryParams = this.props.location.query; const queryParams = this.props.location.query;
@ -70,8 +62,6 @@ let MarketRegisterPiece = React.createClass({
componentWillUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
PieceStore.unlisten(this.onChange); PieceStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
WhitelabelStore.unlisten(this.onChange);
}, },
onChange(state) { onChange(state) {
@ -111,12 +101,12 @@ let MarketRegisterPiece = React.createClass({
}, },
render() { render() {
const { location } = this.props; const {
const { piece, location,
step, whitelabel: {
whitelabel: { name: whitelabelName = 'Market'
name: whitelabelName = 'Market' } } = this.props
} } = this.state; const { piece, step } = this.state;
setDocumentTitle(getLangText('Register a new piece')); setDocumentTitle(getLangText('Register a new piece'));
@ -133,14 +123,14 @@ let MarketRegisterPiece = React.createClass({
<Row className="no-margin"> <Row className="no-margin">
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}> <Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
<RegisterPieceForm <RegisterPieceForm
{...this.props}
disabled={step > 0} disabled={step > 0}
enableLocalHashing={false} enableLocalHashing={false}
headerMessage={getLangText('Consign to %s', whitelabelName)}
submitMessage={getLangText('Proceed to additional details')}
isFineUploaderActive={true}
enableSeparateThumbnail={false} enableSeparateThumbnail={false}
handleSuccess={this.handleRegisterSuccess} handleSuccess={this.handleRegisterSuccess}
location={location}> headerMessage={getLangText('Consign to %s', whitelabelName)}
isFineUploaderActive={true}
submitMessage={getLangText('Proceed to additional details')}>
<Property <Property
name="num_editions" name="num_editions"
label={getLangText('Specify editions')}> label={getLangText('Specify editions')}>

View File

@ -1,57 +1,62 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import Header from '../../header';
import Footer from '../../footer';
import GlobalNotification from '../../global_notification';
import classNames from 'classnames'; import classNames from 'classnames';
import AppBase from '../../app_base';
import AppRouteWrapper from '../../app_route_wrapper';
import Footer from '../../footer';
import Header from '../../header';
import { getSubdomain } from '../../../utils/general_utils'; import { getSubdomain } from '../../../utils/general_utils';
let WalletApp = React.createClass({ let WalletApp = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.oneOfType([ activeRoute: React.PropTypes.object.isRequired,
React.PropTypes.arrayOf(React.PropTypes.element), children: React.PropTypes.element.isRequired,
React.PropTypes.element history: React.PropTypes.object.isRequired,
]), routes: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
history: React.PropTypes.object,
routes: React.PropTypes.arrayOf(React.PropTypes.object) // Provided from AppBase
currentUser: React.PropTypes.object,
whitelabel: React.PropTypes.object
}, },
render() { render() {
const { activeRoute, children, currentUser, history, routes, whitelabel } = this.props;
const subdomain = getSubdomain();
const path = activeRoute && activeRoute.path;
let header = null; let header = null;
let subdomain = getSubdomain();
const { history, routes, children } = this.props;
// The second element of routes is always the active component object, where we can
// extract the path.
let path = routes[1] ? routes[1].path : null;
// if the path of the current activeRoute is not defined, then this is the IndexRoute // if the path of the current activeRoute is not defined, then this is the IndexRoute
if ((!path || history.isActive('/login') || history.isActive('/signup') || history.isActive('/contract_notifications')) if ((!path || history.isActive('/login') || history.isActive('/signup') || history.isActive('/contract_notifications'))
&& (['cyland', 'ikonotv', 'lumenus', '23vivi', 'polline', 'artcity']).indexOf(subdomain) > -1) { && (['cyland', 'ikonotv', 'lumenus', '23vivi', 'polline', 'artcity']).includes(subdomain)) {
header = (<div className="hero"/>); header = (<div className="hero"/>);
} else { } else {
header = <Header routes={routes} />; header = (
<Header
currentUser={currentUser}
routes={routes}
whitelabel={whitelabel} />
);
} }
// In react-router 1.0, Routes have no 'name' property anymore. To keep functionality however, // In react-router 1.0, Routes have no 'name' property anymore. To keep functionality however,
// we split the path by the first occurring slash and take the first splitter. // we split the path by the first occurring slash and take the first splitter.
return ( return (
<div className={classNames('ascribe-wallet-app', 'route--' + (path ? path.split('/')[0] : 'landing'))}> <div className={classNames('ascribe-app', 'ascribe-wallet-app', `route--${(path ? path.split('/')[0] : 'landing')}`)}>
<div className='container'> {header}
{header} <AppRouteWrapper
currentUser={currentUser}
whitelabel={whitelabel}>
{/* Routes are injected here */}
{children} {children}
<GlobalNotification /> </AppRouteWrapper>
<div id="modal" className="container"></div> <Footer activeRoute={activeRoute} />
<Footer />
</div>
</div> </div>
); );
} }
}); });
export default WalletApp; export default AppBase(WalletApp);

View File

@ -52,193 +52,302 @@ import WalletApp from './wallet_app';
let ROUTES = { let ROUTES = {
'cyland': ( 'cyland': (
<Route path='/' component={WalletApp}> <Route path='/' component={WalletApp}>
<IndexRoute component={CylandLanding} /> <IndexRoute
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(CylandLanding)}
hideFooter />
<Route <Route
path='login' path='login'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)}
hideFooter />
<Route <Route
path='logout' path='logout'
component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}
hideFooter />
<Route <Route
path='signup' path='signup'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)}
hideFooter />
<Route <Route
path='password_reset' path='password_reset'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)}
hideFooter />
<Route <Route
path='settings' path='settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)}
hideFooter />
<Route <Route
path='contract_settings' path='contract_settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)}
hideFooter />
<Route <Route
path='register_piece' path='register_piece'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CylandRegisterPiece)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CylandRegisterPiece)}
headerTitle='+ NEW WORK' headerTitle='+ NEW WORK'
aclName='acl_wallet_submit' /> aclName='acl_wallet_submit'
hideFooter />
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CylandPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CylandPieceList)}
headerTitle='COLLECTION' headerTitle='COLLECTION'
disableOn='noPieces' /> disableOn='noPieces'
<Route path='editions/:editionId' component={EditionContainer} /> hideFooter />
<Route path='verify' component={CoaVerifyContainer} /> <Route
<Route path='pieces/:pieceId' component={CylandPieceContainer} /> path='editions/:editionId'
<Route path='*' component={ErrorNotFoundPage} /> component={EditionContainer}
hideFooter />
<Route
path='coa_verify'
component={CoaVerifyContainer}
hideFooter />
<Route
path='pieces/:pieceId'
component={CylandPieceContainer}
hideFooter />
<Route
path='*'
component={ErrorNotFoundPage}
hideFooter />
</Route> </Route>
), ),
'cc': ( 'cc': (
<Route path='/' component={WalletApp}> <Route path='/' component={WalletApp}>
<Route <Route
path='login' path='login'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)}
hideFooter />
<Route <Route
path='logout' path='logout'
component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}
hideFooter />
<Route <Route
path='signup' path='signup'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)}
hideFooter />
<Route <Route
path='password_reset' path='password_reset'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)}
hideFooter />
<Route <Route
path='settings' path='settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)}
hideFooter />
<Route <Route
path='contract_settings' path='contract_settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)}
hideFooter />
<Route <Route
path='register_piece' path='register_piece'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CCRegisterPiece)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(CCRegisterPiece)}
headerTitle='+ NEW WORK' /> headerTitle='+ NEW WORK'
hideFooter />
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(PieceList)}
headerTitle='COLLECTION' headerTitle='COLLECTION'
disableOn='noPieces' /> disableOn='noPieces'
<Route path='pieces/:pieceId' component={PieceContainer} /> hideFooter />
<Route path='editions/:editionId' component={EditionContainer} /> <Route
<Route path='verify' component={CoaVerifyContainer} /> path='pieces/:pieceId'
<Route path='*' component={ErrorNotFoundPage} /> component={PieceContainer}
hideFooter />
<Route
path='editions/:editionId'
component={EditionContainer}
hideFooter />
<Route
path='coa_verify'
component={CoaVerifyContainer}
hideFooter />
<Route
path='*'
component={ErrorNotFoundPage}
hideFooter />
</Route> </Route>
), ),
'ikonotv': ( 'ikonotv': (
<Route path='/' component={WalletApp}> <Route path='/' component={WalletApp}>
<IndexRoute component={IkonotvLanding} /> <IndexRoute
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(IkonotvLanding)}
hideFooter />
<Route <Route
path='login' path='login'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)}
hideFooter />
<Route <Route
path='logout' path='logout'
component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}
hideFooter />
<Route <Route
path='signup' path='signup'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)}
hideFooter />
<Route <Route
path='password_reset' path='password_reset'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)}
hideFooter />
<Route <Route
path='settings' path='settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)}
hideFooter />
<Route <Route
path='contract_settings' path='contract_settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)}
hideFooter />
<Route <Route
path='request_loan' path='request_loan'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SendContractAgreementForm)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SendContractAgreementForm)}
headerTitle='SEND NEW CONTRACT' headerTitle='SEND NEW CONTRACT'
aclName='acl_create_contractagreement' /> aclName='acl_create_contractagreement'
hideFooter />
<Route <Route
path='register_piece' path='register_piece'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvRegisterPiece)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvRegisterPiece)}
headerTitle='+ NEW WORK' headerTitle='+ NEW WORK'
aclName='acl_wallet_submit' /> aclName='acl_wallet_submit'
hideFooter />
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvPieceList)}
headerTitle='COLLECTION' headerTitle='COLLECTION'
disableOn='noPieces' /> disableOn='noPieces'
hideFooter />
<Route <Route
path='contract_notifications' path='contract_notifications'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvContractNotifications)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(IkonotvContractNotifications)}
<Route path='pieces/:pieceId' component={IkonotvPieceContainer} /> hideFooter />
<Route path='editions/:editionId' component={EditionContainer} /> <Route
<Route path='verify' component={CoaVerifyContainer} /> path='pieces/:pieceId'
<Route path='*' component={ErrorNotFoundPage} /> component={IkonotvPieceContainer}
hideFooter />
<Route
path='editions/:editionId'
component={EditionContainer}
hideFooter />
<Route
path='coa_verify'
component={CoaVerifyContainer}
hideFooter />
<Route
path='*'
component={ErrorNotFoundPage}
hideFooter />
</Route> </Route>
), ),
'lumenus': ( 'lumenus': (
<Route path='/' component={WalletApp}> <Route path='/' component={WalletApp}>
<IndexRoute component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LumenusLanding)} /> <IndexRoute
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LumenusLanding)}
hideFooter />
<Route <Route
path='login' path='login'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)}
hideFooter />
<Route <Route
path='logout' path='logout'
component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}
hideFooter />
<Route <Route
path='signup' path='signup'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)}
hideFooter />
<Route <Route
path='password_reset' path='password_reset'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)}
hideFooter />
<Route <Route
path='settings' path='settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)}
hideFooter />
<Route <Route
path='contract_settings' path='contract_settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)}
hideFooter />
<Route <Route
path='register_piece' path='register_piece'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketRegisterPiece)}
headerTitle='+ NEW WORK' headerTitle='+ NEW WORK'
aclName='acl_wallet_submit' /> aclName='acl_wallet_submit'
hideFooter />
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketPieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketPieceList)}
headerTitle='COLLECTION' headerTitle='COLLECTION'
disableOn='noPieces' /> disableOn='noPieces'
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> hideFooter />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route
<Route path='verify' component={CoaVerifyContainer} /> path='pieces/:pieceId'
<Route path='*' component={ErrorNotFoundPage} /> component={MarketPieceContainer}
hideFooter />
<Route
path='editions/:editionId'
component={MarketEditionContainer}
hideFooter />
<Route
path='coa_verify'
component={CoaVerifyContainer}
hideFooter />
<Route
path='*'
component={ErrorNotFoundPage}
hideFooter />
</Route> </Route>
), ),
'23vivi': ( '23vivi': (
<Route path='/' component={WalletApp}> <Route path='/' component={WalletApp}>
<IndexRoute component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(Vivi23Landing)} /> <IndexRoute component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(Vivi23Landing)}
hideFooter />
<Route <Route
path='login' path='login'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(LoginContainer)}
hideFooter />
<Route <Route
path='logout' path='logout'
component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)} /> component={ProxyHandler(AuthRedirect({to: '/', when: 'loggedOut'}))(LogoutContainer)}
hideFooter />
<Route <Route
path='signup' path='signup'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(SignupContainer)}
hideFooter />
<Route <Route
path='password_reset' path='password_reset'
component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)} /> component={ProxyHandler(AuthRedirect({to: '/collection', when: 'loggedIn'}))(PasswordResetContainer)}
hideFooter />
<Route <Route
path='settings' path='settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(SettingsContainer)}
hideFooter />
<Route <Route
path='contract_settings' path='contract_settings'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)} /> component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(ContractSettings)}
hideFooter />
<Route <Route
path='register_piece' path='register_piece'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketRegisterPiece)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(MarketRegisterPiece)}
headerTitle='+ NEW WORK' headerTitle='+ NEW WORK'
aclName='acl_wallet_submit' /> aclName='acl_wallet_submit'
hideFooter />
<Route <Route
path='collection' path='collection'
component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(Vivi23PieceList)} component={ProxyHandler(AuthRedirect({to: '/login', when: 'loggedOut'}))(Vivi23PieceList)}
headerTitle='COLLECTION' headerTitle='COLLECTION'
disableOn='noPieces' /> disableOn='noPieces'
<Route path='pieces/:pieceId' component={MarketPieceContainer} /> hideFooter />
<Route path='editions/:editionId' component={MarketEditionContainer} /> <Route
<Route path='verify' component={CoaVerifyContainer} /> path='pieces/:pieceId'
<Route path='*' component={ErrorNotFoundPage} /> component={MarketPieceContainer}
hideFooter />
<Route
path='editions/:editionId'
component={MarketEditionContainer}
hideFooter />
<Route
path='coa_verify'
component={CoaVerifyContainer}
hideFooter />
<Route
path='*'
component={ErrorNotFoundPage}
hideFooter />
</Route> </Route>
), ),
'polline': ( 'polline': (

View File

@ -83,6 +83,8 @@ const constants = {
'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV', 'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV',
'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'], 'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'],
'locationThreshold': 10,
'searchThreshold': 500, 'searchThreshold': 500,
'supportedThumbnailFileFormats': [ 'supportedThumbnailFileFormats': [

Some files were not shown because too many files have changed in this diff Show More