1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 17:33:14 +01:00

Merge branch 'master' into AD-565-add-landing-page-for-sluice

Conflicts:
	js/components/login_container.js
This commit is contained in:
vrde 2015-07-13 17:19:23 +02:00
commit b4f1d3d6cc
14 changed files with 100 additions and 141 deletions

View File

@ -9,7 +9,6 @@ class PieceListActions {
this.generateActions( this.generateActions(
'updatePieceList', 'updatePieceList',
'updatePieceListRequestActions', 'updatePieceListRequestActions',
'addFirstEditionToPiece',
'updatePropertyForPiece' 'updatePropertyForPiece'
); );
} }
@ -41,17 +40,6 @@ class PieceListActions {
this.actions.updatePieceListRequestActions(res.piece_ids); this.actions.updatePieceListRequestActions(res.piece_ids);
}); });
} }
fetchFirstEditionForPiece(pieceId) {
return new Promise((resolve, reject) => {
PieceListFetcher.fetchFirstEditionForPiece(pieceId)
.then((firstEdition) => {
this.actions.addFirstEditionToPiece({pieceId, firstEdition});
resolve();
})
.catch((err) => reject(err));
});
}
} }
export default alt.createActions(PieceListActions); export default alt.createActions(PieceListActions);

View File

@ -31,17 +31,20 @@ let AccordionListItem = React.createClass({
getInitialState() { getInitialState() {
return { return {
showCreateEditionsDialog: false, showCreateEditionsDialog: false
creatingEditions: false
}; };
}, },
componentDidMount() { componentDidUpdate() {
if(this.props.content.num_editions > 0) { if(this.props.content.num_editions === 0 && typeof this.state.pollingIntervalIndex === 'undefined') {
PieceListActions.fetchFirstEditionForPiece(this.props.content.id); this.startPolling();
} }
}, },
componentWillUnmount() {
clearInterval(this.state.pollingIntervalIndex);
},
onChange(state) { onChange(state) {
this.setState(state); this.setState(state);
}, },
@ -68,22 +71,16 @@ let AccordionListItem = React.createClass({
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
PieceListActions.updatePropertyForPiece({pieceId: this.props.content.id, key: 'num_editions', value: 0}); PieceListActions.updatePropertyForPiece({pieceId: this.props.content.id, key: 'num_editions', value: 0});
this.setState({
creatingEditions: true
});
this.toggleCreateEditionsDialog(); this.toggleCreateEditionsDialog();
},
startPolling() {
// start polling until editions are defined // start polling until editions are defined
let pollingIntervalIndex = setInterval(() => { let pollingIntervalIndex = setInterval(() => {
EditionListActions.fetchEditionList(this.props.content.id) EditionListActions.fetchEditionList(this.props.content.id)
.then((res) => { .then((res) => {
clearInterval(this.state.pollingIntervalIndex); clearInterval(this.state.pollingIntervalIndex);
this.setState({
creatingEditions: false
});
PieceListActions.updatePropertyForPiece({ PieceListActions.updatePropertyForPiece({
pieceId: this.props.content.id, pieceId: this.props.content.id,
@ -118,7 +115,7 @@ let AccordionListItem = React.createClass({
linkData = { linkData = {
to: 'edition', to: 'edition',
params: { params: {
editionId: this.props.content.firstEdition ? this.props.content.firstEdition.bitcoin_id : 0 editionId: this.props.content.first_edition ? this.props.content.first_edition.bitcoin_id : 0
} }
}; };
} }
@ -140,16 +137,11 @@ let AccordionListItem = React.createClass({
</Link> </Link>
<h3>{getLangText('by %s', this.props.content.artist_name)}</h3> <h3>{getLangText('by %s', this.props.content.artist_name)}</h3>
<div> <div>
<span>{this.props.content.date_created.split('-')[0]}</span> <span className="pull-left">{this.props.content.date_created.split('-')[0]}</span>
</div>
<div>
<AccordionListItemEditionWidget <AccordionListItemEditionWidget
className="pull-right"
piece={this.props.content} piece={this.props.content}
toggleCreateEditionsDialog={this.toggleCreateEditionsDialog} toggleCreateEditionsDialog={this.toggleCreateEditionsDialog}/>
creatingEditions={this.state.creatingEditions}/>
{/* <a href={this.props.content.license_type.url} target="_blank" className="pull-right">
{getLangText('%s license', this.props.content.license_type.code)}
</a> */}
</div> </div>
</div> </div>
<span style={{'clear': 'both'}}></span> <span style={{'clear': 'both'}}></span>

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import classNames from 'classnames';
import EditionListActions from '../../actions/edition_list_actions'; import EditionListActions from '../../actions/edition_list_actions';
import EditionListStore from '../../stores/edition_list_store'; import EditionListStore from '../../stores/edition_list_store';
@ -9,9 +10,9 @@ import { getLangText } from '../../utils/lang_utils';
let AccordionListItemEditionWidget = React.createClass({ let AccordionListItemEditionWidget = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string,
piece: React.PropTypes.object.isRequired, piece: React.PropTypes.object.isRequired,
toggleCreateEditionsDialog: React.PropTypes.func.isRequired, toggleCreateEditionsDialog: React.PropTypes.func.isRequired
creatingEditions: React.PropTypes.bool.isRequired
}, },
getInitialState() { getInitialState() {
@ -56,7 +57,7 @@ let AccordionListItemEditionWidget = React.createClass({
if(isEditionListOpen) { if(isEditionListOpen) {
if(typeof this.state.editionList[pieceId] === 'undefined') { if(typeof this.state.editionList[pieceId] === 'undefined') {
return ( return (
<span className="glyph-ascribe-spool-chunked ascribe-color spin"/> <span className="glyph-ascribe-spool-chunked spin"/>
); );
} else { } else {
return ( return (
@ -77,38 +78,38 @@ let AccordionListItemEditionWidget = React.createClass({
if(numEditions === -1) { if(numEditions === -1) {
return ( return (
<span <button
onClick={this.props.toggleCreateEditionsDialog} onClick={this.props.toggleCreateEditionsDialog}
className="ascribe-accordion-list-item-edition-widget"> className={classNames('btn', 'btn-default', 'btn-xs', 'ascribe-accordion-list-item-edition-widget', this.props.className)}>
+ Editions + Editions
</span> </button>
); );
} }
else if(numEditions === 0) { else if(numEditions === 0) {
return ( return (
<span> <button disabled className={classNames('btn', 'btn-default', 'btn-xs', this.props.className)}>
Creating Editions <span className="glyph-ascribe-spool-chunked ascribe-color spin"/> Creating Editions <span className="glyph-ascribe-spool-chunked spin"/>
</span> </button>
); );
} }
else if(numEditions === 1) { else if(numEditions === 1) {
let editionMapping = piece && piece.firstEdition ? piece.firstEdition.edition_number + '/' + piece.num_editions : ''; let editionMapping = piece && piece.first_edition ? piece.first_edition.edition_number + '/' + piece.num_editions : '';
return ( return (
<span <button
onClick={this.toggleTable} onClick={this.toggleTable}
className="ascribe-accordion-list-item-edition-widget"> className={classNames('btn', 'btn-default', 'btn-xs', 'ascribe-accordion-list-item-edition-widget', this.props.className)}>
{editionMapping + ' ' + getLangText('Edition')} {this.getGlyphicon()} {editionMapping + ' ' + getLangText('Edition')} {this.getGlyphicon()}
</span> </button>
); );
} }
else { else {
return ( return (
<span <button
onClick={this.toggleTable} onClick={this.toggleTable}
className="ascribe-accordion-list-item-edition-widget"> className={classNames('btn', 'btn-default', 'btn-xs', 'ascribe-accordion-list-item-edition-widget', this.props.className)}>
{numEditions + ' ' + getLangText('Editions')} {this.getGlyphicon()} {numEditions + ' ' + getLangText('Editions')} {this.getGlyphicon()}
</span> </button>
); );
} }
} }

View File

@ -81,33 +81,6 @@ let Edition = React.createClass({
currentUser={this.state.currentUser} currentUser={this.state.currentUser}
edition={this.props.edition} /> edition={this.props.edition} />
<CollapsibleParagraph
title="Notes"
show={(this.state.currentUser.username && true || false) ||
(this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note)}
defaultExpanded={true}>
<EditionPersonalNote
currentUser={this.state.currentUser}
handleSuccess={this.props.loadEdition}
edition={this.props.edition}/>
<EditionPublicEditionNote
handleSuccess={this.props.loadEdition}
edition={this.props.edition}/>
</CollapsibleParagraph>
<CollapsibleParagraph
title={getLangText('Further Details')}
show={this.props.edition.acl.indexOf('edit') > -1
|| Object.keys(this.props.edition.extra_data).length > 0
|| this.props.edition.other_data !== null}>
<EditionFurtherDetails
editable={this.props.edition.acl.indexOf('edit') > -1}
pieceId={this.props.edition.parent}
extraData={this.props.edition.extra_data}
otherData={this.props.edition.other_data}
handleSuccess={this.props.loadEdition}/>
</CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Certificate of Authenticity')} title={getLangText('Certificate of Authenticity')}
show={this.props.edition.acl.indexOf('coa') > -1}> show={this.props.edition.acl.indexOf('coa') > -1}>
@ -136,6 +109,32 @@ let Edition = React.createClass({
history={this.props.edition.loan_history} /> history={this.props.edition.loan_history} />
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph
title="Notes"
show={(this.state.currentUser.username && true || false) ||
(this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note)}>
<EditionPersonalNote
currentUser={this.state.currentUser}
handleSuccess={this.props.loadEdition}
edition={this.props.edition}/>
<EditionPublicEditionNote
handleSuccess={this.props.loadEdition}
edition={this.props.edition}/>
</CollapsibleParagraph>
<CollapsibleParagraph
title={getLangText('Further Details')}
show={this.props.edition.acl.indexOf('edit') > -1
|| Object.keys(this.props.edition.extra_data).length > 0
|| this.props.edition.other_data !== null}>
<EditionFurtherDetails
editable={this.props.edition.acl.indexOf('edit') > -1}
pieceId={this.props.edition.parent}
extraData={this.props.edition.extra_data}
otherData={this.props.edition.other_data}
handleSuccess={this.props.loadEdition}/>
</CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('SPOOL Details')}> title={getLangText('SPOOL Details')}>
<SpoolDetails <SpoolDetails

View File

@ -7,6 +7,8 @@ import EditionStore from '../../stores/edition_store';
import Edition from './edition'; import Edition from './edition';
import AppConstants from '../../constants/application_constants';
/** /**
* This is the component that implements resource/data specific functionality * This is the component that implements resource/data specific functionality
*/ */
@ -53,8 +55,9 @@ let EditionContainer = React.createClass({
); );
} else { } else {
return ( return (
// TODO translate? <div className="fullpage-spinner">
<p>Loading</p> <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</div>
); );
} }
} }

View File

@ -7,6 +7,8 @@ import PieceStore from '../../stores/piece_store';
import Piece from './piece'; import Piece from './piece';
import AppConstants from '../../constants/application_constants';
/** /**
* This is the component that implements resource/data specific functionality * This is the component that implements resource/data specific functionality
*/ */
@ -48,7 +50,9 @@ let PieceContainer = React.createClass({
); );
} else { } else {
return ( return (
<p>Loading</p> <div className="fullpage-spinner">
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</div>
); );
} }
} }

View File

@ -27,7 +27,7 @@ import { getLangText } from '../utils/lang_utils';
let Header = React.createClass({ let Header = React.createClass({
mixins: [Router.Navigation], mixins: [Router.Navigation, Router.State],
getInitialState() { getInitialState() {
return mergeOptions(WhitelabelStore.getState(), UserStore.getState()); return mergeOptions(WhitelabelStore.getState(), UserStore.getState());
@ -97,7 +97,8 @@ let Header = React.createClass({
<MenuItem eventKey="3" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem> <MenuItem eventKey="3" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem>
</DropdownButton> </DropdownButton>
); );
collection = <NavItemLink to="pieces">{getLangText('COLLECTION')}</NavItemLink>;
collection = <NavItemLink to="pieces" query={this.getQuery()}>{getLangText('COLLECTION')}</NavItemLink>;
addNewWork = <NavItemLink to="register_piece">+ {getLangText('NEW WORK')}</NavItemLink>; addNewWork = <NavItemLink to="register_piece">+ {getLangText('NEW WORK')}</NavItemLink>;
} }
else { else {

View File

@ -25,10 +25,8 @@ let LoginContainer = React.createClass({
return ( return (
<div className="ascribe-login-wrapper"> <div className="ascribe-login-wrapper">
<br/> <br/>
<div className="ascribe-login-text ascribe-login-header"> <LoginForm
{this.props.message} message={this.props.message} />
</div>
<LoginForm />
<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

@ -50,7 +50,7 @@ let PasswordResetContainer = React.createClass({
return ( return (
<div> <div>
<div className="ascribe-login-text ascribe-login-header"> <div className="ascribe-login-text ascribe-login-header">
{getLangText('An email has been sent to')} "{this.state.isRequested}" {getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.')}
</div> </div>
</div> </div>
); );
@ -64,7 +64,7 @@ let PasswordResetContainer = React.createClass({
let PasswordRequestResetForm = React.createClass({ let PasswordRequestResetForm = React.createClass({
handleSuccess() { handleSuccess() {
let notificationText = getLangText('Request successfully sent, check your email'); let 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); let 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);

View File

@ -298,42 +298,4 @@ let FileUploader = React.createClass({
} }
}); });
let InputDate = React.createClass({
propTypes: {
placeholderText: React.PropTypes.string,
onChange: React.PropTypes.func
},
getInitialState() {
return {
value: null,
value_formatted: null
};
},
handleChange(date) {
this.setState({
value: date,
value_formatted: date.format('YYYY')});
let event = document.createEvent('HTMLEvents');
event.initEvent('click', false, true);
document.dispatchEvent(event);
event.target.value = date;
this.props.onChange(event);
},
render: function () {
return (
<DatePicker
key="example2"
dateFormat="YYYY"
selected={this.state.value}
onChange={this.handleChange}
onBlur={this.props.onBlur}
placeholderText={this.props.placeholderText}/>
);
}
});
export default RegisterPiece; export default RegisterPiece;

View File

@ -209,6 +209,7 @@ const languages = {
'Create editions': 'Create editions', 'Create editions': 'Create editions',
'I agree to the Terms of Service': 'I agree to the Terms of Service', 'I agree to the Terms of Service': 'I agree to the Terms of Service',
'read': 'read', 'read': 'read',
'If your email address exists in our database, you will receive a password recovery link in a few minutes.': 'If your email address exists in our database, you will receive a password recovery link in a few minutes.',
}, },
'de': { 'de': {
'ID': 'ID', 'ID': 'ID',
@ -418,6 +419,7 @@ const languages = {
'Create editions': 'Create editions', 'Create editions': 'Create editions',
'I agree to the Terms of Service': 'I agree to the Terms of Service', 'I agree to the Terms of Service': 'I agree to the Terms of Service',
'read': 'read', 'read': 'read',
'If your email address exists in our database, you will receive a password recovery link in a few minutes.': 'If your email address exists in our database, you will receive a password recovery link in a few minutes.',
}, },
'fr': { 'fr': {
'ID': 'ID', 'ID': 'ID',
@ -627,6 +629,7 @@ const languages = {
'Create editions': 'Create editions', 'Create editions': 'Create editions',
'I agree to the Terms of Service': 'I agree to the Terms of Service', 'I agree to the Terms of Service': 'I agree to the Terms of Service',
'read': 'read', 'read': 'read',
'If your email address exists in our database, you will receive a password recovery link in a few minutes.': 'Si votre adresse électronique existe dans notre base de données, vous recevrez un lien de récupération de mot de passe dans quelques minutes.',
} }
}; };

View File

@ -72,19 +72,6 @@ class PieceListStore {
}); });
} }
onAddFirstEditionToPiece({pieceId, firstEdition}) {
let filteredPieceList = this.pieceList.filter((piece) => piece.id === pieceId);
if(filteredPieceList.length === 1) {
let piece = filteredPieceList[0];
piece.firstEdition = firstEdition.edition;
} else {
throw new Error('Could not find a matching piece in piece list since its either not there or piecelist contains duplicates.');
}
}
onUpdatePropertyForPiece({pieceId, key, value}) { onUpdatePropertyForPiece({pieceId, key, value}) {
let filteredPieceList = this.pieceList.filter((piece) => piece.id === pieceId); let filteredPieceList = this.pieceList.filter((piece) => piece.id === pieceId);

View File

@ -59,8 +59,12 @@ $ascribe-accordion-list-font: 'Source Sans Pro';
} }
} }
.accordion-list-item-header > a { .accordion-list-item-header {
text-decoration: none; margin-top:.65em;
> a {
text-decoration: none;
}
} }
.ascribe-accordion-list-loading { .ascribe-accordion-list-loading {

View File

@ -352,3 +352,20 @@ hr {
0% { transform: rotate(0deg); } 0% { transform: rotate(0deg); }
100% { transform: rotate(360deg);} 100% { transform: rotate(360deg);}
} }
.btn-default {
.glyph-ascribe-spool {
color: white;
}
}
.fullpage-spinner {
padding-top: 30%;
padding-bottom: 30%;
text-align: center;
> span {
font-size: 2em;
}
}