diff --git a/js/components/ascribe_forms/alert.js b/js/components/ascribe_forms/alert.js
new file mode 100644
index 00000000..72ced310
--- /dev/null
+++ b/js/components/ascribe_forms/alert.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import Alert from 'react-bootstrap/lib/Alert';
+
+let AlertDismissable = React.createClass({
+ getInitialState() {
+ return {
+ alertVisible: true
+ };
+ },
+ show() {
+ this.setState({alertVisible: true});
+ },
+ hide() {
+ this.setState({alertVisible: false});
+ },
+ render() {
+ if (this.state.alertVisible) {
+ let key = this.props.error;
+ return (
+
+ {this.props.error}
+
+ );
+ }
+ return (
+
+ );
+ }
+});
+
+export default AlertDismissable;
\ No newline at end of file
diff --git a/js/components/ascribe_forms/button_submit_close.js b/js/components/ascribe_forms/button_submit_close.js
new file mode 100644
index 00000000..60091f2f
--- /dev/null
+++ b/js/components/ascribe_forms/button_submit_close.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+let ButtonSubmitOrClose = React.createClass({
+ render() {
+ if (this.props.submitted){
+ return (
+
+ Loading
+
+ )
+ }
+ return (
+
+
+
+
+ )
+ }
+});
+
+export default ButtonSubmitOrClose;
diff --git a/js/components/ascribe_forms/form_share_email.js b/js/components/ascribe_forms/form_share_email.js
index d78c02ec..dd61bc7b 100644
--- a/js/components/ascribe_forms/form_share_email.js
+++ b/js/components/ascribe_forms/form_share_email.js
@@ -2,53 +2,49 @@ import fetch from 'isomorphic-fetch';
import React from 'react';
-import AppConstants from '../../constants/application_constants';
-import FetchApiUtils from '../../utils/fetch_api_utils';
-import FormMixin from '../../mixins/form_alert_mixin';
+import ApiUrls from '../../constants/api_urls';
+import FormMixin from '../../mixins/form_mixin';
+import InputText from './input_text';
+import InputTextArea from './input_textarea';
+import ButtonSubmitOrClose from './button_submit_close';
let ShareForm = React.createClass({
mixins: [FormMixin],
- submit(e) {
- e.preventDefault();
- fetch(AppConstants.baseUrl + 'ownership/shares/mail/', {
- method: 'post',
- headers: {
- 'Authorization': 'Basic ' + AppConstants.debugCredentialBase64,
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(this.getFormData())
- })
- .then(
- (response) => this.handleResponse(response)
- );
+ url() {
+ return ApiUrls.ownership_shares_mail
},
getFormData() {
return {
bitcoin_id: this.props.edition.bitcoin_id,
- share_emails: this.refs.share_emails.getDOMNode().value,
- share_message: this.refs.share_message.getDOMNode().value
+ share_emails: this.refs.share_emails.state.value,
+ share_message: this.refs.share_message.state.value
}
},
- renderMessage() {
- return "" +
- "Hi,\n" +
- "\n" +
- "I am sharing \"" + this.props.edition.title + "\" with you.\n" +
- "\n" +
- "Truly yours,\n" +
- this.props.currentUser.username;
- },
- render() {
+ renderForm() {
+ let message = "Hi,\n" +
+ "\n" +
+ "I am sharing \"" + this.props.edition.title + "\" with you.\n" +
+ "\n" +
+ "Truly yours,\n" +
+ this.props.currentUser.username;
return (
-
);
}
diff --git a/js/components/ascribe_forms/form_transfer.js b/js/components/ascribe_forms/form_transfer.js
new file mode 100644
index 00000000..28e86db3
--- /dev/null
+++ b/js/components/ascribe_forms/form_transfer.js
@@ -0,0 +1,68 @@
+import fetch from 'isomorphic-fetch';
+
+import React from 'react';
+
+import ApiUrls from '../../constants/api_urls';
+import FormMixin from '../../mixins/form_mixin';
+import InputText from './input_text';
+import InputTextArea from './input_textarea';
+import ButtonSubmitOrClose from './button_submit_close';
+
+
+
+let TransferForm = React.createClass({
+ mixins: [FormMixin],
+
+ url() {
+ return ApiUrls.ownership_transfers
+ },
+ getFormData() {
+ return {
+ bitcoin_id: this.props.edition.bitcoin_id,
+ transferee: this.refs.transferee.state.value,
+ transfer_message: this.refs.transfer_message.state.value,
+ password: this.refs.password.state.value
+ }
+ },
+ renderForm() {
+ let message = "Hi,\n" +
+ "\n" +
+ "I transfer ownership of \"" + this.props.edition.title + "\" to you.\n" +
+ "\n" +
+ "Truly yours,\n" +
+ this.props.currentUser.username;
+ return (
+
+ );
+ }
+});
+
+export default TransferForm;
\ No newline at end of file
diff --git a/js/components/ascribe_forms/input_text.js b/js/components/ascribe_forms/input_text.js
new file mode 100644
index 00000000..a65bcc8f
--- /dev/null
+++ b/js/components/ascribe_forms/input_text.js
@@ -0,0 +1,35 @@
+import React from 'react';
+
+import AlertMixin from '../../mixins/alert_mixin'
+
+let InputText = React.createClass({
+
+ mixins : [AlertMixin],
+
+ getInitialState() {
+ return {value: null,
+ alerts: null, // needed in AlertMixin
+ retry: 0 // needed in AlertMixin for generating unique alerts
+ };
+ },
+ handleChange(event) {
+ this.setState({value: event.target.value});
+ },
+ render() {
+ let className = "form-control input-text-ascribe";
+ let alerts = (this.props.submitted) ? null : this.state.alerts;
+ return (
+
+ {alerts}
+
+
+ );
+
+ }
+});
+
+export default InputText;
\ No newline at end of file
diff --git a/js/components/ascribe_forms/input_textarea.js b/js/components/ascribe_forms/input_textarea.js
new file mode 100644
index 00000000..b5f64b66
--- /dev/null
+++ b/js/components/ascribe_forms/input_textarea.js
@@ -0,0 +1,35 @@
+import React from 'react';
+
+import AlertMixin from '../../mixins/alert_mixin'
+
+let InputTextArea = React.createClass({
+
+ mixins : [AlertMixin],
+
+ getInitialState() {
+ return {value: this.props.defaultValue,
+ alerts: null, // needed in AlertMixin
+ retry: 0 // needed in AlertMixin for generating unique alerts
+ };
+ },
+ handleChange(event) {
+ this.setState({value: event.target.value});
+ },
+ render() {
+ let className = "form-control input-text-ascribe textarea-ascribe-message";
+
+ let alerts = (this.props.submitted) ? null : this.state.alerts;
+ return (
+
+ {alerts}
+
+
+ );
+
+ }
+});
+
+export default InputTextArea;
\ No newline at end of file
diff --git a/js/components/ascribe_modal/modal_share.js b/js/components/ascribe_modal/modal_share.js
index 4cc05d5a..176541ee 100644
--- a/js/components/ascribe_modal/modal_share.js
+++ b/js/components/ascribe_modal/modal_share.js
@@ -13,7 +13,7 @@ let ShareModalButton = React.createClass({
return (
Share the artwork}>
}>
+ currentUser={this.props.currentUser}/>}>
@@ -25,7 +25,8 @@ let ShareModalButton = React.createClass({
let ShareModal = React.createClass({
onRequestHide(e){
- e.preventDefault();
+ if (e)
+ e.preventDefault();
this.props.onRequestHide();
},
render() {
diff --git a/js/components/ascribe_modal/modal_transfer.js b/js/components/ascribe_modal/modal_transfer.js
new file mode 100644
index 00000000..b57064ff
--- /dev/null
+++ b/js/components/ascribe_modal/modal_transfer.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import Modal from 'react-bootstrap/lib/Modal';
+import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
+import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
+import Tooltip from 'react-bootstrap/lib/Tooltip';
+
+import TransferForm from '../ascribe_forms/form_transfer'
+
+
+let TransferModalButton = React.createClass({
+ render() {
+ return (
+ Transfer the ownership of the artwork}>
+ }>
+
+ TRANSFER
+
+
+
+ )
+ }
+});
+
+let TransferModal = React.createClass({
+ onRequestHide(e){
+ e.preventDefault();
+ this.props.onRequestHide();
+ },
+ render() {
+ return (
+
+
+
+
+
+ )
+ }
+});
+
+
+export default TransferModalButton;
diff --git a/js/components/edition.js b/js/components/edition.js
index 938293e8..ffeb9b25 100644
--- a/js/components/edition.js
+++ b/js/components/edition.js
@@ -1,6 +1,7 @@
import React from 'react';
import ImageViewer from './ascribe_media/image_viewer';
+import TransferModalButton from './ascribe_modal/modal_transfer';
import ShareModalButton from './ascribe_modal/modal_share';
/**
@@ -46,6 +47,7 @@ let EditionDetails = React.createClass({
+
diff --git a/js/constants/api_urls.js b/js/constants/api_urls.js
new file mode 100644
index 00000000..f0c7f9b6
--- /dev/null
+++ b/js/constants/api_urls.js
@@ -0,0 +1,8 @@
+import AppConstants from './application_constants';
+
+let apiUrls = {
+ 'ownership_shares_mail' : AppConstants.baseUrl + 'ownership/shares/mail/',
+ 'ownership_transfers' : AppConstants.baseUrl + 'ownership/transfers/'
+};
+
+export default apiUrls;
\ No newline at end of file
diff --git a/js/constants/application_constants.js b/js/constants/application_constants.js
index 8efb7196..55b9d5f0 100644
--- a/js/constants/application_constants.js
+++ b/js/constants/application_constants.js
@@ -1,6 +1,8 @@
let constants = {
- 'baseUrl': 'http://staging.ascribe.io/api/',
+ 'baseUrl': 'http://localhost:8000/api/',
+ //'baseUrl': 'http://staging.ascribe.io/api/',
'debugCredentialBase64': 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw' // dimi@mailinator:0000000000
-};
-export default constants;
\ No newline at end of file
+};
+export default constants;
+
diff --git a/js/mixins/alert_mixin.js b/js/mixins/alert_mixin.js
new file mode 100644
index 00000000..478c44db
--- /dev/null
+++ b/js/mixins/alert_mixin.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import AlertDismissable from '../components/ascribe_forms/alert';
+
+let AlertMixin = {
+ setAlerts(errors){
+ let alerts = errors.map(
+ function(error) {
+ let key = error + this.state.retry;
+ return ;
+ }.bind(this)
+ );
+ this.setState({alerts: alerts, retry: this.state.retry + 1});
+ },
+ hideAlerts(){
+ for (alert in this.state.alerts){
+ alert.hide();
+ }
+ }
+};
+
+export default AlertMixin;
\ No newline at end of file
diff --git a/js/mixins/form_alert_mixin.js b/js/mixins/form_alert_mixin.js
deleted file mode 100644
index ff9a5d52..00000000
--- a/js/mixins/form_alert_mixin.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import React from 'react';
-import Alert from 'react-bootstrap/lib/Alert';
-
-let FormMixin = {
- getInitialState() {
- return {errors: null,
- retry: 0 // used for unique keying
- }
- },
- handleResponse(response){
- if (response.status >= 200 && response.status < 300)
- return response;
- this.handleError(response);
- },
- handleError(response){
- response.json().then((response) => this.setState({errors: response.errors,
- retry: this.state.retry + 1}))
- },
- renderAlert(id){
- if (this.state.errors && id in this.state.errors) {
- return this.state.errors[id].map(function(error) {
- let key = error + this.state.retry;
- return ;
- }.bind(this)
- )
- }
- return
- },
- renderTextInput(id, type, placeHolder, required) {
- return (
-
- {this.renderAlert(id)}
-
-
- )
- },
- renderTextArea(id, placeHolder, required) {
- return (
-
- {this.renderAlert(id)}
-
-
- )
- }
-};
-
-let AlertDismissable = React.createClass({
- getInitialState() {
- return {
- alertVisible: true
- };
- },
- show() {
- this.setState({alertVisible: true});
- },
- hide() {
- this.setState({alertVisible: false});
- },
- render() {
- if (this.state.alertVisible) {
- return (
-
- {this.props.error}
-
- );
- }
- return (
-
- );
- }
-});
-
-export default FormMixin;
\ No newline at end of file
diff --git a/js/mixins/form_mixin.js b/js/mixins/form_mixin.js
new file mode 100644
index 00000000..6bbf5efa
--- /dev/null
+++ b/js/mixins/form_mixin.js
@@ -0,0 +1,67 @@
+import React from 'react';
+
+import AppConstants from '../constants/application_constants'
+import AlertDismissable from '../components/ascribe_forms/alert'
+
+export const FormMixin = {
+ getInitialState() {
+ return {
+ submitted: false
+ , status: null
+ }
+ },
+ submit(e) {
+ e.preventDefault();
+ this.setState({submitted: true});
+ fetch(this.url(), {
+ method: 'post',
+ headers: {
+ 'Authorization': 'Basic ' + AppConstants.debugCredentialBase64,
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(this.getFormData())
+ })
+ .then(
+ (response) => this.handleResponse(response)
+ );
+ },
+ handleResponse(response){
+ if (response.status >= 200 && response.status < 300){
+ this.props.onRequestHide();
+ }
+ else if (response.status >= 400 && response.status < 500) {
+ this.handleError(response);
+ }
+ else {
+ this.setState({submitted: false, status: response.status});
+ }
+ },
+ handleError(response){
+ response.json().then((response) => this.dispatchErrors(response.errors));
+
+ },
+ dispatchErrors(errors){
+ for (var input in errors){
+ if (this.refs && this.refs[input] && this.refs[input].state){
+ this.refs[input].setAlerts(errors[input]);
+ }
+ }
+ this.setState({submitted: false});
+ },
+ render(){
+ let alert = null;
+ if (this.state.status >= 500){
+ alert = ;
+ }
+ return (
+
+ {alert}
+ {this.renderForm()}
+
+ )
+ }
+};
+
+export default FormMixin;
+