mirror of
https://github.com/ascribe/onion.git
synced 2024-11-14 09:05:08 +01:00
Merge remote-tracking branch 'origin/master' into AD-901-add-lint-for-scss
Conflicts: sass/main.scss
This commit is contained in:
commit
d2adc7a388
@ -24,8 +24,11 @@ gulp serve
|
||||
Additionally, to work on the white labeling functionality, you need to edit your `/etc/hosts` file and add:
|
||||
|
||||
```
|
||||
127.0.0.1 localhost.com
|
||||
127.0.0.1 cc.localhost.com
|
||||
127.0.0.1 localhost.com
|
||||
127.0.0.1 cc.localhost.com
|
||||
127.0.0.1 cyland.localhost.com
|
||||
127.0.0.1 ikonotv.localhost.com
|
||||
127.0.0.1 sluice.localhost.com
|
||||
```
|
||||
|
||||
|
||||
|
12
gulpfile.js
12
gulpfile.js
@ -23,7 +23,7 @@ var argv = require('yargs').argv;
|
||||
var server = require('./server.js').app;
|
||||
var minifyCss = require('gulp-minify-css');
|
||||
var uglify = require('gulp-uglify');
|
||||
|
||||
var opn = require('opn');
|
||||
|
||||
|
||||
var config = {
|
||||
@ -48,8 +48,7 @@ var config = {
|
||||
},
|
||||
filesToWatch: [
|
||||
'build/css/*.css',
|
||||
'build/js/*.js',
|
||||
'node_modules/react-s3-fine_uploader/*.js'
|
||||
'build/js/*.js'
|
||||
]
|
||||
};
|
||||
|
||||
@ -73,6 +72,9 @@ gulp.task('js:build', function() {
|
||||
|
||||
gulp.task('serve', ['browser-sync', 'run-server', 'sass:build', 'sass:watch', 'copy'], function() {
|
||||
bundle(true);
|
||||
|
||||
// opens the browser window with the correct url, which is localhost.com
|
||||
opn('http://www.localhost.com:3000');
|
||||
});
|
||||
|
||||
gulp.task('jest', function(done) {
|
||||
@ -93,7 +95,9 @@ gulp.task('browser-sync', function() {
|
||||
browserSync({
|
||||
files: config.filesToWatch,
|
||||
proxy: 'http://localhost:4000',
|
||||
port: 3000
|
||||
port: 3000,
|
||||
open: false, // does not open the browser-window anymore (handled manually)
|
||||
ghostMode: false
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -25,8 +25,9 @@ class PieceListActions {
|
||||
orderBy,
|
||||
orderAsc,
|
||||
filterBy,
|
||||
'pieceList': [],
|
||||
'pieceListCount': -1
|
||||
pieceList: [],
|
||||
pieceListCount: -1,
|
||||
unfilteredPieceListCount: -1
|
||||
});
|
||||
|
||||
// afterwards, we can load the list
|
||||
@ -42,8 +43,9 @@ class PieceListActions {
|
||||
orderBy,
|
||||
orderAsc,
|
||||
filterBy,
|
||||
'pieceList': res.pieces,
|
||||
'pieceListCount': res.count
|
||||
pieceList: res.pieces,
|
||||
pieceListCount: res.count,
|
||||
unfilteredPieceListCount: res.unfiltered_count
|
||||
});
|
||||
resolve();
|
||||
})
|
||||
|
@ -6,15 +6,16 @@ import Header from '../components/header';
|
||||
import Footer from '../components/footer';
|
||||
import GlobalNotification from './global_notification';
|
||||
|
||||
// let Link = Router.Link;
|
||||
let RouteHandler = Router.RouteHandler;
|
||||
import getRoutes from '../routes';
|
||||
|
||||
|
||||
let RouteHandler = Router.RouteHandler;
|
||||
|
||||
let AscribeApp = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<div className="container ascribe-default-app">
|
||||
<Header />
|
||||
<Header routes={getRoutes()} />
|
||||
<RouteHandler />
|
||||
<Footer />
|
||||
<GlobalNotification />
|
||||
|
@ -84,6 +84,8 @@ let PieceContainer = React.createClass({
|
||||
|
||||
IT SHOULD BE REMOVED AND REPLACED WITH A BETTER SOLUTION ASAP!
|
||||
|
||||
ALSO, WE ENABLED THE LOAN BUTTON FOR IKONOTV TO LET THEM LOAN ON A PIECE LEVEL
|
||||
|
||||
*/
|
||||
if(state && state.piece && state.piece.acl && typeof state.piece.acl.acl_loan !== 'undefined') {
|
||||
|
||||
@ -202,6 +204,7 @@ let PieceContainer = React.createClass({
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
if('title' in this.state.piece) {
|
||||
return (
|
||||
|
@ -2,20 +2,21 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Property from '../../../../../ascribe_forms/property';
|
||||
import LoanContractListActions from '../../actions/loan_contract_list_actions';
|
||||
import LoanContractListStore from '../../stores/loan_contract_list_store';
|
||||
|
||||
import LoanContractListActions from '../../../../../../actions/loan_contract_list_actions';
|
||||
import LoanContractListStore from '../../../../../../stores/loan_contract_list_store';
|
||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
|
||||
import Form from './form';
|
||||
import Property from './property';
|
||||
import PropertyCollapsible from './property_collapsible';
|
||||
import InputTextAreaToggable from './input_textarea_toggable';
|
||||
|
||||
import Form from '../../../../../ascribe_forms/form';
|
||||
import ApiUrls from '../../constants/api_urls';
|
||||
|
||||
import ApiUrls from '../../../../../../constants/api_urls';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../../utils/general_utils';
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../utils/general_utils';
|
||||
|
||||
|
||||
let ContractForm = React.createClass({
|
||||
@ -96,7 +97,7 @@ let ContractForm = React.createClass({
|
||||
buttons={<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login">
|
||||
{getLangText('SEND LOAN REQUEST')}
|
||||
{getLangText('Send loan request')}
|
||||
</button>}
|
||||
spinner={
|
||||
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||
@ -104,7 +105,7 @@ let ContractForm = React.createClass({
|
||||
</span>
|
||||
}>
|
||||
<div className="ascribe-form-header">
|
||||
<h3>{getLangText('CONTRACT FORM')}</h3>
|
||||
<h3>{getLangText('Contract form')}</h3>
|
||||
</div>
|
||||
<Property
|
||||
name='artist_name'
|
||||
@ -123,14 +124,15 @@ let ContractForm = React.createClass({
|
||||
required/>
|
||||
</Property>
|
||||
{this.getContracts()}
|
||||
<Property
|
||||
<PropertyCollapsible
|
||||
name='appendix'
|
||||
label={getLangText('Appendix')}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={getLangText('Add an appendix to the contract')}
|
||||
required/>
|
||||
</Property>
|
||||
checkboxLabel={getLangText('Add appendix to the contract')}>
|
||||
<span>{getLangText('Appendix')}</span>
|
||||
<InputTextAreaToggable
|
||||
rows={1}
|
||||
editable={true}
|
||||
placeholder={getLangText('This will be appended to the contract selected above')}/>
|
||||
</PropertyCollapsible>
|
||||
</Form>
|
||||
);
|
||||
}
|
@ -68,8 +68,12 @@ let LoanForm = React.createClass({
|
||||
return this.props.id;
|
||||
},
|
||||
|
||||
handleOnBlur(event) {
|
||||
LoanContractActions.fetchLoanContract(event.target.value);
|
||||
handleOnChange(event) {
|
||||
let potentialEmail = event.target.value;
|
||||
|
||||
if(potentialEmail.match(/.*@.*/)) {
|
||||
LoanContractActions.fetchLoanContract(potentialEmail);
|
||||
}
|
||||
},
|
||||
|
||||
getContractCheckbox() {
|
||||
@ -151,7 +155,7 @@ let LoanForm = React.createClass({
|
||||
<Property
|
||||
name='loanee'
|
||||
label={getLangText('Loanee Email')}
|
||||
onBlur={this.handleOnBlur}
|
||||
onChange={this.handleOnChange}
|
||||
editable={!this.props.email}
|
||||
overrideForm={!!this.props.email}>
|
||||
<input
|
||||
|
@ -20,6 +20,7 @@ import NavItemLink from 'react-router-bootstrap/lib/NavItemLink';
|
||||
|
||||
import HeaderNotificationDebug from './header_notification_debug';
|
||||
|
||||
import NavRoutesLinks from './nav_routes_links';
|
||||
|
||||
import { mergeOptions } from '../utils/general_utils';
|
||||
import { getLangText } from '../utils/lang_utils';
|
||||
@ -27,7 +28,8 @@ import { getLangText } from '../utils/lang_utils';
|
||||
|
||||
let Header = React.createClass({
|
||||
propTypes: {
|
||||
showAddWork: React.PropTypes.bool
|
||||
showAddWork: React.PropTypes.bool,
|
||||
routes: React.PropTypes.element
|
||||
},
|
||||
|
||||
mixins: [Router.State],
|
||||
@ -89,10 +91,9 @@ let Header = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let account = null;
|
||||
let signup = null;
|
||||
let collection = null;
|
||||
let addNewWork = null;
|
||||
let account;
|
||||
let signup;
|
||||
let navRoutesLinks;
|
||||
if (this.state.currentUser.username){
|
||||
account = (
|
||||
<DropdownButton eventKey="1" title={this.state.currentUser.username}>
|
||||
@ -101,9 +102,7 @@ let Header = React.createClass({
|
||||
<MenuItemLink eventKey="3" to="logout">{getLangText('Log out')}</MenuItemLink>
|
||||
</DropdownButton>
|
||||
);
|
||||
|
||||
collection = <NavItemLink to="pieces" query={this.getQuery()}>{getLangText('COLLECTION')}</NavItemLink>;
|
||||
addNewWork = this.props.showAddWork ? <NavItemLink to="register_piece" query={{'slide_num': 0}}>+ {getLangText('NEW WORK')}</NavItemLink> : null;
|
||||
navRoutesLinks = <NavRoutesLinks routes={this.props.routes} navbar right/>;
|
||||
}
|
||||
else {
|
||||
account = <NavItemLink to="login">{getLangText('LOGIN')}</NavItemLink>;
|
||||
@ -124,11 +123,10 @@ let Header = React.createClass({
|
||||
</Nav>
|
||||
<Nav navbar right>
|
||||
<HeaderNotificationDebug show={false}/>
|
||||
{addNewWork}
|
||||
{collection}
|
||||
{account}
|
||||
{signup}
|
||||
</Nav>
|
||||
{navRoutesLinks}
|
||||
</CollapsibleNav>
|
||||
</Navbar>
|
||||
</div>
|
||||
|
68
js/components/nav_routes_links.js
Normal file
68
js/components/nav_routes_links.js
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Nav from 'react-bootstrap/lib/Nav';
|
||||
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
||||
import MenuItemLink from 'react-router-bootstrap/lib/MenuItemLink';
|
||||
import NavItemLink from 'react-router-bootstrap/lib/NavItemLink';
|
||||
|
||||
import { sanitizeList } from '../utils/general_utils';
|
||||
|
||||
|
||||
let NavRoutesLinks = React.createClass({
|
||||
propTypes: {
|
||||
routes: React.PropTypes.element
|
||||
},
|
||||
|
||||
extractLinksFromRoutes(node, i) {
|
||||
if(!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
node = node.props;
|
||||
|
||||
let links = node.children.map((child, j) => {
|
||||
|
||||
// check if this a candidate for a link generation
|
||||
if(child.props.headerTitle && typeof child.props.headerTitle === 'string') {
|
||||
|
||||
// also check if it is a candidate for generating a dropdown menu
|
||||
if(child.props.children && child.props.children.length > 0) {
|
||||
return (
|
||||
<DropdownButton title={child.props.headerTitle} key={j}>
|
||||
{this.extractLinksFromRoutes(child, i++)}
|
||||
</DropdownButton>
|
||||
);
|
||||
} else if(i === 1) {
|
||||
// if the node's child is actually a node of level one (a child of a node), we're
|
||||
// returning a DropdownButton matching MenuItemLink
|
||||
return (
|
||||
<MenuItemLink to={child.props.name} key={j}>{child.props.headerTitle}</MenuItemLink>
|
||||
);
|
||||
} else if(i === 0) {
|
||||
return (
|
||||
<NavItemLink to={child.props.name} key={j}>{child.props.headerTitle}</NavItemLink>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// remove all nulls from the list of generated links
|
||||
return sanitizeList(links);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Nav {...this.props}>
|
||||
{this.extractLinksFromRoutes(this.props.routes, 0)}
|
||||
</Nav>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default NavRoutesLinks;
|
@ -69,7 +69,7 @@ let PieceList = React.createClass({
|
||||
},
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.redirectTo && this.state.pieceListCount === 0) {
|
||||
if (this.props.redirectTo && this.state.unfilteredPieceListCount === 0) {
|
||||
// FIXME: hack to redirect out of the dispatch cycle
|
||||
window.setTimeout(() => this.transitionTo(this.props.redirectTo), 0);
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
import App from './ascribe_app';
|
||||
import AppConstants from '../constants/application_constants';
|
||||
|
||||
let Route = Router.Route;
|
||||
let Redirect = Router.Redirect;
|
||||
let baseUrl = AppConstants.baseUrl;
|
||||
|
||||
|
||||
function getRoutes(commonRoutes) {
|
||||
return (
|
||||
<Route name="app" path={baseUrl} handler={App}>
|
||||
<Redirect from={baseUrl} to="login" />
|
||||
<Redirect from={baseUrl + '/'} to="login" />
|
||||
{commonRoutes}
|
||||
</Route>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default getRoutes;
|
@ -7,6 +7,8 @@ import Header from '../../header';
|
||||
import Footer from '../../footer';
|
||||
import GlobalNotification from '../../global_notification';
|
||||
|
||||
import getRoutes from './prize_routes';
|
||||
|
||||
let RouteHandler = Router.RouteHandler;
|
||||
|
||||
let PrizeApp = React.createClass({
|
||||
@ -14,10 +16,14 @@ let PrizeApp = React.createClass({
|
||||
|
||||
render() {
|
||||
let header = null;
|
||||
let subdomain = window.location.host.split('.')[0];
|
||||
|
||||
let ROUTES = getRoutes(null, subdomain);
|
||||
|
||||
if (this.isActive('landing') || this.isActive('login') || this.isActive('signup')) {
|
||||
header = <Hero />;
|
||||
} else {
|
||||
header = <Header showAddWork={false} />;
|
||||
header = <Header showAddWork={false} routes={ROUTES}/>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -29,8 +29,8 @@ function getRoutes() {
|
||||
<Route name="logout" path="logout" handler={LogoutContainer} />
|
||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
<Route name="register_piece" path="register_piece" handler={PrizeRegisterPiece} />
|
||||
<Route name="pieces" path="collection" handler={PrizePieceList} />
|
||||
<Route name="register_piece" path="register_piece" handler={PrizeRegisterPiece} headerTitle="+ NEW WORK" />
|
||||
<Route name="pieces" path="collection" handler={PrizePieceList} headerTitle="COLLECTION" />
|
||||
<Route name="piece" path="pieces/:pieceId" handler={PrizePieceContainer} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||
|
@ -216,6 +216,8 @@ let CylandRegisterPiece = React.createClass({
|
||||
gallery="Cyland Archive"
|
||||
startdate={today}
|
||||
enddate={datetimeWhenWeAllWillBeFlyingCoolHoverboardsAndDinosaursWillLiveAgain}
|
||||
showStartDate={false}
|
||||
showEndDate={false}
|
||||
showPersonalMessage={false}
|
||||
handleSuccess={this.handleLoanSuccess}>
|
||||
<Property
|
||||
|
@ -14,8 +14,11 @@ import GlobalNotificationModel from '../../../../../../models/global_notificatio
|
||||
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
|
||||
|
||||
import IkonotvSubmitButton from '../ascribe_buttons/ikonotv_submit_button';
|
||||
|
||||
import AclProxy from '../../../../../acl_proxy';
|
||||
|
||||
import AclButton from '../../../../../ascribe_buttons/acl_button';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../../utils/general_utils';
|
||||
|
||||
@ -66,9 +69,19 @@ let IkonotvAccordionListItem = React.createClass({
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_submit">
|
||||
<IkonotvSubmitButton
|
||||
className="pull-right"
|
||||
piece={this.props.content}
|
||||
handleSuccess={this.handleSubmitSuccess}/>
|
||||
className="btn-xs pull-right"
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
piece={this.props.content}/>
|
||||
</AclProxy>
|
||||
<AclProxy
|
||||
aclObject={this.props.content.acl}
|
||||
aclName="acl_submitted">
|
||||
<button
|
||||
disabled
|
||||
className="btn btn-default btn-xs pull-right">
|
||||
{getLangText('Loaned to IkonoTV')} <span className="glyphicon glyphicon-ok"
|
||||
aria-hidden="true"></span>
|
||||
</button>
|
||||
</AclProxy>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,10 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Moment from 'moment';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
|
||||
|
||||
import LoanForm from '../../../../../ascribe_forms/form_loan';
|
||||
|
||||
import Property from '../../../../../ascribe_forms/property';
|
||||
import InputCheckbox from '../../../../../ascribe_forms/input_checkbox';
|
||||
|
||||
import ApiUrls from '../../../../../../constants/api_urls';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
|
||||
let IkonotvSubmitButton = React.createClass({
|
||||
@ -17,23 +25,50 @@ let IkonotvSubmitButton = React.createClass({
|
||||
getSubmitButton() {
|
||||
return (
|
||||
<button
|
||||
className={classNames('btn', 'btn-default', 'btn-xs', this.props.className)}>
|
||||
className={classNames('btn', 'btn-default', this.props.className)}>
|
||||
{getLangText('Loan to IkonoTV')}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
render() {
|
||||
|
||||
let today = new Moment();
|
||||
let enddate = new Moment();
|
||||
enddate.add(1, 'years');
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
trigger={this.getSubmitButton()}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
title={getLangText('Loan to IkonoTV')}>
|
||||
|
||||
title={getLangText('Loan to IkonoTV archive')}>
|
||||
<LoanForm
|
||||
id={{piece_id: this.props.piece.id}}
|
||||
url={ApiUrls.ownership_loans_pieces}
|
||||
email="submissions@ikono.org"
|
||||
startdate={today}
|
||||
enddate={enddate}
|
||||
gallery="IkonoTV archive"
|
||||
showPersonalMessage={false}
|
||||
handleSuccess={this.props.handleSuccess}>
|
||||
<Property
|
||||
name="terms"
|
||||
className="ascribe-settings-property-collapsible-toggle"
|
||||
style={{paddingBottom: 0}}>
|
||||
<InputCheckbox>
|
||||
<span>
|
||||
{' ' + getLangText('I agree to the Terms of Service of IkonoTV Archive') + ' '}
|
||||
(<a href="https://d1qjsxua1o9x03.cloudfront.net/live/743394beff4b1282ba735e5e3723ed74/contract/bbc92f1d-4504-49f8-818c-8dd7113c6e06.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
|
||||
{getLangText('read')}
|
||||
</a>)
|
||||
</span>
|
||||
</InputCheckbox>
|
||||
</Property>
|
||||
</LoanForm>
|
||||
</ModalWrapper>
|
||||
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default IkonotvSubmitButton;
|
||||
export default IkonotvSubmitButton;
|
||||
|
@ -0,0 +1,176 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import PieceActions from '../../../../../../actions/piece_actions';
|
||||
import PieceStore from '../../../../../../stores/piece_store';
|
||||
|
||||
import PieceListActions from '../../../../../../actions/piece_list_actions';
|
||||
import PieceListStore from '../../../../../../stores/piece_list_store';
|
||||
|
||||
import UserStore from '../../../../../../stores/user_store';
|
||||
|
||||
import Piece from '../../../../../../components/ascribe_detail/piece';
|
||||
|
||||
import ListRequestActions from '../../../../../ascribe_forms/list_form_request_actions';
|
||||
import AclButtonList from '../../../../../ascribe_buttons/acl_button_list';
|
||||
import DeleteButton from '../../../../../ascribe_buttons/delete_button';
|
||||
|
||||
import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph';
|
||||
|
||||
import IkonotvSubmitButton from '../ascribe_buttons/ikonotv_submit_button';
|
||||
|
||||
import HistoryIterator from '../../../../../ascribe_detail/history_iterator';
|
||||
|
||||
import DetailProperty from '../../../../../ascribe_detail/detail_property';
|
||||
|
||||
|
||||
import GlobalNotificationModel from '../../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
|
||||
|
||||
import AclProxy from '../../../../../acl_proxy';
|
||||
|
||||
import AppConstants from '../../../../../../constants/application_constants';
|
||||
|
||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../../utils/general_utils';
|
||||
|
||||
|
||||
let IkonotvPieceContainer = React.createClass({
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
PieceStore.getState(),
|
||||
UserStore.getState(),
|
||||
PieceListStore.getState()
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
PieceStore.listen(this.onChange);
|
||||
PieceActions.fetchOne(this.props.params.pieceId);
|
||||
UserStore.listen(this.onChange);
|
||||
PieceListStore.listen(this.onChange);
|
||||
},
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(this.props.params.pieceId !== nextProps.params.pieceId) {
|
||||
PieceActions.updatePiece({});
|
||||
PieceActions.fetchOne(nextProps.params.pieceId);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
// Every time we're leaving the piece detail page,
|
||||
// just reset the piece that is saved in the piece store
|
||||
// as it will otherwise display wrong/old data once the user loads
|
||||
// the piece detail a second time
|
||||
PieceActions.updatePiece({});
|
||||
PieceStore.unlisten(this.onChange);
|
||||
UserStore.unlisten(this.onChange);
|
||||
PieceListStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
loadPiece() {
|
||||
PieceActions.fetchOne(this.props.params.pieceId);
|
||||
},
|
||||
|
||||
handleSubmitSuccess(response) {
|
||||
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
|
||||
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
|
||||
|
||||
this.loadPiece();
|
||||
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
getActions(){
|
||||
if (this.state.piece &&
|
||||
this.state.piece.request_action &&
|
||||
this.state.piece.request_action.length > 0) {
|
||||
return (
|
||||
<ListRequestActions
|
||||
pieceOrEditions={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
handleSuccess={this.loadPiece}
|
||||
requestActions={this.state.piece.request_action}/>
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
//We need to disable the normal acl_loan because we're inserting a custom acl_loan button
|
||||
let availableAcls;
|
||||
|
||||
if(this.state.piece && this.state.piece.acl && typeof this.state.piece.acl.acl_loan !== 'undefined') {
|
||||
// make a copy to not have side effects
|
||||
availableAcls = mergeOptions({}, this.state.piece.acl);
|
||||
availableAcls.acl_loan = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<AclButtonList
|
||||
className="text-center ascribe-button-list"
|
||||
availableAcls={availableAcls}
|
||||
editions={this.state.piece}
|
||||
handleSuccess={this.loadPiece}>
|
||||
<AclProxy
|
||||
aclObject={availableAcls}
|
||||
aclName="acl_submit">
|
||||
<IkonotvSubmitButton
|
||||
className="btn-sm"
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
piece={this.state.piece}/>
|
||||
</AclProxy>
|
||||
<DeleteButton
|
||||
handleSuccess={this.handleDeleteSuccess}
|
||||
piece={this.state.piece}/>
|
||||
</AclButtonList>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
if('title' in this.state.piece) {
|
||||
return (
|
||||
<Piece
|
||||
piece={this.state.piece}
|
||||
loadPiece={this.loadPiece}
|
||||
header={
|
||||
<div className="ascribe-detail-header">
|
||||
<hr style={{marginTop: 0}}/>
|
||||
<h1 className="ascribe-detail-title">{this.state.piece.title}</h1>
|
||||
<DetailProperty label="BY" value={this.state.piece.artist_name} />
|
||||
<DetailProperty label="DATE" value={ this.state.piece.date_created.slice(0, 4) } />
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
subheader={
|
||||
<div className="ascribe-detail-header">
|
||||
<DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } />
|
||||
<DetailProperty label={getLangText('ID')} value={ this.state.piece.bitcoin_id } ellipsis={true} />
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
buttons={this.getActions()}>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Loan History')}
|
||||
show={this.state.piece.loan_history && this.state.piece.loan_history.length > 0}>
|
||||
<HistoryIterator
|
||||
history={this.state.piece.loan_history} />
|
||||
</CollapsibleParagraph>
|
||||
</Piece>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="fullpage-spinner">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default IkonotvPieceContainer;
|
@ -1,80 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
|
||||
import WhitelabelStore from '../../../../../stores/whitelabel_store';
|
||||
|
||||
import PieceListStore from '../../../../../stores/piece_list_store';
|
||||
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 PieceActions from '../../../../../actions/piece_actions';
|
||||
|
||||
import ContractForm from './ascribe_forms/ikonotv_contract_form';
|
||||
import RegisterPieceForm from '../../../../../components/ascribe_forms/form_register_piece';
|
||||
import Property from '../../../../../components/ascribe_forms/property';
|
||||
import InputCheckbox from '../../../../../components/ascribe_forms/input_checkbox';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
||||
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||
|
||||
|
||||
let IkonotvRegisterPiece = React.createClass({
|
||||
|
||||
mixins: [Router.Navigation],
|
||||
|
||||
getInitialState(){
|
||||
return mergeOptions(
|
||||
UserStore.getState(),
|
||||
WhitelabelStore.getState());
|
||||
},
|
||||
|
||||
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() {
|
||||
if (this.state.currentUser &&
|
||||
this.state.whitelabel &&
|
||||
this.state.whitelabel.user &&
|
||||
this.state.currentUser.email === this.state.whitelabel.user){
|
||||
return (
|
||||
<ContractForm />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="ascribe-form-bordered ascribe-form-wrapper">
|
||||
<RegisterPieceForm
|
||||
enableLocalHashing={false}
|
||||
headerMessage={getLangText('Register your work')}
|
||||
submitMessage={getLangText('Register')}
|
||||
handleSuccess={this.handleRegisterSuccess}/>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default IkonotvRegisterPiece;
|
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import ContractForm from '../../../../../components/ascribe_forms/contract_form';
|
||||
|
||||
|
||||
let IkonotvRequestLoan = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<ContractForm />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default IkonotvRequestLoan;
|
@ -7,21 +7,26 @@ import Footer from '../../footer';
|
||||
|
||||
import GlobalNotification from '../../global_notification';
|
||||
|
||||
import getRoutes from './wallet_routes';
|
||||
|
||||
let RouteHandler = Router.RouteHandler;
|
||||
|
||||
let WalletApp = React.createClass({
|
||||
mixins: [Router.State],
|
||||
|
||||
render() {
|
||||
let header = null;
|
||||
let subdomain = window.location.host.split('.')[0];
|
||||
let ROUTES = getRoutes(null, subdomain);
|
||||
|
||||
let header = null;
|
||||
if ((this.isActive('landing') || this.isActive('login') || this.isActive('signup'))
|
||||
&& (['ikonotv', 'cyland']).indexOf(subdomain) > -1) {
|
||||
header = (
|
||||
<div className="hero"/>);
|
||||
} else {
|
||||
header = <Header showAddWork={true} />;
|
||||
header = <Header showAddWork={true} routes={ROUTES} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container ascribe-prize-app">
|
||||
{header}
|
||||
|
@ -12,15 +12,16 @@ import PieceList from '../../../components/piece_list';
|
||||
import PieceContainer from '../../../components/ascribe_detail/piece_container';
|
||||
import EditionContainer from '../../../components/ascribe_detail/edition_container';
|
||||
import SettingsContainer from '../../../components/settings_container';
|
||||
import RegisterPiece from '../../../components/register_piece';
|
||||
|
||||
// specific components
|
||||
import CylandLanding from './components/cyland/cyland_landing';
|
||||
import CylandPieceContainer from './components/cyland/ascribe_detail/cyland_piece_container';
|
||||
import CylandRegisterPiece from './components/cyland/cyland_register_piece';
|
||||
import CylandPieceList from './components/cyland/cyland_piece_list';
|
||||
|
||||
import IkonotvPieceList from './components/ikonotv/ikonotv_piece_list';
|
||||
import IkonotvRegisterPiece from './components/ikonotv/ikonotv_register_piece';
|
||||
import IkonotvRequestLoan from './components/ikonotv/ikonotv_request_loan';
|
||||
import IkonotvPieceContainer from './components/ikonotv/ascribe_detail/ikonotv_piece_container';
|
||||
|
||||
import CCRegisterPiece from './components/cc/cc_register_piece';
|
||||
|
||||
@ -40,8 +41,8 @@ let ROUTES = {
|
||||
<Route name="logout" path="logout" handler={LogoutContainer} />
|
||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
<Route name="register_piece" path="register_piece" handler={CylandRegisterPiece} />
|
||||
<Route name="pieces" path="collection" handler={CylandPieceList} />
|
||||
<Route name="register_piece" path="register_piece" handler={CylandRegisterPiece} headerTitle="+ NEW WORK" />
|
||||
<Route name="pieces" path="collection" handler={CylandPieceList} headerTitle="COLLECTION" />
|
||||
<Route name="piece" path="pieces/:pieceId" handler={CylandPieceContainer} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||
@ -55,8 +56,8 @@ let ROUTES = {
|
||||
<Route name="logout" path="logout" handler={LogoutContainer} />
|
||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
<Route name="register_piece" path="register_piece" handler={CCRegisterPiece} />
|
||||
<Route name="pieces" path="collection" handler={PieceList} />
|
||||
<Route name="register_piece" path="register_piece" handler={CCRegisterPiece} headerTitle="+ NEW WORK" />
|
||||
<Route name="pieces" path="collection" handler={PieceList} headerTitle="COLLECTION" />
|
||||
<Route name="piece" path="pieces/:pieceId" handler={PieceContainer} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||
@ -64,14 +65,16 @@ let ROUTES = {
|
||||
),
|
||||
'ikonotv': (
|
||||
<Route name="app" path={baseUrl} handler={WalletApp}>
|
||||
<Route name="landing" path={baseUrl} handler={IkonotvRegisterPiece} />
|
||||
<Redirect from={baseUrl} to="login" />
|
||||
<Redirect from={baseUrl + '/'} to="login" />
|
||||
<Route name="login" path="login" handler={LoginContainer} />
|
||||
<Route name="logout" path="logout" handler={LogoutContainer} />
|
||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
<Route name="register_piece" path="register_piece" handler={IkonotvRegisterPiece} />
|
||||
<Route name="pieces" path="collection" handler={IkonotvPieceList} />
|
||||
<Route name="piece" path="pieces/:pieceId" handler={CylandPieceContainer} />
|
||||
<Route name="request_loan" path="request_loan" handler={IkonotvRequestLoan}/>
|
||||
<Route name="register_piece" path="register_piece" handler={RegisterPiece} headerTitle="+ NEW WORK"/>
|
||||
<Route name="pieces" path="collection" handler={IkonotvPieceList} headerTitle="COLLECTION"/>
|
||||
<Route name="piece" path="pieces/:pieceId" handler={IkonotvPieceContainer} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||
</Route>
|
||||
|
17
js/routes.js
17
js/routes.js
@ -5,7 +5,8 @@ import Router from 'react-router';
|
||||
|
||||
import getPrizeRoutes from './components/whitelabel/prize/prize_routes';
|
||||
import getWalletRoutes from './components/whitelabel/wallet/wallet_routes';
|
||||
import getDefaultRoutes from './components/routes';
|
||||
|
||||
import App from './components/ascribe_app';
|
||||
|
||||
import PieceList from './components/piece_list';
|
||||
import PieceContainer from './components/ascribe_detail/piece_container';
|
||||
@ -23,19 +24,25 @@ import RegisterPiece from './components/register_piece';
|
||||
|
||||
import PrizesDashboard from './components/ascribe_prizes_dashboard/prizes_dashboard';
|
||||
|
||||
import AppConstants from './constants/application_constants';
|
||||
|
||||
let Route = Router.Route;
|
||||
let Redirect = Router.Redirect;
|
||||
let baseUrl = AppConstants.baseUrl;
|
||||
|
||||
|
||||
const COMMON_ROUTES = (
|
||||
<Route>
|
||||
<Route name="app" path={baseUrl} handler={App}>
|
||||
<Redirect from={baseUrl} to="login" />
|
||||
<Redirect from={baseUrl + '/'} to="login" />
|
||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||
<Route name="login" path="login" handler={LoginContainer} />
|
||||
<Route name="logout" path="logout" handler={LogoutContainer} />
|
||||
<Route name="pieces" path="collection" handler={PieceList} />
|
||||
<Route name="register_piece" path="register_piece" handler={RegisterPiece} headerTitle="+ NEW WORK" />
|
||||
<Route name="pieces" path="collection" handler={PieceList} headerTitle="COLLECTION" />
|
||||
<Route name="piece" path="pieces/:pieceId" handler={PieceContainer} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
|
||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||
<Route name="coa_verify" path="verify" handler={CoaVerifyContainer} />
|
||||
<Route name="prizes" path="prizes" handler={PrizesDashboard} />
|
||||
@ -51,7 +58,7 @@ function getRoutes(type, subdomain) {
|
||||
} else if(type === 'wallet') {
|
||||
routes = getWalletRoutes(COMMON_ROUTES, subdomain);
|
||||
} else {
|
||||
routes = getDefaultRoutes(COMMON_ROUTES);
|
||||
routes = COMMON_ROUTES;
|
||||
}
|
||||
|
||||
return routes;
|
||||
|
@ -21,6 +21,7 @@ class PieceListStore {
|
||||
this.pieceList = [];
|
||||
// -1 specifies that the store is currently loading
|
||||
this.pieceListCount = -1;
|
||||
this.unfilteredPieceListCount = -1;
|
||||
this.page = 1;
|
||||
this.pageSize = 10;
|
||||
this.search = '';
|
||||
@ -30,13 +31,14 @@ class PieceListStore {
|
||||
this.bindActions(PieceListActions);
|
||||
}
|
||||
|
||||
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount, filterBy }) {
|
||||
onUpdatePieceList({ page, pageSize, search, pieceList, orderBy, orderAsc, pieceListCount, unfilteredPieceListCount, filterBy }) {
|
||||
this.page = page;
|
||||
this.pageSize = pageSize;
|
||||
this.search = search;
|
||||
this.orderAsc = orderAsc;
|
||||
this.orderBy = orderBy;
|
||||
this.pieceListCount = pieceListCount;
|
||||
this.unfilteredPieceListCount = unfilteredPieceListCount;
|
||||
this.filterBy = filterBy;
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,23 @@ export function sanitize(obj, filterFn) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all falsy values (undefined, null, false, ...) from a list/array
|
||||
* @param {array} l the array to sanitize
|
||||
* @return {array} the sanitized array
|
||||
*/
|
||||
export function sanitizeList(l) {
|
||||
let sanitizedList = [];
|
||||
|
||||
for(let i = 0; i < l.length; i++) {
|
||||
if(l[i]) {
|
||||
sanitizedList.push(l[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return sanitizedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums up a list of numbers. Like a Epsilon-math-kinda-sum...
|
||||
*/
|
||||
|
@ -69,6 +69,7 @@
|
||||
"lodash": "^3.9.3",
|
||||
"moment": "^2.10.6",
|
||||
"object-assign": "^2.0.0",
|
||||
"opn": "^3.0.2",
|
||||
"q": "^1.4.1",
|
||||
"raven-js": "^1.1.19",
|
||||
"react": "^0.13.2",
|
||||
|
@ -115,9 +115,7 @@ hr {
|
||||
|
||||
|
||||
.img-brand {
|
||||
height: 45px;
|
||||
margin: 5px 0;
|
||||
padding: 0;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
@ -381,7 +379,7 @@ hr {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.margin-left-2px{
|
||||
.margin-left-2px {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user