Seed Support #56

Closed
ocnios wants to merge 173 commits from master into build
9 changed files with 169 additions and 140 deletions
Showing only changes of commit cb4af24cd7 - Show all commits

View file

@ -2,7 +2,6 @@ import React from 'react';
import {Line} from 'rc-progress';
import lbry from './lbry.js';
import EmailPage from './page/email.js';
import SettingsPage from './page/settings.js';
import HelpPage from './page/help.js';
import WatchPage from './page/watch.js';
@ -277,8 +276,6 @@ var App = React.createClass({
return <ShowPage uri={this.state.pageArgs} />;
case 'publish':
return <PublishPage />;
case 'email':
return <EmailPage />;
case 'developer':
return <DeveloperPage />;
case 'discover':

View file

@ -15,6 +15,8 @@ export let FormField = React.createClass({
propTypes: {
type: React.PropTypes.string.isRequired,
prefix: React.PropTypes.string,
postfix: React.PropTypes.string,
hasError: React.PropTypes.bool
},
getInitialState: function() {
@ -41,9 +43,6 @@ export let FormField = React.createClass({
errorMessage: text,
});
},
showRequiredError: function() {
this.showError(this._fieldRequiredText);
},
focus: function() {
this.refs.field.focus();
},
@ -51,7 +50,7 @@ export let FormField = React.createClass({
if (this.props.type == 'checkbox') {
return this.refs.field.checked;
} else if (this.props.type == 'file') {
return this.refs.field.files.length && this.refs.field.files[0].path;
return !!(this.refs.field.files.length && this.refs.field.files[0].path);
} else {
return this.refs.field.value;
}
@ -69,6 +68,9 @@ export let FormField = React.createClass({
delete otherProps.type;
delete otherProps.label;
delete otherProps.hasError;
delete otherProps.className;
delete otherProps.postfix;
delete otherProps.prefix;
const element = <this._element id={elementId} type={this._type} name={this.props.name} ref="field" placeholder={this.props.placeholder}
className={'form-field__input form-field__input-' + this.props.type + ' ' + (this.props.className || '') + (isError ? 'form-field__input--error' : '')}
@ -77,35 +79,25 @@ export let FormField = React.createClass({
</this._element>;
return <div className="form-field">
{ this.props.prefix ? <span className="form-field__prefix">{this.props.prefix}</span> : '' }
{ renderElementInsideLabel ?
<label htmlFor={elementId} className={"form-field__label " + (isError ? 'form-field__label--error' : '')}>
{element}
{this.props.label}
</label> : element }
{ isError ? <div className="form-field__error">{this.state.errorMessage}</div> : '' }
</label> :
element }
{ this.props.postfix ? <span className="form-field__postfix">{this.props.postfix}</span> : '' }
{ isError && this.state.errorMessage ? <div className="form-field__error">{this.state.errorMessage}</div> : '' }
</div>
return (
this.props.row ?
<div className="form-row">{field}</div> :
field
);
}
})
export let FormRow = React.createClass({
_fieldRequiredText: 'This field is required',
propTypes: {
label: React.PropTypes.string,
// helper: React.PropTypes.html,
},
getValue: function() {
if (this.props.type == 'checkbox') {
return this.refs.field.checked;
} else if (this.props.type == 'file') {
return this.refs.field.files[0].path;
} else {
return this.refs.field.value;
}
},
getInitialState: function() {
return {
isError: false,
@ -118,12 +110,24 @@ export let FormRow = React.createClass({
errorMessage: text,
});
},
showRequiredError: function() {
this.showError(this._fieldRequiredText);
},
clearError: function(text) {
this.setState({
isError: false,
errorMessage: ''
});
},
getValue: function() {
return this.refs.field.getValue();
},
getSelectedElement: function() {
return this.refs.field.getSelectedElement();
},
focus: function() {
this.refs.field.focus();
},
render: function() {
const fieldProps = Object.assign({}, this.props),
elementId = formFieldId(),

View file

@ -1,36 +0,0 @@
import React from 'react';
import lbryio from '../lbryio.js';
import {getLocal, setLocal} from '../utils.js';
import {FormField} from '../component/form.js'
import {Link} from '../component/link.js'
import rewards from '../rewards.js';
const EmailPage = React.createClass({
handleSubmit: function(event) {
if (event !== undefined) {
event.preventDefault();
}
if (!this.state.email) {
this._emailField.showRequiredError();
}
},
componentWillMount: function() {
this._getRewardType();
},
render: function() {
return (
<main>
<section className="card">
<h1>Verify your Email Address</h1>
<form onSubmit={this.handleSubmit}>
<section><label>Email <FormField ref={(field) => { this._emailField = field }} type="text" name="email" value={this.state.email} /></label></section>
<div><Link button="primary" label="Submit" onClick={this.handleSubmit} /></div>
</form>
</section>
</main>
);
}
});
export default EmailPage;

View file

@ -6,7 +6,7 @@ import {Link} from '../component/link.js';
import Modal from '../component/modal.js';
var PublishPage = React.createClass({
_requiredFields: ['name', 'bid', 'meta_title'],
_requiredFields: ['meta_title', 'name', 'bid'],
_updateChannelList: function(channel) {
// Calls API to update displayed list of channels. If a channel name is provided, will select
@ -27,19 +27,23 @@ var PublishPage = React.createClass({
submitting: true,
});
var checkFields = this._requiredFields.slice();
let checkFields = this._requiredFields;
if (!this.state.myClaimExists) {
checkFields.push('file');
checkFields.unshift('file');
}
var missingFieldFound = false;
let missingFieldFound = false;
for (let fieldName of checkFields) {
var field = this.refs[fieldName];
if (field.getValue() === '') {
field.showRequiredError();
if (!missingFieldFound) {
field.focus();
missingFieldFound = true;
const field = this.refs[fieldName];
if (field) {
if (field.getValue() === '' || field.getValue() === false) {
field.showRequiredError();
if (!missingFieldFound) {
field.focus();
missingFieldFound = true;
}
} else {
field.clearError();
}
}
}
@ -281,6 +285,8 @@ var PublishPage = React.createClass({
if (newChannelName.length > 1 && !lbry.nameIsValid(newChannelName.substr(1), false)) {
this.refs.newChannelName.showError('LBRY channel names must contain only letters, numbers and dashes.');
return;
} else {
this.refs.newChannelName.clearError()
}
this.setState({
@ -351,17 +357,24 @@ var PublishPage = React.createClass({
} else if (this.state.myClaimExists) {
return "You have already used this URL. Publishing to it again will update your previous publish."
} else if (this.state.topClaimValue) {
return <span>A deposit of at least <strong>{this.state.topClaimValue}</strong> {this.state.topClaimValue == 1 ? 'credit' : 'credits'}
return <span>A deposit of at least <strong>{this.state.topClaimValue}</strong> {this.state.topClaimValue == 1 ? 'credit ' : 'credits '}
is required to win <strong>{this.state.name}</strong>. However, you can still get a perminent URL for any amount.</span>
} else {
return '';
}
},
closeModal: function() {
this.setState({
modal: null,
});
},
render: function() {
if (this.state.channels === null) {
return null;
}
const lbcInputHelp = "This LBC remains yours and the deposit can be undone at any time."
return (
<main ref="page">
<form onSubmit={this.handleSubmit}>
@ -377,10 +390,17 @@ var PublishPage = React.createClass({
helper={this.state.myClaimExists ? "If you don't choose a file, the file from your existing claim will be used." : null}/>
</div>
{ !this.state.hasFile ? '' :
<div>
<div className="card__content">
<FormRow label="Title" type="text" ref="meta_title" name="title" placeholder="UFOs Are Real" />
</div>
<div className="card__content">
<FormRow label="Title" type="text" ref="meta_title" name="title" placeholder="Me Not Being A Loser" />
<FormRow type="text" label="Thumbnail URL" ref="meta_thumbnail" name="thumbnail" placeholder="http://mycompany.com/images/ep_1.jpg" />
</div>
<div className="card__content">
<FormRow label="Description" type="textarea" ref="meta_description" name="description" placeholder="Description of your content" />
</div>
<div className="card__content">
<FormRow label="Language" type="select" defaultValue="en" ref="meta_language" name="language">
<option value="en">English</option>
<option value="zh">Chinese</option>
@ -390,12 +410,15 @@ var PublishPage = React.createClass({
<option value="ru">Russian</option>
<option value="es">Spanish</option>
</FormRow>
</div>
<div className="card__content">
<FormRow type="select" label="Maturity" defaultValue="en" ref="meta_nsfw" name="nsfw">
<option value=""></option>
<option value="0">All Ages</option>
<option value="1">Adults Only</option>
</FormRow>
</div> }
</div>
</div>}
</section>
<section className="card">
@ -409,27 +432,28 @@ var PublishPage = React.createClass({
<div className="form-row__label-row">
<label className="form-row__label">Price</label>
</div>
<FormRow label="Free" type="radio" name="isFree" value="1" onChange={ () => { this.handleFeePrefChange(false) } } checked={!this.state.isFee} />
<FormRow label="Free" type="radio" name="isFree" value="1" onChange={ () => { this.handleFeePrefChange(false) } } defaultChecked={!this.state.isFee} />
<FormField type="radio" name="isFree" label={!this.state.isFee ? 'Choose price...' : 'Price ' }
onChange={ () => { this.handleFeePrefChange(true) } } checked={this.state.isFee} />
onChange={ () => { this.handleFeePrefChange(true) } } defaultChecked={this.state.isFee} />
<span className={!this.state.isFee ? 'hidden' : ''}>
<FormField type="number" step="0.01" placeholder="1.00" onChange={this.handleFeeAmountChange} /> <FormField type="select" onChange={this.handleFeeCurrencyChange}>
<FormField type="number" className="form-field__input--inline" step="0.01" placeholder="1.00" onChange={this.handleFeeAmountChange} /> <FormField type="select" onChange={this.handleFeeCurrencyChange}>
<option value="USD">US Dollars</option>
<option value="LBC">LBRY credits</option>
</FormField>
</span>
{ this.state.isFee ?
<div className="help">
<div className="form-field__helper">
If you choose to price this content in dollars, the number of credits charged will be adjusted based on the value of LBRY credits at the time of purchase.
</div> : '' }
<FormRow label="License" type="select" ref="meta_license" name="license" onChange={this.handleLicenseChange}>
<option></option>
<option>Public Domain</option>
<option data-url="https://creativecommons.org/licenses/by/4.0/legalcode">Creative Commons Attribution 4.0 International</option>
<option data-url="https://creativecommons.org/licenses/by-sa/4.0/legalcode">Creative Commons Attribution-ShareAlike 4.0 International</option>
<option data-url="https://creativecommons.org/licenses/by-nd/4.0/legalcode">Creative Commons Attribution-NoDerivatives 4.0 International</option>
<option data-url="https://creativecommons.org/licenses/by-nc/4.0/legalcode">Creative Commons Attribution-NonCommercial 4.0 International</option>
<option data-url="https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International</option>
<option data-url="https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International</option>
<option>Public Domain</option>
<option data-license-type="copyright" {... this.state.copyrightChosen ? {value: this.state.copyrightNotice} : {}}>Copyrighted...</option>
<option data-license-type="other" {... this.state.otherLicenseChosen ? {value: this.state.otherLicenseDescription} : {}}>Other...</option>
</FormRow>
@ -465,7 +489,13 @@ var PublishPage = React.createClass({
<div className="card__content">
<FormRow label="Name" type="text" onChange={this.handleNewChannelNameChange} ref={newChannelName => { this.refs.newChannelName = newChannelName }}
value={this.state.newChannelName} />
<FormRow label="Deposit" step="0.01" defaultValue="0.01" type="number" onChange={this.handleNewChannelBidChange} value={this.state.newChannelBid} />
<FormRow label="Deposit"
postfix="LBC"
step="0.01"
type="number"
helper={lbcInputHelp}
onChange={this.handleNewChannelBidChange}
value={this.state.newChannelBid ? this.state.newChannelBid : '10'} />
<div className="form-row-submit">
<Link button="primary" label={!this.state.creatingChannel ? 'Creating identity' : 'Creating identity...'} onClick={this.handleCreateChannelClick} disabled={this.state.creatingChannel} />
</div>
@ -477,11 +507,11 @@ var PublishPage = React.createClass({
<section className="card">
<div className="card__title-primary">
<h4>Address</h4>
<div className="card__subtitle">Where should this content permanently reside?</div>
<div className="card__subtitle">Where should this content permanently reside? <Link label="Read more" href="https://lbry.io/faq/naming" />.</div>
</div>
<div className="card__content">
<FormRow label="lbry://" type="text" ref="name" placeholder="lbry://myname" value={this.state.rawName} onChange={this.handleNameChange}
helper={(<div>Select a URL for this publish. <Link label="Read more" href="https://lbry.io/faq/naming" />.</div>)} />
<FormRow prefix="lbry://" type="text" ref="name" placeholder="myname" value={this.state.rawName} onChange={this.handleNameChange}
helper={this.getNameBidHelpText()} />
</div>
{ this.state.rawName ?
<div className="card__content">
@ -489,10 +519,11 @@ var PublishPage = React.createClass({
type="number"
step="0.01"
label="Deposit"
postfix="LBC"
onChange={this.handleBidChange}
value={this.state.bid}
value={this.state.bid ? this.state.bid : '1'}
placeholder={this.state.nameResolved ? this.state.topClaimValue + 10 : 100}
helper={this.getNameBidHelpText()} />
helper={lbcInputHelp} />
</div> : '' }
</section>

View file

@ -3,36 +3,51 @@ import {FormField, FormRow} from '../component/form.js';
import lbry from '../lbry.js';
var SettingsPage = React.createClass({
_onSettingSaveSuccess: function() {
// This is bad.
// document.dispatchEvent(new CustomEvent('globalNotice', {
// detail: {
// message: "Settings saved",
// },
// }))
},
setDaemonSetting: function(name, value) {
lbry.setDaemonSetting(name, value, this._onSettingSaveSuccess)
},
setClientSetting: function(name, value) {
lbry.setClientSetting(name, value)
this._onSettingSaveSuccess()
},
onRunOnStartChange: function (event) {
lbry.setDaemonSetting('run_on_startup', event.target.checked);
this.setDaemonSetting('run_on_startup', event.target.checked);
},
onShareDataChange: function (event) {
lbry.setDaemonSetting('share_debug_info', event.target.checked);
this.setDaemonSetting('share_debug_info', event.target.checked);
},
onDownloadDirChange: function(event) {
lbry.setDaemonSetting('download_directory', event.target.value);
this.setDaemonSetting('download_directory', event.target.value);
},
onMaxUploadPrefChange: function(isLimited) {
if (!isLimited) {
lbry.setDaemonSetting('max_upload', 0.0);
this.setDaemonSetting('max_upload', 0.0);
}
this.setState({
isMaxUpload: isLimited
});
},
onMaxUploadFieldChange: function(event) {
lbry.setDaemonSetting('max_upload', Number(event.target.value));
this.setDaemonSetting('max_upload', Number(event.target.value));
},
onMaxDownloadPrefChange: function(isLimited) {
if (!isLimited) {
lbry.setDaemonSetting('max_download', 0.0);
this.setDaemonSetting('max_download', 0.0);
}
this.setState({
isMaxDownload: isLimited
});
},
onMaxDownloadFieldChange: function(event) {
lbry.setDaemonSetting('max_download', Number(event.target.value));
this.setDaemonSetting('max_download', Number(event.target.value));
},
getInitialState: function() {
return {
@ -57,7 +72,7 @@ var SettingsPage = React.createClass({
lbry.setClientSetting('showNsfw', event.target.checked);
},
onShowUnavailableChange: function(event) {
lbry.setClientSetting('showUnavailable', event.target.checked);
},
render: function() {
if (!this.state.daemonSettings) {
@ -94,38 +109,60 @@ var SettingsPage = React.createClass({
<h3>Bandwidth Limits</h3>
</div>
<div className="card__content">
<h4>Max Upload</h4>
<div className="form-row__label-row"><div className="form-field__label">Max Upload</div></div>
<FormRow type="radio"
name="max_upload_pref"
onChange={this.onMaxUploadPrefChange.bind(this, false)}
defaultChecked={!this.state.isMaxUpload}
label="Unlimited" />
<FormRow type="radio"
name="max_upload_pref"
onChange={this.onMaxUploadPrefChange.bind(this, true)}
defaultChecked={this.state.isMaxUpload}
label={ this.state.isMaxUpload ? 'Up to' : 'Choose limit...' } />
{ this.state.isMaxUpload ?
<FormField type="number"
min="0"
step=".5"
label="MB/s"
onChange={this.onMaxUploadFieldChange}
/> : ''
}
<div className="form-row">
<FormField type="radio"
name="max_upload_pref"
onChange={this.onMaxUploadPrefChange.bind(this, true)}
defaultChecked={this.state.isMaxUpload}
label={ this.state.isMaxUpload ? 'Up to' : 'Choose limit...' } />
{ this.state.isMaxUpload ?
<FormField type="number"
min="0"
step=".5"
defaultValue={this.state.daemonSettings.max_upload}
placeholder="10"
className="form-field__input--inline"
onChange={this.onMaxUploadFieldChange}
/>
: ''
}
{ this.state.isMaxUpload ? <span className="form-field__label">MB/s</span> : '' }
</div>
</div>
<div className="card__content">
<h4>Max Download</h4>
<FormField label="Unlimited"
<div className="form-row__label-row"><div className="form-field__label">Max Download</div></div>
<FormRow label="Unlimited"
type="radio"
name="max_download_pref"
onChange={this.onMaxDownloadPrefChange.bind(this, false)}
defaultChecked={!this.state.isMaxDownload} />
{ /*
<label style={settingsRadioOptionStyles}>
<input type="radio" name="max_download_pref" onChange={this.onMaxDownloadPrefChange.bind(this, true)} defaultChecked={this.state.isMaxDownload}/> { this.state.isMaxDownload ? 'Up to' : 'Choose limit...' }
<span className={ this.state.isMaxDownload ? '' : 'hidden'}> <input type="number" min="0" step=".5" defaultValue={this.state.daemonSettings.max_download} style={settingsNumberFieldStyles} onChange={this.onMaxDownloadFieldChange}/> MB/s</span>
</label> */ }
<div className="form-row">
<FormField type="radio"
name="max_download_pref"
onChange={this.onMaxDownloadPrefChange.bind(this, true)}
defaultChecked={this.state.isMaxDownload}
label={ this.state.isMaxDownload ? 'Up to' : 'Choose limit...' } />
{ this.state.isMaxDownload ?
<FormField type="number"
min="0"
step=".5"
defaultValue={this.state.daemonSettings.max_download}
placeholder="10"
className="form-field__input--inline"
onChange={this.onMaxDownloadFieldChange}
/>
: ''
}
{ this.state.isMaxDownload ? <span className="form-field__label">MB/s</span> : '' }
</div>
</div>
</section>
<section className="card">
@ -160,25 +197,4 @@ var SettingsPage = React.createClass({
}
});
/*
<section className="card">
<h3>Search</h3>
<div className="form-row">
<div className="help">
Would you like search results to include items that are not currently available for download?
</div>
<label style={settingsCheckBoxOptionStyles}>
<input type="checkbox" onChange={this.onShowUnavailableChange} defaultChecked={this.state.showUnavailable} /> Show unavailable content in search results
</label>
</div>
</section>
<section className="card">
<h3>Share Diagnostic Data</h3>
<label style={settingsCheckBoxOptionStyles}>
<input type="checkbox" onChange={this.onShareDataChange} defaultChecked={this.state.daemonSettings.upload_log} /> Help make LBRY better by contributing diagnostic data about my usage
</label>
</section>
*/
export default SettingsPage;

View file

@ -152,7 +152,7 @@ var SendToAddressSection = React.createClass({
<h3>Send Credits</h3>
</div>
<div className="card__content">
<FormRow label="Amount" step="0.01" type="number" placeholder="1.23" size="10" onChange={this.setAmount} />
<FormRow label="Amount" postfix="LBC" step="0.01" type="number" placeholder="1.23" size="10" onChange={this.setAmount} />
</div>
<div className="card__content">
<FormRow label="Recipient Address" placeholder="bbFxRyXXXXXXXXXXXZD8nE7XTLUxYnddTs" type="text" size="60" onChange={this.setAddress} />

View file

@ -1,16 +1,19 @@
@import "../global";
.file-tile__row {
height: $spacing-vertical * 7;
.credit-amount {
float: right;
}
//Hack! Remove below!
.card__title-primary {
margin-top: $spacing-vertical * 2/3;
}
}
.file-tile__thumbnail {
max-width: 100%;
max-height: $spacing-vertical * 7;
vertical-align: middle;
display: block;
margin-left: auto;
margin-right: auto;

View file

@ -8,10 +8,10 @@ $width-input-border: 2px;
}
.form-row__label-row {
margin-top: $spacing-vertical * 2/3;
margin-bottom: $spacing-vertical * 1/3;
margin-top: $spacing-vertical * 5/6;
margin-bottom: $spacing-vertical * 1/6;
line-height: 1;
font-size: 0.9em;
font-size: 0.9 * $font-size;
}
.form-row__label-row--prefix {
float: left;
@ -72,6 +72,13 @@ $width-input-border: 2px;
&.form-field__input--error {
border-color: $color-error;
}
&.form-field__input--inline {
padding-top: 0;
padding-bottom: 0;
border-bottom-width: 1px;
margin-left: 8px;
margin-right: 8px;
}
}
textarea:focus,
@ -104,6 +111,13 @@ $width-input-border: 2px;
width: 330px;
}
.form-field__prefix {
margin-right: 4px;
}
.form-field__postfix {
margin-left: 4px;
}
.form-field__input-number {
width: 70px;
text-align: right;