// @flow import * as PAGES from 'constants/pages'; import React from 'react'; import { withRouter } from 'react-router'; import UserEmailNew from 'component/userEmailNew'; import UserEmailVerify from 'component/userEmailVerify'; import UserFirstChannel from 'component/userFirstChannel'; import UserChannelFollowIntro from 'component/userChannelFollowIntro'; import UserTagFollowIntro from 'component/userTagFollowIntro'; import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view'; import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc'; import UserVerify from 'component/userVerify'; import Spinner from 'component/spinner'; import YoutubeTransferStatus from 'component/youtubeTransferStatus'; import SyncPassword from 'component/syncPassword'; import useFetched from 'effects/use-fetched'; import usePersistedState from 'effects/use-persisted-state'; import Confetti from 'react-confetti'; type Props = { user: ?User, emailToVerify: ?string, channels: ?Array, balance: ?number, fetchingChannels: boolean, claimingReward: boolean, claimReward: () => void, fetchUser: () => void, claimedRewards: Array, history: { replace: string => void }, location: { search: string }, youtubeChannels: Array, syncEnabled: boolean, hasSynced: boolean, syncingWallet: boolean, getSyncError: ?string, creatingChannel: boolean, }; function UserSignIn(props: Props) { const { emailToVerify, user, claimingReward, claimedRewards, channels, claimReward, balance, history, location, fetchUser, youtubeChannels, syncEnabled, syncingWallet, getSyncError, hasSynced, fetchingChannels, creatingChannel, } = props; const { search } = location; const urlParams = new URLSearchParams(search); const redirect = urlParams.get('redirect'); const step = urlParams.get('step'); const shouldRedirectImmediately = urlParams.get('immediate'); const [initialSignInStep, setInitialSignInStep] = React.useState(); const [hasSeenFollowList, setHasSeenFollowList] = usePersistedState('channel-follow-intro', false); const [hasSkippedRewards, setHasSkippedRewards] = usePersistedState('skip-rewards-intro', false); const [hasSeenTagsList, setHasSeenTagsList] = usePersistedState('channel-follow-intro', false); const hasVerifiedEmail = user && user.has_verified_email; const rewardsApproved = user && user.is_reward_approved; const isIdentityVerified = user && user.is_identity_verified; const passwordSet = user && user.password_set; const hasFetchedReward = useFetched(claimingReward); const channelCount = channels ? channels.length : 0; const hasClaimedEmailAward = claimedRewards.some(reward => reward.reward_type === REWARDS.TYPE_CONFIRM_EMAIL); const hasYoutubeChannels = youtubeChannels && Boolean(youtubeChannels.length); const isYoutubeTransferComplete = hasYoutubeChannels && youtubeChannels.every(channel => channel.transfer_state === YOUTUBE_STATUSES.COMPLETED_TRANSFER); // Complexity warning // We can't just check if we are currently fetching something // We may want to keep a component rendered while something is being fetched, instead of replacing it with the large spinner // The verbose variable names are an attempt to alleviate _some_ of the confusion from handling all edge cases that come from // reward claiming, channel creation, account syncing, and youtube transfer // The possible screens for the sign in flow const showEmail = !hasVerifiedEmail; const showEmailVerification = (emailToVerify && !hasVerifiedEmail) || (!hasVerifiedEmail && passwordSet); const showUserVerification = hasVerifiedEmail && !rewardsApproved && !isIdentityVerified && !hasSkippedRewards; const showSyncPassword = syncEnabled && getSyncError; const showChannelCreation = hasVerifiedEmail && balance !== undefined && balance !== null && balance > DEFAULT_BID_FOR_FIRST_CHANNEL && channelCount === 0 && !hasYoutubeChannels; const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete; const showFollowIntro = step === 'channels' || (hasVerifiedEmail && !hasSeenFollowList); const showTagsIntro = step === 'tags' || (hasVerifiedEmail && !hasSeenTagsList); const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !getSyncError && !showFollowIntro; const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel; const isWaitingForSomethingToFinish = // If the user has claimed the email award, we need to wait until the balance updates sometime in the future (!hasFetchedReward && !hasClaimedEmailAward) || (syncEnabled && !hasSynced); const showLoadingSpinner = canHijackSignInFlowWithSpinner && (isCurrentlyFetchingSomething || isWaitingForSomethingToFinish); React.useEffect(() => { fetchUser(); }, [fetchUser]); React.useEffect(() => { // Don't claim the reward if sync is enabled until after a sync has been completed successfully // If we do it before, we could end up trying to sync a wallet with a non-zero balance which will fail to sync const delayForSync = syncEnabled && !hasSynced; if (hasVerifiedEmail && !hasClaimedEmailAward && !hasFetchedReward && !delayForSync) { claimReward(); } }, [hasVerifiedEmail, claimReward, hasClaimedEmailAward, hasFetchedReward, syncEnabled, hasSynced, balance]); // Loop through this list from the end, until it finds a matching component // If it never finds one, assume the user has completed every step and redirect them const SIGN_IN_FLOW = [ showEmail && , showEmailVerification && , showUserVerification && setHasSkippedRewards(true)} />, showChannelCreation && , showFollowIntro && ( { let url = `/$/${PAGES.AUTH}?reset_scroll=1`; if (redirect) { url += `&redirect=${redirect}`; } if (shouldRedirectImmediately) { url += `&immediate=true`; } history.replace(url); setHasSeenFollowList(true); }} onBack={() => { let url = `/$/${PAGES.AUTH}?reset_scroll=1&step=tags`; if (redirect) { url += `&redirect=${redirect}`; } if (shouldRedirectImmediately) { url += `&immediate=true`; } history.replace(url); setHasSeenFollowList(false); }} /> ), showTagsIntro && ( { let url = `/$/${PAGES.AUTH}?reset_scroll=1&step=channels`; if (redirect) { url += `&redirect=${redirect}`; } if (shouldRedirectImmediately) { url += `&immediate=true`; } history.replace(url); setHasSeenTagsList(true); }} /> ), showYoutubeTransfer && (
), showSyncPassword && , showLoadingSpinner && (
), ]; function getSignInStep() { for (var i = SIGN_IN_FLOW.length - 1; i > -1; i--) { const Component = SIGN_IN_FLOW[i]; if (Component) { // If we want to redirect immediately, // remember the first step so we can redirect once a new step has been reached // Ignore the loading step if (redirect && shouldRedirectImmediately) { if (!initialSignInStep) { setInitialSignInStep(i); } else if (i !== initialSignInStep && i !== SIGN_IN_FLOW.length - 1) { history.replace(redirect); } } return Component; } } } const componentToRender = getSignInStep(); if (!componentToRender) { history.replace(redirect || '/'); } return
{componentToRender}
; } export default withRouter(UserSignIn);