moderate progress

This commit is contained in:
Jeremy Kauffman 2017-07-16 12:29:46 -04:00
parent b3de4ceee9
commit 16abedbf3a
21 changed files with 228 additions and 78 deletions

View file

@ -8,13 +8,11 @@ import {
selectPageTitle, selectPageTitle,
selectCurrentPage, selectCurrentPage,
selectCurrentParams, selectCurrentParams,
selectWelcomeModalAcknowledged,
} from "selectors/app"; } from "selectors/app";
import { doSearch } from "actions/search"; import { doSearch } from "actions/search";
import { doFetchDaemonSettings } from "actions/settings"; import { doFetchDaemonSettings } from "actions/settings";
import { doAuthenticate } from "actions/user"; import { doAuthenticate } from "actions/user";
import { doFileList } from "actions/file_info"; import { doFileList } from "actions/file_info";
import * as modals from "constants/modal_types";
const { remote, ipcRenderer, shell } = require("electron"); const { remote, ipcRenderer, shell } = require("electron");
const path = require("path"); const path = require("path");
@ -220,17 +218,13 @@ export function doAlertError(errorList) {
} }
export function doDaemonReady() { export function doDaemonReady() {
return function(dispatch, getState) { return function(dispatch) {
const showWelcome = !selectWelcomeModalAcknowledged(getState());
dispatch(doAuthenticate()); dispatch(doAuthenticate());
dispatch({ dispatch({
type: types.DAEMON_READY, type: types.DAEMON_READY,
}); });
dispatch(doFetchDaemonSettings()); dispatch(doFetchDaemonSettings());
dispatch(doFileList()); dispatch(doFileList());
if (showWelcome) {
dispatch(doOpenModal(modals.WELCOME));
}
}; };
} }

View file

@ -1,18 +1,40 @@
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 { doCheckUpgradeAvailable, doAlertError } from "actions/app"; import {
doCheckUpgradeAvailable,
doOpenModal,
doAlertError,
} 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)),
}); });

View file

@ -11,17 +11,43 @@ 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);
}
componentWillReceiveProps(nextProps) {
this.showWelcome(nextProps);
}
showWelcome(props) {
const {
isFetchingRewards,
isWelcomeAcknowledged,
isWelcomeRewardClaimed,
openWelcomeModal,
user,
} = props;
if (
!isWelcomeAcknowledged &&
user &&
(!isFetchingRewards || !isWelcomeRewardClaimed)
) {
openWelcomeModal();
}
} }
render() { render() {

View file

@ -4,12 +4,14 @@ import {
selectAuthenticationIsPending, selectAuthenticationIsPending,
selectEmailToVerify, selectEmailToVerify,
selectUserIsVerificationCandidate, selectUserIsVerificationCandidate,
selectUser,
} from "selectors/user"; } from "selectors/user";
import Auth from "./view"; import Auth from "./view";
const select = state => ({ const select = state => ({
isPending: selectAuthenticationIsPending(state), isPending: selectAuthenticationIsPending(state),
email: selectEmailToVerify(state), email: selectEmailToVerify(state),
user: selectUser(state),
isVerificationCandidate: selectUserIsVerificationCandidate(state), isVerificationCandidate: selectUserIsVerificationCandidate(state),
}); });

View file

@ -2,17 +2,20 @@ import React from "react";
import { BusyMessage } from "component/common"; import { BusyMessage } from "component/common";
import UserEmailNew from "component/userEmailNew"; import UserEmailNew from "component/userEmailNew";
import UserEmailVerify from "component/userEmailVerify"; import UserEmailVerify from "component/userEmailVerify";
import UserVerify from "component/userVerify";
export class Auth extends React.PureComponent { export class Auth extends React.PureComponent {
render() { render() {
const { isPending, email, isVerificationCandidate } = this.props; const { email, isPending, isVerificationCandidate, user } = this.props;
if (isPending) { if (isPending) {
return <BusyMessage message={__("Authenticating")} />; return <BusyMessage message={__("Authenticating")} />;
} else if (!email) { } else if (user && !user.has_verified_email && !email) {
return <UserEmailNew />; return <UserEmailNew />;
} else if (isVerificationCandidate) { } else if (user && !user.has_verified_email) {
return <UserEmailVerify />; return <UserEmailVerify />;
} else if (user && !user.is_identity_verified) {
return <UserVerify />;
} else { } else {
return <span className="empty">{__("No further steps.")}</span>; return <span className="empty">{__("No further steps.")}</span>;
} }

View file

@ -54,7 +54,7 @@ export class AuthOverlay extends React.PureComponent {
? "" ? ""
: <div className="form-row-submit"> : <div className="form-row-submit">
{!hasEmail && this.state.showNoEmailConfirm {!hasEmail && this.state.showNoEmailConfirm
? <div className="help form-input-width"> ? <div>
<p> <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." "If you continue without an email, you will be ineligible to earn free LBC rewards, as well as unable to receive security related communications."

View file

@ -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 || ""}
/> />
); );
} }

View file

@ -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"),
}); });

View file

@ -31,8 +31,11 @@ class ModalWelcome extends React.PureComponent {
)} )}
</p> </p>
<p> <p>
{__("Thank you for making content freedom possible!")} {__("Please have")} {" "}
{" "}{isRewardApproved ? __("Here's a nickel, kid.") : ""} {reward &&
<CreditAmount amount={parseFloat(reward.reward_amount)} />}
{!reward && <span className="credit-amount">{__("??")}</span>}
{" "} {__("as a thank you for building content freedom.")}
</p> </p>
<div className="text-center"> <div className="text-center">
{isRewardApproved && {isRewardApproved &&
@ -40,15 +43,11 @@ class ModalWelcome extends React.PureComponent {
{!isRewardApproved && {!isRewardApproved &&
<Link <Link
button="primary" button="primary"
onClick={closeModal} onClick={verifyAccount}
label={__("Continue")} label={__("Get Welcome Credits")}
/>} />}
{!isRewardApproved && {!isRewardApproved &&
<Link <Link button="alt" onClick={closeModal} label={__("Skip")} />}
button="alt"
onClick={verifyAccount}
label={__("Do Account Thing")}
/>}
</div> </div>
</section> </section>
</Modal> </Modal>

View file

@ -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);
}} }}

View file

@ -27,7 +27,6 @@ class UserEmailVerify extends React.PureComponent {
return ( return (
<form <form
className="form-input-width"
onSubmit={event => { onSubmit={event => {
this.handleSubmit(event); this.handleSubmit(event);
}} }}

View file

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

View file

@ -0,0 +1,69 @@
import React from "react";
import Link from "component/link";
import { FormRow } from "component/form.js";
class UserVerify extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
code: "",
};
}
handleCodeChanged(event) {
this.setState({
code: event.target.value,
});
}
handleSubmit(event) {
event.preventDefault();
this.props.verifyUserEmail(this.state.code);
}
render() {
const { errorMessage, isPending } = this.props;
return <p>VERIFY</p>;
return (
<form
onSubmit={event => {
this.handleSubmit(event);
}}
>
zzzzzzzzzzzzzzzzzzzzzzzzzzzzz
<FormRow
type="text"
label={__("Verification Code")}
placeholder="a94bXXXXXXXXXXXXXX"
name="code"
value={this.state.code}
onChange={event => {
this.handleCodeChanged(event);
}}
errorMessage={errorMessage}
/>
{/* render help separately so it always shows */}
<div className="form-field__helper">
<p>
{__("Email")}{" "}
<Link href="mailto:help@lbry.io" label="help@lbry.io" />{" "}
{__("if you did not receive or are having trouble with your code.")}
</p>
</div>
<div className="form-row-submit form-row-submit--with-footer">
<Link
button="primary"
label={__("Verify")}
disabled={this.state.submitting}
onClick={event => {
this.handleSubmit(event);
}}
/>
</div>
</form>
);
}
}
export default UserVerify;

View file

@ -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", "-");

View file

@ -1,22 +1,31 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { doNavigate } from "actions/app"; import {
import { selectFetchingRewards, selectRewards } from "selectors/rewards"; makeSelectRewardByType,
selectFetchingRewards,
selectRewards,
} from "selectors/rewards";
import { import {
selectUserIsRewardEligible, selectUserIsRewardEligible,
selectUserHasEmail, selectUserHasEmail,
selectUserIsVerificationCandidate, selectUserIsVerificationCandidate,
} from "selectors/user"; } from "selectors/user";
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),
}); hasEmail: selectUserHasEmail(state),
isEligible: selectUserIsRewardEligible(state),
isVerificationCandidate: selectUserIsVerificationCandidate(state),
newUserReward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
};
};
const perform = dispatch => ({ const perform = dispatch => ({
fetchRewards: () => dispatch(doRewardList()), fetchRewards: () => dispatch(doRewardList()),

View file

@ -1,9 +1,7 @@
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 Auth from "component/auth";
import Link from "component/link";
import RewardLink from "component/rewardLink"; import RewardLink from "component/rewardLink";
const RewardTile = props => { const RewardTile = props => {
@ -41,7 +39,9 @@ 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() {
@ -51,6 +51,7 @@ class RewardsPage extends React.PureComponent {
isVerificationCandidate, isVerificationCandidate,
hasEmail, hasEmail,
rewards, rewards,
newUserReward,
} = this.props; } = this.props;
let content, let content,
@ -59,42 +60,55 @@ class RewardsPage extends React.PureComponent {
if (!hasEmail || isVerificationCandidate) { if (!hasEmail || isVerificationCandidate) {
content = ( content = (
<div> <div>
<p> <div className="card__title-primary">
{__( {newUserReward &&
"Additional information is required to be eligible for the rewards program." <CreditAmount amount={newUserReward.reward_amount} />}
)} <h3>Welcome to LBRY</h3>
</p> </div>
<Auth /> <div className="card__content">
<p>
{" "}{__(
"Claim your welcome credits to be able to publish content, pay creators, and have a say over the LBRY network."
)}
</p>
</div>
<div className="card__content"><Auth /></div>
</div> </div>
); );
isCard = true; isCard = true;
} else if (!isEligible) { } else if (!isEligible) {
isCard = true; isCard = true;
content = ( content = (
<div className="empty"> <div className="card__content empty">
<p>{__("You are not eligible to claim rewards.")}</p> <p>{__("You are not eligible to claim rewards.")}</p>
</div> </div>
); );
} else if (fetching) { } else if (fetching) {
content = <BusyMessage message={__("Fetching rewards")} />; content = (
<div className="card__content">
<BusyMessage message={__("Fetching rewards")} />
</div>
);
} else if (rewards.length > 0) { } else if (rewards.length > 0) {
content = rewards.map(reward => content = (
<RewardTile key={reward.reward_type} reward={reward} /> <div>
{rewards.map(reward =>
<RewardTile key={reward.reward_type} reward={reward} />
)}
</div>
); );
} else { } else {
content = <div className="empty">{__("Failed to load rewards.")}</div>; content = (
<div className="card__content empty">
{__("Failed to load rewards.")}
</div>
);
} }
return ( return (
<main className="main--single-column"> <main className="main--single-column">
<SubHeader /> <SubHeader />
{isCard {isCard ? <section className="card">{content}</section> : content}
? <section className="card">
<div className="card__content">
{content}
</div>
</section>
: content}
</main> </main>
); );
} }

View file

@ -12,10 +12,7 @@ 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,
@ -65,7 +62,7 @@ export const selectEmailVerifyErrorMessage = createSelector(
export const selectUserIsVerificationCandidate = createSelector( export const selectUserIsVerificationCandidate = createSelector(
selectUser, selectUser,
user => user && !user.has_verified_email user => user && (!user.has_verified_email || !user.is_identity_verified)
); );
export const selectUserIsAuthRequested = createSelector( export const selectUserIsAuthRequested = createSelector(

View file

@ -29,3 +29,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+$/, "");
}

View file

@ -3,10 +3,6 @@
$width-input-border: 2px; $width-input-border: 2px;
$width-input-text: 330px; $width-input-text: 330px;
.form-input-width {
width: $width-input-text
}
.form-row-submit .form-row-submit
{ {
margin-top: $spacing-vertical; margin-top: $spacing-vertical;