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 (
-
-
{__("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.")}
{__(
- "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.")}
+
{__(
- "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 (
-
+
+
+