mirror of
https://github.com/ascribe/onion.git
synced 2024-11-15 01:25:17 +01:00
Merge branch 'AD-416-account-settings-page-navbar' of bitbucket.org:ascribe/onion into AD-416-account-settings-page-navbar
Conflicts: sass/main.scss
This commit is contained in:
commit
ea88e87355
@ -4,4 +4,5 @@ node_modules
|
|||||||
|
|
||||||
js/**/__tests__
|
js/**/__tests__
|
||||||
|
|
||||||
server.js
|
server.js
|
||||||
|
js/components/ascribe_uploader/vendor
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,7 +12,6 @@ logs
|
|||||||
results
|
results
|
||||||
|
|
||||||
node_modules/*
|
node_modules/*
|
||||||
!node_modules/react-s3-fineuploader
|
|
||||||
|
|
||||||
build
|
build
|
||||||
|
|
||||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
[submodule "node_modules/react-s3-fineuploader"]
|
|
||||||
path = node_modules/react-s3-fineuploader
|
|
||||||
url = https://bitbucket.org/ascribe/react-s3-fineuploader.git
|
|
@ -69,7 +69,7 @@ gulp.task('js:build', function() {
|
|||||||
bundle(false);
|
bundle(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('serve', ['browser-sync', 'run-server', 'sass:watch', 'copy'], function() {
|
gulp.task('serve', ['browser-sync', 'run-server', 'lint:watch', 'sass:build', 'sass:watch', 'copy'], function() {
|
||||||
bundle(true);
|
bundle(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
34
js/actions/application_actions.js
Normal file
34
js/actions/application_actions.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../alt';
|
||||||
|
import ApplicationFetcher from '../fetchers/application_fetcher';
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'updateApplications'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchApplication() {
|
||||||
|
ApplicationFetcher.fetch()
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updateApplications(res.applications);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
refreshApplicationToken(applicationName) {
|
||||||
|
ApplicationFetcher.refreshToken(applicationName)
|
||||||
|
.then((res) => {
|
||||||
|
this.actions.updateApplications(res.applications);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createActions(ApplicationActions);
|
@ -18,6 +18,12 @@ const CollapsibleParagraph = React.createClass({
|
|||||||
iconName: React.PropTypes.string
|
iconName: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
show: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
mixins: [CollapsibleMixin],
|
mixins: [CollapsibleMixin],
|
||||||
|
|
||||||
getCollapsibleDOMNode(){
|
getCollapsibleDOMNode(){
|
||||||
@ -35,19 +41,23 @@ const CollapsibleParagraph = React.createClass({
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let styles = this.getCollapsibleClassSet();
|
let styles = this.getCollapsibleClassSet();
|
||||||
let text = this.isExpanded() ? '-' : '+';
|
let text = this.isExpanded() ? '[hide]' : '[show]';
|
||||||
return (
|
if(this.props.show) {
|
||||||
<div className="ascribe-detail-header">
|
return (
|
||||||
<div className="ascribe-edition-collapsible-wrapper">
|
<div className="ascribe-detail-header">
|
||||||
<div onClick={this.handleToggle}>
|
<div className="ascribe-edition-collapsible-wrapper">
|
||||||
<span>{text} {this.props.title} </span>
|
<div onClick={this.handleToggle}>
|
||||||
</div>
|
<span>{this.props.title}</span><span className="pull-right">{text}</span>
|
||||||
<div ref='panel' className={classNames(styles) + ' ascribe-edition-collapible-content'}>
|
</div>
|
||||||
{this.props.children}
|
<div ref='panel' className={classNames(styles) + ' ascribe-edition-collapible-content'}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ import AlertDismissable from './alert';
|
|||||||
|
|
||||||
let Form = React.createClass({
|
let Form = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
url: React.PropTypes.string,
|
||||||
|
handleSuccess: React.PropTypes.func,
|
||||||
|
getFormData: React.PropTypes.func,
|
||||||
children: React.PropTypes.oneOfType([
|
children: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.object,
|
React.PropTypes.object,
|
||||||
React.PropTypes.array
|
React.PropTypes.array
|
||||||
@ -47,6 +50,9 @@ let Form = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getFormData(){
|
getFormData(){
|
||||||
|
if ('getFormData' in this.props){
|
||||||
|
return this.props.getFormData();
|
||||||
|
}
|
||||||
let data = {};
|
let data = {};
|
||||||
for (let ref in this.refs){
|
for (let ref in this.refs){
|
||||||
data[this.refs[ref].props.name] = this.refs[ref].state.value;
|
data[this.refs[ref].props.name] = this.refs[ref].state.value;
|
||||||
@ -66,7 +72,7 @@ let Form = React.createClass({
|
|||||||
this.refs[ref].handleSuccess();
|
this.refs[ref].handleSuccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({edited: false});
|
this.setState({edited: false, submitted: false});
|
||||||
},
|
},
|
||||||
handleError(err){
|
handleError(err){
|
||||||
if (err.json) {
|
if (err.json) {
|
||||||
@ -102,9 +108,11 @@ let Form = React.createClass({
|
|||||||
|
|
||||||
if (this.state.edited){
|
if (this.state.edited){
|
||||||
buttons = (
|
buttons = (
|
||||||
<div className="pull-right">
|
<div className="row" style={{margin: 0}}>
|
||||||
<Button className="ascribe-btn" type="submit">Save</Button>
|
<p className="pull-right">
|
||||||
<Button className="ascribe-btn" onClick={this.reset}>Cancel</Button>
|
<Button className="ascribe-btn" type="submit">Save</Button>
|
||||||
|
<Button className="ascribe-btn" onClick={this.reset}>Cancel</Button>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5,47 +5,58 @@ import React from 'react';
|
|||||||
import requests from '../../utils/requests';
|
import requests from '../../utils/requests';
|
||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import apiUrls from '../../constants/api_urls';
|
||||||
import FormMixin from '../../mixins/form_mixin';
|
|
||||||
|
|
||||||
|
import Form from './form';
|
||||||
|
import Property from './property';
|
||||||
import InputTextAreaToggable from './input_textarea_toggable';
|
import InputTextAreaToggable from './input_textarea_toggable';
|
||||||
|
|
||||||
|
|
||||||
let PieceExtraDataForm = React.createClass({
|
let PieceExtraDataForm = React.createClass({
|
||||||
mixins: [FormMixin],
|
propTypes: {
|
||||||
|
edition: React.PropTypes.object,
|
||||||
url() {
|
handleSuccess: React.PropTypes.func,
|
||||||
return requests.prepareUrl(apiUrls.piece_extradata, {piece_id: this.props.editions[0].bitcoin_id});
|
name: React.PropTypes.string,
|
||||||
|
title: React.PropTypes.string,
|
||||||
|
editable: React.PropTypes.bool
|
||||||
},
|
},
|
||||||
|
getFormData(){
|
||||||
getFormData() {
|
|
||||||
let extradata = {};
|
let extradata = {};
|
||||||
extradata[this.props.name] = this.refs[this.props.name].state.value;
|
extradata[this.props.name] = this.refs.form.refs[this.props.name].state.value;
|
||||||
return {
|
return {
|
||||||
bitcoin_id: this.getBitcoinIds().join(),
|
bitcoin_id: this.props.edition.bitcoin_id,
|
||||||
extradata: extradata
|
extradata: extradata
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
render() {
|
||||||
renderForm() {
|
let defaultValue = this.props.edition.extra_data[this.props.name] || '';
|
||||||
let defaultValue = this.props.editions[0].extra_data[this.props.name] || '';
|
|
||||||
if (defaultValue.length === 0 && !this.props.editable){
|
if (defaultValue.length === 0 && !this.props.editable){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
let url = requests.prepareUrl(apiUrls.piece_extradata, {piece_id: this.props.edition.bitcoin_id});
|
||||||
return (
|
return (
|
||||||
<form role="form" key={this.props.name}>
|
<Form
|
||||||
<h5>{this.props.title}</h5>
|
ref='form'
|
||||||
<InputTextAreaToggable
|
url={url}
|
||||||
ref={this.props.name}
|
handleSuccess={this.props.handleSuccess}
|
||||||
className="form-control"
|
getFormData={this.getFormData}>
|
||||||
defaultValue={defaultValue}
|
<Property
|
||||||
rows={3}
|
name={this.props.name}
|
||||||
editable={this.props.editable}
|
label={this.props.title}
|
||||||
required=""
|
editable={this.props.editable}>
|
||||||
onSubmit={this.submit}
|
<InputTextAreaToggable
|
||||||
/>
|
rows={3}
|
||||||
</form>
|
editable={this.props.editable}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
placeholder={'Fill in ' + this.props.title}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property hidden={true} name='bitcoin_id'>
|
||||||
|
<input defaultValue={this.props.edition.bitcoin_id}/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default PieceExtraDataForm;
|
export default PieceExtraDataForm;
|
||||||
|
@ -12,7 +12,7 @@ let ShareForm = React.createClass({
|
|||||||
mixins: [FormMixin],
|
mixins: [FormMixin],
|
||||||
|
|
||||||
url() {
|
url() {
|
||||||
return ApiUrls.ownership_shares_mail;
|
return ApiUrls.ownership_shares;
|
||||||
},
|
},
|
||||||
|
|
||||||
getFormData() {
|
getFormData() {
|
||||||
|
@ -2,56 +2,29 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import AlertMixin from '../../mixins/alert_mixin';
|
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
|
||||||
|
|
||||||
let InputTextAreaToggable = React.createClass({
|
let InputTextAreaToggable = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
editable: React.PropTypes.bool.isRequired,
|
editable: React.PropTypes.bool.isRequired,
|
||||||
submitted: React.PropTypes.bool,
|
|
||||||
rows: React.PropTypes.number.isRequired,
|
rows: React.PropTypes.number.isRequired,
|
||||||
onSubmit: React.PropTypes.func.isRequired,
|
|
||||||
required: React.PropTypes.string,
|
required: React.PropTypes.string,
|
||||||
defaultValue: React.PropTypes.string
|
defaultValue: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [AlertMixin],
|
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
value: this.props.defaultValue,
|
value: this.props.defaultValue
|
||||||
edited: false,
|
|
||||||
alerts: null // needed in AlertMixin
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleChange(event) {
|
handleChange(event) {
|
||||||
this.setState({
|
this.setState({value: event.target.value});
|
||||||
value: event.target.value,
|
this.props.onChange(event);
|
||||||
edited: true
|
|
||||||
});
|
|
||||||
},
|
|
||||||
reset(){
|
|
||||||
this.setState(this.getInitialState());
|
|
||||||
},
|
|
||||||
submit(){
|
|
||||||
this.props.onSubmit();
|
|
||||||
this.setState({edited: false});
|
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
let className = 'form-control ascribe-textarea';
|
let className = 'form-control ascribe-textarea';
|
||||||
let buttons = null;
|
|
||||||
let textarea = null;
|
let textarea = null;
|
||||||
if (this.props.editable && this.state.edited){
|
|
||||||
buttons = (
|
|
||||||
<div className="pull-right">
|
|
||||||
<Button className="ascribe-btn" onClick={this.submit}>Save</Button>
|
|
||||||
<Button className="ascribe-btn" onClick={this.reset}>Cancel</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (this.props.editable){
|
if (this.props.editable){
|
||||||
className = className + ' ascribe-textarea-editable';
|
className = className + ' ascribe-textarea-editable';
|
||||||
textarea = (
|
textarea = (
|
||||||
@ -61,21 +34,16 @@ let InputTextAreaToggable = React.createClass({
|
|||||||
rows={this.props.rows}
|
rows={this.props.rows}
|
||||||
required={this.props.required}
|
required={this.props.required}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
placeholder='Write something...' />
|
onBlur={this.props.onBlur}
|
||||||
|
placeholder={this.props.placeholder} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
textarea = <pre className="ascribe-pre">{this.state.value}</pre>;
|
textarea = <pre className="ascribe-pre">{this.state.value}</pre>;
|
||||||
}
|
}
|
||||||
let alerts = (this.props.submitted) ? null : this.state.alerts;
|
return textarea;
|
||||||
return (
|
|
||||||
<div className="form-group">
|
|
||||||
{alerts}
|
|
||||||
{textarea}
|
|
||||||
{buttons}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default InputTextAreaToggable;
|
export default InputTextAreaToggable;
|
@ -8,7 +8,9 @@ import Tooltip from 'react-bootstrap/lib/Tooltip';
|
|||||||
|
|
||||||
let Property = React.createClass({
|
let Property = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
hidden: React.PropTypes.bool,
|
||||||
editable: React.PropTypes.bool,
|
editable: React.PropTypes.bool,
|
||||||
|
tooltip: React.PropTypes.element,
|
||||||
label: React.PropTypes.string,
|
label: React.PropTypes.string,
|
||||||
value: React.PropTypes.oneOfType([
|
value: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
@ -19,7 +21,8 @@ let Property = React.createClass({
|
|||||||
|
|
||||||
getDefaultProps() {
|
getDefaultProps() {
|
||||||
return {
|
return {
|
||||||
editable: true
|
editable: true,
|
||||||
|
hidden: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -38,8 +41,16 @@ let Property = React.createClass({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
reset(){
|
reset(){
|
||||||
|
// maybe do reset by reload instead of frontend state?
|
||||||
this.setState({value: this.state.initialValue});
|
this.setState({value: this.state.initialValue});
|
||||||
this.refs.input.getDOMNode().value = this.state.initialValue;
|
if (this.refs.input.state){
|
||||||
|
// This is probably not the right way but easy fix
|
||||||
|
this.refs.input.state.value = this.state.initialValue;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this.refs.input.getDOMNode().value = this.state.initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleChange(event) {
|
handleChange(event) {
|
||||||
@ -74,6 +85,9 @@ let Property = React.createClass({
|
|||||||
this.setState({errors: null});
|
this.setState({errors: null});
|
||||||
},
|
},
|
||||||
getClassName() {
|
getClassName() {
|
||||||
|
if(this.props.hidden){
|
||||||
|
return 'is-hidden';
|
||||||
|
}
|
||||||
if(!this.props.editable){
|
if(!this.props.editable){
|
||||||
return 'is-fixed';
|
return 'is-fixed';
|
||||||
}
|
}
|
||||||
|
122
js/components/ascribe_uploader/file_drag_and_drop.js
Normal file
122
js/components/ascribe_uploader/file_drag_and_drop.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
|
||||||
|
|
||||||
|
|
||||||
|
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop
|
||||||
|
var FileDragAndDrop = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
onDragStart: React.PropTypes.func,
|
||||||
|
onDrop: React.PropTypes.func.isRequired,
|
||||||
|
onDragEnter: React.PropTypes.func,
|
||||||
|
onLeave: React.PropTypes.func,
|
||||||
|
onDragOver: React.PropTypes.func,
|
||||||
|
onDragEnd: React.PropTypes.func,
|
||||||
|
filesToUpload: React.PropTypes.array,
|
||||||
|
handleDeleteFile: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragStart(event) {
|
||||||
|
if (typeof this.props.onDragStart === 'function') {
|
||||||
|
this.props.onDragStart(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDrag(event) {
|
||||||
|
if (typeof this.props.onDrag === 'function') {
|
||||||
|
this.props.onDrag(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragEnd(event) {
|
||||||
|
if (typeof this.props.onDragEnd === 'function') {
|
||||||
|
this.props.onDragEnd(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragEnter(event) {
|
||||||
|
if (typeof this.props.onDragEnter === 'function') {
|
||||||
|
this.props.onDragEnter(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragLeave(event) {
|
||||||
|
if (typeof this.props.onDragLeave === 'function') {
|
||||||
|
this.props.onDragLeave(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragOver(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (typeof this.props.onDragOver === 'function') {
|
||||||
|
this.props.onDragOver(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
handleDrop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
let files;
|
||||||
|
|
||||||
|
// handle Drag and Drop
|
||||||
|
if(event.dataTransfer && event.dataTransfer.files.length > 0) {
|
||||||
|
files = event.dataTransfer.files;
|
||||||
|
} else if(event.target.files) { // handle input type file
|
||||||
|
files = event.target.files;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof this.props.onDrop === 'function' && files) {
|
||||||
|
this.props.onDrop(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeleteFile(fileId) {
|
||||||
|
// input's value is not change the second time someone
|
||||||
|
// inputs the same file again, therefore we need to reset its value
|
||||||
|
this.refs.fileinput.getDOMNode().value = '';
|
||||||
|
this.props.handleDeleteFile(fileId);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOnClick() {
|
||||||
|
// Simulate click on hidden file input
|
||||||
|
var event = document.createEvent('HTMLEvents');
|
||||||
|
event.initEvent('click', false, true);
|
||||||
|
this.refs.fileinput.getDOMNode().dispatchEvent(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
let hasFiles = this.props.filesToUpload.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={hasFiles ? 'file-drag-and-drop has-files' : 'file-drag-and-drop' }
|
||||||
|
onClick={this.handleOnClick}
|
||||||
|
onDragStart={this.handleDragStart}
|
||||||
|
onDrag={this.handleDrop}
|
||||||
|
onDragEnter={this.handleDragEnter}
|
||||||
|
onDragLeave={this.handleDragLeave}
|
||||||
|
onDragOver={this.handleDragOver}
|
||||||
|
onDrop={this.handleDrop}
|
||||||
|
onDragEnd={this.handleDragEnd}>
|
||||||
|
{hasFiles ? null : <span>Click or drag to add files</span>}
|
||||||
|
<FileDragAndDropPreviewIterator
|
||||||
|
files={this.props.filesToUpload}
|
||||||
|
handleDeleteFile={this.handleDeleteFile}/>
|
||||||
|
<input
|
||||||
|
multiple
|
||||||
|
ref="fileinput"
|
||||||
|
type="file"
|
||||||
|
style={{
|
||||||
|
display: 'none',
|
||||||
|
height: 0,
|
||||||
|
width: 0
|
||||||
|
}}
|
||||||
|
onChange={this.handleDrop} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = FileDragAndDrop;
|
52
js/components/ascribe_uploader/file_drag_and_drop_preview.js
Normal file
52
js/components/ascribe_uploader/file_drag_and_drop_preview.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image';
|
||||||
|
import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other';
|
||||||
|
|
||||||
|
|
||||||
|
let FileDragAndDropPreview = React.createClass({
|
||||||
|
|
||||||
|
propsTypes: {
|
||||||
|
file: React.PropTypes.shape({
|
||||||
|
url: React.PropTypes.string,
|
||||||
|
type: React.PropTypes.string
|
||||||
|
}).isRequired,
|
||||||
|
handleDeleteFile: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeleteFile(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
// handleDeleteFile is optional, so if its not submitted,
|
||||||
|
// don't run it
|
||||||
|
if(this.props.handleDeleteFile) {
|
||||||
|
this.props.handleDeleteFile(this.props.file.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let previewElement;
|
||||||
|
|
||||||
|
// Decide wether an image or a placeholder picture should be displayed
|
||||||
|
if(this.props.file.type.split('/')[0] === 'image') {
|
||||||
|
previewElement = (<FileDragAndDropPreviewImage
|
||||||
|
progress={this.props.file.progress}
|
||||||
|
url={this.props.file.url}/>);
|
||||||
|
} else {
|
||||||
|
previewElement = (<FileDragAndDropPreviewOther
|
||||||
|
progress={this.props.file.progress}
|
||||||
|
type={this.props.file.type.split('/')[1]}/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="file-drag-and-drop-position"
|
||||||
|
onClick={this.handleDeleteFile}>
|
||||||
|
{previewElement}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FileDragAndDropPreview;
|
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ProgressBar from 'react-progressbar';
|
||||||
|
|
||||||
|
let FileDragAndDropPreviewImage = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
progress: React.PropTypes.number,
|
||||||
|
url: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let imageStyle = {
|
||||||
|
backgroundImage: 'url("' + this.props.url + '")',
|
||||||
|
backgroundSize: 'cover'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="file-drag-and-drop-preview-image"
|
||||||
|
style={imageStyle}>
|
||||||
|
<ProgressBar completed={this.props.progress} color="black"/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FileDragAndDropPreviewImage;
|
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import FileDragAndDropPreview from './file_drag_and_drop_preview';
|
||||||
|
|
||||||
|
let FileDragAndDropPreviewIterator = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
files: React.PropTypes.array,
|
||||||
|
handleDeleteFile: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if(this.props.files) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.props.files.map((file, i) => {
|
||||||
|
return (
|
||||||
|
<FileDragAndDropPreview
|
||||||
|
key={i}
|
||||||
|
file={file}
|
||||||
|
handleDeleteFile={this.props.handleDeleteFile}/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FileDragAndDropPreviewIterator;
|
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ProgressBar from 'react-progressbar';
|
||||||
|
|
||||||
|
let FileDragAndDropPreviewOther = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
type: React.PropTypes.string,
|
||||||
|
progress: React.PropTypes.number
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return(
|
||||||
|
<div
|
||||||
|
className="file-drag-and-drop-preview">
|
||||||
|
<ProgressBar completed={this.props.progress} color="black"/>
|
||||||
|
<div className="file-drag-and-drop-preview-table-wrapper">
|
||||||
|
<div className="file-drag-and-drop-preview-other">
|
||||||
|
<span>{'.' + this.props.type}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FileDragAndDropPreviewOther;
|
306
js/components/ascribe_uploader/react_s3_fine_uploader.js
Normal file
306
js/components/ascribe_uploader/react_s3_fine_uploader.js
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react/addons';
|
||||||
|
|
||||||
|
import promise from 'es6-promise';
|
||||||
|
promise.polyfill();
|
||||||
|
|
||||||
|
import fetch from 'isomorphic-fetch';
|
||||||
|
|
||||||
|
import fineUploader from 'fineUploader';
|
||||||
|
import FileDragAndDrop from './file_drag_and_drop';
|
||||||
|
|
||||||
|
var ReactS3FineUploader = React.createClass({
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
keyRoutine: React.PropTypes.shape({
|
||||||
|
url: React.PropTypes.string,
|
||||||
|
fileClass: React.PropTypes.string
|
||||||
|
}),
|
||||||
|
createBlobRoutine: React.PropTypes.shape({
|
||||||
|
url: React.PropTypes.string
|
||||||
|
}),
|
||||||
|
handleChange: React.PropTypes.func,
|
||||||
|
autoUpload: React.PropTypes.bool,
|
||||||
|
debug: React.PropTypes.bool,
|
||||||
|
objectProperties: React.PropTypes.shape({
|
||||||
|
acl: React.PropTypes.string
|
||||||
|
}),
|
||||||
|
request: React.PropTypes.shape({
|
||||||
|
endpoint: React.PropTypes.string,
|
||||||
|
accessKey: React.PropTypes.string,
|
||||||
|
params: React.PropTypes.shape({
|
||||||
|
csrfmiddlewaretoken: React.PropTypes.string
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
signature: React.PropTypes.shape({
|
||||||
|
endpoint: React.PropTypes.string
|
||||||
|
}),
|
||||||
|
uploadSuccess: React.PropTypes.shape({
|
||||||
|
method: React.PropTypes.string,
|
||||||
|
endpoint: React.PropTypes.string,
|
||||||
|
params: React.PropTypes.shape({
|
||||||
|
isBrowserPreviewCapable: React.PropTypes.any, // maybe fix this later
|
||||||
|
bitcoin_ID_noPrefix: React.PropTypes.string
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
cors: React.PropTypes.shape({
|
||||||
|
expected: React.PropTypes.bool
|
||||||
|
}),
|
||||||
|
chunking: React.PropTypes.shape({
|
||||||
|
enabled: React.PropTypes.bool
|
||||||
|
}),
|
||||||
|
resume: React.PropTypes.shape({
|
||||||
|
enabled: React.PropTypes.bool
|
||||||
|
}),
|
||||||
|
deleteFile: React.PropTypes.shape({
|
||||||
|
enabled: React.PropTypes.bool,
|
||||||
|
method: React.PropTypes.string,
|
||||||
|
endpoint: React.PropTypes.string
|
||||||
|
}),
|
||||||
|
session: React.PropTypes.shape({
|
||||||
|
endpoint: React.PropTypes.bool
|
||||||
|
}),
|
||||||
|
validation: React.PropTypes.shape({
|
||||||
|
itemLimit: React.PropTypes.number,
|
||||||
|
sizeLimit: React.PropTypes.string
|
||||||
|
}),
|
||||||
|
messages: React.PropTypes.shape({
|
||||||
|
unsupportedBrowser: React.PropTypes.string
|
||||||
|
}),
|
||||||
|
formatFileName: React.PropTypes.func,
|
||||||
|
multiple: React.PropTypes.bool,
|
||||||
|
retry: React.PropTypes.shape({
|
||||||
|
enableAuto: React.PropTypes.bool
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
filesToUpload: [],
|
||||||
|
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig())
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
propsToConfig() {
|
||||||
|
let objectProperties = this.props.objectProperties;
|
||||||
|
objectProperties.key = this.requestKey;
|
||||||
|
|
||||||
|
return {
|
||||||
|
autoUpload: this.props.autoUpload,
|
||||||
|
debug: this.props.debug,
|
||||||
|
objectProperties: objectProperties, // do a special key handling here
|
||||||
|
request: this.props.request,
|
||||||
|
signature: this.props.signature,
|
||||||
|
uploadSuccess: this.props.uploadSuccess,
|
||||||
|
cors: this.props.cors,
|
||||||
|
chunking: this.props.chunking,
|
||||||
|
resume: this.props.resume,
|
||||||
|
deleteFile: this.props.deleteFile,
|
||||||
|
session: this.props.session,
|
||||||
|
validation: this.props.validation,
|
||||||
|
messages: this.props.messages,
|
||||||
|
formatFileName: this.props.formatFileName,
|
||||||
|
multiple: this.props.multiple,
|
||||||
|
retry: this.props.retry,
|
||||||
|
callbacks: {
|
||||||
|
onSubmit: this.onSubmit,
|
||||||
|
onComplete: this.onComplete,
|
||||||
|
onDelete: this.onDelete,
|
||||||
|
onSessionRequestComplete: this.onSessionRequestComplete,
|
||||||
|
onProgress: this.onProgress,
|
||||||
|
onRetry: this.onRetry,
|
||||||
|
onAutoRetry: this.onAutoRetry,
|
||||||
|
onManualRetry: this.onManualRetry,
|
||||||
|
onDeleteComplete: this.onDeleteComplete
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getCookie(name) {
|
||||||
|
console.log(document.cookie);
|
||||||
|
let value = '; ' + document.cookie;
|
||||||
|
let parts = value.split('; ' + name + '=');
|
||||||
|
if (parts.length === 2) {
|
||||||
|
return parts.pop().split(';').shift();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
requestKey(fileId) {
|
||||||
|
let filename = this.state.uploader.getName(fileId);
|
||||||
|
let defer = new fineUploader.Promise();
|
||||||
|
fetch(this.props.keyRoutine.url, {
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': this.getCookie('csrftoken')
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify({
|
||||||
|
'filename': filename,
|
||||||
|
'file_class': 'digitalwork'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) =>{
|
||||||
|
defer.success(res.key);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
return defer;
|
||||||
|
},
|
||||||
|
|
||||||
|
/* FineUploader specific callback function handlers */
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
console.log('submit');
|
||||||
|
},
|
||||||
|
|
||||||
|
onComplete(id) {
|
||||||
|
let files = this.state.filesToUpload;
|
||||||
|
files[id].status = 'upload successful';
|
||||||
|
files[id].key = this.state.uploader.getKey(id);
|
||||||
|
|
||||||
|
let newState = React.addons.update(this.state, {
|
||||||
|
filesToUpload: { $set: files }
|
||||||
|
});
|
||||||
|
this.setState(newState);
|
||||||
|
this.createBlob(files[id]);
|
||||||
|
this.props.handleChange();
|
||||||
|
console.log('completed ' + files[id].name);
|
||||||
|
},
|
||||||
|
|
||||||
|
createBlob(file) {
|
||||||
|
let defer = new fineUploader.Promise();
|
||||||
|
fetch(this.props.createBlobRoutine.url, {
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': this.getCookie('csrftoken')
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify({
|
||||||
|
'filename': file.name,
|
||||||
|
'key': file.key
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) =>{
|
||||||
|
defer.success(res.key);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
return defer;
|
||||||
|
},
|
||||||
|
|
||||||
|
onRetry() {
|
||||||
|
console.log('retry');
|
||||||
|
},
|
||||||
|
|
||||||
|
onAutoRetry() {
|
||||||
|
console.log('autoretry');
|
||||||
|
},
|
||||||
|
|
||||||
|
onManualRetry() {
|
||||||
|
console.log('manualretry');
|
||||||
|
},
|
||||||
|
|
||||||
|
onDelete() {
|
||||||
|
console.log('delete');
|
||||||
|
},
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
console.log('cancel');
|
||||||
|
},
|
||||||
|
|
||||||
|
onSessionRequestComplete() {
|
||||||
|
console.log('sessionrequestcomplete');
|
||||||
|
},
|
||||||
|
|
||||||
|
onDeleteComplete(id, xhr, isError) {
|
||||||
|
if(isError) {
|
||||||
|
// also, sync files from state with the ones from fineuploader
|
||||||
|
let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload));
|
||||||
|
// splice because I can
|
||||||
|
filesToUpload.splice(id, 1);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
this.setState({
|
||||||
|
filesToUpload: React.addons.update(this.state.filesToUpload, {$set: filesToUpload})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(id);
|
||||||
|
// TODO: add global notification
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onProgress(id, name, uploadedBytes, totalBytes) {
|
||||||
|
var newState = React.addons.update(this.state, {
|
||||||
|
filesToUpload: { [id]: {
|
||||||
|
progress: { $set: (uploadedBytes / totalBytes) * 100} }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeleteFile(fileId) {
|
||||||
|
// delete file from server
|
||||||
|
this.state.uploader.deleteFile(fileId);
|
||||||
|
// this is being continues in onDeleteFile, as
|
||||||
|
// fineuploaders deleteFile does not return a correct callback or
|
||||||
|
// promise
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUploadFile(files) {
|
||||||
|
this.state.uploader.addFiles(files);
|
||||||
|
let oldFiles = this.state.filesToUpload;
|
||||||
|
let oldAndNewFiles = this.state.uploader.getUploads();
|
||||||
|
|
||||||
|
// Add fineuploader specific information to new files
|
||||||
|
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
||||||
|
for(let j = 0; j < files.length; j++) {
|
||||||
|
if(oldAndNewFiles[i].originalName === files[j].name) {
|
||||||
|
oldAndNewFiles[i].progress = 0;
|
||||||
|
oldAndNewFiles[i].type = files[j].type;
|
||||||
|
oldAndNewFiles[i].url = URL.createObjectURL(files[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// and re-add fineuploader specific information for old files as well
|
||||||
|
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
||||||
|
for(let j = 0; j < oldFiles.length; j++) {
|
||||||
|
if(oldAndNewFiles[i].originalName === oldFiles[j].name) {
|
||||||
|
oldAndNewFiles[i].progress = oldFiles[j].progress;
|
||||||
|
oldAndNewFiles[i].type = oldFiles[j].type;
|
||||||
|
oldAndNewFiles[i].url = oldFiles[j].url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let newState = React.addons.update(this.state, {
|
||||||
|
filesToUpload: { $set: oldAndNewFiles }
|
||||||
|
});
|
||||||
|
this.setState(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<FileDragAndDrop
|
||||||
|
onDrop={this.handleUploadFile}
|
||||||
|
filesToUpload={this.state.filesToUpload}
|
||||||
|
handleDeleteFile={this.handleDeleteFile}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default ReactS3FineUploader;
|
14408
js/components/ascribe_uploader/vendor/s3.fine-uploader.js
vendored
Normal file
14408
js/components/ascribe_uploader/vendor/s3.fine-uploader.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
js/components/ascribe_uploader/vendor/s3.fine-uploader.min.js
vendored
Normal file
22
js/components/ascribe_uploader/vendor/s3.fine-uploader.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -2,19 +2,21 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import Row from 'react-bootstrap/lib/Row';
|
||||||
|
import Col from 'react-bootstrap/lib/Col';
|
||||||
|
import Button from 'react-bootstrap/lib/Button';
|
||||||
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||||
|
|
||||||
import UserActions from '../actions/user_actions';
|
import UserActions from '../actions/user_actions';
|
||||||
import UserStore from '../stores/user_store';
|
import UserStore from '../stores/user_store';
|
||||||
|
|
||||||
import MediaPlayer from './ascribe_media/media_player';
|
import MediaPlayer from './ascribe_media/media_player';
|
||||||
|
|
||||||
import CollapsibleMixin from 'react-bootstrap/lib/CollapsibleMixin';
|
import CollapsibleParagraph from './ascribe_collapsible/collapsible_paragraph';
|
||||||
import Row from 'react-bootstrap/lib/Row';
|
|
||||||
import Col from 'react-bootstrap/lib/Col';
|
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
|
||||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
|
||||||
|
|
||||||
import PersonalNoteForm from './ascribe_forms/form_note_personal';
|
import Form from './ascribe_forms/form';
|
||||||
import EditionNoteForm from './ascribe_forms/form_note_edition';
|
import Property from './ascribe_forms/property';
|
||||||
|
import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable';
|
||||||
|
|
||||||
import PieceExtraDataForm from './ascribe_forms/form_piece_extradata';
|
import PieceExtraDataForm from './ascribe_forms/form_piece_extradata';
|
||||||
import RequestActionForm from './ascribe_forms/form_request_action';
|
import RequestActionForm from './ascribe_forms/form_request_action';
|
||||||
@ -25,7 +27,8 @@ import AclButtonList from './ascribe_buttons/acl_button_list';
|
|||||||
import GlobalNotificationModel from '../models/global_notification_model';
|
import GlobalNotificationModel from '../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../actions/global_notification_actions';
|
import GlobalNotificationActions from '../actions/global_notification_actions';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import requests from '../utils/requests';
|
||||||
|
import apiUrls from '../constants/api_urls';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that implements display-specific functionality
|
* This is the component that implements display-specific functionality
|
||||||
@ -92,64 +95,72 @@ let Edition = React.createClass({
|
|||||||
<EditionSummary
|
<EditionSummary
|
||||||
edition={this.props.edition} />
|
edition={this.props.edition} />
|
||||||
|
|
||||||
<CollapsibleEditionDetails
|
<CollapsibleParagraph
|
||||||
title="Personal Note (private)"
|
title="Notes"
|
||||||
show={this.state.currentUser.username && true || false}>
|
show={(this.state.currentUser.username && true || false) ||
|
||||||
|
(this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note)}>
|
||||||
<EditionPersonalNote
|
<EditionPersonalNote
|
||||||
currentUser={this.state.currentUser}
|
currentUser={this.state.currentUser}
|
||||||
handleSuccess={this.props.loadEdition}
|
handleSuccess={this.props.loadEdition}
|
||||||
edition={this.props.edition}/>
|
edition={this.props.edition}/>
|
||||||
</CollapsibleEditionDetails>
|
|
||||||
|
|
||||||
<CollapsibleEditionDetails
|
|
||||||
title="Edition Note (public)"
|
|
||||||
show={this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note}>
|
|
||||||
<EditionPublicEditionNote
|
<EditionPublicEditionNote
|
||||||
handleSuccess={this.props.loadEdition}
|
handleSuccess={this.props.loadEdition}
|
||||||
edition={this.props.edition}/>
|
edition={this.props.edition}/>
|
||||||
</CollapsibleEditionDetails>
|
</CollapsibleParagraph>
|
||||||
|
|
||||||
<CollapsibleEditionDetails
|
<CollapsibleParagraph
|
||||||
title="Further Details (all editions)"
|
title="Further Details (all editions)"
|
||||||
show={this.props.edition.acl.indexOf('edit') > -1 || Object.keys(this.props.edition.extra_data).length > 0}>
|
show={this.props.edition.acl.indexOf('edit') > -1 || Object.keys(this.props.edition.extra_data).length > 0}>
|
||||||
<EditionFurtherDetails
|
<EditionFurtherDetails
|
||||||
handleSuccess={this.props.loadEdition}
|
handleSuccess={this.props.loadEdition}
|
||||||
edition={this.props.edition}/>
|
edition={this.props.edition}/>
|
||||||
</CollapsibleEditionDetails>
|
</CollapsibleParagraph>
|
||||||
|
|
||||||
<CollapsibleEditionDetails
|
<CollapsibleParagraph
|
||||||
title="Provenance/Ownership History"
|
title="Provenance/Ownership History"
|
||||||
show={this.props.edition.ownership_history && this.props.edition.ownership_history.length > 0}>
|
show={this.props.edition.ownership_history && this.props.edition.ownership_history.length > 0}>
|
||||||
<EditionDetailHistoryIterator
|
<EditionDetailHistoryIterator
|
||||||
history={this.props.edition.ownership_history} />
|
history={this.props.edition.ownership_history} />
|
||||||
</CollapsibleEditionDetails>
|
</CollapsibleParagraph>
|
||||||
|
|
||||||
<CollapsibleEditionDetails
|
<CollapsibleParagraph
|
||||||
title="Consignment History"
|
title="Consignment History"
|
||||||
show={this.props.edition.consign_history && this.props.edition.consign_history.length > 0}>
|
show={this.props.edition.consign_history && this.props.edition.consign_history.length > 0}>
|
||||||
<EditionDetailHistoryIterator
|
<EditionDetailHistoryIterator
|
||||||
history={this.props.edition.consign_history} />
|
history={this.props.edition.consign_history} />
|
||||||
</CollapsibleEditionDetails>
|
</CollapsibleParagraph>
|
||||||
|
|
||||||
<CollapsibleEditionDetails
|
<CollapsibleParagraph
|
||||||
title="Loan History"
|
title="Loan History"
|
||||||
show={this.props.edition.loan_history && this.props.edition.loan_history.length > 0}>
|
show={this.props.edition.loan_history && this.props.edition.loan_history.length > 0}>
|
||||||
<EditionDetailHistoryIterator
|
<EditionDetailHistoryIterator
|
||||||
history={this.props.edition.loan_history} />
|
history={this.props.edition.loan_history} />
|
||||||
</CollapsibleEditionDetails>
|
</CollapsibleParagraph>
|
||||||
|
|
||||||
<CollapsibleEditionDetails
|
<CollapsibleParagraph
|
||||||
title="SPOOL Details">
|
title="SPOOL Details">
|
||||||
<EditionDetailProperty
|
<Form >
|
||||||
label="Artwork ID"
|
<Property
|
||||||
value={bitcoinIdValue} />
|
name='artwork_id'
|
||||||
<EditionDetailProperty
|
label="Artwork ID"
|
||||||
label="Hash of Artwork, title, etc"
|
editable={false}>
|
||||||
value={hashOfArtwork} />
|
<pre className="ascribe-pre">{bitcoinIdValue}</pre>
|
||||||
<EditionDetailProperty
|
</Property>
|
||||||
label="Owned by SPOOL address"
|
<Property
|
||||||
value={ownerAddress} />
|
name='hash_of_artwork'
|
||||||
</CollapsibleEditionDetails>
|
label="Hash of Artwork, title, etc"
|
||||||
|
editable={false}>
|
||||||
|
<pre className="ascribe-pre">{hashOfArtwork}</pre>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='owner_address'
|
||||||
|
label="Owned by SPOOL address"
|
||||||
|
editable={false}>
|
||||||
|
<pre className="ascribe-pre">{ownerAddress}</pre>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
</CollapsibleParagraph>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
@ -229,85 +240,6 @@ let EditionSummary = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let CollapsibleEditionDetails = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
title: React.PropTypes.string,
|
|
||||||
children: React.PropTypes.oneOfType([
|
|
||||||
React.PropTypes.object,
|
|
||||||
React.PropTypes.array
|
|
||||||
]),
|
|
||||||
show: React.PropTypes.bool,
|
|
||||||
iconName: React.PropTypes.string
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
show: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if(this.props.show) {
|
|
||||||
return (
|
|
||||||
<div className="ascribe-detail-header">
|
|
||||||
<CollapsibleParagraph
|
|
||||||
title={this.props.title}
|
|
||||||
iconName={this.props.iconName}>
|
|
||||||
{this.props.children}
|
|
||||||
</CollapsibleParagraph>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const CollapsibleParagraph = React.createClass({
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
title: React.PropTypes.string,
|
|
||||||
children: React.PropTypes.oneOfType([
|
|
||||||
React.PropTypes.object,
|
|
||||||
React.PropTypes.array
|
|
||||||
]),
|
|
||||||
iconName: React.PropTypes.string
|
|
||||||
},
|
|
||||||
|
|
||||||
mixins: [CollapsibleMixin],
|
|
||||||
|
|
||||||
getCollapsibleDOMNode(){
|
|
||||||
return React.findDOMNode(this.refs.panel);
|
|
||||||
},
|
|
||||||
|
|
||||||
getCollapsibleDimensionValue(){
|
|
||||||
return React.findDOMNode(this.refs.panel).scrollHeight;
|
|
||||||
},
|
|
||||||
|
|
||||||
onHandleToggle(e){
|
|
||||||
e.preventDefault();
|
|
||||||
this.setState({expanded: !this.state.expanded});
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let styles = this.getCollapsibleClassSet();
|
|
||||||
let text = this.isExpanded() ? '-' : '+';
|
|
||||||
|
|
||||||
let icon = this.props.iconName ? (<Glyphicon style={{fontSize: '.75em'}} glyph={this.props.iconName} />) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="ascribe-edition-collapsible-wrapper">
|
|
||||||
<div onClick={this.onHandleToggle}>
|
|
||||||
<span>{text} {icon} {this.props.title} </span>
|
|
||||||
</div>
|
|
||||||
<div ref='panel' className={classNames(styles) + ' ascribe-edition-collapible-content'}>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let EditionDetailProperty = React.createClass({
|
let EditionDetailProperty = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -372,6 +304,7 @@ let EditionDetailHistoryIterator = React.createClass({
|
|||||||
let EditionPersonalNote = React.createClass({
|
let EditionPersonalNote = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
edition: React.PropTypes.object,
|
edition: React.PropTypes.object,
|
||||||
|
currentUser: React.PropTypes.object,
|
||||||
handleSuccess: React.PropTypes.func
|
handleSuccess: React.PropTypes.func
|
||||||
},
|
},
|
||||||
showNotification(){
|
showNotification(){
|
||||||
@ -379,21 +312,35 @@ let EditionPersonalNote = React.createClass({
|
|||||||
let notification = new GlobalNotificationModel('Private note saved', 'success');
|
let notification = new GlobalNotificationModel('Private note saved', 'success');
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
if (this.props.currentUser.username && true || false) {
|
||||||
<Row>
|
return (
|
||||||
<Col md={12} className="ascribe-edition-personal-note">
|
<Form
|
||||||
<PersonalNoteForm
|
url={apiUrls.note_notes}
|
||||||
editable={true}
|
handleSuccess={this.showNotification}>
|
||||||
handleSuccess={this.showNotification}
|
<Property
|
||||||
editions={[this.props.edition]} />
|
name='note'
|
||||||
</Col>
|
label='Personal note (private)'
|
||||||
</Row>
|
editable={true}>
|
||||||
);
|
<InputTextAreaToggable
|
||||||
|
rows={3}
|
||||||
|
editable={true}
|
||||||
|
defaultValue={this.props.edition.note_from_user}
|
||||||
|
placeholder='Enter a personal note...'
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property hidden={true} name='bitcoin_id'>
|
||||||
|
<input defaultValue={this.props.edition.bitcoin_id}/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
let EditionPublicEditionNote = React.createClass({
|
let EditionPublicEditionNote = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
edition: React.PropTypes.object,
|
edition: React.PropTypes.object,
|
||||||
@ -405,16 +352,31 @@ let EditionPublicEditionNote = React.createClass({
|
|||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return (
|
let isEditable = this.props.edition.acl.indexOf('edit') > -1;
|
||||||
<Row>
|
if (this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note){
|
||||||
<Col md={12} className="ascribe-edition-personal-note">
|
return (
|
||||||
<EditionNoteForm
|
<Form
|
||||||
editable={this.props.edition.acl.indexOf('edit') > -1}
|
url={apiUrls.note_edition}
|
||||||
handleSuccess={this.showNotification}
|
handleSuccess={this.showNotification}>
|
||||||
editions={[this.props.edition]} />
|
<Property
|
||||||
</Col>
|
name='note'
|
||||||
</Row>
|
label='Edition note (public)'
|
||||||
);
|
editable={isEditable}>
|
||||||
|
<InputTextAreaToggable
|
||||||
|
rows={3}
|
||||||
|
editable={isEditable}
|
||||||
|
defaultValue={this.props.edition.public_note}
|
||||||
|
placeholder='Enter a public note for this edition...'
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property hidden={true} name='bitcoin_id'>
|
||||||
|
<input defaultValue={this.props.edition.bitcoin_id}/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -440,19 +402,19 @@ let EditionFurtherDetails = React.createClass({
|
|||||||
title='Artist Contact Info'
|
title='Artist Contact Info'
|
||||||
handleSuccess={this.showNotification}
|
handleSuccess={this.showNotification}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
editions={[this.props.edition]} />
|
edition={this.props.edition} />
|
||||||
<PieceExtraDataForm
|
<PieceExtraDataForm
|
||||||
name='display_instructions'
|
name='display_instructions'
|
||||||
title='Display Instructions'
|
title='Display Instructions'
|
||||||
handleSuccess={this.showNotification}
|
handleSuccess={this.showNotification}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
editions={[this.props.edition]} />
|
edition={this.props.edition} />
|
||||||
<PieceExtraDataForm
|
<PieceExtraDataForm
|
||||||
name='technology_details'
|
name='technology_details'
|
||||||
title='Technology Details'
|
title='Technology Details'
|
||||||
handleSuccess={this.showNotification}
|
handleSuccess={this.showNotification}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
editions={[this.props.edition]} />
|
edition={this.props.edition} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
@ -39,6 +39,7 @@ let Header = React.createClass({
|
|||||||
handleLogout(){
|
handleLogout(){
|
||||||
UserActions.logoutCurrentUser();
|
UserActions.logoutCurrentUser();
|
||||||
Alt.flush();
|
Alt.flush();
|
||||||
|
this.transitionTo('login');
|
||||||
},
|
},
|
||||||
onChange(state) {
|
onChange(state) {
|
||||||
this.setState(state);
|
this.setState(state);
|
||||||
@ -71,7 +72,6 @@ let Header = React.createClass({
|
|||||||
|
|
||||||
<Navbar brand={brand} toggleNavKey={0}>
|
<Navbar brand={brand} toggleNavKey={0}>
|
||||||
<CollapsibleNav eventKey={0}>
|
<CollapsibleNav eventKey={0}>
|
||||||
<Nav navbar />
|
|
||||||
<Nav navbar right>
|
<Nav navbar right>
|
||||||
{account}
|
{account}
|
||||||
{signup}
|
{signup}
|
||||||
|
@ -15,18 +15,99 @@ import Property from './ascribe_forms/property';
|
|||||||
|
|
||||||
import apiUrls from '../constants/api_urls';
|
import apiUrls from '../constants/api_urls';
|
||||||
|
|
||||||
import ReactS3FineUploader from 'ReactS3FineUploader';
|
import ReactS3FineUploader from './ascribe_uploader/react_s3_fine_uploader';
|
||||||
|
|
||||||
|
import DatePicker from 'react-datepicker/dist/react-datepicker';
|
||||||
|
|
||||||
let RegisterPiece = React.createClass( {
|
let RegisterPiece = React.createClass( {
|
||||||
render() {
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
|
getInitialState(){
|
||||||
|
return {digital_work_key: null};
|
||||||
|
},
|
||||||
|
handleSuccess(){
|
||||||
|
let notification = new GlobalNotificationModel('Login successsful', 'success', 10000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
this.transitionTo('pieces');
|
||||||
|
},
|
||||||
|
|
||||||
|
getFormData(){
|
||||||
|
let data = {};
|
||||||
|
for (let ref in this.refs.form.refs){
|
||||||
|
data[this.refs.form.refs[ref].props.name] = this.refs.form.refs[ref].state.value;
|
||||||
|
}
|
||||||
|
data.digital_work_key = this.state.digital_work_key;
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
handleChange(){
|
||||||
|
this.setState({digital_work_key: this.refs.uploader.refs.fineuploader.state.filesToUpload[0].key})
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let buttons = null;
|
||||||
|
if (this.refs.uploader && this.refs.uploader.refs.fineuploader.state.filesToUpload[0].status === 'upload successful'){
|
||||||
|
buttons = (
|
||||||
|
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||||
|
Register your artwork
|
||||||
|
</button>);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="row ascribe-row">
|
<div className="row ascribe-row">
|
||||||
<div className="col-md-6">
|
<div className="col-md-5">
|
||||||
<FileUploader />
|
<FileUploader
|
||||||
|
ref='uploader'
|
||||||
|
handleChange={this.handleChange}/>
|
||||||
|
<br />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-7">
|
||||||
<LoginForm />
|
<h3 style={{'marginTop': 0}}>Lock down title</h3>
|
||||||
|
<Form
|
||||||
|
ref='form'
|
||||||
|
url={apiUrls.pieces_list}
|
||||||
|
getFormData={this.getFormData}
|
||||||
|
handleSuccess={this.handleSuccess}
|
||||||
|
buttons={buttons}
|
||||||
|
spinner={
|
||||||
|
<button className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||||
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
|
</button>
|
||||||
|
}>
|
||||||
|
<Property
|
||||||
|
name='artist_name'
|
||||||
|
label="Artist Name">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="The name of the creator"
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='title'
|
||||||
|
label="Artwork title">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="The title of the artwork"
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='date_created'
|
||||||
|
label="Year Created">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="Year Created (e.g. 2015)"
|
||||||
|
min={0}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name='num_editions'
|
||||||
|
label="Number of editions">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="Specify the number of unique editions for this artwork"
|
||||||
|
min={1}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -36,13 +117,17 @@ let RegisterPiece = React.createClass( {
|
|||||||
|
|
||||||
let FileUploader = React.createClass( {
|
let FileUploader = React.createClass( {
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactS3FineUploader
|
<ReactS3FineUploader
|
||||||
|
ref='fineuploader'
|
||||||
keyRoutine={{
|
keyRoutine={{
|
||||||
url: AppConstants.serverUrl + 's3/key/',
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
fileClass: 'digitalwork'
|
fileClass: 'digitalwork'
|
||||||
}}
|
}}
|
||||||
|
createBlobRoutine={{
|
||||||
|
url: apiUrls.blob_digitalworks
|
||||||
|
}}
|
||||||
|
handleChange={this.props.handleChange}
|
||||||
autoUpload={true}
|
autoUpload={true}
|
||||||
debug={false}
|
debug={false}
|
||||||
objectProperties={{
|
objectProperties={{
|
||||||
@ -94,60 +179,46 @@ let FileUploader = React.createClass( {
|
|||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}}
|
}}
|
||||||
multiple={true}/>
|
multiple={false}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let LoginForm = React.createClass({
|
|
||||||
mixins: [Router.Navigation],
|
|
||||||
|
|
||||||
|
|
||||||
handleSuccess(){
|
|
||||||
let notification = new GlobalNotificationModel('Login successsful', 'success', 10000);
|
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
||||||
this.transitionTo('pieces');
|
|
||||||
|
|
||||||
|
let InputDate = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
placeholderText: React.PropTypes.string
|
||||||
},
|
},
|
||||||
render() {
|
|
||||||
|
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 (
|
return (
|
||||||
<Form
|
<DatePicker
|
||||||
url={apiUrls.users_login}
|
key="example2"
|
||||||
handleSuccess={this.handleSuccess}
|
dateFormat="YYYY"
|
||||||
buttons={
|
selected={this.state.value}
|
||||||
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
onChange={this.handleChange}
|
||||||
Log in to ascribe
|
onBlur={this.props.onBlur}
|
||||||
</button>}
|
placeholderText={this.props.placeholderText}/>
|
||||||
spinner={
|
|
||||||
<button className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
|
||||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
|
||||||
</button>
|
|
||||||
}>
|
|
||||||
<Property
|
|
||||||
name='email'
|
|
||||||
label="Email">
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
placeholder="Enter your email"
|
|
||||||
autoComplete="on"
|
|
||||||
required/>
|
|
||||||
</Property>
|
|
||||||
<Property
|
|
||||||
name='password'
|
|
||||||
label="Password">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
placeholder="Enter your password"
|
|
||||||
autoComplete="on"
|
|
||||||
required/>
|
|
||||||
</Property>
|
|
||||||
<hr />
|
|
||||||
<div className="ascribe-login-text">
|
|
||||||
Not an ascribe user? Sign up...<br/>
|
|
||||||
Forgot my password? Rescue me...
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default RegisterPiece;
|
export default RegisterPiece;
|
||||||
|
@ -9,6 +9,9 @@ import UserStore from '../stores/user_store';
|
|||||||
import WalletSettingsActions from '../actions/wallet_settings_actions';
|
import WalletSettingsActions from '../actions/wallet_settings_actions';
|
||||||
import WalletSettingsStore from '../stores/wallet_settings_store';
|
import WalletSettingsStore from '../stores/wallet_settings_store';
|
||||||
|
|
||||||
|
import ApplicationActions from '../actions/application_actions';
|
||||||
|
import ApplicationStore from '../stores/application_store';
|
||||||
|
|
||||||
import GlobalNotificationModel from '../models/global_notification_model';
|
import GlobalNotificationModel from '../models/global_notification_model';
|
||||||
import GlobalNotificationActions from '../actions/global_notification_actions';
|
import GlobalNotificationActions from '../actions/global_notification_actions';
|
||||||
|
|
||||||
@ -28,6 +31,7 @@ let SettingsContainer = React.createClass({
|
|||||||
<div>
|
<div>
|
||||||
<AccountSettings />
|
<AccountSettings />
|
||||||
<BitcoinWalletSettings />
|
<BitcoinWalletSettings />
|
||||||
|
<APISettings />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -54,47 +58,65 @@ let AccountSettings = React.createClass({
|
|||||||
|
|
||||||
handleSuccess(){
|
handleSuccess(){
|
||||||
UserActions.fetchCurrentUser();
|
UserActions.fetchCurrentUser();
|
||||||
let notification = new GlobalNotificationModel('username succesfully updated', 'success', 10000);
|
let notification = new GlobalNotificationModel('username succesfully updated', 'success', 5000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
|
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
||||||
if (this.state.currentUser.username) {
|
if (this.state.currentUser.username) {
|
||||||
return (
|
content = (
|
||||||
<CollapsibleParagraph
|
<Form
|
||||||
title="Account"
|
url={apiUrls.users_username}
|
||||||
show={true}
|
handleSuccess={this.handleSuccess}>
|
||||||
defaultExpanded={true}>
|
<Property
|
||||||
<Form
|
name='username'
|
||||||
url={apiUrls.users_username}
|
label="Username">
|
||||||
handleSuccess={this.handleSuccess}>
|
<input
|
||||||
<Property
|
type="text"
|
||||||
name='username'
|
defaultValue={this.state.currentUser.username}
|
||||||
label="Username">
|
placeholder="Enter your username"
|
||||||
<input
|
required/>
|
||||||
type="text"
|
</Property>
|
||||||
defaultValue={this.state.currentUser.username}
|
<Property
|
||||||
placeholder="Enter your username"
|
name='email'
|
||||||
required/>
|
label="Email"
|
||||||
</Property>
|
editable={false}>
|
||||||
<Property
|
<input
|
||||||
name='email'
|
type="text"
|
||||||
label="Email"
|
defaultValue={this.state.currentUser.email}
|
||||||
editable={false}>
|
placeholder="Enter your username"
|
||||||
<input
|
required/>
|
||||||
type="text"
|
</Property>
|
||||||
defaultValue={this.state.currentUser.email}
|
<hr />
|
||||||
placeholder="Enter your username"
|
</Form>
|
||||||
required/>
|
|
||||||
</Property>
|
|
||||||
</Form>
|
|
||||||
</CollapsibleParagraph>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title="Account"
|
||||||
|
show={true}
|
||||||
|
defaultExpanded={true}>
|
||||||
|
{content}
|
||||||
|
<Form
|
||||||
|
url={AppConstants.serverUrl + 'api/users/set_language/'}>
|
||||||
|
<Property
|
||||||
|
name='language'
|
||||||
|
label="Choose your Language"
|
||||||
|
editable={true}>
|
||||||
|
<select id="select-lang" name="language">
|
||||||
|
<option value="fr">
|
||||||
|
Français
|
||||||
|
</option>
|
||||||
|
<option value="en"
|
||||||
|
selected="selected">
|
||||||
|
English
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -120,42 +142,33 @@ let BitcoinWalletSettings = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.walletSettings.btc_public_key) {
|
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
||||||
return (
|
if (this.state.walletSettings.btc_public_key) {
|
||||||
<CollapsibleParagraph
|
content = (
|
||||||
title="Crypto Wallet"
|
<Form >
|
||||||
show={true}
|
<Property
|
||||||
defaultExpanded={true}>
|
name='btc_public_key'
|
||||||
<Form
|
label="Bitcoin public key"
|
||||||
url={apiUrls.users_username}
|
editable={false}>
|
||||||
handleSuccess={this.handleSuccess}>
|
<pre className="ascribe-pre">{this.state.walletSettings.btc_public_key}</pre>
|
||||||
<Property
|
</Property>
|
||||||
name='btc_public_key'
|
<Property
|
||||||
label="Bitcoin public key"
|
name='btc_root_address'
|
||||||
editable={false}>
|
label="Root Address"
|
||||||
<input
|
editable={false}>
|
||||||
type="text"
|
<pre className="ascribe-pre">{this.state.walletSettings.btc_root_address}</pre>
|
||||||
defaultValue={this.state.walletSettings.btc_public_key}
|
</Property>
|
||||||
placeholder="Enter your username"
|
<hr />
|
||||||
required/>
|
</Form>);
|
||||||
</Property>
|
|
||||||
<Property
|
|
||||||
name='btc_root_address'
|
|
||||||
label="Root Address"
|
|
||||||
editable={false}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
defaultValue={this.state.walletSettings.btc_root_address}/>
|
|
||||||
</Property>
|
|
||||||
</Form>
|
|
||||||
</CollapsibleParagraph>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title="Crypto Wallet"
|
||||||
|
show={true}
|
||||||
|
defaultExpanded={true}>
|
||||||
|
{content}
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -177,18 +190,88 @@ let ContractSettings = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
let APISettings = React.createClass({
|
let APISettings = React.createClass({
|
||||||
|
getInitialState() {
|
||||||
propTypes: {
|
return ApplicationStore.getState();
|
||||||
currentUser: React.PropTypes.object
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
componentDidMount() {
|
||||||
|
ApplicationStore.listen(this.onChange);
|
||||||
|
ApplicationActions.fetchApplication();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
ApplicationStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
handleCreateSuccess: function(){
|
||||||
|
ApplicationActions.fetchApplication();
|
||||||
|
let notification = new GlobalNotificationModel('Application successfully created', 'success', 5000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTokenRefresh: function(event){
|
||||||
|
let applicationName = event.target.getAttribute('data-id');
|
||||||
|
ApplicationActions.refreshApplicationToken(applicationName);
|
||||||
|
let notification = new GlobalNotificationModel('Token refreshed', 'success', 2000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
||||||
|
if (this.state.applications.length > -1) {
|
||||||
|
content = this.state.applications.map(function(app) {
|
||||||
|
return (
|
||||||
|
<Property
|
||||||
|
name={app.name}
|
||||||
|
label={app.name}>
|
||||||
|
<div className="row-same-height">
|
||||||
|
<div className="col-xs-6 col-xs-height col-middle">
|
||||||
|
{'Bearer ' + app.bearer_token.token}
|
||||||
|
</div>
|
||||||
|
<div className="col-xs-6 col-xs-height">
|
||||||
|
<button
|
||||||
|
className="pull-right btn btn-default btn-sm"
|
||||||
|
onClick={this.handleTokenRefresh}
|
||||||
|
data-id={app.name}>
|
||||||
|
REFRESH
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Property>);
|
||||||
|
}, this);
|
||||||
|
content = (
|
||||||
|
<div>
|
||||||
|
<Form>
|
||||||
|
{content}
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<CollapsibleParagraph
|
||||||
<div>Username: {this.props.currentUser.username}</div>
|
title="API Integration"
|
||||||
<div>Email: {this.props.currentUser.email}</div>
|
show={true}
|
||||||
</div>
|
defaultExpanded={true}>
|
||||||
|
<Form
|
||||||
|
url={apiUrls.applications}
|
||||||
|
handleSuccess={this.handleCreateSuccess}>
|
||||||
|
<Property
|
||||||
|
name='name'
|
||||||
|
label='Application Name'>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter the name of your app"
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
<pre>
|
||||||
|
Usage: curl <url> -H 'Authorization: Bearer <token>'
|
||||||
|
</pre>
|
||||||
|
{content}
|
||||||
|
</CollapsibleParagraph>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
import AppConstants from './application_constants';
|
import AppConstants from './application_constants';
|
||||||
|
|
||||||
let apiUrls = {
|
let apiUrls = {
|
||||||
|
'applications': AppConstants.apiEndpoint + 'applications/',
|
||||||
|
'application_token_refresh': AppConstants.apiEndpoint + 'applications/refresh_token/',
|
||||||
|
'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/',
|
||||||
'edition': AppConstants.apiEndpoint + 'editions/${bitcoin_id}/',
|
'edition': AppConstants.apiEndpoint + 'editions/${bitcoin_id}/',
|
||||||
'edition_delete': AppConstants.apiEndpoint + 'editions/${edition_id}/',
|
'edition_delete': AppConstants.apiEndpoint + 'editions/${edition_id}/',
|
||||||
'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/${edition_id}/',
|
'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/${edition_id}/',
|
||||||
@ -15,7 +18,7 @@ let apiUrls = {
|
|||||||
'ownership_loans': AppConstants.apiEndpoint + 'ownership/loans/',
|
'ownership_loans': AppConstants.apiEndpoint + 'ownership/loans/',
|
||||||
'ownership_loans_confirm': AppConstants.apiEndpoint + 'ownership/loans/confirm/',
|
'ownership_loans_confirm': AppConstants.apiEndpoint + 'ownership/loans/confirm/',
|
||||||
'ownership_loans_deny': AppConstants.apiEndpoint + 'ownership/loans/deny/',
|
'ownership_loans_deny': AppConstants.apiEndpoint + 'ownership/loans/deny/',
|
||||||
'ownership_shares_mail': AppConstants.apiEndpoint + 'ownership/shares/mail/',
|
'ownership_shares': AppConstants.apiEndpoint + 'ownership/shares/',
|
||||||
'ownership_transfers': AppConstants.apiEndpoint + 'ownership/transfers/',
|
'ownership_transfers': AppConstants.apiEndpoint + 'ownership/transfers/',
|
||||||
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/',
|
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/',
|
||||||
'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/',
|
'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/',
|
||||||
|
17
js/fetchers/application_fetcher.js
Normal file
17
js/fetchers/application_fetcher.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../utils/requests';
|
||||||
|
|
||||||
|
let ApplicationFetcher = {
|
||||||
|
/**
|
||||||
|
* Fetch the registered applications of a user from the API.
|
||||||
|
*/
|
||||||
|
fetch() {
|
||||||
|
return requests.get('applications');
|
||||||
|
},
|
||||||
|
refreshToken(applicationName) {
|
||||||
|
return requests.post('application_token_refresh', { body: {'name': applicationName}});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ApplicationFetcher;
|
@ -19,7 +19,6 @@ let Route = Router.Route;
|
|||||||
let Redirect = Router.Redirect;
|
let Redirect = Router.Redirect;
|
||||||
let baseUrl = AppConstants.baseUrl;
|
let baseUrl = AppConstants.baseUrl;
|
||||||
|
|
||||||
|
|
||||||
let routes = (
|
let routes = (
|
||||||
<Route name="app" path={baseUrl} handler={AscribeApp}>
|
<Route name="app" path={baseUrl} handler={AscribeApp}>
|
||||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||||
@ -30,8 +29,8 @@ let routes = (
|
|||||||
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
|
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
|
||||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||||
|
|
||||||
<Redirect from={baseUrl} to="pieces" />
|
<Redirect from={baseUrl} to="login" />
|
||||||
<Redirect from={baseUrl + '/'} to="pieces" />
|
<Redirect from={baseUrl + '/'} to="login" />
|
||||||
</Route>
|
</Route>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
18
js/stores/application_store.js
Normal file
18
js/stores/application_store.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import alt from '../alt';
|
||||||
|
import ApplicationActions from '../actions/application_actions';
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationStore {
|
||||||
|
constructor() {
|
||||||
|
this.applications = {};
|
||||||
|
this.bindActions(ApplicationActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateApplications(applications) {
|
||||||
|
this.applications = applications;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(ApplicationStore, 'ApplicationStore');
|
1
node_modules/react-s3-fineuploader
generated
vendored
1
node_modules/react-s3-fineuploader
generated
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit e2890cf3d28d010abc04e0de3b218afe8c949596
|
|
@ -13,8 +13,7 @@
|
|||||||
"start": "node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"fineUploader": "./node_modules/react-s3-fineuploader/vendor/s3.fine-uploader.js",
|
"fineUploader": "./js/components/ascribe_uploader/vendor/s3.fine-uploader.js"
|
||||||
"ReactS3FineUploader": "./node_modules/react-s3-fineuploader/react_s3_fine_uploader.js"
|
|
||||||
},
|
},
|
||||||
"browserify-shim": {
|
"browserify-shim": {
|
||||||
"fineUploader": "qq"
|
"fineUploader": "qq"
|
||||||
@ -60,7 +59,7 @@
|
|||||||
"gulp-notify": "^2.2.0",
|
"gulp-notify": "^2.2.0",
|
||||||
"gulp-sass": "^2.0.1",
|
"gulp-sass": "^2.0.1",
|
||||||
"gulp-sourcemaps": "^1.5.2",
|
"gulp-sourcemaps": "^1.5.2",
|
||||||
"gulp-template": "^3.0.0",
|
"gulp-template": "~3.0.0",
|
||||||
"gulp-uglify": "^1.2.0",
|
"gulp-uglify": "^1.2.0",
|
||||||
"gulp-util": "^3.0.4",
|
"gulp-util": "^3.0.4",
|
||||||
"harmonize": "^1.4.2",
|
"harmonize": "^1.4.2",
|
||||||
@ -71,6 +70,7 @@
|
|||||||
"react": "^0.13.2",
|
"react": "^0.13.2",
|
||||||
"react-bootstrap": "~0.22.6",
|
"react-bootstrap": "~0.22.6",
|
||||||
"react-datepicker": "~0.8.0",
|
"react-datepicker": "~0.8.0",
|
||||||
|
"react-progressbar": "^1.1.0",
|
||||||
"react-router": "^0.13.3",
|
"react-router": "^0.13.3",
|
||||||
"react-textarea-autosize": "^2.2.3",
|
"react-textarea-autosize": "^2.2.3",
|
||||||
"reactify": "^1.1.0",
|
"reactify": "^1.1.0",
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
.ascribe-edition-details hr {
|
|
||||||
border-top: 1px solid #616161;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ascribe-edition-personal-note > textarea {
|
.ascribe-edition-personal-note > textarea {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
@ -12,16 +9,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ascribe-edition-collapsible-wrapper > div:first-child {
|
.ascribe-edition-collapsible-wrapper > div:first-child {
|
||||||
width:100%;
|
width: 100%;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
background-color: #EEE;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #CCC;
|
||||||
|
|
||||||
|
}
|
||||||
.ascribe-edition-collapsible-wrapper > div > span {
|
.ascribe-edition-collapsible-wrapper > div > span {
|
||||||
font-size:1.3em;
|
font-size:1.3em;
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
.ascribe-edition-collapsible-wrapper > div > span:nth-child(2) {
|
||||||
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ascribe-edition-collapible-content {
|
.ascribe-edition-collapible-content {
|
||||||
width:100%;
|
width:100%;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,13 @@
|
|||||||
$break-small: 764px;
|
$break-small: 764px;
|
||||||
|
$break-medium: 991px;
|
||||||
|
|
||||||
|
|
||||||
.ascribe-row {
|
.ascribe-row {
|
||||||
max-width: 600px;
|
@media screen and (max-width: $break-medium) {
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: $break-medium) {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
margin: 0 auto
|
margin: 0 auto
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
.ascribe-settings-wrapper {
|
.ascribe-settings-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -10,11 +11,18 @@
|
|||||||
&div:last-of-type {
|
&div:last-of-type {
|
||||||
border-bottom: 1px solid rgba(0,0,0,.2);
|
border-bottom: 1px solid rgba(0,0,0,.2);
|
||||||
}
|
}
|
||||||
|
&:hover{
|
||||||
|
border-left: 3px solid rgba(2, 182, 163, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-hidden{
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-focused {
|
.is-focused {
|
||||||
background-color: rgba(2, 182, 163, 0.05);
|
background-color: rgba(2, 182, 163, 0.05);
|
||||||
border-left: 3px solid rgba(2, 182, 163, 1);
|
border-left: 3px solid rgba(2, 182, 163, 1)!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-error {
|
.is-error {
|
||||||
@ -26,13 +34,14 @@
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
span {
|
input, textarea {
|
||||||
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover{
|
||||||
|
border-left: 3px solid rgba(169, 68, 66, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-fixed {
|
.is-fixed {
|
||||||
@ -42,13 +51,15 @@
|
|||||||
span {
|
span {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
input {
|
input, div, pre, textarea {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.ascribe-settings-property {
|
.ascribe-settings-property {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -60,15 +71,28 @@
|
|||||||
|
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
|
|
||||||
span {
|
input, div, span, pre, textarea, select {
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
span {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: rgba(0,0,0,.7);
|
color: rgba(0,0,0,.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
div {
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
margin-top: 10px;
|
||||||
|
div {
|
||||||
|
padding-left: 0;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1.1em;
|
||||||
|
cursor: default;
|
||||||
|
color: rgba(0, 0, 0, .7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input, pre, textarea, select {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
width:100%;
|
width:100%;
|
||||||
@ -76,10 +100,12 @@
|
|||||||
border: 0;
|
border: 0;
|
||||||
background-color: rgba(0,0,0,0);
|
background-color: rgba(0,0,0,0);
|
||||||
color: #38BAAD;
|
color: #38BAAD;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
border:0;
|
border:0;
|
||||||
outline:0;
|
outline:0;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::selection {
|
&::selection {
|
||||||
@ -89,4 +115,9 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea{
|
||||||
|
color: #666;
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,14 @@
|
|||||||
|
|
||||||
.ascribe-pre{
|
.ascribe-pre{
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
/* white-space: pre-wrap; */
|
white-space: pre-wrap;
|
||||||
white-space: -moz-pre-wrap;
|
white-space: -moz-pre-wrap;
|
||||||
white-space: -pre-wrap;
|
white-space: -pre-wrap;
|
||||||
white-space: -o-pre-wrap;
|
white-space: -o-pre-wrap;
|
||||||
/* word-wrap: break-word; */
|
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin:0;
|
||||||
}
|
}
|
||||||
|
64
sass/ascribe_uploader.scss
Normal file
64
sass/ascribe_uploader.scss
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
.file-drag-and-drop {
|
||||||
|
display: table-cell;
|
||||||
|
outline: 1px dashed #616161;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
height:208px;
|
||||||
|
width: 672px;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
transition: .1s linear background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop:hover {
|
||||||
|
background-color: rgba(72, 218, 203, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop > span {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-files {
|
||||||
|
text-align: left;
|
||||||
|
padding: 3em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-position {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0 3em 3em;
|
||||||
|
float:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-preview-table-wrapper {
|
||||||
|
display: table;
|
||||||
|
height:94px;
|
||||||
|
width:104px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-preview {
|
||||||
|
overflow:hidden;
|
||||||
|
cursor: default;
|
||||||
|
background-color: #EEEEEE;
|
||||||
|
border: 1px solid #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-preview-image {
|
||||||
|
display: table;
|
||||||
|
height:104px;
|
||||||
|
width:104px;
|
||||||
|
overflow:hidden;
|
||||||
|
border: 1px solid #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-preview-other {
|
||||||
|
display: table-cell;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-preview-other span {
|
||||||
|
font-size: 1.1em;
|
||||||
|
display: block;
|
||||||
|
margin-top: -10px;
|
||||||
|
}
|
@ -17,6 +17,7 @@ $BASE_URL: '<%= BASE_URL %>';
|
|||||||
@import 'ascribe_edition';
|
@import 'ascribe_edition';
|
||||||
@import 'ascribe_textarea';
|
@import 'ascribe_textarea';
|
||||||
@import 'ascribe_media_player';
|
@import 'ascribe_media_player';
|
||||||
|
@import 'ascribe_uploader';
|
||||||
@import 'ascribe-global-notification';
|
@import 'ascribe-global-notification';
|
||||||
@import 'ascribe_piece_register';
|
@import 'ascribe_piece_register';
|
||||||
@import 'offset_right';
|
@import 'offset_right';
|
||||||
|
Loading…
Reference in New Issue
Block a user