Commit 39ac3399 authored by tom's avatar tom

add screens to modal for wallet authentication

parent 64946979
......@@ -226,6 +226,14 @@ export const RESOURCES = {
path: '/api/account/v2/confirm_otp',
},
auth_siwe_message: {
path: '/api/account/v2/siwe_message',
},
auth_siwe_verify: {
path: '/api/account/v2/authenticate_via_wallet',
},
// STATS MICROSERVICE API
stats_counters: {
path: '/api/v1/counters',
......
import getErrorObj from './getErrorObj';
export default function getErrorMessage(error: Error | undefined): string | undefined {
export default function getErrorMessage(error: unknown): string | undefined {
const errorObj = getErrorObj(error);
return errorObj && 'message' in errorObj && typeof errorObj.message === 'string' ? errorObj.message : undefined;
}
......@@ -24,7 +24,7 @@ const init = () => {
'--w3m-font-family': `${ BODY_TYPEFACE }, sans-serif`,
'--w3m-accent': colors.blue[600],
'--w3m-border-radius-master': '2px',
'--w3m-z-index': zIndices.modal,
'--w3m-z-index': zIndices.popover,
},
featuredWalletIds: [],
allowUnsupportedChain: true,
......
......@@ -5,10 +5,12 @@ import type { Screen } from './types';
import IconSvg from 'ui/shared/IconSvg';
import AuthModalScreenConnectWallet from './screens/AuthModalScreenConnectWallet';
import AuthModalScreenEmail from './screens/AuthModalScreenEmail';
import AuthModalScreenOtpCode from './screens/AuthModalScreenOtpCode';
import AuthModalScreenSelectMethod from './screens/AuthModalScreenSelectMethod';
import AuthModalScreenSuccessCreatedEmail from './screens/AuthModalScreenSuccessCreatedEmail';
import AuthModalScreenSuccessCreatedWallet from './screens/AuthModalScreenSuccessCreatedWallet';
interface Props {
initialScreen: Screen;
......@@ -26,16 +28,23 @@ const AuthModal = ({ initialScreen, onClose }: Props) => {
setSteps((prev) => prev.length > 1 ? prev.slice(0, -1) : prev);
}, []);
const onReset = React.useCallback(() => {
setSteps([ initialScreen ]);
}, [ initialScreen ]);
const header = (() => {
const currentStep = steps[steps.length - 1];
switch (currentStep.type) {
case 'select_method':
return 'Select a way to connect';
case 'connect_wallet':
return 'Continue with wallet';
case 'email':
return 'Continue with email';
return currentStep.isAccountExists ? 'Add email' : 'Continue with email';
case 'otp_code':
return 'Confirmation code';
case 'success_created_email':
case 'success_created_wallet':
return 'Congrats!';
}
})();
......@@ -45,12 +54,16 @@ const AuthModal = ({ initialScreen, onClose }: Props) => {
switch (currentStep.type) {
case 'select_method':
return <AuthModalScreenSelectMethod onSelectMethod={ onNextStep }/>;
case 'connect_wallet':
return <AuthModalScreenConnectWallet onSuccess={ onNextStep } onError={ onReset }/>;
case 'email':
return <AuthModalScreenEmail onSubmit={ onNextStep }/>;
case 'otp_code':
return <AuthModalScreenOtpCode email={ currentStep.email } onSubmit={ onNextStep }/>;
case 'success_created_email':
return <AuthModalScreenSuccessCreatedEmail/>;
case 'success_created_wallet':
return <AuthModalScreenSuccessCreatedWallet address={ currentStep.address } onAddEmail={ onNextStep }/>;
}
})();
......
import { Center, Spinner } from '@chakra-ui/react';
import { useWeb3Modal } from '@web3modal/wagmi/react';
import React from 'react';
import { useAccount, useSignMessage } from 'wagmi';
import type { Screen } from '../types';
import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage';
import useToast from 'lib/hooks/useToast';
interface Props {
onSuccess: (screen: Screen) => void;
onError: () => void;
}
const AuthModalScreenConnectWallet = ({ onSuccess, onError }: Props) => {
const isSigningRef = React.useRef(false);
const apiFetch = useApiFetch();
const toast = useToast();
const web3Modal = useWeb3Modal();
const { isConnected, address } = useAccount();
const { signMessageAsync } = useSignMessage();
React.useEffect(() => {
!isConnected && web3Modal.open();
}, [ isConnected, web3Modal ]);
const proceedToAuth = React.useCallback(async(address: string) => {
try {
const siweMessage = await apiFetch('auth_siwe_message', { queryParams: { address } }) as { siwe_message: string };
const signature = await signMessageAsync({ message: siweMessage.siwe_message });
await apiFetch('auth_siwe_verify', {
fetchParams: {
method: 'POST',
body: { message: siweMessage.siwe_message, signature },
},
});
onSuccess({ type: 'success_created_wallet', address });
} catch (error) {
// TODO @tom2drum show better error message
onError();
toast({
status: 'error',
title: 'Error',
description: getErrorMessage(error) || 'Something went wrong',
});
}
}, [ apiFetch, onError, onSuccess, signMessageAsync, toast ]);
React.useEffect(() => {
if (isConnected && address && !isSigningRef.current) {
isSigningRef.current = true;
proceedToAuth(address);
}
}, [ address, isConnected, proceedToAuth ]);
return <Center h="100px"><Spinner/></Center>;
};
export default React.memo(AuthModalScreenConnectWallet);
......@@ -13,9 +13,13 @@ const AuthModalScreenSelectMethod = ({ onSelectMethod }: Props) => {
onSelectMethod({ type: 'email' });
}, [ onSelectMethod ]);
const handleConnectWalletClick = React.useCallback(() => {
onSelectMethod({ type: 'connect_wallet' });
}, [ onSelectMethod ]);
return (
<VStack spacing={ 3 } mt={ 4 } align="stretch">
<Button variant="outline">Connect Web3 wallet</Button>
<Button variant="outline" onClick={ handleConnectWalletClick }>Connect Web3 wallet</Button>
<Button variant="outline" onClick={ handleEmailClick }>Continue with email</Button>
</VStack>
);
......
import { chakra, Box, Text, Button } from '@chakra-ui/react';
import React from 'react';
import type { Screen } from '../types';
import shortenString from 'lib/shortenString';
interface Props {
address: string;
onAddEmail: (screen: Screen) => void;
}
const AuthModalScreenSuccessCreatedWallet = ({ address, onAddEmail }: Props) => {
const handleAddEmailClick = React.useCallback(() => {
onAddEmail({ type: 'email', isAccountExists: true });
}, [ onAddEmail ]);
return (
<Box>
<Text>
Your account was linked to{ ' ' }
<chakra.span fontWeight="700">{ shortenString(address) }</chakra.span>{ ' ' }
wallet. Use for the next login.
</Text>
<Text mt={ 6 }>Add your email to receive notifications about addresses in your watch list.</Text>
<Button mt={ 6 } onClick={ handleAddEmailClick }>Add email</Button>
</Box>
);
};
export default React.memo(AuthModalScreenSuccessCreatedWallet);
export type Screen = {
type: 'select_method';
} | {
type: 'connect_wallet';
} | {
type: 'email';
isAccountExists?: boolean;
} | {
type: 'otp_code';
email: string;
} | {
type: 'success_created_email';
} | {
type: 'success_created_wallet';
address: string;
}
export interface EmailFormFields {
......
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