Commit f363957a authored by tom's avatar tom

fix linking email and wallet to existing account

parent 000bf0d6
...@@ -226,18 +226,21 @@ export const RESOURCES = { ...@@ -226,18 +226,21 @@ export const RESOURCES = {
auth_send_otp: { auth_send_otp: {
path: '/api/account/v2/send_otp', path: '/api/account/v2/send_otp',
}, },
auth_confirm_otp: { auth_confirm_otp: {
path: '/api/account/v2/confirm_otp', path: '/api/account/v2/confirm_otp',
}, },
auth_siwe_message: { auth_siwe_message: {
path: '/api/account/v2/siwe_message', path: '/api/account/v2/siwe_message',
}, },
auth_siwe_verify: { auth_siwe_verify: {
path: '/api/account/v2/authenticate_via_wallet', path: '/api/account/v2/authenticate_via_wallet',
}, },
auth_link_email: {
path: '/api/account/v2/email/link',
},
auth_link_address: {
path: '/api/account/v2/address/link',
},
// STATS MICROSERVICE API // STATS MICROSERVICE API
stats_counters: { stats_counters: {
......
import { Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay } from '@chakra-ui/react'; import { Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3'; import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
...@@ -6,6 +7,7 @@ import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3'; ...@@ -6,6 +7,7 @@ import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
import type { Screen, ScreenSuccess } from './types'; import type { Screen, ScreenSuccess } from './types';
import config from 'configs/app'; import config from 'configs/app';
import { getResourceKey } from 'lib/api/useApiQuery';
import useGetCsrfToken from 'lib/hooks/useGetCsrfToken'; import useGetCsrfToken from 'lib/hooks/useGetCsrfToken';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -16,7 +18,6 @@ import AuthModalScreenOtpCode from './screens/AuthModalScreenOtpCode'; ...@@ -16,7 +18,6 @@ import AuthModalScreenOtpCode from './screens/AuthModalScreenOtpCode';
import AuthModalScreenSelectMethod from './screens/AuthModalScreenSelectMethod'; import AuthModalScreenSelectMethod from './screens/AuthModalScreenSelectMethod';
import AuthModalScreenSuccessEmail from './screens/AuthModalScreenSuccessEmail'; import AuthModalScreenSuccessEmail from './screens/AuthModalScreenSuccessEmail';
import AuthModalScreenSuccessWallet from './screens/AuthModalScreenSuccessWallet'; import AuthModalScreenSuccessWallet from './screens/AuthModalScreenSuccessWallet';
import useProfileQuery from './useProfileQuery';
const feature = config.features.account; const feature = config.features.account;
...@@ -38,8 +39,8 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => { ...@@ -38,8 +39,8 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => {
const [ isSuccess, setIsSuccess ] = React.useState(false); const [ isSuccess, setIsSuccess ] = React.useState(false);
const router = useRouter(); const router = useRouter();
const profileQuery = useProfileQuery();
const csrfQuery = useGetCsrfToken(); const csrfQuery = useGetCsrfToken();
const queryClient = useQueryClient();
React.useEffect(() => { React.useEffect(() => {
if ('isAuth' in initialScreen && initialScreen.isAuth) { if ('isAuth' in initialScreen && initialScreen.isAuth) {
...@@ -84,13 +85,10 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => { ...@@ -84,13 +85,10 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => {
}); });
} }
const { data } = await profileQuery.refetch(); queryClient.setQueryData(getResourceKey('user_info'), () => screen.profile);
await csrfQuery.refetch(); await csrfQuery.refetch();
if (data) { onNextStep(screen);
onNextStep({ ...screen, profile: data }); }, [ initialScreen, mixpanelConfig?.account_link_info.source, onNextStep, csrfQuery, queryClient ]);
}
// TODO @tom2drum handle error case
}, [ initialScreen, mixpanelConfig?.account_link_info.source, onNextStep, profileQuery, csrfQuery ]);
const onModalClose = React.useCallback(() => { const onModalClose = React.useCallback(() => {
onClose(isSuccess); onClose(isSuccess);
......
...@@ -2,6 +2,7 @@ import { Center, Spinner } from '@chakra-ui/react'; ...@@ -2,6 +2,7 @@ import { Center, Spinner } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ScreenSuccess } from '../types'; import type { ScreenSuccess } from '../types';
import type { UserInfo } from 'types/api/account';
import type * as mixpanel from 'lib/mixpanel'; import type * as mixpanel from 'lib/mixpanel';
...@@ -17,15 +18,15 @@ interface Props { ...@@ -17,15 +18,15 @@ interface Props {
const AuthModalScreenConnectWallet = ({ onSuccess, onError, isAuth, source }: Props) => { const AuthModalScreenConnectWallet = ({ onSuccess, onError, isAuth, source }: Props) => {
const isStartedRef = React.useRef(false); const isStartedRef = React.useRef(false);
const handleSignInSuccess = React.useCallback(({ address }: { address: string }) => { const handleSignInSuccess = React.useCallback(({ address, profile }: { address: string; profile: UserInfo }) => {
onSuccess({ type: 'success_wallet', address, isAuth }); onSuccess({ type: 'success_wallet', address, isAuth, profile });
}, [ onSuccess, isAuth ]); }, [ onSuccess, isAuth ]);
const handleSignInError = React.useCallback(() => { const handleSignInError = React.useCallback(() => {
onError(isAuth); onError(isAuth);
}, [ onError, isAuth ]); }, [ onError, isAuth ]);
const { start } = useSignInWithWallet({ onSuccess: handleSignInSuccess, onError: handleSignInError, source }); const { start } = useSignInWithWallet({ onSuccess: handleSignInSuccess, onError: handleSignInError, source, isAuth });
React.useEffect(() => { React.useEffect(() => {
if (!isStartedRef.current) { if (!isStartedRef.current) {
......
...@@ -8,6 +8,7 @@ import type { EmailFormFields, Screen } from '../types'; ...@@ -8,6 +8,7 @@ import type { EmailFormFields, Screen } from '../types';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
...@@ -65,7 +66,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { ...@@ -65,7 +66,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
toast({ toast({
status: 'error', status: 'error',
title: 'Error', title: 'Error',
description: getErrorMessage(error) || 'Something went wrong', description: getErrorObjPayload<{ message: string }>(error)?.message || getErrorMessage(error) || 'Something went wrong',
}); });
} }
}, [ executeRecaptcha, apiFetch, isAuth, onSubmit, mixpanelConfig?.account_link_info.source, toast ]); }, [ executeRecaptcha, apiFetch, isAuth, onSubmit, mixpanelConfig?.account_link_info.source, toast ]);
......
...@@ -5,6 +5,7 @@ import type { SubmitHandler } from 'react-hook-form'; ...@@ -5,6 +5,7 @@ import type { SubmitHandler } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import type { OtpCodeFormFields, ScreenSuccess } from '../types'; import type { OtpCodeFormFields, ScreenSuccess } from '../types';
import type { UserInfo } from 'types/api/account';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
...@@ -35,7 +36,8 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => { ...@@ -35,7 +36,8 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
}); });
const onFormSubmit: SubmitHandler<OtpCodeFormFields> = React.useCallback((formData) => { const onFormSubmit: SubmitHandler<OtpCodeFormFields> = React.useCallback((formData) => {
return apiFetch('auth_confirm_otp', { const resource = isAuth ? 'auth_link_email' : 'auth_confirm_otp';
return apiFetch<typeof resource, UserInfo, unknown>(resource, {
fetchParams: { fetchParams: {
method: 'POST', method: 'POST',
body: { body: {
...@@ -44,8 +46,11 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => { ...@@ -44,8 +46,11 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
}, },
}, },
}) })
.then(() => { .then((response) => {
onSuccess({ type: 'success_email', email, isAuth }); if (!('name' in response)) {
throw Error('Something went wrong');
}
onSuccess({ type: 'success_email', email, isAuth, profile: response });
}) })
.catch((error) => { .catch((error) => {
const apiError = getErrorObjPayload<{ message: string }>(error); const apiError = getErrorObjPayload<{ message: string }>(error);
......
...@@ -3,12 +3,12 @@ import type { UserInfo } from 'types/api/account'; ...@@ -3,12 +3,12 @@ import type { UserInfo } from 'types/api/account';
export type ScreenSuccess = { export type ScreenSuccess = {
type: 'success_email'; type: 'success_email';
email: string; email: string;
profile?: UserInfo; profile: UserInfo;
isAuth?: boolean; isAuth?: boolean;
} | { } | {
type: 'success_wallet'; type: 'success_wallet';
address: string; address: string;
profile?: UserInfo; profile: UserInfo;
isAuth?: boolean; isAuth?: boolean;
} }
export type Screen = { export type Screen = {
......
...@@ -2,6 +2,8 @@ import { useWeb3Modal } from '@web3modal/wagmi/react'; ...@@ -2,6 +2,8 @@ import { useWeb3Modal } from '@web3modal/wagmi/react';
import React from 'react'; import React from 'react';
import { useSignMessage } from 'wagmi'; import { useSignMessage } from 'wagmi';
import type { UserInfo } from 'types/api/account';
import config from 'configs/app'; import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
...@@ -11,12 +13,13 @@ import * as mixpanel from 'lib/mixpanel'; ...@@ -11,12 +13,13 @@ import * as mixpanel from 'lib/mixpanel';
import useAccount from 'lib/web3/useAccount'; import useAccount from 'lib/web3/useAccount';
interface Props { interface Props {
onSuccess?: ({ address }: { address: string }) => void; onSuccess?: ({ address, profile }: { address: string; profile: UserInfo }) => void;
onError?: () => void; onError?: () => void;
source?: mixpanel.EventPayload<mixpanel.EventTypes.WALLET_CONNECT>['Source']; source?: mixpanel.EventPayload<mixpanel.EventTypes.WALLET_CONNECT>['Source'];
isAuth?: boolean;
} }
function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) { function useSignInWithWallet({ onSuccess, onError, source = 'Login', isAuth }: Props) {
const [ isPending, setIsPending ] = React.useState(false); const [ isPending, setIsPending ] = React.useState(false);
const isConnectingWalletRef = React.useRef(false); const isConnectingWalletRef = React.useRef(false);
...@@ -30,13 +33,17 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) { ...@@ -30,13 +33,17 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) {
try { try {
const siweMessage = await apiFetch('auth_siwe_message', { queryParams: { address } }) as { siwe_message: string }; const siweMessage = await apiFetch('auth_siwe_message', { queryParams: { address } }) as { siwe_message: string };
const signature = await signMessageAsync({ message: siweMessage.siwe_message }); const signature = await signMessageAsync({ message: siweMessage.siwe_message });
await apiFetch('auth_siwe_verify', { const resource = isAuth ? 'auth_link_address' : 'auth_siwe_verify';
const response = await apiFetch<typeof resource, UserInfo, unknown>(resource, {
fetchParams: { fetchParams: {
method: 'POST', method: 'POST',
body: { message: siweMessage.siwe_message, signature }, body: { message: siweMessage.siwe_message, signature },
}, },
}); });
onSuccess?.({ address }); if (!('name' in response)) {
throw Error('Something went wrong');
}
onSuccess?.({ address, profile: response });
} catch (error) { } catch (error) {
const errorObj = getErrorObj(error); const errorObj = getErrorObj(error);
const shortMessage = errorObj && 'shortMessage' in errorObj && typeof errorObj.shortMessage === 'string' ? errorObj.shortMessage : undefined; const shortMessage = errorObj && 'shortMessage' in errorObj && typeof errorObj.shortMessage === 'string' ? errorObj.shortMessage : undefined;
...@@ -49,7 +56,7 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) { ...@@ -49,7 +56,7 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) {
} finally { } finally {
setIsPending(false); setIsPending(false);
} }
}, [ apiFetch, onError, onSuccess, signMessageAsync, toast ]); }, [ apiFetch, isAuth, onError, onSuccess, signMessageAsync, toast ]);
const start = React.useCallback(() => { const start = React.useCallback(() => {
setIsPending(true); setIsPending(true);
......
...@@ -52,6 +52,7 @@ const UserProfileContentWallet = ({ onClose, className }: Props) => { ...@@ -52,6 +52,7 @@ const UserProfileContentWallet = ({ onClose, className }: Props) => {
onClick={ handleOpenWalletClick } onClick={ handleOpenWalletClick }
isLoading={ web3Wallet.isOpen } isLoading={ web3Wallet.isOpen }
flexShrink={ 0 } flexShrink={ 0 }
ml="auto"
/> />
</Flex> </Flex>
); );
......
...@@ -40,6 +40,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) => ...@@ -40,6 +40,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
return; return;
} }
// TODO @tom2drum use auth modal instead
if (router.pathname === '/apps/[id]') { if (router.pathname === '/apps/[id]') {
signInWithWallet.start(); signInWithWallet.start();
return; return;
......
...@@ -34,6 +34,7 @@ const UserProfileMobile = () => { ...@@ -34,6 +34,7 @@ const UserProfileMobile = () => {
return; return;
} }
// TODO @tom2drum use auth modal instead
if (router.pathname === '/apps/[id]') { if (router.pathname === '/apps/[id]') {
signInWithWallet.start(); signInWithWallet.start();
return; return;
......
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