From 4f659572875d0f70025ce7d6effa83814484e85e Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Fri, 2 Jun 2017 19:09:52 -0400 Subject: [PATCH] not enough progress --- ui/js/actions/app.js | 3 + ui/js/actions/rewards.js | 13 +- ui/js/actions/user.js | 35 +- ui/js/component/auth/index.js | 35 +- ui/js/component/auth/view.jsx | 528 +--------------------- ui/js/component/authOverlay/view.jsx | 431 ++++++------------ ui/js/component/router/view.jsx | 2 + ui/js/component/userEmailVerify/index.jsx | 25 + ui/js/component/userEmailVerify/view.jsx | 47 ++ ui/js/constants/action_types.js | 3 + ui/js/lbryio.js | 2 +- ui/js/page/auth/index.jsx | 25 + ui/js/page/auth/view.jsx | 19 + ui/js/page/rewards/index.js | 8 + ui/js/page/rewards/view.jsx | 18 +- ui/js/reducers/user.js | 20 + ui/js/selectors/app.js | 2 + ui/js/selectors/search.js | 2 + ui/js/selectors/user.js | 22 +- 19 files changed, 414 insertions(+), 826 deletions(-) create mode 100644 ui/js/component/userEmailVerify/index.jsx create mode 100644 ui/js/component/userEmailVerify/view.jsx create mode 100644 ui/js/page/auth/index.jsx create mode 100644 ui/js/page/auth/view.jsx diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js index 1454f8d2c..63c6de0f6 100644 --- a/ui/js/actions/app.js +++ b/ui/js/actions/app.js @@ -18,6 +18,9 @@ import { import { doAuthenticate } from 'actions/user' +import { + doRewardList +} from 'actions/rewards' import { doFileList } from 'actions/file_info' diff --git a/ui/js/actions/rewards.js b/ui/js/actions/rewards.js index 922e7976d..ac368483d 100644 --- a/ui/js/actions/rewards.js +++ b/ui/js/actions/rewards.js @@ -3,7 +3,7 @@ import lbry from "lbry"; import lbryio from "lbryio"; import rewards from "rewards"; -export function doFetchRewards() { +export function doRewardList() { return function(dispatch, getState) { const state = getState(); @@ -11,11 +11,16 @@ export function doFetchRewards() { type: types.FETCH_REWARDS_STARTED, }); - lbryio.call("reward", "list", {}).then(function(userRewards) { + lbryio.call('reward', 'list', {}).then((userRewards) => { dispatch({ type: types.FETCH_REWARDS_COMPLETED, - data: { userRewards }, - }); + data: { userRewards } + }) + }).catch(() => { + dispatch({ + type: types.FETCH_REWARDS_COMPLETED, + data: { userRewards: [] } + }) }); }; } diff --git a/ui/js/actions/user.js b/ui/js/actions/user.js index da8c73c61..a55fb8092 100644 --- a/ui/js/actions/user.js +++ b/ui/js/actions/user.js @@ -4,7 +4,7 @@ import { setLocal } from 'utils' import { - doFetchRewards + doRewardList } from 'actions/rewards' export function doAuthenticate() { @@ -17,7 +17,12 @@ export function doAuthenticate() { type: types.AUTHENTICATION_SUCCESS, data: { user } }) + + dispatch(doRewardList()) //FIXME - where should this happen? + }).catch((error) => { + console.log('auth error') + console.log(error) dispatch({ type: types.AUTHENTICATION_FAILURE, data: { error } @@ -30,6 +35,7 @@ export function doUserEmailNew(email) { return function(dispatch, getState) { dispatch({ type: types.USER_EMAIL_NEW_STARTED, + email: email }) lbryio.call('user_email', 'new', { email }, 'post').then(() => { dispatch({ @@ -59,4 +65,31 @@ export function doUserEmailDecline() { type: types.USER_EMAIL_DECLINE, }) } +} + +export function doUserEmailVerify(email, verificationToken) { + return function(dispatch, getState) { + dispatch({ + type: types.USER_EMAIL_VERIFY_STARTED, + code: code + }) + + const failure = (error) => { + dispatch({ + type: types.USER_EMAIL_VERIFY_FAILURE, + data: { error: error.message } + }) + } + + lbryio.call('user_email', 'confirm', {verification_token: verificationToken, email: email }, 'post').then((userEmail) => { + if (userEmail.is_verified) { + dispatch({ + type: types.USER_EMAIL_VERIFY_SUCCESS, + data: { email } + }) + } else { + failure(new Error("Your email is still not verified.")) //shouldn't happen? + } + }, failure); + } } \ No newline at end of file diff --git a/ui/js/component/auth/index.js b/ui/js/component/auth/index.js index 5cddc68f6..6dd09c335 100644 --- a/ui/js/component/auth/index.js +++ b/ui/js/component/auth/index.js @@ -1,27 +1,22 @@ import React from 'react' import { - connect, + connect } from 'react-redux' import { - selectFetchingRewards, - selectClaimedRewardsByType, - makeSelectRewardByType, -} from 'selectors/rewards' -import AuthOverlay from './view' + doUserEmailDecline +} from 'actions/user' +import { + selectAuthenticationIsPending, + selectEmailNewDeclined, + selectEmailNewExistingEmail, + selectUser, +} from 'selectors/user' +import Auth from './view' -const makeSelect = () => { - const selectRewardByType = makeSelectRewardByType() - - const select = (state) => ({ - fetchingRewards: selectFetchingRewards(state), - claimedRewardsByType: selectClaimedRewardsByType(state), - newUserReward: selectRewardByType(state, { reward_type: 'new_user' }), - }) - - return select -} - -const perform = (dispatch) => ({ +const select = (state) => ({ + isPending: selectAuthenticationIsPending(state), + existingEmail: selectEmailNewExistingEmail(state), + user: selectUser(state), }) -export default connect(makeSelect, perform)(AuthOverlay) +export default connect(select, null)(Auth) diff --git a/ui/js/component/auth/view.jsx b/ui/js/component/auth/view.jsx index c8e29787a..c6932bf5d 100644 --- a/ui/js/component/auth/view.jsx +++ b/ui/js/component/auth/view.jsx @@ -1,517 +1,29 @@ -import React from "react"; -import lbry from "../lbry.js"; -import lbryio from "../lbryio.js"; -import Modal from "./modal.js"; -import ModalPage from "./modal-page.js"; -import Link from "component/link"; -import { RewardLink } from "component/reward-link"; -import { FormRow } from "../component/form.js"; -import { CreditAmount, Address } from "../component/common.js"; -import { getLocal, setLocal } from "../utils.js"; - -class SubmitEmailStage extends React.Component { - constructor(props) { - super(props); - - this.state = { - rewardType: null, - email: '', - showNoEmailConfirm: false, - submitting: false - }; - } - - handleEmailChanged(event) { - this.setState({ - email: event.target.value, - }); - } - - onEmailSaved(email) { - this.props.setStage("confirm", { email: email }); - } - - onEmailSkipClick() { - this.setState({ showNoEmailConfirm: true }) - } - - onEmailSkipConfirm() { - setLocal('auth_bypassed', true); - this.props.setStage(null) - } - - handleSubmit(event) { - event.preventDefault(); - - this.setState({ - submitting: true, - }); - lbryio.call("user_email", "new", { email: this.state.email }, "post").then( - () => { - this.onEmailSaved(this.state.email); - }, - error => { - if ( - error.xhr && - (error.xhr.status == 409 || - error.message == __("This email is already in use")) - ) { - this.onEmailSaved(this.state.email); - return; - } else if (this._emailRow) { - this._emailRow.showError(error.message); - } - this.setState({ submitting: false }); - } - ); - } - - render() { - return ( -
-
{ this.handleSubmit(event) }}> - { this._emailRow = ref }} type="text" label={__("Email")} placeholder="scrwvwls@lbry.io" - name="email" value={this.state.email} - onChange={(event) => { this.handleEmailChanged(event) }} /> -
- { this.handleSubmit(event) }} /> -
- { this.state.showNoEmailConfirm ? -
-

If you continue without an email, you will be ineligible to earn free LBC rewards, as well as unable to receive security related communications.

- { this.onEmailSkipConfirm() }} label="Continue without email" /> -
- : - { this.onEmailSkipClick() }} label="Do I have to?" /> - } - -
- ); - } -} - -class ConfirmEmailStage extends React.Component { - constructor(props) { - super(props); - - this.state = { - rewardType: null, - code: "", - submitting: false, - errorMessage: null, - }; - } - - handleCodeChanged(event) { - this.setState({ - code: event.target.value, - }); - } - - handleSubmit(event) { - event.preventDefault(); - this.setState({ - submitting: true, - }); - - const onSubmitError = error => { - if (this._codeRow) { - this._codeRow.showError(error.message); - } - this.setState({ submitting: false }); - }; - - lbryio - .call( - "user_email", - "confirm", - { verification_token: this.state.code, email: this.props.email }, - "post" - ) - .then(userEmail => { - if (userEmail.is_verified) { - this.props.setStage("welcome"); - } else { - onSubmitError(new Error(__("Your email is still not verified."))); //shouldn't happen? - } - }, onSubmitError); - } - - render() { - return ( -
-
{ - this.handleSubmit(event); - }} - > - { - this._codeRow = ref; - }} - type="text" - name="code" - placeholder="a94bXXXXXXXXXXXXXX" - value={this.state.code} - onChange={event => { - this.handleCodeChanged(event); - }} - helper={__( - "A verification code is required to access this version." - )} - /> -
- { - this.handleSubmit(event); - }} - /> -
-
- {__("No code?")} - {" "} - { - this.props.setStage("nocode"); - }} - label={__("Click here")} - />. -
- -
- ); - } -} - -class WelcomeStage extends React.Component { - static propTypes = { - endAuth: React.PropTypes.func, - }; - - constructor(props) { - super(props); - - this.state = { - hasReward: true, - rewardAmount: null, - }; - } - - onRewardClaim(reward) { - this.setState({ - hasReward: true, - rewardAmount: reward.amount, - }); - } +import React from 'react' +import {BusyMessage} from 'component/common' +import UserEmailNew from 'component/userEmailNew' +import UserEmailVerify from 'component/userEmailVerify' +export class Auth extends React.Component { render() { const { - claimedRewardsByType, - fetchingRewards, - newUserReward, + isPending, + existingEmail, + user, } = this.props - const hasReward = claimedRewardsByType.length > 0 + console.log('auth render') + console.log(this.props) - if (fetchingRewards) return null - if (!newUserReward) return null - - return !hasReward - ? -
-

{__("Welcome to LBRY.")}

-

- {__( - "Using LBRY is like dating a centaur. Totally normal up top, and way different underneath." - )} -

-

{__("Up top, LBRY is similar to popular media sites.")}

-

- {__( - "Below, LBRY is controlled by users -- you -- via blockchain and decentralization." - )} -

-

- {__( - "Thank you for making content freedom possible! Here's a nickel, kid." - )} -

-
- { - this.onRewardClaim(event); - }} - onRewardFailure={() => this.props.setStage(null)} - onConfirmed={() => { - this.props.setStage(null); - }} - /> -
-
-
- : { - this.props.setStage(null); - }} - > -
-

{__("About Your Reward")}

-

- {__("You earned a reward of ")} - {" "} - - {" "}{__('LBRY credits, or "LBC".')} -

-

- {__( - "This reward will show in your Wallet momentarily, probably while you are reading this message." - )} -

-

- {__( - "LBC is used to compensate creators, to publish, and to have say in how the network works." - )} -

-

- {__( - "No need to understand it all just yet! Try watching or downloading something next." - )} -

-

- {__( - "Finally, know that LBRY is an early beta and that it earns the name." - )} -

-
-
; + if (isPending) { + return + } else if (!existingEmail && !user.has_email) { + return + } else if (!user.has_verified_email) { + return + } else { + return Auth is done fix this yo + } } } -const ErrorStage = props => { - return ( -
-

{__("An error was encountered that we cannot continue from.")}

-

{__("At least we're earning the name beta.")}

- {props.errorText ?

{__("Message:")} {props.errorText}

: ""} - { - window.location.reload(); - }} - /> -
- ); -}; - -const PendingStage = props => { - return ( -
-

- {__("Preparing for first access")} -

-
- ); -}; - -class CodeRequiredStage extends React.Component { - constructor(props) { - super(props); - - this._balanceSubscribeId = null; - - this.state = { - balance: 0, - address: getLocal("wallet_address"), - }; - } - - componentWillMount() { - this._balanceSubscribeId = lbry.balanceSubscribe(balance => { - this.setState({ - balance: balance, - }); - }); - - if (!this.state.address) { - lbry.wallet_unused_address().then(address => { - setLocal("wallet_address", address); - this.setState({ address: address }); - }); - } - } - - componentWillUnmount() { - if (this._balanceSubscribeId) { - lbry.balanceUnsubscribe(this._balanceSubscribeId); - } - } - - render() { - const disabled = this.state.balance < 1; - return ( -
-
-

- {__( - "Early access to LBRY is restricted as we build and scale the network." - )} -

-

{__("There are two ways in:")}

-

{__("Own LBRY Credits")}

-

{__("If you own at least 1 LBC, you can get in right now.")}

-

- { - setLocal("auth_bypassed", true); - this.props.setStage(null); - }} - disabled={disabled} - label={__("Let Me In")} - button={disabled ? "alt" : "primary"} - /> -

-

- {__("Your balance is ")}. {__("To increase your balance, send credits to this address:")} -

-

-

-

-

{__("If you don't understand how to send credits, then...")}

-
-
-

{__("Wait For A Code")}

-

- {__( - "If you provide your email, you'll automatically receive a notification when the system is open." - )} -

-

- { - this.props.setStage("email"); - }} - label={__("Return")} - /> -

-
-
- ); - } -} - -export class AuthOverlay extends React.Component { - constructor(props) { - super(props); - - this._stages = { - pending: PendingStage, - error: ErrorStage, - nocode: CodeRequiredStage, - email: SubmitEmailStage, - confirm: ConfirmEmailStage, - welcome: WelcomeStage, - }; - - this.state = { - stage: "pending", - stageProps: {}, - }; - } - - setStage(stage, stageProps = {}) { - this.setState({ - stage: stage, - stageProps: stageProps, - }); - } - - componentWillMount() { - lbryio - .authenticate() - .then(user => { - if (!user.has_verified_email) { - if (getLocal("auth_bypassed")) { - this.setStage(null); - } else { - this.setStage("email", {}); - } - } else { - const { - claimedRewardsByType, - } = this.props - claimedRewardsByType[rewards.TYPE_NEW_USER] ? this.setStage(null) : this.setStage("welcome") - }}) - .catch(err => { - this.setStage("error", { errorText: err.message }); - document.dispatchEvent( - new CustomEvent("unhandledError", { - detail: { - message: err.message, - data: err.stack, - }, - }) - ); - }); - } - - render() { - if (!this.state.stage) { - return null; - } - const StageContent = this._stages[this.state.stage]; - - if (!StageContent) { - return ( - {__("Unknown authentication step.")} - ); - } - - return this.state.stage != "welcome" - ? -

{__("LBRY Early Access")}

- { - this.setStage(stage, stageProps); - }} - /> -
- : { - this.setStage(stage, stageProps); - }} - {...this.state.stageProps} - />; - } -} - -export default AuthOverlay +export default Auth diff --git a/ui/js/component/authOverlay/view.jsx b/ui/js/component/authOverlay/view.jsx index 1356892fe..16774d2f9 100644 --- a/ui/js/component/authOverlay/view.jsx +++ b/ui/js/component/authOverlay/view.jsx @@ -1,17 +1,10 @@ import React from "react"; -import lbry from "lbry.js"; -import lbryio from "lbryio.js"; -import Modal from "component/modal.js"; import ModalPage from "component/modal-page.js"; +import {BusyMessage} from 'component/common' +import Auth from 'component/auth' import Link from "component/link" -import {BusyMessage} from "component/common" -import {RewardLink} from 'component/rewardLink'; -import UserEmailNew from 'component/userEmailNew'; -import {FormRow} from "component/form.js"; -import {CreditAmount, Address} from "component/common.js"; -import {getLocal, setLocal} from 'utils.js'; -class EmailStage extends React.Component { +export class AuthOverlay extends React.Component { constructor(props) { super(props); @@ -29,9 +22,19 @@ class EmailStage extends React.Component { } render() { - return ( -
- + const { + isPending, + isEmailDeclined, + user, + } = this.props + + if (!isEmailDeclined && (isPending || (user && !user.has_email))) { + return +

LBRY Early Access

+ { isPending ? + : + } + { isPending ? '' :
{ this.state.showNoEmailConfirm ?
@@ -41,282 +44,136 @@ class EmailStage extends React.Component { : { this.onEmailSkipClick() }} label="Do I have to?" /> } -
-
- ); - } -} - -class ConfirmEmailStage extends React.Component { - constructor(props) { - super(props); - - this.state = { - code: '', - submitting: false, - errorMessage: null, - }; - } - - handleCodeChanged(event) { - this.setState({ - code: event.target.value, - }); - } - - handleSubmit(event) { - event.preventDefault(); - this.setState({ - submitting: true, - }); - - const onSubmitError = (error) => { - if (this._codeRow) { - this._codeRow.showError(error.message) - } - this.setState({ submitting: false }); - }; - - lbryio.call('user_email', 'confirm', {verification_token: this.state.code, email: this.props.email}, 'post').then((userEmail) => { - if (userEmail.is_verified) { - this.props.setStage("welcome") - } else { - onSubmitError(new Error("Your email is still not verified.")) //shouldn't happen? - } - }, onSubmitError); - } - - render() { - return ( -
-
{ this.handleSubmit(event) }}> - { this._codeRow = ref }} type="text" - name="code" placeholder="a94bXXXXXXXXXXXXXX" value={this.state.code} onChange={(event) => { this.handleCodeChanged(event) }} - helper="A verification code is required to access this version."/> -
- { this.handleSubmit(event)}} /> -
-
- No code? { this.props.setStage("nocode")}} label="Click here" />. -
- -
- ); - } -} - -class WelcomeStage extends React.Component { - static propTypes = { - endAuth: React.PropTypes.func, - } - - constructor(props) { - super(props); - - this.state = { - hasReward: false, - rewardAmount: null, - }; - } - - onRewardClaim(reward) { - this.setState({ - hasReward: true, - rewardAmount: reward.amount - }) - } - - render() { - return ( - !this.state.hasReward ? - -
-

Welcome to LBRY.

-

Using LBRY is like dating a centaur. Totally normal up top, and way different underneath.

-

Up top, LBRY is similar to popular media sites.

-

Below, LBRY is controlled by users -- you -- via blockchain and decentralization.

-

Thank you for making content freedom possible! Here's a nickel, kid.

-
- { this.onRewardClaim(event) }} onRewardFailure={() => this.props.setStage(null)} onConfirmed={() => { this.props.setStage(null) }} /> -
-
-
: - { this.props.setStage(null) }}> -
-

About Your Reward

-

You earned a reward of LBRY credits, or LBC.

-

This reward will show in your Wallet momentarily, probably while you are reading this message.

-

LBC is used to compensate creators, to publish, and to have say in how the network works.

-

No need to understand it all just yet! Try watching or downloading something next.

-

Finally, know that LBRY is an early beta and that it earns the name.

-
-
- ); - } -} - -const ErrorStage = (props) => { - return
-

An error was encountered that we cannot continue from.

-

At least we're earning the name beta.

- { props.errorText ?

Message: {props.errorText}

: '' } - { window.location.reload() } } /> -
-} - -const PendingStage = (props) => { - return
- -
-} - - -class CodeRequiredStage extends React.Component { - constructor(props) { - super(props); - - this._balanceSubscribeId = null - - this.state = { - balance: 0, - address: getLocal('wallet_address') - }; - } - - componentWillMount() { - this._balanceSubscribeId = lbry.balanceSubscribe((balance) => { - this.setState({ - balance: balance - }); - }) - - if (!this.state.address) { - lbry.wallet_unused_address().then((address) => { - setLocal('wallet_address', address); - this.setState({ address: address }); - }); - } - } - - componentWillUnmount() { - if (this._balanceSubscribeId) { - lbry.balanceUnsubscribe(this._balanceSubscribeId) - } - } - - render() { - const disabled = this.state.balance < 1; - return ( -
-
-

Early access to LBRY is restricted as we build and scale the network.

-

There are two ways in.

-

Own LBRY Credits

-

If you own at least 1 LBC, you can get in right now.

-

{ setLocal('auth_bypassed', true); this.props.setStage(null); }} - disabled={disabled} label="Let Me In" button={ disabled ? "alt" : "primary" } />

-

Your balance is . To increase your balance, send credits to this address:

-

-

If you don't understand how to send credits, then...

-
-
-

Wait For A Code

-

If you provide your email, you'll automatically receive a notification when the system is open.

-

{ this.props.setStage("email"); }} label="Return" />

-
-
- ); - } -} - - -export class AuthOverlay extends React.Component { - constructor(props) { - super(props); - - this._stages = { - error: ErrorStage, - nocode: CodeRequiredStage, - confirm: ConfirmEmailStage, - welcome: WelcomeStage - } - } - - setStage(stage, stageProps = {}) { - this.setState({ - stage: stage, - stageProps: stageProps - }) - } - - componentWillMount() { - // lbryio.authenticate().then((user) => { - // if (!user.has_verified_email) { - // if (getLocal('auth_bypassed')) { - // this.setStage(null) - // } else { - // this.setStage("email", {}) - // } - // } else { - // lbryio.call('reward', 'list', {}).then((userRewards) => { - // userRewards.filter(function(reward) { - // return reward.reward_type == rewards.TYPE_NEW_USER && reward.transaction_id; - // }).length ? - // this.setStage(null) : - // this.setStage("welcome") - // }); - // } - // }).catch((err) => { - // this.setStage("error", { errorText: err.message }) - // document.dispatchEvent(new CustomEvent('unhandledError', { - // detail: { - // message: err.message, - // data: err.stack - // } - // })); - // }) - } - - render() { - let stageContent - - const { - isPending, - isEmailDeclined, - user, - userEmailDecline - } = this.props - - console.log('auth overlay render') - console.log(user) - - if (isEmailDeclined) { - return null - } else if (isPending) { - stageContent = ; - } else if (!user.has_email) { - stageContent = ; - } - else { - return null - //StageContent = this._stages[this.state.stage]; + } + } - return -

LBRY Early Access

- {stageContent} -
; - -//setStage={(stage, stageProps) => { this.setStage(stage, stageProps) }} {...this.state.stageProps} - return ( - true || this.state.stage != "welcome" ? - -

LBRY Early Access

- -
: - - ); + return null } } -export default AuthOverlay \ No newline at end of file +export default AuthOverlay + +// class WelcomeStage extends React.Component { +// static propTypes = { +// endAuth: React.PropTypes.func, +// } +// +// constructor(props) { +// super(props); +// +// this.state = { +// hasReward: false, +// rewardAmount: null, +// }; +// } +// +// onRewardClaim(reward) { +// this.setState({ +// hasReward: true, +// rewardAmount: reward.amount +// }) +// } +// +// render() { +// return ( +// !this.state.hasReward ? +// +//
+//

Welcome to LBRY.

+//

Using LBRY is like dating a centaur. Totally normal up top, and way different underneath.

+//

Up top, LBRY is similar to popular media sites.

+//

Below, LBRY is controlled by users -- you -- via blockchain and decentralization.

+//

Thank you for making content freedom possible! Here's a nickel, kid.

+//
+// { this.onRewardClaim(event) }} onRewardFailure={() => this.props.setStage(null)} onConfirmed={() => { this.props.setStage(null) }} /> +//
+//
+//
: +// { this.props.setStage(null) }}> +//
+//

About Your Reward

+//

You earned a reward of LBRY credits, or LBC.

+//

This reward will show in your Wallet momentarily, probably while you are reading this message.

+//

LBC is used to compensate creators, to publish, and to have say in how the network works.

+//

No need to understand it all just yet! Try watching or downloading something next.

+//

Finally, know that LBRY is an early beta and that it earns the name.

+//
+//
+// ); +// } +// // } +// // +// // const ErrorStage = (props) => { +// // return
+// //

An error was encountered that we cannot continue from.

+// //

At least we're earning the name beta.

+// // { props.errorText ?

Message: {props.errorText}

: '' } +// // { window.location.reload() } } /> +// //
+// // } +// // // +// // // const PendingStage = (props) => { +// // // return
+// // // +// // //
+// // // } +// // // // +// // // // +// // // // class CodeRequiredStage extends React.Component { +// // // // constructor(props) { +// // // // super(props); +// // // // +// // // // this._balanceSubscribeId = null +// // // // +// // // // this.state = { +// // // // balance: 0, +// // // // address: getLocal('wallet_address') +// // // // }; +// // // // } +// // // // +// // // // componentWillMount() { +// // // // this._balanceSubscribeId = lbry.balanceSubscribe((balance) => { +// // // // this.setState({ +// // // // balance: balance +// // // // }); +// // // // }) +// // // // +// // // // if (!this.state.address) { +// // // // lbry.wallet_unused_address().then((address) => { +// // // // setLocal('wallet_address', address); +// // // // this.setState({ address: address }); +// // // // }); +// // // // } +// // // // } +// // // // +// // // // componentWillUnmount() { +// // // // if (this._balanceSubscribeId) { +// // // // lbry.balanceUnsubscribe(this._balanceSubscribeId) +// // // // } +// // // // } +// // // // +// // // // render() { +// // // // const disabled = this.state.balance < 1; +// // // // return ( +// // // //
+// // // //
+// // // //

Early access to LBRY is restricted as we build and scale the network.

+// // // //

There are two ways in.

+// // // //

Own LBRY Credits

+// // // //

If you own at least 1 LBC, you can get in right now.

+// // // //

{ setLocal('auth_bypassed', true); this.props.setStage(null); }} +// // // // disabled={disabled} label="Let Me In" button={ disabled ? "alt" : "primary" } />

+// // // //

Your balance is . To increase your balance, send credits to this address:

+// // // //

+// // // //

If you don't understand how to send credits, then...

+// // // //
+// // // //
+// // // //

Wait For A Code

+// // // //

If you provide your email, you'll automatically receive a notification when the system is open.

+// // // //

{ this.props.setStage("email"); }} label="Return" />

+// // // //
+// // // //
+// // // // ); +// // // // } +// // // // } diff --git a/ui/js/component/router/view.jsx b/ui/js/component/router/view.jsx index 131f222e6..84c7bf964 100644 --- a/ui/js/component/router/view.jsx +++ b/ui/js/component/router/view.jsx @@ -1,4 +1,5 @@ import React from "react"; +import AuthPage from 'page/auth'; import SettingsPage from "page/settings"; import HelpPage from "page/help"; import ReportPage from "page/report.js"; @@ -41,6 +42,7 @@ const Router = props => { discover: , rewards: , search: , + "account-verification": }); }; diff --git a/ui/js/component/userEmailVerify/index.jsx b/ui/js/component/userEmailVerify/index.jsx new file mode 100644 index 000000000..dd02f136c --- /dev/null +++ b/ui/js/component/userEmailVerify/index.jsx @@ -0,0 +1,25 @@ +import React from 'react' +import { + connect +} from 'react-redux' +import { + doUserEmailVerify +} from 'actions/user' +import { + selectEmailVerifyIsPending, + selectEmailNewExistingEmail, + selectEmailVerifyErrorMessage, +} from 'selectors/user' +import UserEmailVerify from './view' + +const select = (state) => ({ + isPending: selectEmailVerifyIsPending(state), + email: selectEmailNewExistingEmail, + errorMessage: selectEmailVerifyErrorMessage(state), +}) + +const perform = (dispatch) => ({ + verifyUserEmail: (email, code) => dispatch(doUserEmailVerify(email, code)) +}) + +export default connect(select, perform)(UserEmailVerify) diff --git a/ui/js/component/userEmailVerify/view.jsx b/ui/js/component/userEmailVerify/view.jsx new file mode 100644 index 000000000..c039ab63e --- /dev/null +++ b/ui/js/component/userEmailVerify/view.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import Link from 'component/link'; +import {FormRow} from 'component/form.js'; + +class UserEmailVerify extends React.Component { + constructor(props) { + super(props); + + this.state = { + code: '', + }; + } + + handleCodeChanged(event) { + this.setState({ + code: event.target.value, + }); + } + + handleSubmit(event) { + event.preventDefault(); + this.props.verifyUserEmail(this.state.code) + } +// +//
+// No code? { this.props.setStage("nocode")}} label="Click here" />. +//
+ + render() { + const { + errorMessage, + isPending + } = this.props + + return
{ this.handleSubmit(event) }}> + { this.handleCodeChanged(event) }} + errorMessage={errorMessage} + helper="A verification code is required to access this version."/> +
+ { this.handleSubmit(event)}} /> +
+ + } +} + +export default UserEmailVerify \ No newline at end of file diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js index 8bcabc328..647c6e108 100644 --- a/ui/js/constants/action_types.js +++ b/ui/js/constants/action_types.js @@ -79,6 +79,9 @@ export const USER_EMAIL_NEW_STARTED = 'USER_EMAIL_NEW_STARTED' export const USER_EMAIL_NEW_SUCCESS = 'USER_EMAIL_NEW_SUCCESS' export const USER_EMAIL_NEW_EXISTS = 'USER_EMAIL_NEW_EXISTS' export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE' +export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED' +export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS' +export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE' // Rewards export const FETCH_REWARDS_STARTED = 'FETCH_REWARDS_STARTED' diff --git a/ui/js/lbryio.js b/ui/js/lbryio.js index 4fa78b94a..839af4cb2 100644 --- a/ui/js/lbryio.js +++ b/ui/js/lbryio.js @@ -144,7 +144,7 @@ lbryio.authenticate = function() { lbryio._authenticationPromise = new Promise((resolve, reject) => { lbry.status().then((response) => { - let installation_id = response.installation_id.substring(0, response.installation_id.length - 2) + "D"; + let installation_id = response.installation_id.substring(0, response.installation_id.length - 2) + "E"; function setCurrentUser() { lbryio.call('user', 'me').then((data) => { diff --git a/ui/js/page/auth/index.jsx b/ui/js/page/auth/index.jsx new file mode 100644 index 000000000..91fdb0012 --- /dev/null +++ b/ui/js/page/auth/index.jsx @@ -0,0 +1,25 @@ +import React from 'react' +import { + connect +} from 'react-redux' +import { + doUserEmailDecline +} from 'actions/user' +import { + selectAuthenticationIsPending, + selectEmailNewDeclined, + selectUser, +} from 'selectors/user' +import AuthPage from './view' + +const select = (state) => ({ + isPending: selectAuthenticationIsPending(state), + isEmailDeclined: selectEmailNewDeclined(state), + user: selectUser(state), +}) + +const perform = (dispatch) => ({ + userEmailDecline: () => dispatch(doUserEmailDecline()) +}) + +export default connect(select, perform)(AuthPage) diff --git a/ui/js/page/auth/view.jsx b/ui/js/page/auth/view.jsx new file mode 100644 index 000000000..c27c683f2 --- /dev/null +++ b/ui/js/page/auth/view.jsx @@ -0,0 +1,19 @@ +import React from 'react' +import Auth from 'component/auth' + +export class AuthPage extends React.Component { + render() { + return
+
+
+

Early Access Verification

+
+
+ +
+
+
+ } +} + +export default AuthPage \ No newline at end of file diff --git a/ui/js/page/rewards/index.js b/ui/js/page/rewards/index.js index 5e7e0aa5c..a3e4b102b 100644 --- a/ui/js/page/rewards/index.js +++ b/ui/js/page/rewards/index.js @@ -2,19 +2,27 @@ import React from 'react' import { connect, } from 'react-redux' +import { + doNavigate +} from 'actions/app' import { selectFetchingRewards, selectIsRewardEligible, selectRewards, } from 'selectors/rewards' +import { + selectUserIsRewardEligible +} from 'selectors/user' import RewardsPage from './view' const select = (state) => ({ fetching: selectFetchingRewards(state), rewards: selectRewards(state), + isEligible: selectUserIsRewardEligible(state) }) const perform = (dispatch) => ({ + navigateToAuth: () => dispatch(doNavigate('/account-verification')) }) export default connect(select, perform)(RewardsPage) diff --git a/ui/js/page/rewards/view.jsx b/ui/js/page/rewards/view.jsx index 1071c4aab..f02fbb977 100644 --- a/ui/js/page/rewards/view.jsx +++ b/ui/js/page/rewards/view.jsx @@ -1,7 +1,8 @@ import React from 'react'; import lbryio from 'lbryio'; -import {CreditAmount, Icon} from 'component/common'; +import {BusyMessage, CreditAmount, Icon} from 'component/common'; import SubHeader from 'component/subHeader' +import Link from 'component/link' import RewardLink from 'component/rewardLink'; const RewardTile = (props) => { @@ -32,15 +33,24 @@ const RewardTile = (props) => { const RewardsPage = (props) => { const { fetching, + isEligible, + navigateToAuth, rewards, } = props let content - if (fetching) content =
Fetching rewards
- if (!fetching && rewards.length == 0) content =
Failed to load rewards.
- if (!fetching && rewards.length > 0) { + if (!isEligible) { + content =
+ You are not eligible to claim rewards. { ' ' } + . +
+ } else if (fetching) { + content = + } else if (rewards.length > 0) { content = rewards.map(reward => ) + } else { + content =
Failed to load rewards.
} return ( diff --git a/ui/js/reducers/user.js b/ui/js/reducers/user.js index 40b2c1627..28dc981f2 100644 --- a/ui/js/reducers/user.js +++ b/ui/js/reducers/user.js @@ -54,6 +54,7 @@ reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) { reducers[types.USER_EMAIL_NEW_EXISTS] = function(state, action) { return Object.assign({}, state, { + emailNewExistingEmail: action.data.email, emailNewIsPending: false, }) } @@ -65,6 +66,25 @@ reducers[types.USER_EMAIL_NEW_FAILURE] = function(state, action) { }) } +reducers[types.USER_EMAIL_VERIFY_STARTED] = function(state, action) { + return Object.assign({}, state, { + emailVerifyIsPending: true, + emailVerifyErrorMessage: '' + }) +} + +reducers[types.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) { + return Object.assign({}, state, { + emailVerifyIsPending: false, + }) +} + +reducers[types.USER_EMAIL_VERIFY_FAILURE] = function(state, action) { + return Object.assign({}, state, { + emailVerifyIsPending: false, + emailVerifyErrorMessage: action.data.error + }) +} export default function reducer(state = defaultState, action) { diff --git a/ui/js/selectors/app.js b/ui/js/selectors/app.js index c03bb28d3..d704709ab 100644 --- a/ui/js/selectors/app.js +++ b/ui/js/selectors/app.js @@ -54,6 +54,8 @@ export const selectPageTitle = createSelector( return __("Publishes"); case "discover": return __("Home"); + case 'account-verification': + return __('Early Access Verification') default: return ""; } diff --git a/ui/js/selectors/search.js b/ui/js/selectors/search.js index 9660cdcb5..2d1e9c444 100644 --- a/ui/js/selectors/search.js +++ b/ui/js/selectors/search.js @@ -68,5 +68,7 @@ export const selectWunderBarIcon = createSelector(selectCurrentPage, page => { return "icon-code"; case "discover": return "icon-home"; + case 'account-verification': + return 'icon-lock' } }); diff --git a/ui/js/selectors/user.js b/ui/js/selectors/user.js index 5374fa3fe..2b182ba2f 100644 --- a/ui/js/selectors/user.js +++ b/ui/js/selectors/user.js @@ -12,6 +12,11 @@ export const selectUser = createSelector( (state) => state.user ) +export const selectUserIsRewardEligible = createSelector( + _selectState, + (state) => state.user.can_claim_rewards +) + export const selectEmailNewIsPending = createSelector( _selectState, (state) => state.emailNewIsPending @@ -25,4 +30,19 @@ export const selectEmailNewErrorMessage = createSelector( export const selectEmailNewDeclined = createSelector( _selectState, (state) => state.emailNewDeclined -) \ No newline at end of file +) + +export const selectEmailNewExistingEmail = createSelector( + _selectState, + (state) => state.emailNewExistingEmail +) + +export const selectEmailVerifyIsPending = createSelector( + _selectState, + (state) => state.emailVerifyIsPending +) + +export const selectEmailVerifyErrorMessage = createSelector( + _selectState, + (state) => state.emailVerifyErrorMessage +)