diff --git a/components/Modal/Sign.tsx b/components/Modal/Sign.tsx new file mode 100644 index 00000000..befa44ac --- /dev/null +++ b/components/Modal/Sign.tsx @@ -0,0 +1,78 @@ +import { + Button, + Heading, + Icon, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Text, +} from '@chakra-ui/react' +import { FaFileSignature } from '@react-icons/all-files/fa/FaFileSignature' +import useTranslation from 'next-translate/useTranslation' +import { FC, useCallback, useState } from 'react' +import { Connector, useDisconnect } from 'wagmi' +import useAccount from '../../hooks/useAccount' + +type Props = { + connector?: Connector + isOpen: boolean + onClose: () => void +} + +const SignModal: FC = ({ connector, isOpen, onClose }) => { + const { t } = useTranslation('components') + const { sign } = useAccount() + const { disconnect } = useDisconnect() + const [isLoading, setLoading] = useState(false) + + const onSign = useCallback(async () => { + if (!connector) return + setLoading(true) + try { + await sign(connector) + } catch (e: any) { + disconnect() + } finally { + onClose() + setLoading(false) + } + }, [connector, disconnect, onClose, sign]) + + return ( + { + onClose() + disconnect() + }} + isCentered + size="sm" + > + + + + + {t('modal.signature.title')} + + + + + + {t('modal.signature.sub-title')} + {t('modal.signature.description')} + + + + + + + ) +} + +export default SignModal diff --git a/hooks/useAccount.ts b/hooks/useAccount.ts index 31b50267..e7c9ae7b 100644 --- a/hooks/useAccount.ts +++ b/hooks/useAccount.ts @@ -1,6 +1,6 @@ import { useAuthenticate, useIsLoggedIn } from '@liteflow/react' import { JwtPayload, jwtDecode } from 'jwt-decode' -import { useCallback, useEffect, useMemo } from 'react' +import { useCallback, useMemo } from 'react' import { useCookies } from 'react-cookie' import { Connector, useAccount as useWagmiAccount } from 'wagmi' import { walletClientToSigner } from './useSigner' @@ -12,6 +12,7 @@ type AccountDetail = { jwtToken: string | null logout: () => Promise login: (connector: Connector) => Promise + sign: (connector: Connector) => Promise } export const COOKIE_JWT_TOKEN = 'jwt-token' @@ -57,14 +58,6 @@ export default function useAccount(): AccountDetail { [isLoggedInWhileReconnect, isLoggedInToAPI], ) - // Reconnect based on the token and mark as logged in - useEffect(() => { - if (isLoggedInToAPI) return - if (!isReconnecting) return - if (!jwt) return - setAuthenticationToken(jwt.token) - }, [isLoggedInToAPI, isReconnecting, jwt, setAuthenticationToken]) - const login = useCallback( async (connector: Connector) => { const wallet = await connector.getWalletClient() @@ -73,6 +66,14 @@ export default function useAccount(): AccountDetail { if (jwt && currentAddress === jwt.address) { return setAuthenticationToken(jwt.token) } + }, + [jwt, setAuthenticationToken], + ) + + const sign = useCallback( + async (connector: Connector) => { + const wallet = await connector.getWalletClient() + const signer = walletClientToSigner(wallet) const { jwtToken } = await authenticate(signer) const newJwt = jwtDecode(jwtToken) @@ -86,7 +87,7 @@ export default function useAccount(): AccountDetail { : {}), }) }, - [jwt, authenticate, setAuthenticationToken, setCookie], + [authenticate, setCookie], ) // Server side @@ -98,6 +99,7 @@ export default function useAccount(): AccountDetail { isConnected: !!jwt, logout, login, + sign, } } @@ -108,5 +110,6 @@ export default function useAccount(): AccountDetail { isConnected, logout, login, + sign, } } diff --git a/locales/en/components.json b/locales/en/components.json index f01a4ec8..92d1703d 100644 --- a/locales/en/components.json +++ b/locales/en/components.json @@ -475,6 +475,12 @@ "title": "Sign in with your wallet", "description": "Connect with one of the following options.", "alternative": "Or sign in with" + }, + "signature": { + "title": "Signature", + "sub-title": "Verify your account", + "description": "To complete the connection, you must sign a message in your wallet to verify that you are the owner of this account.", + "action": "Send message" } }, "back": "Go back", diff --git a/locales/es-mx/components.json b/locales/es-mx/components.json index 9410f455..3971ced8 100644 --- a/locales/es-mx/components.json +++ b/locales/es-mx/components.json @@ -475,6 +475,12 @@ "title": "Inicia sesión con tu Wallet", "description": "Conéctate con alguna de las siguientes opciones:", "alternative": "O inicia sesión con:" + }, + "signature": { + "title": "Signature", + "sub-title": "Verify your account", + "description": "To complete the connection, you must sign a message in your wallet to verify that you are the owner of this account.", + "action": "Send message" } }, "back": "Volver", diff --git a/locales/ja/components.json b/locales/ja/components.json index 1e6fb175..69897be5 100644 --- a/locales/ja/components.json +++ b/locales/ja/components.json @@ -475,6 +475,12 @@ "title": "ウォレットでサインインする", "description": "次のいずれかのオプションを使用して接続します。", "alternative": "または、" + }, + "signature": { + "title": "Signature", + "sub-title": "Verify your account", + "description": "To complete the connection, you must sign a message in your wallet to verify that you are the owner of this account.", + "action": "Send message" } }, "back": "戻る", diff --git a/locales/zh-cn/components.json b/locales/zh-cn/components.json index 097fb1b4..54cd161e 100644 --- a/locales/zh-cn/components.json +++ b/locales/zh-cn/components.json @@ -475,6 +475,12 @@ "title": "使用钱包登录", "description": "使用以下选项之一进行连接。", "alternative": "或使用" + }, + "signature": { + "title": "Signature", + "sub-title": "Verify your account", + "description": "To complete the connection, you must sign a message in your wallet to verify that you are the owner of this account.", + "action": "Send message" } }, "back": "返回", diff --git a/pages/_app.tsx b/pages/_app.tsx index 26c9752f..08a91ca1 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,7 +1,7 @@ import { ApolloProvider } from '@apollo/client' import Bugsnag from '@bugsnag/js' import BugsnagPluginReact from '@bugsnag/plugin-react' -import { Box, ChakraProvider } from '@chakra-ui/react' +import { Box, ChakraProvider, useDisclosure } from '@chakra-ui/react' import { LiteflowProvider } from '@liteflow/react' import { RainbowKitProvider, lightTheme } from '@rainbow-me/rainbowkit' import '@rainbow-me/rainbowkit/styles.css' @@ -22,7 +22,7 @@ import React, { useMemo, useState, } from 'react' -import { Cookies, CookiesProvider } from 'react-cookie' +import { Cookies, CookiesProvider, useCookies } from 'react-cookie' import { WagmiConfig, useDisconnect, @@ -31,6 +31,7 @@ import { import getClient from '../client' import CartContext from '../components/CartContext' import Footer from '../components/Footer/Footer' +import SignModal from '../components/Modal/Sign' import Navbar from '../components/Navbar/Navbar' import connectors from '../connectors' import getEnvironment, { Environment, EnvironmentContext } from '../environment' @@ -125,14 +126,18 @@ function AccountProvider({ onError, }: PropsWithChildren<{ onError: (code: number) => void }>) { const { LITEFLOW_API_KEY, BASE_URL } = useEnvironment() - const { login, jwtToken, logout } = useAccount() + const { jwtToken, login, logout } = useAccount() const { disconnect } = useDisconnect() + const { isOpen, onOpen, onClose } = useDisclosure() + const [cookies] = useCookies([COOKIE_JWT_TOKEN]) const { connector } = useWagmiAccount({ async onConnect({ connector }) { if (!connector) return try { await login(connector) + // jwtToken from useAccount is null on refresh, so we need to check the cookies + if (!cookies[COOKIE_JWT_TOKEN]) onOpen() } catch (e: any) { disconnect() } @@ -145,12 +150,14 @@ function AccountProvider({ // handle change of account useEffect(() => { if (!connector) return - const handleLogin = () => login(connector) + const handleLogin = () => { + login(connector).then(onOpen).catch(onError) + } connector.on('change', handleLogin) return () => { connector.off('change', handleLogin) } - }, [connector, login]) + }, [connector, login, onError, onOpen]) const client = useMemo( // The client needs to be reset when the jwtToken changes but only on the client as the server will @@ -166,7 +173,12 @@ function AccountProvider({ [jwtToken, onError, LITEFLOW_API_KEY, BASE_URL], ) - return {children} + return ( + <> + {children} + + + ) } export type MyAppProps = {