1
0
mirror of https://github.com/ascribe/onion.git synced 2024-11-15 09:35:10 +01:00

Merge branch 'tmp2' into AD-613-cyland-white-label-page

This commit is contained in:
diminator 2015-08-19 16:46:07 +02:00
commit b66fdc332d
16 changed files with 612 additions and 4 deletions

View File

@ -0,0 +1,27 @@
'use strict';
import alt from '../alt';
import OwnershipFetcher from '../fetchers/ownership_fetcher';
class LoanContractListActions {
constructor() {
this.generateActions(
'updateLoanContractList',
'flushLoanContractList'
);
}
fetchLoanContractList() {
OwnershipFetcher.fetchLoanContractList()
.then((contracts) => {
this.actions.updateLoanContractList(contracts);
})
.catch((err) => {
console.logGlobal(err);
this.actions.updateLoanContractList([]);
});
}
}
export default alt.createActions(LoanContractListActions);

View File

@ -74,6 +74,7 @@ let PasswordRequestResetForm = React.createClass({
return ( return (
<Form <Form
ref="form" ref="form"
className='ascribe-form-wrapper'
url={ApiUrls.users_password_reset_request} url={ApiUrls.users_password_reset_request}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
buttons={ buttons={
@ -130,6 +131,7 @@ let PasswordResetForm = React.createClass({
return ( return (
<Form <Form
ref="form" ref="form"
className='ascribe-form-wrapper'
url={ApiUrls.users_password_reset} url={ApiUrls.users_password_reset}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
getFormData={this.getFormData} getFormData={this.getFormData}

View File

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

View File

@ -0,0 +1,92 @@
'use strict';
import React from 'react';
import Router from 'react-router';
import WhitelabelActions from '../../../../../actions/whitelabel_actions';
import WhitelabelStore from '../../../../../stores/whitelabel_store';
import ButtonLink from 'react-router-bootstrap/lib/ButtonLink';
import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
import UserStore from '../../../../../stores/user_store';
import UserActions from '../../../../../actions/user_actions';
import { mergeOptions } from '../../../../../utils/general_utils';
let CylandLanding = React.createClass({
mixins: [Router.Navigation],
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.replaceWith('pieces'), 0);
}
},
render() {
return (
<div className="container ascribe-form-wrapper">
<div className="row">
<div className="col-xs-12 wp-landing-wrapper">
<div className="row" style={{border: '1px solid #CCC', padding: '2em'}}>
<img src={this.state.whitelabel.logo} width="400px"/>
<div style={{marginTop: '1em'}}>
Submissions to Cyland Archive are powered by
<span>
<span> ascribe </span>
<span className="glyph-ascribe-spool-chunked ascribe-color"></span>
</span>
</div>
</div>
<div className="row" style={{border: '1px solid #CCC', borderTop: 'none', padding: '2em'}}>
<div className="col-sm-6">
<p>
Existing ascribe user?
</p>
<ButtonLink to="login">
Log in
</ButtonLink>
</div>
<div className="col-sm-6">
<p>
Do you need an account?
</p>
<ButtonLink to="signup">
Sign up
</ButtonLink>
</div>
</div>
</div>
</div>
</div>
);
}
});
export default CylandLanding;

View File

@ -0,0 +1,93 @@
'use strict';
import React from 'react';
import Router from 'react-router';
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
import PieceListActions from '../../../../../../actions/piece_list_actions';
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 '../ascribe_buttons/ikonotv_submit_button';
import AclProxy from '../../../../../acl_proxy';
import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
let IkonotvAccordionListItem = React.createClass({
propTypes: {
className: React.PropTypes.string,
content: React.PropTypes.object,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
])
},
getInitialState() {
return mergeOptions(
PieceListStore.getState(),
UserStore.getState()
);
},
componentDidMount() {
PieceListStore.listen(this.onChange);
UserStore.listen(this.onChange);
},
componentWillUnmount() {
PieceListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
handleSubmitSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search,
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
},
getSubmitButtons() {
return (
<div>
<AclProxy
aclObject={this.props.content.acl}
aclName="acl_submit">
<IkonotvSubmitButton
className="pull-right"
piece={this.props.content}
handleSuccess={this.handleSubmitSuccess}/>
</AclProxy>
</div>
);
},
render() {
return (
<AccordionListItemPiece
className={this.props.className}
piece={this.props.content}
subsubheading={
<div className="pull-left">
<span>{this.props.content.date_created.split('-')[0]}</span>
</div>}
buttons={this.getSubmitButtons()}>
{this.props.children}
</AccordionListItemPiece>
);
}
});
export default IkonotvAccordionListItem;

View File

@ -0,0 +1,39 @@
'use strict';
import React from 'react';
import classNames from 'classnames';
import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper';
import { getLangText } from '../../../../../../utils/lang_utils';
let IkonotvSubmitButton = React.createClass({
propTypes: {
className: React.PropTypes.string,
handleSuccess: React.PropTypes.func,
piece: React.PropTypes.object.isRequired
},
getSubmitButton() {
return (
<button
className={classNames('btn', 'btn-default', 'btn-xs', this.props.className)}>
{getLangText('Loan to IkonoTV')}
</button>
);
},
render() {
return (
<ModalWrapper
trigger={this.getSubmitButton()}
handleSuccess={this.props.handleSuccess}
title={getLangText('Loan to IkonoTV')}>
</ModalWrapper>
);
}
});
export default IkonotvSubmitButton;

View File

@ -0,0 +1,135 @@
'use strict';
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 GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
import Form from '../../../../../ascribe_forms/form';
import ApiUrls from '../../../../../../constants/api_urls';
import { getLangText } from '../../../../../../utils/lang_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
let ContractForm = React.createClass({
getInitialState() {
return mergeOptions(
LoanContractListStore.getState(),
{
selectedContract: 0
}
);
},
componentDidMount() {
LoanContractListStore.listen(this.onChange);
LoanContractListActions.fetchLoanContractList();
},
componentWillUnmount() {
LoanContractListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
onContractChange(event){
this.setState({selectedContract: event.target.selectedIndex});
},
handleSubmitSuccess(response) {
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
},
getContracts() {
if (this.state.contractList && this.state.contractList.length > 0) {
return (
<Property
name='contract'
label={getLangText('Contract Type')}
onChange={this.onContractChange}
footer={
<a
className="pull-right"
href={this.state.contractList[this.state.selectedContract].s3UrlSafe}
target="_blank">
{getLangText('Learn more')}
</a>
}>
<select name="contract">
{this.state.contractList.map((contract, i) => {
return (
<option
name={i}
key={i}
value={ contract.name }>
{ contract.name }
</option>
);
})}
</select>
</Property>);
}
return null;
},
render() {
return (
<Form
className="ascribe-form-bordered ascribe-form-wrapper"
ref='form'
url={ApiUrls.ownership_loans_contract}
handleSuccess={this.props.handleSuccess}
buttons={<button
type="submit"
className="btn ascribe-btn ascribe-btn-login">
SEND LOAN REQUEST
</button>}
spinner={
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
</span>
}>
<div className="ascribe-form-header">
<h3>CONTRACT FORM</h3>
</div>
<Property
name='artist_name'
label={getLangText('Artist Name')}>
<input
type="text"
placeholder="(e.g. Andy Warhol)"
required/>
</Property>
<Property
name='artist_email'
label={getLangText('Artist Email')}>
<input
type="email"
placeholder="(e.g. andy@warhol.co.uk)"
required/>
</Property>
{this.getContracts()}
<Property
name='appendix'
label={getLangText('Appendix')}>
<input
type="text"
placeholder="Add an appendix to the contract"
required/>
</Property>
</Form>
);
}
});
export default ContractForm;

View File

@ -0,0 +1,42 @@
'use strict';
import React from 'react';
import PieceList from '../../../../piece_list';
import UserActions from '../../../../../actions/user_actions';
import UserStore from '../../../../../stores/user_store';
import IkonotvAccordionListItem from './ascribe_accordion_list/ikonotv_accordion_list_item';
let IkonotvPieceList = React.createClass({
getInitialState() {
return UserStore.getState();
},
componentDidMount() {
UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
},
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() {
return (
<div>
<PieceList
redirectTo="register_piece"
accordionListItemType={IkonotvAccordionListItem}
/>
</div>
);
}
});
export default IkonotvPieceList;

View File

@ -0,0 +1,80 @@
'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;

View File

@ -10,6 +10,12 @@ function getWalletApiUrls(subdomain) {
'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/' 'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/'
}; };
} }
else if (subdomain === 'ikonotv'){
return {
'pieces_list': walletConstants.walletApiEndpoint + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + subdomain + '/pieces/${piece_id}/'
};
}
return {}; return {};
} }

View File

@ -10,10 +10,19 @@ import GlobalNotification from '../../global_notification';
let RouteHandler = Router.RouteHandler; let RouteHandler = Router.RouteHandler;
let WalletApp = React.createClass({ let WalletApp = React.createClass({
mixins: [Router.State],
render() { render() {
let header = null;
if (this.isActive('landing') || this.isActive('login') || this.isActive('signup')) {
header = (
<div className="hero"/>);
} else {
header = <Header showAddWork={false} />;
}
return ( return (
<div className="container ascribe-prize-app"> <div className="container ascribe-prize-app">
<Header /> {header}
<RouteHandler /> <RouteHandler />
<GlobalNotification /> <GlobalNotification />
<div id="modal" className="container"></div> <div id="modal" className="container"></div>

View File

@ -14,10 +14,14 @@ import EditionContainer from '../../../components/ascribe_detail/edition_contain
import SettingsContainer from '../../../components/settings_container'; import SettingsContainer from '../../../components/settings_container';
// specific components // specific components
import CylandLanding from './components/cyland/cyland_landing';
import CylandPieceContainer from './components/cyland/ascribe_detail/cyland_piece_container'; import CylandPieceContainer from './components/cyland/ascribe_detail/cyland_piece_container';
import CylandRegisterPiece from './components/cyland/cyland_register_piece'; import CylandRegisterPiece from './components/cyland/cyland_register_piece';
import CylandPieceList from './components/cyland/cyland_piece_list'; 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 CCRegisterPiece from './components/cc/cc_register_piece'; import CCRegisterPiece from './components/cc/cc_register_piece';
import WalletApp from './wallet_app'; import WalletApp from './wallet_app';
@ -31,7 +35,7 @@ let baseUrl = AppConstants.baseUrl;
let ROUTES = { let ROUTES = {
'cyland': ( 'cyland': (
<Route name="app" path={baseUrl} handler={WalletApp}> <Route name="app" path={baseUrl} handler={WalletApp}>
<Route name="landing" path={baseUrl} handler={CylandRegisterPiece} /> <Route name="landing" path={baseUrl} handler={CylandLanding} />
<Route name="login" path="login" handler={LoginContainer} /> <Route name="login" path="login" handler={LoginContainer} />
<Route name="logout" path="logout" handler={LogoutContainer} /> <Route name="logout" path="logout" handler={LogoutContainer} />
<Route name="signup" path="signup" handler={SignupContainer} /> <Route name="signup" path="signup" handler={SignupContainer} />
@ -57,6 +61,20 @@ let ROUTES = {
<Route name="edition" path="editions/:editionId" handler={EditionContainer} /> <Route name="edition" path="editions/:editionId" handler={EditionContainer} />
<Route name="settings" path="settings" handler={SettingsContainer} /> <Route name="settings" path="settings" handler={SettingsContainer} />
</Route> </Route>
),
'ikonotv': (
<Route name="app" path={baseUrl} handler={WalletApp}>
<Route name="landing" path={baseUrl} handler={IkonotvRegisterPiece} />
<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="edition" path="editions/:editionId" handler={EditionContainer} />
<Route name="settings" path="settings" handler={SettingsContainer} />
</Route>
) )
}; };

View File

@ -39,6 +39,13 @@ let constants = {
'logo': 'https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/cyland/logo.gif', 'logo': 'https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/cyland/logo.gif',
'permissions': ['register', 'edit', 'share', 'del_from_collection'], 'permissions': ['register', 'edit', 'share', 'del_from_collection'],
'type': 'wallet' 'type': 'wallet'
},
{
'subdomain': 'ikonotv',
'name': 'IkonoTV',
'logo': 'https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/ikonotv/ikono-logo-black.png',
'permissions': ['register', 'edit', 'share', 'del_from_collection'],
'type': 'wallet'
} }
], ],
'defaultDomain': { 'defaultDomain': {

View File

@ -6,12 +6,19 @@ import ApiUrls from '../constants/api_urls';
let OwnershipFetcher = { let OwnershipFetcher = {
/** /**
* Fetch one user from the API. * Fetch the default, public loan contract of a user from the API.
* If no arg is supplied, load the current user
*/ */
fetchLoanContract(email) { fetchLoanContract(email) {
return requests.get(ApiUrls.ownership_loans_contract + '?loanee=' + email); return requests.get(ApiUrls.ownership_loans_contract + '?loanee=' + email);
},
/**
* Fetch the contracts of the logged-in user from the API.
*/
fetchLoanContractList(){
return requests.get(ApiUrls.ownership_loans_contract);
} }
}; };
export default OwnershipFetcher; export default OwnershipFetcher;

View File

@ -0,0 +1,22 @@
'use strict';
import alt from '../alt';
import LoanContractListActions from '../actions/loan_contract_list_actions';
class LoanContractListStore {
constructor() {
this.contractList = [];
this.bindActions(LoanContractListActions);
}
onUpdateLoanContractList(contractList) {
this.contractList = contractList;
}
onFlushLoanContractList() {
this.contractList = [];
}
}
export default alt.createStore(LoanContractListStore, 'LoanContractListStore');

View File

@ -14,3 +14,12 @@
margin-bottom: 0; margin-bottom: 0;
color: #616161; color: #616161;
} }
.ascribe-form-wrapper {
width: 80%;
margin: 0 auto;
max-width: 600px;
@media (max-width: 550px) {
width: 100%;
}
}