Add identity verification to app #366
58 changed files with 1088 additions and 500 deletions
|
@ -4,7 +4,6 @@ import {
|
||||||
selectUpdateUrl,
|
selectUpdateUrl,
|
||||||
selectUpgradeDownloadPath,
|
selectUpgradeDownloadPath,
|
||||||
selectUpgradeDownloadItem,
|
selectUpgradeDownloadItem,
|
||||||
selectUpgradeFilename,
|
|
||||||
selectPageTitle,
|
selectPageTitle,
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentParams,
|
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 = {}) {
|
export function doChangePath(path, options = {}) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -237,8 +250,6 @@ export function doCheckDaemonVersion() {
|
||||||
export function doAlertError(errorList) {
|
export function doAlertError(errorList) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
console.log("do alert error");
|
|
||||||
console.log(errorList);
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.OPEN_MODAL,
|
type: types.OPEN_MODAL,
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -15,8 +15,8 @@ import { selectBadgeNumber } from "selectors/app";
|
||||||
import { selectTotalDownloadProgress } from "selectors/file_info";
|
import { selectTotalDownloadProgress } from "selectors/file_info";
|
||||||
import setBadge from "util/setBadge";
|
import setBadge from "util/setBadge";
|
||||||
import setProgressBar from "util/setProgressBar";
|
import setProgressBar from "util/setProgressBar";
|
||||||
import { doFileList } from "actions/file_info";
|
|
||||||
import batchActions from "util/batchActions";
|
import batchActions from "util/batchActions";
|
||||||
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
const { ipcRenderer } = require("electron");
|
const { ipcRenderer } = require("electron");
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ export function doPurchaseUri(uri, purchaseModalName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cost > balance) {
|
if (cost > balance) {
|
||||||
dispatch(doOpenModal("notEnoughCredits"));
|
dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS));
|
||||||
} else {
|
} else {
|
||||||
dispatch(doOpenModal(purchaseModalName));
|
dispatch(doOpenModal(purchaseModalName));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
|
import * as modals from "constants/modal_types";
|
||||||
import lbryio from "lbryio";
|
import lbryio from "lbryio";
|
||||||
import rewards from "rewards";
|
import rewards from "rewards";
|
||||||
import { selectRewardsByType } from "selectors/rewards";
|
import { selectRewardsByType } from "selectors/rewards";
|
||||||
|
@ -58,6 +59,12 @@ export function doClaimReward(reward, saveError = false) {
|
||||||
reward,
|
reward,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (reward.reward_type == rewards.TYPE_NEW_USER) {
|
||||||
|
dispatch({
|
||||||
|
type: types.OPEN_MODAL,
|
||||||
|
data: { modal: modals.FIRST_REWARD },
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const failure = error => {
|
const failure = error => {
|
||||||
|
@ -99,9 +106,7 @@ export function doClaimEligiblePurchaseRewards() {
|
||||||
if (unclaimedType) {
|
if (unclaimedType) {
|
||||||
dispatch(doClaimRewardType(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 * 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, doClaimRewardType } from "actions/rewards";
|
||||||
import { selectEmailToVerify } from "selectors/user";
|
import { selectEmailToVerify, selectUser } from "selectors/user";
|
||||||
|
import rewards from "rewards";
|
||||||
|
|
||||||
export function doAuthenticate() {
|
export function doAuthenticate() {
|
||||||
return function(dispatch, getState) {
|
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() {
|
export function doFetchAccessToken() {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const success = token =>
|
const success = token =>
|
||||||
|
|
|
@ -1,23 +1,41 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
import { selectCurrentModal } from "selectors/app";
|
import { selectCurrentModal } from "selectors/app";
|
||||||
import {
|
import {
|
||||||
doCheckUpgradeAvailable,
|
doCheckUpgradeAvailable,
|
||||||
|
doOpenModal,
|
||||||
doAlertError,
|
doAlertError,
|
||||||
doRecordScroll,
|
doRecordScroll,
|
||||||
doCheckDaemonVersion,
|
|
||||||
} from "actions/app";
|
} from "actions/app";
|
||||||
import { doUpdateBalance } from "actions/wallet";
|
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 App from "./view";
|
||||||
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
const select = state => ({
|
const select = (state, props) => {
|
||||||
modal: selectCurrentModal(state),
|
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 => ({
|
const perform = dispatch => ({
|
||||||
alertError: errorList => dispatch(doAlertError(errorList)),
|
alertError: errorList => dispatch(doAlertError(errorList)),
|
||||||
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
||||||
|
openWelcomeModal: () => dispatch(doOpenModal(modals.WELCOME)),
|
||||||
updateBalance: balance => dispatch(doUpdateBalance(balance)),
|
updateBalance: balance => dispatch(doUpdateBalance(balance)),
|
||||||
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
|
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,30 +3,58 @@ import Router from "component/router";
|
||||||
import Header from "component/header";
|
import Header from "component/header";
|
||||||
import ModalError from "component/modalError";
|
import ModalError from "component/modalError";
|
||||||
import ModalDownloading from "component/modalDownloading";
|
import ModalDownloading from "component/modalDownloading";
|
||||||
|
import ModalInsufficientCredits from "component/modalInsufficientCredits";
|
||||||
import ModalUpgrade from "component/modalUpgrade";
|
import ModalUpgrade from "component/modalUpgrade";
|
||||||
import ModalWelcome from "component/modalWelcome";
|
import ModalWelcome from "component/modalWelcome";
|
||||||
|
import ModalFirstReward from "component/modalFirstReward";
|
||||||
import lbry from "lbry";
|
import lbry from "lbry";
|
||||||
import { Line } from "rc-progress";
|
import * as modals from "constants/modal_types";
|
||||||
|
|
||||||
class App extends React.PureComponent {
|
class App extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
const { alertError, checkUpgradeAvailable, updateBalance } = this.props;
|
||||||
|
|
||||||
document.addEventListener("unhandledError", event => {
|
document.addEventListener("unhandledError", event => {
|
||||||
this.props.alertError(event.detail);
|
alertError(event.detail);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.props.upgradeSkipped) {
|
if (!this.props.upgradeSkipped) {
|
||||||
this.props.checkUpgradeAvailable();
|
checkUpgradeAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.balanceSubscribe(balance => {
|
lbry.balanceSubscribe(balance => {
|
||||||
this.props.updateBalance(balance);
|
updateBalance(balance);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.showWelcome(this.props);
|
||||||
|
|
||||||
this.scrollListener = () => this.props.recordScroll(window.scrollY);
|
this.scrollListener = () => this.props.recordScroll(window.scrollY);
|
||||||
|
|
||||||
window.addEventListener("scroll", this.scrollListener);
|
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() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener("scroll", this.scrollListener);
|
window.removeEventListener("scroll", this.scrollListener);
|
||||||
}
|
}
|
||||||
|
@ -40,10 +68,12 @@ class App extends React.PureComponent {
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<Router />
|
<Router />
|
||||||
</div>
|
</div>
|
||||||
{modal == "upgrade" && <ModalUpgrade />}
|
{modal == modals.UPGRADE && <ModalUpgrade />}
|
||||||
{modal == "downloading" && <ModalDownloading />}
|
{modal == modals.DOWNLOADING && <ModalDownloading />}
|
||||||
{modal == "error" && <ModalError />}
|
{modal == modals.ERROR && <ModalError />}
|
||||||
{modal == "welcome" && <ModalWelcome />}
|
{modal == modals.INSUFFICIENT_CREDITS && <ModalInsufficientCredits />}
|
||||||
|
{modal == modals.WELCOME && <ModalWelcome />}
|
||||||
|
{modal == modals.FIRST_REWARD && <ModalFirstReward />}
|
||||||
</div>
|
</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 React from "react";
|
||||||
|
import { formatCredits } from "utils";
|
||||||
import lbry from "../lbry.js";
|
import lbry from "../lbry.js";
|
||||||
|
|
||||||
//component/icon.js
|
//component/icon.js
|
||||||
|
@ -78,7 +79,7 @@ export class CreditAmount extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const formattedAmount = lbry.formatCredits(
|
const formattedAmount = formatCredits(
|
||||||
this.props.amount,
|
this.props.amount,
|
||||||
this.props.precision
|
this.props.precision
|
||||||
);
|
);
|
||||||
|
@ -140,7 +141,7 @@ export class Address extends React.PureComponent {
|
||||||
}}
|
}}
|
||||||
style={addressStyle}
|
style={addressStyle}
|
||||||
readOnly="readonly"
|
readOnly="readonly"
|
||||||
value={this.props.address}
|
value={this.props.address || ""}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,13 +181,6 @@ class FileActions extends React.PureComponent {
|
||||||
</strong>{" "}
|
</strong>{" "}
|
||||||
{__("credits")}.
|
{__("credits")}.
|
||||||
</Modal>
|
</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
|
<Modal
|
||||||
isOpen={modal == "timedOut"}
|
isOpen={modal == "timedOut"}
|
||||||
contentLabel={__("Download failed")}
|
contentLabel={__("Download failed")}
|
||||||
|
|
|
@ -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,7 +208,9 @@ 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()
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import lbry from "lbry";
|
import { formatCredits } from "utils";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { selectBalance } from "selectors/wallet";
|
import { selectBalance } from "selectors/wallet";
|
||||||
import { doNavigate, doHistoryBack } from "actions/app";
|
import { doNavigate, doHistoryBack } from "actions/app";
|
||||||
import Header from "./view";
|
import Header from "./view";
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
balance: lbry.formatCredits(selectBalance(state), 1),
|
balance: formatCredits(selectBalance(state), 1),
|
||||||
publish: __("Publish"),
|
publish: __("Publish"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ const Link = props => {
|
||||||
icon,
|
icon,
|
||||||
badge,
|
badge,
|
||||||
button,
|
button,
|
||||||
hidden,
|
|
||||||
disabled,
|
disabled,
|
||||||
children,
|
children,
|
||||||
} = props;
|
} = 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 React from "react";
|
||||||
import { Modal } from "component/modal";
|
import { Modal } from "component/modal";
|
||||||
import { downloadUpgrade, skipUpgrade } from "actions/app";
|
|
||||||
|
|
||||||
class ModalUpgrade extends React.PureComponent {
|
class ModalUpgrade extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import rewards from "rewards";
|
import rewards from "rewards";
|
||||||
import { connect } from "react-redux";
|
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 { selectUserIsRewardApproved } from "selectors/user";
|
||||||
import {
|
import {
|
||||||
makeSelectHasClaimedReward,
|
makeSelectHasClaimedReward,
|
||||||
makeSelectClaimRewardError,
|
|
||||||
makeSelectRewardByType,
|
makeSelectRewardByType,
|
||||||
} from "selectors/rewards";
|
} from "selectors/rewards";
|
||||||
import ModalWelcome from "./view";
|
import ModalWelcome from "./view";
|
||||||
|
@ -15,14 +15,24 @@ const select = (state, props) => {
|
||||||
selectReward = makeSelectRewardByType();
|
selectReward = makeSelectRewardByType();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasClaimed: selectHasClaimed(state, { reward_type: rewards.TYPE_NEW_USER }),
|
|
||||||
isRewardApproved: selectUserIsRewardApproved(state),
|
isRewardApproved: selectUserIsRewardApproved(state),
|
||||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => () => {
|
||||||
closeModal: () => dispatch(doCloseModal()),
|
const closeModal = () => {
|
||||||
});
|
dispatch(doSetClientSetting("welcome_acknowledged", true));
|
||||||
|
dispatch(doCloseModal());
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
verifyAccount: () => {
|
||||||
|
closeModal();
|
||||||
|
dispatch(doAuthNavigate("/rewards"));
|
||||||
|
},
|
||||||
|
closeModal: closeModal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(select, perform)(ModalWelcome);
|
export default connect(select, perform)(ModalWelcome);
|
||||||
|
|
|
@ -6,75 +6,45 @@ import RewardLink from "component/rewardLink";
|
||||||
|
|
||||||
class ModalWelcome extends React.PureComponent {
|
class ModalWelcome extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { closeModal, hasClaimed, isRewardApproved, reward } = this.props;
|
const { closeModal, isRewardApproved, reward, verifyAccount } = this.props;
|
||||||
|
|
||||||
return !hasClaimed
|
return (
|
||||||
? <Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
|
<Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
|
||||||
<section>
|
<section>
|
||||||
<h3 className="modal__header">{__("Welcome to LBRY.")}</h3>
|
<h3 className="modal__header">{__("Welcome to LBRY.")}</h3>
|
||||||
<p>
|
<p>
|
||||||
{__(
|
{__(
|
||||||
"Using LBRY is like dating a centaur. Totally normal up top, and"
|
"Using LBRY is like dating a centaur. Totally normal up top, and"
|
||||||
)}
|
)}
|
||||||
{" "}<em>{__("way different")}</em> {__("underneath.")}
|
{" "}<em>{__("way different")}</em> {__("underneath.")}
|
||||||
</p>
|
</p>
|
||||||
<p>{__("Up top, LBRY is similar to popular media sites.")}</p>
|
<p>{__("Up top, LBRY is similar to popular media sites.")}</p>
|
||||||
<p>
|
<p>
|
||||||
{__(
|
{__(
|
||||||
"Below, LBRY is controlled by users -- you -- via blockchain and decentralization."
|
"Below, LBRY is controlled by users -- you -- via blockchain and decentralization."
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{__("Thank you for making content freedom possible!")}
|
{__("Please have")} {" "}
|
||||||
{" "}{isRewardApproved ? __("Here's a nickel, kid.") : ""}
|
{reward &&
|
||||||
</p>
|
<CreditAmount amount={parseFloat(reward.reward_amount)} />}
|
||||||
<div className="text-center">
|
{!reward && <span className="credit-amount">{__("??")}</span>}
|
||||||
{isRewardApproved
|
{" "} {__("as a thank you for building content freedom.")}
|
||||||
? <RewardLink reward_type="new_user" button="primary" />
|
</p>
|
||||||
: <Link
|
<div className="text-center">
|
||||||
button="primary"
|
{isRewardApproved &&
|
||||||
onClick={closeModal}
|
<RewardLink reward_type="new_user" button="primary" />}
|
||||||
label={__("Continue")}
|
{!isRewardApproved &&
|
||||||
/>}
|
<Link
|
||||||
</div>
|
button="primary"
|
||||||
</section>
|
onClick={verifyAccount}
|
||||||
</Modal>
|
label={__("Get Welcome Credits")}
|
||||||
: <Modal
|
/>}
|
||||||
type="alert"
|
<Link button="alt" onClick={closeModal} label={__("Skip")} />
|
||||||
overlayClassName="modal-overlay modal-overlay--clear"
|
</div>
|
||||||
isOpen={true}
|
</section>
|
||||||
contentLabel={__("Welcome to LBRY")}
|
</Modal>
|
||||||
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>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import {
|
import {
|
||||||
makeSelectHasClaimedReward,
|
|
||||||
makeSelectClaimRewardError,
|
makeSelectClaimRewardError,
|
||||||
makeSelectRewardByType,
|
makeSelectRewardByType,
|
||||||
makeSelectIsRewardClaimPending,
|
makeSelectIsRewardClaimPending,
|
||||||
|
@ -11,13 +10,11 @@ import { doClaimReward, doClaimRewardClearError } from "actions/rewards";
|
||||||
import RewardLink from "./view";
|
import RewardLink from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const makeSelect = () => {
|
||||||
const selectHasClaimedReward = makeSelectHasClaimedReward();
|
|
||||||
const selectIsPending = makeSelectIsRewardClaimPending();
|
const selectIsPending = makeSelectIsRewardClaimPending();
|
||||||
const selectReward = makeSelectRewardByType();
|
const selectReward = makeSelectRewardByType();
|
||||||
const selectError = makeSelectClaimRewardError();
|
const selectError = makeSelectClaimRewardError();
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
isClaimed: selectHasClaimedReward(state, props),
|
|
||||||
errorMessage: selectError(state, props),
|
errorMessage: selectError(state, props),
|
||||||
isPending: selectIsPending(state, props),
|
isPending: selectIsPending(state, props),
|
||||||
reward: selectReward(state, props),
|
reward: selectReward(state, props),
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Icon } from "component/common";
|
|
||||||
import Modal from "component/modal";
|
import Modal from "component/modal";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
|
|
||||||
|
@ -10,22 +9,19 @@ const RewardLink = props => {
|
||||||
claimReward,
|
claimReward,
|
||||||
clearError,
|
clearError,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
isClaimed,
|
|
||||||
isPending,
|
isPending,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="reward-link">
|
<div className="reward-link">
|
||||||
{isClaimed
|
<Link
|
||||||
? <span><Icon icon="icon-check" /> Reward claimed.</span>
|
button={button ? button : "alt"}
|
||||||
: <Link
|
disabled={isPending}
|
||||||
button={button ? button : "alt"}
|
label={isPending ? __("Claiming...") : __("Claim Reward")}
|
||||||
disabled={isPending}
|
onClick={() => {
|
||||||
label={isPending ? __("Claiming...") : __("Claim Reward")}
|
claimReward(reward);
|
||||||
onClick={() => {
|
}}
|
||||||
claimReward(reward);
|
/>
|
||||||
}}
|
|
||||||
/>}
|
|
||||||
{errorMessage
|
{errorMessage
|
||||||
? <Modal
|
? <Modal
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
|
|
|
@ -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} />,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ class UserEmailNew extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="form-input-width"
|
|
||||||
onSubmit={event => {
|
onSubmit={event => {
|
||||||
this.handleSubmit(event);
|
this.handleSubmit(event);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -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,14 +24,13 @@ class UserEmailVerify extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending } = this.props;
|
const { errorMessage, isPending } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="form-input-width"
|
|
||||||
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")}
|
||||||
|
|
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}
|
icon={icon}
|
||||||
onClick={this.onWatchClick.bind(this)}
|
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
|
<Modal
|
||||||
type="confirm"
|
type="confirm"
|
||||||
isOpen={modal == "affirmPurchaseAndPlay"}
|
isOpen={modal == "affirmPurchaseAndPlay"}
|
||||||
|
|
|
@ -5,7 +5,7 @@ export const HISTORY_BACK = "HISTORY_BACK";
|
||||||
export const SHOW_SNACKBAR = "SHOW_SNACKBAR";
|
export const SHOW_SNACKBAR = "SHOW_SNACKBAR";
|
||||||
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
|
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
|
||||||
export const WINDOW_FOCUSED = "WINDOW_FOCUSED";
|
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_READY = "DAEMON_READY";
|
||||||
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH";
|
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH";
|
||||||
export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
|
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_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";
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
export const WELCOME = "welcome";
|
|
||||||
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
|
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
|
||||||
export const INCOMPATIBLE_DAEMON = "incompatibleDaemon";
|
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));
|
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) {
|
lbry.formatName = function(name) {
|
||||||
// Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes)
|
// Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes)
|
||||||
name = name.replace("/s+/g", "-");
|
name = name.replace("/s+/g", "-");
|
||||||
|
|
|
@ -136,8 +136,9 @@ lbryio.authenticate = function() {
|
||||||
resolve({
|
resolve({
|
||||||
id: 1,
|
id: 1,
|
||||||
language: "en",
|
language: "en",
|
||||||
has_email: true,
|
primary_email: "disabled@lbry.io",
|
||||||
has_verified_email: true,
|
has_verified_email: true,
|
||||||
|
is_identity_verified: true,
|
||||||
is_reward_approved: false,
|
is_reward_approved: false,
|
||||||
is_reward_eligible: false,
|
is_reward_eligible: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,10 +6,8 @@ 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";
|
import SplashScreen from "component/splash";
|
||||||
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 { selectBadgeNumber } from "selectors/app";
|
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
|
|
||||||
const env = ENV;
|
const env = ENV;
|
||||||
|
@ -97,19 +95,6 @@ const updateProgress = () => {
|
||||||
|
|
||||||
const initialState = app.store.getState();
|
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() {
|
var init = function() {
|
||||||
function onDaemonReady() {
|
function onDaemonReady() {
|
||||||
window.sessionStorage.setItem("loaded", "y"); //once we've made it here once per session, we don't need to show splash again
|
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(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<div><AuthOverlay /><App /><SnackBar /></div>
|
<div><App /><SnackBar /></div>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
canvas
|
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 { doNavigate } from "actions/app";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doFetchAccessToken } from "actions/user";
|
import { doFetchAccessToken } from "actions/user";
|
||||||
import { selectAccessToken } from "selectors/user";
|
import { selectAccessToken, selectUser } from "selectors/user";
|
||||||
import HelpPage from "./view";
|
import HelpPage from "./view";
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
user: selectUser(state),
|
||||||
accessToken: selectAccessToken(state),
|
accessToken: selectAccessToken(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ class HelpPage extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
let ver, osName, platform, newVerLink;
|
let ver, osName, platform, newVerLink;
|
||||||
|
|
||||||
const { navigate } = this.props;
|
const { navigate, user } = this.props;
|
||||||
|
|
||||||
if (this.state.versionInfo) {
|
if (this.state.versionInfo) {
|
||||||
ver = this.state.versionInfo;
|
ver = this.state.versionInfo;
|
||||||
|
@ -146,16 +146,24 @@ class HelpPage extends React.PureComponent {
|
||||||
? <table className="table-standard">
|
? <table className="table-standard">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{__("daemon (lbrynet)")}</th>
|
<th>{__("App")}</th>
|
||||||
|
<td>{this.state.uiVersion}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{__("Daemon (lbrynet)")}</th>
|
||||||
<td>{ver.lbrynet_version}</td>
|
<td>{ver.lbrynet_version}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{__("wallet (lbryum)")}</th>
|
<th>{__("Wallet (lbryum)")}</th>
|
||||||
<td>{ver.lbryum_version}</td>
|
<td>{ver.lbryum_version}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{__("interface")}</th>
|
<th>{__("Connected Email")}</th>
|
||||||
<td>{this.state.uiVersion}</td>
|
<td>
|
||||||
|
{user && user.primary_email
|
||||||
|
? user.primary_email
|
||||||
|
: <span className="empty">{__("none")}</span>}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{__("Platform")}</th>
|
<th>{__("Platform")}</th>
|
||||||
|
|
|
@ -1,25 +1,33 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { doNavigate } from "actions/app";
|
|
||||||
import { selectFetchingRewards, selectRewards } from "selectors/rewards";
|
|
||||||
import {
|
import {
|
||||||
selectUserIsRewardEligible,
|
makeSelectRewardByType,
|
||||||
selectUserHasEmail,
|
selectFetchingRewards,
|
||||||
selectUserIsVerificationCandidate,
|
selectRewards,
|
||||||
} from "selectors/user";
|
} from "selectors/rewards";
|
||||||
|
import { selectUser } from "selectors/user";
|
||||||
|
import { doAuthNavigate, doNavigate } from "actions/app";
|
||||||
import { doRewardList } from "actions/rewards";
|
import { doRewardList } from "actions/rewards";
|
||||||
|
import rewards from "rewards";
|
||||||
import RewardsPage from "./view";
|
import RewardsPage from "./view";
|
||||||
|
|
||||||
const select = state => ({
|
const select = (state, props) => {
|
||||||
fetching: selectFetchingRewards(state),
|
const selectReward = makeSelectRewardByType();
|
||||||
rewards: selectRewards(state),
|
|
||||||
hasEmail: selectUserHasEmail(state),
|
return {
|
||||||
isEligible: selectUserIsRewardEligible(state),
|
fetching: selectFetchingRewards(state),
|
||||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
rewards: selectRewards(state),
|
||||||
});
|
newUserReward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||||
|
user: selectUser(state),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchRewards: () => dispatch(doRewardList()),
|
fetchRewards: () => dispatch(doRewardList()),
|
||||||
|
navigate: path => dispatch(doNavigate(path)),
|
||||||
|
doAuth: () => {
|
||||||
|
dispatch(doAuthNavigate("/rewards"));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(RewardsPage);
|
export default connect(select, perform)(RewardsPage);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import lbryio from "lbryio";
|
|
||||||
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 Link from "component/link";
|
||||||
import RewardLink from "component/rewardLink";
|
import RewardLink from "component/rewardLink";
|
||||||
|
|
||||||
|
@ -41,60 +39,91 @@ class RewardsPage extends React.PureComponent {
|
||||||
fetchRewards(props) {
|
fetchRewards(props) {
|
||||||
const { fetching, rewards, fetchRewards } = props;
|
const { fetching, rewards, fetchRewards } = props;
|
||||||
|
|
||||||
if (!fetching && Object.keys(rewards).length < 1) fetchRewards();
|
if (!fetching && (!rewards || !rewards.length)) {
|
||||||
|
fetchRewards();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { doAuth, fetching, navigate, rewards, user } = this.props;
|
||||||
fetching,
|
|
||||||
isEligible,
|
|
||||||
isVerificationCandidate,
|
|
||||||
hasEmail,
|
|
||||||
rewards,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let content,
|
let content, cardHeader;
|
||||||
isCard = false;
|
|
||||||
|
|
||||||
if (!hasEmail || isVerificationCandidate) {
|
if (fetching) {
|
||||||
|
content = (
|
||||||
|
<div className="card__content">
|
||||||
|
<BusyMessage message={__("Fetching rewards")} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (rewards.length > 0) {
|
||||||
content = (
|
content = (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
{rewards.map(reward =>
|
||||||
{__(
|
<RewardTile key={reward.reward_type} reward={reward} />
|
||||||
"Additional information is required to be eligible for the rewards program."
|
)}
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<Auth />
|
|
||||||
</div>
|
</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 {
|
} 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 (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<SubHeader />
|
<SubHeader />
|
||||||
{isCard
|
{cardHeader && <section className="card">{cardHeader}</section>}
|
||||||
? <section className="card">
|
{content}
|
||||||
<div className="card__content">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
: content}
|
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
isLoaded: false,
|
isLoaded: false,
|
||||||
currentPath: currentPath(),
|
currentPath: currentPath(),
|
||||||
|
pathAfterAuth: "/discover",
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
|
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
|
||||||
daemonVersionMatched: null,
|
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) {
|
reducers[types.UPGRADE_CANCELLED] = function(state, action) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
downloadProgress: null,
|
downloadProgress: null,
|
||||||
|
|
|
@ -73,7 +73,7 @@ reducers[types.USER_EMAIL_NEW_STARTED] = function(state, action) {
|
||||||
|
|
||||||
reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
|
reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
|
||||||
let user = Object.assign({}, state.user);
|
let user = Object.assign({}, state.user);
|
||||||
user.has_email = true;
|
user.primary_email = action.data.email;
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
emailToVerify: action.data.email,
|
emailToVerify: action.data.email,
|
||||||
emailNewIsPending: false,
|
emailNewIsPending: false,
|
||||||
|
@ -105,7 +105,7 @@ reducers[types.USER_EMAIL_VERIFY_STARTED] = function(state, action) {
|
||||||
|
|
||||||
reducers[types.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
|
reducers[types.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
|
||||||
let user = Object.assign({}, state.user);
|
let user = Object.assign({}, state.user);
|
||||||
user.has_email = true;
|
user.primary_email = action.data.email;
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
emailToVerify: "",
|
emailToVerify: "",
|
||||||
emailVerifyIsPending: false,
|
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) {
|
reducers[types.FETCH_ACCESS_TOKEN_SUCCESS] = function(state, action) {
|
||||||
const { token } = action.data;
|
const { token } = action.data;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,10 @@ function rewardMessage(type, amount) {
|
||||||
"You earned %s LBC for making your first publication.",
|
"You earned %s LBC for making your first publication.",
|
||||||
amount
|
amount
|
||||||
),
|
),
|
||||||
|
featured_download: __(
|
||||||
|
"You earned %s LBC for watching a featured download.",
|
||||||
|
amount
|
||||||
|
),
|
||||||
}[type];
|
}[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,17 @@ export const selectSnackBarSnacks = createSelector(
|
||||||
snackBar => snackBar.snacks || []
|
snackBar => snackBar.snacks || []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectWelcomeModalAcknowledged = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => lbry.getClientSetting("welcome_acknowledged")
|
||||||
|
);
|
||||||
|
|
||||||
export const selectBadgeNumber = createSelector(
|
export const selectBadgeNumber = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
state => state.badgeNumber
|
state => state.badgeNumber
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectPathAfterAuth = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.pathAfterAuth
|
||||||
|
);
|
||||||
|
|
|
@ -12,25 +12,16 @@ export const selectUserIsPending = createSelector(
|
||||||
state => state.userIsPending
|
state => state.userIsPending
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectUser = createSelector(
|
export const selectUser = createSelector(_selectState, state => state.user);
|
||||||
_selectState,
|
|
||||||
state => state.user || {}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectEmailToVerify = createSelector(
|
export const selectEmailToVerify = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
state => state.emailToVerify
|
state => state.emailToVerify
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectUserHasEmail = createSelector(
|
export const selectUserEmail = createSelector(
|
||||||
selectUser,
|
selectUser,
|
||||||
selectEmailToVerify,
|
user => (user ? user.primary_email : null)
|
||||||
(user, email) => (user && user.has_email) || !!email
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectUserIsRewardEligible = createSelector(
|
|
||||||
selectUser,
|
|
||||||
user => user && user.is_reward_eligible
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectUserIsRewardApproved = createSelector(
|
export const selectUserIsRewardApproved = createSelector(
|
||||||
|
@ -63,18 +54,19 @@ export const selectEmailVerifyErrorMessage = createSelector(
|
||||||
state => state.emailVerifyErrorMessage
|
state => state.emailVerifyErrorMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectUserIsVerificationCandidate = createSelector(
|
export const selectIdentityVerifyIsPending = createSelector(
|
||||||
selectUser,
|
_selectState,
|
||||||
user => user && !user.has_verified_email
|
state => state.identityVerifyIsPending
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectUserIsAuthRequested = createSelector(
|
export const selectIdentityVerifyErrorMessage = createSelector(
|
||||||
selectEmailNewDeclined,
|
_selectState,
|
||||||
selectAuthenticationIsPending,
|
state => state.identityVerifyErrorMessage
|
||||||
selectUserIsVerificationCandidate,
|
);
|
||||||
selectUserHasEmail,
|
|
||||||
(isEmailDeclined, isPending, isVerificationCandidate, hasEmail) =>
|
export const selectUserIsVerificationCandidate = createSelector(
|
||||||
!isEmailDeclined && (isPending || !hasEmail || isVerificationCandidate)
|
selectUser,
|
||||||
|
user => user && (!user.has_verified_email || !user.is_identity_verified)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectAccessToken = createSelector(
|
export const selectAccessToken = createSelector(
|
||||||
|
|
|
@ -31,3 +31,7 @@ export function getSession(key, fallback = undefined) {
|
||||||
export function setSession(key, value) {
|
export function setSession(key, value) {
|
||||||
sessionStorage.setItem(key, JSON.stringify(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": "^2.6.1",
|
||||||
"webpack-dev-server": "^2.4.4",
|
"webpack-dev-server": "^2.4.4",
|
||||||
"webpack-notifier": "^1.5.0",
|
"webpack-notifier": "^1.5.0",
|
||||||
"webpack-target-electron-renderer": "^0.4.0",
|
"webpack-target-electron-renderer": "^0.4.0"
|
||||||
"why-did-you-update": "0.0.8"
|
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"gitDir": "../",
|
"gitDir": "../",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
@import "component/_channel-indicator.scss";
|
@import "component/_channel-indicator.scss";
|
||||||
@import "component/_notice.scss";
|
@import "component/_notice.scss";
|
||||||
@import "component/_modal.scss";
|
@import "component/_modal.scss";
|
||||||
@import "component/_modal-page.scss";
|
|
||||||
@import "component/_snack-bar.scss";
|
@import "component/_snack-bar.scss";
|
||||||
@import "component/_video.scss";
|
@import "component/_video.scss";
|
||||||
@import "page/_developer.scss";
|
@import "page/_developer.scss";
|
||||||
|
|
|
@ -164,6 +164,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,11 +1,6 @@
|
||||||
@import "../global";
|
@import "../global";
|
||||||
|
|
||||||
$width-input-border: 2px;
|
$width-input-border: 2px;
|
||||||
$width-input-text: 330px;
|
|
||||||
|
|
||||||
.form-input-width {
|
|
||||||
width: $width-input-text
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row-submit
|
.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