diff --git a/.env.defaults b/.env.defaults index af17f2375..1c519e9bb 100644 --- a/.env.defaults +++ b/.env.defaults @@ -41,7 +41,6 @@ SITE_HELP_EMAIL=help@lbry.com LOGO_TITLE=lbry.tv ## Social media TWITTER_ACCOUNT=LBRYcom -BRANDED_SITE=odysee ## IMAGE ASSETS YRBL_HAPPY_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a diff --git a/package.json b/package.json index 0c4ee9992..d387e3456 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "imagesloaded": "^4.1.4", "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", - "lbry-redux": "lbryio/lbry-redux#e4d0662100a5f4b28bb1bf3cbc1e51b2eebab5b6", + "lbry-redux": "lbryio/lbry-redux#7cc9923ed9ff1940b508842af6be44c8da906a60", "lbryinc": "lbryio/lbryinc#8f9a58bfc8312a65614fd7327661cdcc502c4e59", "lint-staged": "^7.0.2", "localforage": "^1.7.1", diff --git a/ui/component/channelEdit/index.js b/ui/component/channelEdit/index.js index 82618960f..9a195039b 100644 --- a/ui/component/channelEdit/index.js +++ b/ui/component/channelEdit/index.js @@ -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); diff --git a/ui/component/channelEdit/view.jsx b/ui/component/channelEdit/view.jsx index ccd452142..61c9df69f 100644 --- a/ui/component/channelEdit/view.jsx +++ b/ui/component/channelEdit/view.jsx @@ -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={ <>
-
{errorMsg ? ( diff --git a/ui/component/publishBid/index.js b/ui/component/publishBid/index.js index 17ce7cf2a..c570d5d7d 100644 --- a/ui/component/publishBid/index.js +++ b/ui/component/publishBid/index.js @@ -3,25 +3,23 @@ import { makeSelectPublishFormValue, selectMyClaimForUri, selectIsResolvingPublishUris, - selectTakeOverAmount, doUpdatePublishForm, doPrepareEdit, selectBalance, } from 'lbry-redux'; import PublishPage from './view'; -const select = (state) => ({ +const select = state => ({ name: makeSelectPublishFormValue('name')(state), bid: makeSelectPublishFormValue('bid')(state), uri: makeSelectPublishFormValue('uri')(state), isResolvingUri: selectIsResolvingPublishUris(state), balance: selectBalance(state), myClaimForUri: selectMyClaimForUri(state), - amountNeededForTakeover: selectTakeOverAmount(state), }); -const perform = (dispatch) => ({ - updatePublishForm: (value) => dispatch(doUpdatePublishForm(value)), +const perform = dispatch => ({ + updatePublishForm: value => dispatch(doUpdatePublishForm(value)), prepareEdit: (claim, uri) => dispatch(doPrepareEdit(claim, uri)), }); diff --git a/ui/component/publishForm/index.js b/ui/component/publishForm/index.js index 47ed81a46..58b24bdb5 100644 --- a/ui/component/publishForm/index.js +++ b/ui/component/publishForm/index.js @@ -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); diff --git a/ui/component/publishForm/view.jsx b/ui/component/publishForm/view.jsx index 2089fb6ca..eac23885b 100644 --- a/ui/component/publishForm/view.jsx +++ b/ui/component/publishForm/view.jsx @@ -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 || diff --git a/ui/component/publishName/index.js b/ui/component/publishName/index.js index 4159056e3..67f5142fb 100644 --- a/ui/component/publishName/index.js +++ b/ui/component/publishName/index.js @@ -3,7 +3,6 @@ import { makeSelectPublishFormValue, selectIsStillEditing, selectMyClaimForUri, - selectTakeOverAmount, doUpdatePublishForm, doPrepareEdit, } from 'lbry-redux'; @@ -18,7 +17,6 @@ const select = (state) => ({ myClaimForUri: selectMyClaimForUri(state), activeChannelClaim: selectActiveChannelClaim(state), incognito: selectIncognito(state), - amountNeededForTakeover: selectTakeOverAmount(state), }); const perform = (dispatch) => ({ diff --git a/ui/redux/actions/rewards.js b/ui/redux/actions/rewards.js index 4af3b8c07..98351fd42 100644 --- a/ui/redux/actions/rewards.js +++ b/ui/redux/actions/rewards.js @@ -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: { diff --git a/ui/redux/selectors/reactions.js b/ui/redux/selectors/reactions.js index 76feeebdc..0058d2095 100644 --- a/ui/redux/selectors/reactions.js +++ b/ui/redux/selectors/reactions.js @@ -54,10 +54,11 @@ export const makeSelectLikeCountForUri = (uri) => export const makeSelectDislikeCountForUri = (uri) => createSelector(makeSelectClaimForUri(uri), makeSelectReactionsForUri(uri), (claim, reactions) => { - if (!claim || !reactions || reactions.my_reactions === null || reactions.others_reactions === null) { + const claimId = claim.claim_id; + + if (!reactions || reactions.my_reactions === null || reactions.others_reactions === null) { return 0; } - const claimId = claim.claim_id; let count = 0; if (reactions.others_reactions) { diff --git a/ui/redux/selectors/rewards.js b/ui/redux/selectors/rewards.js index c70b3cdc8..00d6d390a 100644 --- a/ui/redux/selectors/rewards.js +++ b/ui/redux/selectors/rewards.js @@ -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; +}); diff --git a/web/component/nag-sunset.jsx b/web/component/nag-sunset.jsx index 27c60eb44..37cd8a5ce 100644 --- a/web/component/nag-sunset.jsx +++ b/web/component/nag-sunset.jsx @@ -4,7 +4,6 @@ import Nag from 'component/common/nag'; import I18nMessage from 'component/i18nMessage'; import * as PAGES from 'constants/pages'; import { useHistory } from 'react-router'; -import Button from '../../ui/component/button'; type Props = { email?: User, @@ -21,15 +20,7 @@ export default function NagSunset(props: Props) { return ( , - }} - > - lbry.tv has been retired (%more%). You have been magically transported to Odysee.com - - } + message={lbry.tv has been retired. You have been magically transported to odysee.com} actionText={__('Sign In')} onClick={!email ? handleOnClick : undefined} onClose={onClose} diff --git a/yarn.lock b/yarn.lock index 9ac3c7109..3206d9f07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10129,9 +10129,9 @@ lazy-val@^1.0.4: yargs "^13.2.2" zstd-codec "^0.1.1" -lbry-redux@lbryio/lbry-redux#e4d0662100a5f4b28bb1bf3cbc1e51b2eebab5b6: +lbry-redux@lbryio/lbry-redux#7cc9923ed9ff1940b508842af6be44c8da906a60: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/e4d0662100a5f4b28bb1bf3cbc1e51b2eebab5b6" + resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/7cc9923ed9ff1940b508842af6be44c8da906a60" dependencies: proxy-polyfill "0.1.6" reselect "^3.0.0"