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:
commit
ad6dd40867
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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')}? <Link to="signup">{getLangText('Sign up')}...</Link><br/>
|
||||
{getLangText('Forgot my password')}? <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>
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.',
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user