diff --git a/js/actions/edition_list_actions.js b/js/actions/edition_list_actions.js index d13882cd..fb0a2249 100644 --- a/js/actions/edition_list_actions.js +++ b/js/actions/edition_list_actions.js @@ -33,6 +33,10 @@ class EditionListActions { EditionListFetcher .fetch(pieceId, page, pageSize, orderBy, orderAsc, filterBy) .then((res) => { + if(res && !res.editions) { + throw new Error('Piece has no editions to fetch.'); + } + this.actions.updateEditionList({ pieceId, page, @@ -46,6 +50,7 @@ class EditionListActions { resolve(res); }) .catch((err) => { + console.logGlobal(err); reject(err); }); }); diff --git a/js/actions/user_actions.js b/js/actions/user_actions.js index 2a2c3c05..365db176 100644 --- a/js/actions/user_actions.js +++ b/js/actions/user_actions.js @@ -13,7 +13,7 @@ class UserActions { } fetchCurrentUser() { - return UserFetcher.fetchOne() + UserFetcher.fetchOne() .then((res) => { this.actions.updateCurrentUser(res.users[0]); }) @@ -24,7 +24,7 @@ class UserActions { } logoutCurrentUser() { - return UserFetcher.logout() + UserFetcher.logout() .then(() => { this.actions.deleteCurrentUser(); }) diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index 19c58fe8..3f56f9a0 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -6,15 +6,11 @@ import { Link, History } from 'react-router'; import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; import Glyphicon from 'react-bootstrap/lib/Glyphicon'; -import Button from 'react-bootstrap/lib/Button'; import UserActions from '../../actions/user_actions'; import UserStore from '../../stores/user_store'; import CoaActions from '../../actions/coa_actions'; import CoaStore from '../../stores/coa_store'; -import PieceListActions from '../../actions/piece_list_actions'; -import PieceListStore from '../../stores/piece_list_store'; -import EditionListActions from '../../actions/edition_list_actions'; import HistoryIterator from './history_iterator'; @@ -28,13 +24,7 @@ import EditionDetailProperty from './detail_property'; import LicenseDetail from './license_detail'; import FurtherDetails from './further_details'; -import ListRequestActions from './../ascribe_forms/list_form_request_actions'; -import AclButtonList from './../ascribe_buttons/acl_button_list'; -import UnConsignRequestButton from './../ascribe_buttons/unconsign_request_button'; -import DeleteButton from '../ascribe_buttons/delete_button'; - -import GlobalNotificationModel from '../../models/global_notification_model'; -import GlobalNotificationActions from '../../actions/global_notification_actions'; +import EditionActionPanel from './edition_action_panel'; import Note from './note'; @@ -42,7 +32,6 @@ import ApiUrls from '../../constants/api_urls'; import AppConstants from '../../constants/application_constants'; import { getLangText } from '../../utils/lang_utils'; -import { mergeOptions } from '../../utils/general_utils'; /** @@ -58,15 +47,11 @@ let Edition = React.createClass({ mixins: [History], getInitialState() { - return mergeOptions( - UserStore.getState(), - PieceListStore.getState() - ); + return UserStore.getState(); }, componentDidMount() { UserStore.listen(this.onChange); - PieceListStore.listen(this.onChange); UserActions.fetchCurrentUser(); }, @@ -81,31 +66,12 @@ let Edition = React.createClass({ CoaActions.flushCoa(); UserStore.unlisten(this.onChange); - PieceListStore.unlisten(this.onChange); }, onChange(state) { this.setState(state); }, - handleDeleteSuccess(response) { - this.refreshCollection(); - - EditionListActions.closeAllEditionLists(); - EditionListActions.clearAllEditionSelections(); - - let notification = new GlobalNotificationModel(response.notification, 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - - this.history.pushState(null, '/collection'); - }, - - refreshCollection() { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, - this.state.orderBy, this.state.orderAsc, this.state.filterBy); - EditionListActions.refreshEditionList({pieceId: this.props.edition.parent}); - }, - render() { return ( @@ -122,12 +88,9 @@ let Edition = React.createClass({
- + currentUser={this.state.currentUser} + handleSuccess={this.props.loadEdition}/> @@ -211,29 +174,14 @@ let Edition = React.createClass({ let EditionSummary = React.createClass({ propTypes: { edition: React.PropTypes.object, - handleSuccess: React.PropTypes.func, currentUser: React.PropTypes.object, - handleDeleteSuccess: React.PropTypes.func, - refreshCollection: React.PropTypes.func - }, - - getTransferWithdrawData(){ - return {'bitcoin_id': this.props.edition.bitcoin_id}; + handleSuccess: React.PropTypes.func }, handleSuccess() { - this.props.refreshCollection(); this.props.handleSuccess(); }, - showNotification(response){ - this.props.handleSuccess(); - - if (response){ - let notification = new GlobalNotificationModel(response.notification, 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - } - }, getStatus(){ let status = null; if (this.props.edition.status.length > 0){ @@ -248,79 +196,26 @@ let EditionSummary = React.createClass({ return status; }, - getActions(){ - let actions = null; - if (this.props.edition && - this.props.edition.notifications && - this.props.edition.notifications.length > 0){ - actions = ( - ); - } - - else { - let withdrawButton = null; - if (this.props.edition.status.length > 0 && this.props.edition.pending_new_owner && this.props.edition.acl.acl_withdraw_transfer) { - withdrawButton = ( -
- -
- ); - } - let unconsignRequestButton = null; - if (this.props.edition.acl.acl_request_unconsign) { - unconsignRequestButton = ( - - ); - } - actions = ( - - - - {withdrawButton} - - {unconsignRequestButton} - - - ); - } - return actions; - }, render() { + let { edition, currentUser } = this.props; return (
+ value={ edition.edition_number + ' ' + getLangText('of') + ' ' + edition.num_editions} /> - + value={ edition.owner } /> + {this.getStatus()} - {this.getActions()} +
); diff --git a/js/components/ascribe_detail/edition_action_panel.js b/js/components/ascribe_detail/edition_action_panel.js new file mode 100644 index 00000000..d82a6b8f --- /dev/null +++ b/js/components/ascribe_detail/edition_action_panel.js @@ -0,0 +1,169 @@ +'use strict'; + +import React from 'react'; +import Router from 'react-router'; + +import Row from 'react-bootstrap/lib/Row'; +import Col from 'react-bootstrap/lib/Col'; +import Button from 'react-bootstrap/lib/Button'; + +import EditionListActions from '../../actions/edition_list_actions'; +import PieceListActions from '../../actions/piece_list_actions'; +import PieceListStore from '../../stores/piece_list_store'; + +import Form from './../ascribe_forms/form'; +import Property from './../ascribe_forms/property'; + +import ListRequestActions from './../ascribe_forms/list_form_request_actions'; +import AclButtonList from './../ascribe_buttons/acl_button_list'; +import UnConsignRequestButton from './../ascribe_buttons/unconsign_request_button'; +import DeleteButton from '../ascribe_buttons/delete_button'; + +import GlobalNotificationModel from '../../models/global_notification_model'; +import GlobalNotificationActions from '../../actions/global_notification_actions'; + +import AclProxy from '../acl_proxy'; + +import ApiUrls from '../../constants/api_urls'; + +import { getLangText } from '../../utils/lang_utils'; + +/* + A component that handles all the actions inside of the edition detail + handleSuccess requires a loadEdition action (could be refactored) + */ +let EditionActionPanel = React.createClass({ + propTypes: { + edition: React.PropTypes.object, + currentUser: React.PropTypes.object, + handleSuccess: React.PropTypes.func + }, + + mixins: [Router.Navigation], + + getInitialState() { + return PieceListStore.getState(); + }, + + componentDidMount() { + PieceListStore.listen(this.onChange); + }, + + componentWillUnmount() { + PieceListStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + handleDeleteSuccess(response) { + this.refreshCollection(); + + EditionListActions.closeAllEditionLists(); + EditionListActions.clearAllEditionSelections(); + + let notification = new GlobalNotificationModel(response.notification, 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + + this.transitionTo('pieces'); + }, + + refreshCollection() { + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); + EditionListActions.refreshEditionList({pieceId: this.props.edition.parent}); + }, + + handleSuccess(response){ + this.refreshCollection(); + this.props.handleSuccess(); + if (response){ + let notification = new GlobalNotificationModel(response.notification, 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + } + }, + + render(){ + let {edition, currentUser} = this.props; + + if (edition && + edition.notifications && + edition.notifications.length > 0){ + return ( + ); + } + + else { + return ( + + + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ +
+ ); + } + } +}); + +export default EditionActionPanel; \ No newline at end of file diff --git a/js/components/ascribe_forms/form_login.js b/js/components/ascribe_forms/form_login.js index 1b52c1ce..46eb0cf9 100644 --- a/js/components/ascribe_forms/form_login.js +++ b/js/components/ascribe_forms/form_login.js @@ -24,7 +24,6 @@ let LoginForm = React.createClass({ submitMessage: React.PropTypes.string, redirectOnLoggedIn: React.PropTypes.bool, redirectOnLoginSuccess: React.PropTypes.bool, - onLogin: React.PropTypes.func, location: React.PropTypes.object }, @@ -45,6 +44,29 @@ let LoginForm = React.createClass({ componentDidMount() { UserStore.listen(this.onChange); + let { redirect } = this.props.location.query; + if (redirect && redirect !== 'login'){ + this.histoy.pushState(null, redirect, this.props.location.query); + } + }, + + componentDidUpdate() { + // if user is already logged in, redirect him to piece list + if(this.state.currentUser && this.state.currentUser.email && this.props.redirectOnLoggedIn + && this.props.redirectOnLoginSuccess) { + let { redirectAuthenticated } = this.props.location.query; + if(redirectAuthenticated) { + /* + * redirectAuthenticated contains an arbirary path + * eg pieces/, editions/, collection, settings, ... + * hence transitionTo cannot be used directly + */ + window.location = AppConstants.baseUrl + redirectAuthenticated; + } else { + this.history.pushState(null, 'collection'); + } + + } }, componentWillUnmount() { @@ -53,12 +75,6 @@ let LoginForm = React.createClass({ onChange(state) { this.setState(state); - - // if user is already logged in, redirect him to piece list - if(this.state.currentUser && this.state.currentUser.email && this.props.redirectOnLoggedIn) { - // FIXME: hack to redirect out of the dispatch cycle - window.setTimeout(() => this.history.pushState(null, '/collection'), 0); - } }, handleSuccess(){ @@ -70,28 +86,7 @@ let LoginForm = React.createClass({ // The easiest way to check if the user was successfully logged in is to fetch the user // in the user store (which is obviously only possible if the user is logged in), since // register_piece is listening to the changes of the user_store. - UserActions.fetchCurrentUser() - .then(() => { - if(this.props.redirectOnLoginSuccess) { - /* Taken from http://stackoverflow.com/a/14916411 */ - /* - We actually have to trick the Browser into showing the "save password" dialog - as Chrome expects the login page to be reloaded after the login. - Users on Stack Overflow claim this is a bug in chrome and should be fixed in the future. - Until then, we redirect the HARD way, but reloading the whole page using window.location - */ - window.location = AppConstants.baseUrl + 'collection'; - } else if(this.props.onLogin) { - // In some instances we want to give a callback to an outer container, - // to show that the one login action the user triggered actually went through. - // We can not do this by listening on a store's state as it wouldn't really tell us - // if the user did log in or was just fetching the user's data again - this.props.onLogin(); - } - }) - .catch((err) => { - console.logGlobal(err); - }); + UserActions.fetchCurrentUser(); }, diff --git a/js/components/ascribe_forms/form_signup.js b/js/components/ascribe_forms/form_signup.js index 60962508..155854da 100644 --- a/js/components/ascribe_forms/form_signup.js +++ b/js/components/ascribe_forms/form_signup.js @@ -6,6 +6,7 @@ import { History } from 'react-router'; import { getLangText } from '../../utils/lang_utils'; import UserStore from '../../stores/user_store'; +import UserActions from '../../actions/user_actions'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; @@ -14,6 +15,7 @@ import Form from './form'; import Property from './property'; import InputCheckbox from './input_checkbox'; +import AppConstants from '../../constants/application_constants'; import ApiUrls from '../../constants/api_urls'; @@ -34,12 +36,35 @@ let SignupForm = React.createClass({ submitMessage: getLangText('Sign up') }; }, + getInitialState() { return UserStore.getState(); }, componentDidMount() { UserStore.listen(this.onChange); + + let { redirect } = this.props.location.query; + if (redirect && redirect !== 'signup'){ + this.history.pushState(null, redirect, this.props.location.query); + } + }, + + componentDidUpdate() { + // if user is already logged in, redirect him to piece list + if(this.state.currentUser && this.state.currentUser.email) { + //this.history.pushState(null, '/collection'); + let { redirectAuthenticated } = this.props.location.query; + if(redirectAuthenticated) { + /* + * redirectAuthenticated contains an arbirary path + * eg pieces/, editions/, collection, settings, ... + * hence transitionTo cannot be used directly + */ + window.location = AppConstants.baseUrl + redirectAuthenticated; + } + this.history.pushState(null, '/collection'); + } }, componentWillUnmount() { @@ -48,21 +73,17 @@ let SignupForm = React.createClass({ onChange(state) { this.setState(state); - - // if user is already logged in, redirect him to piece list - if(this.state.currentUser && this.state.currentUser.email) { - this.history.pushState(null, '/collection'); - } }, handleSuccess(response){ if (response.user) { let notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000); GlobalNotificationActions.appendGlobalNotification(notification); + + // Refactor this to its own component this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.'); - } - else if (response.redirect) { - this.history.pushState(null, '/collection'); + } else { + UserActions.fetchCurrentUser(); } }, @@ -77,7 +98,9 @@ let SignupForm = React.createClass({ let tooltipPassword = getLangText('Your password must be at least 10 characters') + '.\n ' + getLangText('This password is securing your digital property like a bank account') + '.\n ' + getLangText('Store it in a safe place') + '!'; + let email = this.props.location.query.email || null; + return (
{ diff --git a/js/components/ascribe_routes/auth_component.js b/js/components/ascribe_routes/auth_component.js index 7c5ec8ed..53e7abb3 100644 --- a/js/components/ascribe_routes/auth_component.js +++ b/js/components/ascribe_routes/auth_component.js @@ -22,6 +22,7 @@ export function AuthComponent(Component) { componentDidUpdate() { if(this.state.currentUser && !this.state.currentUser.email) { + console.log('redirect'); this.history.pushState(null, '/login'); } }, diff --git a/js/components/register_piece.js b/js/components/register_piece.js index 83bcfb3d..c00f3242 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -61,10 +61,10 @@ let RegisterPiece = React.createClass( { }, componentDidMount() { - WhitelabelActions.fetchWhitelabel(); PieceListStore.listen(this.onChange); UserStore.listen(this.onChange); WhitelabelStore.listen(this.onChange); + WhitelabelActions.fetchWhitelabel(); }, componentWillUnmount() { @@ -118,56 +118,16 @@ let RegisterPiece = React.createClass( { } }, - // basically redirects to the second slide (index: 1), when the user is not logged in - onLoggedOut() { - // only transition to the login store, if user is not logged in - // ergo the currentUser object is not properly defined - if(this.state.currentUser && !this.state.currentUser.email) { - this.refs.slidesContainer.setSlideNum(1); - } - }, - - onLogin() { - // once the currentUser object from UserStore is defined (eventually the user was transitioned - // to the login form via the slider and successfully logged in), we can direct him back to the - // register_piece slide - if(this.state.currentUser && this.state.currentUser.email) { - window.history.back(); - } - }, - render() { return ( - -
- - - - {this.props.children} - {this.getSpecifyEditions()} - - - -
-
- -
-
+ {this.props.children} + {this.getSpecifyEditions()} + ); } }); diff --git a/js/constants/api_urls.js b/js/constants/api_urls.js index b8d6713d..2ff689fe 100644 --- a/js/constants/api_urls.js +++ b/js/constants/api_urls.js @@ -38,6 +38,7 @@ let ApiUrls = { 'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/', 'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/', 'ownership_consigns_deny': AppConstants.apiEndpoint + 'ownership/consigns/deny/', + 'ownership_consigns_withdraw': AppConstants.apiEndpoint + 'ownership/consigns/withdraw/', 'ownership_loans_pieces': AppConstants.apiEndpoint + 'ownership/loans/pieces/', 'ownership_loans_pieces_confirm': AppConstants.apiEndpoint + 'ownership/loans/pieces/confirm/', 'ownership_loans_pieces_deny': AppConstants.apiEndpoint + 'ownership/loans/pieces/deny/', diff --git a/js/stores/edition_list_store.js b/js/stores/edition_list_store.js index 87b4df76..b3b152d3 100644 --- a/js/stores/edition_list_store.js +++ b/js/stores/edition_list_store.js @@ -13,7 +13,6 @@ class EditionListStore { } onUpdateEditionList({pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count, filterBy}) { - /* Basically there are two modes an edition list can be updated.