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 lbryio from "lbryio";
|
||||||
import { setLocal } from "utils";
|
import { setLocal } from "utils";
|
||||||
import { doRewardList } from "actions/rewards";
|
import { doRewardList } from "actions/rewards";
|
||||||
import { selectEmailToVerify } from "selectors/user";
|
import { selectEmailToVerify, selectUser } from "selectors/user";
|
||||||
|
|
||||||
export function doAuthenticate() {
|
export function doAuthenticate() {
|
||||||
return function(dispatch, getState) {
|
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.string,
|
||||||
React.PropTypes.element,
|
React.PropTypes.element,
|
||||||
]),
|
]),
|
||||||
|
errorMessage: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.object,
|
||||||
|
]),
|
||||||
// helper: React.PropTypes.html,
|
// helper: React.PropTypes.html,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -204,6 +208,8 @@ export class FormRow extends React.PureComponent {
|
||||||
isError: !!props.errorMessage,
|
isError: !!props.errorMessage,
|
||||||
errorMessage: typeof props.errorMessage === "string"
|
errorMessage: typeof props.errorMessage === "string"
|
||||||
? props.errorMessage
|
? props.errorMessage
|
||||||
|
: props.errorMessage instanceof Error
|
||||||
|
? props.errorMessage.toString()
|
||||||
: "",
|
: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ const perform = dispatch => () => {
|
||||||
return {
|
return {
|
||||||
verifyAccount: () => {
|
verifyAccount: () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
dispatch(doNavigate("/rewards"));
|
dispatch(doNavigate("/auth"));
|
||||||
},
|
},
|
||||||
closeModal: closeModal,
|
closeModal: closeModal,
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ import FileListDownloaded from "page/fileListDownloaded";
|
||||||
import FileListPublished from "page/fileListPublished";
|
import FileListPublished from "page/fileListPublished";
|
||||||
import ChannelPage from "page/channel";
|
import ChannelPage from "page/channel";
|
||||||
import SearchPage from "page/search";
|
import SearchPage from "page/search";
|
||||||
|
import AuthPage from "page/auth";
|
||||||
|
|
||||||
const route = (page, routesMap) => {
|
const route = (page, routesMap) => {
|
||||||
const component = routesMap[page];
|
const component = routesMap[page];
|
||||||
|
@ -24,22 +25,23 @@ const Router = props => {
|
||||||
const { currentPage, params } = props;
|
const { currentPage, params } = props;
|
||||||
|
|
||||||
return route(currentPage, {
|
return route(currentPage, {
|
||||||
settings: <SettingsPage {...params} />,
|
auth: <AuthPage {...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} />,
|
|
||||||
channel: <ChannelPage {...params} />,
|
channel: <ChannelPage {...params} />,
|
||||||
publish: <PublishPage {...params} />,
|
|
||||||
developer: <DeveloperPage {...params} />,
|
developer: <DeveloperPage {...params} />,
|
||||||
discover: <DiscoverPage {...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} />,
|
rewards: <RewardsPage {...params} />,
|
||||||
search: <SearchPage {...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) {
|
handleCodeChanged(event) {
|
||||||
this.setState({
|
this.setState({
|
||||||
code: event.target.value,
|
code: String(event.target.value).trim(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,13 +24,15 @@ class UserEmailVerify extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending } = this.props;
|
const { errorMessage, isPending } = this.props;
|
||||||
|
console.log("user email verify render");
|
||||||
|
console.log(this.props);
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={event => {
|
onSubmit={event => {
|
||||||
this.handleSubmit(event);
|
this.handleSubmit(event);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<p>{__("Please enter the verification code emailed to you.")}</p>
|
||||||
<FormRow
|
<FormRow
|
||||||
type="text"
|
type="text"
|
||||||
label={__("Verification Code")}
|
label={__("Verification Code")}
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
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 {
|
import {
|
||||||
selectEmailVerifyIsPending,
|
selectIdentityVerifyIsPending,
|
||||||
selectEmailToVerify,
|
selectIdentityVerifyErrorMessage,
|
||||||
selectEmailVerifyErrorMessage,
|
|
||||||
} from "selectors/user";
|
} from "selectors/user";
|
||||||
import UserVerify from "./view";
|
import UserVerify from "./view";
|
||||||
|
|
||||||
const select = state => ({
|
const select = (state, props) => {
|
||||||
isPending: selectEmailVerifyIsPending(state),
|
const selectReward = makeSelectRewardByType();
|
||||||
email: selectEmailToVerify(state),
|
|
||||||
errorMessage: selectEmailVerifyErrorMessage(state),
|
return {
|
||||||
});
|
isPending: selectIdentityVerifyIsPending(state),
|
||||||
|
errorMessage: selectIdentityVerifyErrorMessage(state),
|
||||||
|
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
verifyUserEmail: code => dispatch(doUserEmailVerify(code)),
|
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(UserVerify);
|
export default connect(select, perform)(UserVerify);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "component/link";
|
import { CreditAmount } from "component/common";
|
||||||
import { FormRow } from "component/form.js";
|
import CardVerify from "component/cardVerify";
|
||||||
|
|
||||||
class UserVerify extends React.PureComponent {
|
class UserVerify extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -17,51 +17,32 @@ class UserVerify extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
onToken(data) {
|
||||||
event.preventDefault();
|
this.props.verifyUserIdentity(data.id);
|
||||||
this.props.verifyUserEmail(this.state.code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending } = this.props;
|
const { errorMessage, isPending, reward } = this.props;
|
||||||
return <p>VERIFY</p>;
|
|
||||||
return (
|
return (
|
||||||
<form
|
<div>
|
||||||
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">
|
|
||||||
<p>
|
<p>
|
||||||
{__("Email")}{" "}
|
<span>
|
||||||
<Link href="mailto:help@lbry.io" label="help@lbry.io" />{" "}
|
Please link a credit card to confirm your identity and receive{" "}
|
||||||
{__("if you did not receive or are having trouble with your code.")}
|
</span>
|
||||||
|
{reward
|
||||||
|
? <CreditAmount amount={parseFloat(reward.reward_amount)} />
|
||||||
|
: <span>your reward</span>}
|
||||||
|
{"."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<p>{__("This is to prevent abuse. You will not be charged.")}</p>
|
||||||
<div className="form-row-submit form-row-submit--with-footer">
|
{errorMessage && <p className="form-field__error">{errorMessage}</p>}
|
||||||
<Link
|
<CardVerify
|
||||||
button="primary"
|
label={__("Link Card and Finish")}
|
||||||
label={__("Verify")}
|
disabled={isPending}
|
||||||
disabled={this.state.submitting}
|
token={this.onToken.bind(this)}
|
||||||
onClick={event => {
|
stripeKey="pk_test_NoL1JWL7i1ipfhVId5KfDZgo"
|
||||||
this.handleSubmit(event);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</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_STARTED = "USER_EMAIL_VERIFY_STARTED";
|
||||||
export const USER_EMAIL_VERIFY_SUCCESS = "USER_EMAIL_VERIFY_SUCCESS";
|
export const USER_EMAIL_VERIFY_SUCCESS = "USER_EMAIL_VERIFY_SUCCESS";
|
||||||
export const USER_EMAIL_VERIFY_FAILURE = "USER_EMAIL_VERIFY_FAILURE";
|
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_STARTED = "USER_FETCH_STARTED";
|
||||||
export const USER_FETCH_SUCCESS = "USER_FETCH_SUCCESS";
|
export const USER_FETCH_SUCCESS = "USER_FETCH_SUCCESS";
|
||||||
export const USER_FETCH_FAILURE = "USER_FETCH_FAILURE";
|
export const USER_FETCH_FAILURE = "USER_FETCH_FAILURE";
|
||||||
|
|
|
@ -6,7 +6,6 @@ import SnackBar from "component/snackBar";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import store from "store.js";
|
import store from "store.js";
|
||||||
import SplashScreen from "component/splash.js";
|
import SplashScreen from "component/splash.js";
|
||||||
import AuthOverlay from "component/authOverlay";
|
|
||||||
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
|
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
|
||||||
import { toQueryString } from "util/query_params";
|
import { toQueryString } from "util/query_params";
|
||||||
import * as types from "constants/action_types";
|
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,
|
selectUserHasEmail,
|
||||||
selectUserIsVerificationCandidate,
|
selectUserIsVerificationCandidate,
|
||||||
} from "selectors/user";
|
} from "selectors/user";
|
||||||
|
import { doNavigate } from "actions/app";
|
||||||
import { doRewardList } from "actions/rewards";
|
import { doRewardList } from "actions/rewards";
|
||||||
import rewards from "rewards";
|
import rewards from "rewards";
|
||||||
import RewardsPage from "./view";
|
import RewardsPage from "./view";
|
||||||
|
@ -29,6 +30,7 @@ const select = (state, props) => {
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchRewards: () => dispatch(doRewardList()),
|
fetchRewards: () => dispatch(doRewardList()),
|
||||||
|
doAuth: () => dispatch(doNavigate("/auth")),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(RewardsPage);
|
export default connect(select, perform)(RewardsPage);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { BusyMessage, CreditAmount, Icon } from "component/common";
|
import { BusyMessage, CreditAmount, Icon } from "component/common";
|
||||||
import SubHeader from "component/subHeader";
|
import SubHeader from "component/subHeader";
|
||||||
import Auth from "component/auth";
|
import Link from "component/link";
|
||||||
import RewardLink from "component/rewardLink";
|
import RewardLink from "component/rewardLink";
|
||||||
|
|
||||||
const RewardTile = props => {
|
const RewardTile = props => {
|
||||||
|
@ -46,6 +46,7 @@ class RewardsPage extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
doAuth,
|
||||||
fetching,
|
fetching,
|
||||||
isEligible,
|
isEligible,
|
||||||
isVerificationCandidate,
|
isVerificationCandidate,
|
||||||
|
@ -60,19 +61,12 @@ class RewardsPage extends React.PureComponent {
|
||||||
if (!hasEmail || isVerificationCandidate) {
|
if (!hasEmail || isVerificationCandidate) {
|
||||||
content = (
|
content = (
|
||||||
<div>
|
<div>
|
||||||
<div className="card__title-primary">
|
<div className="card__content empty">
|
||||||
{newUserReward &&
|
<p>{__("Only verified accounts are eligible to earn rewards.")}</p>
|
||||||
<CreditAmount amount={newUserReward.reward_amount} />}
|
|
||||||
<h3>Welcome to LBRY</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<p>
|
<Link onClick={doAuth} button="primary" label="Become Verified" />
|
||||||
{" "}{__(
|
|
||||||
"Claim your welcome credits to be able to publish content, pay creators, and have a say over the LBRY network."
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content"><Auth /></div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
isCard = true;
|
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) {
|
export default function reducer(state = defaultState, action) {
|
||||||
const handler = reducers[action.type];
|
const handler = reducers[action.type];
|
||||||
if (handler) return handler(state, action);
|
if (handler) return handler(state, action);
|
||||||
|
|
|
@ -19,6 +19,11 @@ export const selectEmailToVerify = createSelector(
|
||||||
state => state.emailToVerify
|
state => state.emailToVerify
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectUserEmail = createSelector(
|
||||||
|
selectUser,
|
||||||
|
user => (user && user.email ? user.email : "fake@lbry.io")
|
||||||
|
);
|
||||||
|
|
||||||
export const selectUserHasEmail = createSelector(
|
export const selectUserHasEmail = createSelector(
|
||||||
selectUser,
|
selectUser,
|
||||||
selectEmailToVerify,
|
selectEmailToVerify,
|
||||||
|
@ -60,6 +65,16 @@ export const selectEmailVerifyErrorMessage = createSelector(
|
||||||
state => state.emailVerifyErrorMessage
|
state => state.emailVerifyErrorMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectIdentityVerifyIsPending = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.identityVerifyIsPending
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectIdentityVerifyErrorMessage = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.identityVerifyErrorMessage
|
||||||
|
);
|
||||||
|
|
||||||
export const selectUserIsVerificationCandidate = createSelector(
|
export const selectUserIsVerificationCandidate = createSelector(
|
||||||
selectUser,
|
selectUser,
|
||||||
user => user && (!user.has_verified_email || !user.is_identity_verified)
|
user => user && (!user.has_verified_email || !user.is_identity_verified)
|
||||||
|
|
|
@ -29,6 +29,7 @@ $max-content-width: 1000px;
|
||||||
$max-text-width: 660px;
|
$max-text-width: 660px;
|
||||||
|
|
||||||
$width-page-constrained: 800px;
|
$width-page-constrained: 800px;
|
||||||
|
$width-input-text: 330px;
|
||||||
|
|
||||||
$height-header: $spacing-vertical * 2.5;
|
$height-header: $spacing-vertical * 2.5;
|
||||||
$height-button: $spacing-vertical * 1.5;
|
$height-button: $spacing-vertical * 1.5;
|
||||||
|
|
|
@ -156,6 +156,10 @@ $height-card-small: $spacing-vertical * 15;
|
||||||
height: $width-card-small * 9 / 16;
|
height: $width-card-small * 9 / 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card--form {
|
||||||
|
width: $width-input-text + $padding-card-horizontal * 2;
|
||||||
|
}
|
||||||
|
|
||||||
.card__subtitle {
|
.card__subtitle {
|
||||||
color: $color-help;
|
color: $color-help;
|
||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
@import "../global";
|
@import "../global";
|
||||||
|
|
||||||
$width-input-border: 2px;
|
$width-input-border: 2px;
|
||||||
$width-input-text: 330px;
|
|
||||||
|
|
||||||
.form-row-submit
|
.form-row-submit
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue