lbry-desktop/ui/component/userSignIn/view.jsx

216 lines
8 KiB
React
Raw Normal View History

2019-08-27 16:43:42 +02:00
// @flow
import * as PAGES from 'constants/pages';
2019-08-27 16:43:42 +02:00
import React from 'react';
2019-09-26 18:07:11 +02:00
import { withRouter } from 'react-router';
2019-08-27 16:43:42 +02:00
import UserEmailNew from 'component/userEmailNew';
import UserEmailVerify from 'component/userEmailVerify';
2019-09-26 18:07:11 +02:00
import UserFirstChannel from 'component/userFirstChannel';
2020-02-26 19:39:03 +01:00
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
import UserTagFollowIntro from 'component/userTagFollowIntro';
2019-09-26 18:07:11 +02:00
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
2019-10-01 06:53:33 +02:00
import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc';
import UserVerify from 'component/userVerify';
import Spinner from 'component/spinner';
2019-10-03 23:40:54 +02:00
import YoutubeTransferStatus from 'component/youtubeTransferStatus';
import SyncPassword from 'component/syncPassword';
2019-09-27 20:56:15 +02:00
import useFetched from 'effects/use-fetched';
2020-02-26 19:39:03 +01:00
import usePersistedState from 'effects/use-persisted-state';
2019-10-03 23:40:54 +02:00
import Confetti from 'react-confetti';
2019-08-27 16:43:42 +02:00
type Props = {
user: ?User,
emailToVerify: ?string,
2019-09-26 18:07:11 +02:00
channels: ?Array<string>,
balance: ?number,
fetchingChannels: boolean,
claimingReward: boolean,
claimReward: () => void,
fetchUser: () => void,
2019-09-26 18:07:11 +02:00
claimedRewards: Array<Reward>,
history: { replace: string => void },
location: { search: string },
youtubeChannels: Array<any>,
2019-09-27 20:56:15 +02:00
syncEnabled: boolean,
hasSynced: boolean,
syncingWallet: boolean,
getSyncError: ?string,
2019-10-03 23:40:54 +02:00
creatingChannel: boolean,
2019-08-27 16:43:42 +02:00
};
2019-09-26 18:07:11 +02:00
function UserSignIn(props: Props) {
const {
emailToVerify,
user,
claimingReward,
claimedRewards,
channels,
claimReward,
balance,
history,
location,
fetchUser,
youtubeChannels,
syncEnabled,
2019-09-27 20:56:15 +02:00
syncingWallet,
getSyncError,
2019-09-27 20:56:15 +02:00
hasSynced,
fetchingChannels,
2019-10-03 23:40:54 +02:00
creatingChannel,
} = props;
2019-09-26 18:07:11 +02:00
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();
2020-02-26 19:39:03 +01:00
const [hasSeenFollowList, setHasSeenFollowList] = usePersistedState('channel-follow-intro', false);
2020-03-16 16:56:24 +01:00
const [hasSkippedRewards, setHasSkippedRewards] = usePersistedState('skip-rewards-intro', false);
const [hasSeenTagsList, setHasSeenTagsList] = usePersistedState('channel-follow-intro', false);
2019-09-26 18:07:11 +02:00
const hasVerifiedEmail = user && user.has_verified_email;
const rewardsApproved = user && user.is_reward_approved;
2020-03-16 16:56:24 +01:00
const isIdentityVerified = user && user.is_identity_verified;
const hasFetchedReward = useFetched(claimingReward);
2019-09-26 18:07:11 +02:00
const channelCount = channels ? channels.length : 0;
const hasClaimedEmailAward = claimedRewards.some(reward => reward.reward_type === REWARDS.TYPE_CONFIRM_EMAIL);
2019-09-27 20:56:15 +02:00
const hasYoutubeChannels = youtubeChannels && Boolean(youtubeChannels.length);
2019-10-03 23:40:54 +02:00
const isYoutubeTransferComplete =
hasYoutubeChannels &&
youtubeChannels.every(channel => channel.transfer_state === YOUTUBE_STATUSES.COMPLETED_TRANSFER);
2019-09-26 18:07:11 +02:00
2019-09-27 20:56:15 +02:00
// 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
2019-10-03 23:40:54 +02:00
// reward claiming, channel creation, account syncing, and youtube transfer
2019-09-27 20:56:15 +02:00
// The possible screens for the sign in flow
const showEmail = !emailToVerify && !hasVerifiedEmail;
const showEmailVerification = emailToVerify && !hasVerifiedEmail;
2020-03-16 16:56:24 +01:00
const showUserVerification = hasVerifiedEmail && !rewardsApproved && !isIdentityVerified && !hasSkippedRewards;
const showSyncPassword = syncEnabled && getSyncError;
2019-09-27 20:56:15 +02:00
const showChannelCreation =
2019-10-03 23:40:54 +02:00
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);
2020-02-26 19:39:03 +01:00
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);
2019-09-27 20:56:15 +02:00
const showLoadingSpinner =
canHijackSignInFlowWithSpinner && (isCurrentlyFetchingSomething || isWaitingForSomethingToFinish);
2019-09-26 18:07:11 +02:00
React.useEffect(() => {
fetchUser();
}, [fetchUser]);
2019-09-26 18:07:11 +02:00
2019-09-27 20:56:15 +02:00
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();
}
2019-09-27 22:03:05 +02:00
}, [hasVerifiedEmail, claimReward, hasClaimedEmailAward, hasFetchedReward, syncEnabled, hasSynced, balance]);
2019-09-27 20:56:15 +02:00
// 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 = [
2019-09-27 20:56:15 +02:00
showEmail && <UserEmailNew />,
showEmailVerification && <UserEmailVerify />,
showUserVerification && <UserVerify onSkip={() => setHasSkippedRewards(true)} />,
2019-09-27 20:56:15 +02:00
showChannelCreation && <UserFirstChannel />,
showFollowIntro && (
<UserChannelFollowIntro
onContinue={() => {
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 && (
<UserTagFollowIntro
onContinue={() => {
let url = `/$/${PAGES.AUTH}?reset_scroll=1&step=channels`;
if (redirect) {
url += `&redirect=${redirect}`;
}
if (shouldRedirectImmediately) {
url += `&immediate=true`;
}
history.replace(url);
setHasSeenTagsList(true);
}}
/>
),
2019-10-03 23:40:54 +02:00
showYoutubeTransfer && (
<div>
<YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} />
</div>
),
2019-10-15 06:20:12 +02:00
showSyncPassword && <SyncPassword />,
2019-09-27 20:56:15 +02:00
showLoadingSpinner && (
<div className="main--empty">
<Spinner />
</div>
),
];
2019-09-26 18:07:11 +02:00
2019-10-01 06:53:33 +02:00
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);
}
}
2019-10-01 06:53:33 +02:00
return Component;
}
}
2019-09-26 18:07:11 +02:00
}
2019-10-01 06:53:33 +02:00
const componentToRender = getSignInStep();
if (!componentToRender) {
2019-09-26 18:07:11 +02:00
history.replace(redirect || '/');
2019-08-27 16:43:42 +02:00
}
return <section className="main--contained">{componentToRender}</section>;
2019-08-27 16:43:42 +02:00
}
2019-09-26 18:07:11 +02:00
export default withRouter(UserSignIn);