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

Merge branch 'master' into AD-419-decouple-piece-registration-from-

This commit is contained in:
Tim Daubenschütz 2015-07-13 15:31:44 +02:00
commit ad6dd40867
13 changed files with 107 additions and 105 deletions

View File

@ -9,7 +9,6 @@ class PieceListActions {
this.generateActions(
'updatePieceList',
'updatePieceListRequestActions',
'addFirstEditionToPiece',
'updatePropertyForPiece'
);
}
@ -41,17 +40,6 @@ class PieceListActions {
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);

View File

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

View File

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

View File

@ -81,33 +81,6 @@ let Edition = React.createClass({
currentUser={this.state.currentUser}
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
title={getLangText('Certificate of Authenticity')}
show={this.props.edition.acl.indexOf('coa') > -1}>
@ -136,6 +109,32 @@ let Edition = React.createClass({
history={this.props.edition.loan_history} />
</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
title={getLangText('SPOOL Details')}>
<SpoolDetails

View File

@ -7,6 +7,8 @@ import EditionStore from '../../stores/edition_store';
import Edition from './edition';
import AppConstants from '../../constants/application_constants';
/**
* This is the component that implements resource/data specific functionality
*/
@ -53,8 +55,9 @@ let EditionContainer = React.createClass({
);
} else {
return (
// TODO translate?
<p>Loading</p>
<div className="fullpage-spinner">
<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 AppConstants from '../../constants/application_constants';
/**
* This is the component that implements resource/data specific functionality
*/
@ -48,7 +50,9 @@ let PieceContainer = React.createClass({
);
} else {
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({
mixins: [Router.Navigation],
mixins: [Router.Navigation, Router.State],
getInitialState() {
return mergeOptions(WhitelabelStore.getState(), UserStore.getState());
@ -97,7 +97,8 @@ let Header = React.createClass({
<MenuItem eventKey="3" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem>
</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>;
}
else {

View File

@ -11,6 +11,7 @@ import UserActions from '../actions/user_actions';
import Form from './ascribe_forms/form';
import Property from './ascribe_forms/property';
import FormPropertyHeader from './ascribe_forms/form_property_header';
import apiUrls from '../constants/api_urls';
import AppConstants from '../constants/application_constants';
@ -61,10 +62,8 @@ let LoginContainer = React.createClass({
return (
<div className="ascribe-login-wrapper">
<br/>
<div className="ascribe-login-text ascribe-login-header">
{this.props.message}
</div>
<LoginForm />
<LoginForm
message={this.props.message} />
<div className="ascribe-login-text">
{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>
@ -77,7 +76,8 @@ let LoginContainer = React.createClass({
let LoginForm = React.createClass({
propTypes: {
redirectOnLoginSuccess: React.PropTypes.bool
redirectOnLoginSuccess: React.PropTypes.bool,
message: React.PropTypes.string
},
handleSuccess(){
@ -106,6 +106,7 @@ let LoginForm = React.createClass({
render() {
return (
<Form
className="ascribe-form-bordered"
ref="loginForm"
url={apiUrls.users_login}
handleSuccess={this.handleSuccess}
@ -120,6 +121,9 @@ let LoginForm = React.createClass({
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
</button>
}>
<FormPropertyHeader>
<h3>{this.props.message}</h3>
</FormPropertyHeader>
<Property
name='email'
label={getLangText('Email')}>
@ -140,7 +144,6 @@ let LoginForm = React.createClass({
name="password"
required/>
</Property>
<hr />
</Form>
);
}

View File

@ -50,7 +50,7 @@ let PasswordResetContainer = React.createClass({
return (
<div>
<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>
);
@ -64,7 +64,7 @@ let PasswordResetContainer = React.createClass({
let PasswordRequestResetForm = React.createClass({
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);
GlobalNotificationActions.appendGlobalNotification(notification);
this.props.handleRequestSuccess(this.refs.form.refs.email.state.value);

View File

@ -209,6 +209,7 @@ const languages = {
'Create editions': 'Create editions',
'I agree to the Terms of Service': 'I agree to the Terms of Service',
'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': {
'ID': 'ID',
@ -418,6 +419,7 @@ const languages = {
'Create editions': 'Create editions',
'I agree to the Terms of Service': 'I agree to the Terms of Service',
'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': {
'ID': 'ID',
@ -627,6 +629,7 @@ const languages = {
'Create editions': 'Create editions',
'I agree to the Terms of Service': 'I agree to the Terms of Service',
'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}) {
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 {
text-decoration: none;
.accordion-list-item-header {
margin-top:.65em;
> a {
text-decoration: none;
}
}
.ascribe-accordion-list-loading {

View File

@ -346,3 +346,20 @@ hr {
0% { transform: rotate(0deg); }
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;
}
}