Working on rewards refactor
This commit is contained in:
parent
e0b8afe028
commit
eb170b9720
11 changed files with 394 additions and 27 deletions
|
@ -20,16 +20,26 @@ export function doFetchRewards() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doClaimReward(rewardType) {
|
||||
export function doClaimReward(reward) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_STARTED,
|
||||
data: { reward }
|
||||
})
|
||||
try {
|
||||
rewards.claimReward(rewards[rewardType]);
|
||||
dispatch({
|
||||
type: types.REWARD_CLAIMED,
|
||||
data: {
|
||||
reward: rewards[rewardType],
|
||||
},
|
||||
});
|
||||
} catch (err) {}
|
||||
};
|
||||
const success = (a) => {
|
||||
console.log(a)
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_COMPLETED,
|
||||
data: {
|
||||
a
|
||||
}
|
||||
})
|
||||
}
|
||||
const failure = (a) => console.error(a)
|
||||
rewards.claimReward(reward.reward_type).then(success, failure)
|
||||
} catch(err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
27
ui/js/component/auth/index.js
Normal file
27
ui/js/component/auth/index.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
connect,
|
||||
} from 'react-redux'
|
||||
import {
|
||||
selectFetchingRewards,
|
||||
selectClaimedRewardsByType,
|
||||
makeSelectRewardByType,
|
||||
} from 'selectors/rewards'
|
||||
import AuthOverlay from './view'
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectRewardByType = makeSelectRewardByType()
|
||||
|
||||
const select = (state) => ({
|
||||
fetchingRewards: selectFetchingRewards(state),
|
||||
claimedRewardsByType: selectClaimedRewardsByType(state),
|
||||
newUserReward: selectRewardByType(state, { reward_type: 'new_user' }),
|
||||
})
|
||||
|
||||
return select
|
||||
}
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
})
|
||||
|
||||
export default connect(makeSelect, perform)(AuthOverlay)
|
|
@ -8,7 +8,6 @@ import { RewardLink } from "component/reward-link";
|
|||
import { FormRow } from "../component/form.js";
|
||||
import { CreditAmount, Address } from "../component/common.js";
|
||||
import { getLocal, setLocal } from "../utils.js";
|
||||
import rewards from "../rewards";
|
||||
|
||||
class SubmitEmailStage extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -197,7 +196,7 @@ class WelcomeStage extends React.Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
hasReward: false,
|
||||
hasReward: true,
|
||||
rewardAmount: null,
|
||||
};
|
||||
}
|
||||
|
@ -210,7 +209,18 @@ class WelcomeStage extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return !this.state.hasReward
|
||||
const {
|
||||
claimedRewardsByType,
|
||||
fetchingRewards,
|
||||
newUserReward,
|
||||
} = this.props
|
||||
|
||||
const hasReward = claimedRewardsByType.length > 0
|
||||
|
||||
if (fetchingRewards) return null
|
||||
if (!newUserReward) return null
|
||||
|
||||
return !hasReward
|
||||
? <Modal
|
||||
type="custom"
|
||||
isOpen={true}
|
||||
|
@ -451,18 +461,11 @@ export class AuthOverlay extends React.Component {
|
|||
this.setStage("email", {});
|
||||
}
|
||||
} else {
|
||||
lbryio.call("reward", "list", {}).then(userRewards => {
|
||||
userRewards.filter(function(reward) {
|
||||
return (
|
||||
reward.reward_type == rewards.TYPE_NEW_USER &&
|
||||
reward.transaction_id
|
||||
);
|
||||
}).length
|
||||
? this.setStage(null)
|
||||
: this.setStage("welcome");
|
||||
});
|
||||
}
|
||||
})
|
||||
const {
|
||||
claimedRewardsByType,
|
||||
} = this.props
|
||||
claimedRewardsByType[rewards.TYPE_NEW_USER] ? this.setStage(null) : this.setStage("welcome")
|
||||
}})
|
||||
.catch(err => {
|
||||
this.setStage("error", { errorText: err.message });
|
||||
document.dispatchEvent(
|
||||
|
@ -510,3 +513,5 @@ export class AuthOverlay extends React.Component {
|
|||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthOverlay
|
27
ui/js/component/rewardLink/index.js
Normal file
27
ui/js/component/rewardLink/index.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
connect,
|
||||
} from 'react-redux'
|
||||
import {
|
||||
makeSelectHasClaimedReward,
|
||||
} from 'selectors/rewards'
|
||||
import {
|
||||
doClaimReward,
|
||||
} from 'actions/rewards'
|
||||
import RewardLink from './view'
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectHasClaimedReward = makeSelectHasClaimedReward()
|
||||
|
||||
const select = (state, props) => ({
|
||||
claimed: selectHasClaimedReward(state, props)
|
||||
})
|
||||
|
||||
return select
|
||||
}
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
claimReward: (reward) => dispatch(doClaimReward(reward)),
|
||||
})
|
||||
|
||||
export default connect(makeSelect, perform)(RewardLink)
|
120
ui/js/component/rewardLink/view.jsx
Normal file
120
ui/js/component/rewardLink/view.jsx
Normal file
|
@ -0,0 +1,120 @@
|
|||
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'
|
||||
|
||||
// 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>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
const RewardLink = (props) => {
|
||||
const {
|
||||
reward,
|
||||
claimed,
|
||||
button,
|
||||
pending,
|
||||
claimable = true,
|
||||
claimReward,
|
||||
errorMessage,
|
||||
clearError,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div className="reward-link">
|
||||
{claimed
|
||||
? <span><Icon icon="icon-check" /> Reward claimed.</span>
|
||||
: <Link button={button ? button : 'alt'} disabled={pending || !claimable }
|
||||
label={ pending ? "Claiming..." : "Claim Reward"} onClick={() => { claimReward(reward) }} />}
|
||||
{errorMessage ?
|
||||
<Modal isOpen={true} contentLabel="Reward Claim Error" className="error-modal" onConfirmed={() => { clearError() }}>
|
||||
{errorMessage}
|
||||
</Modal>
|
||||
: ''}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default RewardLink
|
|
@ -74,3 +74,10 @@ export const DAEMON_SETTINGS_RECEIVED = "DAEMON_SETTINGS_RECEIVED";
|
|||
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED'
|
||||
export const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS'
|
||||
export const AUTHENTICATION_FAILURE = 'AUTHENTICATION_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_COMPLETED = 'CLAIM_REWARD_COMPLETED'
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import SnackBar from 'component/snackBar';
|
|||
import { Provider } from 'react-redux';
|
||||
import store from 'store.js';
|
||||
import SplashScreen from 'component/splash.js';
|
||||
import { AuthOverlay } from 'component/auth.js';
|
||||
import AuthOverlay from 'component/authOverlay';
|
||||
import { doChangePath, doNavigate, doDaemonReady } from 'actions/app';
|
||||
import { toQueryString } from 'util/query_params';
|
||||
|
||||
|
|
19
ui/js/page/rewards/index.js
Normal file
19
ui/js/page/rewards/index.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
connect,
|
||||
} from 'react-redux'
|
||||
import {
|
||||
selectFetchingRewards,
|
||||
selectRewards,
|
||||
} from 'selectors/rewards'
|
||||
import RewardsPage from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
fetching: selectFetchingRewards(state),
|
||||
rewards: selectRewards(state),
|
||||
})
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
})
|
||||
|
||||
export default connect(select, perform)(RewardsPage)
|
54
ui/js/page/rewards/view.jsx
Normal file
54
ui/js/page/rewards/view.jsx
Normal file
|
@ -0,0 +1,54 @@
|
|||
import React from 'react';
|
||||
import lbryio from 'lbryio';
|
||||
import {CreditAmount, Icon} from 'component/common';
|
||||
import SubHeader from 'component/subHeader'
|
||||
import RewardLink from 'component/rewardLink';
|
||||
|
||||
const RewardTile = (props) => {
|
||||
const {
|
||||
reward,
|
||||
} = props
|
||||
|
||||
const claimed = !!reward.transaction_id
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-primary">
|
||||
<CreditAmount amount={reward.reward_amount} />
|
||||
<h3>{reward.title}</h3>
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
{claimed
|
||||
? <span><Icon icon="icon-check" /> Reward claimed.</span>
|
||||
: <RewardLink reward={reward} />}
|
||||
</div>
|
||||
<div className="card__content">{reward.reward_description}</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const RewardsPage = (props) => {
|
||||
const {
|
||||
fetching,
|
||||
rewards,
|
||||
} = props
|
||||
|
||||
let content
|
||||
|
||||
if (fetching) content = <div className="empty">Fetching rewards</div>
|
||||
if (!fetching && rewards.length == 0) content = <div className="empty">Failed to load rewards.</div>
|
||||
if (!fetching && rewards.length > 0) {
|
||||
content = rewards.map(reward => <RewardTile key={reward.reward_type} reward={reward} />)
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
{content}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default RewardsPage;
|
|
@ -3,6 +3,53 @@ import * as types from "constants/action_types";
|
|||
const reducers = {};
|
||||
const defaultState = {};
|
||||
|
||||
reducers[types.FETCH_REWARDS_STARTED] = function(state, action) {
|
||||
const newRewards = Object.assign({}, state.rewards, {
|
||||
fetching: true,
|
||||
})
|
||||
|
||||
return Object.assign({}, state, newRewards)
|
||||
}
|
||||
|
||||
reducers[types.FETCH_REWARDS_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
userRewards,
|
||||
} = action.data
|
||||
|
||||
const byRewardType = {}
|
||||
userRewards.forEach(reward => byRewardType[reward.reward_type] = reward)
|
||||
const newRewards = Object.assign({}, state.rewards, {
|
||||
byRewardType: byRewardType,
|
||||
fetching: false
|
||||
})
|
||||
|
||||
return Object.assign({}, state, newRewards)
|
||||
}
|
||||
|
||||
reducers[types.CLAIM_REWARD_STARTED] = function(state, action) {
|
||||
const {
|
||||
reward,
|
||||
} = action.data
|
||||
|
||||
const newRewards = Object.assign({}, state, {
|
||||
claiming: true,
|
||||
})
|
||||
|
||||
return Object.assign({}, state, newRewards)
|
||||
}
|
||||
|
||||
reducers[types.CLAIM_REWARD_COMPLETED] = function(state, action) {
|
||||
const {
|
||||
reward,
|
||||
} = action.data
|
||||
|
||||
const newRewards = Object.assign({}, state, {
|
||||
claiming: false,
|
||||
})
|
||||
|
||||
return Object.assign({}, state, newRewards)
|
||||
}
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
|
|
|
@ -1,3 +1,54 @@
|
|||
import { createSelector } from "reselect";
|
||||
|
||||
export const _selectState = state => state.rewards || {};
|
||||
const _selectState = state => state.rewards || {};
|
||||
|
||||
export const selectRewardsByType = createSelector(
|
||||
_selectState,
|
||||
(state) => state.byRewardType || {}
|
||||
)
|
||||
|
||||
export const selectRewards = createSelector(
|
||||
selectRewardsByType,
|
||||
(byType) => Object.values(byType) || []
|
||||
)
|
||||
|
||||
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.reward_type]
|
||||
}
|
||||
|
||||
export const makeSelectHasClaimedReward = () => {
|
||||
return createSelector(
|
||||
selectHasClaimedReward,
|
||||
(claimed) => claimed
|
||||
)
|
||||
}
|
||||
|
||||
const selectRewardByType = (state, props) => {
|
||||
return selectRewardsByType(state)[props.reward_type]
|
||||
}
|
||||
|
||||
export const makeSelectRewardByType = () => {
|
||||
return createSelector(
|
||||
selectRewardByType,
|
||||
(reward) => reward
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue