merging is hard

releasable?
This commit is contained in:
Jeremy Kauffman 2017-08-25 23:21:26 -04:00
parent 2fb0bf4a14
commit c7522dc0a5
46 changed files with 519 additions and 276 deletions

View file

@ -26,6 +26,10 @@ const { lbrySettings: config } = require("../../../app/package.json");
export function doNavigate(path, params = {}, options = {}) { export function doNavigate(path, params = {}, options = {}) {
return function(dispatch, getState) { return function(dispatch, getState) {
if (!path) {
return;
}
let url = path; let url = path;
if (params) url = `${url}?${toQueryString(params)}`; if (params) url = `${url}?${toQueryString(params)}`;

View file

@ -3,6 +3,7 @@ import * as modals from "constants/modal_types";
import lbryio from "lbryio"; import lbryio from "lbryio";
import rewards from "rewards"; import rewards from "rewards";
import { selectUnclaimedRewardsByType } from "selectors/rewards"; import { selectUnclaimedRewardsByType } from "selectors/rewards";
import { selectUserIsRewardApproved } from "selectors/user";
export function doRewardList() { export function doRewardList() {
return function(dispatch, getState) { return function(dispatch, getState) {
@ -31,14 +32,23 @@ export function doRewardList() {
export function doClaimRewardType(rewardType) { export function doClaimRewardType(rewardType) {
return function(dispatch, getState) { return function(dispatch, getState) {
const rewardsByType = selectUnclaimedRewardsByType(getState()), const state = getState(),
reward = rewardsByType[rewardType]; rewardsByType = selectUnclaimedRewardsByType(state),
reward = rewardsByType[rewardType],
userIsRewardApproved = selectUserIsRewardApproved(state);
if (reward.transaction_id) { if (reward.transaction_id) {
//already claimed, do nothing //already claimed, do nothing
return; return;
} }
if (!userIsRewardApproved) {
return dispatch({
type: types.OPEN_MODAL,
data: { modal: modals.REWARD_APPROVAL_REQUIRED },
});
}
dispatch({ dispatch({
type: types.CLAIM_REWARD_STARTED, type: types.CLAIM_REWARD_STARTED,
data: { reward }, data: { reward },

View file

@ -1,7 +1,6 @@
import * as types from "constants/action_types"; import * as types from "constants/action_types";
import * as modals from "constants/modal_types"; import * as modals from "constants/modal_types";
import lbryio from "lbryio"; import lbryio from "lbryio";
import { doOpenModal } from "actions/app";
import { doOpenModal, doShowSnackBar } from "actions/app"; import { doOpenModal, doShowSnackBar } from "actions/app";
import { doRewardList, doClaimRewardType } from "actions/rewards"; import { doRewardList, doClaimRewardType } from "actions/rewards";
import { selectEmailToVerify, selectUser } from "selectors/user"; import { selectEmailToVerify, selectUser } from "selectors/user";

View file

@ -165,7 +165,7 @@ class CardVerify extends React.Component {
render() { render() {
return ( return (
<Link <Link
button="primary" button="alt"
label={this.props.label} label={this.props.label}
icon="icon-lock" icon="icon-lock"
disabled={ disabled={

View file

@ -167,18 +167,20 @@ class FileActions extends React.PureComponent {
<section className="file-actions"> <section className="file-actions">
{content} {content}
{showMenu {showMenu
? <DropDownMenu> ? <div className="button-set-item">
<DropDownMenuItem <DropDownMenu>
key={0} <DropDownMenuItem
onClick={() => openInFolder(fileInfo)} key={0}
label={openInFolderMessage} onClick={() => openInFolder(fileInfo)}
/> label={openInFolderMessage}
<DropDownMenuItem />
key={1} <DropDownMenuItem
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE)} key={1}
label={__("Remove...")} onClick={() => openModal(modals.CONFIRM_FILE_REMOVE)}
/> label={__("Remove...")}
</DropDownMenu> />
</DropDownMenu>
</div>
: ""} : ""}
<Modal <Modal
type="confirm" type="confirm"

View file

@ -47,11 +47,14 @@ class InviteList extends React.PureComponent {
<td className="text-center"> <td className="text-center">
{invitee.invite_reward_claimed {invitee.invite_reward_claimed
? <Icon icon="icon-check" /> ? <Icon icon="icon-check" />
: <RewardLink : invitee.invite_accepted
label={__("Claim")} ? <RewardLink
button="text" label={__("Claim")}
reward_type={rewards.TYPE_FIRST_PUBLISH} reward_type={rewards.TYPE_FIRST_PUBLISH}
/>} />
: <span className="empty">
{__("unclaimable")}
</span>}
</td> </td>
</tr> </tr>
); );

View file

@ -6,13 +6,21 @@ import {
selectUserInviteNewIsPending, selectUserInviteNewIsPending,
selectUserInviteNewErrorMessage, selectUserInviteNewErrorMessage,
} from "selectors/user"; } from "selectors/user";
import rewards from "rewards";
import { makeSelectRewardAmountByType } from "selectors/rewards";
import { doUserInviteNew } from "actions/user"; import { doUserInviteNew } from "actions/user";
const select = state => ({ const select = state => {
errorMessage: selectUserInviteNewErrorMessage(state), const selectReward = makeSelectRewardAmountByType();
invitesRemaining: selectUserInvitesRemaining(state),
isPending: selectUserInviteNewIsPending(state), return {
}); errorMessage: selectUserInviteNewErrorMessage(state),
invitesRemaining: selectUserInvitesRemaining(state),
isPending: selectUserInviteNewIsPending(state),
rewardAmount: selectReward(state, { reward_type: rewards.TYPE_REFERRAL }),
};
};
const perform = dispatch => ({ const perform = dispatch => ({
inviteNew: email => dispatch(doUserInviteNew(email)), inviteNew: email => dispatch(doUserInviteNew(email)),

View file

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { BusyMessage } from "component/common"; import { BusyMessage, CreditAmount } from "component/common";
import Link from "component/link"; import Link from "component/link";
import { FormRow } from "component/form.js"; import { FormRow } from "component/form.js";
@ -62,15 +62,15 @@ class InviteNew extends React.PureComponent {
inviteNew, inviteNew,
inviteStatusIsPending, inviteStatusIsPending,
isPending, isPending,
rewardAmount,
} = this.props; } = this.props;
return ( return (
<section className="card"> <section className="card">
<div className="card__title-primary"> <div className="card__title-primary">
<CreditAmount amount={rewardAmount} />
<h3> <h3>
{__( {__("Invite a Friend")}
"Invite a Friend (or Enemy) (or Someone You Are Somewhat Ambivalent About)"
)}
</h3> </h3>
</div> </div>
{/* {/*
@ -81,6 +81,11 @@ class InviteNew extends React.PureComponent {
<p className="empty">{__("You have no invites.")}</p>} <p className="empty">{__("You have no invites.")}</p>}
</div> */} </div> */}
<div className="card__content"> <div className="card__content">
<p>
{__(
"Or an enemy. Or your cousin Jerry, who you're kind of unsure about."
)}
</p>
<FormInviteNew <FormInviteNew
errorMessage={errorMessage} errorMessage={errorMessage}
inviteNew={inviteNew} inviteNew={inviteNew}

View file

@ -1,14 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import {
selectUserInvitesRemaining,
selectUserInviteNewIsPending,
} from "selectors/user";
import InviteSummary from "./view";
const select = state => ({
invitesRemaining: selectUserInvitesRemaining(state),
isPending: selectUserInviteNewIsPending(state),
});
export default connect(select)(InviteSummary);

View file

@ -1,35 +0,0 @@
import React from "react";
import Link from "component/link";
import { CreditAmount, BusyMessage } from "component/common";
const InviteSummary = props => {
const { isPending, invitesRemaining } = props;
return (
<section className="card">
<div className="card__title-primary">
<h3>{__("Invites")}</h3>
</div>
<div className="card__content">
{isPending && <BusyMessage message={__("Checking invite status")} />}
{!isPending &&
<p>
{__n(
"You have %d invite remaining.",
"You have %d invites remaining.",
invitesRemaining
)}
</p>}
</div>
<div className="card__content">
<Link
button={invitesRemaining > 0 ? "primary" : "text"}
navigate="/invite"
label={__("Go To Invites")}
/>
</div>
</section>
);
};
export default InviteSummary;

View file

@ -22,11 +22,11 @@ const Link = props => {
(button ? " button-block button-" + button + " button-set-item" : "") + (button ? " button-block button-" + button + " button-set-item" : "") +
(disabled ? " disabled" : ""); (disabled ? " disabled" : "");
const onClick = props.onClick const onClick = !props.onClick && navigate
? props.onClick ? () => {
: () => {
doNavigate(navigate); doNavigate(navigate);
}; }
: props.onClick;
let content; let content;
if (children) { if (children) {

View file

@ -16,7 +16,7 @@ const RewardLink = props => {
return ( return (
<div className="reward-link"> <div className="reward-link">
<Link <Link
button={button ? button : "alt"} button={button}
disabled={isPending} disabled={isPending}
label={ label={
isPending ? __("Claiming...") : label ? label : __("Claim Reward") isPending ? __("Claiming...") : label ? label : __("Claim Reward")

View file

@ -18,12 +18,9 @@ const RewardSummary = props => {
unclaimed rewards. unclaimed rewards.
</p>} </p>}
</div> </div>
<div className="card__content"> <div className="card__actions card__actions--bottom">
<Link <Link button="text" navigate="/rewards" label={__("Rewards")} />
button={unclaimedRewardAmount > 0 ? "primary" : "text"} <Link button="text" navigate="/invite" label={__("Invites")} />
navigate="/rewards"
label={__("Go To Rewards")}
/>
</div> </div>
</section> </section>
); );

View file

@ -1,6 +1,8 @@
import React from "react"; import React from "react";
import { CreditAmount, Icon } from "component/common"; import { CreditAmount, Icon } from "component/common";
import RewardLink from "component/rewardLink"; import RewardLink from "component/rewardLink";
import Link from "component/link";
import rewards from "rewards";
const RewardTile = props => { const RewardTile = props => {
const { reward } = props; const { reward } = props;
@ -14,12 +16,19 @@ const RewardTile = props => {
<CreditAmount amount={reward.reward_amount} /> <CreditAmount amount={reward.reward_amount} />
<h3>{reward.reward_title}</h3> <h3>{reward.reward_title}</h3>
</div> </div>
<div className="card__actions">
{claimed
? <span><Icon icon="icon-check" /> {__("Reward claimed.")}</span>
: <RewardLink reward_type={reward.reward_type} />}
</div>
<div className="card__content">{reward.reward_description}</div> <div className="card__content">{reward.reward_description}</div>
<div className="card__actions card__actions--bottom ">
{reward.reward_type == rewards.TYPE_REFERRAL &&
<Link
button="alt"
navigate="/invite"
label={__("Go To Invites")}
/>}
{reward.reward_type !== rewards.TYPE_REFERRAL &&
(claimed
? <span><Icon icon="icon-check" /> {__("Reward claimed.")}</span>
: <RewardLink button="alt" reward_type={reward.reward_type} />)}
</div>
</div> </div>
</section> </section>
); );

View file

@ -30,7 +30,6 @@ const Router = props => {
const { currentPage, params } = props; const { currentPage, params } = props;
return route(currentPage, { return route(currentPage, {
address: <ReceiveCreditsPage params={params} />,
auth: <AuthPage params={params} />, auth: <AuthPage params={params} />,
backup: <BackupPage params={params} />, backup: <BackupPage params={params} />,
channel: <ChannelPage params={params} />, channel: <ChannelPage params={params} />,
@ -42,6 +41,7 @@ const Router = props => {
invite: <InvitePage params={params} />, invite: <InvitePage params={params} />,
publish: <PublishPage params={params} />, publish: <PublishPage params={params} />,
published: <FileListPublished params={params} />, published: <FileListPublished params={params} />,
receive: <ReceiveCreditsPage params={params} />,
report: <ReportPage params={params} />, report: <ReportPage params={params} />,
rewards: <RewardsPage params={params} />, rewards: <RewardsPage params={params} />,
search: <SearchPage params={params} />, search: <SearchPage params={params} />,

View file

@ -26,7 +26,7 @@ class TransactionListRecent extends React.PureComponent {
/>} />}
</div> </div>
{hasTransactions && {hasTransactions &&
<div className="card__content"> <div className="card__actions card__actions--bottom">
<Link <Link
navigate="/history" navigate="/history"
label={__("See Full History")} label={__("See Full History")}

View file

@ -31,6 +31,16 @@ class UserEmailNew extends React.PureComponent {
this.handleSubmit(event); this.handleSubmit(event);
}} }}
> >
<p>
{__(
"This process is required to prevent abuse of the rewards program."
)}
</p>
<p>
{__(
"We will also contact you about updates and new content, but you can unsubscribe at any time."
)}
</p>
<FormRow <FormRow
type="text" type="text"
label="Email" label="Email"

View file

@ -26,37 +26,121 @@ class UserVerify extends React.PureComponent {
const { errorMessage, isPending, navigate } = this.props; const { errorMessage, isPending, navigate } = this.props;
return ( return (
<div> <div>
<p> <section className="card card--form">
{__( <div className="card__title-primary">
"To ensure you are a real person, we require a valid credit or debit card." <h1>{__("Final Human Proof")}</h1>
) + </div>
" " + <div className="card__content">
__("There is no charge at all, now or in the future.") + <p>
" "} Finally, please complete <strong>one and only one</strong> of the
<Link options below.
href="https://lbry.io/faq/identity-requirements" </p>
label={__("Read More")} </div>
/> </section>
</p> <section className="card card--form">
{errorMessage && <p className="form-field__error">{errorMessage}</p>} <div className="card__title-primary">
<p> <h3>{__("1) Proof via Credit")}</h3>
<CardVerify </div>
label={__("Link Card and Finish")} <div className="card__content">
disabled={isPending} <p>
token={this.onToken.bind(this)} {__(
stripeKey={lbryio.getStripeToken()} "If you have a valid credit or debit card, you can instantly prove your humanity."
/> ) +
</p> " " +
<p> __(
{__( "There is no charge at all for this, now or in the future."
"You can continue without this step, but you will not be eligible to earn rewards." ) +
)} " "}
</p> <Link
<Link href="https://lbry.io/faq/identity-requirements"
onClick={() => navigate("/discover")} label={__("Read More")}
button="alt" />
label={__("Skip Rewards")} </p>
/> {errorMessage &&
<p className="form-field__error">{errorMessage}</p>}
<p>
<CardVerify
label={__("Perform Card Verification")}
disabled={isPending}
token={this.onToken.bind(this)}
stripeKey={lbryio.getStripeToken()}
/>
</p>
</div>
</section>
<section className="card card--form">
<div className="card__title-primary">
<h3>{__("2) Proof via YouTube")}</h3>
</div>
<div className="card__content">
<p>
{__(
"If you have a YouTube account with published videos that you want to make available on LBRY, syncing your account will grant instant authorization."
)}
</p>
</div>
<div className="card__actions">
<Link
href="https://api.lbry.io/yt/sync"
button="alt"
icon="icon-youtube"
label={__("YouTube Account Sync")}
/>
</div>
<div className="card__content">
<div className="meta">
This will not automatically refresh after approval. Once you have
synced your account, just navigate away or click
{" "} <Link navigate="/rewards" label="here" />.
</div>
</div>
</section>
<section className="card card--form">
<div className="card__title-primary">
<h3>{__("3) Proof via Chat")}</h3>
</div>
<div className="card__content">
<p>
{__(
"A moderator capable of approving you is typically available in the #verification channel of our chat room."
)}
</p>
<p>
{__(
"This process will likely involve providing proof of a stable and established online identity of some kind."
)}
</p>
</div>
<div className="card__actions">
<Link
href="https://slack.lbry.io"
button="alt"
icon="icon-slack"
label={__("Join LBRY Chat")}
/>
</div>
</section>
<section className="card card--form">
<div className="card__title-primary">
<h5>{__("Or, Skip It Entirely")}</h5>
</div>
<div className="card__content">
<p className="meta">
{__(
"You can continue without this step, but you will not be eligible to earn rewards."
)}
</p>
</div>
<div className="card__actions">
<Link
onClick={() => navigate("/discover")}
button="alt"
label={__("Skip Rewards")}
/>
</div>
</section>
</div> </div>
); );
} }

View file

@ -18,7 +18,7 @@ class WalletAddress extends React.PureComponent {
<div className="card__content"> <div className="card__content">
<p> <p>
{__( {__(
'Other LBRY users may send credits to you by entering this address on the "Send" page.' "Use this address to receive credits send by another user (or yourself)."
)} )}
</p> </p>
<Address address={receiveAddress} /> <Address address={receiveAddress} />

View file

@ -21,13 +21,19 @@ const WalletBalance = props => {
{(balance || balance === 0) && {(balance || balance === 0) &&
<CreditAmount amount={balance} precision={8} />} <CreditAmount amount={balance} precision={8} />}
</div> </div>
<div className="card__content"> <div className="card__actions card__actions--bottom">
<Link button="text" navigate="/send" label={__("Send")} />
<Link button="text" navigate="/address" label={__("Address")} />
<Link <Link
button="text" button="text"
navigate="/send"
disabled={balance === 0}
label={__("Send")}
/>
<Link button="text" navigate="/receive" label={__("Receive")} />
<Link
button="text"
disabled={balance === 0}
navigate="/backup" navigate="/backup"
label={__("Backup Your Wallet")} label={__("Backup")}
/> />
</div> </div>
</section> </section>

View file

@ -42,15 +42,15 @@ const WalletSend = props => {
onChange={setAddress} onChange={setAddress}
value={address} value={address}
/> />
</div> <div className="form-row-submit">
<div className="card__actions card__actions--form-submit"> <Link
<Link button="primary"
button="primary" label={__("Send")}
label={__("Send")} onClick={sendToAddress}
onClick={sendToAddress} disabled={!(parseFloat(amount) > 0.0) || !address}
disabled={!(parseFloat(amount) > 0.0) || !address} />
/> <input type="submit" className="hidden" />
<input type="submit" className="hidden" /> </div>
</div> </div>
</form> </form>
{modal == "insufficientBalance" && {modal == "insufficientBalance" &&

View file

@ -7,4 +7,5 @@ export const UPGRADE = "upgrade";
export const WELCOME = "welcome"; export const WELCOME = "welcome";
export const FIRST_REWARD = "first_reward"; export const FIRST_REWARD = "first_reward";
export const AUTHENTICATION_FAILURE = "auth_failure"; export const AUTHENTICATION_FAILURE = "auth_failure";
export const REWARD_APPROVAL_REQUIRED = "REWARD_APPROVAL_REQUIRED";
export const CREDIT_INTRO = "credit_intro"; export const CREDIT_INTRO = "credit_intro";

View file

@ -2,7 +2,7 @@ import React from "react";
import { Modal } from "modal/modal"; import { Modal } from "modal/modal";
import { CreditAmount, CurrencySymbol } from "component/common"; import { CreditAmount, CurrencySymbol } from "component/common";
import Link from "component/link/index"; import Link from "component/link/index";
import { formatCredits } from "utils"; import { formatCredits } from "util/formatCredits";
const ModalCreditIntro = props => { const ModalCreditIntro = props => {
const { closeModal, currentBalance, totalRewardValue, verifyAccount } = props; const { closeModal, currentBalance, totalRewardValue, verifyAccount } = props;
@ -12,50 +12,51 @@ const ModalCreditIntro = props => {
return ( return (
<Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY"> <Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
<section> <section>
<h3 className="modal__header">{__("Quick Credit Intro")}</h3> <h3 className="modal__header">{__("Blockchain 101")}</h3>
<p> <p>
The LBRY network is controlled and powered by credits called{" "} LBRY is controlled and powered by a blockchain asset called {" "}
<em><CurrencySymbol /></em>, a blockchain asset. {" "} <em><CurrencySymbol /></em>.{" "}
<CurrencySymbol />{" "} <CurrencySymbol />{" "}
{__( {__(
"is used to publish content, to have a say in the network rules, and to access paid content." "is used to publish content, to have a say in the network rules, and to access paid content."
)} )}
</p> </p>
<p>
{__("New verified users can receive more than ")} {" "}
{totalRewardValue
? <CreditAmount amount={totalRewardRounded} />
: <span className="credit-amount">{__("credits")}</span>}
{" "} {__(" in rewards for usage and influence of the network.")}
</p>
{currentBalance <= 0 {currentBalance <= 0
? <p> ? <div>
<strong> <p>
You currently have <CreditAmount amount={currentBalance} />, so
the actions you can take are limited.
</p>
<p>
However, there are a variety of ways to get credits, including
more than {" "}
{totalRewardValue
? <CreditAmount amount={totalRewardRounded} />
: <span className="credit-amount">{__("?? credits")}</span>}
{" "}{" "}
{__( {__(
"Without any credits, you will not be able to take this action." " in rewards available for being a proven human during the LBRY beta."
)} )}
</strong> </p>
</p> </div>
: <p> : <div>
{__( <p>
"But you probably knew all this, since you've already got %s of them!", But you probably knew this, since you've already got{" "}
formatCredits(currentBalance, 2) <CreditAmount amount={currentBalance} />.
)} </p>
</p>} </div>}
<div className="modal__buttons"> <div className="modal__buttons">
<Link <Link
button="primary" button="primary"
onClick={verifyAccount} onClick={verifyAccount}
label={__("You Had Me At Free LBC")} label={__("I'm Totally A Human")}
/> />
<Link <Link
button="alt" button="alt"
onClick={closeModal} onClick={closeModal}
label={ label={
currentBalance <= 0 currentBalance <= 0 ? __("Use Without LBC") : __("Meh, Not Now")
? __("Continue Without LBC")
: __("Meh, Not Now")
} }
/> />
</div> </div>

View file

@ -7,7 +7,7 @@ const select = state => ({});
const perform = dispatch => ({ const perform = dispatch => ({
addFunds: () => { addFunds: () => {
dispatch(doNavigate("/rewards")); dispatch(doNavigate("/wallet"));
dispatch(doCloseModal()); dispatch(doCloseModal());
}, },
closeModal: () => dispatch(doCloseModal()), closeModal: () => dispatch(doCloseModal()),

View file

@ -1,5 +1,6 @@
import React from "react"; import React from "react";
import { Modal } from "modal/modal"; import { Modal } from "modal/modal";
import { CurrencySymbol } from "component/common";
class ModalInsufficientCredits extends React.PureComponent { class ModalInsufficientCredits extends React.PureComponent {
render() { render() {
@ -15,7 +16,8 @@ class ModalInsufficientCredits extends React.PureComponent {
onAborted={closeModal} onAborted={closeModal}
onConfirmed={addFunds} onConfirmed={addFunds}
> >
<p>{__("More LBRY credits are required to take this action.")}</p> <h3 className="modal__header">{__("More Credits Required")}</h3>
<p>You'll need more <CurrencySymbol /> to do this.</p>
</Modal> </Modal>
); );
} }

View file

@ -0,0 +1,14 @@
import React from "react";
import { connect } from "react-redux";
import { doCloseModal, doAuthNavigate } from "actions/app";
import ModalRewardApprovalRequired from "./view";
const perform = dispatch => ({
doAuth: () => {
dispatch(doCloseModal());
dispatch(doAuthNavigate());
},
closeModal: () => dispatch(doCloseModal()),
});
export default connect(null, perform)(ModalRewardApprovalRequired);

View file

@ -0,0 +1,33 @@
import React from "react";
import { Modal } from "modal/modal";
class ModalRewardApprovalRequired extends React.PureComponent {
render() {
const { closeModal, doAuth } = this.props;
return (
<Modal
isOpen={true}
contentLabel={__("Human Verification Required")}
onConfirmed={doAuth}
onAborted={closeModal}
type="confirm"
confirmButtonLabel={__("I'm Totally Real")}
abortButtonLabel={__("Nevermind")}
>
<section>
<h3 className="modal__header">
{__("This is awkward. Are you real?")}
</h3>
<p>
{__(
"Before we can give you any credits, we need to perform a brief check to make sure you're a new and unique person."
)}
</p>
</section>
</Modal>
);
}
}
export default ModalRewardApprovalRequired;

View file

@ -6,9 +6,11 @@ import { makeSelectClientSetting } from "selectors/settings";
import { selectUser } from "selectors/user"; import { selectUser } from "selectors/user";
import { selectCostForCurrentPageUri } from "selectors/cost_info"; import { selectCostForCurrentPageUri } from "selectors/cost_info";
import * as settings from "constants/settings"; import * as settings from "constants/settings";
import { selectBalance } from "selectors/wallet";
import ModalRouter from "./view"; import ModalRouter from "./view";
const select = (state, props) => ({ const select = (state, props) => ({
balance: selectBalance(state),
showPageCost: selectCostForCurrentPageUri(state), showPageCost: selectCostForCurrentPageUri(state),
modal: selectCurrentModal(state), modal: selectCurrentModal(state),
page: selectCurrentPage(state), page: selectCurrentPage(state),

View file

@ -6,10 +6,20 @@ import ModalInsufficientCredits from "modal/modalInsufficientCredits";
import ModalUpgrade from "modal/modalUpgrade"; import ModalUpgrade from "modal/modalUpgrade";
import ModalWelcome from "modal/modalWelcome"; import ModalWelcome from "modal/modalWelcome";
import ModalFirstReward from "modal/modalFirstReward"; import ModalFirstReward from "modal/modalFirstReward";
import ModalRewardApprovalRequired from "modal/modalRewardApprovalRequired";
import * as modals from "constants/modal_types"; import * as modals from "constants/modal_types";
import ModalCreditIntro from "modal/modalCreditIntro"; import ModalCreditIntro from "modal/modalCreditIntro";
class ModalRouter extends React.PureComponent { class ModalRouter extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
lastTransitionModal: null,
lastTransitionPage: null,
};
}
componentWillMount() { componentWillMount() {
this.showTransitionModals(this.props); this.showTransitionModals(this.props);
} }
@ -19,55 +29,72 @@ class ModalRouter extends React.PureComponent {
} }
showTransitionModals(props) { showTransitionModals(props) {
const { modal } = props; const { modal, openModal, page } = props;
if (modal) { if (modal) {
return; return;
} }
[ const transitionModal = [
this.checkShowWelcome.bind(this), this.checkShowWelcome,
this.checkShowCreditIntro.bind(this), this.checkShowCreditIntro,
].find(func => func(props)); this.checkShowInsufficientCredits,
].reduce((acc, func) => {
return !acc ? func.bind(this)(props) : acc;
}, false);
if (
transitionModal &&
(transitionModal != this.state.lastTransitionModal ||
page != this.state.lastTransitionPage)
) {
openModal(transitionModal);
this.setState({
lastTransitionModal: transitionModal,
lastTransitionPage: page,
});
}
} }
checkShowWelcome(props) { checkShowWelcome(props) {
const { isWelcomeAcknowledged, openModal, user } = props; const { isWelcomeAcknowledged, user } = props;
if ( if (
!isWelcomeAcknowledged && !isWelcomeAcknowledged &&
user && user &&
!user.is_reward_approved && !user.is_reward_approved &&
!user.is_identity_verified !user.is_identity_verified
) { ) {
openModal(modals.WELCOME); return modals.WELCOME;
return true;
} }
} }
checkShowCreditIntro(props) { checkShowCreditIntro(props) {
const { const { page, isCreditIntroAcknowledged, user } = props;
page,
isCreditIntroAcknowledged,
openModal,
user,
showPageCost,
} = props;
if ( if (
!isCreditIntroAcknowledged && !isCreditIntroAcknowledged &&
user && user &&
!user.is_reward_approved && !user.is_reward_approved &&
!user.is_identity_verified && (["rewards", "send", "receive", "publish", "wallet"].includes(page) ||
["rewards", "send", "address", "show", "publish", "wallet"].includes( this.isPaidShowPage(props))
page
) &&
(page != "show" || showPageCost > 0)
) { ) {
openModal(modals.CREDIT_INTRO); return modals.CREDIT_INTRO;
return true;
} }
} }
checkShowInsufficientCredits(props) {
const { balance, page } = props;
if (balance <= 0 && ["send", "publish"].includes(page)) {
return modals.INSUFFICIENT_CREDITS;
}
}
isPaidShowPage(props) {
const { page, showPageCost } = props;
return page === "show" && showPageCost > 0;
}
render() { render() {
const { modal } = this.props; const { modal } = this.props;
@ -88,6 +115,8 @@ class ModalRouter extends React.PureComponent {
return <ModalAuthFailure />; return <ModalAuthFailure />;
case modals.CREDIT_INTRO: case modals.CREDIT_INTRO:
return <ModalCreditIntro />; return <ModalCreditIntro />;
case modals.REWARD_APPROVAL_REQUIRED:
return <ModalRewardApprovalRequired />;
default: default:
return null; return null;
} }

View file

@ -30,11 +30,11 @@ export class AuthPage extends React.PureComponent {
const { email, isPending, isVerificationCandidate, user } = this.props; const { email, isPending, isVerificationCandidate, user } = this.props;
if (isPending || (user && !user.has_verified_email && !email)) { if (isPending || (user && !user.has_verified_email && !email)) {
return __("Welcome to LBRY"); return __("Human Proofing");
} else if (user && !user.has_verified_email) { } else if (user && !user.has_verified_email) {
return __("Confirm Email"); return __("Confirm Email");
} else if (user && !user.is_identity_verified && !user.is_reward_approved) { } else if (user && !user.is_identity_verified && !user.is_reward_approved) {
return __("Confirm Identity"); return __("Final Verification");
} else { } else {
return __("Welcome to LBRY"); return __("Welcome to LBRY");
} }
@ -44,51 +44,45 @@ export class AuthPage extends React.PureComponent {
const { email, isPending, isVerificationCandidate, user } = this.props; const { email, isPending, isVerificationCandidate, user } = this.props;
if (isPending) { if (isPending) {
return <BusyMessage message={__("Authenticating")} />; return [<BusyMessage message={__("Authenticating")} />, true];
} else if (user && !user.has_verified_email && !email) { } else if (user && !user.has_verified_email && !email) {
return <UserEmailNew />; return [<UserEmailNew />, true];
} else if (user && !user.has_verified_email) { } else if (user && !user.has_verified_email) {
return <UserEmailVerify />; return [<UserEmailVerify />, true];
} else if (user && !user.is_identity_verified) { } else if (user && !user.is_identity_verified) {
return <UserVerify />; return [<UserVerify />, false];
} else { } else {
return <span className="empty">{__("No further steps.")}</span>; return [<span className="empty">{__("No further steps.")}</span>, true];
} }
} }
render() { render() {
const { email, user, isPending, navigate } = this.props; const { email, user, isPending, navigate } = this.props;
const [innerContent, useTemplate] = this.renderMain();
return ( return useTemplate
<main className=""> ? <main>
<section className="card card--form"> <section className="card card--form">
<div className="card__title-primary"> <div className="card__title-primary">
<h1>{this.getTitle()}</h1> <h1>{this.getTitle()}</h1>
</div>
<div className="card__content">
{!isPending &&
!email &&
user &&
!user.has_verified_email &&
<p>
{__("Create a verified identity and receive LBC rewards.")}
</p>}
{this.renderMain()}
</div>
<div className="card__content">
<div className="help">
{__(
"This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards."
) + " "}
<Link
onClick={() => navigate("/discover")}
label={__("Return home")}
/>.
</div> </div>
</div> <div className="card__content">
</section> {innerContent}
</main> </div>
); <div className="card__content">
<div className="help">
{__(
"This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards."
) + " "}
<Link
onClick={() => navigate("/discover")}
label={__("Return home")}
/>.
</div>
</div>
</section>
</main>
: innerContent;
} }
} }

View file

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { doNavigate } from "actions/app"; import { doAuthNavigate } from "actions/app";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { doFetchAccessToken } from "actions/user"; import { doFetchAccessToken } from "actions/user";
import { selectAccessToken, selectUser } from "selectors/user"; import { selectAccessToken, selectUser } from "selectors/user";
@ -11,7 +11,7 @@ const select = state => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
navigate: (path, params) => dispatch(doNavigate(path, params)), doAuth: () => dispatch(doAuthNavigate("/help")),
fetchAccessToken: () => dispatch(doFetchAccessToken()), fetchAccessToken: () => dispatch(doFetchAccessToken()),
}); });

View file

@ -3,7 +3,7 @@ import React from "react";
import lbry from "lbry.js"; import lbry from "lbry.js";
import Link from "component/link"; import Link from "component/link";
import SubHeader from "component/subHeader"; import SubHeader from "component/subHeader";
import { BusyMessage } from "component/common"; import { BusyMessage, Icon } from "component/common";
class HelpPage extends React.PureComponent { class HelpPage extends React.PureComponent {
constructor(props) { constructor(props) {
@ -50,7 +50,7 @@ class HelpPage extends React.PureComponent {
render() { render() {
let ver, osName, platform, newVerLink; let ver, osName, platform, newVerLink;
const { navigate, user } = this.props; const { doAuth, user } = this.props;
if (this.state.versionInfo) { if (this.state.versionInfo) {
ver = this.state.versionInfo; ver = this.state.versionInfo;
@ -119,7 +119,7 @@ class HelpPage extends React.PureComponent {
<p>{__("Did you find something wrong?")}</p> <p>{__("Did you find something wrong?")}</p>
<p> <p>
<Link <Link
onClick={() => navigate("report")} navigate="/report"
label={__("Submit a Bug Report")} label={__("Submit a Bug Report")}
icon="icon-bug" icon="icon-bug"
button="alt" button="alt"
@ -143,7 +143,7 @@ class HelpPage extends React.PureComponent {
</p> </p>
: <p>{__("Your copy of LBRY is up to date.")}</p>} : <p>{__("Your copy of LBRY is up to date.")}</p>}
{this.state.uiVersion && ver {this.state.uiVersion && ver
? <table className="table-standard"> ? <table className="table-standard table-stretch table-standard--definition-list">
<tbody> <tbody>
<tr> <tr>
<th>{__("App")}</th> <th>{__("App")}</th>
@ -162,7 +162,21 @@ class HelpPage extends React.PureComponent {
<td> <td>
{user && user.primary_email {user && user.primary_email
? user.primary_email ? user.primary_email
: <span className="empty">{__("none")}</span>} : <span>
<span className="empty">{__("none")} </span>
(<Link
onClick={() => doAuth()}
label={__("set email")}
/>)
</span>}
</td>
</tr>
<tr>
<th>{__("Reward Eligible")}</th>
<td>
{user && user.is_reward_approved
? <Icon icon="icon-check" />
: <Icon icon="icon-ban" />}
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -16,10 +16,12 @@ import {
doCreateChannel, doCreateChannel,
doPublish, doPublish,
} from "actions/content"; } from "actions/content";
import { selectBalance } from "selectors/wallet";
import rewards from "rewards"; import rewards from "rewards";
import PublishPage from "./view"; import PublishPage from "./view";
const select = state => ({ const select = state => ({
balance: selectBalance(state),
myClaims: selectMyClaims(state), myClaims: selectMyClaims(state),
fetchingChannels: selectFetchingMyChannels(state), fetchingChannels: selectFetchingMyChannels(state),
channels: selectMyChannelClaims(state), channels: selectMyChannelClaims(state),

View file

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import SubHeader from "component/subHeader"; import SubHeader from "component/subHeader";
import WalletBalance from "component/walletBalance"; import Link from "component/link";
import WalletAddress from "component/walletAddress"; import WalletAddress from "component/walletAddress";
const ReceiveCreditsPage = props => { const ReceiveCreditsPage = props => {
@ -8,6 +8,25 @@ const ReceiveCreditsPage = props => {
<main className="main--single-column"> <main className="main--single-column">
<SubHeader /> <SubHeader />
<WalletAddress /> <WalletAddress />
<section className="card">
<div className="card__title-primary">
<h3>{__("Where To Find Credits")}</h3>
</div>
<div className="card__content">
<p>
{
"LBRY credits can be purchased on exchanges, earned for contributions, for mining, and more."
}
</p>
</div>
<div className="card__actions">
<Link
button="alt"
href="https://lbry.io/faq/earn-credits"
label={__("Read More")}
/>
</div>
</section>
</main> </main>
); );
}; };

View file

@ -33,13 +33,20 @@ class RewardsPage extends React.PureComponent {
) { ) {
return ( return (
<section className="card"> <section className="card">
<div className="card__title-primary">
<h3>{__("Humans Only")}</h3>
</div>
<div className="card__content empty"> <div className="card__content empty">
<p> <p>
{__("Only verified accounts are eligible to earn rewards.")} {__("Rewards are for human beings only.")}
{" "}
{__(
"You'll have to prove you're one of us before you can claim any rewards."
)}
</p> </p>
</div> </div>
<div className="card__content"> <div className="card__content">
<Link onClick={doAuth} button="primary" label="Become Verified" /> <Link onClick={doAuth} button="primary" label="Prove Humanity" />
</div> </div>
</section> </section>
); );
@ -89,12 +96,11 @@ class RewardsPage extends React.PureComponent {
</div> </div>
); );
} else if (user === null) { } else if (user === null) {
return ( return (
<div className="card__content empty"> <div className="card__content empty">
<p> <p>
{__( {__(
"This application is unable to earn rewards due to an authentication failure." "This application is unable to earn rewards due to an authentication failure."
)} )}
</p> </p>
</div> </div>
@ -106,8 +112,12 @@ class RewardsPage extends React.PureComponent {
</div> </div>
); );
} else { } else {
return rewards.map(reward => return (
<RewardTile key={reward.reward_type} reward={reward} /> <div className="card-grid">
{rewards.map(reward =>
<RewardTile key={reward.reward_type} reward={reward} />
)}
</div>
); );
} }
} }

View file

@ -1,6 +1,5 @@
import React from "react"; import React from "react";
import SubHeader from "component/subHeader"; import SubHeader from "component/subHeader";
import WalletBalance from "component/walletBalance";
import WalletSend from "component/walletSend"; import WalletSend from "component/walletSend";
const SendCreditsPage = props => { const SendCreditsPage = props => {

View file

@ -2,16 +2,16 @@ import React from "react";
import SubHeader from "component/subHeader"; import SubHeader from "component/subHeader";
import WalletBalance from "component/walletBalance"; import WalletBalance from "component/walletBalance";
import RewardSummary from "component/rewardSummary"; import RewardSummary from "component/rewardSummary";
import InviteSummary from "component/inviteSummary";
import TransactionListRecent from "component/transactionListRecent"; import TransactionListRecent from "component/transactionListRecent";
const WalletPage = props => { const WalletPage = props => {
return ( return (
<main className="main--single-column"> <main className="main--single-column page--wallet">
<SubHeader /> <SubHeader />
<WalletBalance /> <div className="card-grid">
<RewardSummary /> <WalletBalance />
<InviteSummary /> <RewardSummary />
</div>
<TransactionListRecent /> <TransactionListRecent />
</main> </main>
); );

View file

@ -96,6 +96,17 @@ rewards.TYPE_MANY_DOWNLOADS = "many_downloads";
rewards.TYPE_FIRST_PUBLISH = "first_publish"; rewards.TYPE_FIRST_PUBLISH = "first_publish";
rewards.TYPE_FEATURED_DOWNLOAD = "featured_download"; rewards.TYPE_FEATURED_DOWNLOAD = "featured_download";
rewards.TYPE_REFERRAL = "referral"; rewards.TYPE_REFERRAL = "referral";
rewards.SORT_ORDER = [
rewards.TYPE_NEW_USER,
rewards.TYPE_CONFIRM_EMAIL,
rewards.TYPE_FIRST_STREAM,
rewards.TYPE_FIRST_CHANNEL,
rewards.TYPE_FIRST_PUBLISH,
rewards.TYPE_FEATURED_DOWNLOAD,
rewards.TYPE_MANY_DOWNLOADS,
rewards.TYPE_REFERRAL,
rewards.TYPE_NEW_DEVELOPER,
];
rewards.claimReward = function(type) { rewards.claimReward = function(type) {
function requestReward(resolve, reject, params) { function requestReward(resolve, reject, params) {

View file

@ -39,7 +39,7 @@ export const selectPageTitle = createSelector(
return __("Wallet"); return __("Wallet");
case "send": case "send":
return __("Send Credits"); return __("Send Credits");
case "address": case "receive":
return __("Wallet Address"); return __("Wallet Address");
case "backup": case "backup":
return __("Backup Your Wallet"); return __("Backup Your Wallet");
@ -143,17 +143,17 @@ export const selectHeaderLinks = createSelector(selectCurrentPage, page => {
case "wallet": case "wallet":
case "history": case "history":
case "send": case "send":
case "address": case "receive":
case "invite": case "invite":
case "rewards": case "rewards":
case "backup": case "backup":
return { return {
wallet: __("Overview"), wallet: __("Overview"),
rewards: __("Rewards"),
invite: __("Invites"),
history: __("History"), history: __("History"),
send: __("Send"), send: __("Send"),
address: __("Address"), receive: __("Receive"),
rewards: __("Rewards"),
invite: __("Invites"),
}; };
case "downloaded": case "downloaded":
case "published": case "published":

View file

@ -1,5 +1,6 @@
import { createSelector } from "reselect"; import { createSelector } from "reselect";
import { selectUser } from "selectors/user"; import { selectUser } from "selectors/user";
import rewards from "rewards";
const _selectState = state => state.rewards || {}; const _selectState = state => state.rewards || {};
@ -20,7 +21,13 @@ export const selectClaimedRewards = createSelector(
export const selectUnclaimedRewards = createSelector( export const selectUnclaimedRewards = createSelector(
selectUnclaimedRewardsByType, selectUnclaimedRewardsByType,
byType => Object.values(byType) || [] byType =>
Object.values(byType).sort(function(a, b) {
return rewards.SORT_ORDER.indexOf(a.reward_type) <
rewards.SORT_ORDER.indexOf(b.reward_type)
? -1
: 1;
}) || []
); );
export const selectIsRewardEligible = createSelector( export const selectIsRewardEligible = createSelector(
@ -83,3 +90,10 @@ const selectRewardByType = (state, props) => {
export const makeSelectRewardByType = () => { export const makeSelectRewardByType = () => {
return createSelector(selectRewardByType, reward => reward); return createSelector(selectRewardByType, reward => reward);
}; };
export const makeSelectRewardAmountByType = () => {
return createSelector(
selectRewardByType,
reward => (reward ? reward.reward_amount : 0)
);
};

View file

@ -38,6 +38,8 @@ export const selectWunderBarAddress = createSelector(
export const selectWunderBarIcon = createSelector(selectCurrentPage, page => { export const selectWunderBarIcon = createSelector(selectCurrentPage, page => {
switch (page) { switch (page) {
case "auth":
return "icon-user";
case "search": case "search":
return "icon-search"; return "icon-search";
case "settings": case "settings":

View file

@ -2,8 +2,8 @@
$spacing-vertical: 24px; $spacing-vertical: 24px;
$padding-button: 12px; $padding-button: $spacing-vertical * 2/3;
$padding-text-link: 4px; $padding-text-link: 0px;
$color-primary: #155B4A; $color-primary: #155B4A;
$color-primary-light: saturate(lighten($color-primary, 50%), 20%); $color-primary-light: saturate(lighten($color-primary, 50%), 20%);

View file

@ -134,15 +134,6 @@ p
} }
} }
/*should this be here or work this way? had to hack additional rule below*/
.icon:only-child {
position: relative;
top: 0.16em;
}
.icon-featured > .icon {
top: 0;
}
.help { .help {
font-size: .85em; font-size: .85em;
color: $color-help; color: $color-help;

View file

@ -8,7 +8,7 @@ $button-focus-shift: 12%;
+ .button-set-item + .button-set-item
{ {
margin-left: $padding-button; margin-left: $spacing-vertical;
} }
} }

View file

@ -23,10 +23,11 @@ $width-card-small: $spacing-vertical * 10;
} }
.card__title-primary, .card__title-primary,
.card__title-identity, .card__title-identity,
.card__actions,
.card__content, .card__content,
.card__subtext { .card__subtext,
padding: 0 $padding-card-horizontal; .card__actions {
padding-left: $padding-card-horizontal;
padding-right: $padding-card-horizontal;
} }
.card--small { .card--small {
.card__title-primary, .card__title-primary,
@ -39,6 +40,7 @@ $width-card-small: $spacing-vertical * 10;
} }
.card__title-primary { .card__title-primary {
margin-top: $spacing-vertical * 2/3; margin-top: $spacing-vertical * 2/3;
margin-bottom: $spacing-vertical * 2/3;
} }
.card__title-identity { .card__title-identity {
margin-top: $spacing-vertical * 1/3; margin-top: $spacing-vertical * 1/3;
@ -46,13 +48,6 @@ $width-card-small: $spacing-vertical * 10;
} }
.card__actions { .card__actions {
margin-top: $spacing-vertical * 2/3; margin-top: $spacing-vertical * 2/3;
}
.card__actions--bottom {
margin-top: $spacing-vertical * 1/3;
margin-bottom: $spacing-vertical * 1/3;
}
.card__actions--form-submit {
margin-top: $spacing-vertical;
margin-bottom: $spacing-vertical * 2/3; margin-bottom: $spacing-vertical * 2/3;
} }
.card__content { .card__content {
@ -259,4 +254,21 @@ $padding-right-card-hover-hack: 30px;
.card__icon-featured-content { .card__icon-featured-content {
color: orangered; color: orangered;
}
/*
if we keep doing things like this, we should add a real grid system, but I'm going to be a selective dick about it - Jeremy
*/
.card-grid {
$margin-card-grid: $spacing-vertical * 2/3;
display:flex;
flex-wrap: wrap;
> .card {
width: $width-page-constrained / 2 - $margin-card-grid / 2;
flex-grow:1;
}
> .card:nth-of-type(2n - 1) {
margin-right: $margin-card-grid;
}
} }

View file

@ -52,6 +52,11 @@ table.table-standard {
} }
} }
} }
.table-standard--definition-list {
th {
text-align: right;
}
}
table.table-stretch { table.table-stretch {
width: 100%; width: 100%;