reviewable
This commit is contained in:
parent
18a40defba
commit
09ecae7e0d
27 changed files with 522 additions and 525 deletions
|
@ -2,7 +2,6 @@ import * as types from "constants/action_types";
|
|||
import lbry from "lbry";
|
||||
import lbryio from "lbryio";
|
||||
import lbryuri from "lbryuri";
|
||||
import rewards from "rewards";
|
||||
import { selectBalance } from "selectors/wallet";
|
||||
import {
|
||||
selectFileInfoForUri,
|
||||
|
@ -10,8 +9,8 @@ import {
|
|||
} from "selectors/file_info";
|
||||
import { selectResolvingUris } from "selectors/content";
|
||||
import { selectCostInfoForUri } from "selectors/cost_info";
|
||||
import { selectClaimsByUri } from "selectors/claims";
|
||||
import { doOpenModal } from "actions/app";
|
||||
import { doClaimEligiblePurchaseRewards } from "actions/rewards";
|
||||
|
||||
export function doResolveUri(uri) {
|
||||
return function(dispatch, getState) {
|
||||
|
@ -171,7 +170,7 @@ export function doDownloadFile(uri, streamInfo) {
|
|||
})
|
||||
.catch(() => {});
|
||||
|
||||
rewards.claimEligiblePurchaseRewards();
|
||||
dispatch(doClaimEligiblePurchaseRewards());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as types from "constants/action_types";
|
|||
import lbry from "lbry";
|
||||
import lbryio from "lbryio";
|
||||
import rewards from "rewards";
|
||||
import { selectRewards, selectRewardsByType } from "selectors/rewards";
|
||||
|
||||
export function doRewardList() {
|
||||
return function(dispatch, getState) {
|
||||
|
@ -11,56 +12,105 @@ export function doRewardList() {
|
|||
type: types.FETCH_REWARDS_STARTED,
|
||||
});
|
||||
|
||||
lbryio.call('reward', 'list', {}).then((userRewards) => {
|
||||
dispatch({
|
||||
type: types.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards }
|
||||
lbryio
|
||||
.call("reward", "list", {})
|
||||
.then(userRewards => {
|
||||
dispatch({
|
||||
type: types.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards },
|
||||
});
|
||||
})
|
||||
}).catch(() => {
|
||||
dispatch({
|
||||
type: types.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards: [] }
|
||||
})
|
||||
});
|
||||
.catch(() => {
|
||||
dispatch({
|
||||
type: types.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards: [] },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimReward(reward) {
|
||||
export function doClaimRewardType(rewardType) {
|
||||
return function(dispatch, getState) {
|
||||
const rewardsByType = selectRewardsByType(getState()),
|
||||
reward = rewardsByType[rewardType];
|
||||
|
||||
if (reward) {
|
||||
dispatch(doClaimReward(reward));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimReward(reward, saveError = false) {
|
||||
return function(dispatch, getState) {
|
||||
if (reward.transaction_id) {
|
||||
//already claimed, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_STARTED,
|
||||
data: { reward }
|
||||
})
|
||||
data: { reward },
|
||||
});
|
||||
|
||||
const success = (a) => {
|
||||
console.log(a)
|
||||
const success = reward => {
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_SUCCESS,
|
||||
data: {
|
||||
a
|
||||
}
|
||||
})
|
||||
}
|
||||
reward,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const failure = (error) => {
|
||||
const failure = error => {
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_FAILURE,
|
||||
data: {
|
||||
reward,
|
||||
error
|
||||
}
|
||||
})
|
||||
error: saveError ? error : null,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
rewards.claimReward(reward.reward_type).then(success, failure);
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimEligiblePurchaseRewards() {
|
||||
return function(dispatch, getState) {
|
||||
if (!lbryio.enabled || !lbryio.getAccessToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rewards.claimReward(reward.reward_type).then(success, failure)
|
||||
}
|
||||
const rewardsByType = selectRewardsByType(getState());
|
||||
|
||||
let types = {};
|
||||
|
||||
types[rewards.TYPE_FIRST_STREAM] = false;
|
||||
types[rewards.TYPE_FEATURED_DOWNLOAD] = false;
|
||||
types[rewards.TYPE_MANY_DOWNLOADS] = false;
|
||||
Object.values(rewardsByType).forEach(reward => {
|
||||
if (types[reward.reward_type] === false && reward.transaction_id) {
|
||||
types[reward.reward_type] = true;
|
||||
}
|
||||
});
|
||||
|
||||
let unclaimedType = Object.keys(types).find(type => {
|
||||
return types[type] === false && type !== rewards.TYPE_FEATURED_DOWNLOAD; //handled below
|
||||
});
|
||||
if (unclaimedType) {
|
||||
dispatch(doClaimRewardType(unclaimedType));
|
||||
}
|
||||
if (types[rewards.TYPE_FEATURED_DOWNLOAD] === false) {
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FEATURED_DOWNLOAD));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimRewardClearError(reward) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_CLEAR_ERROR,
|
||||
data: { reward }
|
||||
})
|
||||
}
|
||||
data: { reward },
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -20,8 +20,6 @@ export function doAuthenticate() {
|
|||
dispatch(doRewardList()); //FIXME - where should this happen?
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("auth error");
|
||||
console.log(error);
|
||||
dispatch({
|
||||
type: types.AUTHENTICATION_FAILURE,
|
||||
data: { error },
|
||||
|
@ -30,6 +28,28 @@ export function doAuthenticate() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doUserFetch() {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.USER_FETCH_STARTED,
|
||||
});
|
||||
lbryio.setCurrentUser(
|
||||
user => {
|
||||
dispatch({
|
||||
type: types.USER_FETCH_SUCCESS,
|
||||
data: { user },
|
||||
});
|
||||
},
|
||||
error => {
|
||||
dispatch({
|
||||
type: types.USER_FETCH_FAILURE,
|
||||
data: { error },
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserEmailNew(email) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
|
@ -42,6 +62,7 @@ export function doUserEmailNew(email) {
|
|||
type: types.USER_EMAIL_NEW_SUCCESS,
|
||||
data: { email },
|
||||
});
|
||||
dispatch(doUserFetch());
|
||||
},
|
||||
error => {
|
||||
if (
|
||||
|
@ -102,6 +123,7 @@ export function doUserEmailVerify(verificationToken) {
|
|||
type: types.USER_EMAIL_VERIFY_SUCCESS,
|
||||
data: { email },
|
||||
});
|
||||
dispatch(doUserFetch());
|
||||
} else {
|
||||
failure(new Error("Your email is still not verified.")); //shouldn't happen?
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ import { doUserEmailDecline } from "actions/user";
|
|||
import { doOpenModal } from "actions/app";
|
||||
import {
|
||||
selectAuthenticationIsPending,
|
||||
selectUserHasEmail,
|
||||
selectUserIsAuthRequested,
|
||||
} from "selectors/user";
|
||||
import AuthOverlay from "./view";
|
||||
|
||||
const select = state => ({
|
||||
hasEmail: selectUserHasEmail(state),
|
||||
isPending: selectAuthenticationIsPending(state),
|
||||
isShowing: selectUserIsAuthRequested(state),
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ export class AuthOverlay extends React.Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
const { isPending, isShowing } = this.props;
|
||||
const { isPending, isShowing, hasEmail } = this.props;
|
||||
|
||||
if (isShowing) {
|
||||
return (
|
||||
|
@ -46,15 +46,14 @@ export class AuthOverlay extends React.Component {
|
|||
{isPending
|
||||
? ""
|
||||
: <div className="form-row-submit">
|
||||
{this.state.showNoEmailConfirm
|
||||
? <div>
|
||||
<p className="help form-input-width">
|
||||
{!hasEmail && this.state.showNoEmailConfirm
|
||||
? <div className="help form-input-width">
|
||||
<p>
|
||||
{__(
|
||||
"If you continue without an email, you will be ineligible to earn free LBC rewards, as well as unable to receive security related communications."
|
||||
)}
|
||||
</p>
|
||||
<Link
|
||||
className="button-text-help"
|
||||
onClick={() => {
|
||||
this.onEmailSkipConfirm();
|
||||
}}
|
||||
|
@ -62,11 +61,15 @@ export class AuthOverlay extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
: <Link
|
||||
className="button-text-help"
|
||||
className={"button-text-help"}
|
||||
onClick={() => {
|
||||
this.onEmailSkipClick();
|
||||
hasEmail
|
||||
? this.onEmailSkipConfirm()
|
||||
: this.onEmailSkipClick();
|
||||
}}
|
||||
label={__("Do I have to?")}
|
||||
label={
|
||||
hasEmail ? __("Skip for now") : __("Do I have to?")
|
||||
}
|
||||
/>}
|
||||
</div>}
|
||||
</ModalPage>
|
||||
|
|
|
@ -178,9 +178,19 @@ export class FormRow extends React.Component {
|
|||
|
||||
this._fieldRequiredText = __("This field is required");
|
||||
|
||||
this.state = {
|
||||
this.state = this.getStateFromProps(props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState(this.getStateFromProps(nextProps));
|
||||
}
|
||||
|
||||
getStateFromProps(props) {
|
||||
return {
|
||||
isError: !!props.errorMessage,
|
||||
errorMessage: typeof props.errorMessage === "string" ? props.errorMessage : '',
|
||||
errorMessage: typeof props.errorMessage === "string"
|
||||
? props.errorMessage
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
import React from "react";
|
||||
import lbry from "lbry";
|
||||
import { Icon } from "component/common";
|
||||
import Modal from "component/modal";
|
||||
import rewards from "rewards";
|
||||
import Link from "component/link";
|
||||
|
||||
export class RewardLink extends React.Component {
|
||||
static propTypes = {
|
||||
type: React.PropTypes.string.isRequired,
|
||||
claimed: React.PropTypes.bool,
|
||||
onRewardClaim: React.PropTypes.func,
|
||||
onRewardFailure: React.PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
claimable: true,
|
||||
pending: false,
|
||||
errorMessage: null,
|
||||
};
|
||||
}
|
||||
|
||||
refreshClaimable() {
|
||||
switch (this.props.type) {
|
||||
case "new_user":
|
||||
this.setState({ claimable: true });
|
||||
return;
|
||||
|
||||
case "first_publish":
|
||||
lbry.claim_list_mine().then(list => {
|
||||
this.setState({
|
||||
claimable: list.length > 0,
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.refreshClaimable();
|
||||
}
|
||||
|
||||
claimReward() {
|
||||
this.setState({
|
||||
pending: true,
|
||||
});
|
||||
|
||||
rewards
|
||||
.claimReward(this.props.type)
|
||||
.then(reward => {
|
||||
this.setState({
|
||||
pending: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
if (this.props.onRewardClaim) {
|
||||
this.props.onRewardClaim(reward);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
errorMessage: error.message,
|
||||
pending: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
clearError() {
|
||||
if (this.props.onRewardFailure) {
|
||||
this.props.onRewardFailure();
|
||||
}
|
||||
this.setState({
|
||||
errorMessage: null,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="reward-link">
|
||||
{this.props.claimed
|
||||
? <span><Icon icon="icon-check" /> {__("Reward claimed.")}</span>
|
||||
: <Link
|
||||
button={this.props.button ? this.props.button : "alt"}
|
||||
disabled={this.state.pending || !this.state.claimable}
|
||||
label={
|
||||
this.state.pending ? __("Claiming...") : __("Claim Reward")
|
||||
}
|
||||
onClick={() => {
|
||||
this.claimReward();
|
||||
}}
|
||||
/>}
|
||||
{this.state.errorMessage
|
||||
? <Modal
|
||||
isOpen={true}
|
||||
contentLabel={__("Reward Claim Error")}
|
||||
className="error-modal"
|
||||
onConfirmed={() => {
|
||||
this.clearError();
|
||||
}}
|
||||
>
|
||||
{this.state.errorMessage}
|
||||
</Modal>
|
||||
: ""}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -20,14 +20,14 @@ const makeSelect = () => {
|
|||
isClaimed: selectHasClaimedReward(state, props),
|
||||
errorMessage: selectError(state, props),
|
||||
isPending: selectIsPending(state, props),
|
||||
reward: select,
|
||||
reward: selectReward(state, props),
|
||||
});
|
||||
|
||||
return select;
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
claimReward: reward => dispatch(doClaimReward(reward)),
|
||||
claimReward: reward => dispatch(doClaimReward(reward, true)),
|
||||
clearError: reward => dispatch(doClaimRewardClearError(reward)),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import {FormRow} from 'component/form.js';
|
||||
import React from "react";
|
||||
import Link from "component/link";
|
||||
import { FormRow } from "component/form.js";
|
||||
|
||||
class UserEmailNew extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
email: ''
|
||||
email: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -19,25 +19,43 @@ class UserEmailNew extends React.Component {
|
|||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.props.addUserEmail(this.state.email)
|
||||
this.props.addUserEmail(this.state.email);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
errorMessage,
|
||||
isPending
|
||||
} = this.props
|
||||
const { errorMessage, isPending } = this.props;
|
||||
|
||||
return <form onSubmit={(event) => { this.handleSubmit(event) }}>
|
||||
<FormRow type="text" label="Email" placeholder="scrwvwls@lbry.io"
|
||||
name="email" value={this.state.email}
|
||||
errorMessage={errorMessage}
|
||||
onChange={(event) => { this.handleEmailChanged(event) }} />
|
||||
<div className="form-row-submit">
|
||||
<Link button="primary" label="Next" disabled={isPending} onClick={(event) => { this.handleSubmit(event) }} />
|
||||
</div>
|
||||
</form>
|
||||
return (
|
||||
<form
|
||||
className="form-input-width"
|
||||
onSubmit={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
>
|
||||
<FormRow
|
||||
type="text"
|
||||
label="Email"
|
||||
placeholder="scrwvwls@lbry.io"
|
||||
name="email"
|
||||
value={this.state.email}
|
||||
errorMessage={errorMessage}
|
||||
onChange={event => {
|
||||
this.handleEmailChanged(event);
|
||||
}}
|
||||
/>
|
||||
<div className="form-row-submit">
|
||||
<Link
|
||||
button="primary"
|
||||
label="Next"
|
||||
disabled={isPending}
|
||||
onClick={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserEmailNew
|
||||
export default UserEmailNew;
|
||||
|
|
|
@ -10,7 +10,7 @@ import UserEmailVerify from "./view";
|
|||
|
||||
const select = state => ({
|
||||
isPending: selectEmailVerifyIsPending(state),
|
||||
email: selectEmailToVerify,
|
||||
email: selectEmailToVerify(state),
|
||||
errorMessage: selectEmailVerifyErrorMessage(state),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import {FormRow} from 'component/form.js';
|
||||
import React from "react";
|
||||
import Link from "component/link";
|
||||
import { FormRow } from "component/form.js";
|
||||
|
||||
class UserEmailVerify extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
code: '',
|
||||
code: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -19,25 +19,50 @@ class UserEmailVerify extends React.Component {
|
|||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.props.verifyUserEmail(this.state.code)
|
||||
this.props.verifyUserEmail(this.state.code);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
errorMessage,
|
||||
isPending
|
||||
} = this.props
|
||||
const { errorMessage, isPending } = this.props;
|
||||
|
||||
return <form onSubmit={(event) => { this.handleSubmit(event) }}>
|
||||
<FormRow type="text" label="Verification Code" placeholder="a94bXXXXXXXXXXXXXX"
|
||||
name="code" value={this.state.code} onChange={(event) => { this.handleCodeChanged(event) }}
|
||||
errorMessage={errorMessage}
|
||||
helper="A verification code is required to participate in early access rewards."/>
|
||||
<div className="form-row-submit form-row-submit--with-footer">
|
||||
<Link button="primary" label="Verify" disabled={this.state.submitting} onClick={(event) => { this.handleSubmit(event)}} />
|
||||
</div>
|
||||
</form>
|
||||
return (
|
||||
<form
|
||||
className="form-input-width"
|
||||
onSubmit={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
>
|
||||
<FormRow
|
||||
type="text"
|
||||
label="Verification Code"
|
||||
placeholder="a94bXXXXXXXXXXXXXX"
|
||||
name="code"
|
||||
value={this.state.code}
|
||||
onChange={event => {
|
||||
this.handleCodeChanged(event);
|
||||
}}
|
||||
errorMessage={errorMessage}
|
||||
/>
|
||||
{/* render help separately so it always shows */}
|
||||
<div className="form-field__helper">
|
||||
<p>
|
||||
Email <Link href="mailto:help@lbry.io" label="help@lbry.io" /> if
|
||||
you did not receive or are having trouble with your code.
|
||||
</p>
|
||||
</div>
|
||||
<div className="form-row-submit form-row-submit--with-footer">
|
||||
<Link
|
||||
button="primary"
|
||||
label="Verify"
|
||||
disabled={this.state.submitting}
|
||||
onClick={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserEmailVerify
|
||||
export default UserEmailVerify;
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
import React from "react";
|
||||
import rewards from "rewards";
|
||||
import { connect } from "react-redux";
|
||||
import { doCloseModal } from "actions/app";
|
||||
import { selectUserIsRewardApproved } from "selectors/user";
|
||||
import { makeSelectHasClaimedReward } from "selectors/rewards";
|
||||
import {
|
||||
makeSelectHasClaimedReward,
|
||||
makeSelectClaimRewardError,
|
||||
makeSelectRewardByType,
|
||||
} from "selectors/rewards";
|
||||
import WelcomeModal from "./view";
|
||||
|
||||
const select = (state, props) => {
|
||||
const selectHasReward = makeSelectHasClaimedReward();
|
||||
const selectHasClaimed = makeSelectHasClaimedReward(),
|
||||
selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
hasReward: selectHasReward(state, { reward_type: "new_user" }),
|
||||
hasClaimed: selectHasClaimed(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
isRewardApproved: selectUserIsRewardApproved(state),
|
||||
rewardAmount: 5,
|
||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -6,14 +6,12 @@ import RewardLink from "component/rewardLink";
|
|||
|
||||
class WelcomeModal extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
closeModal,
|
||||
hasReward,
|
||||
isRewardApproved,
|
||||
rewardAmount,
|
||||
} = this.props;
|
||||
const { closeModal, hasClaimed, isRewardApproved, reward } = this.props;
|
||||
|
||||
return !hasReward
|
||||
console.log("welcome");
|
||||
console.log(this.props);
|
||||
|
||||
return !hasClaimed
|
||||
? <Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
|
||||
<section>
|
||||
<h3 className="modal__header">Welcome to LBRY.</h3>
|
||||
|
@ -30,7 +28,7 @@ class WelcomeModal extends React.Component {
|
|||
Thank you for making content freedom possible!
|
||||
{" "}{isRewardApproved ? __("Here's a nickel, kid.") : ""}
|
||||
</p>
|
||||
<div style={{ textAlign: "center", marginBottom: "12px" }}>
|
||||
<div className="text-center">
|
||||
{isRewardApproved
|
||||
? <RewardLink reward_type="new_user" button="primary" />
|
||||
: <Link
|
||||
|
@ -52,7 +50,8 @@ class WelcomeModal extends React.Component {
|
|||
<h3 className="modal__header">About Your Reward</h3>
|
||||
<p>
|
||||
You earned a reward of
|
||||
{" "}<CreditAmount amount={rewardAmount} label={false} /> LBRY
|
||||
{" "}<CreditAmount amount={reward.reward_amount} label={false} />
|
||||
{" "}LBRY
|
||||
credits, or <em>LBC</em>.
|
||||
</p>
|
||||
<p>
|
||||
|
|
|
@ -71,22 +71,25 @@ export const SEARCH_CANCELLED = "SEARCH_CANCELLED";
|
|||
export const DAEMON_SETTINGS_RECEIVED = "DAEMON_SETTINGS_RECEIVED";
|
||||
|
||||
// User
|
||||
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED'
|
||||
export const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS'
|
||||
export const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE'
|
||||
export const USER_EMAIL_DECLINE = 'USER_EMAIL_DECLINE'
|
||||
export const USER_EMAIL_NEW_STARTED = 'USER_EMAIL_NEW_STARTED'
|
||||
export const USER_EMAIL_NEW_SUCCESS = 'USER_EMAIL_NEW_SUCCESS'
|
||||
export const USER_EMAIL_NEW_EXISTS = 'USER_EMAIL_NEW_EXISTS'
|
||||
export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE'
|
||||
export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED'
|
||||
export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS'
|
||||
export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE'
|
||||
export const AUTHENTICATION_STARTED = "AUTHENTICATION_STARTED";
|
||||
export const AUTHENTICATION_SUCCESS = "AUTHENTICATION_SUCCESS";
|
||||
export const AUTHENTICATION_FAILURE = "AUTHENTICATION_FAILURE";
|
||||
export const USER_EMAIL_DECLINE = "USER_EMAIL_DECLINE";
|
||||
export const USER_EMAIL_NEW_STARTED = "USER_EMAIL_NEW_STARTED";
|
||||
export const USER_EMAIL_NEW_SUCCESS = "USER_EMAIL_NEW_SUCCESS";
|
||||
export const USER_EMAIL_NEW_EXISTS = "USER_EMAIL_NEW_EXISTS";
|
||||
export const USER_EMAIL_NEW_FAILURE = "USER_EMAIL_NEW_FAILURE";
|
||||
export const USER_EMAIL_VERIFY_STARTED = "USER_EMAIL_VERIFY_STARTED";
|
||||
export const USER_EMAIL_VERIFY_SUCCESS = "USER_EMAIL_VERIFY_SUCCESS";
|
||||
export const USER_EMAIL_VERIFY_FAILURE = "USER_EMAIL_VERIFY_FAILURE";
|
||||
export const USER_FETCH_STARTED = "USER_FETCH_STARTED";
|
||||
export const USER_FETCH_SUCCESS = "USER_FETCH_SUCCESS";
|
||||
export const USER_FETCH_FAILURE = "USER_FETCH_FAILURE";
|
||||
|
||||
// Rewards
|
||||
export const FETCH_REWARDS_STARTED = 'FETCH_REWARDS_STARTED'
|
||||
export const FETCH_REWARDS_COMPLETED = 'FETCH_REWARDS_COMPLETED'
|
||||
export const CLAIM_REWARD_STARTED = 'CLAIM_REWARD_STARTED'
|
||||
export const CLAIM_REWARD_SUCCESS = 'CLAIM_REWARD_SUCCESS'
|
||||
export const CLAIM_REWARD_FAILURE = 'CLAIM_REWARD_FAILURE'
|
||||
export const CLAIM_REWARD_CLEAR_ERROR = 'CLAIM_REWARD_CLEAR_ERROR'
|
||||
export const FETCH_REWARDS_STARTED = "FETCH_REWARDS_STARTED";
|
||||
export const FETCH_REWARDS_COMPLETED = "FETCH_REWARDS_COMPLETED";
|
||||
export const CLAIM_REWARD_STARTED = "CLAIM_REWARD_STARTED";
|
||||
export const CLAIM_REWARD_SUCCESS = "CLAIM_REWARD_SUCCESS";
|
||||
export const CLAIM_REWARD_FAILURE = "CLAIM_REWARD_FAILURE";
|
||||
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR";
|
||||
|
|
|
@ -6,7 +6,6 @@ const querystring = require("querystring");
|
|||
const lbryio = {
|
||||
_accessToken: getSession("accessToken"),
|
||||
_authenticationPromise: null,
|
||||
_user: null,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
|
@ -36,20 +35,9 @@ lbryio.getExchangeRates = function() {
|
|||
return lbryio._exchangePromise;
|
||||
};
|
||||
|
||||
lbryio.call = function(
|
||||
resource,
|
||||
action,
|
||||
params = {},
|
||||
method = "get",
|
||||
evenIfDisabled = false
|
||||
) {
|
||||
// evenIfDisabled is just for development, when we may have some calls working and some not
|
||||
lbryio.call = function(resource, action, params = {}, method = "get") {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (
|
||||
!lbryio.enabled &&
|
||||
!evenIfDisabled &&
|
||||
(resource != "discover" || action != "list")
|
||||
) {
|
||||
if (!lbryio.enabled && (resource != "discover" || action != "list")) {
|
||||
console.log(__("Internal API disabled"));
|
||||
reject(new Error(__("LBRY internal API is disabled")));
|
||||
return;
|
||||
|
@ -135,7 +123,6 @@ lbryio.setCurrentUser = (resolve, reject) => {
|
|||
lbryio
|
||||
.call("user", "me")
|
||||
.then(data => {
|
||||
lbryio.user = data;
|
||||
resolve(data);
|
||||
})
|
||||
.catch(function(err) {
|
||||
|
|
|
@ -51,7 +51,10 @@ ipcRenderer.on("open-uri-requested", (event, uri) => {
|
|||
document.addEventListener("click", event => {
|
||||
var target = event.target;
|
||||
while (target && target !== document) {
|
||||
if (target.matches('a[href^="http"]')) {
|
||||
if (
|
||||
target.matches('a[href^="http"]') ||
|
||||
target.matches('a[href^="mailto"]')
|
||||
) {
|
||||
event.preventDefault();
|
||||
shell.openExternal(target.href);
|
||||
return;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import React from "react";
|
||||
import rewards from "rewards";
|
||||
import { connect } from "react-redux";
|
||||
import { doFetchFileInfosAndPublishedClaims } from "actions/file_info";
|
||||
import {
|
||||
selectFileInfosPublished,
|
||||
selectFileListDownloadedOrPublishedIsPending,
|
||||
} from "selectors/file_info";
|
||||
import { doClaimRewardType } from "actions/rewards";
|
||||
import { doNavigate } from "actions/app";
|
||||
import FileListPublished from "./view";
|
||||
|
||||
|
@ -16,6 +18,8 @@ const select = state => ({
|
|||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
fetchFileListPublished: () => dispatch(doFetchFileInfosAndPublishedClaims()),
|
||||
claimFirstPublishReward: () =>
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FIRST_PUBLISH)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FileListPublished);
|
||||
|
|
|
@ -16,24 +16,7 @@ class FileListPublished extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.fileInfos.length > 0) this._requestPublishReward();
|
||||
}
|
||||
|
||||
_requestPublishReward() {
|
||||
// TODO this is throwing an error now
|
||||
// Error: LBRY internal API is disabled
|
||||
//
|
||||
// lbryio.call('reward', 'list', {}).then(function(userRewards) {
|
||||
// //already rewarded
|
||||
// if (userRewards.filter(function (reward) {
|
||||
// return reward.reward_type == rewards.TYPE_FIRST_PUBLISH && reward.transaction_id
|
||||
// }).length) {
|
||||
// return
|
||||
// }
|
||||
// else {
|
||||
// rewards.claimReward(rewards.TYPE_FIRST_PUBLISH).catch(() => {})
|
||||
// }
|
||||
// })
|
||||
if (this.props.fileInfos.length > 0) this.props.claimFirstPublishReward();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doNavigate, doHistoryBack } from "actions/app";
|
||||
import { doClaimRewardType } from "actions/rewards";
|
||||
import { selectMyClaims } from "selectors/claims";
|
||||
import rewards from "rewards";
|
||||
import PublishPage from "./view";
|
||||
|
||||
const select = state => ({
|
||||
|
@ -11,6 +13,8 @@ const select = state => ({
|
|||
const perform = dispatch => ({
|
||||
back: () => dispatch(doHistoryBack()),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
claimFirstChannelReward: () =>
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FIRST_CHANNEL)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(PublishPage);
|
||||
|
|
|
@ -44,7 +44,7 @@ class PublishPage extends React.Component {
|
|||
// Calls API to update displayed list of channels. If a channel name is provided, will select
|
||||
// that channel at the same time (used immediately after creating a channel)
|
||||
lbry.channel_list_mine().then(channels => {
|
||||
rewards.claimReward(rewards.TYPE_FIRST_CHANNEL).then(() => {}, () => {});
|
||||
this.props.claimFirstChannelReward();
|
||||
this.setState({
|
||||
channels: channels,
|
||||
...(channel ? { channel } : {}),
|
||||
|
@ -373,7 +373,7 @@ class PublishPage extends React.Component {
|
|||
lbry
|
||||
.channel_new({
|
||||
channel_name: newChannelName,
|
||||
amount: parseInt(this.state.newChannelBid),
|
||||
amount: parseFloat(this.state.newChannelBid),
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doNavigate } from "actions/app";
|
||||
import {
|
||||
selectFetchingRewards,
|
||||
selectIsRewardEligible,
|
||||
selectRewards,
|
||||
} from "selectors/rewards";
|
||||
import { selectFetchingRewards, selectRewards } from "selectors/rewards";
|
||||
import {
|
||||
selectUserIsRewardEligible,
|
||||
selectUserHasEmail,
|
||||
selectUserIsRewardApproved,
|
||||
selectUserIsVerificationCandidate,
|
||||
} from "selectors/user";
|
||||
import RewardsPage from "./view";
|
||||
|
|
|
@ -1,87 +1,90 @@
|
|||
import * as types from "constants/action_types";
|
||||
|
||||
const reducers = {}
|
||||
const reducers = {};
|
||||
const defaultState = {
|
||||
fetching: false,
|
||||
rewardsByType: {},
|
||||
claimPendingByType: {},
|
||||
claimErrorsByType: {}
|
||||
claimErrorsByType: {},
|
||||
};
|
||||
|
||||
reducers[types.FETCH_REWARDS_STARTED] = function(state, action) {
|
||||
const newRewards = Object.assign({}, state.rewards, {
|
||||
return Object.assign({}, state, {
|
||||
fetching: true,
|
||||
})
|
||||
|
||||
return Object.assign({}, state, newRewards)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.FETCH_REWARDS_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
userRewards,
|
||||
} = action.data
|
||||
const { userRewards } = action.data;
|
||||
|
||||
const byRewardType = {}
|
||||
userRewards.forEach(reward => byRewardType[reward.reward_type] = reward)
|
||||
const newRewards = Object.assign({}, state.rewards, {
|
||||
byRewardType: byRewardType,
|
||||
fetching: false
|
||||
})
|
||||
const rewardsByType = {};
|
||||
userRewards.forEach(reward => (rewardsByType[reward.reward_type] = reward));
|
||||
|
||||
return Object.assign({}, state, newRewards)
|
||||
}
|
||||
return Object.assign({}, state, {
|
||||
rewardsByType: rewardsByType,
|
||||
fetching: false,
|
||||
});
|
||||
};
|
||||
|
||||
function setClaimRewardState(state, reward, isClaiming, errorMessage="") {
|
||||
const newClaimPendingByType = Object.assign({}, state.claimPendingByType)
|
||||
const newClaimErrorsByType = Object.assign({}, state.claimErrorsByType)
|
||||
function setClaimRewardState(state, reward, isClaiming, errorMessage = "") {
|
||||
const newClaimPendingByType = Object.assign({}, state.claimPendingByType);
|
||||
const newClaimErrorsByType = Object.assign({}, state.claimErrorsByType);
|
||||
if (isClaiming) {
|
||||
newClaimPendingByType[reward.reward_type] = isClaiming
|
||||
newClaimPendingByType[reward.reward_type] = isClaiming;
|
||||
} else {
|
||||
delete newClaimPendingByType[reward.reward_type]
|
||||
delete newClaimPendingByType[reward.reward_type];
|
||||
}
|
||||
if (errorMessage) {
|
||||
newClaimErrorsByType[reward.reward_type] = errorMessage
|
||||
newClaimErrorsByType[reward.reward_type] = errorMessage;
|
||||
} else {
|
||||
delete newClaimErrorsByType[reward.reward_type]
|
||||
delete newClaimErrorsByType[reward.reward_type];
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
claimPendingByType: newClaimPendingByType,
|
||||
claimErrorsByType: newClaimErrorsByType,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
reducers[types.CLAIM_REWARD_STARTED] = function(state, action) {
|
||||
const {
|
||||
reward,
|
||||
} = action.data
|
||||
const { reward } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, true, "")
|
||||
}
|
||||
return setClaimRewardState(state, reward, true, "");
|
||||
};
|
||||
|
||||
reducers[types.CLAIM_REWARD_SUCCESS] = function(state, action) {
|
||||
const {
|
||||
reward,
|
||||
} = action.data
|
||||
const { reward } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, false, "")
|
||||
}
|
||||
const existingReward = state.rewardsByType[reward.reward_type];
|
||||
const newReward = Object.assign({}, reward, {
|
||||
reward_title: existingReward.reward_title,
|
||||
reward_description: existingReward.reward_description,
|
||||
});
|
||||
const rewardsByType = Object.assign({}, state.rewardsByType);
|
||||
|
||||
rewardsByType[reward.reward_type] = newReward;
|
||||
|
||||
const newState = Object.assign({}, state, { rewardsByType });
|
||||
|
||||
return setClaimRewardState(newState, newReward, false, "");
|
||||
};
|
||||
|
||||
reducers[types.CLAIM_REWARD_FAILURE] = function(state, action) {
|
||||
const {
|
||||
reward,
|
||||
error
|
||||
} = action.data
|
||||
const { reward, error } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, false, error.message)
|
||||
}
|
||||
return setClaimRewardState(state, reward, false, error ? error.message : "");
|
||||
};
|
||||
|
||||
reducers[types.CLAIM_REWARD_CLEAR_ERROR] = function(state, action) {
|
||||
const {
|
||||
reward
|
||||
} = action.data
|
||||
const { reward } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, state.claimPendingByType[reward.reward_type], "")
|
||||
}
|
||||
return setClaimRewardState(
|
||||
state,
|
||||
reward,
|
||||
state.claimPendingByType[reward.reward_type],
|
||||
""
|
||||
);
|
||||
};
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
|
|
|
@ -5,6 +5,7 @@ const reducers = {};
|
|||
|
||||
const defaultState = {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
emailNewIsPending: false,
|
||||
emailNewErrorMessage: "",
|
||||
emailNewDeclined: getLocal("user_email_declined", false),
|
||||
|
@ -15,12 +16,15 @@ const defaultState = {
|
|||
reducers[types.AUTHENTICATION_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
authenticationIsPending: true,
|
||||
userIsPending: true,
|
||||
user: defaultState.user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.AUTHENTICATION_SUCCESS] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
user: action.data.user,
|
||||
});
|
||||
};
|
||||
|
@ -28,6 +32,28 @@ reducers[types.AUTHENTICATION_SUCCESS] = function(state, action) {
|
|||
reducers[types.AUTHENTICATION_FAILURE] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
user: null,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_FETCH_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
userIsPending: true,
|
||||
user: defaultState.user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_FETCH_SUCCESS] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
userIsPending: false,
|
||||
user: action.data.user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_FETCH_FAILURE] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
userIsPending: true,
|
||||
user: null,
|
||||
});
|
||||
};
|
||||
|
|
354
ui/js/rewards.js
354
ui/js/rewards.js
|
@ -1,225 +1,191 @@
|
|||
const hashes = require('jshashes');
|
||||
import lbry from 'lbry';
|
||||
import lbryio from 'lbryio';
|
||||
import { doShowSnackBar } from 'actions/app';
|
||||
const hashes = require("jshashes");
|
||||
import lbry from "lbry";
|
||||
import lbryio from "lbryio";
|
||||
import { doShowSnackBar } from "actions/app";
|
||||
|
||||
function rewardMessage(type, amount) {
|
||||
return {
|
||||
new_developer: __(
|
||||
'You earned %s for registering as a new developer.',
|
||||
amount
|
||||
),
|
||||
new_user: __('You earned %s LBC new user reward.', amount),
|
||||
confirm_email: __(
|
||||
'You earned %s LBC for verifying your email address.',
|
||||
amount
|
||||
),
|
||||
new_channel: __(
|
||||
'You earned %s LBC for creating a publisher identity.',
|
||||
amount
|
||||
),
|
||||
first_stream: __(
|
||||
'You earned %s LBC for streaming your first video.',
|
||||
amount
|
||||
),
|
||||
many_downloads: __(
|
||||
'You earned %s LBC for downloading some of the things.',
|
||||
amount
|
||||
),
|
||||
first_publish: __(
|
||||
'You earned %s LBC for making your first publication.',
|
||||
amount
|
||||
)
|
||||
}[type];
|
||||
return {
|
||||
new_developer: __(
|
||||
"You earned %s for registering as a new developer.",
|
||||
amount
|
||||
),
|
||||
new_user: __("You earned %s LBC new user reward.", amount),
|
||||
confirm_email: __(
|
||||
"You earned %s LBC for verifying your email address.",
|
||||
amount
|
||||
),
|
||||
new_channel: __(
|
||||
"You earned %s LBC for creating a publisher identity.",
|
||||
amount
|
||||
),
|
||||
first_stream: __(
|
||||
"You earned %s LBC for streaming your first video.",
|
||||
amount
|
||||
),
|
||||
many_downloads: __(
|
||||
"You earned %s LBC for downloading some of the things.",
|
||||
amount
|
||||
),
|
||||
first_publish: __(
|
||||
"You earned %s LBC for making your first publication.",
|
||||
amount
|
||||
),
|
||||
}[type];
|
||||
}
|
||||
|
||||
function toHex(s) {
|
||||
let h = '';
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
let c = s.charCodeAt(i).toString(16);
|
||||
if (c.length < 2) {
|
||||
c = '0'.concat(c);
|
||||
}
|
||||
h += c;
|
||||
}
|
||||
return h;
|
||||
let h = "";
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
let c = s.charCodeAt(i).toString(16);
|
||||
if (c.length < 2) {
|
||||
c = "0".concat(c);
|
||||
}
|
||||
h += c;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
function fromHex(h) {
|
||||
let s = '';
|
||||
for (let i = 0; i < h.length; i += 2) {
|
||||
s += String.fromCharCode(parseInt(h.substr(i, 2), 16));
|
||||
}
|
||||
return s;
|
||||
let s = "";
|
||||
for (let i = 0; i < h.length; i += 2) {
|
||||
s += String.fromCharCode(parseInt(h.substr(i, 2), 16));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function reverseString(s) {
|
||||
let o = '';
|
||||
for (let i = s.length - 1; i >= 0; i--) {
|
||||
o += s[i];
|
||||
}
|
||||
return o;
|
||||
let o = "";
|
||||
for (let i = s.length - 1; i >= 0; i--) {
|
||||
o += s[i];
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
function pack(num) {
|
||||
return (
|
||||
'' +
|
||||
String.fromCharCode(num & 0xff) +
|
||||
String.fromCharCode((num >> 8) & 0xff) +
|
||||
String.fromCharCode((num >> 16) & 0xff) +
|
||||
String.fromCharCode((num >> 24) & 0xff)
|
||||
);
|
||||
return (
|
||||
"" +
|
||||
String.fromCharCode(num & 0xff) +
|
||||
String.fromCharCode((num >> 8) & 0xff) +
|
||||
String.fromCharCode((num >> 16) & 0xff) +
|
||||
String.fromCharCode((num >> 24) & 0xff)
|
||||
);
|
||||
}
|
||||
|
||||
// Returns true if claim is an initial claim, false if it's an update to an existing claim
|
||||
function isInitialClaim(claim) {
|
||||
const reversed = reverseString(fromHex(claim.txid));
|
||||
const concat = reversed.concat(pack(claim.nout));
|
||||
const sha256 = new hashes.SHA256({ utf8: false }).raw(concat);
|
||||
const ripemd160 = new hashes.RMD160({ utf8: false }).raw(sha256);
|
||||
const hash = toHex(reverseString(ripemd160));
|
||||
return hash == claim.claim_id;
|
||||
const reversed = reverseString(fromHex(claim.txid));
|
||||
const concat = reversed.concat(pack(claim.nout));
|
||||
const sha256 = new hashes.SHA256({ utf8: false }).raw(concat);
|
||||
const ripemd160 = new hashes.RMD160({ utf8: false }).raw(sha256);
|
||||
const hash = toHex(reverseString(ripemd160));
|
||||
return hash == claim.claim_id;
|
||||
}
|
||||
|
||||
const rewards = {};
|
||||
|
||||
(rewards.TYPE_NEW_DEVELOPER = 'new_developer'), (rewards.TYPE_NEW_USER =
|
||||
'new_user'), (rewards.TYPE_CONFIRM_EMAIL =
|
||||
'confirm_email'), (rewards.TYPE_FIRST_CHANNEL =
|
||||
'new_channel'), (rewards.TYPE_FIRST_STREAM =
|
||||
'first_stream'), (rewards.TYPE_MANY_DOWNLOADS =
|
||||
'many_downloads'), (rewards.TYPE_FIRST_PUBLISH = 'first_publish');
|
||||
rewards.TYPE_FEATURED_DOWNLOAD = 'featured_download';
|
||||
(rewards.TYPE_NEW_DEVELOPER = "new_developer"), (rewards.TYPE_NEW_USER =
|
||||
"new_user"), (rewards.TYPE_CONFIRM_EMAIL =
|
||||
"confirm_email"), (rewards.TYPE_FIRST_CHANNEL =
|
||||
"new_channel"), (rewards.TYPE_FIRST_STREAM =
|
||||
"first_stream"), (rewards.TYPE_MANY_DOWNLOADS =
|
||||
"many_downloads"), (rewards.TYPE_FIRST_PUBLISH = "first_publish");
|
||||
rewards.TYPE_FEATURED_DOWNLOAD = "featured_download";
|
||||
|
||||
rewards.claimReward = function(type) {
|
||||
function requestReward(resolve, reject, params) {
|
||||
if (!lbryio.enabled || !lbryio.getAccessToken()) {
|
||||
reject(new Error(__('Rewards are not enabled.')));
|
||||
return;
|
||||
}
|
||||
lbryio.call('reward', 'new', params, 'post').then(({ reward_amount }) => {
|
||||
const message = rewardMessage(type, reward_amount),
|
||||
result = {
|
||||
type: type,
|
||||
amount: reward_amount,
|
||||
message: message
|
||||
};
|
||||
function requestReward(resolve, reject, params) {
|
||||
if (!lbryio.enabled || !lbryio.getAccessToken()) {
|
||||
reject(new Error(__("Rewards are not enabled.")));
|
||||
return;
|
||||
}
|
||||
lbryio.call("reward", "new", params, "post").then(reward => {
|
||||
const message = rewardMessage(type, reward.reward_amount);
|
||||
|
||||
// Display global notice
|
||||
const action = doShowSnackBar({
|
||||
message,
|
||||
linkText: __('Show All'),
|
||||
linkTarget: '/rewards',
|
||||
isError: false
|
||||
});
|
||||
window.app.store.dispatch(action);
|
||||
// Display global notice
|
||||
const action = doShowSnackBar({
|
||||
message,
|
||||
linkText: __("Show All"),
|
||||
linkTarget: "/rewards",
|
||||
isError: false,
|
||||
});
|
||||
window.app.store.dispatch(action);
|
||||
|
||||
// Add more events here to display other places
|
||||
// Add more events here to display other places
|
||||
|
||||
resolve(result);
|
||||
}, reject);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
lbry.wallet_unused_address().then(address => {
|
||||
const params = {
|
||||
reward_type: type,
|
||||
wallet_address: address
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case rewards.TYPE_FIRST_CHANNEL:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(function(claims) {
|
||||
let claim = claims.find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] == '@' &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
new Error(__('Please create a channel identity first.'))
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_PUBLISH:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(claims => {
|
||||
let claim = claims.find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] != '@' &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
claims.length
|
||||
? new Error(
|
||||
__(
|
||||
'Please publish something and wait for confirmation by the network to claim this reward.'
|
||||
)
|
||||
)
|
||||
: new Error(
|
||||
__('Please publish something to claim this reward.')
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_STREAM:
|
||||
case rewards.TYPE_NEW_USER:
|
||||
default:
|
||||
requestReward(resolve, reject, params);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
rewards.claimEligiblePurchaseRewards = () => {
|
||||
if (!lbryio.enabled || !lbryio.getAccessToken()) {
|
||||
return;
|
||||
resolve(reward);
|
||||
}, reject);
|
||||
}
|
||||
let types = {};
|
||||
types[rewards.TYPE_FIRST_STREAM] = false;
|
||||
types[rewards.TYPE_FEATURED_DOWNLOAD] = false;
|
||||
types[rewards.TYPE_MANY_DOWNLOADS] = false;
|
||||
lbryio.call('reward', 'list', {}).then(
|
||||
userRewards => {
|
||||
userRewards.forEach(reward => {
|
||||
if (types[reward.reward_type] === false && reward.transaction_id) {
|
||||
types[reward.reward_type] = true;
|
||||
}
|
||||
});
|
||||
let unclaimedType = Object.keys(types).find(type => {
|
||||
return types[type] === false && type !== rewards.TYPE_FEATURED_DOWNLOAD; //handled below
|
||||
});
|
||||
if (unclaimedType) {
|
||||
rewards.claimReward(unclaimedType);
|
||||
}
|
||||
if (types[rewards.TYPE_FEATURED_DOWNLOAD] === false) {
|
||||
rewards.claimReward(rewards.TYPE_FEATURED_DOWNLOAD);
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
lbry.wallet_unused_address().then(address => {
|
||||
const params = {
|
||||
reward_type: type,
|
||||
wallet_address: address,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case rewards.TYPE_FIRST_CHANNEL:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(function(claims) {
|
||||
let claim = claims.reverse().find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] == "@" &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
new Error(__("Please create a channel identity first."))
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_PUBLISH:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(claims => {
|
||||
let claim = claims.reverse().find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] != "@" &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
claims.length
|
||||
? new Error(
|
||||
__(
|
||||
"Please publish something and wait for confirmation by the network to claim this reward."
|
||||
)
|
||||
)
|
||||
: new Error(
|
||||
__("Please publish something to claim this reward.")
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_STREAM:
|
||||
case rewards.TYPE_NEW_USER:
|
||||
default:
|
||||
requestReward(resolve, reject, params);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default rewards;
|
||||
|
|
|
@ -5,7 +5,7 @@ const _selectState = state => state.rewards || {};
|
|||
|
||||
export const selectRewardsByType = createSelector(
|
||||
_selectState,
|
||||
state => state.byRewardType || {}
|
||||
state => state.rewardsByType || {}
|
||||
);
|
||||
|
||||
export const selectRewards = createSelector(
|
||||
|
@ -18,26 +18,14 @@ export const selectIsRewardEligible = createSelector(
|
|||
user => user.can_claim_rewards
|
||||
);
|
||||
|
||||
export const selectClaimedRewards = createSelector(selectRewards, rewards =>
|
||||
rewards.filter(reward => reward.transaction_id !== "")
|
||||
);
|
||||
|
||||
export const selectClaimedRewardsByType = createSelector(
|
||||
selectClaimedRewards,
|
||||
claimedRewards => {
|
||||
const byType = {};
|
||||
claimedRewards.forEach(reward => (byType[reward.reward_type] = reward));
|
||||
return byType;
|
||||
}
|
||||
);
|
||||
|
||||
export const selectFetchingRewards = createSelector(
|
||||
_selectState,
|
||||
state => !!state.fetching
|
||||
);
|
||||
|
||||
export const selectHasClaimedReward = (state, props) => {
|
||||
return !!selectClaimedRewardsByType[props.reward_type];
|
||||
const reward = selectRewardsByType(state)[props.reward_type];
|
||||
return reward && reward.transaction_id !== "";
|
||||
};
|
||||
|
||||
export const makeSelectHasClaimedReward = () => {
|
||||
|
|
|
@ -7,6 +7,11 @@ export const selectAuthenticationIsPending = createSelector(
|
|||
state => state.authenticationIsPending
|
||||
);
|
||||
|
||||
export const selectUserIsPending = createSelector(
|
||||
_selectState,
|
||||
state => state.userIsPending
|
||||
);
|
||||
|
||||
export const selectUser = createSelector(
|
||||
_selectState,
|
||||
state => state.user || {}
|
||||
|
@ -20,17 +25,17 @@ export const selectEmailToVerify = createSelector(
|
|||
export const selectUserHasEmail = createSelector(
|
||||
selectUser,
|
||||
selectEmailToVerify,
|
||||
(user, email) => user.has_email || email
|
||||
(user, email) => (user && user.has_email) || email
|
||||
);
|
||||
|
||||
export const selectUserIsRewardEligible = createSelector(
|
||||
selectUser,
|
||||
user => user.is_reward_eligible
|
||||
user => user && user.is_reward_eligible
|
||||
);
|
||||
|
||||
export const selectUserIsRewardApproved = createSelector(
|
||||
selectUser,
|
||||
user => user.is_reward_approved
|
||||
user => user && user.is_reward_approved
|
||||
);
|
||||
|
||||
export const selectEmailNewIsPending = createSelector(
|
||||
|
@ -64,7 +69,7 @@ export const selectUserIsVerificationCandidate = createSelector(
|
|||
selectEmailToVerify,
|
||||
selectUser,
|
||||
(isEligible, isApproved, emailToVerify, user) =>
|
||||
(isEligible && !isApproved) || (emailToVerify && !user.has_email)
|
||||
(isEligible && !isApproved) || (emailToVerify && user && !user.has_email)
|
||||
);
|
||||
|
||||
export const selectUserIsAuthRequested = createSelector(
|
||||
|
|
|
@ -165,3 +165,8 @@ p
|
|||
section.section-spaced {
|
||||
margin-bottom: $spacing-vertical;
|
||||
}
|
||||
|
||||
.text-center
|
||||
{
|
||||
text-align: center;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue