- { this.props.label ?
-
-
-
: ''
- }
-
- {this.props.children}
-
- { !hasError && this.props.helper ?
{this.props.helper}
: '' }
- { hasError ?
{this.state.adviceText}
: '' }
+ return
+ { renderElementInsideLabel ?
+
: element }
+ { isError ?
{this.state.errorMessage}
: '' }
return (
this.props.row ?
@@ -108,6 +90,57 @@ var FormField = React.createClass({
field
);
}
-});
+})
-export default FormField;
+export let FormRow = React.createClass({
+ 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,
+ errorMessage: null,
+ }
+ },
+ showError: function(text) {
+ this.setState({
+ isError: true,
+ errorMessage: text,
+ });
+ },
+ getValue: function() {
+ return this.refs.field.getValue();
+ },
+ render: function() {
+ const fieldProps = Object.assign({}, this.props),
+ elementId = formFieldId(),
+ renderLabelInFormField = formFieldNestedLabelTypes.includes(this.props.type);
+
+ if (!renderLabelInFormField) {
+ delete fieldProps.label;
+ }
+ delete fieldProps.helper;
+
+ return
+ { this.props.label && !renderLabelInFormField ?
+
+
+
: '' }
+
+ { !this.state.isError && this.props.helper ?
{this.props.helper}
: '' }
+ { this.state.isError ?
{this.state.errorMessage}
: '' }
+
+ }
+})
diff --git a/ui/js/component/header.js b/ui/js/component/header.js
index 6e186cc44..463042cff 100644
--- a/ui/js/component/header.js
+++ b/ui/js/component/header.js
@@ -1,6 +1,6 @@
import React from 'react';
import {Link} from './link.js';
-import NotificationBar from './notification-bar.js';
+import {Icon} from './common.js';
var Header = React.createClass({
getInitialState: function() {
@@ -53,6 +53,7 @@ var Header = React.createClass({
{ this.state.title }
+
@@ -62,7 +63,6 @@ var Header = React.createClass({
:
''
}
-
);
}
diff --git a/ui/js/component/link.js b/ui/js/component/link.js
index 8a4d76f76..8bcaddd48 100644
--- a/ui/js/component/link.js
+++ b/ui/js/component/link.js
@@ -1,5 +1,7 @@
import React from 'react';
import {Icon} from './common.js';
+import Modal from '../component/modal.js';
+import rewards from '../rewards.js';
export let Link = React.createClass({
propTypes: {
@@ -52,4 +54,76 @@ export let Link = React.createClass({
);
}
+});
+
+export let RewardLink = React.createClass({
+ propTypes: {
+ type: React.PropTypes.string.isRequired,
+ claimed: React.PropTypes.bool,
+ onRewardClaim: React.PropTypes.func
+ },
+ refreshClaimable: function() {
+ switch(this.props.type) {
+ case 'new_user':
+ this.setState({ claimable: true });
+ return;
+
+ case 'first_publish':
+ lbry.claim_list_mine().then(function(list) {
+ this.setState({
+ claimable: list.length > 0
+ })
+ }.bind(this));
+ return;
+ }
+ },
+ componentWillMount: function() {
+ this.refreshClaimable();
+ },
+ getInitialState: function() {
+ return {
+ claimable: true,
+ pending: false,
+ errorMessage: null
+ }
+ },
+ claimReward: function() {
+ this.setState({
+ pending: true
+ })
+ rewards.claimReward(this.props.type).then(function(reward) {
+ console.log(reward);
+ this.setState({
+ pending: false,
+ errorMessage: null
+ })
+ if (this.props.onRewardClaim) {
+ this.props.onRewardClaim();
+ }
+ }.bind(this)).catch(function(error) {
+ this.setState({
+ errorMessage: error.message,
+ pending: false
+ })
+ }.bind(this))
+ },
+ clearError: function() {
+ this.setState({
+ errorMessage: null
+ })
+ },
+ render: function() {
+ return (
+
+ {this.props.claimed
+ ? Reward claimed.
+ : }
+ {this.state.errorMessage ?
+
+ {this.state.errorMessage}
+
+ : ''}
+
+ );
+ }
});
\ No newline at end of file
diff --git a/ui/js/component/notification-bar.js b/ui/js/component/notification-bar.js
deleted file mode 100644
index f6662b552..000000000
--- a/ui/js/component/notification-bar.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import lbry from '../lbry.js';
-import Notice from '../component/notice.js';
-
-export const NotificationBar = React.createClass({
- _displayTime: 8, // in seconds
-
- _hideTimeout: null,
-
- getInitialState: function() {
- return {
- message: null,
- isError: null,
- }
- },
- handleNoticeReceived: function(event) {
- if (this._hideTimeout) {
- clearTimeout(this._hideTimeout);
- }
-
- const {detail: {message, isError}} = event;
- this.setState({
- message: message,
- isError: isError,
- });
-
- this._hideTimeout = setTimeout(() => {
- this.setState({
- message: null,
- isError: null,
- });
- }, this._displayTime * 1000);
- },
- componentWillMount: function() {
- document.addEventListener('globalNotice', this.handleNoticeReceived);
- },
- componentWillUnmount: function() {
- document.removeEventListener('globalNotice', this.handleNoticeReceived);
- },
- render: function() {
- if (!this.state.message) {
- return null;
- }
-
- return (
-
- {this.state.message}
-
- );
- },
-});
-
-export default NotificationBar;
\ No newline at end of file
diff --git a/ui/js/component/snack-bar.js b/ui/js/component/snack-bar.js
new file mode 100644
index 000000000..e1ddb01b0
--- /dev/null
+++ b/ui/js/component/snack-bar.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import lbry from '../lbry.js';
+
+export const SnackBar = React.createClass({
+
+ _displayTime: 5, // in seconds
+
+ _hideTimeout: null,
+
+ getInitialState: function() {
+ return {
+ snacks: []
+ }
+ },
+ handleSnackReceived: function(event) {
+ // console.log(event);
+ // if (this._hideTimeout) {
+ // clearTimeout(this._hideTimeout);
+ // }
+
+ let snacks = this.state.snacks;
+ snacks.push(event.detail);
+ this.setState({ snacks: snacks});
+ },
+ componentWillMount: function() {
+ document.addEventListener('globalNotice', this.handleSnackReceived);
+ },
+ componentWillUnmount: function() {
+ document.removeEventListener('globalNotice', this.handleSnackReceived);
+ },
+ render: function() {
+ if (!this.state.snacks.length) {
+ this._hideTimeout = null; //should be unmounting anyway, but be safe?
+ return null;
+ }
+
+ let snack = this.state.snacks[0];
+
+ if (this._hideTimeout === null) {
+ this._hideTimeout = setTimeout(function() {
+ this._hideTimeout = null;
+ let snacks = this.state.snacks;
+ snacks.shift();
+ this.setState({ snacks: snacks });
+ }.bind(this), this._displayTime * 1000);
+ }
+
+ return (
+
+ {snack.message}
+ {snack.linkText && snack.linkTarget ?
+
{snack.linkText} : ''}
+
+ );
+ },
+});
+
+export default SnackBar;
\ No newline at end of file
diff --git a/ui/js/component/welcome.js b/ui/js/component/welcome.js
deleted file mode 100644
index 36036abe1..000000000
--- a/ui/js/component/welcome.js
+++ /dev/null
@@ -1,156 +0,0 @@
-import React from 'react';
-import lbryio from '../lbryio.js';
-
-import ModalPage from './modal-page.js';
-import {Link} from '../component/link.js';
-import FormField from '../component/form.js';
-import Notice from '../component/notice.js'
-
-
-const SubmitEmailStage = React.createClass({
- getInitialState: function() {
- return {
- rewardType: null,
- email: '',
- submitting: false,
- errorMessage: null,
- };
- },
- handleEmailChanged: function(event) {
- this.setState({
- email: event.target.value,
- });
- },
- handleSubmit: function(event) {
- event.preventDefault();
-
- this.setState({
- submitting: true,
- });
- lbryio.call('user_email', 'new', {email: this.state.email}, 'post').then(() => {
- this.props.onDone();
- }, (error) => {
- this.setState({
- submitting: false,
- errorMessage: error.message,
- });
- });
- },
- render: function() {
- return (
-
- Welcome to LBRY
- {this.state.errorMessage
- ?
- {this.state.errorMessage}
-
- : null}
- Copy here explaining what we do with your email, and the reward.
-
-
- );
- }
-});
-
-const ConfirmEmailStage = React.createClass({
- getInitialState: function() {
- return {
- rewardType: null,
- code: '',
- submitting: false,
- errorMessage: null,
- };
- },
- handleCodeChanged: function(event) {
- this.setState({
- code: event.target.value,
- });
- },
- handleSubmit: function(event) {
- event.preventDefault();
- this.setState({
- submitting: true,
- });
-
- lbryio.call('user_email', 'confirm', {verification_token: this.state.code}, 'post').then(() => {
- rewards.claimReward('confirm_email').then(() => {
- console.log('succeeded');
- this.props.onDone();
- }, (err) => {
- console.log('failed');
- this.props.onDone();
- });
- }, (error) => {
- this.setState({
- submitting: false,
- errorMessage: error.message,
- });
- });
- },
- render: function() {
- return (
-
- Confirm Your Email Address
- {this.state.errorMessage
- ?
- {this.state.errorMessage}
-
- : null}
- Please enter your verification code to confirm your email address.
-
-
- );
- }
-});
-
-const FinalMessageStage = React.createClass({
- render: function() {
- return (
-
- Email verified
- Text here about what happens next
-
-
- );
- }
-});
-
-export const Welcome = React.createClass({
- _stages: [
- SubmitEmailStage,
- ConfirmEmailStage,
- FinalMessageStage,
- ],
- propTypes: {
- onDone: React.PropTypes.func.isRequired,
- },
- getInitialState: function() {
- return {
- stageNum: 0,
- };
- },
- handleStageDone: function() {
- if (this.state.stageNum >= this._stages.length - 1) {
- this.props.onDone();
- } else {
- this.setState({
- stageNum: this.state.stageNum + 1,
- });
- }
- },
- render: function() {
- const Content = this._stages[this.state.stageNum];
- return (
-
-
-
- );
- }
-});
-
diff --git a/ui/js/lbry.js b/ui/js/lbry.js
index 4d227248e..8fbfdb04d 100644
--- a/ui/js/lbry.js
+++ b/ui/js/lbry.js
@@ -282,29 +282,6 @@ lbry.getCostInfo = function(lbryUri, callback, errorCallback) {
});
}
-lbry.getFeaturedDiscoverNames = function(callback) {
- return new Promise(function(resolve, reject) {
- var xhr = new XMLHttpRequest;
- xhr.open('GET', 'https://api.lbry.io/discover/list', true);
- xhr.onload = () => {
- if (xhr.status === 200) {
- var responseData = JSON.parse(xhr.responseText);
- if (responseData.data) //new signature, once api.lbry.io is updated
- {
- resolve(responseData.data);
- }
- else
- {
- resolve(responseData);
- }
- } else {
- reject(Error('Failed to fetch featured names.'));
- }
- };
- xhr.send();
- });
-}
-
lbry.getMyClaims = function(callback) {
lbry.call('get_name_claims', {}, callback);
}
diff --git a/ui/js/lbryio.js b/ui/js/lbryio.js
index 582592b71..9c6d37f03 100644
--- a/ui/js/lbryio.js
+++ b/ui/js/lbryio.js
@@ -1,4 +1,4 @@
-import {getLocal, setLocal} from './utils.js';
+import {getLocal, getSession, setSession, setLocal} from './utils.js';
import lbry from './lbry.js';
const querystring = require('querystring');
@@ -20,18 +20,7 @@ const mocks = {
value: 50,
claimed: false,
};
- },
- 'reward_type.list': () => {
- return [
- {
- name: 'link_github',
- title: 'Link your GitHub account',
- description: 'Link LBRY to your GitHub account',
- value: 50,
- claimed: false,
- },
- ];
- },
+ }
};
lbryio.call = function(resource, action, params={}, method='get') {
@@ -103,7 +92,7 @@ lbryio.setAccessToken = (token) => {
lbryio._accessToken = token
}
-lbryio.authenticate = () => {
+lbryio.authenticate = function() {
if (lbryio._authenticationPromise === null) {
lbryio._authenticationPromise = new Promise((resolve, reject) => {
lbry.status().then(({installation_id}) => {
@@ -117,7 +106,12 @@ lbryio.authenticate = () => {
resolve(data)
}).catch(function(err) {
lbryio.setAccessToken(null);
- reject(err);
+ if (!getSession('reloadedOnFailedAuth')) {
+ setSession('reloadedOnFailedAuth', true)
+ window.location.reload();
+ } else {
+ reject(err);
+ }
})
}
diff --git a/ui/js/main.js b/ui/js/main.js
index 12dee5d92..544ebb0a2 100644
--- a/ui/js/main.js
+++ b/ui/js/main.js
@@ -5,8 +5,8 @@ import lbryio from './lbryio.js';
import lighthouse from './lighthouse.js';
import App from './app.js';
import SplashScreen from './component/splash.js';
+import SnackBar from './component/snack-bar.js';
import {AuthOverlay} from './component/auth.js';
-import {Welcome} from './component/welcome.js';
const {remote} = require('electron');
const contextMenu = remote.require('./menu/context-menu');
@@ -28,12 +28,12 @@ let init = function() {
lbryio.authenticate() //start auth process as soon as soon as we can get an install ID
})
- async function onDaemonReady() {
+ function onDaemonReady() {
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
- ReactDOM.render(
, canvas)
+ ReactDOM.render(
, canvas)
}
- if (window.sessionStorage.getItem('loaded') == 'y' && false) {
+ if (window.sessionStorage.getItem('loaded') == 'y') {
onDaemonReady();
} else {
ReactDOM.render(
, canvas);
diff --git a/ui/js/page/claim_code.js b/ui/js/page/claim_code.js
deleted file mode 100644
index 7a9976824..000000000
--- a/ui/js/page/claim_code.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import React from 'react';
-import lbry from '../lbry.js';
-import Modal from '../component/modal.js';
-import {Link} from '../component/link.js';
-
-var claimCodeContentStyle = {
- display: 'inline-block',
- textAlign: 'left',
- width: '600px',
-}, claimCodeLabelStyle = {
- display: 'inline-block',
- cursor: 'default',
- width: '130px',
- textAlign: 'right',
- marginRight: '6px',
-};
-
-var ClaimCodePage = React.createClass({
- getInitialState: function() {
- return {
- submitting: false,
- modal: null,
- referralCredits: null,
- activationCredits: null,
- failureReason: null,
- }
- },
- handleSubmit: function(event) {
- if (typeof event !== 'undefined') {
- event.preventDefault();
- }
-
- if (!this.refs.code.value) {
- this.setState({
- modal: 'missingCode',
- });
- return;
- } else if (!this.refs.email.value) {
- this.setState({
- modal: 'missingEmail',
- });
- return;
- }
-
- this.setState({
- submitting: true,
- });
-
- lbry.getUnusedAddress((address) => {
- var code = this.refs.code.value;
- var email = this.refs.email.value;
-
- var xhr = new XMLHttpRequest;
- xhr.addEventListener('load', () => {
- var response = JSON.parse(xhr.responseText);
-
- if (response.success) {
- this.setState({
- modal: 'codeRedeemed',
- referralCredits: response.referralCredits,
- activationCredits: response.activationCredits,
- });
- } else {
- this.setState({
- submitting: false,
- modal: 'codeRedeemFailed',
- failureReason: response.reason,
- });
- }
- });
-
- xhr.addEventListener('error', () => {
- this.setState({
- submitting: false,
- modal: 'couldNotConnect',
- });
- });
-
- xhr.open('POST', 'https://invites.lbry.io', true);
- xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
- xhr.send('code=' + encodeURIComponent(code) + '&address=' + encodeURIComponent(address) +
- '&email=' + encodeURIComponent(email));
- });
- },
- handleSkip: function() {
- this.setState({
- modal: 'skipped',
- });
- },
- handleFinished: function() {
- localStorage.setItem('claimCodeDone', true);
- window.location = '?home';
- },
- closeModal: function() {
- this.setState({
- modal: null,
- });
- },
- render: function() {
- return (
-
-
-
- Please enter an invitation code or choose "Skip."
-
-
- Please enter an email address or choose "Skip."
-
-
- {this.state.failureReason}
-
-
- Your invite code has been redeemed. { ' ' }
- {this.state.referralCredits > 0
- ? `You have also earned ${referralCredits} credits from referrals. A total of ${activationCredits + referralCredits}
- will be added to your balance shortly.`
- : (this.state.activationCredits > 0
- ? `${this.state.activationCredits} credits will be added to your balance shortly.`
- : 'The credits will be added to your balance shortly.')}
-
-
- Welcome to LBRY! You can visit the Wallet page to redeem an invite code at any time.
-
-
- LBRY couldn't connect to our servers to confirm your invitation code. Please check your internet connection.
- If you continue to have problems, you can still browse LBRY and visit the Settings page to redeem your code later.
-
-
- );
- }
-});
-
-export default ClaimCodePage;
diff --git a/ui/js/page/developer.js b/ui/js/page/developer.js
index 93eb1cc11..377204852 100644
--- a/ui/js/page/developer.js
+++ b/ui/js/page/developer.js
@@ -1,6 +1,6 @@
import lbry from '../lbry.js';
import React from 'react';
-import FormField from '../component/form.js';
+import {FormField} from '../component/form.js';
import {Link} from '../component/link.js';
const fs = require('fs');
diff --git a/ui/js/page/discover.js b/ui/js/page/discover.js
index 993481d07..7751db2db 100644
--- a/ui/js/page/discover.js
+++ b/ui/js/page/discover.js
@@ -1,5 +1,6 @@
import React from 'react';
import lbry from '../lbry.js';
+import lbryio from '../lbryio.js';
import lighthouse from '../lighthouse.js';
import {FileTile} from '../component/file-tile.js';
import {Link} from '../component/link.js';
@@ -58,45 +59,44 @@ var SearchResults = React.createClass({
}
});
-var featuredContentLegendStyle = {
- fontSize: '12px',
- color: '#aaa',
- verticalAlign: '15%',
-};
+const communityCategoryToolTipText = ('Community Content is a public space where anyone can share content with the ' +
+'rest of the LBRY community. Bid on the names "one," "two," "three," "four" and ' +
+'"five" to put your content here!');
+
+var FeaturedCategory = React.createClass({
+ render: function() {
+ return (
+ { this.props.category ?
+
{this.props.category}
+ { this.props.category == "community" ?
+
+ : '' }
+ : '' }
+ { this.props.names.map((name) => { return }) }
+ )
+ }
+})
var FeaturedContent = React.createClass({
getInitialState: function() {
return {
- featuredNames: [],
+ featuredNames: {},
};
},
componentWillMount: function() {
- lbry.getFeaturedDiscoverNames().then((featuredNames) => {
+ lbryio.call('discover', 'list', { version: "early-access" } ).then((featuredNames) => {
this.setState({ featuredNames: featuredNames });
});
},
render: function() {
- const toolTipText = ('Community Content is a public space where anyone can share content with the ' +
- 'rest of the LBRY community. Bid on the names "one," "two," "three," "four" and ' +
- '"five" to put your content here!');
-
+ console.log(this.state.featuredNames);
return (
-
-
-
Featured Content
- { this.state.featuredNames.map(name => ) }
-
-
-
- Community Content
-
-
-
-
-
-
-
-
+
+ {
+ Object.keys(this.state.featuredNames).map(function(category) {
+ return
+ }.bind(this))
+ }
);
}
@@ -173,12 +173,12 @@ var DiscoverPage = React.createClass({
},
render: function() {
- //{ !this.props.query && !this.state.searching ?
: null }
return (
{ this.state.searching ? : null }
{ !this.state.searching && this.props.query && this.state.results.length ? : null }
{ !this.state.searching && this.props.query && !this.state.results.length ? : null }
+ { !this.props.query && !this.state.searching ? : null }
);
}
diff --git a/ui/js/page/email.js b/ui/js/page/email.js
index b7f31cd49..76b031737 100644
--- a/ui/js/page/email.js
+++ b/ui/js/page/email.js
@@ -1,7 +1,7 @@
import React from 'react';
import lbryio from '../lbryio.js';
import {getLocal, setLocal} from '../utils.js';
-import FormField from '../component/form.js'
+import {FormField} from '../component/form.js'
import {Link} from '../component/link.js'
import rewards from '../rewards.js';
@@ -12,7 +12,7 @@ const EmailPage = React.createClass({
}
if (!this.state.email) {
- this._emailField.warnRequired();
+ this._emailField.showRequiredError();
}
},
componentWillMount: function() {
diff --git a/ui/js/page/file-list.js b/ui/js/page/file-list.js
index ba91835e7..518bb85d6 100644
--- a/ui/js/page/file-list.js
+++ b/ui/js/page/file-list.js
@@ -2,7 +2,7 @@ import React from 'react';
import lbry from '../lbry.js';
import uri from '../uri.js';
import {Link} from '../component/link.js';
-import FormField from '../component/form.js';
+import {FormField} from '../component/form.js';
import {FileTileStream} from '../component/file-tile.js';
import {BusyMessage, Thumbnail} from '../component/common.js';
diff --git a/ui/js/page/publish.js b/ui/js/page/publish.js
index 6e0799263..4f28dc567 100644
--- a/ui/js/page/publish.js
+++ b/ui/js/page/publish.js
@@ -1,7 +1,7 @@
import React from 'react';
import lbry from '../lbry.js';
import uri from '../uri.js';
-import FormField from '../component/form.js';
+import {FormField, FormRow} from '../component/form.js';
import {Link} from '../component/link.js';
import Modal from '../component/modal.js';
@@ -36,7 +36,7 @@ var PublishPage = React.createClass({
for (let fieldName of checkFields) {
var field = this.refs[fieldName];
if (field.getValue() === '') {
- field.warnRequired();
+ field.showRequiredError();
if (!missingFieldFound) {
field.focus();
missingFieldFound = true;
@@ -84,7 +84,7 @@ var PublishPage = React.createClass({
if (this.refs.file.getValue() !== '') {
publishArgs.file_path = this.refs.file.getValue();
}
-
+
lbry.publish(publishArgs, (message) => {
this.handlePublishStarted();
}, null, (error) => {
@@ -344,9 +344,12 @@ var PublishPage = React.createClass({