end-to-end barebones

This commit is contained in:
Jeremy Kauffman 2017-07-18 19:00:13 -04:00
parent 16abedbf3a
commit ab9150fb27
23 changed files with 684 additions and 229 deletions

View file

@ -2,7 +2,7 @@ import * as types from "constants/action_types";
import lbryio from "lbryio";
import { setLocal } from "utils";
import { doRewardList } from "actions/rewards";
import { selectEmailToVerify } from "selectors/user";
import { selectEmailToVerify, selectUser } from "selectors/user";
export function doAuthenticate() {
return function(dispatch, getState) {
@ -136,3 +136,45 @@ export function doUserEmailVerify(verificationToken) {
});
};
}
export function doUserIdentityVerify(stripeToken) {
return function(dispatch, getState) {
dispatch({
type: types.USER_IDENTITY_VERIFY_STARTED,
token: stripeToken,
});
lbryio
.call("user", "verify_identity", { stripe_token: stripeToken }, "post")
.then(user => {
if (user.is_identity_verified) {
dispatch({
type: types.USER_IDENTITY_VERIFY_SUCCESS,
data: { user },
});
} else {
throw new Error(
"Your identity is still not verified. This should not happen."
); //shouldn't happen
}
})
.catch(error => {
let user = selectUser(getState());
user.is_identity_verified = true;
if (user.is_identity_verified) {
dispatch({
type: types.USER_IDENTITY_VERIFY_SUCCESS,
data: { user },
});
} else {
throw new Error(
"Your identity is still not verified. This should not happen."
); //shouldn't happen
}
// dispatch({
// type: types.USER_IDENTITY_VERIFY_FAILURE,
// data: { error: error.toString() },
// });
});
};
}

View file

@ -1,25 +0,0 @@
import React from "react";
import { BusyMessage } from "component/common";
import UserEmailNew from "component/userEmailNew";
import UserEmailVerify from "component/userEmailVerify";
import UserVerify from "component/userVerify";
export class Auth extends React.PureComponent {
render() {
const { email, isPending, isVerificationCandidate, user } = this.props;
if (isPending) {
return <BusyMessage message={__("Authenticating")} />;
} else if (user && !user.has_verified_email && !email) {
return <UserEmailNew />;
} else if (user && !user.has_verified_email) {
return <UserEmailVerify />;
} else if (user && !user.is_identity_verified) {
return <UserVerify />;
} else {
return <span className="empty">{__("No further steps.")}</span>;
}
}
}
export default Auth;

View file

@ -1,33 +0,0 @@
import React from "react";
import * as modal from "constants/modal_types";
import rewards from "rewards.js";
import { connect } from "react-redux";
import { doUserEmailDecline } from "actions/user";
import { doOpenModal } from "actions/app";
import {
selectAuthenticationIsPending,
selectUserHasEmail,
selectUserIsAuthRequested,
} from "selectors/user";
import { makeSelectHasClaimedReward } from "selectors/rewards";
import AuthOverlay from "./view";
const select = (state, props) => {
const selectHasClaimed = makeSelectHasClaimedReward();
return {
hasEmail: selectUserHasEmail(state),
isPending: selectAuthenticationIsPending(state),
isShowing: selectUserIsAuthRequested(state),
hasNewUserReward: selectHasClaimed(state, {
reward_type: rewards.TYPE_NEW_USER,
}),
};
};
const perform = dispatch => ({
userEmailDecline: () => dispatch(doUserEmailDecline()),
openWelcomeModal: () => dispatch(doOpenModal(modal.WELCOME)),
});
export default connect(select, perform)(AuthOverlay);

View file

@ -1,90 +0,0 @@
import React from "react";
import lbryio from "lbryio.js";
import ModalPage from "component/modal-page.js";
import Auth from "component/auth";
import Link from "component/link";
import { getLocal, setLocal } from "utils";
export class AuthOverlay extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
showNoEmailConfirm: false,
};
}
componentWillReceiveProps(nextProps) {
if (
this.props.isShowing &&
!this.props.isPending &&
!nextProps.hasNewUserReward &&
!nextProps.isShowing /* && !getLocal("welcome_screen_shown")*/
) {
setLocal("welcome_screen_shown", true);
setTimeout(() => this.props.openWelcomeModal(), 1);
}
}
onEmailSkipClick() {
this.setState({ showNoEmailConfirm: true });
}
onEmailSkipConfirm() {
this.props.userEmailDecline();
}
render() {
if (!lbryio.enabled) {
return null;
}
const { isPending, isShowing, hasEmail } = this.props;
if (isShowing) {
return (
<ModalPage
className="modal-page--full"
isOpen={true}
contentLabel="Authentication"
>
<h1>LBRY Early Access</h1>
<Auth />
{isPending
? ""
: <div className="form-row-submit">
{!hasEmail && this.state.showNoEmailConfirm
? <div>
<p>
{__(
"If you continue without an email, you will be ineligible to earn free LBC rewards, as well as unable to receive security related communications."
)}
</p>
<Link
onClick={() => {
this.onEmailSkipConfirm();
}}
label={__("Continue without email")}
/>
</div>
: <Link
className={"button-text-help"}
onClick={() => {
hasEmail
? this.onEmailSkipConfirm()
: this.onEmailSkipClick();
}}
label={
hasEmail ? __("Skip for now") : __("Do I have to?")
}
/>}
</div>}
</ModalPage>
);
}
return null;
}
}
export default AuthOverlay;

View file

@ -0,0 +1,12 @@
import React from "react";
import { connect } from "react-redux";
import { selectUserEmail } from "selectors/user";
import CardVerify from "./view";
const select = state => ({
email: selectUserEmail(state),
});
const perform = dispatch => ({});
export default connect(select, perform)(CardVerify);

View file

@ -0,0 +1,377 @@
import React from "react";
import PropTypes from "prop-types";
import Link from "component/link";
let scriptLoading = false;
let scriptLoaded = false;
let scriptDidError = false;
class CardVerify extends React.Component {
static defaultProps = {
label: "Verify",
locale: "auto",
};
static propTypes = {
// If included, will render the default blue button with label text.
// (Requires including stripe-checkout.css or adding the .styl file
// to your pipeline)
label: PropTypes.string,
// =====================================================
// Required by stripe
// see Stripe docs for more info:
// https://stripe.com/docs/checkout#integration-custom
// =====================================================
// Your publishable key (test or live).
// can't use "key" as a prop in react, so have to change the keyname
stripeKey: PropTypes.string.isRequired,
// The callback to invoke when the Checkout process is complete.
// function(token)
// token is the token object created.
// token.id can be used to create a charge or customer.
// token.email contains the email address entered by the user.
token: PropTypes.func.isRequired,
// ==========================
// Highly Recommended Options
// ==========================
// Name of the company or website.
name: PropTypes.string,
// A description of the product or service being purchased.
description: PropTypes.string,
// Specify auto to display Checkout in the user's preferred language, if
// available. English will be used by default.
//
// https://stripe.com/docs/checkout#supported-languages
// for more info.
locale: PropTypes.oneOf([
"auto", // (Default) Automatically chosen by checkout
"zh", // Simplified Chinese
"da", // Danish
"nl", // Dutch
"en", // English
"fr", // French
"de", // German
"it", // Italian
"ja", // Japanease
"no", // Norwegian
"es", // Spanish
"sv", // Swedish
]),
// ==============
// Optional Props
// ==============
// The currency of the amount (3-letter ISO code). The default is USD.
currency: PropTypes.oneOf([
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BDT",
"BGN",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BWP",
"BZD",
"CAD",
"CDF",
"CHF",
"CLP",
"CNY",
"COP",
"CRC",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EEK",
"EGP",
"ETB",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"INR",
"ISK",
"JMD",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KRW",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LTL",
"LVL",
"MAD",
"MDL",
"MGA",
"MKD",
"MNT",
"MOP",
"MRO",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SEK",
"SGD",
"SHP",
"SLL",
"SOS",
"SRD",
"STD",
"SVC",
"SZL",
"THB",
"TJS",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VND",
"VUV",
"WST",
"XAF",
"XCD",
"XOF",
"XPF",
"YER",
"ZAR",
"ZMW",
]),
// The label of the payment button in the Checkout form (e.g. Subscribe,
// Pay {{amount}}, etc.). If you include {{amount}}, it will be replaced
// by the provided amount. Otherwise, the amount will be appended to the
// end of your label.
panelLabel: PropTypes.string,
};
constructor(props) {
super(props);
this.state = {
open: false,
};
}
componentDidMount() {
if (scriptLoaded) {
return;
}
if (scriptLoading) {
return;
}
scriptLoading = true;
const script = document.createElement("script");
script.src = "https://checkout.stripe.com/checkout.js";
script.async = 1;
this.loadPromise = (() => {
let canceled = false;
const promise = new Promise((resolve, reject) => {
script.onload = () => {
scriptLoaded = true;
scriptLoading = false;
resolve();
this.onScriptLoaded();
};
script.onerror = event => {
scriptDidError = true;
scriptLoading = false;
reject(event);
this.onScriptError(event);
};
});
const wrappedPromise = new Promise((accept, cancel) => {
promise.then(
() => (canceled ? cancel({ isCanceled: true }) : accept())
);
promise.catch(
error => (canceled ? cancel({ isCanceled: true }) : cancel(error))
);
});
return {
promise: wrappedPromise,
cancel() {
canceled = true;
},
};
})();
this.loadPromise.promise
.then(this.onScriptLoaded)
.catch(this.onScriptError);
document.body.appendChild(script);
}
componentDidUpdate() {
if (!scriptLoading) {
this.updateStripeHandler();
}
}
componentWillUnmount() {
if (this.loadPromise) {
this.loadPromise.cancel();
}
if (CardVerify.stripeHandler && this.state.open) {
CardVerify.stripeHandler.close();
}
}
onScriptLoaded = () => {
if (!CardVerify.stripeHandler) {
CardVerify.stripeHandler = StripeCheckout.configure({
key: this.props.stripeKey,
});
if (this.hasPendingClick) {
this.showStripeDialog();
}
}
};
onScriptError = (...args) => {
throw new Error("Unable to load credit validation script.");
};
onClosed = () => {
this.setState({ open: false });
};
getConfig = () =>
["token", "name", "description"].reduce(
(config, key) =>
Object.assign(
{},
config,
this.props.hasOwnProperty(key) && {
[key]: this.props[key],
}
),
{
allowRememberMe: false,
closed: this.onClosed,
description: __("Confirm Identity"),
email: this.props.email,
panelLabel: "Verify",
}
);
updateStripeHandler() {
if (!CardVerify.stripeHandler) {
CardVerify.stripeHandler = StripeCheckout.configure({
key: this.props.stripeKey,
});
}
}
showStripeDialog() {
this.setState({ open: true });
CardVerify.stripeHandler.open(this.getConfig());
}
onClick = () => {
if (scriptDidError) {
try {
throw new Error(
"Tried to call onClick, but StripeCheckout failed to load"
);
} catch (x) {}
} else if (CardVerify.stripeHandler) {
this.showStripeDialog();
} else {
this.hasPendingClick = true;
}
};
render() {
return (
<Link
button="primary"
label={this.props.label}
disabled={
this.props.disabled || this.state.open || this.hasPendingClick
}
onClick={this.onClick.bind(this)}
/>
);
}
}
export default CardVerify;

View file

@ -184,6 +184,10 @@ export class FormRow extends React.PureComponent {
React.PropTypes.string,
React.PropTypes.element,
]),
errorMessage: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.object,
]),
// helper: React.PropTypes.html,
};
@ -204,6 +208,8 @@ export class FormRow extends React.PureComponent {
isError: !!props.errorMessage,
errorMessage: typeof props.errorMessage === "string"
? props.errorMessage
: props.errorMessage instanceof Error
? props.errorMessage.toString()
: "",
};
}

View file

@ -30,7 +30,7 @@ const perform = dispatch => () => {
return {
verifyAccount: () => {
closeModal();
dispatch(doNavigate("/rewards"));
dispatch(doNavigate("/auth"));
},
closeModal: closeModal,
};

View file

@ -13,6 +13,7 @@ import FileListDownloaded from "page/fileListDownloaded";
import FileListPublished from "page/fileListPublished";
import ChannelPage from "page/channel";
import SearchPage from "page/search";
import AuthPage from "page/auth";
const route = (page, routesMap) => {
const component = routesMap[page];
@ -24,22 +25,23 @@ const Router = props => {
const { currentPage, params } = props;
return route(currentPage, {
settings: <SettingsPage {...params} />,
help: <HelpPage {...params} />,
report: <ReportPage {...params} />,
downloaded: <FileListDownloaded {...params} />,
published: <FileListPublished {...params} />,
start: <StartPage {...params} />,
wallet: <WalletPage {...params} />,
send: <WalletPage {...params} />,
receive: <WalletPage {...params} />,
show: <ShowPage {...params} />,
auth: <AuthPage {...params} />,
channel: <ChannelPage {...params} />,
publish: <PublishPage {...params} />,
developer: <DeveloperPage {...params} />,
discover: <DiscoverPage {...params} />,
downloaded: <FileListDownloaded {...params} />,
help: <HelpPage {...params} />,
publish: <PublishPage {...params} />,
published: <FileListPublished {...params} />,
receive: <WalletPage {...params} />,
report: <ReportPage {...params} />,
rewards: <RewardsPage {...params} />,
search: <SearchPage {...params} />,
send: <WalletPage {...params} />,
settings: <SettingsPage {...params} />,
show: <ShowPage {...params} />,
start: <StartPage {...params} />,
wallet: <WalletPage {...params} />,
});
};

View file

@ -13,7 +13,7 @@ class UserEmailVerify extends React.PureComponent {
handleCodeChanged(event) {
this.setState({
code: event.target.value,
code: String(event.target.value).trim(),
});
}
@ -24,13 +24,15 @@ class UserEmailVerify extends React.PureComponent {
render() {
const { errorMessage, isPending } = this.props;
console.log("user email verify render");
console.log(this.props);
return (
<form
onSubmit={event => {
this.handleSubmit(event);
}}
>
<p>{__("Please enter the verification code emailed to you.")}</p>
<FormRow
type="text"
label={__("Verification Code")}

View file

@ -1,21 +1,26 @@
import React from "react";
import { connect } from "react-redux";
import { doUserEmailVerify } from "actions/user";
import { doUserIdentityVerify } from "actions/user";
import rewards from "rewards";
import { makeSelectRewardByType } from "selectors/rewards";
import {
selectEmailVerifyIsPending,
selectEmailToVerify,
selectEmailVerifyErrorMessage,
selectIdentityVerifyIsPending,
selectIdentityVerifyErrorMessage,
} from "selectors/user";
import UserVerify from "./view";
const select = state => ({
isPending: selectEmailVerifyIsPending(state),
email: selectEmailToVerify(state),
errorMessage: selectEmailVerifyErrorMessage(state),
});
const select = (state, props) => {
const selectReward = makeSelectRewardByType();
return {
isPending: selectIdentityVerifyIsPending(state),
errorMessage: selectIdentityVerifyErrorMessage(state),
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
};
};
const perform = dispatch => ({
verifyUserEmail: code => dispatch(doUserEmailVerify(code)),
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
});
export default connect(select, perform)(UserVerify);

View file

@ -1,6 +1,6 @@
import React from "react";
import Link from "component/link";
import { FormRow } from "component/form.js";
import { CreditAmount } from "component/common";
import CardVerify from "component/cardVerify";
class UserVerify extends React.PureComponent {
constructor(props) {
@ -17,51 +17,32 @@ class UserVerify extends React.PureComponent {
});
}
handleSubmit(event) {
event.preventDefault();
this.props.verifyUserEmail(this.state.code);
onToken(data) {
this.props.verifyUserIdentity(data.id);
}
render() {
const { errorMessage, isPending } = this.props;
return <p>VERIFY</p>;
const { errorMessage, isPending, reward } = this.props;
return (
<form
onSubmit={event => {
this.handleSubmit(event);
}}
>
zzzzzzzzzzzzzzzzzzzzzzzzzzzzz
<FormRow
type="text"
label={__("Verification Code")}
placeholder="a94bXXXXXXXXXXXXXX"
name="code"
value={this.state.code}
onChange={event => {
this.handleCodeChanged(event);
}}
errorMessage={errorMessage}
/>
{/* render help separately so it always shows */}
<div className="form-field__helper">
<div>
<p>
{__("Email")}{" "}
<Link href="mailto:help@lbry.io" label="help@lbry.io" />{" "}
{__("if you did not receive or are having trouble with your code.")}
<span>
Please link a credit card to confirm your identity and receive{" "}
</span>
{reward
? <CreditAmount amount={parseFloat(reward.reward_amount)} />
: <span>your reward</span>}
{"."}
</p>
</div>
<div className="form-row-submit form-row-submit--with-footer">
<Link
button="primary"
label={__("Verify")}
disabled={this.state.submitting}
onClick={event => {
this.handleSubmit(event);
}}
<p>{__("This is to prevent abuse. You will not be charged.")}</p>
{errorMessage && <p className="form-field__error">{errorMessage}</p>}
<CardVerify
label={__("Link Card and Finish")}
disabled={isPending}
token={this.onToken.bind(this)}
stripeKey="pk_test_NoL1JWL7i1ipfhVId5KfDZgo"
/>
</div>
</form>
);
}
}

View file

@ -95,6 +95,9 @@ 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";
export const USER_IDENTITY_VERIFY_STARTED = "USER_IDENTITY_VERIFY_STARTED";
export const USER_IDENTITY_VERIFY_SUCCESS = "USER_IDENTITY_VERIFY_SUCCESS";
export const USER_IDENTITY_VERIFY_FAILURE = "USER_IDENTITY_VERIFY_FAILURE";
export const USER_FETCH_STARTED = "USER_FETCH_STARTED";
export const USER_FETCH_SUCCESS = "USER_FETCH_SUCCESS";
export const USER_FETCH_FAILURE = "USER_FETCH_FAILURE";

View file

@ -6,7 +6,6 @@ import SnackBar from "component/snackBar";
import { Provider } from "react-redux";
import store from "store.js";
import SplashScreen from "component/splash.js";
import AuthOverlay from "component/authOverlay";
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
import { toQueryString } from "util/query_params";
import * as types from "constants/action_types";

30
ui/js/page/auth/index.js Normal file
View file

@ -0,0 +1,30 @@
import React from "react";
import { doNavigate } from "actions/app";
import { connect } from "react-redux";
import {
selectAuthenticationIsPending,
selectUserHasEmail,
selectEmailToVerify,
selectUserIsVerificationCandidate,
selectUser,
selectUserIsPending,
selectIdentityVerifyIsPending,
} from "selectors/user";
import AuthPage from "./view";
const select = state => ({
isPending:
selectAuthenticationIsPending(state) ||
selectUserIsPending(state) ||
selectIdentityVerifyIsPending(state),
email: selectEmailToVerify(state),
hasEmail: selectUserHasEmail(state),
user: selectUser(state),
isVerificationCandidate: selectUserIsVerificationCandidate(state),
});
const perform = dispatch => ({
onAuthComplete: () => dispatch(doNavigate("/discover")),
});
export default connect(select, perform)(AuthPage);

107
ui/js/page/auth/view.jsx Normal file
View file

@ -0,0 +1,107 @@
import React from "react";
import { BusyMessage } from "component/common";
import UserEmailNew from "component/userEmailNew";
import UserEmailVerify from "component/userEmailVerify";
import UserVerify from "component/userVerify";
export class AuthPage extends React.PureComponent {
/*
<div className="card__title-primary">
{newUserReward &&
<CreditAmount amount={newUserReward.reward_amount} />}
<h3>Welcome to LBRY</h3>
</div>
<div className="card__content">
<p>
{" "}{__(
"Claim your welcome credits to be able to publish content, pay creators, and have a say over the LBRY network."
)}
</p>
</div>
<div className="card__content"><Auth /></div>
*/
componentWillMount() {
console.log("will mount");
this.navigateIfAuthenticated(this.props);
}
componentWillReceiveProps(nextProps) {
console.log("will receive");
this.navigateIfAuthenticated(nextProps);
}
navigateIfAuthenticated(props) {
const { isPending, user } = props;
console.log(props);
if (
!isPending &&
user &&
user.has_verified_email &&
user.is_identity_verified
) {
props.onAuthComplete();
}
}
getTitle() {
const { email, isPending, isVerificationCandidate, user } = this.props;
if (isPending || (user && !user.has_verified_email && !email)) {
return __("Welcome to LBRY");
} else if (user && !user.has_verified_email) {
return __("Confirm Email");
} else if (user && !user.is_identity_verified) {
return __("Confirm Identity");
} else {
return __("Welcome to LBRY");
}
}
renderMain() {
const { email, isPending, isVerificationCandidate, user } = this.props;
if (isPending) {
return <BusyMessage message={__("Authenticating")} />;
} else if (user && !user.has_verified_email && !email) {
return <UserEmailNew />;
} else if (user && !user.has_verified_email) {
return <UserEmailVerify />;
} else if (user && !user.is_identity_verified) {
return <UserVerify />;
} else {
return <span className="empty">{__("No further steps.")}</span>;
}
}
render() {
const { email, hasEmail, isPending } = this.props;
return (
<main className="">
<section className="card card--form">
<div className="card__title-primary">
<h1>{this.getTitle()}</h1>
</div>
<div className="card__content">
{!isPending &&
!email &&
!hasEmail &&
<p>
{__("Create a verified identity and receive LBC rewards.")}
</p>}
{this.renderMain()}
</div>
<div className="card__content">
<div className="help">
{__(
"This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is collected to provide communication and prevent abuse."
)}
</div>
</div>
</section>
</main>
);
}
}
export default AuthPage;

View file

@ -10,6 +10,7 @@ import {
selectUserHasEmail,
selectUserIsVerificationCandidate,
} from "selectors/user";
import { doNavigate } from "actions/app";
import { doRewardList } from "actions/rewards";
import rewards from "rewards";
import RewardsPage from "./view";
@ -29,6 +30,7 @@ const select = (state, props) => {
const perform = dispatch => ({
fetchRewards: () => dispatch(doRewardList()),
doAuth: () => dispatch(doNavigate("/auth")),
});
export default connect(select, perform)(RewardsPage);

View file

@ -1,7 +1,7 @@
import React from "react";
import { BusyMessage, CreditAmount, Icon } from "component/common";
import SubHeader from "component/subHeader";
import Auth from "component/auth";
import Link from "component/link";
import RewardLink from "component/rewardLink";
const RewardTile = props => {
@ -46,6 +46,7 @@ class RewardsPage extends React.PureComponent {
render() {
const {
doAuth,
fetching,
isEligible,
isVerificationCandidate,
@ -60,19 +61,12 @@ class RewardsPage extends React.PureComponent {
if (!hasEmail || isVerificationCandidate) {
content = (
<div>
<div className="card__title-primary">
{newUserReward &&
<CreditAmount amount={newUserReward.reward_amount} />}
<h3>Welcome to LBRY</h3>
<div className="card__content empty">
<p>{__("Only verified accounts are eligible to earn rewards.")}</p>
</div>
<div className="card__content">
<p>
{" "}{__(
"Claim your welcome credits to be able to publish content, pay creators, and have a say over the LBRY network."
)}
</p>
<Link onClick={doAuth} button="primary" label="Become Verified" />
</div>
<div className="card__content"><Auth /></div>
</div>
);
isCard = true;

View file

@ -120,6 +120,28 @@ reducers[types.USER_EMAIL_VERIFY_FAILURE] = function(state, action) {
});
};
reducers[types.USER_IDENTITY_VERIFY_STARTED] = function(state, action) {
return Object.assign({}, state, {
identityVerifyIsPending: true,
identityVerifyErrorMessage: "",
});
};
reducers[types.USER_IDENTITY_VERIFY_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
identityVerifyIsPending: false,
identityVerifyErrorMessage: "",
user: action.data.user,
});
};
reducers[types.USER_IDENTITY_VERIFY_FAILURE] = function(state, action) {
return Object.assign({}, state, {
identityVerifyIsPending: false,
identityVerifyErrorMessage: action.data.error,
});
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);

View file

@ -19,6 +19,11 @@ export const selectEmailToVerify = createSelector(
state => state.emailToVerify
);
export const selectUserEmail = createSelector(
selectUser,
user => (user && user.email ? user.email : "fake@lbry.io")
);
export const selectUserHasEmail = createSelector(
selectUser,
selectEmailToVerify,
@ -60,6 +65,16 @@ export const selectEmailVerifyErrorMessage = createSelector(
state => state.emailVerifyErrorMessage
);
export const selectIdentityVerifyIsPending = createSelector(
_selectState,
state => state.identityVerifyIsPending
);
export const selectIdentityVerifyErrorMessage = createSelector(
_selectState,
state => state.identityVerifyErrorMessage
);
export const selectUserIsVerificationCandidate = createSelector(
selectUser,
user => user && (!user.has_verified_email || !user.is_identity_verified)

View file

@ -29,6 +29,7 @@ $max-content-width: 1000px;
$max-text-width: 660px;
$width-page-constrained: 800px;
$width-input-text: 330px;
$height-header: $spacing-vertical * 2.5;
$height-button: $spacing-vertical * 1.5;

View file

@ -156,6 +156,10 @@ $height-card-small: $spacing-vertical * 15;
height: $width-card-small * 9 / 16;
}
.card--form {
width: $width-input-text + $padding-card-horizontal * 2;
}
.card__subtitle {
color: $color-help;
font-size: 0.85em;

View file

@ -1,7 +1,6 @@
@import "../global";
$width-input-border: 2px;
$width-input-text: 330px;
.form-row-submit
{