end-to-end barebones
This commit is contained in:
parent
16abedbf3a
commit
ab9150fb27
23 changed files with 684 additions and 229 deletions
|
@ -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() },
|
||||
// });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
|
@ -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;
|
12
ui/js/component/cardVerify/index.js
Normal file
12
ui/js/component/cardVerify/index.js
Normal 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);
|
377
ui/js/component/cardVerify/view.jsx
Normal file
377
ui/js/component/cardVerify/view.jsx
Normal 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;
|
|
@ -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()
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ const perform = dispatch => () => {
|
|||
return {
|
||||
verifyAccount: () => {
|
||||
closeModal();
|
||||
dispatch(doNavigate("/rewards"));
|
||||
dispatch(doNavigate("/auth"));
|
||||
},
|
||||
closeModal: closeModal,
|
||||
};
|
||||
|
|
|
@ -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} />,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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")}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
30
ui/js/page/auth/index.js
Normal 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
107
ui/js/page/auth/view.jsx
Normal 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;
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
@import "../global";
|
||||
|
||||
$width-input-border: 2px;
|
||||
$width-input-text: 330px;
|
||||
|
||||
.form-row-submit
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue