From fc1c8df06b1b8fc35e280c7e7809c2805cb5dc12 Mon Sep 17 00:00:00 2001 From: nabarun Date: Fri, 9 Aug 2024 06:24:29 +0000 Subject: [PATCH] Persist subscriber ID in local storage (#26) Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675) - Disable nitro key sign button if subscribe ID does not exist Co-authored-by: Shreerang Kale Reviewed-on: https://git.vdb.to/cerc-io/testnet-onboarding-app/pulls/26 --- .eslintrc.json | 3 ++- src/components/Header.tsx | 2 +- src/components/SelectRoleCard.tsx | 8 +++---- src/components/TermsAndConditionsBox.tsx | 2 +- src/constants.ts | 6 +++-- src/pages/Email.tsx | 12 +++++----- src/pages/OnboardingSuccess.tsx | 6 +++-- src/pages/SignWithCosmos.tsx | 5 ++-- src/pages/SignWithNitroKey.tsx | 30 +++++++++++------------- src/pages/Thanks.tsx | 16 ++++++------- src/pages/UserVerification.tsx | 5 ++-- src/pages/VerifyEmail.tsx | 8 +++---- src/utils/sumsub.ts | 2 +- 13 files changed, 55 insertions(+), 50 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 98c2686..2e2a623 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -12,6 +12,7 @@ } }, "rules": { - "indent": ["error", 2, { "SwitchCase": 1 }] + "indent": ["error", 2, { "SwitchCase": 1 }], + "semi": ["error", "always"] } } diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 0b627a6..c1bd679 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -4,7 +4,7 @@ import { Link, useLocation } from 'react-router-dom'; import { AppBar, Toolbar, Avatar, Box, IconButton } from '@mui/material'; const Header: React.FC = () => { - const location = useLocation() + const location = useLocation(); return ( diff --git a/src/components/SelectRoleCard.tsx b/src/components/SelectRoleCard.tsx index 061a9e9..4397de9 100644 --- a/src/components/SelectRoleCard.tsx +++ b/src/components/SelectRoleCard.tsx @@ -14,16 +14,16 @@ const SelectRoleCard = ({ handleAccept, handleRoleChange }: { handleAccept: () = const [checked, setChecked] = useState(false); const [isHidden, setIsHidden] = useState(false); - const [isDialogOpen, setisDialogOpen] = useState(false) + const [isDialogOpen, setisDialogOpen] = useState(false); const handleCheckboxChange = (event: React.ChangeEvent) => { setChecked(event.target.checked); }; const handleContinue = () => { - handleAccept() - setIsHidden(true) - } + handleAccept(); + setIsHidden(true); + }; const handleRadioChange = (event: React.ChangeEvent) => { setSelectedRole(event.target.value as Role); diff --git a/src/components/TermsAndConditionsBox.tsx b/src/components/TermsAndConditionsBox.tsx index ddddc42..30685b9 100644 --- a/src/components/TermsAndConditionsBox.tsx +++ b/src/components/TermsAndConditionsBox.tsx @@ -4,7 +4,7 @@ import { Document, Page, pdfjs } from 'react-pdf'; import { Typography } from '@mui/material'; // https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#copy-worker-to-public-directory -pdfjs.GlobalWorkerOptions.workerSrc = process.env.PUBLIC_URL + '/pdf.worker.min.mjs' +pdfjs.GlobalWorkerOptions.workerSrc = process.env.PUBLIC_URL + '/pdf.worker.min.mjs'; const TermsAndConditionsBox = ({height}: {height: string}) => { const [numPages, setNumPages] = useState(); diff --git a/src/constants.ts b/src/constants.ts index ccceafc..8df2b99 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,7 @@ -export const WALLET_DISCLAIMER_MSG = 'You are connecting to an experimental wallet! It is not secure. Do not use it elsewhere and/or for managing real assets.' +export const WALLET_DISCLAIMER_MSG = 'You are connecting to an experimental wallet! It is not secure. Do not use it elsewhere and/or for managing real assets.'; -export const REDIRECT_EMAIL_MSG = 'Close this tab and the confirmation link in your email will bring you back to the onboarding app.' +export const REDIRECT_EMAIL_MSG = 'Close this tab and the confirmation link in your email will bring you back to the onboarding app.'; export const ENABLE_KYC = false; + +export const SUBSCRIBER_ID_HASH_KEY = 'subscriberIdHash'; diff --git a/src/pages/Email.tsx b/src/pages/Email.tsx index 630145d..31491eb 100644 --- a/src/pages/Email.tsx +++ b/src/pages/Email.tsx @@ -1,8 +1,8 @@ -import React from 'react' +import React from 'react'; -import { Box, Typography } from '@mui/material' +import { Box, Typography } from '@mui/material'; -import { REDIRECT_EMAIL_MSG } from '../constants' +import { REDIRECT_EMAIL_MSG } from '../constants'; const Email = () => { return ( @@ -27,7 +27,7 @@ const Email = () => { {REDIRECT_EMAIL_MSG} - ) -} + ); +}; -export default Email +export default Email; diff --git a/src/pages/OnboardingSuccess.tsx b/src/pages/OnboardingSuccess.tsx index c06bdb5..aa64f56 100644 --- a/src/pages/OnboardingSuccess.tsx +++ b/src/pages/OnboardingSuccess.tsx @@ -8,7 +8,7 @@ import SumsubWebSdk from "@sumsub/websdk-react"; import { MessageHandler } from "@sumsub/websdk"; import { config, fetchAccessToken, getAccessTokenExpirationHandler, options } from "../utils/sumsub"; -import { ENABLE_KYC } from "../constants"; +import { ENABLE_KYC, SUBSCRIBER_ID_HASH_KEY } from "../constants"; interface Participant { cosmosAddress: string; @@ -25,7 +25,7 @@ const OnboardingSuccess = () => { const location = useLocation(); const { cosmosAddress } = location.state as { cosmosAddress?: string - } + }; const [participant, setParticipant] = useState(); const [token, setToken] = useState(''); @@ -48,6 +48,8 @@ const OnboardingSuccess = () => { return; } + localStorage.removeItem(SUBSCRIBER_ID_HASH_KEY); + setParticipant(participant); } catch (error) { console.error("Error fetching participants", error); diff --git a/src/pages/SignWithCosmos.tsx b/src/pages/SignWithCosmos.tsx index 46a1db8..c5db0a2 100644 --- a/src/pages/SignWithCosmos.tsx +++ b/src/pages/SignWithCosmos.tsx @@ -12,6 +12,7 @@ import { StargateClient } from "@cosmjs/stargate"; import { useWalletConnectContext } from "../context/WalletConnectContext"; import SelectRoleCard, {Role} from "../components/SelectRoleCard"; +import { SUBSCRIBER_ID_HASH_KEY } from "../constants"; const SignWithCosmos = () => { const { session, signClient } = useWalletConnectContext(); @@ -25,17 +26,17 @@ const SignWithCosmos = () => { const navigate = useNavigate(); - const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature, subscriberIdHash} = location.state as { + const {message: innerMessage, cosmosAddress, receivedEthSig: ethSignature} = location.state as { message?: { msg: string; address: string; }; cosmosAddress?: string; receivedEthSig?: string; - subscriberIdHash?: string; }; const ethAddress = innerMessage!.address; + const subscriberIdHash = localStorage.getItem(SUBSCRIBER_ID_HASH_KEY); const createCosmosClient = useCallback(async (endpoint: string) => { return await StargateClient.connect(endpoint); diff --git a/src/pages/SignWithNitroKey.tsx b/src/pages/SignWithNitroKey.tsx index 51b9754..70eb635 100644 --- a/src/pages/SignWithNitroKey.tsx +++ b/src/pages/SignWithNitroKey.tsx @@ -1,5 +1,5 @@ import React, { useState, useMemo, useEffect } from "react"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { enqueueSnackbar } from "notistack"; import canonicalStringify from "canonical-json"; @@ -13,7 +13,7 @@ import LoadingButton from '@mui/lab/LoadingButton'; import { utf8ToHex } from "@walletconnect/encoding"; import { useWalletConnectContext } from "../context/WalletConnectContext"; -import { ENABLE_KYC } from "../constants"; +import { ENABLE_KYC, SUBSCRIBER_ID_HASH_KEY } from "../constants"; const SignWithNitroKey = () => { @@ -27,13 +27,20 @@ const SignWithNitroKey = () => { }, [session, signClient, checkPersistedState]); const navigate = useNavigate(); - const location = useLocation(); const [ethAddress, setEthAddress] = useState(""); const [ethSignature, setEthSignature] = useState(""); const [cosmosAddress, setCosmosAddress] = useState(""); const [isLoading, setIsLoading] = useState(false); + const subscriberIdHash = localStorage.getItem(SUBSCRIBER_ID_HASH_KEY); + + useEffect(() => { + if (!subscriberIdHash) { + setIsLoading(false); + enqueueSnackbar("Subscriber ID not found. Please verify your email and try again", { variant: "error" }); + } + }, [subscriberIdHash]); const message = useMemo(() => { return { @@ -45,7 +52,7 @@ const SignWithNitroKey = () => { const signEth = async () => { if (session && signClient) { try { - setIsLoading(true) + setIsLoading(true); const jsonMessage = canonicalStringify(message); const hexMsg = utf8ToHex(jsonMessage, true); const receivedEthSig: string = await signClient!.request({ @@ -56,7 +63,7 @@ const SignWithNitroKey = () => { params: [hexMsg, ethAddress], }, }); - setIsLoading(false) + setIsLoading(false); setEthSignature(ethSignature); if (ENABLE_KYC) { @@ -68,26 +75,17 @@ const SignWithNitroKey = () => { }, }); } else { - const state = location.state as { - subscriberIdHash?: string - } - - if (!state.subscriberIdHash) { - throw new Error("Subscriber ID not found. Please verify your email and try again") - } - navigate("/sign-with-cosmos", { state: { message, cosmosAddress, receivedEthSig, - subscriberIdHash: state.subscriberIdHash, }, }); } } catch (error) { console.log("err in signing ", error); - setIsLoading(false) + setIsLoading(false); enqueueSnackbar("Error signing message", { variant: "error" }); } } @@ -151,7 +149,7 @@ const SignWithNitroKey = () => { diff --git a/src/pages/Thanks.tsx b/src/pages/Thanks.tsx index c8a18f0..6cd4c3e 100644 --- a/src/pages/Thanks.tsx +++ b/src/pages/Thanks.tsx @@ -5,6 +5,8 @@ import { ethers } from 'ethers'; import { Box, colors, Typography } from '@mui/material'; +import { SUBSCRIBER_ID_HASH_KEY } from '../constants'; + interface JwtPayload { subscriber_id: string; exp: number; @@ -24,28 +26,26 @@ const Thanks: React.FC = () => { try { if(!token){ - throw new Error("Invalid JWT Token") + throw new Error("Invalid JWT Token"); } const decoded = jwtDecode(token) as JwtPayload; const currentTime = Math.floor(Date.now() / 1000); if (!decoded.subscriber_id) { - throw new Error("Subscriber ID not found") + throw new Error("Subscriber ID not found"); } if (decoded.exp < currentTime) { throw new Error("Token has expired"); } - const subscriberIdBytes = ethers.utils.toUtf8Bytes(decoded.subscriber_id) + const subscriberIdBytes = ethers.utils.toUtf8Bytes(decoded.subscriber_id); const subscriberIdHash = ethers.utils.sha256(subscriberIdBytes); - navigate('/connect-wallet', { - state:{ - subscriberIdHash - } - }); + localStorage.setItem(SUBSCRIBER_ID_HASH_KEY, subscriberIdHash); + + navigate('/connect-wallet'); } catch (error) { setErr(String(error)); } diff --git a/src/pages/UserVerification.tsx b/src/pages/UserVerification.tsx index 31ffac7..e2ad450 100644 --- a/src/pages/UserVerification.tsx +++ b/src/pages/UserVerification.tsx @@ -45,13 +45,14 @@ const UserVerification = () => { useEffect(() => { if (applicationSubmitted && kycId !== '') { const kycIdHash = ethers.utils.sha256(ethers.utils.toUtf8Bytes(kycId)); + navigate("/sign-with-cosmos", { state: { message, cosmosAddress, receivedEthSig, - subscriberIdHash: kycIdHash, - }}) + kycIdHash + }}); } }, [applicationSubmitted, kycId, navigate, cosmosAddress, message, receivedEthSig]); diff --git a/src/pages/VerifyEmail.tsx b/src/pages/VerifyEmail.tsx index 4c8f053..218997b 100644 --- a/src/pages/VerifyEmail.tsx +++ b/src/pages/VerifyEmail.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React from 'react'; const VerifyEmail = () => { return ( @@ -18,7 +18,7 @@ const VerifyEmail = () => { }} > - ) -} + ); +}; -export default VerifyEmail +export default VerifyEmail; diff --git a/src/utils/sumsub.ts b/src/utils/sumsub.ts index 3d4feeb..3ba3abf 100644 --- a/src/utils/sumsub.ts +++ b/src/utils/sumsub.ts @@ -29,5 +29,5 @@ export const getAccessTokenExpirationHandler = (userId: string) => { return async () => { const newToken = await fetchAccessToken(userId); return newToken; - } + }; };