mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-08-28 07:51:31 +00:00
Merge pull request #1509 from lbryio/issue/1329
Rewards reorder and snackbar notifications
This commit is contained in:
commit
d65d92e0b8
11 changed files with 41 additions and 91 deletions
|
@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
||||||
* Add flair to snackbar ([#1313](https://github.com/lbryio/lbry-app/pull/1313))
|
* Add flair to snackbar ([#1313](https://github.com/lbryio/lbry-app/pull/1313))
|
||||||
* Made font in price badge larger ([#1420](https://github.com/lbryio/lbry-app/pull/1420))
|
* Made font in price badge larger ([#1420](https://github.com/lbryio/lbry-app/pull/1420))
|
||||||
* Store subscriptions in internal database ([#1424](https://github.com/lbryio/lbry-app/pull/1424))
|
* Store subscriptions in internal database ([#1424](https://github.com/lbryio/lbry-app/pull/1424))
|
||||||
|
* Move rewards logic to interal api ([#1509](https://github.com/lbryio/lbry-app/pull/1509))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Fix content-type not shown correctly in file description ([#863](https://github.com/lbryio/lbry-app/pull/863))
|
* Fix content-type not shown correctly in file description ([#863](https://github.com/lbryio/lbry-app/pull/863))
|
||||||
|
|
|
@ -1,26 +1,17 @@
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import InviteNew from './view';
|
|
||||||
import {
|
import {
|
||||||
selectUserInvitesRemaining,
|
selectUserInvitesRemaining,
|
||||||
selectUserInviteNewIsPending,
|
selectUserInviteNewIsPending,
|
||||||
selectUserInviteNewErrorMessage,
|
selectUserInviteNewErrorMessage,
|
||||||
} from 'redux/selectors/user';
|
} from 'redux/selectors/user';
|
||||||
import rewards from 'rewards';
|
|
||||||
import { makeSelectRewardAmountByType } from 'redux/selectors/rewards';
|
|
||||||
|
|
||||||
import { doUserInviteNew } from 'redux/actions/user';
|
import { doUserInviteNew } from 'redux/actions/user';
|
||||||
|
import InviteNew from './view';
|
||||||
|
|
||||||
const select = state => {
|
const select = state => ({
|
||||||
const selectReward = makeSelectRewardAmountByType();
|
errorMessage: selectUserInviteNewErrorMessage(state),
|
||||||
|
invitesRemaining: selectUserInvitesRemaining(state),
|
||||||
return {
|
isPending: selectUserInviteNewIsPending(state),
|
||||||
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)),
|
||||||
|
|
|
@ -29,8 +29,7 @@ class FormInviteNew extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending, rewardAmount } = this.props;
|
const { errorMessage, isPending } = this.props;
|
||||||
const label = `${__('Get')} ${rewardAmount} LBC`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form onSubmit={this.handleSubmit}>
|
<Form onSubmit={this.handleSubmit}>
|
||||||
|
@ -49,7 +48,7 @@ class FormInviteNew extends React.PureComponent {
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Submit label={label} disabled={isPending} />
|
<Submit label="Invite" disabled={isPending} />
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,7 +16,7 @@ const makeSelect = () => {
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
errorMessage: selectError(state, props),
|
errorMessage: selectError(state, props),
|
||||||
isPending: selectIsPending(state, props),
|
isPending: selectIsPending(state, props),
|
||||||
reward: selectReward(state, props),
|
reward: selectReward(state, props.reward_type),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
return select;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { doNotify, MODALS } from 'lbry-redux';
|
||||||
import { doNavigate } from 'redux/actions/navigation';
|
import { doNavigate } from 'redux/actions/navigation';
|
||||||
import { doUserIdentityVerify } from 'redux/actions/user';
|
import { doUserIdentityVerify } from 'redux/actions/user';
|
||||||
import rewards from 'rewards';
|
import rewards from 'rewards';
|
||||||
|
@ -8,15 +9,14 @@ import {
|
||||||
selectIdentityVerifyErrorMessage,
|
selectIdentityVerifyErrorMessage,
|
||||||
} from 'redux/selectors/user';
|
} from 'redux/selectors/user';
|
||||||
import UserVerify from './view';
|
import UserVerify from './view';
|
||||||
import { doNotify, MODALS } from 'lbry-redux';
|
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = state => {
|
||||||
const selectReward = makeSelectRewardByType();
|
const selectReward = makeSelectRewardByType();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isPending: selectIdentityVerifyIsPending(state),
|
isPending: selectIdentityVerifyIsPending(state),
|
||||||
errorMessage: selectIdentityVerifyErrorMessage(state),
|
errorMessage: selectIdentityVerifyErrorMessage(state),
|
||||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
reward: selectReward(state, rewards.TYPE_NEW_USER),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ const select = state => {
|
||||||
const selectReward = makeSelectRewardByType();
|
const selectReward = makeSelectRewardByType();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
reward: selectReward(state, rewards.TYPE_NEW_USER),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import CreditAmount from 'component/common/credit-amount';
|
||||||
|
|
||||||
class ModalFirstReward extends React.PureComponent {
|
class ModalFirstReward extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { closeModal, reward } = this.props;
|
const { closeModal } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -18,10 +18,7 @@ class ModalFirstReward extends React.PureComponent {
|
||||||
>
|
>
|
||||||
<section>
|
<section>
|
||||||
<h3 className="modal__header">{__('Your First Reward')}</h3>
|
<h3 className="modal__header">{__('Your First Reward')}</h3>
|
||||||
<p>
|
<p>{__('You just earned your first reward!')}</p>
|
||||||
{__('You just earned your first reward of')}{' '}
|
|
||||||
<CreditAmount amount={reward.reward_amount} />.
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
{__(
|
{__(
|
||||||
"This reward will show in your Wallet in the top right momentarily (if it hasn't already)."
|
"This reward will show in your Wallet in the top right momentarily (if it hasn't already)."
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import Lbryio from 'lbryio';
|
import Lbryio from 'lbryio';
|
||||||
import { doNotify, MODALS } from 'lbry-redux';
|
import { doNotify, MODALS } from 'lbry-redux';
|
||||||
import { selectUnclaimedRewardsByType } from 'redux/selectors/rewards';
|
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
|
||||||
import { selectUserIsRewardApproved } from 'redux/selectors/user';
|
import { selectUserIsRewardApproved } from 'redux/selectors/user';
|
||||||
import rewards from 'rewards';
|
import rewards from 'rewards';
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ export function doRewardList() {
|
||||||
export function doClaimRewardType(rewardType) {
|
export function doClaimRewardType(rewardType) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const rewardsByType = selectUnclaimedRewardsByType(state);
|
const unclaimedRewards = selectUnclaimedRewards(state);
|
||||||
const reward = rewardsByType[rewardType];
|
const reward = unclaimedRewards.find(ur => ur.reward_type === rewardType);
|
||||||
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
||||||
|
|
||||||
if (!reward || reward.transaction_id) {
|
if (!reward || reward.transaction_id) {
|
||||||
|
@ -84,14 +84,14 @@ export function doClaimRewardType(rewardType) {
|
||||||
export function doClaimEligiblePurchaseRewards() {
|
export function doClaimEligiblePurchaseRewards() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const rewardsByType = selectUnclaimedRewardsByType(state);
|
const unclaimedRewards = selectUnclaimedRewards(state);
|
||||||
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
||||||
|
|
||||||
if (!userIsRewardApproved || !Lbryio.enabled) {
|
if (!userIsRewardApproved || !Lbryio.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rewardsByType[rewards.TYPE_FIRST_STREAM]) {
|
if (unclaimedRewards.find(ur => ur.reward_type === rewards.TYPE_FIRST_STREAM)) {
|
||||||
dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
|
dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
|
||||||
} else {
|
} else {
|
||||||
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach(type => {
|
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach(type => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
fetching: false,
|
fetching: false,
|
||||||
claimedRewardsById: {}, // id => reward
|
claimedRewardsById: {}, // id => reward
|
||||||
unclaimedRewardsByType: {},
|
unclaimedRewards: [],
|
||||||
claimPendingByType: {},
|
claimPendingByType: {},
|
||||||
claimErrorsByType: {},
|
claimErrorsByType: {},
|
||||||
};
|
};
|
||||||
|
@ -17,19 +17,19 @@ reducers[ACTIONS.FETCH_REWARDS_STARTED] = state =>
|
||||||
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => {
|
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => {
|
||||||
const { userRewards } = action.data;
|
const { userRewards } = action.data;
|
||||||
|
|
||||||
const unclaimedRewards = {};
|
const unclaimedRewards = [];
|
||||||
const claimedRewards = {};
|
const claimedRewards = {};
|
||||||
userRewards.forEach(reward => {
|
userRewards.forEach(reward => {
|
||||||
if (reward.transaction_id) {
|
if (reward.transaction_id) {
|
||||||
claimedRewards[reward.id] = reward;
|
claimedRewards[reward.id] = reward;
|
||||||
} else {
|
} else {
|
||||||
unclaimedRewards[reward.reward_type] = reward;
|
unclaimedRewards.push(reward);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
claimedRewardsById: claimedRewards,
|
claimedRewardsById: claimedRewards,
|
||||||
unclaimedRewardsByType: unclaimedRewards,
|
unclaimedRewards,
|
||||||
fetching: false,
|
fetching: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -62,24 +62,21 @@ reducers[ACTIONS.CLAIM_REWARD_STARTED] = (state, action) => {
|
||||||
|
|
||||||
reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = (state, action) => {
|
reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = (state, action) => {
|
||||||
const { reward } = action.data;
|
const { reward } = action.data;
|
||||||
|
const { unclaimedRewards } = state;
|
||||||
|
|
||||||
const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType);
|
const index = unclaimedRewards.findIndex(ur => ur.reward_type === reward.reward_type);
|
||||||
const existingReward = unclaimedRewardsByType[reward.reward_type];
|
unclaimedRewards.splice(index, 1);
|
||||||
|
|
||||||
const newReward = Object.assign({}, reward, {
|
const { claimedRewardsById } = state;
|
||||||
reward_title: existingReward.reward_title,
|
claimedRewardsById[reward.id] = reward;
|
||||||
reward_description: existingReward.reward_description,
|
|
||||||
});
|
|
||||||
|
|
||||||
const claimedRewardsById = Object.assign({}, state.claimedRewardsById);
|
const newState = {
|
||||||
claimedRewardsById[reward.id] = newReward;
|
...state,
|
||||||
|
unclaimedRewards: [...unclaimedRewards],
|
||||||
|
claimedRewardsById: { ...claimedRewardsById },
|
||||||
|
};
|
||||||
|
|
||||||
const newState = Object.assign({}, state, {
|
return setClaimRewardState(newState, reward, false, '');
|
||||||
unclaimedRewardsByType,
|
|
||||||
claimedRewardsById,
|
|
||||||
});
|
|
||||||
|
|
||||||
return setClaimRewardState(newState, newReward, false, '');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.CLAIM_REWARD_FAILURE] = (state, action) => {
|
reducers[ACTIONS.CLAIM_REWARD_FAILURE] = (state, action) => {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import REWARDS from 'rewards';
|
|
||||||
|
|
||||||
const selectState = state => state.rewards || {};
|
const selectState = state => state.rewards || {};
|
||||||
|
|
||||||
|
@ -26,16 +25,7 @@ export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedR
|
||||||
}, {})
|
}, {})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectUnclaimedRewards = createSelector(
|
export const selectUnclaimedRewards = createSelector(selectState, state => state.unclaimedRewards);
|
||||||
selectUnclaimedRewardsByType,
|
|
||||||
byType =>
|
|
||||||
Object.values(byType).sort(
|
|
||||||
(a, b) =>
|
|
||||||
REWARDS.SORT_ORDER.indexOf(a.reward_type) < REWARDS.SORT_ORDER.indexOf(b.reward_type)
|
|
||||||
? -1
|
|
||||||
: 1
|
|
||||||
) || []
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching);
|
export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching);
|
||||||
|
|
||||||
|
@ -65,7 +55,8 @@ const selectClaimRewardError = (state, props) =>
|
||||||
export const makeSelectClaimRewardError = () =>
|
export const makeSelectClaimRewardError = () =>
|
||||||
createSelector(selectClaimRewardError, errorMessage => errorMessage);
|
createSelector(selectClaimRewardError, errorMessage => errorMessage);
|
||||||
|
|
||||||
const selectRewardByType = (state, props) => selectUnclaimedRewardsByType(state)[props.reward_type];
|
const selectRewardByType = (state, rewardType) =>
|
||||||
|
selectUnclaimedRewards(state).find(reward => reward.reward_type === rewardType);
|
||||||
|
|
||||||
export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward);
|
export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward);
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,6 @@
|
||||||
import { Lbry, doNotify } from 'lbry-redux';
|
import { Lbry, doNotify } from 'lbry-redux';
|
||||||
import Lbryio from 'lbryio';
|
import Lbryio from 'lbryio';
|
||||||
|
|
||||||
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),
|
|
||||||
verified_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 a bunch of things.', amount),
|
|
||||||
first_publish: __('You earned %s LBC for making your first publication.', amount),
|
|
||||||
featured_download: __('You earned %s LBC for watching a featured download.', amount),
|
|
||||||
referral: __('You earned %s LBC for referring someone.', amount),
|
|
||||||
youtube_creator: __('You earned %s LBC for syncing your YouTube channel.', amount),
|
|
||||||
}[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
const rewards = {};
|
const rewards = {};
|
||||||
|
|
||||||
rewards.TYPE_NEW_DEVELOPER = 'new_developer';
|
rewards.TYPE_NEW_DEVELOPER = 'new_developer';
|
||||||
|
@ -28,18 +13,6 @@ 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.YOUTUBE_CREATOR = 'youtube_creator';
|
rewards.YOUTUBE_CREATOR = 'youtube_creator';
|
||||||
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.YOUTUBE_CREATOR,
|
|
||||||
];
|
|
||||||
|
|
||||||
rewards.claimReward = type => {
|
rewards.claimReward = type => {
|
||||||
function requestReward(resolve, reject, params) {
|
function requestReward(resolve, reject, params) {
|
||||||
|
@ -48,7 +21,8 @@ rewards.claimReward = type => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Lbryio.call('reward', 'new', params, 'post').then(reward => {
|
Lbryio.call('reward', 'new', params, 'post').then(reward => {
|
||||||
const message = rewardMessage(type, reward.reward_amount);
|
const message =
|
||||||
|
reward.reward_notification || `You have claimed a ${reward.reward_amount} LBC reward.`;
|
||||||
|
|
||||||
// Display global notice
|
// Display global notice
|
||||||
const action = doNotify({
|
const action = doNotify({
|
||||||
|
|
Loading…
Add table
Reference in a new issue