Automatically claim initial rewards (new_user & email_verified) when … #6807
6 changed files with 110 additions and 42 deletions
|
@ -17,6 +17,8 @@ import {
|
|||
} from 'lbry-redux';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { doUpdateBlockListForPublishedChannel } from 'redux/actions/comments';
|
||||
import { doClaimInitialRewards } from 'redux/actions/rewards';
|
||||
import { selectIsClaimingInitialRewards, selectHasClaimedInitialRewards } from 'redux/selectors/rewards';
|
||||
import ChannelForm from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
@ -36,6 +38,8 @@ const select = (state, props) => ({
|
|||
createError: selectCreateChannelError(state),
|
||||
creatingChannel: selectCreatingChannel(state),
|
||||
balance: selectBalance(state),
|
||||
isClaimingInitialRewards: selectIsClaimingInitialRewards(state),
|
||||
hasClaimedInitialRewards: selectHasClaimedInitialRewards(state),
|
||||
});
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
|
@ -50,6 +54,7 @@ const perform = (dispatch) => ({
|
|||
);
|
||||
},
|
||||
clearChannelErrors: () => dispatch(doClearChannelErrors()),
|
||||
claimInitialRewards: () => dispatch(doClaimInitialRewards()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(ChannelForm);
|
||||
|
|
|
@ -48,6 +48,7 @@ type Props = {
|
|||
createError: string,
|
||||
creatingChannel: boolean,
|
||||
clearChannelErrors: () => void,
|
||||
claimInitialRewards: () => void,
|
||||
onDone: () => void,
|
||||
openModal: (
|
||||
id: string,
|
||||
|
@ -55,6 +56,8 @@ type Props = {
|
|||
) => void,
|
||||
uri: string,
|
||||
disabled: boolean,
|
||||
isClaimingInitialRewards: boolean,
|
||||
hasClaimedInitialRewards: boolean,
|
||||
};
|
||||
|
||||
function ChannelForm(props: Props) {
|
||||
|
@ -79,8 +82,11 @@ function ChannelForm(props: Props) {
|
|||
creatingChannel,
|
||||
createError,
|
||||
clearChannelErrors,
|
||||
claimInitialRewards,
|
||||
openModal,
|
||||
disabled,
|
||||
isClaimingInitialRewards,
|
||||
hasClaimedInitialRewards,
|
||||
} = props;
|
||||
const [nameError, setNameError] = React.useState(undefined);
|
||||
const [bidError, setBidError] = React.useState('');
|
||||
|
@ -94,6 +100,22 @@ function ChannelForm(props: Props) {
|
|||
const languageParam = params.languages;
|
||||
const primaryLanguage = Array.isArray(languageParam) && languageParam.length && languageParam[0];
|
||||
const secondaryLanguage = Array.isArray(languageParam) && languageParam.length >= 2 && languageParam[1];
|
||||
const submitLabel = React.useMemo(() => {
|
||||
if (isClaimingInitialRewards) {
|
||||
return __('Claiming credits...');
|
||||
}
|
||||
return creatingChannel || updatingChannel ? __('Submitting') : __('Submit');
|
||||
}, [isClaimingInitialRewards, creatingChannel, updatingChannel]);
|
||||
const submitDisabled = React.useMemo(() => {
|
||||
return (
|
||||
isClaimingInitialRewards ||
|
||||
creatingChannel ||
|
||||
updatingChannel ||
|
||||
nameError ||
|
||||
bidError ||
|
||||
(isNewChannel && !params.name)
|
||||
);
|
||||
}, [isClaimingInitialRewards, creatingChannel, updatingChannel, nameError, bidError, isNewChannel, params]);
|
||||
|
||||
function getChannelParams() {
|
||||
// fill this in with sdk data
|
||||
|
@ -219,6 +241,12 @@ function ChannelForm(props: Props) {
|
|||
clearChannelErrors();
|
||||
}, [clearChannelErrors]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!hasClaimedInitialRewards) {
|
||||
claimInitialRewards();
|
||||
}
|
||||
}, [hasClaimedInitialRewards, claimInitialRewards]);
|
||||
|
||||
// TODO clear and bail after submit
|
||||
return (
|
||||
<>
|
||||
|
@ -453,14 +481,7 @@ function ChannelForm(props: Props) {
|
|||
actions={
|
||||
<>
|
||||
<div className="section__actions">
|
||||
<Button
|
||||
button="primary"
|
||||
disabled={
|
||||
creatingChannel || updatingChannel || nameError || bidError || (isNewChannel && !params.name)
|
||||
}
|
||||
label={creatingChannel || updatingChannel ? __('Submitting') : __('Submit')}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<Button button="primary" disabled={submitDisabled} label={submitLabel} onClick={handleSubmit} />
|
||||
<Button button="link" label={__('Cancel')} onClick={onDone} />
|
||||
</div>
|
||||
{errorMsg ? (
|
||||
|
|
|
@ -18,7 +18,12 @@ import {
|
|||
} from 'lbry-redux';
|
||||
import * as RENDER_MODES from 'constants/file_render_modes';
|
||||
import { doPublishDesktop } from 'redux/actions/publish';
|
||||
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
|
||||
import { doClaimInitialRewards } from 'redux/actions/rewards';
|
||||
import {
|
||||
selectUnclaimedRewardValue,
|
||||
selectIsClaimingInitialRewards,
|
||||
selectHasClaimedInitialRewards,
|
||||
} from 'redux/selectors/rewards';
|
||||
import {
|
||||
selectModal,
|
||||
selectActiveChannelClaim,
|
||||
|
@ -27,8 +32,8 @@ import {
|
|||
} from 'redux/selectors/app';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { makeSelectFileRenderModeForUri } from 'redux/selectors/content';
|
||||
import PublishPage from './view';
|
||||
import { selectUser } from 'redux/selectors/user';
|
||||
import PublishPage from './view';
|
||||
|
||||
const select = (state) => {
|
||||
const myClaimForUri = selectMyClaimForUri(state);
|
||||
|
@ -59,6 +64,8 @@ const select = (state) => {
|
|||
myChannels: selectMyChannelClaims(state),
|
||||
incognito: selectIncognito(state),
|
||||
activeChannelStakedLevel: selectActiveChannelStakedLevel(state),
|
||||
isClaimingInitialRewards: selectIsClaimingInitialRewards(state),
|
||||
hasClaimedInitialRewards: selectHasClaimedInitialRewards(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -70,6 +77,7 @@ const perform = (dispatch) => ({
|
|||
prepareEdit: (claim, uri) => dispatch(doPrepareEdit(claim, uri)),
|
||||
resetThumbnailStatus: () => dispatch(doResetThumbnailStatus()),
|
||||
checkAvailability: (name) => dispatch(doCheckPublishNameAvailability(name)),
|
||||
claimInitialRewards: () => dispatch(doClaimInitialRewards()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(PublishPage);
|
||||
|
|
|
@ -90,6 +90,9 @@ type Props = {
|
|||
isPostClaim: boolean,
|
||||
permanentUrl: ?string,
|
||||
remoteUrl: ?string,
|
||||
isClaimingInitialRewards: boolean,
|
||||
claimInitialRewards: () => void,
|
||||
hasClaimedInitialRewards: boolean,
|
||||
};
|
||||
|
||||
function PublishForm(props: Props) {
|
||||
|
@ -128,6 +131,9 @@ function PublishForm(props: Props) {
|
|||
isPostClaim,
|
||||
permanentUrl,
|
||||
remoteUrl,
|
||||
isClaimingInitialRewards,
|
||||
claimInitialRewards,
|
||||
hasClaimedInitialRewards,
|
||||
} = props;
|
||||
|
||||
const { replace, location } = useHistory();
|
||||
|
@ -263,6 +269,12 @@ function PublishForm(props: Props) {
|
|||
}
|
||||
}, [activeChannelClaimStr, setSignedMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasClaimedInitialRewards) {
|
||||
claimInitialRewards();
|
||||
}
|
||||
}, [hasClaimedInitialRewards, claimInitialRewards]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!modal) {
|
||||
setTimeout(() => {
|
||||
|
@ -298,7 +310,10 @@ function PublishForm(props: Props) {
|
|||
|
||||
const isLivestreamMode = mode === PUBLISH_MODES.LIVESTREAM;
|
||||
let submitLabel;
|
||||
if (publishing) {
|
||||
|
||||
if (isClaimingInitialRewards) {
|
||||
submitLabel = __('Claiming credits...');
|
||||
} else if (publishing) {
|
||||
if (isStillEditing) {
|
||||
submitLabel = __('Saving...');
|
||||
} else if (isLivestreamMode) {
|
||||
|
@ -623,6 +638,7 @@ function PublishForm(props: Props) {
|
|||
onClick={handlePublish}
|
||||
label={submitLabel}
|
||||
disabled={
|
||||
isClaimingInitialRewards ||
|
||||
formDisabled ||
|
||||
!formValid ||
|
||||
uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS ||
|
||||
|
|
|
@ -6,13 +6,13 @@ import { doFetchInviteStatus } from 'redux/actions/user';
|
|||
import rewards from 'rewards';
|
||||
|
||||
export function doRewardList() {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_REWARDS_STARTED,
|
||||
});
|
||||
|
||||
Lbryio.call('reward', 'list', { multiple_rewards_per_type: true })
|
||||
.then(userRewards => {
|
||||
.then((userRewards) => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards },
|
||||
|
@ -35,7 +35,7 @@ export function doClaimRewardType(rewardType, options = {}) {
|
|||
const reward =
|
||||
rewardType === rewards.TYPE_REWARD_CODE || rewardType === rewards.TYPE_NEW_ANDROID
|
||||
? { reward_type: rewards.TYPE_REWARD_CODE }
|
||||
: unclaimedRewards.find(ur => ur.reward_type === rewardType);
|
||||
: unclaimedRewards.find((ur) => ur.reward_type === rewardType);
|
||||
|
||||
// Try to claim the email reward right away, even if we haven't called reward_list yet
|
||||
if (
|
||||
|
@ -74,7 +74,7 @@ export function doClaimRewardType(rewardType, options = {}) {
|
|||
data: { reward },
|
||||
});
|
||||
|
||||
const success = successReward => {
|
||||
const success = (successReward) => {
|
||||
// Temporary timeout to ensure the sdk has the correct balance after claiming a reward
|
||||
setTimeout(() => {
|
||||
dispatch(doUpdateBalance()).then(() => {
|
||||
|
@ -99,7 +99,7 @@ export function doClaimRewardType(rewardType, options = {}) {
|
|||
}, 2000);
|
||||
};
|
||||
|
||||
const failure = error => {
|
||||
const failure = (error) => {
|
||||
dispatch({
|
||||
type: ACTIONS.CLAIM_REWARD_FAILURE,
|
||||
data: {
|
||||
|
@ -121,6 +121,13 @@ export function doClaimRewardType(rewardType, options = {}) {
|
|||
};
|
||||
}
|
||||
|
||||
export function doClaimInitialRewards() {
|
||||
return (dispatch) => {
|
||||
dispatch(doClaimRewardType(rewards.TYPE_NEW_USER));
|
||||
dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL));
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimEligiblePurchaseRewards() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
@ -131,10 +138,10 @@ export function doClaimEligiblePurchaseRewards() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (unclaimedRewards.find(ur => ur.reward_type === rewards.TYPE_FIRST_STREAM)) {
|
||||
if (unclaimedRewards.find((ur) => ur.reward_type === rewards.TYPE_FIRST_STREAM)) {
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
|
||||
} else {
|
||||
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_DAILY_VIEW].forEach(type => {
|
||||
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_DAILY_VIEW].forEach((type) => {
|
||||
dispatch(doClaimRewardType(type, { failSilently: true }));
|
||||
});
|
||||
}
|
||||
|
@ -142,7 +149,7 @@ export function doClaimEligiblePurchaseRewards() {
|
|||
}
|
||||
|
||||
export function doClaimRewardClearError(reward) {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.CLAIM_REWARD_CLEAR_ERROR,
|
||||
data: { reward },
|
||||
|
@ -151,8 +158,8 @@ export function doClaimRewardClearError(reward) {
|
|||
}
|
||||
|
||||
export function doFetchRewardedContent() {
|
||||
return dispatch => {
|
||||
const success = nameToClaimId => {
|
||||
return (dispatch) => {
|
||||
const success = (nameToClaimId) => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
|
||||
data: {
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import REWARDS from 'rewards';
|
||||
|
||||
const selectState = state => state.rewards || {};
|
||||
const selectState = (state) => state.rewards || {};
|
||||
|
||||
export const selectUnclaimedRewardsByType = createSelector(selectState, state => state.unclaimedRewardsByType);
|
||||
export const selectUnclaimedRewardsByType = createSelector(selectState, (state) => state.unclaimedRewardsByType);
|
||||
|
||||
export const selectClaimedRewardsById = createSelector(selectState, state => state.claimedRewardsById);
|
||||
export const selectClaimedRewardsById = createSelector(selectState, (state) => state.claimedRewardsById);
|
||||
|
||||
export const selectClaimedRewards = createSelector(selectClaimedRewardsById, byId => Object.values(byId) || []);
|
||||
export const selectClaimedRewards = createSelector(selectClaimedRewardsById, (byId) => Object.values(byId) || []);
|
||||
|
||||
export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedRewards, rewards =>
|
||||
export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedRewards, (rewards) =>
|
||||
rewards.reduce((mapParam, reward) => {
|
||||
const map = mapParam;
|
||||
map[reward.transaction_id] = reward;
|
||||
|
@ -17,47 +17,58 @@ export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedR
|
|||
}, {})
|
||||
);
|
||||
|
||||
export const selectUnclaimedRewards = createSelector(selectState, state => state.unclaimedRewards);
|
||||
export const selectUnclaimedRewards = createSelector(selectState, (state) => state.unclaimedRewards);
|
||||
|
||||
export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching);
|
||||
export const selectFetchingRewards = createSelector(selectState, (state) => !!state.fetching);
|
||||
|
||||
export const selectUnclaimedRewardValue = createSelector(selectUnclaimedRewards, rewards =>
|
||||
export const selectUnclaimedRewardValue = createSelector(selectUnclaimedRewards, (rewards) =>
|
||||
rewards.reduce((sum, reward) => sum + reward.reward_amount, 0)
|
||||
);
|
||||
|
||||
export const selectClaimsPendingByType = createSelector(selectState, state => state.claimPendingByType);
|
||||
export const selectClaimsPendingByType = createSelector(selectState, (state) => state.claimPendingByType);
|
||||
|
||||
const selectIsClaimRewardPending = (state, props) => selectClaimsPendingByType(state, props)[props.reward_type];
|
||||
|
||||
export const makeSelectIsRewardClaimPending = () =>
|
||||
createSelector(selectIsClaimRewardPending, isClaiming => isClaiming);
|
||||
createSelector(selectIsClaimRewardPending, (isClaiming) => isClaiming);
|
||||
|
||||
export const selectClaimErrorsByType = createSelector(selectState, state => state.claimErrorsByType);
|
||||
export const selectClaimErrorsByType = createSelector(selectState, (state) => state.claimErrorsByType);
|
||||
|
||||
const selectClaimRewardError = (state, props) => selectClaimErrorsByType(state, props)[props.reward_type];
|
||||
|
||||
export const makeSelectClaimRewardError = () => createSelector(selectClaimRewardError, errorMessage => errorMessage);
|
||||
export const makeSelectClaimRewardError = () => createSelector(selectClaimRewardError, (errorMessage) => errorMessage);
|
||||
|
||||
const selectRewardByType = (state, rewardType) =>
|
||||
selectUnclaimedRewards(state).find(reward => reward.reward_type === rewardType);
|
||||
selectUnclaimedRewards(state).find((reward) => reward.reward_type === rewardType);
|
||||
|
||||
export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward);
|
||||
export const makeSelectRewardByType = () => createSelector(selectRewardByType, (reward) => reward);
|
||||
|
||||
const selectRewardByClaimCode = (state, claimCode) =>
|
||||
selectUnclaimedRewards(state).find(reward => reward.claim_code === claimCode);
|
||||
selectUnclaimedRewards(state).find((reward) => reward.claim_code === claimCode);
|
||||
|
||||
export const makeSelectRewardByClaimCode = () => createSelector(selectRewardByClaimCode, reward => reward);
|
||||
export const makeSelectRewardByClaimCode = () => createSelector(selectRewardByClaimCode, (reward) => reward);
|
||||
|
||||
export const makeSelectRewardAmountByType = () =>
|
||||
createSelector(selectRewardByType, reward => (reward ? reward.reward_amount : 0));
|
||||
createSelector(selectRewardByType, (reward) => (reward ? reward.reward_amount : 0));
|
||||
|
||||
export const selectRewardContentClaimIds = createSelector(selectState, state => state.rewardedContentClaimIds);
|
||||
export const selectRewardContentClaimIds = createSelector(selectState, (state) => state.rewardedContentClaimIds);
|
||||
|
||||
export const selectReferralReward = createSelector(
|
||||
selectUnclaimedRewards,
|
||||
unclaimedRewards => unclaimedRewards.filter(reward => reward.reward_type === REWARDS.TYPE_REFERRAL)[0]
|
||||
(unclaimedRewards) => unclaimedRewards.filter((reward) => reward.reward_type === REWARDS.TYPE_REFERRAL)[0]
|
||||
);
|
||||
|
||||
export const selectHasUnclaimedRefereeReward = createSelector(selectUnclaimedRewards, unclaimedRewards =>
|
||||
unclaimedRewards.some(reward => reward.reward_type === REWARDS.TYPE_REFEREE)
|
||||
export const selectHasUnclaimedRefereeReward = createSelector(selectUnclaimedRewards, (unclaimedRewards) =>
|
||||
unclaimedRewards.some((reward) => reward.reward_type === REWARDS.TYPE_REFEREE)
|
||||
);
|
||||
|
||||
export const selectIsClaimingInitialRewards = createSelector(selectClaimsPendingByType, (claimsPendingByType) => {
|
||||
return !!(claimsPendingByType[REWARDS.TYPE_NEW_USER] || claimsPendingByType[REWARDS.TYPE_CONFIRM_EMAIL]);
|
||||
});
|
||||
|
||||
export const selectHasClaimedInitialRewards = createSelector(selectClaimedRewardsById, (claimedRewardsById) => {
|
||||
const claims = Object.values(claimedRewardsById);
|
||||
const newUserClaimed = !!claims.find((claim) => claim && claim.reward_type === REWARDS.TYPE_NEW_USER);
|
||||
const confirmEmailClaimed = !!claims.find((claim) => claim && claim.reward_type === REWARDS.TYPE_CONFIRM_EMAIL);
|
||||
return newUserClaimed && confirmEmailClaimed;
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue