From d1c4e96d60e07d01892edf08f1139d14d9e5caff Mon Sep 17 00:00:00 2001 From: jessop Date: Thu, 9 Jan 2020 22:50:47 -0500 Subject: [PATCH] rename setReferrerPending and Error bump lbryinc improve invite states register channels with apis fix duplicate subscriptions --- package.json | 2 +- ui/component/app/view.jsx | 1 - ui/component/inviteNew/index.js | 10 +++- ui/component/inviteNew/view.jsx | 77 +++++++++++++++++++++--------- ui/component/invited/index.js | 6 ++- ui/component/invited/view.jsx | 59 +++++++++++++++++++---- ui/modal/modalSetReferrer/index.js | 4 +- ui/modal/modalSetReferrer/view.jsx | 4 +- ui/redux/reducers/subscriptions.js | 5 +- 9 files changed, 126 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 45f95efed..92ae54ab5 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-redux": "lbryio/lbry-redux#a2be979986dc93be4c2c596846109f5394f64fa1", - "lbryinc": "lbryio/lbryinc#6042c6f7bbf5fe7c6db2bd169f5b1c4558485c4c", + "lbryinc": "lbryio/lbryinc#018d149fb493615c7179d1ea3c497f07b9c8aed6", "lint-staged": "^7.0.2", "localforage": "^1.7.1", "lodash-es": "^4.17.14", diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index d6cbe13fb..08ae794f4 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -41,7 +41,6 @@ type Props = { fetchChannelListMine: () => void, signIn: () => void, requestDownloadUpgrade: () => void, - fetchChannelListMine: () => void, onSignedIn: () => void, setLanguage: string => void, isUpgradeAvailable: boolean, diff --git a/ui/component/inviteNew/index.js b/ui/component/inviteNew/index.js index fdd549d70..065a298f4 100644 --- a/ui/component/inviteNew/index.js +++ b/ui/component/inviteNew/index.js @@ -7,7 +7,13 @@ import { selectUserInviteReferralCode, doUserInviteNew, } from 'lbryinc'; -import { selectMyChannelClaims, selectFetchingMyChannels, doFetchChannelListMine } from 'lbry-redux'; +import { + selectMyChannelClaims, + selectFetchingMyChannels, + doFetchChannelListMine, + doResolveUris, + selectResolvingUris, +} from 'lbry-redux'; import InviteNew from './view'; const select = state => ({ @@ -18,11 +24,13 @@ const select = state => ({ isPending: selectUserInviteNewIsPending(state), channels: selectMyChannelClaims(state), fetchingChannels: selectFetchingMyChannels(state), + resolvingUris: selectResolvingUris(state), }); const perform = dispatch => ({ inviteNew: email => dispatch(doUserInviteNew(email)), fetchChannelListMine: () => dispatch(doFetchChannelListMine()), + resolveUris: uris => dispatch(doResolveUris(uris)), }); export default connect( diff --git a/ui/component/inviteNew/view.jsx b/ui/component/inviteNew/view.jsx index e15b900ab..fc4e50c9d 100644 --- a/ui/component/inviteNew/view.jsx +++ b/ui/component/inviteNew/view.jsx @@ -6,6 +6,7 @@ import CopyableText from 'component/copyableText'; import Card from 'component/common/card'; import { URL } from 'config'; import SelectChannel from 'component/selectChannel'; +import analytics from 'analytics'; import I18nMessage from 'component/i18nMessage'; type Props = { @@ -15,14 +16,42 @@ type Props = { referralLink: string, referralCode: string, channels: any, - fetchChannelListMine: () => void, - fetchingChannels: boolean, + resolvingUris: Array, + resolveUris: (Array) => void, }; function InviteNew(props: Props) { - const { inviteNew, fetchChannelListMine, errorMessage, isPending, referralCode, channels } = props; + const { inviteNew, errorMessage, isPending, referralCode = '', channels, resolveUris, resolvingUris } = props; + // Email const [email, setEmail] = useState(''); + function handleSubmit() { + inviteNew(email); + } + + function handleEmailChanged(event: any) { + setEmail(event.target.value); + } + + // Referral link const [referralSource, setReferralSource] = useState(referralCode); + /* Canonical Referral links + * We need to make sure our channels are resolved so that canonical_url is present + */ + + function handleReferralChange(code) { + setReferralSource(code); + // TODO: keep track of this in an array? + if (code !== referralCode) { + analytics.apiLogPublish(channels.find(ch => ch.name === code)); + } + } + + const [resolveStarted, setResolveStarted] = useState(false); + const [hasResolved, setHasResolved] = useState(false); + // join them so that useEffect doesn't update on new objects + const uris = channels && channels.map(channel => channel.permanent_url).join(','); + const channelCount = channels && channels.length; + const resolvingCount = resolvingUris && resolvingUris.length; const topChannel = channels && @@ -31,32 +60,36 @@ function InviteNew(props: Props) { ); const referralString = channels && channels.length && referralSource !== referralCode - ? getUrlFromName(referralSource, channels) + ? lookupUrlByClaimName(referralSource, channels) : referralSource; - const referral = `${URL}/$/invite/${referralString}`; + + const referral = `${URL}/$/invite/${referralString.replace('#', ':')}`; useEffect(() => { - // check emailverified and is_web? - fetchChannelListMine(); - }, []); - - useEffect(() => { - if (topChannel) { - setReferralSource(topChannel.name); + // resolve once, after we have channel list + if (!hasResolved && !resolveStarted && channelCount) { + setResolveStarted(true); + resolveUris(uris.split(',')); } - }, [topChannel]); + }, [channelCount, resolveStarted, hasResolved, resolvingCount, uris]); - function handleEmailChanged(event: any) { - setEmail(event.target.value); - } + useEffect(() => { + // once resolving count is 0, we know we're done + if (resolveStarted && !hasResolved && resolvingCount === 0) { + setHasResolved(true); + } + }, [resolveStarted, hasResolved, resolvingCount]); - function handleSubmit() { - inviteNew(email); - } + useEffect(() => { + // set default channel + if (topChannel && hasResolved) { + handleReferralChange(topChannel.name); + } + }, [topChannel, hasResolved]); - function getUrlFromName(name, channels) { + function lookupUrlByClaimName(name, channels) { const claim = channels.find(channel => channel.name === name); - return claim ? claim.permanent_url.replace('lbry://', '') : name; + return claim ? claim.canonical_url.replace('lbry://', '') : name; } return ( @@ -68,7 +101,7 @@ function InviteNew(props: Props) {
setReferralSource(channel)} + onChannelChange={channel => handleReferralChange(channel)} label={'Code or Channel'} injected={[referralCode]} /> diff --git a/ui/component/invited/index.js b/ui/component/invited/index.js index f0ee2c6f1..9237df1e2 100644 --- a/ui/component/invited/index.js +++ b/ui/component/invited/index.js @@ -7,6 +7,7 @@ import { selectSetReferrerPending, selectSetReferrerError, rewards as REWARDS, + selectUnclaimedRewards, } from 'lbryinc'; import { doChannelSubscribe } from 'redux/actions/subscriptions'; import Invited from './view'; @@ -14,8 +15,9 @@ import { withRouter } from 'react-router'; const select = state => ({ user: selectUser(state), - setReferrerPending: selectSetReferrerPending(state), - setReferrerError: selectSetReferrerError(state), + referrerSetPending: selectSetReferrerPending(state), + referrerSetError: selectSetReferrerError(state), + rewards: selectUnclaimedRewards(state), }); const perform = dispatch => ({ diff --git a/ui/component/invited/view.jsx b/ui/component/invited/view.jsx index db7837907..466dd5030 100644 --- a/ui/component/invited/view.jsx +++ b/ui/component/invited/view.jsx @@ -6,16 +6,18 @@ import Button from 'component/button'; import ClaimPreview from 'component/claimPreview'; import Card from 'component/common/card'; import { parseURI } from 'lbry-redux'; +import { rewards as REWARDS, ERRORS } from 'lbryinc'; type Props = { user: any, fetchUser: () => void, claimReward: () => void, setReferrer: string => void, - setReferrerPending: boolean, - setReferrerError: string, + referrerSetPending: boolean, + referrerSetError: string, channelSubscribe: (sub: Subscription) => void, history: { push: string => void }, + rewards: Array, }; function Invited(props: Props) { @@ -24,10 +26,11 @@ function Invited(props: Props) { fetchUser, claimReward, setReferrer, - setReferrerPending, - setReferrerError, + referrerSetPending, + referrerSetError, channelSubscribe, history, + rewards, } = props; // useParams requires react-router-dom ^v5.1.0 @@ -36,16 +39,17 @@ function Invited(props: Props) { const referrerIsChannel = parseURI(refUri).isChannel; const rewardsApproved = user && user.is_reward_approved; const hasVerifiedEmail = user && user.has_verified_email; + const referredRewardAvailable = rewards && rewards.some(reward => reward.reward_type === REWARDS.TYPE_REFEREE); useEffect(() => { fetchUser(); }, []); useEffect(() => { - if (!setReferrerPending && hasVerifiedEmail) { + if (!referrerSetPending && hasVerifiedEmail) { claimReward(); } - }, [setReferrerPending, hasVerifiedEmail]); + }, [referrerSetPending, hasVerifiedEmail]); useEffect(() => { if (referrer) { @@ -53,6 +57,16 @@ function Invited(props: Props) { } }, [referrer]); + // if they land here due to a referrer but already claimed, make them follow anyway + useEffect(() => { + if (!referredRewardAvailable && referrerIsChannel) { + channelSubscribe({ + channelName: parseURI(refUri).claimName, + uri: refUri, + }); + } + }, [referredRewardAvailable, referrerIsChannel]); + function handleDone() { if (hasVerifiedEmail && referrerIsChannel) { channelSubscribe({ @@ -63,18 +77,43 @@ function Invited(props: Props) { history.push(`/$/${PAGES.DISCOVER}`); } - if (setReferrerError) { + if (referrerSetError === ERRORS.ALREADY_CLAIMED) { + return ( + + {referrerIsChannel && ( +
+ +
+ )} +
+
+ + } + /> + ); + } + + if (referrerSetError && referredRewardAvailable) { return (

{__('Not a valid referral')}

-

{setReferrerError}

+
@@ -112,7 +151,7 @@ function Invited(props: Props) { return ( {referrerIsChannel && ( diff --git a/ui/modal/modalSetReferrer/index.js b/ui/modal/modalSetReferrer/index.js index 8070503fd..52ead0f95 100644 --- a/ui/modal/modalSetReferrer/index.js +++ b/ui/modal/modalSetReferrer/index.js @@ -4,8 +4,8 @@ import { doUserSetReferrer, selectSetReferrerError, selectSetReferrerPending } f import ModalSetReferrer from './view'; const select = state => ({ - setReferrerPending: selectSetReferrerPending(state), - setReferrerError: selectSetReferrerError(state), + referrerSetPending: selectSetReferrerPending(state), + referrerSetError: selectSetReferrerError(state), }); const perform = dispatch => ({ diff --git a/ui/modal/modalSetReferrer/view.jsx b/ui/modal/modalSetReferrer/view.jsx index e1b41d036..078cbd61c 100644 --- a/ui/modal/modalSetReferrer/view.jsx +++ b/ui/modal/modalSetReferrer/view.jsx @@ -9,8 +9,8 @@ type Props = { error: ?string, rewardIsPending: boolean, setReferrer: (string, boolean) => void, - setReferrerPending: boolean, - setReferrerError?: string, + referrerSetPending: boolean, + referrerSetError?: string, }; type State = { diff --git a/ui/redux/reducers/subscriptions.js b/ui/redux/reducers/subscriptions.js index 176b42e87..496d53013 100644 --- a/ui/redux/reducers/subscriptions.js +++ b/ui/redux/reducers/subscriptions.js @@ -21,7 +21,10 @@ export default handleActions( [ACTIONS.CHANNEL_SUBSCRIBE]: (state: SubscriptionState, action: DoChannelSubscribe): SubscriptionState => { const newSubscription: Subscription = action.data; const newSubscriptions: Array = state.subscriptions.slice(); - newSubscriptions.unshift(newSubscription); + // prevent duplicates in the sidebar + if (!newSubscriptions.some(sub => sub.uri === newSubscription.uri)) { + newSubscriptions.unshift(newSubscription); + } return { ...state,