Add identity verification to app #366
58 changed files with 1088 additions and 500 deletions
|
@ -4,7 +4,6 @@ import {
|
|||
selectUpdateUrl,
|
||||
selectUpgradeDownloadPath,
|
||||
selectUpgradeDownloadItem,
|
||||
selectUpgradeFilename,
|
||||
selectPageTitle,
|
||||
selectCurrentPage,
|
||||
selectCurrentParams,
|
||||
|
@ -35,6 +34,20 @@ export function doNavigate(path, params = {}) {
|
|||
};
|
||||
}
|
||||
|
||||
export function doAuthNavigate(pathAfterAuth = null, params = {}) {
|
||||
return function(dispatch, getState) {
|
||||
if (pathAfterAuth) {
|
||||
dispatch({
|
||||
type: types.CHANGE_AFTER_AUTH_PATH,
|
||||
data: {
|
||||
path: `${pathAfterAuth}?${toQueryString(params)}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
dispatch(doNavigate("/auth"));
|
||||
};
|
||||
}
|
||||
|
||||
export function doChangePath(path, options = {}) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
|
@ -237,8 +250,6 @@ export function doCheckDaemonVersion() {
|
|||
export function doAlertError(errorList) {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState();
|
||||
console.log("do alert error");
|
||||
console.log(errorList);
|
||||
dispatch({
|
||||
type: types.OPEN_MODAL,
|
||||
data: {
|
||||
|
|
|
@ -15,8 +15,8 @@ import { selectBadgeNumber } from "selectors/app";
|
|||
import { selectTotalDownloadProgress } from "selectors/file_info";
|
||||
import setBadge from "util/setBadge";
|
||||
import setProgressBar from "util/setProgressBar";
|
||||
import { doFileList } from "actions/file_info";
|
||||
import batchActions from "util/batchActions";
|
||||
import * as modals from "constants/modal_types";
|
||||
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
|
@ -294,7 +294,7 @@ export function doPurchaseUri(uri, purchaseModalName) {
|
|||
}
|
||||
|
||||
if (cost > balance) {
|
||||
dispatch(doOpenModal("notEnoughCredits"));
|
||||
dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS));
|
||||
} else {
|
||||
dispatch(doOpenModal(purchaseModalName));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as types from "constants/action_types";
|
||||
import * as modals from "constants/modal_types";
|
||||
import lbryio from "lbryio";
|
||||
import rewards from "rewards";
|
||||
import { selectRewardsByType } from "selectors/rewards";
|
||||
|
@ -58,6 +59,12 @@ export function doClaimReward(reward, saveError = false) {
|
|||
reward,
|
||||
},
|
||||
});
|
||||
if (reward.reward_type == rewards.TYPE_NEW_USER) {
|
||||
dispatch({
|
||||
type: types.OPEN_MODAL,
|
||||
data: { modal: modals.FIRST_REWARD },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const failure = error => {
|
||||
|
@ -99,9 +106,7 @@ export function doClaimEligiblePurchaseRewards() {
|
|||
if (unclaimedType) {
|
||||
dispatch(doClaimRewardType(unclaimedType));
|
||||
}
|
||||
if (types[rewards.TYPE_FEATURED_DOWNLOAD] === false) {
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FEATURED_DOWNLOAD));
|
||||
}
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FEATURED_DOWNLOAD));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
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 { doRewardList, doClaimRewardType } from "actions/rewards";
|
||||
import { selectEmailToVerify, selectUser } from "selectors/user";
|
||||
import rewards from "rewards";
|
||||
|
||||
export function doAuthenticate() {
|
||||
return function(dispatch, getState) {
|
||||
|
@ -137,6 +138,37 @@ 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 },
|
||||
});
|
||||
dispatch(doClaimRewardType(rewards.TYPE_NEW_USER));
|
||||
} else {
|
||||
throw new Error(
|
||||
"Your identity is still not verified. This should not happen."
|
||||
); //shouldn't happen
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({
|
||||
type: types.USER_IDENTITY_VERIFY_FAILURE,
|
||||
data: { error: error.toString() },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doFetchAccessToken() {
|
||||
return function(dispatch, getState) {
|
||||
const success = token =>
|
||||
|
|
|
@ -1,23 +1,41 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { selectCurrentModal } from "selectors/app";
|
||||
import {
|
||||
doCheckUpgradeAvailable,
|
||||
doOpenModal,
|
||||
doAlertError,
|
||||
doRecordScroll,
|
||||
doCheckDaemonVersion,
|
||||
} from "actions/app";
|
||||
import { doUpdateBalance } from "actions/wallet";
|
||||
import { selectWelcomeModalAcknowledged } from "selectors/app";
|
||||
import rewards from "rewards";
|
||||
import {
|
||||
selectFetchingRewards,
|
||||
makeSelectHasClaimedReward,
|
||||
} from "selectors/rewards";
|
||||
import { selectUser } from "selectors/user";
|
||||
import App from "./view";
|
||||
import * as modals from "constants/modal_types";
|
||||
|
||||
const select = state => ({
|
||||
modal: selectCurrentModal(state),
|
||||
});
|
||||
const select = (state, props) => {
|
||||
const selectHasClaimed = makeSelectHasClaimedReward();
|
||||
|
||||
return {
|
||||
modal: selectCurrentModal(state),
|
||||
isWelcomeAcknowledged: selectWelcomeModalAcknowledged(state),
|
||||
isFetchingRewards: selectFetchingRewards(state),
|
||||
isWelcomeRewardClaimed: selectHasClaimed(state, {
|
||||
reward_type: rewards.TYPE_NEW_USER,
|
||||
}),
|
||||
user: selectUser(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
alertError: errorList => dispatch(doAlertError(errorList)),
|
||||
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
||||
openWelcomeModal: () => dispatch(doOpenModal(modals.WELCOME)),
|
||||
updateBalance: balance => dispatch(doUpdateBalance(balance)),
|
||||
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
|
||||
});
|
||||
|
|
|
@ -3,30 +3,58 @@ import Router from "component/router";
|
|||
import Header from "component/header";
|
||||
import ModalError from "component/modalError";
|
||||
import ModalDownloading from "component/modalDownloading";
|
||||
import ModalInsufficientCredits from "component/modalInsufficientCredits";
|
||||
import ModalUpgrade from "component/modalUpgrade";
|
||||
import ModalWelcome from "component/modalWelcome";
|
||||
import ModalFirstReward from "component/modalFirstReward";
|
||||
import lbry from "lbry";
|
||||
import { Line } from "rc-progress";
|
||||
import * as modals from "constants/modal_types";
|
||||
|
||||
class App extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
const { alertError, checkUpgradeAvailable, updateBalance } = this.props;
|
||||
|
||||
document.addEventListener("unhandledError", event => {
|
||||
this.props.alertError(event.detail);
|
||||
alertError(event.detail);
|
||||
});
|
||||
|
||||
if (!this.props.upgradeSkipped) {
|
||||
this.props.checkUpgradeAvailable();
|
||||
checkUpgradeAvailable();
|
||||
}
|
||||
|
||||
lbry.balanceSubscribe(balance => {
|
||||
this.props.updateBalance(balance);
|
||||
updateBalance(balance);
|
||||
});
|
||||
|
||||
this.showWelcome(this.props);
|
||||
|
||||
this.scrollListener = () => this.props.recordScroll(window.scrollY);
|
||||
|
||||
window.addEventListener("scroll", this.scrollListener);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.showWelcome(nextProps);
|
||||
}
|
||||
|
||||
showWelcome(props) {
|
||||
const {
|
||||
isFetchingRewards,
|
||||
isWelcomeAcknowledged,
|
||||
isWelcomeRewardClaimed,
|
||||
openWelcomeModal,
|
||||
user,
|
||||
} = props;
|
||||
|
||||
if (
|
||||
!isWelcomeAcknowledged &&
|
||||
user &&
|
||||
(isFetchingRewards === false && isWelcomeRewardClaimed === false)
|
||||
) {
|
||||
openWelcomeModal();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("scroll", this.scrollListener);
|
||||
}
|
||||
|
@ -40,10 +68,12 @@ class App extends React.PureComponent {
|
|||
<div id="main-content">
|
||||
<Router />
|
||||
</div>
|
||||
{modal == "upgrade" && <ModalUpgrade />}
|
||||
{modal == "downloading" && <ModalDownloading />}
|
||||
{modal == "error" && <ModalError />}
|
||||
{modal == "welcome" && <ModalWelcome />}
|
||||
{modal == modals.UPGRADE && <ModalUpgrade />}
|
||||
{modal == modals.DOWNLOADING && <ModalDownloading />}
|
||||
{modal == modals.ERROR && <ModalError />}
|
||||
{modal == modals.INSUFFICIENT_CREDITS && <ModalInsufficientCredits />}
|
||||
{modal == modals.WELCOME && <ModalWelcome />}
|
||||
{modal == modals.FIRST_REWARD && <ModalFirstReward />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
selectAuthenticationIsPending,
|
||||
selectEmailToVerify,
|
||||
selectUserIsVerificationCandidate,
|
||||
} from "selectors/user";
|
||||
import Auth from "./view";
|
||||
|
||||
const select = state => ({
|
||||
isPending: selectAuthenticationIsPending(state),
|
||||
email: selectEmailToVerify(state),
|
||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
||||
});
|
||||
|
||||
export default connect(select, null)(Auth);
|
|
@ -1,22 +0,0 @@
|
|||
import React from "react";
|
||||
import { BusyMessage } from "component/common";
|
||||
import UserEmailNew from "component/userEmailNew";
|
||||
import UserEmailVerify from "component/userEmailVerify";
|
||||
|
||||
export class Auth extends React.PureComponent {
|
||||
render() {
|
||||
const { isPending, email, isVerificationCandidate } = this.props;
|
||||
|
||||
if (isPending) {
|
||||
return <BusyMessage message={__("Authenticating")} />;
|
||||
} else if (!email) {
|
||||
return <UserEmailNew />;
|
||||
} else if (isVerificationCandidate) {
|
||||
return <UserEmailVerify />;
|
||||
} 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 className="help form-input-width">
|
||||
<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;
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { formatCredits } from "utils";
|
||||
import lbry from "../lbry.js";
|
||||
|
||||
//component/icon.js
|
||||
|
@ -78,7 +79,7 @@ export class CreditAmount extends React.PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const formattedAmount = lbry.formatCredits(
|
||||
const formattedAmount = formatCredits(
|
||||
this.props.amount,
|
||||
this.props.precision
|
||||
);
|
||||
|
@ -140,7 +141,7 @@ export class Address extends React.PureComponent {
|
|||
}}
|
||||
style={addressStyle}
|
||||
readOnly="readonly"
|
||||
value={this.props.address}
|
||||
value={this.props.address || ""}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -181,13 +181,6 @@ class FileActions extends React.PureComponent {
|
|||
</strong>{" "}
|
||||
{__("credits")}.
|
||||
</Modal>
|
||||
<Modal
|
||||
isOpen={modal == "notEnoughCredits"}
|
||||
contentLabel={__("Not enough credits")}
|
||||
onConfirmed={closeModal}
|
||||
>
|
||||
{__("You don't have enough LBRY credits to pay for this stream.")}
|
||||
</Modal>
|
||||
<Modal
|
||||
isOpen={modal == "timedOut"}
|
||||
contentLabel={__("Download failed")}
|
||||
|
|
|
@ -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,7 +208,9 @@ export class FormRow extends React.PureComponent {
|
|||
isError: !!props.errorMessage,
|
||||
errorMessage: typeof props.errorMessage === "string"
|
||||
? props.errorMessage
|
||||
: "",
|
||||
: props.errorMessage instanceof Error
|
||||
? props.errorMessage.toString()
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from "react";
|
||||
import lbry from "lbry";
|
||||
import { formatCredits } from "utils";
|
||||
import { connect } from "react-redux";
|
||||
import { selectBalance } from "selectors/wallet";
|
||||
import { doNavigate, doHistoryBack } from "actions/app";
|
||||
import Header from "./view";
|
||||
|
||||
const select = state => ({
|
||||
balance: lbry.formatCredits(selectBalance(state), 1),
|
||||
balance: formatCredits(selectBalance(state), 1),
|
||||
publish: __("Publish"),
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ const Link = props => {
|
|||
icon,
|
||||
badge,
|
||||
button,
|
||||
hidden,
|
||||
disabled,
|
||||
children,
|
||||
} = props;
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import React from "react";
|
||||
import ReactModal from "react-modal";
|
||||
|
||||
export class ModalPage extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<ReactModal
|
||||
onCloseRequested={this.props.onAborted || this.props.onConfirmed}
|
||||
{...this.props}
|
||||
className={(this.props.className || "") + " modal-page"}
|
||||
overlayClassName="modal-overlay"
|
||||
>
|
||||
<div className="modal-page__content">
|
||||
{this.props.children}
|
||||
</div>
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalPage;
|
20
ui/js/component/modalFirstReward/index.js
Normal file
20
ui/js/component/modalFirstReward/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from "react";
|
||||
import rewards from "rewards";
|
||||
import { connect } from "react-redux";
|
||||
import { doCloseModal } from "actions/app";
|
||||
import { makeSelectRewardByType } from "selectors/rewards";
|
||||
import ModalFirstReward from "./view";
|
||||
|
||||
const select = (state, props) => {
|
||||
const selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(ModalFirstReward);
|
50
ui/js/component/modalFirstReward/view.jsx
Normal file
50
ui/js/component/modalFirstReward/view.jsx
Normal file
|
@ -0,0 +1,50 @@
|
|||
import React from "react";
|
||||
import { Modal } from "component/modal";
|
||||
import { CreditAmount } from "component/common";
|
||||
|
||||
class ModalFirstReward extends React.PureComponent {
|
||||
render() {
|
||||
const { closeModal, reward } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
type="alert"
|
||||
overlayClassName="modal-overlay modal-overlay--clear"
|
||||
isOpen={true}
|
||||
contentLabel={__("Welcome to LBRY")}
|
||||
onConfirmed={closeModal}
|
||||
>
|
||||
<section>
|
||||
<h3 className="modal__header">{__("About Your Reward")}</h3>
|
||||
<p>
|
||||
{__("You earned a reward of")}
|
||||
{" "}<CreditAmount amount={reward.reward_amount} label={false} />
|
||||
{" "}{__("LBRY credits, or")} <em>{__("LBC")}</em>.
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"This reward will show in your Wallet momentarily, shown in the top right, probably while you are reading this message."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"LBC is used to compensate creators, to publish, and to have say in how the network works."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"No need to understand it all just yet! Try watching or downloading something next."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"Finally, pleaseh know that LBRY is an early beta and that it earns the name."
|
||||
)}
|
||||
</p>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalFirstReward;
|
16
ui/js/component/modalInsufficientCredits/index.js
Normal file
16
ui/js/component/modalInsufficientCredits/index.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doCloseModal, doNavigate } from "actions/app";
|
||||
import ModalInsufficientCredits from "./view";
|
||||
|
||||
const select = state => ({});
|
||||
|
||||
const perform = dispatch => ({
|
||||
addFunds: () => {
|
||||
dispatch(doNavigate("/rewards"));
|
||||
dispatch(doCloseModal());
|
||||
},
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(ModalInsufficientCredits);
|
24
ui/js/component/modalInsufficientCredits/view.jsx
Normal file
24
ui/js/component/modalInsufficientCredits/view.jsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
import { Modal } from "component/modal";
|
||||
|
||||
class ModalInsufficientCredits extends React.PureComponent {
|
||||
render() {
|
||||
const { addFunds, closeModal } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={true}
|
||||
type="confirm"
|
||||
contentLabel={__("Not enough credits")}
|
||||
confirmButtonLabel={__("Get Credits")}
|
||||
abortButtonLabel={__("Cancel")}
|
||||
onAborted={closeModal}
|
||||
onConfirmed={addFunds}
|
||||
>
|
||||
{__("More LBRY credits are required to purchase this.")}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalInsufficientCredits;
|
|
@ -1,6 +1,5 @@
|
|||
import React from "react";
|
||||
import { Modal } from "component/modal";
|
||||
import { downloadUpgrade, skipUpgrade } from "actions/app";
|
||||
|
||||
class ModalUpgrade extends React.PureComponent {
|
||||
render() {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from "react";
|
||||
import rewards from "rewards";
|
||||
import { connect } from "react-redux";
|
||||
import { doCloseModal } from "actions/app";
|
||||
import { doCloseModal, doAuthNavigate } from "actions/app";
|
||||
import { doSetClientSetting } from "actions/settings";
|
||||
import { selectUserIsRewardApproved } from "selectors/user";
|
||||
import {
|
||||
makeSelectHasClaimedReward,
|
||||
makeSelectClaimRewardError,
|
||||
makeSelectRewardByType,
|
||||
} from "selectors/rewards";
|
||||
import ModalWelcome from "./view";
|
||||
|
@ -15,14 +15,24 @@ const select = (state, props) => {
|
|||
selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
hasClaimed: selectHasClaimed(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
isRewardApproved: selectUserIsRewardApproved(state),
|
||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
});
|
||||
const perform = dispatch => () => {
|
||||
const closeModal = () => {
|
||||
dispatch(doSetClientSetting("welcome_acknowledged", true));
|
||||
dispatch(doCloseModal());
|
||||
};
|
||||
|
||||
return {
|
||||
verifyAccount: () => {
|
||||
closeModal();
|
||||
dispatch(doAuthNavigate("/rewards"));
|
||||
},
|
||||
closeModal: closeModal,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(select, perform)(ModalWelcome);
|
||||
|
|
|
@ -6,75 +6,45 @@ import RewardLink from "component/rewardLink";
|
|||
|
||||
class ModalWelcome extends React.PureComponent {
|
||||
render() {
|
||||
const { closeModal, hasClaimed, isRewardApproved, reward } = this.props;
|
||||
const { closeModal, isRewardApproved, reward, verifyAccount } = this.props;
|
||||
|
||||
return !hasClaimed
|
||||
? <Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
|
||||
<section>
|
||||
<h3 className="modal__header">{__("Welcome to LBRY.")}</h3>
|
||||
<p>
|
||||
{__(
|
||||
"Using LBRY is like dating a centaur. Totally normal up top, and"
|
||||
)}
|
||||
{" "}<em>{__("way different")}</em> {__("underneath.")}
|
||||
</p>
|
||||
<p>{__("Up top, LBRY is similar to popular media sites.")}</p>
|
||||
<p>
|
||||
{__(
|
||||
"Below, LBRY is controlled by users -- you -- via blockchain and decentralization."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__("Thank you for making content freedom possible!")}
|
||||
{" "}{isRewardApproved ? __("Here's a nickel, kid.") : ""}
|
||||
</p>
|
||||
<div className="text-center">
|
||||
{isRewardApproved
|
||||
? <RewardLink reward_type="new_user" button="primary" />
|
||||
: <Link
|
||||
button="primary"
|
||||
onClick={closeModal}
|
||||
label={__("Continue")}
|
||||
/>}
|
||||
</div>
|
||||
</section>
|
||||
</Modal>
|
||||
: <Modal
|
||||
type="alert"
|
||||
overlayClassName="modal-overlay modal-overlay--clear"
|
||||
isOpen={true}
|
||||
contentLabel={__("Welcome to LBRY")}
|
||||
onConfirmed={closeModal}
|
||||
>
|
||||
<section>
|
||||
<h3 className="modal__header">{__("About Your Reward")}</h3>
|
||||
<p>
|
||||
{__("You earned a reward of")}
|
||||
{" "}<CreditAmount amount={reward.reward_amount} label={false} />
|
||||
{" "}{__("LBRY credits, or")} <em>{__("LBC")}</em>.
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"This reward will show in your Wallet momentarily, probably while you are reading this message."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"LBC is used to compensate creators, to publish, and to have say in how the network works."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"No need to understand it all just yet! Try watching or downloading something next."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
"Finally, know that LBRY is an early beta and that it earns the name."
|
||||
)}
|
||||
</p>
|
||||
</section>
|
||||
</Modal>;
|
||||
return (
|
||||
<Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
|
||||
<section>
|
||||
<h3 className="modal__header">{__("Welcome to LBRY.")}</h3>
|
||||
<p>
|
||||
{__(
|
||||
"Using LBRY is like dating a centaur. Totally normal up top, and"
|
||||
)}
|
||||
{" "}<em>{__("way different")}</em> {__("underneath.")}
|
||||
</p>
|
||||
<p>{__("Up top, LBRY is similar to popular media sites.")}</p>
|
||||
<p>
|
||||
{__(
|
||||
"Below, LBRY is controlled by users -- you -- via blockchain and decentralization."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__("Please have")} {" "}
|
||||
{reward &&
|
||||
<CreditAmount amount={parseFloat(reward.reward_amount)} />}
|
||||
{!reward && <span className="credit-amount">{__("??")}</span>}
|
||||
{" "} {__("as a thank you for building content freedom.")}
|
||||
</p>
|
||||
<div className="text-center">
|
||||
{isRewardApproved &&
|
||||
<RewardLink reward_type="new_user" button="primary" />}
|
||||
{!isRewardApproved &&
|
||||
<Link
|
||||
button="primary"
|
||||
onClick={verifyAccount}
|
||||
label={__("Get Welcome Credits")}
|
||||
/>}
|
||||
<Link button="alt" onClick={closeModal} label={__("Skip")} />
|
||||
</div>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
makeSelectHasClaimedReward,
|
||||
makeSelectClaimRewardError,
|
||||
makeSelectRewardByType,
|
||||
makeSelectIsRewardClaimPending,
|
||||
|
@ -11,13 +10,11 @@ import { doClaimReward, doClaimRewardClearError } from "actions/rewards";
|
|||
import RewardLink from "./view";
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectHasClaimedReward = makeSelectHasClaimedReward();
|
||||
const selectIsPending = makeSelectIsRewardClaimPending();
|
||||
const selectReward = makeSelectRewardByType();
|
||||
const selectError = makeSelectClaimRewardError();
|
||||
|
||||
const select = (state, props) => ({
|
||||
isClaimed: selectHasClaimedReward(state, props),
|
||||
errorMessage: selectError(state, props),
|
||||
isPending: selectIsPending(state, props),
|
||||
reward: selectReward(state, props),
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from "react";
|
||||
import { Icon } from "component/common";
|
||||
import Modal from "component/modal";
|
||||
import Link from "component/link";
|
||||
|
||||
|
@ -10,22 +9,19 @@ const RewardLink = props => {
|
|||
claimReward,
|
||||
clearError,
|
||||
errorMessage,
|
||||
isClaimed,
|
||||
isPending,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="reward-link">
|
||||
{isClaimed
|
||||
? <span><Icon icon="icon-check" /> Reward claimed.</span>
|
||||
: <Link
|
||||
button={button ? button : "alt"}
|
||||
disabled={isPending}
|
||||
label={isPending ? __("Claiming...") : __("Claim Reward")}
|
||||
onClick={() => {
|
||||
claimReward(reward);
|
||||
}}
|
||||
/>}
|
||||
<Link
|
||||
button={button ? button : "alt"}
|
||||
disabled={isPending}
|
||||
label={isPending ? __("Claiming...") : __("Claim Reward")}
|
||||
onClick={() => {
|
||||
claimReward(reward);
|
||||
}}
|
||||
/>
|
||||
{errorMessage
|
||||
? <Modal
|
||||
isOpen={true}
|
||||
|
|
|
@ -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} />,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ class UserEmailNew extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<form
|
||||
className="form-input-width"
|
||||
onSubmit={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
|
|
|
@ -13,7 +13,7 @@ class UserEmailVerify extends React.PureComponent {
|
|||
|
||||
handleCodeChanged(event) {
|
||||
this.setState({
|
||||
code: event.target.value,
|
||||
code: String(event.target.value).trim(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -24,14 +24,13 @@ class UserEmailVerify extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { errorMessage, isPending } = this.props;
|
||||
|
||||
return (
|
||||
<form
|
||||
className="form-input-width"
|
||||
onSubmit={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
>
|
||||
<p>{__("Please enter the verification code emailed to you.")}</p>
|
||||
<FormRow
|
||||
type="text"
|
||||
label={__("Verification Code")}
|
||||
|
|
26
ui/js/component/userVerify/index.js
Normal file
26
ui/js/component/userVerify/index.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doUserIdentityVerify } from "actions/user";
|
||||
import rewards from "rewards";
|
||||
import { makeSelectRewardByType } from "selectors/rewards";
|
||||
import {
|
||||
selectIdentityVerifyIsPending,
|
||||
selectIdentityVerifyErrorMessage,
|
||||
} from "selectors/user";
|
||||
import UserVerify from "./view";
|
||||
|
||||
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 => ({
|
||||
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(UserVerify);
|
48
ui/js/component/userVerify/view.jsx
Normal file
48
ui/js/component/userVerify/view.jsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import React from "react";
|
||||
import { CreditAmount } from "component/common";
|
||||
import CardVerify from "component/cardVerify";
|
||||
|
||||
class UserVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
code: "",
|
||||
};
|
||||
}
|
||||
|
||||
handleCodeChanged(event) {
|
||||
this.setState({
|
||||
code: event.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
onToken(data) {
|
||||
this.props.verifyUserIdentity(data.id);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { errorMessage, isPending, reward } = this.props;
|
||||
return (
|
||||
<div>
|
||||
{(!reward || !reward.transaction_id) &&
|
||||
<p>
|
||||
Please link a credit card to confirm your identity and receive{" "}
|
||||
{reward
|
||||
? <CreditAmount amount={parseFloat(reward.reward_amount)} />
|
||||
: <span>your reward</span>}
|
||||
</p>}
|
||||
<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_live_e8M4dRNnCCbmpZzduEUZBgJO"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserVerify;
|
|
@ -78,13 +78,6 @@ class VideoPlayButton extends React.PureComponent {
|
|||
icon={icon}
|
||||
onClick={this.onWatchClick.bind(this)}
|
||||
/>
|
||||
<Modal
|
||||
contentLabel={__("Not enough credits")}
|
||||
isOpen={modal == "notEnoughCredits"}
|
||||
onConfirmed={closeModal}
|
||||
>
|
||||
{__("You don't have enough LBRY credits to pay for this stream.")}
|
||||
</Modal>
|
||||
<Modal
|
||||
type="confirm"
|
||||
isOpen={modal == "affirmPurchaseAndPlay"}
|
||||
|
|
|
@ -5,7 +5,7 @@ export const HISTORY_BACK = "HISTORY_BACK";
|
|||
export const SHOW_SNACKBAR = "SHOW_SNACKBAR";
|
||||
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
|
||||
export const WINDOW_FOCUSED = "WINDOW_FOCUSED";
|
||||
|
||||
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
|
||||
export const DAEMON_READY = "DAEMON_READY";
|
||||
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH";
|
||||
export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
|
||||
|
@ -97,6 +97,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";
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
export const WELCOME = "welcome";
|
||||
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
|
||||
export const INCOMPATIBLE_DAEMON = "incompatibleDaemon";
|
||||
export const DOWNLOADING = "downloading";
|
||||
export const ERROR = "error";
|
||||
export const INSUFFICIENT_CREDITS = "insufficient_credits";
|
||||
export const UPGRADE = "upgrade";
|
||||
export const WELCOME = "welcome";
|
||||
export const FIRST_REWARD = "first_reward";
|
||||
|
|
|
@ -288,11 +288,6 @@ lbry.setClientSetting = function(setting, value) {
|
|||
return localStorage.setItem("setting_" + setting, JSON.stringify(value));
|
||||
};
|
||||
|
||||
//utilities
|
||||
lbry.formatCredits = function(amount, precision) {
|
||||
return amount.toFixed(precision || 1).replace(/\.?0+$/, "");
|
||||
};
|
||||
|
||||
lbry.formatName = function(name) {
|
||||
// Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes)
|
||||
name = name.replace("/s+/g", "-");
|
||||
|
|
|
@ -136,8 +136,9 @@ lbryio.authenticate = function() {
|
|||
resolve({
|
||||
id: 1,
|
||||
language: "en",
|
||||
has_email: true,
|
||||
primary_email: "disabled@lbry.io",
|
||||
has_verified_email: true,
|
||||
is_identity_verified: true,
|
||||
is_reward_approved: false,
|
||||
is_reward_eligible: false,
|
||||
});
|
||||
|
|
|
@ -6,10 +6,8 @@ import SnackBar from "component/snackBar";
|
|||
import { Provider } from "react-redux";
|
||||
import store from "store.js";
|
||||
import SplashScreen from "component/splash";
|
||||
import AuthOverlay from "component/authOverlay";
|
||||
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
|
||||
import { toQueryString } from "util/query_params";
|
||||
import { selectBadgeNumber } from "selectors/app";
|
||||
import * as types from "constants/action_types";
|
||||
|
||||
const env = ENV;
|
||||
|
@ -97,19 +95,6 @@ const updateProgress = () => {
|
|||
|
||||
const initialState = app.store.getState();
|
||||
|
||||
// import whyDidYouUpdate from "why-did-you-update";
|
||||
// if (env === "development") {
|
||||
// /*
|
||||
// https://github.com/garbles/why-did-you-update
|
||||
// "A function that monkey patches React and notifies you in the console when
|
||||
// potentially unnecessary re-renders occur."
|
||||
//
|
||||
// Just checks if props change between updates. Can be fixed by manually
|
||||
// adding a check in shouldComponentUpdate or using React.PureComponent
|
||||
// */
|
||||
// whyDidYouUpdate(React);
|
||||
// }
|
||||
|
||||
var init = function() {
|
||||
function onDaemonReady() {
|
||||
window.sessionStorage.setItem("loaded", "y"); //once we've made it here once per session, we don't need to show splash again
|
||||
|
@ -117,7 +102,7 @@ var init = function() {
|
|||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<div><AuthOverlay /><App /><SnackBar /></div>
|
||||
<div><App /><SnackBar /></div>
|
||||
</Provider>,
|
||||
canvas
|
||||
);
|
||||
|
|
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 { selectPathAfterAuth } from "selectors/app";
|
||||
import {
|
||||
selectAuthenticationIsPending,
|
||||
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),
|
||||
pathAfterAuth: selectPathAfterAuth(state),
|
||||
user: selectUser(state),
|
||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(AuthPage);
|
89
ui/js/page/auth/view.jsx
Normal file
89
ui/js/page/auth/view.jsx
Normal file
|
@ -0,0 +1,89 @@
|
|||
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 {
|
||||
componentWillMount() {
|
||||
this.navigateIfAuthenticated(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.navigateIfAuthenticated(nextProps);
|
||||
}
|
||||
|
||||
navigateIfAuthenticated(props) {
|
||||
const { isPending, user } = props;
|
||||
if (
|
||||
!isPending &&
|
||||
user &&
|
||||
user.has_verified_email &&
|
||||
user.is_identity_verified
|
||||
) {
|
||||
props.navigate(props.pathAfterAuth);
|
||||
}
|
||||
}
|
||||
|
||||
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, user, 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 &&
|
||||
!user.has_verified_email &&
|
||||
<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 optional and only collected to provide communication and prevent abuse. You may use LBRY without providing this information."
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthPage;
|
|
@ -2,10 +2,11 @@ import React from "react";
|
|||
import { doNavigate } from "actions/app";
|
||||
import { connect } from "react-redux";
|
||||
import { doFetchAccessToken } from "actions/user";
|
||||
import { selectAccessToken } from "selectors/user";
|
||||
import { selectAccessToken, selectUser } from "selectors/user";
|
||||
import HelpPage from "./view";
|
||||
|
||||
const select = state => ({
|
||||
user: selectUser(state),
|
||||
accessToken: selectAccessToken(state),
|
||||
});
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class HelpPage extends React.PureComponent {
|
|||
render() {
|
||||
let ver, osName, platform, newVerLink;
|
||||
|
||||
const { navigate } = this.props;
|
||||
const { navigate, user } = this.props;
|
||||
|
||||
if (this.state.versionInfo) {
|
||||
ver = this.state.versionInfo;
|
||||
|
@ -146,16 +146,24 @@ class HelpPage extends React.PureComponent {
|
|||
? <table className="table-standard">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{__("daemon (lbrynet)")}</th>
|
||||
<th>{__("App")}</th>
|
||||
<td>{this.state.uiVersion}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{__("Daemon (lbrynet)")}</th>
|
||||
<td>{ver.lbrynet_version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{__("wallet (lbryum)")}</th>
|
||||
<th>{__("Wallet (lbryum)")}</th>
|
||||
<td>{ver.lbryum_version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{__("interface")}</th>
|
||||
<td>{this.state.uiVersion}</td>
|
||||
<th>{__("Connected Email")}</th>
|
||||
<td>
|
||||
{user && user.primary_email
|
||||
? user.primary_email
|
||||
: <span className="empty">{__("none")}</span>}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{__("Platform")}</th>
|
||||
|
|
|
@ -1,25 +1,33 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doNavigate } from "actions/app";
|
||||
import { selectFetchingRewards, selectRewards } from "selectors/rewards";
|
||||
import {
|
||||
selectUserIsRewardEligible,
|
||||
selectUserHasEmail,
|
||||
selectUserIsVerificationCandidate,
|
||||
} from "selectors/user";
|
||||
makeSelectRewardByType,
|
||||
selectFetchingRewards,
|
||||
selectRewards,
|
||||
} from "selectors/rewards";
|
||||
import { selectUser } from "selectors/user";
|
||||
import { doAuthNavigate, doNavigate } from "actions/app";
|
||||
import { doRewardList } from "actions/rewards";
|
||||
import rewards from "rewards";
|
||||
import RewardsPage from "./view";
|
||||
|
||||
const select = state => ({
|
||||
fetching: selectFetchingRewards(state),
|
||||
rewards: selectRewards(state),
|
||||
hasEmail: selectUserHasEmail(state),
|
||||
isEligible: selectUserIsRewardEligible(state),
|
||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
||||
});
|
||||
const select = (state, props) => {
|
||||
const selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
fetching: selectFetchingRewards(state),
|
||||
rewards: selectRewards(state),
|
||||
newUserReward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
user: selectUser(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
fetchRewards: () => dispatch(doRewardList()),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
doAuth: () => {
|
||||
dispatch(doAuthNavigate("/rewards"));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(select, perform)(RewardsPage);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React from "react";
|
||||
import lbryio from "lbryio";
|
||||
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";
|
||||
|
||||
|
@ -41,60 +39,91 @@ class RewardsPage extends React.PureComponent {
|
|||
fetchRewards(props) {
|
||||
const { fetching, rewards, fetchRewards } = props;
|
||||
|
||||
if (!fetching && Object.keys(rewards).length < 1) fetchRewards();
|
||||
if (!fetching && (!rewards || !rewards.length)) {
|
||||
fetchRewards();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
fetching,
|
||||
isEligible,
|
||||
isVerificationCandidate,
|
||||
hasEmail,
|
||||
rewards,
|
||||
} = this.props;
|
||||
const { doAuth, fetching, navigate, rewards, user } = this.props;
|
||||
|
||||
let content,
|
||||
isCard = false;
|
||||
let content, cardHeader;
|
||||
|
||||
if (!hasEmail || isVerificationCandidate) {
|
||||
if (fetching) {
|
||||
content = (
|
||||
<div className="card__content">
|
||||
<BusyMessage message={__("Fetching rewards")} />
|
||||
</div>
|
||||
);
|
||||
} else if (rewards.length > 0) {
|
||||
content = (
|
||||
<div>
|
||||
<p>
|
||||
{__(
|
||||
"Additional information is required to be eligible for the rewards program."
|
||||
)}
|
||||
</p>
|
||||
<Auth />
|
||||
{rewards.map(reward =>
|
||||
<RewardTile key={reward.reward_type} reward={reward} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
isCard = true;
|
||||
} else if (!isEligible) {
|
||||
isCard = true;
|
||||
content = (
|
||||
<div className="empty">
|
||||
<p>{__("You are not eligible to claim rewards.")}</p>
|
||||
</div>
|
||||
);
|
||||
} else if (fetching) {
|
||||
content = <BusyMessage message={__("Fetching rewards")} />;
|
||||
} else if (rewards.length > 0) {
|
||||
content = rewards.map(reward =>
|
||||
<RewardTile key={reward.reward_type} reward={reward} />
|
||||
);
|
||||
} else {
|
||||
content = <div className="empty">{__("Failed to load rewards.")}</div>;
|
||||
content = (
|
||||
<div className="card__content empty">
|
||||
{__("Failed to load rewards.")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
user &&
|
||||
(!user.primary_email ||
|
||||
!user.has_verified_email ||
|
||||
!user.is_identity_verified)
|
||||
) {
|
||||
cardHeader = (
|
||||
<div>
|
||||
<div className="card__content empty">
|
||||
<p>{__("Only verified accounts are eligible to earn rewards.")}</p>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<Link onClick={doAuth} button="primary" label="Become Verified" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (user && !user.is_reward_approved) {
|
||||
cardHeader = (
|
||||
<div className="card__content">
|
||||
<p>
|
||||
{__(
|
||||
"This account must undergo review before you can participate in the rewards program."
|
||||
)}
|
||||
{" "}
|
||||
{__("This can take anywhere from several minutes to several days.")}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{__(
|
||||
"We apologize for this inconvenience, but have added this additional step to prevent fraud."
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__("You will receive an email when this process is complete.") +
|
||||
" " +
|
||||
__("Please enjoy free content in the meantime!")}
|
||||
</p>
|
||||
<p>
|
||||
<Link
|
||||
onClick={() => navigate("/discover")}
|
||||
button="primary"
|
||||
label="Return Home"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
{isCard
|
||||
? <section className="card">
|
||||
<div className="card__content">
|
||||
{content}
|
||||
</div>
|
||||
</section>
|
||||
: content}
|
||||
{cardHeader && <section className="card">{cardHeader}</section>}
|
||||
{content}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ const reducers = {};
|
|||
const defaultState = {
|
||||
isLoaded: false,
|
||||
currentPath: currentPath(),
|
||||
pathAfterAuth: "/discover",
|
||||
platform: process.platform,
|
||||
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
|
||||
daemonVersionMatched: null,
|
||||
|
@ -49,6 +50,12 @@ reducers[types.CHANGE_PATH] = function(state, action) {
|
|||
});
|
||||
};
|
||||
|
||||
reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
pathAfterAuth: action.data.path,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.UPGRADE_CANCELLED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
downloadProgress: null,
|
||||
|
|
|
@ -73,7 +73,7 @@ reducers[types.USER_EMAIL_NEW_STARTED] = function(state, action) {
|
|||
|
||||
reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
|
||||
let user = Object.assign({}, state.user);
|
||||
user.has_email = true;
|
||||
user.primary_email = action.data.email;
|
||||
return Object.assign({}, state, {
|
||||
emailToVerify: action.data.email,
|
||||
emailNewIsPending: false,
|
||||
|
@ -105,7 +105,7 @@ reducers[types.USER_EMAIL_VERIFY_STARTED] = function(state, action) {
|
|||
|
||||
reducers[types.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
|
||||
let user = Object.assign({}, state.user);
|
||||
user.has_email = true;
|
||||
user.primary_email = action.data.email;
|
||||
return Object.assign({}, state, {
|
||||
emailToVerify: "",
|
||||
emailVerifyIsPending: false,
|
||||
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.FETCH_ACCESS_TOKEN_SUCCESS] = function(state, action) {
|
||||
const { token } = action.data;
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ function rewardMessage(type, amount) {
|
|||
"You earned %s LBC for making your first publication.",
|
||||
amount
|
||||
),
|
||||
featured_download: __(
|
||||
"You earned %s LBC for watching a featured download.",
|
||||
amount
|
||||
),
|
||||
}[type];
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,17 @@ export const selectSnackBarSnacks = createSelector(
|
|||
snackBar => snackBar.snacks || []
|
||||
);
|
||||
|
||||
export const selectWelcomeModalAcknowledged = createSelector(
|
||||
_selectState,
|
||||
state => lbry.getClientSetting("welcome_acknowledged")
|
||||
);
|
||||
|
||||
export const selectBadgeNumber = createSelector(
|
||||
_selectState,
|
||||
state => state.badgeNumber
|
||||
);
|
||||
|
||||
export const selectPathAfterAuth = createSelector(
|
||||
_selectState,
|
||||
state => state.pathAfterAuth
|
||||
);
|
||||
|
|
|
@ -12,25 +12,16 @@ export const selectUserIsPending = createSelector(
|
|||
state => state.userIsPending
|
||||
);
|
||||
|
||||
export const selectUser = createSelector(
|
||||
_selectState,
|
||||
state => state.user || {}
|
||||
);
|
||||
export const selectUser = createSelector(_selectState, state => state.user);
|
||||
|
||||
export const selectEmailToVerify = createSelector(
|
||||
_selectState,
|
||||
state => state.emailToVerify
|
||||
);
|
||||
|
||||
export const selectUserHasEmail = createSelector(
|
||||
export const selectUserEmail = createSelector(
|
||||
selectUser,
|
||||
selectEmailToVerify,
|
||||
(user, email) => (user && user.has_email) || !!email
|
||||
);
|
||||
|
||||
export const selectUserIsRewardEligible = createSelector(
|
||||
selectUser,
|
||||
user => user && user.is_reward_eligible
|
||||
user => (user ? user.primary_email : null)
|
||||
);
|
||||
|
||||
export const selectUserIsRewardApproved = createSelector(
|
||||
|
@ -63,18 +54,19 @@ export const selectEmailVerifyErrorMessage = createSelector(
|
|||
state => state.emailVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectUserIsVerificationCandidate = createSelector(
|
||||
selectUser,
|
||||
user => user && !user.has_verified_email
|
||||
export const selectIdentityVerifyIsPending = createSelector(
|
||||
_selectState,
|
||||
state => state.identityVerifyIsPending
|
||||
);
|
||||
|
||||
export const selectUserIsAuthRequested = createSelector(
|
||||
selectEmailNewDeclined,
|
||||
selectAuthenticationIsPending,
|
||||
selectUserIsVerificationCandidate,
|
||||
selectUserHasEmail,
|
||||
(isEmailDeclined, isPending, isVerificationCandidate, hasEmail) =>
|
||||
!isEmailDeclined && (isPending || !hasEmail || isVerificationCandidate)
|
||||
export const selectIdentityVerifyErrorMessage = createSelector(
|
||||
_selectState,
|
||||
state => state.identityVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectUserIsVerificationCandidate = createSelector(
|
||||
selectUser,
|
||||
user => user && (!user.has_verified_email || !user.is_identity_verified)
|
||||
);
|
||||
|
||||
export const selectAccessToken = createSelector(
|
||||
|
|
|
@ -31,3 +31,7 @@ export function getSession(key, fallback = undefined) {
|
|||
export function setSession(key, value) {
|
||||
sessionStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
export function formatCredits(amount, precision) {
|
||||
return amount.toFixed(precision || 1).replace(/\.?0+$/, "");
|
||||
}
|
||||
|
|
|
@ -72,8 +72,7 @@
|
|||
"webpack": "^2.6.1",
|
||||
"webpack-dev-server": "^2.4.4",
|
||||
"webpack-notifier": "^1.5.0",
|
||||
"webpack-target-electron-renderer": "^0.4.0",
|
||||
"why-did-you-update": "0.0.8"
|
||||
"webpack-target-electron-renderer": "^0.4.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"gitDir": "../",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
@import "component/_channel-indicator.scss";
|
||||
@import "component/_notice.scss";
|
||||
@import "component/_modal.scss";
|
||||
@import "component/_modal-page.scss";
|
||||
@import "component/_snack-bar.scss";
|
||||
@import "component/_video.scss";
|
||||
@import "page/_developer.scss";
|
||||
|
|
|
@ -164,6 +164,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,11 +1,6 @@
|
|||
@import "../global";
|
||||
|
||||
$width-input-border: 2px;
|
||||
$width-input-text: 330px;
|
||||
|
||||
.form-input-width {
|
||||
width: $width-input-text
|
||||
}
|
||||
|
||||
.form-row-submit
|
||||
{
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
@import "../global";
|
||||
|
||||
.modal-page {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
border: 1px solid rgb(204, 204, 204);
|
||||
background: rgb(255, 255, 255);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.modal-page--full {
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
.modal-page__content {
|
||||
max-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.modal-page {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
border: 1px solid rgb(204, 204, 204);
|
||||
background: rgb(255, 255, 255);
|
||||
overflow: auto;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
padding: 36px;
|
||||
|
||||
top: 25px;
|
||||
left: 25px;
|
||||
right: 25px;
|
||||
bottom: 25px;
|
||||
}
|
||||
*/
|
||||
|
||||
.modal-page__content {
|
||||
h1, h2 {
|
||||
margin-bottom: $spacing-vertical / 2;
|
||||
}
|
||||
h3, h4 {
|
||||
margin-bottom: $spacing-vertical / 4;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue