diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d265b9d6..85a98bc7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,11 @@ Web UI version numbers should always match the corresponding version of LBRY App ## [Unreleased] ### Added * ShapeShift integration - * ### Changed + * The first run process for new users has changed substantially. New users can now easily receive one credit. + * The wallet area has been re-organized. Send and Receive are now on the same page. A new page, "Get Credits", explains how users can add LBRY credits to the app. + * The prompt for an insufficient balance is much more user-friendly. * The credit balance displayed in the main app navigation displays two decimal places instead of one. * Moved all redux code into /redux folder * Channel names in pages are highlighted to indicate them being clickable(#814) @@ -27,7 +29,7 @@ Web UI version numbers should always match the corresponding version of LBRY App * ### Deprecated - * + * We previous two separate modals for insufficient credits. These have been combined. * ### Removed diff --git a/src/renderer/component/app/view.jsx b/src/renderer/component/app/view.jsx index edc9fa2dc..11e66e252 100644 --- a/src/renderer/component/app/view.jsx +++ b/src/renderer/component/app/view.jsx @@ -3,7 +3,7 @@ import Router from "component/router/index"; import Header from "component/header"; import Theme from "component/theme"; import ModalRouter from "modal/modalRouter"; -import lbry from "lbry"; +import ReactModal from "react-modal"; import throttle from "util/throttle"; class App extends React.PureComponent { @@ -28,6 +28,8 @@ class App extends React.PureComponent { const scrollListener = () => recordScroll(this.mainContent.scrollTop); this.mainContent.addEventListener("scroll", throttle(scrollListener, 750)); + + ReactModal.setAppElement("#window"); //fuck this } componentWillUnmount() { diff --git a/src/renderer/component/common.js b/src/renderer/component/common.js index 2f8ae5489..2be86b00b 100644 --- a/src/renderer/component/common.js +++ b/src/renderer/component/common.js @@ -69,7 +69,7 @@ export class CreditAmount extends React.PureComponent { amount: PropTypes.number.isRequired, precision: PropTypes.number, isEstimate: PropTypes.bool, - label: PropTypes.bool, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), showFree: PropTypes.bool, showFullPrice: PropTypes.bool, showPlus: PropTypes.bool, @@ -106,10 +106,12 @@ export class CreditAmount extends React.PureComponent { amountText = __("free"); } else { if (this.props.label) { - amountText = - formattedAmount + - " " + - (parseFloat(amount) == 1 ? __("credit") : __("credits")); + const label = + typeof this.props.label === "string" + ? this.props.label + : parseFloat(amount) == 1 ? __("credit") : __("credits"); + + amountText = formattedAmount + " " + label; } else { amountText = formattedAmount; } diff --git a/src/renderer/component/publishForm/view.jsx b/src/renderer/component/publishForm/view.jsx index deb16e248..5a82737d9 100644 --- a/src/renderer/component/publishForm/view.jsx +++ b/src/renderer/component/publishForm/view.jsx @@ -882,6 +882,7 @@ class PublishForm extends React.PureComponent { !this.state.submitting ? __("Publish") : __("Publishing...") } disabled={ + this.props.balance <= 0 || this.state.submitting || (this.state.uri && this.props.resolvingUris.indexOf(this.state.uri) !== -1) || diff --git a/src/renderer/component/rewardSummary/view.jsx b/src/renderer/component/rewardSummary/view.jsx index 3c97c50bb..cf4401054 100644 --- a/src/renderer/component/rewardSummary/view.jsx +++ b/src/renderer/component/rewardSummary/view.jsx @@ -20,7 +20,11 @@ const RewardSummary = props => { )}
- +
); diff --git a/src/renderer/component/router/view.jsx b/src/renderer/component/router/view.jsx index ed3c5f7bf..1c594783e 100644 --- a/src/renderer/component/router/view.jsx +++ b/src/renderer/component/router/view.jsx @@ -3,8 +3,8 @@ import SettingsPage from "page/settings"; import HelpPage from "page/help"; import ReportPage from "page/report.js"; import WalletPage from "page/wallet"; -import ReceiveCreditsPage from "page/receiveCredits"; -import SendCreditsPage from "page/sendCredits"; +import GetCreditsPage from "../../page/getCredits"; +import SendReceivePage from "page/sendCredits"; import ShowPage from "page/show"; import PublishPage from "page/publish"; import DiscoverPage from "page/discover"; @@ -38,11 +38,11 @@ const Router = props => { invite: , publish: , published: , - receive: , + getcredits: , report: , rewards: , search: , - send: , + send: , settings: , show: , wallet: , diff --git a/src/renderer/component/userEmailNew/index.js b/src/renderer/component/userEmailNew/index.js index 2efc29794..f5ddbd8e9 100644 --- a/src/renderer/component/userEmailNew/index.js +++ b/src/renderer/component/userEmailNew/index.js @@ -6,10 +6,15 @@ import { selectEmailNewErrorMessage, } from "redux/selectors/user"; import UserEmailNew from "./view"; +import rewards from "rewards"; +import { makeSelectRewardAmountByType } from "redux/selectors/rewards"; const select = state => ({ isPending: selectEmailNewIsPending(state), errorMessage: selectEmailNewErrorMessage(state), + rewardAmount: makeSelectRewardAmountByType()(state, { + reward_type: rewards.TYPE_CONFIRM_EMAIL, + }), }); const perform = dispatch => ({ diff --git a/src/renderer/component/userEmailNew/view.jsx b/src/renderer/component/userEmailNew/view.jsx index 5b34e62e6..8f6c9a434 100644 --- a/src/renderer/component/userEmailNew/view.jsx +++ b/src/renderer/component/userEmailNew/view.jsx @@ -1,5 +1,5 @@ import React from "react"; -import Link from "component/link"; +import { CreditAmount } from "component/common"; import { Form, FormRow, Submit } from "component/form.js"; class UserEmailNew extends React.PureComponent { @@ -23,35 +23,41 @@ class UserEmailNew extends React.PureComponent { } render() { - const { errorMessage, isPending } = this.props; + const { cancelButton, errorMessage, isPending, rewardAmount } = this.props; return ( -
+

- {__( - "This process is required to prevent abuse of the rewards program." - )} + Let us know your email and you'll receive{" "} + , the blockchain + token used by LBRY.

{__( - "We will also contact you about updates and new content, but you can unsubscribe at any time." + "We'll also let you know about LBRY updates, security issues, and great new content." )}

- { - this.handleEmailChanged(event); - }} - /> -
- -
- +

+ {__("We'll never sell your email, and you can unsubscribe at any time.")} +

+
+ { + this.handleEmailChanged(event); + }} + /> +
+ + {cancelButton} +
+ +
); } } diff --git a/src/renderer/component/userEmailVerify/index.js b/src/renderer/component/userEmailVerify/index.js index d267c1c89..e0fa0902b 100644 --- a/src/renderer/component/userEmailVerify/index.js +++ b/src/renderer/component/userEmailVerify/index.js @@ -7,11 +7,16 @@ import { selectEmailVerifyErrorMessage, } from "redux/selectors/user"; import UserEmailVerify from "./view"; +import rewards from "rewards"; +import { makeSelectRewardAmountByType } from "redux/selectors/rewards"; const select = state => ({ isPending: selectEmailVerifyIsPending(state), email: selectEmailToVerify(state), errorMessage: selectEmailVerifyErrorMessage(state), + rewardAmount: makeSelectRewardAmountByType()(state, { + reward_type: rewards.TYPE_CONFIRM_EMAIL, + }), }); const perform = dispatch => ({ diff --git a/src/renderer/component/userEmailVerify/view.jsx b/src/renderer/component/userEmailVerify/view.jsx index 1ec92202e..2e330acbe 100644 --- a/src/renderer/component/userEmailVerify/view.jsx +++ b/src/renderer/component/userEmailVerify/view.jsx @@ -1,5 +1,6 @@ import React from "react"; import Link from "component/link"; +import { CreditAmount } from "component/common"; import { Form, FormRow, Submit } from "component/form.js"; class UserEmailVerify extends React.PureComponent { @@ -23,10 +24,16 @@ class UserEmailVerify extends React.PureComponent { } render() { - const { errorMessage, isPending } = this.props; + const { + cancelButton, + errorMessage, + email, + isPending, + rewardAmount, + } = this.props; return (
-

{__("Please enter the verification code emailed to you.")}

+

Please enter the verification code emailed to {email}.

{__("Email")}{" "} - {" "} + or join our{" "} + {" "} {__("if you encounter any trouble with your code.")}

-
- +
+ + {cancelButton}
); diff --git a/src/renderer/component/userVerify/view.jsx b/src/renderer/component/userVerify/view.jsx index 07b3b2ef0..7e62abde3 100644 --- a/src/renderer/component/userVerify/view.jsx +++ b/src/renderer/component/userVerify/view.jsx @@ -79,9 +79,16 @@ class UserVerify extends React.PureComponent {

{__( - "If you have a YouTube account with subscribers and views, you can sync your account to be granted instant verification." + "If you have a YouTube account with subscribers and views, you can sync your account and content to be granted instant verification." )}

+

+ {__("Some account minimums apply.")}{" "} + +

{ )}
+
diff --git a/src/renderer/constants/modal_types.js b/src/renderer/constants/modal_types.js index d68349d44..3781ca220 100644 --- a/src/renderer/constants/modal_types.js +++ b/src/renderer/constants/modal_types.js @@ -6,11 +6,10 @@ export const ERROR = "error"; export const INSUFFICIENT_CREDITS = "insufficient_credits"; export const UPGRADE = "upgrade"; export const WELCOME = "welcome"; +export const EMAIL_COLLECTION = "email_collection"; export const FIRST_REWARD = "first_reward"; export const AUTHENTICATION_FAILURE = "auth_failure"; export const TRANSACTION_FAILED = "transaction_failed"; -export const INSUFFICIENT_BALANCE = "insufficient_balance"; export const REWARD_APPROVAL_REQUIRED = "reward_approval_required"; export const AFFIRM_PURCHASE = "affirm_purchase"; -export const CREDIT_INTRO = "credit_intro"; export const CONFIRM_CLAIM_REVOKE = "confirmClaimRevoke"; diff --git a/src/renderer/constants/settings.js b/src/renderer/constants/settings.js index 9346cfb70..c6c0ebd86 100644 --- a/src/renderer/constants/settings.js +++ b/src/renderer/constants/settings.js @@ -1,8 +1,9 @@ /*hardcoded names still exist for these in reducers/settings.js - only discovered when debugging*/ /*Many settings are stored in the localStorage by their name - be careful about changing the value of a settings constant, as doing so can invalidate existing settings*/ -export const CREDIT_INTRO_ACKNOWLEDGED = "credit_intro_acknowledged"; +export const CREDIT_REQUIRED_ACKNOWLEDGED = "credit_required_acknowledged"; export const NEW_USER_ACKNOWLEDGED = "welcome_acknowledged"; +export const EMAIL_COLLECTION_ACKNOWLEDGED = "email_collection_acknowledged"; export const LANGUAGE = "language"; export const SHOW_NSFW = "showNsfw"; export const SHOW_UNAVAILABLE = "showUnavailable"; diff --git a/src/renderer/main.js b/src/renderer/main.js index defd3c686..155278bda 100644 --- a/src/renderer/main.js +++ b/src/renderer/main.js @@ -61,7 +61,7 @@ document.addEventListener("click", event => { if (target.matches("a") || target.matches("button")) { // TODO: Look into using accessiblity labels (this would also make the app more accessible) let hrefParts = window.location.href.split("#"); - let element = target.title || target.text.trim(); + let element = target.title || (target.text && target.text.trim()); if (element) { amplitude.getInstance().logEvent("CLICK", { target: element, diff --git a/src/renderer/modal/modalCreditIntro/index.js b/src/renderer/modal/modalCreditIntro/index.js index b07a868b7..b212a3900 100644 --- a/src/renderer/modal/modalCreditIntro/index.js +++ b/src/renderer/modal/modalCreditIntro/index.js @@ -1,7 +1,7 @@ import React from "react"; import { connect } from "react-redux"; import { doCloseModal } from "redux/actions/app"; -import { doAuthNavigate } from "redux/actions/navigation"; +import { doNavigate } from "redux/actions/navigation"; import { doSetClientSetting } from "redux/actions/settings"; import { selectUserIsRewardApproved } from "redux/selectors/user"; import { selectBalance } from "redux/selectors/wallet"; @@ -25,17 +25,16 @@ const select = (state, props) => { }; const perform = dispatch => () => { - const closeModal = () => { - dispatch(doSetClientSetting(settings.CREDIT_INTRO_ACKNOWLEDGED, true)); - dispatch(doCloseModal()); - }; - return { - verifyAccount: () => { - closeModal(); - dispatch(doAuthNavigate("/discover")); + addBalance: () => { + dispatch(doSetClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED, true)); + dispatch(doNavigate("/getcredits")); + dispatch(doCloseModal()); + }, + closeModal: () => { + dispatch(doSetClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED, true)); + dispatch(doCloseModal()); }, - closeModal: closeModal, }; }; diff --git a/src/renderer/modal/modalCreditIntro/view.jsx b/src/renderer/modal/modalCreditIntro/view.jsx index c7db6127a..b44fe9b77 100644 --- a/src/renderer/modal/modalCreditIntro/view.jsx +++ b/src/renderer/modal/modalCreditIntro/view.jsx @@ -4,55 +4,42 @@ import { CreditAmount, CurrencySymbol } from "component/common"; import Link from "component/link/index"; const ModalCreditIntro = props => { - const { closeModal, totalRewardValue, currentBalance, verifyAccount } = props; + const { closeModal, totalRewardValue, currentBalance, addBalance } = props; const totalRewardRounded = Math.round(totalRewardValue / 10) * 10; return (
-

{__("Blockchain 101")}

+

+ {__("Computer Wizard Needs Tokens Badly")} +

- LBRY is controlled and powered by a blockchain asset called{" "} - + Some actions require{" "} LBRY credits + ( - . {" "} - {__( - "is used to publish content, to have a say in the network rules, and to access paid content." - )} + ), the blockchain token that powers the LBRY network.

- {currentBalance <= 0 ? ( -
-

- You currently have , so - the actions you can take are limited. -

-

- However, there are a variety of ways to get credits, including - more than{" "} - {totalRewardValue ? ( - - ) : ( - {__("?? credits")} - )}{" "} - {__( - " in rewards available for being a proven human during the LBRY beta." - )} -

-
- ) : ( -
-

- But you probably knew this, since you've already got{" "} - . -

-
+ {currentBalance <= 0 && ( +

+ You currently have , so the + actions you can take are limited. +

)} +

+ There are a variety of ways to get credits, including more than{" "} + {totalRewardValue ? ( + + ) : ( + {__("?? credits")} + )}{" "} + {__(" in free rewards for participating in the LBRY beta.")} +

({ + email: selectEmailToVerify(state), + user: selectUser(state), +}); + +const perform = dispatch => () => ({ + closeModal: () => { + dispatch(doSetClientSetting(settings.EMAIL_COLLECTION_ACKNOWLEDGED, true)); + dispatch(doCloseModal()); + }, +}); + +export default connect(select, perform)(ModalEmailCollection); diff --git a/src/renderer/modal/modalEmailCollection/view.jsx b/src/renderer/modal/modalEmailCollection/view.jsx new file mode 100644 index 000000000..8c0c40555 --- /dev/null +++ b/src/renderer/modal/modalEmailCollection/view.jsx @@ -0,0 +1,45 @@ +import React from "react"; +import { Modal } from "modal/modal"; +import Link from "component/link/index"; +import UserEmailNew from "component/userEmailNew"; +import UserEmailVerify from "component/userEmailVerify"; + +class ModalEmailCollection extends React.PureComponent { + renderInner() { + const { closeModal, email, user } = this.props; + + const cancelButton = ( + + ); + + if (!user.has_verified_email && !email) { + return ; + } else if (!user.has_verified_email) { + return ; + } else { + closeModal(); + } + } + + render() { + const { user } = this.props; + + //this shouldn't happen + if (!user) { + return null; + } + + return ( + +
+

+ Can We Touch You Stay In Touch? +

+ {this.renderInner()} +
+
+ ); + } +} + +export default ModalEmailCollection; diff --git a/src/renderer/modal/modalFirstReward/view.jsx b/src/renderer/modal/modalFirstReward/view.jsx index 05fb0c0e0..0744f44f7 100644 --- a/src/renderer/modal/modalFirstReward/view.jsx +++ b/src/renderer/modal/modalFirstReward/view.jsx @@ -32,12 +32,7 @@ class ModalFirstReward extends React.PureComponent {

{__( - "No need to understand it all just yet! Try watching or downloading something next." - )} -

-

- {__( - "Finally, please know that LBRY is an early beta and that it earns the name." + "No need to understand it all just yet! Try watching or publishing something next." )}

diff --git a/src/renderer/modal/modalInsufficientBalance/index.js b/src/renderer/modal/modalInsufficientBalance/index.js deleted file mode 100644 index 9ffcc3342..000000000 --- a/src/renderer/modal/modalInsufficientBalance/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import { doCloseModal } from "redux/actions/app"; -import { doNavigate } from "redux/actions/navigation"; -import ModalInsufficientBalance from "./view"; - -const select = state => ({}); - -const perform = dispatch => ({ - addBalance: () => { - dispatch(doNavigate("/wallet")); - dispatch(doCloseModal()); - }, - closeModal: () => dispatch(doCloseModal()), -}); - -export default connect(select, perform)(ModalInsufficientBalance); diff --git a/src/renderer/modal/modalInsufficientBalance/view.jsx b/src/renderer/modal/modalInsufficientBalance/view.jsx deleted file mode 100644 index 5b43583cd..000000000 --- a/src/renderer/modal/modalInsufficientBalance/view.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import { Modal } from "modal/modal"; - -class ModalInsufficientBalance extends React.PureComponent { - render() { - const { addBalance, closeModal } = this.props; - - return ( - - {__( - "Insufficient balance: after this transaction you would have less than 0 LBCs in your wallet." - )} - - ); - } -} - -export default ModalInsufficientBalance; diff --git a/src/renderer/modal/modalInsufficientCredits/index.js b/src/renderer/modal/modalInsufficientCredits/index.js deleted file mode 100644 index aea41dd13..000000000 --- a/src/renderer/modal/modalInsufficientCredits/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import { doCloseModal } from "redux/actions/app"; -import { doNavigate } from "redux/actions/navigation"; -import ModalInsufficientCredits from "./view"; - -const select = state => ({}); - -const perform = dispatch => ({ - addFunds: () => { - dispatch(doNavigate("/wallet")); - dispatch(doCloseModal()); - }, - closeModal: () => dispatch(doCloseModal()), -}); - -export default connect(select, perform)(ModalInsufficientCredits); diff --git a/src/renderer/modal/modalInsufficientCredits/view.jsx b/src/renderer/modal/modalInsufficientCredits/view.jsx deleted file mode 100644 index 7da75a7dd..000000000 --- a/src/renderer/modal/modalInsufficientCredits/view.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; -import { Modal } from "modal/modal"; -import { CurrencySymbol } from "component/common"; - -class ModalInsufficientCredits extends React.PureComponent { - render() { - const { addFunds, closeModal } = this.props; - - return ( - -

{__("More Credits Required")}

-

- You'll need more to do this. -

-
- ); - } -} - -export default ModalInsufficientCredits; diff --git a/src/renderer/modal/modalRouter/index.js b/src/renderer/modal/modalRouter/index.js index 9b4f4d00d..0b00c82a9 100644 --- a/src/renderer/modal/modalRouter/index.js +++ b/src/renderer/modal/modalRouter/index.js @@ -6,8 +6,12 @@ import { selectCurrentModal, selectModalProps } from "redux/selectors/app"; import { selectCurrentPage } from "redux/selectors/navigation"; import { selectCostForCurrentPageUri } from "redux/selectors/cost_info"; import { makeSelectClientSetting } from "redux/selectors/settings"; -import { selectUser } from "redux/selectors/user"; +import { + selectUser, + selectUserIsVerificationCandidate, +} from "redux/selectors/user"; import { selectBalance } from "redux/selectors/wallet"; + import ModalRouter from "./view"; const select = (state, props) => ({ @@ -16,12 +20,16 @@ const select = (state, props) => ({ modal: selectCurrentModal(state), modalProps: selectModalProps(state), page: selectCurrentPage(state), + isVerificationCandidate: selectUserIsVerificationCandidate(state), + isCreditIntroAcknowledged: makeSelectClientSetting( + settings.CREDIT_REQUIRED_ACKNOWLEDGED + )(state), + isEmailCollectionAcknowledged: makeSelectClientSetting( + settings.EMAIL_COLLECTION_ACKNOWLEDGED + )(state), isWelcomeAcknowledged: makeSelectClientSetting( settings.NEW_USER_ACKNOWLEDGED )(state), - isCreditIntroAcknowledged: makeSelectClientSetting( - settings.CREDIT_INTRO_ACKNOWLEDGED - )(state), user: selectUser(state), }); diff --git a/src/renderer/modal/modalRouter/view.jsx b/src/renderer/modal/modalRouter/view.jsx index d7beb7d3d..6ffcb7151 100644 --- a/src/renderer/modal/modalRouter/view.jsx +++ b/src/renderer/modal/modalRouter/view.jsx @@ -2,7 +2,6 @@ import React from "react"; import ModalError from "modal/modalError"; import ModalAuthFailure from "modal/modalAuthFailure"; import ModalDownloading from "modal/modalDownloading"; -import ModalInsufficientCredits from "modal/modalInsufficientCredits"; import ModalUpgrade from "modal/modalUpgrade"; import ModalWelcome from "modal/modalWelcome"; import ModalFirstReward from "modal/modalFirstReward"; @@ -10,10 +9,10 @@ import ModalRewardApprovalRequired from "modal/modalRewardApprovalRequired"; import ModalCreditIntro from "modal/modalCreditIntro"; import ModalRemoveFile from "modal/modalRemoveFile"; import ModalTransactionFailed from "modal/modalTransactionFailed"; -import ModalInsufficientBalance from "modal/modalInsufficientBalance"; import ModalFileTimeout from "modal/modalFileTimeout"; import ModalAffirmPurchase from "modal/modalAffirmPurchase"; import ModalRevokeClaim from "modal/modalRevokeClaim"; +import ModalEmailCollection from "../modalEmailCollection"; import * as modals from "constants/modal_types"; class ModalRouter extends React.PureComponent { @@ -43,8 +42,8 @@ class ModalRouter extends React.PureComponent { const transitionModal = [ this.checkShowWelcome, + this.checkShowEmail, this.checkShowCreditIntro, - this.checkShowInsufficientCredits, ].reduce((acc, func) => { return !acc ? func.bind(this)(props) : acc; }, false); @@ -74,24 +73,30 @@ class ModalRouter extends React.PureComponent { } } - checkShowCreditIntro(props) { - const { page, isCreditIntroAcknowledged, user } = props; - + checkShowEmail(props) { + const { + isEmailCollectionAcknowledged, + isVerificationCandidate, + user, + } = props; if ( - !isCreditIntroAcknowledged && + !isEmailCollectionAcknowledged && + isVerificationCandidate && user && - !user.is_reward_approved && - (["rewards", "send", "receive", "publish", "wallet"].includes(page) || - this.isPaidShowPage(props)) + !user.has_verified_email ) { - return modals.CREDIT_INTRO; + return modals.EMAIL_COLLECTION; } } - checkShowInsufficientCredits(props) { - const { balance, page } = props; + checkShowCreditIntro(props) { + const { balance, page, isCreditIntroAcknowledged } = props; - if (balance <= 0 && ["send", "publish"].includes(page)) { + if ( + balance <= 0 && + !isCreditIntroAcknowledged && + (["send", "publish"].includes(page) || this.isPaidShowPage(props)) + ) { return modals.INSUFFICIENT_CREDITS; } } @@ -114,19 +119,15 @@ class ModalRouter extends React.PureComponent { case modals.FILE_TIMEOUT: return ; case modals.INSUFFICIENT_CREDITS: - return ; + return ; case modals.WELCOME: return ; case modals.FIRST_REWARD: return ; case modals.AUTHENTICATION_FAILURE: return ; - case modals.CREDIT_INTRO: - return ; case modals.TRANSACTION_FAILED: return ; - case modals.INSUFFICIENT_BALANCE: - return ; case modals.REWARD_APPROVAL_REQUIRED: return ; case modals.CONFIRM_FILE_REMOVE: @@ -135,6 +136,8 @@ class ModalRouter extends React.PureComponent { return ; case modals.CONFIRM_CLAIM_REVOKE: return ; + case modals.EMAIL_COLLECTION: + return ; default: return null; } diff --git a/src/renderer/page/getCredits/index.js b/src/renderer/page/getCredits/index.js new file mode 100644 index 000000000..a28ca3e0a --- /dev/null +++ b/src/renderer/page/getCredits/index.js @@ -0,0 +1,5 @@ +import React from "react"; +import { connect } from "react-redux"; +import GetCreditsPage from "./view"; + +export default connect(null, null)(GetCreditsPage); diff --git a/src/renderer/page/receiveCredits/view.jsx b/src/renderer/page/getCredits/view.jsx similarity index 62% rename from src/renderer/page/receiveCredits/view.jsx rename to src/renderer/page/getCredits/view.jsx index 5052b868e..cd214e628 100644 --- a/src/renderer/page/receiveCredits/view.jsx +++ b/src/renderer/page/getCredits/view.jsx @@ -1,15 +1,28 @@ import React from "react"; import SubHeader from "component/subHeader"; import Link from "component/link"; -import WalletAddress from "component/walletAddress"; +import RewardSummary from "component/rewardSummary"; import ShapeShift from "component/shapeShift"; -const ReceiveCreditsPage = props => { +const GetCreditsPage = props => { return (
- + +
+
+

{__("From External Wallet")}

+
+
+ +
+

{__("More ways to get LBRY Credits")}

@@ -33,4 +46,4 @@ const ReceiveCreditsPage = props => { ); }; -export default ReceiveCreditsPage; +export default GetCreditsPage; diff --git a/src/renderer/page/receiveCredits/index.js b/src/renderer/page/receiveCredits/index.js deleted file mode 100644 index 475548da7..000000000 --- a/src/renderer/page/receiveCredits/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import ReceiveCreditsPage from "./view"; - -export default connect(null, null)(ReceiveCreditsPage); diff --git a/src/renderer/page/sendCredits/index.js b/src/renderer/page/sendCredits/index.js index 2a36f2719..a03af086a 100644 --- a/src/renderer/page/sendCredits/index.js +++ b/src/renderer/page/sendCredits/index.js @@ -1,5 +1,5 @@ import React from "react"; import { connect } from "react-redux"; -import SendCreditsPage from "./view"; +import SendReceivePage from "./view"; -export default connect(null, null)(SendCreditsPage); +export default connect(null, null)(SendReceivePage); diff --git a/src/renderer/page/sendCredits/view.jsx b/src/renderer/page/sendCredits/view.jsx index 96937bcc6..c5d1b4ca8 100644 --- a/src/renderer/page/sendCredits/view.jsx +++ b/src/renderer/page/sendCredits/view.jsx @@ -1,14 +1,16 @@ import React from "react"; import SubHeader from "component/subHeader"; import WalletSend from "component/walletSend"; +import WalletAddress from "component/walletAddress"; -const SendCreditsPage = props => { +const SendReceivePage = props => { return (
+
); }; -export default SendCreditsPage; +export default SendReceivePage; diff --git a/src/renderer/redux/actions/rewards.js b/src/renderer/redux/actions/rewards.js index 3bdf140e8..4ed2332f3 100644 --- a/src/renderer/redux/actions/rewards.js +++ b/src/renderer/redux/actions/rewards.js @@ -4,6 +4,7 @@ import lbryio from "lbryio"; import rewards from "rewards"; import { selectUnclaimedRewardsByType } from "redux/selectors/rewards"; import { selectUserIsRewardApproved } from "redux/selectors/user"; +import { selectClaimedRewardsById } from "../selectors/rewards"; export function doRewardList() { return function(dispatch, getState) { @@ -42,7 +43,7 @@ export function doClaimRewardType(rewardType) { return; } - if (!userIsRewardApproved) { + if (!userIsRewardApproved && rewardType !== rewards.TYPE_CONFIRM_EMAIL) { return dispatch({ type: types.OPEN_MODAL, data: { modal: modals.REWARD_APPROVAL_REQUIRED }, @@ -61,7 +62,7 @@ export function doClaimRewardType(rewardType) { reward, }, }); - if (reward.reward_type == rewards.TYPE_NEW_USER) { + if (reward.reward_type == rewards.TYPE_CONFIRM_EMAIL) { dispatch({ type: types.OPEN_MODAL, data: { modal: modals.FIRST_REWARD }, diff --git a/src/renderer/redux/actions/user.js b/src/renderer/redux/actions/user.js index 5f1acf595..7111369e8 100644 --- a/src/renderer/redux/actions/user.js +++ b/src/renderer/redux/actions/user.js @@ -61,6 +61,22 @@ export function doUserEmailNew(email) { type: types.USER_EMAIL_NEW_STARTED, email: email, }); + + const success = () => { + dispatch({ + type: types.USER_EMAIL_NEW_SUCCESS, + data: { email }, + }); + dispatch(doUserFetch()); + } + + const failure = error => { + dispatch({ + type: types.USER_EMAIL_NEW_FAILURE, + data: { error }, + }); + } + lbryio .call( "user_email", @@ -75,23 +91,11 @@ export function doUserEmailNew(email) { "resend_token", { email: email, only_if_expired: true }, "post" - ); + ).then(success, failure); } throw error; }) - .then(() => { - dispatch({ - type: types.USER_EMAIL_NEW_SUCCESS, - data: { email }, - }); - dispatch(doUserFetch()); - }) - .catch(error => { - dispatch({ - type: types.USER_EMAIL_NEW_FAILURE, - data: { error }, - }); - }); + .then(success, failure); }; } @@ -118,7 +122,8 @@ export function doUserEmailVerify(verificationToken) { type: types.USER_EMAIL_VERIFY_SUCCESS, data: { email }, }); - dispatch(doUserFetch()); + dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL)), + dispatch(doUserFetch()); } else { throw new Error("Your email is still not verified."); //shouldn't happen } diff --git a/src/renderer/redux/actions/wallet.js b/src/renderer/redux/actions/wallet.js index fbd744ecb..09efea390 100644 --- a/src/renderer/redux/actions/wallet.js +++ b/src/renderer/redux/actions/wallet.js @@ -97,7 +97,7 @@ export function doSendDraftTransaction() { const amount = selectDraftTransactionAmount(state); if (balance - amount <= 0) { - return dispatch(doOpenModal(modals.INSUFFICIENT_BALANCE)); + return dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS)); } dispatch({ @@ -162,7 +162,7 @@ export function doSendSupport(amount, claim_id, uri) { const balance = selectBalance(state); if (balance - amount <= 0) { - return dispatch(doOpenModal(modals.INSUFFICIENT_BALANCE)); + return dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS)); } dispatch({ diff --git a/src/renderer/redux/reducers/settings.js b/src/renderer/redux/reducers/settings.js index 784b6aa8f..99d46a87d 100644 --- a/src/renderer/redux/reducers/settings.js +++ b/src/renderer/redux/reducers/settings.js @@ -24,9 +24,10 @@ const defaultState = { settings.NEW_USER_ACKNOWLEDGED, false ), - credit_intro_acknowledged: getLocalStorageSetting( - settings.CREDIT_INTRO_ACKNOWLEDGED + email_collection_acknowledged: getLocalStorageSetting( + settings.EMAIL_COLLECTION_ACKNOWLEDGED ), + credit_required_acknowledged: false, //this needs to be re-acknowledged every run language: getLocalStorageSetting(settings.LANGUAGE, "en"), theme: getLocalStorageSetting(settings.THEME, "light"), themes: getLocalStorageSetting(settings.THEMES, []), diff --git a/src/renderer/redux/selectors/navigation.js b/src/renderer/redux/selectors/navigation.js index 7633e81c2..7f78f7b13 100644 --- a/src/renderer/redux/selectors/navigation.js +++ b/src/renderer/redux/selectors/navigation.js @@ -37,17 +37,17 @@ export const selectHeaderLinks = createSelector(selectCurrentPage, page => { case "wallet": case "history": case "send": - case "receive": + case "getcredits": case "invite": case "rewards": case "backup": return { wallet: __("Overview"), - history: __("History"), - send: __("Send Credits"), - receive: __("Get Credits"), + getcredits: __("Get Credits"), + send: __("Send / Receive"), rewards: __("Rewards"), invite: __("Invites"), + history: __("History"), }; case "downloaded": case "published": @@ -78,8 +78,8 @@ export const selectPageTitle = createSelector( case "wallet": return __("Wallet"); case "send": - return __("Send LBRY Credits"); - case "receive": + return __("Send or Receive LBRY Credits"); + case "getcredits": return __("Get LBRY Credits"); case "backup": return __("Backup Your Wallet"); diff --git a/src/renderer/redux/selectors/search.js b/src/renderer/redux/selectors/search.js index fc78e50f4..259971a9e 100644 --- a/src/renderer/redux/selectors/search.js +++ b/src/renderer/redux/selectors/search.js @@ -63,9 +63,8 @@ export const selectWunderBarIcon = createSelector( return "icon-rocket"; case "invite": return "icon-envelope-open"; - case "address": - case "receive": - return "icon-credit-card"; + case "getcredits": + return "icon-shopping-cart"; case "wallet": case "backup": return "icon-bank"; diff --git a/src/renderer/redux/selectors/user.js b/src/renderer/redux/selectors/user.js index 43ac07413..8cc59bab0 100644 --- a/src/renderer/redux/selectors/user.js +++ b/src/renderer/redux/selectors/user.js @@ -14,16 +14,17 @@ export const selectUserIsPending = createSelector( export const selectUser = createSelector(_selectState, state => state.user); -export const selectEmailToVerify = createSelector( - _selectState, - state => state.emailToVerify -); - export const selectUserEmail = createSelector( selectUser, user => (user ? user.primary_email : null) ); +export const selectEmailToVerify = createSelector( + _selectState, + selectUserEmail, + (state, userEmail) => state.emailToVerify || userEmail +); + export const selectUserIsRewardApproved = createSelector( selectUser, user => user && user.is_reward_approved diff --git a/src/renderer/rewards.js b/src/renderer/rewards.js index 1482fc1ca..0fe0121e0 100644 --- a/src/renderer/rewards.js +++ b/src/renderer/rewards.js @@ -33,10 +33,7 @@ function rewardMessage(type, amount) { "You earned %s LBC for watching a featured download.", amount ), - referral: __( - "You earned %s LBC for referring someone.", - amount - ), + referral: __("You earned %s LBC for referring someone.", amount), }[type]; } @@ -44,7 +41,7 @@ const rewards = {}; rewards.TYPE_NEW_DEVELOPER = "new_developer"; rewards.TYPE_NEW_USER = "new_user"; -rewards.TYPE_CONFIRM_EMAIL = "confirm_email"; +rewards.TYPE_CONFIRM_EMAIL = "verified_email"; rewards.TYPE_FIRST_CHANNEL = "new_channel"; rewards.TYPE_FIRST_STREAM = "first_stream"; rewards.TYPE_MANY_DOWNLOADS = "many_downloads"; diff --git a/src/renderer/scss/_vars.scss b/src/renderer/scss/_vars.scss index 44c6087aa..b9e0f49e8 100644 --- a/src/renderer/scss/_vars.scss +++ b/src/renderer/scss/_vars.scss @@ -128,7 +128,7 @@ $text-color: #000; --card-small-width: $spacing-vertical * 10; /* Modal */ - --modal-width: 420px; + --modal-width: 440px; --modal-bg: var(--color-bg); --modal-overlay-bg: rgba(#F5F5F5, 0.75); // --color-canvas: #F5F5F5 --modal-border: 1px solid rgb(204, 204, 204);