Commit 6a5ee48d authored by Max Alekseenko's avatar Max Alekseenko Committed by tom goriunov

Adding an email when logging into merits (#2369)

* add email step

* fix preset

* update text

* fix login with wallet

* update button text and var names

* update mixpanel events
parent 20444de7
...@@ -62,4 +62,4 @@ NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves ...@@ -62,4 +62,4 @@ NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://points.k8s-dev.blockscout.com NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://points.k8s-dev.blockscout.com
NEXT_PUBLIC_XSTAR_SCORE_URL='https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address' NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
...@@ -74,7 +74,7 @@ Type extends EventTypes.LOGIN ? ( ...@@ -74,7 +74,7 @@ Type extends EventTypes.LOGIN ? (
} }
) : ) :
Type extends EventTypes.ACCOUNT_LINK_INFO ? { Type extends EventTypes.ACCOUNT_LINK_INFO ? {
'Source': 'Profile' | 'Login modal' | 'Profile dropdown'; 'Source': 'Profile' | 'Login modal' | 'Profile dropdown' | 'Merits';
'Status': 'Started' | 'OTP sent' | 'Finished'; 'Status': 'Started' | 'OTP sent' | 'Finished';
'Type': 'Email' | 'Wallet'; 'Type': 'Email' | 'Wallet';
} : } :
......
import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, useBoolean } from '@chakra-ui/react'; import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, useBoolean, useDisclosure } from '@chakra-ui/react';
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
import { useRewardsContext } from 'lib/contexts/rewards'; import { useRewardsContext } from 'lib/contexts/rewards';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useWallet from 'lib/web3/useWallet'; import useWallet from 'lib/web3/useWallet';
import AuthModal from 'ui/snippets/auth/AuthModal';
import CongratsStepContent from './steps/CongratsStepContent'; import CongratsStepContent from './steps/CongratsStepContent';
import LoginStepContent from './steps/LoginStepContent'; import LoginStepContent from './steps/LoginStepContent';
const MIXPANEL_CONFIG = {
account_link_info: {
source: 'Merits' as const,
},
wallet_connect: {
source: 'Merits' as const,
},
};
const RewardsLoginModal = () => { const RewardsLoginModal = () => {
const { isOpen: isWalletModalOpen } = useWallet({ source: 'Merits' }); const { isOpen: isWalletModalOpen } = useWallet({ source: 'Merits' });
const isMobile = useIsMobile(); const isMobile = useIsMobile();
...@@ -15,6 +25,8 @@ const RewardsLoginModal = () => { ...@@ -15,6 +25,8 @@ const RewardsLoginModal = () => {
const [ isLoginStep, setIsLoginStep ] = useBoolean(true); const [ isLoginStep, setIsLoginStep ] = useBoolean(true);
const [ isReferral, setIsReferral ] = useBoolean(false); const [ isReferral, setIsReferral ] = useBoolean(false);
const [ isAuth, setIsAuth ] = useBoolean(false);
const authModal = useDisclosure();
useEffect(() => { useEffect(() => {
if (!isLoginModalOpen) { if (!isLoginModalOpen) {
...@@ -30,27 +42,47 @@ const RewardsLoginModal = () => { ...@@ -30,27 +42,47 @@ const RewardsLoginModal = () => {
setIsLoginStep.off(); setIsLoginStep.off();
}, [ setIsLoginStep, setIsReferral ]); }, [ setIsLoginStep, setIsReferral ]);
const handleAuthModalOpen = useCallback((isAuth: boolean) => {
setIsAuth[isAuth ? 'on' : 'off']();
authModal.onOpen();
}, [ authModal, setIsAuth ]);
const handleAuthModalClose = useCallback(() => {
setIsAuth.off();
authModal.onClose();
}, [ authModal, setIsAuth ]);
return ( return (
<Modal <>
isOpen={ isLoginModalOpen && !isWalletModalOpen } <Modal
onClose={ closeLoginModal } isOpen={ isLoginModalOpen && !isWalletModalOpen && !authModal.isOpen }
size={ isMobile ? 'full' : 'sm' } onClose={ closeLoginModal }
isCentered size={ isMobile ? 'full' : 'sm' }
> isCentered
<ModalOverlay/> >
<ModalContent width={ isLoginStep ? '400px' : '560px' } p={ 6 }> <ModalOverlay/>
<ModalHeader fontWeight="500" textStyle="h3" mb={ 3 }> <ModalContent width={ isLoginStep ? '400px' : '560px' } p={ 6 }>
{ isLoginStep ? 'Login' : 'Congratulations' } <ModalHeader fontWeight="500" textStyle="h3" mb={ 3 }>
</ModalHeader> { isLoginStep ? 'Login' : 'Congratulations' }
<ModalCloseButton top={ 6 } right={ 6 }/> </ModalHeader>
<ModalBody mb={ 0 }> <ModalCloseButton top={ 6 } right={ 6 }/>
{ isLoginStep ? <ModalBody mb={ 0 }>
<LoginStepContent goNext={ goNext } closeModal={ closeLoginModal }/> : { isLoginStep ?
<CongratsStepContent isReferral={ isReferral }/> <LoginStepContent goNext={ goNext } openAuthModal={ handleAuthModalOpen } closeModal={ closeLoginModal }/> :
} <CongratsStepContent isReferral={ isReferral }/>
</ModalBody> }
</ModalContent> </ModalBody>
</Modal> </ModalContent>
</Modal>
{ authModal.isOpen && (
<AuthModal
onClose={ handleAuthModalClose }
initialScreen={{ type: 'connect_wallet', isAuth }}
mixpanelConfig={ MIXPANEL_CONFIG }
closeOnError
/>
) }
</>
); );
}; };
......
...@@ -10,14 +10,14 @@ import useWallet from 'lib/web3/useWallet'; ...@@ -10,14 +10,14 @@ import useWallet from 'lib/web3/useWallet';
import FormInputPlaceholder from 'ui/shared/forms/inputs/FormInputPlaceholder'; import FormInputPlaceholder from 'ui/shared/forms/inputs/FormInputPlaceholder';
import LinkExternal from 'ui/shared/links/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
import useSignInWithWallet from 'ui/snippets/auth/useSignInWithWallet';
type Props = { type Props = {
goNext: (isReferral: boolean) => void; goNext: (isReferral: boolean) => void;
closeModal: () => void; closeModal: () => void;
openAuthModal: (isAuth: boolean) => void;
}; };
const LoginStepContent = ({ goNext, closeModal }: Props) => { const LoginStepContent = ({ goNext, closeModal, openAuthModal }: Props) => {
const router = useRouter(); const router = useRouter();
const { connect, isConnected, address } = useWallet({ source: 'Merits' }); const { connect, isConnected, address } = useWallet({ source: 'Merits' });
const savedRefCode = cookies.get(cookies.NAMES.REWARDS_REFERRAL_CODE); const savedRefCode = cookies.get(cookies.NAMES.REWARDS_REFERRAL_CODE);
...@@ -34,6 +34,10 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => { ...@@ -34,6 +34,10 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => {
profileQuery.data?.address_hash !== address, profileQuery.data?.address_hash !== address,
[ address, profileQuery.data ]); [ address, profileQuery.data ]);
const isLoggedIntoAccountWithWallet = useMemo(() =>
!profileQuery.isLoading && profileQuery.data?.address_hash,
[ profileQuery ]);
const isSignUp = useMemo(() => const isSignUp = useMemo(() =>
isConnected && !isAddressMismatch && !checkUserQuery.isFetching && !checkUserQuery.data?.exists, isConnected && !isAddressMismatch && !checkUserQuery.isFetching && !checkUserQuery.data?.exists,
[ isConnected, isAddressMismatch, checkUserQuery ]); [ isConnected, isAddressMismatch, checkUserQuery ]);
...@@ -69,20 +73,23 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => { ...@@ -69,20 +73,23 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => {
} }
}, [ refCode, isRefCodeUsed, isSignUp ]); // eslint-disable-line react-hooks/exhaustive-deps }, [ refCode, isRefCodeUsed, isSignUp ]); // eslint-disable-line react-hooks/exhaustive-deps
const { start: loginToAccount } = useSignInWithWallet({
isAuth: Boolean(!profileQuery.isLoading && profileQuery.data?.email),
onSuccess: loginToRewardsProgram,
onError: setIsLoading.off,
});
const handleLogin = useCallback(async() => { const handleLogin = useCallback(async() => {
if (!profileQuery.isLoading && !profileQuery.data?.address_hash) { if (isLoggedIntoAccountWithWallet) {
setIsLoading.on(); loginToRewardsProgram();
loginToAccount(); } else {
return; openAuthModal(Boolean(profileQuery.data?.email));
}
}, [ loginToRewardsProgram, openAuthModal, isLoggedIntoAccountWithWallet, profileQuery ]);
const buttonText = useMemo(() => {
if (!isConnected) {
return 'Connect wallet';
}
if (isLoggedIntoAccountWithWallet) {
return 'Get started';
} }
loginToRewardsProgram(); return profileQuery.data?.email ? 'Add wallet to account' : 'Log in to account';
}, [ loginToAccount, loginToRewardsProgram, profileQuery, setIsLoading ]); }, [ isConnected, isLoggedIntoAccountWithWallet, profileQuery.data ]);
return ( return (
<> <>
...@@ -98,7 +105,7 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => { ...@@ -98,7 +105,7 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => {
More about Blockscout Merits More about Blockscout Merits
</LinkExternal> </LinkExternal>
</Box> </Box>
{ isSignUp && ( { isSignUp && isLoggedIntoAccountWithWallet && (
<Box mb={ 6 }> <Box mb={ 6 }>
<Divider bgColor="divider" mb={ 6 }/> <Divider bgColor="divider" mb={ 6 }/>
<Flex w="full" alignItems="center" justifyContent="space-between"> <Flex w="full" alignItems="center" justifyContent="space-between">
...@@ -145,7 +152,7 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => { ...@@ -145,7 +152,7 @@ const LoginStepContent = ({ goNext, closeModal }: Props) => {
loadingText={ isLoading ? 'Sign message in your wallet' : undefined } loadingText={ isLoading ? 'Sign message in your wallet' : undefined }
isDisabled={ isAddressMismatch || refCodeError } isDisabled={ isAddressMismatch || refCodeError }
> >
{ isConnected ? 'Get started' : 'Connect wallet' } { buttonText }
</Button> </Button>
<Text fontSize="sm" color={ useColorModeValue('blackAlpha.500', 'whiteAlpha.500') } textAlign="center"> <Text fontSize="sm" color={ useColorModeValue('blackAlpha.500', 'whiteAlpha.500') } textAlign="center">
Already registered for Blockscout Merits on another network or chain? Connect the same wallet here. Already registered for Blockscout Merits on another network or chain? Connect the same wallet here.
......
...@@ -32,9 +32,10 @@ interface Props { ...@@ -32,9 +32,10 @@ interface Props {
source: mixpanel.EventPayload<mixpanel.EventTypes.ACCOUNT_LINK_INFO>['Source']; source: mixpanel.EventPayload<mixpanel.EventTypes.ACCOUNT_LINK_INFO>['Source'];
}; };
}; };
closeOnError?: boolean;
} }
const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => { const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Props) => {
const [ steps, setSteps ] = React.useState<Array<Screen>>([ initialScreen ]); const [ steps, setSteps ] = React.useState<Array<Screen>>([ initialScreen ]);
const [ isSuccess, setIsSuccess ] = React.useState(false); const [ isSuccess, setIsSuccess ] = React.useState(false);
...@@ -66,8 +67,8 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => { ...@@ -66,8 +67,8 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => {
}, []); }, []);
const onReset = React.useCallback((isAuth?: boolean) => { const onReset = React.useCallback((isAuth?: boolean) => {
isAuth ? onClose() : setSteps([ initialScreen ]); isAuth || closeOnError ? onClose() : setSteps([ initialScreen ]);
}, [ initialScreen, onClose ]); }, [ initialScreen, onClose, closeOnError ]);
const onAuthSuccess = React.useCallback(async(screen: ScreenSuccess) => { const onAuthSuccess = React.useCallback(async(screen: ScreenSuccess) => {
setIsSuccess(true); setIsSuccess(true);
......
import { chakra, Box, Text, Button } from '@chakra-ui/react'; import { chakra, Box, Text, Button, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Screen } from '../types'; import type { Screen } from '../types';
import type { UserInfo } from 'types/api/account'; import type { UserInfo } from 'types/api/account';
import config from 'configs/app';
import { apos } from 'lib/html-entities';
import shortenString from 'lib/shortenString'; import shortenString from 'lib/shortenString';
interface Props { interface Props {
...@@ -47,8 +49,14 @@ const AuthModalScreenSuccessWallet = ({ address, onAddEmail, onClose, isAuth, pr ...@@ -47,8 +49,14 @@ const AuthModalScreenSuccessWallet = ({ address, onAddEmail, onClose, isAuth, pr
</Text> </Text>
{ !profile?.email ? ( { !profile?.email ? (
<> <>
<Text mt={ 6 }>Add your email to receive notifications about addresses in your watch list.</Text> <Text mt={ 6 }>
<Button mt={ 6 } onClick={ handleAddEmailClick }>Add email</Button> Add your email to receive exclusive updates about Blockscout { config.features.rewards.isEnabled ? 'Merits ' : ' ' }
and notifications about addresses in your watch list.
</Text>
<Flex mt={ 6 } gap={ 2 }>
<Button onClick={ handleAddEmailClick }>Add email</Button>
<Button variant="simple" onClick={ onClose }>I{ apos }ll do it later</Button>
</Flex>
</> </>
) : ( ) : (
<Button <Button
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment