From 972a35707ba19cef4e707b415131fa1fd6c5d68f Mon Sep 17 00:00:00 2001 From: phipsae Date: Sat, 2 Dec 2023 14:54:01 -0500 Subject: [PATCH] modal in works, before delete modal --- .../scaffold-eth/AddressPrivateKey.tsx | 55 +++++++ .../AccountSwitcher/AccountSwitcher.tsx | 142 ++++++++++++++++++ .../wallet/AccountSwitcher/DeleteAccount.tsx | 38 +++++ .../wallet/AccountSwitcher/ShowPrivateKey.tsx | 0 packages/nextjs/pages/wallet.tsx | 73 +++++++-- packages/nextjs/sharedStateContext.tsx | 20 ++- 6 files changed, 310 insertions(+), 18 deletions(-) create mode 100644 packages/nextjs/components/scaffold-eth/AddressPrivateKey.tsx create mode 100644 packages/nextjs/components/wallet/AccountSwitcher/AccountSwitcher.tsx create mode 100644 packages/nextjs/components/wallet/AccountSwitcher/DeleteAccount.tsx create mode 100644 packages/nextjs/components/wallet/AccountSwitcher/ShowPrivateKey.tsx diff --git a/packages/nextjs/components/scaffold-eth/AddressPrivateKey.tsx b/packages/nextjs/components/scaffold-eth/AddressPrivateKey.tsx new file mode 100644 index 0000000..d0b212c --- /dev/null +++ b/packages/nextjs/components/scaffold-eth/AddressPrivateKey.tsx @@ -0,0 +1,55 @@ +import { useState } from "react"; +import { CopyToClipboard } from "react-copy-to-clipboard"; +import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline"; + +type TAddressProps = { + address?: string; + disableAddressLink?: boolean; + format?: "short" | "long"; + size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl"; +}; + +export const AddressPrivateKey = ({ address, disableAddressLink, format, size = "base" }: TAddressProps) => { + const [addressCopied, setAddressCopied] = useState(false); + + let displayAddress = address?.slice(0, 5) + "..." + address?.slice(-4); + + if (format === "long" && address) { + displayAddress = address; + } + + return ( + <> +
+ {disableAddressLink ? ( + {displayAddress} + ) : ( +
{displayAddress}
+ )} + {addressCopied ? ( +
+ + ); +}; diff --git a/packages/nextjs/components/wallet/AccountSwitcher/AccountSwitcher.tsx b/packages/nextjs/components/wallet/AccountSwitcher/AccountSwitcher.tsx new file mode 100644 index 0000000..089b34a --- /dev/null +++ b/packages/nextjs/components/wallet/AccountSwitcher/AccountSwitcher.tsx @@ -0,0 +1,142 @@ +import { useCallback, useEffect, useState } from "react"; +import { DeleteAccount } from "./DeleteAccount"; +import { PrivateKeyAccount, generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { AddressAdapted } from "~~/components/scaffold-eth/AddressAdapted"; +import { AddressPrivateKey } from "~~/components/scaffold-eth/AddressPrivateKey"; +import { useSharedState } from "~~/sharedStateContext"; + +interface AccountWithPrivateKey { + account: PrivateKeyAccount; + privateKey: string; +} + +export const AccountSwitcher = () => { + // const [accounts, setAccounts] = useState([]); + // const [selectedAccount, setSelectedAccount] = useState(); + const { accounts, setAccounts, selectedAccount, setSelectedAccount } = useSharedState(); + const [revealedPrivateKey, setRevealedPrivateKey] = useState(""); + const [showPrivateKey, setShowPrivateKey] = useState(false); + + const generateAccount = useCallback(() => { + const privateKey = generatePrivateKey(); + const newAccount = privateKeyToAccount(privateKey); + + const newAccountWithKey: AccountWithPrivateKey = { + account: newAccount, + privateKey: privateKey, + }; + setAccounts([...accounts, newAccountWithKey]); + return newAccountWithKey; + }, [accounts, setAccounts]); + + const handleRowClick = (account: PrivateKeyAccount, privateKey: string) => { + setSelectedAccount(account); + setRevealedPrivateKey(privateKey); + console.log("selected Account", selectedAccount); + }; + + const togglePrivateKey = () => { + setShowPrivateKey(!showPrivateKey); + }; + + useEffect(() => { + if (accounts.length === 0) { + // Generate the initial account + const initialAccount = generateAccount(); + + // Set the initial account as the selected account + setSelectedAccount(initialAccount.account); + + // Set the private key of the initial account + setRevealedPrivateKey(initialAccount.privateKey); + } + }, [accounts, generateAccount, setSelectedAccount]); // Dependency array includes 'accounts' + + useEffect(() => { + // Set 'showPrivateKey' to false whenever 'selectedAccount' changes + setShowPrivateKey(false); + }, [selectedAccount]); + + const openModal = () => { + const modal = document.getElementById("my_modal_2") as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } else { + console.error("Modal element not found!"); + } + }; + + return ( + <> +
+ + {/* head */} + + + + + + + +
#AccountDelete
+
+ + + {/* row */} + {accounts.map(account => ( + + + + + + ))} + +
+ handleRowClick(account.account, account.privateKey)} + /> + + {/* {account.account.address?.slice(0, 5) + "..." + account.account.address?.slice(-4)} */} + + + + +
+
+
+ + + {showPrivateKey && ( +
+ Private Key: + +
+ )} + + ); +}; diff --git a/packages/nextjs/components/wallet/AccountSwitcher/DeleteAccount.tsx b/packages/nextjs/components/wallet/AccountSwitcher/DeleteAccount.tsx new file mode 100644 index 0000000..111f7b0 --- /dev/null +++ b/packages/nextjs/components/wallet/AccountSwitcher/DeleteAccount.tsx @@ -0,0 +1,38 @@ +import { useSharedState } from "~~/sharedStateContext"; + +interface DeleteAccountProps { + address: string; +} + +export const DeleteAccount = ({ address }: DeleteAccountProps) => { + const { accounts, setAccounts } = useSharedState(); + + const deleteAccount = (address: string) => { + const filteredAccounts = accounts.filter(acc => acc.account.address !== address); + setAccounts(filteredAccounts); + }; + return ( + <> + +
+

Hello!

+

Press ESC key or click the button below to close

+
+
+ + + +
+
+
+
+ + ); +}; diff --git a/packages/nextjs/components/wallet/AccountSwitcher/ShowPrivateKey.tsx b/packages/nextjs/components/wallet/AccountSwitcher/ShowPrivateKey.tsx new file mode 100644 index 0000000..e69de29 diff --git a/packages/nextjs/pages/wallet.tsx b/packages/nextjs/pages/wallet.tsx index b4f30db..621bbf0 100644 --- a/packages/nextjs/pages/wallet.tsx +++ b/packages/nextjs/pages/wallet.tsx @@ -1,5 +1,7 @@ import type { NextPage } from "next"; import { privateKeyToAccount } from "viem/accounts"; +import { ExclamationTriangleIcon } from "@heroicons/react/20/solid"; +import { AccountSwitcher } from "~~/components/wallet/AccountSwitcher/AccountSwitcher"; import { NetworkMenu } from "~~/components/wallet/NetworkMenu"; import { TokenOverview } from "~~/components/wallet/TokenOverview/TokenOverview"; import { SelectedTokenTransaction } from "~~/components/wallet/Transaction/SelectedTokenTransaction"; @@ -8,27 +10,68 @@ import { useSharedState } from "~~/sharedStateContext"; const Wallet: NextPage = () => { const account = privateKeyToAccount(`0x${process.env.NEXT_PUBLIC_PRIVATE_KEY_WALLET}`); - const { selectedChain, selectedTokenAddress, selectedTokenName, selectedTokenImage } = useSharedState(); + const { selectedChain, selectedTokenAddress, selectedTokenName, selectedTokenImage, selectedAccount } = + useSharedState(); + + const openModal = (modalName: string) => { + const modal = document.getElementById(modalName) as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } else { + console.error("Modal element not found!"); + } + }; return ( <>
+ + +
+

+ +

+
+
+
+ +
+
+ + Warning: Never disclose your private key. Anyone with your private keys can steal any assets held in + your account. + +
+
+
+
+
+ +
+
+
+
+
-
- - -
+ {selectedAccount && ( +
+ + +
+ )}
💸 Token Overview diff --git a/packages/nextjs/sharedStateContext.tsx b/packages/nextjs/sharedStateContext.tsx index 629f2ed..10e29bf 100644 --- a/packages/nextjs/sharedStateContext.tsx +++ b/packages/nextjs/sharedStateContext.tsx @@ -1,4 +1,5 @@ import { ReactNode, createContext, useContext, useState } from "react"; +import { PrivateKeyAccount } from "viem"; interface SharedStateContextProps { selectedChain: string; @@ -13,6 +14,10 @@ interface SharedStateContextProps { setSelectedBlockExplorer: (value: string) => void; isConfirmed: boolean; setIsConfirmed: (value: boolean) => void; + accounts: AccountWithPrivateKey[]; + setAccounts: (value: AccountWithPrivateKey[]) => void; + selectedAccount: PrivateKeyAccount | undefined; + setSelectedAccount: (value: PrivateKeyAccount) => void; } const SharedStateContext = createContext(undefined); @@ -22,10 +27,10 @@ export const SharedStateProvider: React.FC<{ children: ReactNode }> = ({ childre const [selectedTokenAddress, setSelectedTokenAddress] = useState("nativeToken"); const [selectedTokenName, setSelectedTokenName] = useState("ETH"); const [selectedTokenImage, setSelectedTokenImage] = useState("/ETH.png"); - const [selectedBlockExplorer, setSelectedBlockExplorer] = useState("/ETH.png"); + const [selectedBlockExplorer, setSelectedBlockExplorer] = useState("https://etherscan.io/"); const [isConfirmed, setIsConfirmed] = useState(false); - - // "https://etherscan.io/" + const [accounts, setAccounts] = useState([]); + const [selectedAccount, setSelectedAccount] = useState(); return ( = ({ childre setSelectedTokenImage, selectedBlockExplorer, setSelectedBlockExplorer, + accounts, + setAccounts, + selectedAccount, + setSelectedAccount, }} > {children} @@ -56,3 +65,8 @@ export const useSharedState = () => { } return context; }; + +interface AccountWithPrivateKey { + account: PrivateKeyAccount; + privateKey: string; +}