mostly done?

This commit is contained in:
Jeremy Kauffman 2017-06-07 20:56:52 -04:00
parent cbf35014cd
commit 18a40defba
28 changed files with 812 additions and 894 deletions

View file

@ -9,21 +9,11 @@ import {
selectCurrentPage,
selectCurrentParams,
} from "selectors/app";
import {
doSearch,
} from 'actions/search'
import {
doFetchDaemonSettings
} from 'actions/settings'
import {
doAuthenticate
} from 'actions/user'
import {
doRewardList
} from 'actions/rewards'
import {
doFileList
} from 'actions/file_info'
import { doSearch } from "actions/search";
import { doFetchDaemonSettings } from "actions/settings";
import { doAuthenticate } from "actions/user";
import { doRewardList } from "actions/rewards";
import { doFileList } from "actions/file_info";
const { remote, ipcRenderer, shell } = require("electron");
const path = require("path");
@ -231,14 +221,14 @@ export function doAlertError(errorList) {
export function doDaemonReady() {
return function(dispatch, getState) {
dispatch(doAuthenticate());
dispatch({
type: types.DAEMON_READY
})
dispatch(doAuthenticate())
dispatch(doChangePath('/discover'))
dispatch(doFetchDaemonSettings())
dispatch(doFileList())
}
type: types.DAEMON_READY,
});
dispatch(doChangePath("/discover"));
dispatch(doFetchDaemonSettings());
dispatch(doFileList());
};
}
export function doShowSnackBar(data) {

View file

@ -1,95 +1,110 @@
import * as types from 'constants/action_types'
import lbryio from 'lbryio'
import {
setLocal
} from 'utils'
import {
doRewardList
} from 'actions/rewards'
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";
export function doAuthenticate() {
return function(dispatch, getState) {
dispatch({
type: types.AUTHENTICATION_STARTED,
})
lbryio.authenticate().then((user) => {
dispatch({
type: types.AUTHENTICATION_SUCCESS,
data: { user }
})
});
lbryio
.authenticate()
.then(user => {
dispatch({
type: types.AUTHENTICATION_SUCCESS,
data: { user },
});
dispatch(doRewardList()) //FIXME - where should this happen?
}).catch((error) => {
console.log('auth error')
console.log(error)
dispatch({
type: types.AUTHENTICATION_FAILURE,
data: { error }
dispatch(doRewardList()); //FIXME - where should this happen?
})
})
}
.catch(error => {
console.log("auth error");
console.log(error);
dispatch({
type: types.AUTHENTICATION_FAILURE,
data: { error },
});
});
};
}
export function doUserEmailNew(email) {
return function(dispatch, getState) {
dispatch({
type: types.USER_EMAIL_NEW_STARTED,
email: email
})
lbryio.call('user_email', 'new', { email }, 'post').then(() => {
dispatch({
type: types.USER_EMAIL_NEW_SUCCESS,
data: { email }
})
}, (error) => {
if (error.xhr && (error.xhr.status == 409 || error.message == "This email is already in use")) {
dispatch({
type: types.USER_EMAIL_NEW_EXISTS,
data: { email }
})
} else {
dispatch({
type: types.USER_EMAIL_NEW_FAILURE,
data: { error: error.message }
})
}
email: email,
});
}
lbryio.call("user_email", "new", { email }, "post").then(
() => {
dispatch({
type: types.USER_EMAIL_NEW_SUCCESS,
data: { email },
});
},
error => {
if (
error.xhr &&
(error.xhr.status == 409 ||
error.message == "This email is already in use")
) {
dispatch({
type: types.USER_EMAIL_NEW_EXISTS,
data: { email },
});
} else {
dispatch({
type: types.USER_EMAIL_NEW_FAILURE,
data: { error: error.message },
});
}
}
);
};
}
export function doUserEmailDecline() {
return function(dispatch, getState) {
setLocal('user_email_declined', true)
setLocal("user_email_declined", true);
dispatch({
type: types.USER_EMAIL_DECLINE,
})
}
});
};
}
export function doUserEmailVerify(email, verificationToken) {
export function doUserEmailVerify(verificationToken) {
return function(dispatch, getState) {
const email = selectEmailToVerify(getState());
dispatch({
type: types.USER_EMAIL_VERIFY_STARTED,
code: code
})
code: verificationToken,
});
const failure = (error) => {
const failure = error => {
dispatch({
type: types.USER_EMAIL_VERIFY_FAILURE,
data: { error: error.message }
})
}
data: { error: error.message },
});
};
lbryio.call('user_email', 'confirm', {verification_token: verificationToken, email: email }, 'post').then((userEmail) => {
if (userEmail.is_verified) {
dispatch({
type: types.USER_EMAIL_VERIFY_SUCCESS,
data: { email }
})
} else {
failure(new Error("Your email is still not verified.")) //shouldn't happen?
}
}, failure);
}
}
lbryio
.call(
"user_email",
"confirm",
{ verification_token: verificationToken, email: email },
"post"
)
.then(userEmail => {
if (userEmail.is_verified) {
dispatch({
type: types.USER_EMAIL_VERIFY_SUCCESS,
data: { email },
});
} else {
failure(new Error("Your email is still not verified.")); //shouldn't happen?
}
}, failure);
};
}

View file

@ -4,6 +4,7 @@ import Header from "component/header";
import ErrorModal from "component/errorModal";
import DownloadingModal from "component/downloadingModal";
import UpgradeModal from "component/upgradeModal";
import WelcomeModal from "component/welcomeModal";
import lbry from "lbry";
import { Line } from "rc-progress";
@ -34,6 +35,7 @@ class App extends React.Component {
{modal == "upgrade" && <UpgradeModal />}
{modal == "downloading" && <DownloadingModal />}
{modal == "error" && <ErrorModal />}
{modal == "welcome" && <WelcomeModal />}
</div>
);
}

View file

@ -1,22 +1,16 @@
import React from 'react'
import {
connect
} from 'react-redux'
import {
doUserEmailDecline
} from 'actions/user'
import React from "react";
import { connect } from "react-redux";
import {
selectAuthenticationIsPending,
selectEmailNewDeclined,
selectEmailNewExistingEmail,
selectUser,
} from 'selectors/user'
import Auth from './view'
selectEmailToVerify,
selectUserIsVerificationCandidate,
} from "selectors/user";
import Auth from "./view";
const select = (state) => ({
const select = state => ({
isPending: selectAuthenticationIsPending(state),
existingEmail: selectEmailNewExistingEmail(state),
user: selectUser(state),
})
email: selectEmailToVerify(state),
isVerificationCandidate: selectUserIsVerificationCandidate(state),
});
export default connect(select, null)(Auth)
export default connect(select, null)(Auth);

View file

@ -1,29 +1,22 @@
import React from 'react'
import {BusyMessage} from 'component/common'
import UserEmailNew from 'component/userEmailNew'
import UserEmailVerify from 'component/userEmailVerify'
import React from "react";
import { BusyMessage } from "component/common";
import UserEmailNew from "component/userEmailNew";
import UserEmailVerify from "component/userEmailVerify";
export class Auth extends React.Component {
render() {
const {
isPending,
existingEmail,
user,
} = this.props
console.log('auth render')
console.log(this.props)
const { isPending, email, isVerificationCandidate } = this.props;
if (isPending) {
return <BusyMessage message="Authenticating" />
} else if (!existingEmail && !user.has_email) {
return <UserEmailNew />
} else if (!user.has_verified_email) {
return <UserEmailVerify />
return <BusyMessage message={__("Authenticating")} />;
} else if (!email) {
return <UserEmailNew />;
} else if (isVerificationCandidate) {
return <UserEmailVerify />;
} else {
return <span className="empty">Auth is done fix this yo</span>
return <span className="empty">{__("No further steps.")}</span>;
}
}
}
export default Auth
export default Auth;

View file

@ -1,25 +1,22 @@
import React from 'react'
import {
connect
} from 'react-redux'
import {
doUserEmailDecline
} from 'actions/user'
import React from "react";
import * as modal from "constants/modal_types";
import { connect } from "react-redux";
import { doUserEmailDecline } from "actions/user";
import { doOpenModal } from "actions/app";
import {
selectAuthenticationIsPending,
selectEmailNewDeclined,
selectUser,
} from 'selectors/user'
import AuthOverlay from './view'
selectUserIsAuthRequested,
} from "selectors/user";
import AuthOverlay from "./view";
const select = (state) => ({
const select = state => ({
isPending: selectAuthenticationIsPending(state),
isEmailDeclined: selectEmailNewDeclined(state),
user: selectUser(state),
})
isShowing: selectUserIsAuthRequested(state),
});
const perform = (dispatch) => ({
userEmailDecline: () => dispatch(doUserEmailDecline())
})
const perform = dispatch => ({
userEmailDecline: () => dispatch(doUserEmailDecline()),
openWelcomeModal: () => dispatch(doOpenModal(modal.WELCOME)),
});
export default connect(select, perform)(AuthOverlay)
export default connect(select, perform)(AuthOverlay);

View file

@ -1,8 +1,8 @@
import React from "react";
import lbryio from "lbryio.js";
import ModalPage from "component/modal-page.js";
import {BusyMessage} from 'component/common'
import Auth from 'component/auth'
import Link from "component/link"
import Auth from "component/auth";
import Link from "component/link";
export class AuthOverlay extends React.Component {
constructor(props) {
@ -13,167 +13,68 @@ export class AuthOverlay extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
if (this.props.isShowing && !this.props.isPending && !nextProps.isShowing) {
setTimeout(() => this.props.openWelcomeModal(), 1);
}
}
onEmailSkipClick() {
this.setState({ showNoEmailConfirm: true })
this.setState({ showNoEmailConfirm: true });
}
onEmailSkipConfirm() {
this.props.userEmailDecline()
this.props.userEmailDecline();
}
render() {
const {
isPending,
isEmailDeclined,
user,
} = this.props
if (!isEmailDeclined && (isPending || (user && !user.has_email))) {
return <ModalPage className="modal-page--full" isOpen={true} contentLabel="Authentication">
<h1>LBRY Early Access</h1>
{ isPending ?
<BusyMessage message="Preparing for early access" /> :
<Auth /> }
{ isPending ? '' :
<div className="form-row-submit">
{ this.state.showNoEmailConfirm ?
<div>
<p className="help form-input-width">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 className="button-text-help" onClick={ () => { this.onEmailSkipConfirm() }} label="Continue without email" />
</div>
:
<Link className="button-text-help" onClick={ () => { this.onEmailSkipClick() }} label="Do I have to?" />
}
</div> }
</ModalPage>
if (!lbryio.enabled) {
return null;
}
return null
const { isPending, isShowing } = 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">
{this.state.showNoEmailConfirm
? <div>
<p className="help form-input-width">
{__(
"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
className="button-text-help"
onClick={() => {
this.onEmailSkipConfirm();
}}
label={__("Continue without email")}
/>
</div>
: <Link
className="button-text-help"
onClick={() => {
this.onEmailSkipClick();
}}
label={__("Do I have to?")}
/>}
</div>}
</ModalPage>
);
}
return null;
}
}
export default AuthOverlay
// class WelcomeStage extends React.Component {
// static propTypes = {
// endAuth: React.PropTypes.func,
// }
//
// constructor(props) {
// super(props);
//
// this.state = {
// hasReward: false,
// rewardAmount: null,
// };
// }
//
// onRewardClaim(reward) {
// this.setState({
// hasReward: true,
// rewardAmount: reward.amount
// })
// }
//
// render() {
// return (
// !this.state.hasReward ?
// <Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY" {...this.props}>
// <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! Here's a nickel, kid.</p>
// <div style={{textAlign: "center", marginBottom: "12px"}}>
// <RewardLink type="new_user" button="primary" onRewardClaim={(event) => { this.onRewardClaim(event) }} onRewardFailure={() => this.props.setStage(null)} onConfirmed={() => { this.props.setStage(null) }} />
// </div>
// </section>
// </Modal> :
// <Modal type="alert" overlayClassName="modal-overlay modal-overlay--clear" isOpen={true} contentLabel="Welcome to LBRY" {...this.props} onConfirmed={() => { this.props.setStage(null) }}>
// <section>
// <h3 className="modal__header">About Your Reward</h3>
// <p>You earned a reward of <CreditAmount amount={this.state.rewardAmount} 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>
// );
// }
// // }
// //
// // const ErrorStage = (props) => {
// // return <section>
// // <p>An error was encountered that we cannot continue from.</p>
// // <p>At least we're earning the name beta.</p>
// // { props.errorText ? <p>Message: {props.errorText}</p> : '' }
// // <Link button="alt" label="Try Reload" onClick={() => { window.location.reload() } } />
// // </section>
// // }
// // //
// // // const PendingStage = (props) => {
// // // return <section>
// // // <BusyMessage message="Authenticating" />
// // // </section>
// // // }
// // // //
// // // //
// // // // class CodeRequiredStage extends React.Component {
// // // // constructor(props) {
// // // // super(props);
// // // //
// // // // this._balanceSubscribeId = null
// // // //
// // // // this.state = {
// // // // balance: 0,
// // // // address: getLocal('wallet_address')
// // // // };
// // // // }
// // // //
// // // // componentWillMount() {
// // // // this._balanceSubscribeId = lbry.balanceSubscribe((balance) => {
// // // // this.setState({
// // // // balance: balance
// // // // });
// // // // })
// // // //
// // // // if (!this.state.address) {
// // // // lbry.wallet_unused_address().then((address) => {
// // // // setLocal('wallet_address', address);
// // // // this.setState({ address: address });
// // // // });
// // // // }
// // // // }
// // // //
// // // // componentWillUnmount() {
// // // // if (this._balanceSubscribeId) {
// // // // lbry.balanceUnsubscribe(this._balanceSubscribeId)
// // // // }
// // // // }
// // // //
// // // // render() {
// // // // const disabled = this.state.balance < 1;
// // // // return (
// // // // <div>
// // // // <section className="section-spaced">
// // // // <p>Early access to LBRY is restricted as we build and scale the network.</p>
// // // // <p>There are two ways in.</p>
// // // // <h3>Own LBRY Credits</h3>
// // // // <p>If you own at least 1 LBC, you can get in right now.</p>
// // // // <p style={{ textAlign: "center"}}><Link onClick={() => { setLocal('auth_bypassed', true); this.props.setStage(null); }}
// // // // disabled={disabled} label="Let Me In" button={ disabled ? "alt" : "primary" } /></p>
// // // // <p>Your balance is <CreditAmount amount={this.state.balance} />. To increase your balance, send credits to this address:</p>
// // // // <p><Address address={ this.state.address ? this.state.address : "Generating Address..." } /></p>
// // // // <p>If you don't understand how to send credits, then...</p>
// // // // </section>
// // // // <section>
// // // // <h3>Wait For A Code</h3>
// // // // <p>If you provide your email, you'll automatically receive a notification when the system is open.</p>
// // // // <p><Link onClick={() => { this.props.setStage("email"); }} label="Return" /></p>
// // // // </section>
// // // // </div>
// // // // );
// // // // }
// // // // }
export default AuthOverlay;

View file

@ -1,41 +1,35 @@
import React from 'react'
import {
connect,
} from 'react-redux'
import React from "react";
import { connect } from "react-redux";
import {
makeSelectHasClaimedReward,
makeSelectClaimRewardError,
makeSelectRewardByType,
makeSelectIsRewardClaimPending,
selectIsRewardEligible,
} from 'selectors/rewards'
import {
doNavigate
} from 'actions/app'
import {
doClaimReward,
doClaimRewardClearError
} from 'actions/rewards'
import RewardLink from './view'
} from "selectors/rewards";
import { doNavigate } from "actions/app";
import { doClaimReward, doClaimRewardClearError } from "actions/rewards";
import RewardLink from "./view";
const makeSelect = () => {
const selectHasClaimedReward = makeSelectHasClaimedReward()
const selectIsPending = makeSelectIsRewardClaimPending()
const selectError = makeSelectClaimRewardError()
const selectHasClaimedReward = makeSelectHasClaimedReward();
const selectIsPending = makeSelectIsRewardClaimPending();
const selectReward = makeSelectRewardByType();
const selectError = makeSelectClaimRewardError();
const select = (state, props) => ({
isClaimed: selectHasClaimedReward(state, props),
isEligible: selectIsRewardEligible(state),
errorMessage: selectError(state, props),
isPending: selectIsPending(state, props)
})
isPending: selectIsPending(state, props),
reward: select,
});
return select
}
return select;
};
const perform = (dispatch) => ({
claimReward: (reward) => dispatch(doClaimReward(reward)),
clearError: (reward) => dispatch(doClaimRewardClearError(reward)),
navigate: (path) => dispatch(doNavigate(path)),
})
const perform = dispatch => ({
claimReward: reward => dispatch(doClaimReward(reward)),
clearError: reward => dispatch(doClaimRewardClearError(reward)),
navigate: path => dispatch(doNavigate(path)),
});
export default connect(makeSelect, perform)(RewardLink)
export default connect(makeSelect, perform)(RewardLink);

View file

@ -1,9 +1,9 @@
import React from 'react';
import {Icon} from 'component/common';
import Modal from 'component/modal';
import Link from 'component/link'
import React from "react";
import { Icon } from "component/common";
import Modal from "component/modal";
import Link from "component/link";
const RewardLink = (props) => {
const RewardLink = props => {
const {
reward,
button,
@ -11,22 +11,34 @@ const RewardLink = (props) => {
clearError,
errorMessage,
isClaimed,
isEligible,
isPending
} = props
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) }} />}
{errorMessage ?
<Modal isOpen={true} contentLabel="Reward Claim Error" className="error-modal" onConfirmed={() => { clearError(reward) }}>
{errorMessage}
</Modal>
: ''}
: <Link
button={button ? button : "alt"}
disabled={isPending}
label={isPending ? "Claiming..." : "Claim Reward"}
onClick={() => {
claimReward(reward);
}}
/>}
{errorMessage
? <Modal
isOpen={true}
contentLabel="Reward Claim Error"
className="error-modal"
onConfirmed={() => {
clearError(reward);
}}
>
{errorMessage}
</Modal>
: ""}
</div>
)
}
export default RewardLink
);
};
export default RewardLink;

View file

@ -1,5 +1,4 @@
import React from "react";
import AuthPage from 'page/auth';
import SettingsPage from "page/settings";
import HelpPage from "page/help";
import ReportPage from "page/report.js";
@ -8,9 +7,8 @@ import WalletPage from "page/wallet";
import ShowPage from "page/showPage";
import PublishPage from "page/publish";
import DiscoverPage from "page/discover";
import SplashScreen from "component/splash.js";
import DeveloperPage from "page/developer.js";
import RewardsPage from "page/rewards.js";
import RewardsPage from "page/rewards";
import FileListDownloaded from "page/fileListDownloaded";
import FileListPublished from "page/fileListPublished";
import ChannelPage from "page/channel";
@ -42,7 +40,6 @@ const Router = props => {
discover: <DiscoverPage {...params} />,
rewards: <RewardsPage {...params} />,
search: <SearchPage {...params} />,
"account-verification": <AuthPage {...params} />
});
};

View file

@ -1,25 +1,21 @@
import React from 'react'
import {
connect
} from 'react-redux'
import {
doUserEmailVerify
} from 'actions/user'
import React from "react";
import { connect } from "react-redux";
import { doUserEmailVerify } from "actions/user";
import {
selectEmailVerifyIsPending,
selectEmailNewExistingEmail,
selectEmailToVerify,
selectEmailVerifyErrorMessage,
} from 'selectors/user'
import UserEmailVerify from './view'
} from "selectors/user";
import UserEmailVerify from "./view";
const select = (state) => ({
const select = state => ({
isPending: selectEmailVerifyIsPending(state),
email: selectEmailNewExistingEmail,
email: selectEmailToVerify,
errorMessage: selectEmailVerifyErrorMessage(state),
})
});
const perform = (dispatch) => ({
verifyUserEmail: (email, code) => dispatch(doUserEmailVerify(email, code))
})
const perform = dispatch => ({
verifyUserEmail: code => dispatch(doUserEmailVerify(code)),
});
export default connect(select, perform)(UserEmailVerify)
export default connect(select, perform)(UserEmailVerify);

View file

@ -0,0 +1,22 @@
import React from "react";
import { connect } from "react-redux";
import { doCloseModal } from "actions/app";
import { selectUserIsRewardApproved } from "selectors/user";
import { makeSelectHasClaimedReward } from "selectors/rewards";
import WelcomeModal from "./view";
const select = (state, props) => {
const selectHasReward = makeSelectHasClaimedReward();
return {
hasReward: selectHasReward(state, { reward_type: "new_user" }),
isRewardApproved: selectUserIsRewardApproved(state),
rewardAmount: 5,
};
};
const perform = dispatch => ({
closeModal: () => dispatch(doCloseModal()),
});
export default connect(select, perform)(WelcomeModal);

View file

@ -0,0 +1,79 @@
import React from "react";
import { Modal } from "component/modal";
import { CreditAmount } from "component/common";
import Link from "component/link";
import RewardLink from "component/rewardLink";
class WelcomeModal extends React.Component {
render() {
const {
closeModal,
hasReward,
isRewardApproved,
rewardAmount,
} = this.props;
return !hasReward
? <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 style={{ textAlign: "center", marginBottom: "12px" }}>
{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={rewardAmount} 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>;
}
}
export default WelcomeModal;

View file

@ -0,0 +1 @@
export const WELCOME = "welcome";

View file

@ -1,134 +1,147 @@
import { getSession, setSession, setLocal } from './utils.js';
import lbry from './lbry.js';
import { getSession, setSession, setLocal } from "./utils.js";
import lbry from "./lbry.js";
const querystring = require('querystring');
const querystring = require("querystring");
const lbryio = {
_accessToken: getSession('accessToken'),
_authenticationPromise: null,
_user: null,
enabled: true
_accessToken: getSession("accessToken"),
_authenticationPromise: null,
_user: null,
enabled: true,
};
const CONNECTION_STRING = process.env.LBRY_APP_API_URL
? process.env.LBRY_APP_API_URL.replace(/\/*$/, '/') // exactly one slash at the end
: 'https://api.lbry.io/';
? process.env.LBRY_APP_API_URL.replace(/\/*$/, "/") // exactly one slash at the end
: "https://api.lbry.io/";
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
lbryio._exchangePromise = null;
lbryio._exchangeLastFetched = null;
lbryio.getExchangeRates = function() {
if (
!lbryio._exchangeLastFetched ||
Date.now() - lbryio._exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
) {
lbryio._exchangePromise = new Promise((resolve, reject) => {
lbryio
.call('lbc', 'exchange_rate', {}, 'get', true)
.then(({ lbc_usd, lbc_btc, btc_usd }) => {
const rates = { lbc_usd, lbc_btc, btc_usd };
resolve(rates);
})
.catch(reject);
});
lbryio._exchangeLastFetched = Date.now();
}
return lbryio._exchangePromise;
if (
!lbryio._exchangeLastFetched ||
Date.now() - lbryio._exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
) {
lbryio._exchangePromise = new Promise((resolve, reject) => {
lbryio
.call("lbc", "exchange_rate", {}, "get", true)
.then(({ lbc_usd, lbc_btc, btc_usd }) => {
const rates = { lbc_usd, lbc_btc, btc_usd };
resolve(rates);
})
.catch(reject);
});
lbryio._exchangeLastFetched = Date.now();
}
return lbryio._exchangePromise;
};
lbryio.call = function(
resource,
action,
params = {},
method = 'get',
evenIfDisabled = false
resource,
action,
params = {},
method = "get",
evenIfDisabled = false
) {
// evenIfDisabled is just for development, when we may have some calls working and some not
return new Promise((resolve, reject) => {
if (
!lbryio.enabled &&
!evenIfDisabled &&
(resource != 'discover' || action != 'list')
) {
console.log(__('Internal API disabled'));
reject(new Error(__('LBRY internal API is disabled')));
return;
}
// evenIfDisabled is just for development, when we may have some calls working and some not
return new Promise((resolve, reject) => {
if (
!lbryio.enabled &&
!evenIfDisabled &&
(resource != "discover" || action != "list")
) {
console.log(__("Internal API disabled"));
reject(new Error(__("LBRY internal API is disabled")));
return;
}
const xhr = new XMLHttpRequest();
const xhr = new XMLHttpRequest();
xhr.addEventListener('error', function(event) {
reject(
new Error(__('Something went wrong making an internal API call.'))
);
});
xhr.addEventListener("error", function(event) {
reject(
new Error(__("Something went wrong making an internal API call."))
);
});
xhr.addEventListener('timeout', function() {
reject(new Error(__('XMLHttpRequest connection timed out')));
});
xhr.addEventListener("timeout", function() {
reject(new Error(__("XMLHttpRequest connection timed out")));
});
xhr.addEventListener('load', function() {
const response = JSON.parse(xhr.responseText);
xhr.addEventListener("load", function() {
const response = JSON.parse(xhr.responseText);
if (!response.success) {
if (reject) {
let error = new Error(response.error);
error.xhr = xhr;
reject(error);
} else {
document.dispatchEvent(
new CustomEvent('unhandledError', {
detail: {
connectionString: connectionString,
method: action,
params: params,
message: response.error.message,
...(response.error.data ? { data: response.error.data } : {})
}
})
);
}
} else {
resolve(response.data);
}
});
if (!response.success) {
if (reject) {
let error = new Error(response.error);
error.xhr = xhr;
reject(error);
} else {
document.dispatchEvent(
new CustomEvent("unhandledError", {
detail: {
connectionString: connectionString,
method: action,
params: params,
message: response.error.message,
...(response.error.data ? { data: response.error.data } : {}),
},
})
);
}
} else {
resolve(response.data);
}
});
// For social media auth:
//const accessToken = localStorage.getItem('accessToken');
//const fullParams = {...params, ... accessToken ? {access_token: accessToken} : {}};
// For social media auth:
//const accessToken = localStorage.getItem('accessToken');
//const fullParams = {...params, ... accessToken ? {access_token: accessToken} : {}};
// Temp app ID based auth:
const fullParams = { app_id: lbryio.getAccessToken(), ...params };
// Temp app ID based auth:
const fullParams = { app_id: lbryio.getAccessToken(), ...params };
if (method == 'get') {
xhr.open(
'get',
CONNECTION_STRING +
resource +
'/' +
action +
'?' +
querystring.stringify(fullParams),
true
);
xhr.send();
} else if (method == 'post') {
xhr.open('post', CONNECTION_STRING + resource + '/' + action, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(querystring.stringify(fullParams));
} else {
reject(new Error(__('Invalid method')));
}
});
if (method == "get") {
xhr.open(
"get",
CONNECTION_STRING +
resource +
"/" +
action +
"?" +
querystring.stringify(fullParams),
true
);
xhr.send();
} else if (method == "post") {
xhr.open("post", CONNECTION_STRING + resource + "/" + action, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(querystring.stringify(fullParams));
} else {
reject(new Error(__("Invalid method")));
}
});
};
lbryio.getAccessToken = () => {
const token = getSession('accessToken');
return token ? token.toString().trim() : token;
const token = getSession("accessToken");
return token ? token.toString().trim() : token;
};
lbryio.setAccessToken = token => {
setSession('accessToken', token ? token.toString().trim() : token);
setSession("accessToken", token ? token.toString().trim() : token);
};
lbryio.setCurrentUser = (resolve, reject) => {
lbryio
.call("user", "me")
.then(data => {
lbryio.user = data;
resolve(data);
})
.catch(function(err) {
lbryio.setAccessToken(null);
reject(err);
});
};
lbryio.authenticate = function() {
@ -136,53 +149,57 @@ lbryio.authenticate = function() {
return new Promise((resolve, reject) => {
resolve({
id: 1,
has_verified_email: true
})
})
language: "en",
has_email: true,
has_verified_email: true,
is_reward_approved: false,
is_reward_eligible: false,
});
});
}
if (lbryio._authenticationPromise === null) {
lbryio._authenticationPromise = new Promise((resolve, reject) => {
lbry.status().then((response) => {
lbry
.status()
.then(response => {
let installation_id = response.installation_id;
let installation_id = response.installation_id.substring(0, response.installation_id.length - 2) + "E";
function setCurrentUser() {
lbryio.call('user', 'me').then((data) => {
lbryio.user = data
resolve(data)
}).catch(function(err) {
lbryio.setAccessToken(null);
reject(err);
})
}
if (!lbryio.getAccessToken()) {
lbryio.call('user', 'new', {
language: 'en',
app_id: installation_id,
}, 'post').then(function(responseData) {
if (!responseData.id) {
reject(new Error("Received invalid authentication response."));
}
lbryio.setAccessToken(installation_id)
setLocal('auth_bypassed', false)
setCurrentUser()
}).catch(function(error) {
/*
until we have better error code format, assume all errors are duplicate application id
if we're wrong, this will be caught by later attempts to make a valid call
*/
lbryio.setAccessToken(installation_id);
setCurrentUser();
});
} else {
setCurrentUser();
}
})
.catch(reject);
});
}
return lbryio._authenticationPromise;
if (!lbryio.getAccessToken()) {
lbryio
.call(
"user",
"new",
{
language: "en",
app_id: installation_id,
},
"post"
)
.then(function(responseData) {
if (!responseData.id) {
reject(
new Error("Received invalid authentication response.")
);
}
lbryio.setAccessToken(installation_id);
lbryio.setCurrentUser(resolve, reject);
})
.catch(function(error) {
/*
until we have better error code format, assume all errors are duplicate application id
if we're wrong, this will be caught by later attempts to make a valid call
*/
lbryio.setAccessToken(installation_id);
lbryio.setCurrentUser(resolve, reject);
});
} else {
lbryio.setCurrentUser(resolve, reject);
}
})
.catch(reject);
});
}
return lbryio._authenticationPromise;
};
export default lbryio;

View file

@ -1,86 +1,85 @@
import React from 'react';
import ReactDOM from 'react-dom';
import lbry from './lbry.js';
import lbryio from './lbryio.js';
import App from 'component/app/index.js';
import SnackBar from 'component/snackBar';
import { Provider } from 'react-redux';
import store from 'store.js';
import SplashScreen from 'component/splash.js';
import AuthOverlay from 'component/authOverlay';
import { doChangePath, doNavigate, doDaemonReady } from 'actions/app';
import { toQueryString } from 'util/query_params';
import React from "react";
import ReactDOM from "react-dom";
import lbry from "./lbry.js";
import App from "component/app/index.js";
import SnackBar from "component/snackBar";
import { Provider } from "react-redux";
import store from "store.js";
import SplashScreen from "component/splash.js";
import AuthOverlay from "component/authOverlay";
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
import { toQueryString } from "util/query_params";
const { remote, ipcRenderer, shell } = require('electron');
const contextMenu = remote.require('./menu/context-menu');
const app = require('./app');
const { remote, ipcRenderer, shell } = require("electron");
const contextMenu = remote.require("./menu/context-menu");
const app = require("./app");
lbry.showMenuIfNeeded();
window.addEventListener('contextmenu', event => {
contextMenu.showContextMenu(
remote.getCurrentWindow(),
event.x,
event.y,
lbry.getClientSetting('showDeveloperMenu')
);
event.preventDefault();
window.addEventListener("contextmenu", event => {
contextMenu.showContextMenu(
remote.getCurrentWindow(),
event.x,
event.y,
lbry.getClientSetting("showDeveloperMenu")
);
event.preventDefault();
});
window.addEventListener('popstate', (event, param) => {
const params = event.state;
const pathParts = document.location.pathname.split('/');
const route = '/' + pathParts[pathParts.length - 1];
const queryString = toQueryString(params);
window.addEventListener("popstate", (event, param) => {
const params = event.state;
const pathParts = document.location.pathname.split("/");
const route = "/" + pathParts[pathParts.length - 1];
const queryString = toQueryString(params);
let action;
if (route.match(/html$/)) {
action = doChangePath('/discover');
} else {
action = doChangePath(`${route}?${queryString}`);
}
let action;
if (route.match(/html$/)) {
action = doChangePath("/discover");
} else {
action = doChangePath(`${route}?${queryString}`);
}
app.store.dispatch(action);
app.store.dispatch(action);
});
ipcRenderer.on('open-uri-requested', (event, uri) => {
if (uri && uri.startsWith('lbry://')) {
app.store.dispatch(doNavigate('/show', { uri }));
}
ipcRenderer.on("open-uri-requested", (event, uri) => {
if (uri && uri.startsWith("lbry://")) {
app.store.dispatch(doNavigate("/show", { uri }));
}
});
document.addEventListener('click', event => {
var target = event.target;
while (target && target !== document) {
if (target.matches('a[href^="http"]')) {
event.preventDefault();
shell.openExternal(target.href);
return;
}
target = target.parentNode;
}
document.addEventListener("click", event => {
var target = event.target;
while (target && target !== document) {
if (target.matches('a[href^="http"]')) {
event.preventDefault();
shell.openExternal(target.href);
return;
}
target = target.parentNode;
}
});
const initialState = app.store.getState();
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
app.store.dispatch(doDaemonReady())
function onDaemonReady() {
window.sessionStorage.setItem("loaded", "y"); //once we've made it here once per session, we don't need to show splash again
app.store.dispatch(doDaemonReady());
ReactDOM.render(
<Provider store={store}>
<div>{lbryio.enabled ? <AuthOverlay /> : ''}<App /><SnackBar /></div>
</Provider>,
canvas
);
}
ReactDOM.render(
<Provider store={store}>
<div><AuthOverlay /><App /><SnackBar /></div>
</Provider>,
canvas
);
}
if (window.sessionStorage.getItem('loaded') == 'y') {
onDaemonReady();
} else {
ReactDOM.render(<SplashScreen onLoadDone={onDaemonReady} />, canvas);
}
if (window.sessionStorage.getItem("loaded") == "y") {
onDaemonReady();
} else {
ReactDOM.render(<SplashScreen onLoadDone={onDaemonReady} />, canvas);
}
};
init();

View file

@ -1,25 +0,0 @@
import React from 'react'
import {
connect
} from 'react-redux'
import {
doUserEmailDecline
} from 'actions/user'
import {
selectAuthenticationIsPending,
selectEmailNewDeclined,
selectUser,
} from 'selectors/user'
import AuthPage from './view'
const select = (state) => ({
isPending: selectAuthenticationIsPending(state),
isEmailDeclined: selectEmailNewDeclined(state),
user: selectUser(state),
})
const perform = (dispatch) => ({
userEmailDecline: () => dispatch(doUserEmailDecline())
})
export default connect(select, perform)(AuthPage)

View file

@ -1,19 +0,0 @@
import React from 'react'
import Auth from 'component/auth'
export class AuthPage extends React.Component {
render() {
return <main className="main--single-column">
<section className="card">
<div className="card__inner">
<div className="card__title-identity"><h1>Early Access Verification</h1></div>
</div>
<div className="card__content">
<Auth />
</div>
</section>
</main>
}
}
export default AuthPage

View file

@ -1,100 +0,0 @@
import React from "react";
import lbryio from "lbryio";
import { CreditAmount, Icon } from "component/common.js";
import SubHeader from "component/subHeader";
import { RewardLink } from "component/reward-link";
export class RewardTile extends React.Component {
static propTypes = {
type: React.PropTypes.string.isRequired,
title: React.PropTypes.string.isRequired,
description: React.PropTypes.string.isRequired,
claimed: React.PropTypes.bool.isRequired,
value: React.PropTypes.number.isRequired,
onRewardClaim: React.PropTypes.func,
};
render() {
return (
<section className="card">
<div className="card__inner">
<div className="card__title-primary">
<CreditAmount amount={this.props.value} />
<h3>{this.props.title}</h3>
</div>
<div className="card__actions">
{this.props.claimed
? <span><Icon icon="icon-check" /> {__("Reward claimed.")}</span>
: <RewardLink {...this.props} />}
</div>
<div className="card__content">{this.props.description}</div>
</div>
</section>
);
}
}
export class RewardsPage extends React.Component {
constructor(props) {
super(props);
this.state = {
userRewards: null,
failed: null,
};
}
componentWillMount() {
this.loadRewards();
}
loadRewards() {
lbryio.call("reward", "list", {}).then(
userRewards => {
this.setState({
userRewards: userRewards,
});
},
() => {
this.setState({ failed: true });
}
);
}
render() {
return (
<main className="main--single-column">
<SubHeader />
<div>
{!this.state.userRewards
? this.state.failed
? <div className="empty">{__("Failed to load rewards.")}</div>
: ""
: this.state.userRewards.map(
({
reward_type,
reward_title,
reward_description,
transaction_id,
reward_amount,
}) => {
return (
<RewardTile
key={reward_type}
onRewardClaim={this.loadRewards}
type={reward_type}
title={__(reward_title)}
description={__(reward_description)}
claimed={!!transaction_id}
value={reward_amount}
/>
);
}
)}
</div>
</main>
);
}
}
export default RewardsPage;

View file

@ -1,28 +1,25 @@
import React from 'react'
import {
connect,
} from 'react-redux'
import {
doNavigate
} from 'actions/app'
import React from "react";
import { connect } from "react-redux";
import { doNavigate } from "actions/app";
import {
selectFetchingRewards,
selectIsRewardEligible,
selectRewards,
} from 'selectors/rewards'
} from "selectors/rewards";
import {
selectUserIsRewardEligible
} from 'selectors/user'
import RewardsPage from './view'
selectUserIsRewardEligible,
selectUserHasEmail,
selectUserIsRewardApproved,
selectUserIsVerificationCandidate,
} from "selectors/user";
import RewardsPage from "./view";
const select = (state) => ({
const select = state => ({
fetching: selectFetchingRewards(state),
rewards: selectRewards(state),
isEligible: selectUserIsRewardEligible(state)
})
hasEmail: selectUserHasEmail(state),
isEligible: selectUserIsRewardEligible(state),
isVerificationCandidate: selectUserIsVerificationCandidate(state),
});
const perform = (dispatch) => ({
navigateToAuth: () => dispatch(doNavigate('/account-verification'))
})
export default connect(select, perform)(RewardsPage)
export default connect(select, null)(RewardsPage);

View file

@ -1,16 +1,15 @@
import React from 'react';
import lbryio from 'lbryio';
import {BusyMessage, CreditAmount, Icon} from 'component/common';
import SubHeader from 'component/subHeader'
import Link from 'component/link'
import RewardLink from 'component/rewardLink';
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";
const RewardTile = (props) => {
const {
reward,
} = props
const RewardTile = props => {
const { reward } = props;
const claimed = !!reward.transaction_id
const claimed = !!reward.transaction_id;
return (
<section className="card">
@ -22,43 +21,74 @@ const RewardTile = (props) => {
<div className="card__actions">
{claimed
? <span><Icon icon="icon-check" /> Reward claimed.</span>
: <RewardLink reward={reward} />}
: <RewardLink reward_type={reward.reward_type} />}
</div>
<div className="card__content">{reward.reward_description}</div>
</div>
</section>
)
}
);
};
const RewardsPage = (props) => {
const RewardsPage = props => {
const {
fetching,
isEligible,
navigateToAuth,
isVerificationCandidate,
hasEmail,
rewards,
} = props
} = props;
let content
console.log(props);
if (!isEligible) {
content = <div className="empty">
You are not eligible to claim rewards. { ' ' }
<Link onClick={navigateToAuth} label="Become eligible" />.
</div>
let content,
isCard = false;
if (!hasEmail || isVerificationCandidate) {
content = (
<div>
<p>
{__(
"Additional information is required to be eligible for the rewards program."
)}
</p>
<Auth />
</div>
);
isCard = true;
} else if (!isEligible) {
isCard = true;
content = (
<div className="empty">
<p>{__("You are not eligible to claim rewards.")}</p>
<p>
To become eligible, email
{" "}<Link href="mailto:help@lbry.io" label="help@lbry.io" /> with a
link to a public social media profile.
</p>
</div>
);
} else if (fetching) {
content = <BusyMessage message="Fetching rewards" />
content = <BusyMessage message="Fetching rewards" />;
} else if (rewards.length > 0) {
content = rewards.map(reward => <RewardTile key={reward.reward_type} reward={reward} />)
content = rewards.map(reward =>
<RewardTile key={reward.reward_type} reward={reward} />
);
} else {
content = <div className="empty">Failed to load rewards.</div>
content = <div className="empty">{__("Failed to load rewards.")}</div>;
}
return (
<main className="main--single-column">
<SubHeader />
{content}
{isCard
? <section className="card">
<div className="card__content">
{content}
</div>
</section>
: content}
</main>
)
}
);
};
export default RewardsPage;

View file

@ -229,25 +229,32 @@ class SettingsPage extends React.Component {
</div>
</section>
{/*}
<section className="card">
<div className="card__content">
<h3>{__("Language")}</h3>
</div>
<div className="card__content">
<div className="form-row">
<FormField type="radio"
name="language"
label={__("English")}
onChange={() => { this.onLanguageChange('en') }}
defaultChecked={this.state.language=='en'} />
<FormField
type="radio"
name="language"
label={__("English")}
onChange={() => {
this.onLanguageChange("en");
}}
defaultChecked={this.state.language == "en"}
/>
</div>
<div className="form-row">
<FormField type="radio"
name="language"
label="Serbian"
onChange={() => { this.onLanguageChange('rs') }}
defaultChecked={this.state.language=='rs'} />
<FormField
type="radio"
name="language"
label="Serbian"
onChange={() => {
this.onLanguageChange("rs");
}}
defaultChecked={this.state.language == "rs"}
/>
</div>
</div>
</section>*/}

View file

@ -1,91 +1,98 @@
import * as types from 'constants/action_types'
import {
getLocal
} from 'utils'
import * as types from "constants/action_types";
import { getLocal } from "utils";
const reducers = {}
const reducers = {};
const defaultState = {
authenticationIsPending: false,
emailNewIsPending: false,
emailNewErrorMessage: '',
emailNewDeclined: getLocal('user_email_declined', false),
user: undefined
}
emailNewErrorMessage: "",
emailNewDeclined: getLocal("user_email_declined", false),
emailToVerify: "",
user: undefined,
};
reducers[types.AUTHENTICATION_STARTED] = function(state, action) {
return Object.assign({}, state, {
authenticationIsPending: true
})
}
authenticationIsPending: true,
});
};
reducers[types.AUTHENTICATION_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
authenticationIsPending: false,
user: action.data.user,
})
}
});
};
reducers[types.AUTHENTICATION_FAILURE] = function(state, action) {
return Object.assign({}, state, {
authenticationIsPending: false,
user: null,
})
}
});
};
reducers[types.USER_EMAIL_DECLINE] = function(state, action) {
return Object.assign({}, state, {
emailNewDeclined: true
})
}
emailNewDeclined: true,
});
};
reducers[types.USER_EMAIL_NEW_STARTED] = function(state, action) {
return Object.assign({}, state, {
emailNewIsPending: true,
emailNewErrorMessage: ''
})
}
emailNewErrorMessage: "",
});
};
reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
let user = Object.assign({}, state.user);
user.has_email = true;
return Object.assign({}, state, {
emailToVerify: action.data.email,
emailNewIsPending: false,
})
}
user: user,
});
};
reducers[types.USER_EMAIL_NEW_EXISTS] = function(state, action) {
let user = Object.assign({}, state.user);
return Object.assign({}, state, {
emailNewExistingEmail: action.data.email,
emailToVerify: action.data.email,
emailNewIsPending: false,
})
}
});
};
reducers[types.USER_EMAIL_NEW_FAILURE] = function(state, action) {
return Object.assign({}, state, {
emailNewIsPending: false,
emailNewErrorMessage: action.data.error
})
}
emailNewErrorMessage: action.data.error,
});
};
reducers[types.USER_EMAIL_VERIFY_STARTED] = function(state, action) {
return Object.assign({}, state, {
emailVerifyIsPending: true,
emailVerifyErrorMessage: ''
})
}
emailVerifyErrorMessage: "",
});
};
reducers[types.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
let user = Object.assign({}, state.user);
user.has_email = true;
return Object.assign({}, state, {
emailToVerify: "",
emailVerifyIsPending: false,
})
}
user: user,
});
};
reducers[types.USER_EMAIL_VERIFY_FAILURE] = function(state, action) {
return Object.assign({}, state, {
emailVerifyIsPending: false,
emailVerifyErrorMessage: action.data.error
})
}
emailVerifyErrorMessage: action.data.error,
});
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];

View file

@ -54,8 +54,6 @@ export const selectPageTitle = createSelector(
return __("Publishes");
case "discover":
return __("Home");
case 'account-verification':
return __('Early Access Verification')
default:
return "";
}

View file

@ -5,88 +5,75 @@ const _selectState = state => state.rewards || {};
export const selectRewardsByType = createSelector(
_selectState,
(state) => state.byRewardType || {}
)
state => state.byRewardType || {}
);
export const selectRewards = createSelector(
selectRewardsByType,
(byType) => Object.values(byType) || []
)
byType => Object.values(byType) || []
);
export const selectIsRewardEligible = createSelector(
selectUser,
(user) => user.can_claim_rewards
)
user => user.can_claim_rewards
);
export const selectClaimedRewards = createSelector(
selectRewards,
(rewards) => rewards.filter(reward => reward.transaction_id !== "")
)
export const selectClaimedRewards = createSelector(selectRewards, rewards =>
rewards.filter(reward => reward.transaction_id !== "")
);
export const selectClaimedRewardsByType = createSelector(
selectClaimedRewards,
(claimedRewards) => {
const byType = {}
claimedRewards.forEach(reward => byType[reward.reward_type] = reward)
return byType
claimedRewards => {
const byType = {};
claimedRewards.forEach(reward => (byType[reward.reward_type] = reward));
return byType;
}
)
);
export const selectFetchingRewards = createSelector(
_selectState,
(state) => !!state.fetching
)
state => !!state.fetching
);
export const selectHasClaimedReward = (state, props) => {
return !!selectClaimedRewardsByType[props.reward.reward_type]
}
return !!selectClaimedRewardsByType[props.reward_type];
};
export const makeSelectHasClaimedReward = () => {
return createSelector(
selectHasClaimedReward,
(claimed) => claimed
)
}
return createSelector(selectHasClaimedReward, claimed => claimed);
};
export const selectClaimsPendingByType = createSelector(
_selectState,
(state) => state.claimPendingByType
)
state => state.claimPendingByType
);
const selectIsClaimRewardPending = (state, props) => {
return selectClaimsPendingByType(state, props)[props.reward.reward_type]
}
return selectClaimsPendingByType(state, props)[props.reward_type];
};
export const makeSelectIsRewardClaimPending = () => {
return createSelector(
selectIsClaimRewardPending,
(isClaiming) => isClaiming
)
}
return createSelector(selectIsClaimRewardPending, isClaiming => isClaiming);
};
export const selectClaimErrorsByType = createSelector(
_selectState,
(state) => state.claimErrorsByType
)
state => state.claimErrorsByType
);
const selectClaimRewardError = (state, props) => {
return selectClaimErrorsByType(state, props)[props.reward.reward_type]
}
return selectClaimErrorsByType(state, props)[props.reward_type];
};
export const makeSelectClaimRewardError = () => {
return createSelector(
selectClaimRewardError,
(errorMessage) => errorMessage
)
}
return createSelector(selectClaimRewardError, errorMessage => errorMessage);
};
const selectRewardByType = (state, props) => {
return selectRewardsByType(state)[props.reward_type]
}
return selectRewardsByType(state)[props.reward_type];
};
export const makeSelectRewardByType = () => {
return createSelector(
selectRewardByType,
(reward) => reward
)
}
return createSelector(selectRewardByType, reward => reward);
};

View file

@ -68,7 +68,5 @@ export const selectWunderBarIcon = createSelector(selectCurrentPage, page => {
return "icon-code";
case "discover":
return "icon-home";
case 'account-verification':
return 'icon-lock'
}
});

View file

@ -1,48 +1,77 @@
import { createSelector } from 'reselect'
import { createSelector } from "reselect";
export const _selectState = state => state.user || {}
export const _selectState = state => state.user || {};
export const selectAuthenticationIsPending = createSelector(
_selectState,
(state) => state.authenticationIsPending
)
state => state.authenticationIsPending
);
export const selectUser = createSelector(
_selectState,
(state) => state.user
)
state => state.user || {}
);
export const selectEmailToVerify = createSelector(
_selectState,
state => state.emailToVerify
);
export const selectUserHasEmail = createSelector(
selectUser,
selectEmailToVerify,
(user, email) => user.has_email || email
);
export const selectUserIsRewardEligible = createSelector(
_selectState,
(state) => state.user.can_claim_rewards
)
selectUser,
user => user.is_reward_eligible
);
export const selectUserIsRewardApproved = createSelector(
selectUser,
user => user.is_reward_approved
);
export const selectEmailNewIsPending = createSelector(
_selectState,
(state) => state.emailNewIsPending
)
state => state.emailNewIsPending
);
export const selectEmailNewErrorMessage = createSelector(
_selectState,
(state) => state.emailNewErrorMessage
)
state => state.emailNewErrorMessage
);
export const selectEmailNewDeclined = createSelector(
_selectState,
(state) => state.emailNewDeclined
)
export const selectEmailNewExistingEmail = createSelector(
_selectState,
(state) => state.emailNewExistingEmail
)
state => state.emailNewDeclined
);
export const selectEmailVerifyIsPending = createSelector(
_selectState,
(state) => state.emailVerifyIsPending
)
state => state.emailVerifyIsPending
);
export const selectEmailVerifyErrorMessage = createSelector(
_selectState,
(state) => state.emailVerifyErrorMessage
)
state => state.emailVerifyErrorMessage
);
export const selectUserIsVerificationCandidate = createSelector(
selectUserIsRewardEligible,
selectUserIsRewardApproved,
selectEmailToVerify,
selectUser,
(isEligible, isApproved, emailToVerify, user) =>
(isEligible && !isApproved) || (emailToVerify && !user.has_email)
);
export const selectUserIsAuthRequested = createSelector(
selectEmailNewDeclined,
selectAuthenticationIsPending,
selectUserIsVerificationCandidate,
selectUserHasEmail,
(isEmailDeclined, isPending, isVerificationCandidate, hasEmail) =>
!isEmailDeclined && (isPending || !hasEmail || isVerificationCandidate)
);

View file

@ -25,7 +25,7 @@ $padding-card-horizontal: $spacing-vertical * 2/3;
}
.card__title-primary {
padding: 0 $padding-card-horizontal;
margin-top: $spacing-vertical;
margin-top: $spacing-vertical * 2/3;
}
.card__title-identity {
padding: 0 $padding-card-horizontal;