From 6d276f2e1eed3424377ae2f5a1ce0e8ae2901ba7 Mon Sep 17 00:00:00 2001 From: arjanjohan Date: Mon, 8 Jul 2024 22:44:08 +0200 Subject: [PATCH 1/4] dropdown layout --- packages/nextjs/components/Header.tsx | 2 + .../AddressInfoDropdown.tsx | 134 ++++++++++++++++++ .../AddressQRCodeModal.tsx | 33 +++++ .../CustomConnectButton/NetworkOptions.tsx | 43 ++++++ .../WrongNetworkDropdown.tsx | 32 +++++ .../CustomConnectButton/index.tsx | 69 +++++++++ .../nextjs/components/scaffold-move/index.tsx | 3 +- packages/nextjs/hooks/scaffold-move/index.ts | 3 +- .../hooks/scaffold-move/useOutsideClick.ts | 23 +++ 9 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx create mode 100644 packages/nextjs/components/scaffold-move/CustomConnectButton/AddressQRCodeModal.tsx create mode 100644 packages/nextjs/components/scaffold-move/CustomConnectButton/NetworkOptions.tsx create mode 100644 packages/nextjs/components/scaffold-move/CustomConnectButton/WrongNetworkDropdown.tsx create mode 100644 packages/nextjs/components/scaffold-move/CustomConnectButton/index.tsx create mode 100644 packages/nextjs/hooks/scaffold-move/useOutsideClick.ts diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx index 57430b7..99ff9f2 100644 --- a/packages/nextjs/components/Header.tsx +++ b/packages/nextjs/components/Header.tsx @@ -8,6 +8,7 @@ import { WalletSelector } from "@aptos-labs/wallet-adapter-ant-design"; import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline"; // import { FaucetButton } from "~~/components/scaffold-eth"; // import { useOutsideClick } from "~~/hooks/scaffold-eth"; +import {CustomConnectButton} from "~~/components/scaffold-move"; type HeaderMenuLink = { label: string; @@ -110,6 +111,7 @@ export const Header = () => {
+ ); diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx new file mode 100644 index 0000000..a58a1ba --- /dev/null +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx @@ -0,0 +1,134 @@ +import { useRef, useState } from "react"; +import { NetworkOptions } from "./NetworkOptions"; +import CopyToClipboard from "react-copy-to-clipboard"; +import { useDisconnect } from "wagmi"; +import { + ArrowLeftOnRectangleIcon, + ArrowTopRightOnSquareIcon, + ArrowsRightLeftIcon, + CheckCircleIcon, + ChevronDownIcon, + DocumentDuplicateIcon, + QrCodeIcon, +} from "@heroicons/react/24/outline"; +import { BlockieAvatar, isENS } from "~~/components/scaffold-eth"; +import { useOutsideClick } from "~~/hooks/scaffold-move"; +import { getTargetNetworks } from "~~/utils/scaffold-eth"; + +const allowedNetworks = getTargetNetworks(); + +type AddressInfoDropdownProps = { + address: string; + blockExplorerAddressLink: string | undefined; + displayName: string; + ensAvatar?: string; +}; + +export const AddressInfoDropdown = ({ + address, + ensAvatar, + displayName, + blockExplorerAddressLink, +}: AddressInfoDropdownProps) => { + const { disconnect } = useDisconnect(); + + const [addressCopied, setAddressCopied] = useState(false); + + const [selectingNetwork, setSelectingNetwork] = useState(false); + const dropdownRef = useRef(null); + const closeDropdown = () => { + setSelectingNetwork(false); + dropdownRef.current?.removeAttribute("open"); + }; + useOutsideClick(dropdownRef, closeDropdown); + + return ( + <> +
+ + + + {isENS(displayName) ? displayName : address?.slice(0, 6) + "..." + address?.slice(-4)} + + + +
    +
+
+ + ); +}; diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressQRCodeModal.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressQRCodeModal.tsx new file mode 100644 index 0000000..b5bb2ef --- /dev/null +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressQRCodeModal.tsx @@ -0,0 +1,33 @@ +import { QRCodeSVG } from "qrcode.react"; +import { Address as AddressType } from "viem"; +import { Address } from "~~/components/scaffold-eth"; + +type AddressQRCodeModalProps = { + address: AddressType; + modalId: string; +}; + +export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps) => { + return ( + <> +
+ + +
+ + ); +}; diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/NetworkOptions.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/NetworkOptions.tsx new file mode 100644 index 0000000..ee75438 --- /dev/null +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/NetworkOptions.tsx @@ -0,0 +1,43 @@ +import { useTheme } from "next-themes"; +import { useAccount, useSwitchChain } from "wagmi"; +import { ArrowsRightLeftIcon } from "@heroicons/react/24/solid"; +import { getTargetNetworks } from "~~/utils/scaffold-eth"; + +const allowedNetworks = getTargetNetworks(); + +type NetworkOptionsProps = { + hidden?: boolean; +}; + +export const NetworkOptions = ({ hidden = false }: NetworkOptionsProps) => { + const { switchChain } = useSwitchChain(); + const { chain } = useAccount(); + const { resolvedTheme } = useTheme(); + const isDarkMode = resolvedTheme === "dark"; + + return ( + <> + {allowedNetworks + .filter(allowedNetwork => allowedNetwork.id !== chain?.id) + .map(allowedNetwork => ( +
  • + +
  • + ))} + + ); +}; diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/WrongNetworkDropdown.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/WrongNetworkDropdown.tsx new file mode 100644 index 0000000..f9f0fd8 --- /dev/null +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/WrongNetworkDropdown.tsx @@ -0,0 +1,32 @@ +import { NetworkOptions } from "./NetworkOptions"; +import { useDisconnect } from "wagmi"; +import { ArrowLeftOnRectangleIcon, ChevronDownIcon } from "@heroicons/react/24/outline"; + +export const WrongNetworkDropdown = () => { + const { disconnect } = useDisconnect(); + + return ( +
    + +
      + +
    • + +
    • +
    +
    + ); +}; diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/index.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/index.tsx new file mode 100644 index 0000000..8424b09 --- /dev/null +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/index.tsx @@ -0,0 +1,69 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Balance } from "../Balance"; +import { AddressInfoDropdown } from "./AddressInfoDropdown"; +import { WrongNetworkDropdown } from "./WrongNetworkDropdown"; +import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; +import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth"; + +export const CustomConnectButton = () => { + const { targetNetwork } = useTargetNetwork(); + const [provider, setProvider] = useState(null); + const [account, setAccount] = useState(null); + const [chainId, setChainId] = useState(null); + const chain = {name: "devnet"}; // TODO: replace + + + + const handleAccountsChanged = (accounts) => { + if (accounts.length === 0) { + setAccount(null); + } else { + setAccount(accounts[0]); + } + }; + + const handleChainChanged = (chainId) => { + setChainId(chainId); + }; + + const connectWallet = async () => { + try { + // const accounts = await provider.send("eth_requestAccounts", []); + // setAccount(accounts[0]); + // const network = await provider.getNetwork(); + // setChainId(network.chainId); + } catch (error) { + console.error(error); + } + }; + + const blockExplorerAddressLink = account ? getBlockExplorerAddressLink(targetNetwork, account) : undefined; + const connected = account && chainId; + + return ( + <> + {/* {!connected ? ( + + ) : chainId !== targetNetwork.id ? ( + + ) : ( */} + <> +
    + {/* */} + {chain ? chain.name : "Loading..."} +
    + + + {/* )} */} + + ); +}; diff --git a/packages/nextjs/components/scaffold-move/index.tsx b/packages/nextjs/components/scaffold-move/index.tsx index c64f173..8064f6b 100644 --- a/packages/nextjs/components/scaffold-move/index.tsx +++ b/packages/nextjs/components/scaffold-move/index.tsx @@ -1,2 +1,3 @@ export * from "./Address"; -export * from "./Balance"; \ No newline at end of file +export * from "./Balance"; +export * from "./CustomConnectButton"; diff --git a/packages/nextjs/hooks/scaffold-move/index.ts b/packages/nextjs/hooks/scaffold-move/index.ts index ae664be..b3ad99f 100644 --- a/packages/nextjs/hooks/scaffold-move/index.ts +++ b/packages/nextjs/hooks/scaffold-move/index.ts @@ -1,3 +1,4 @@ export * from "./useDeployedContractInfo"; export * from "./useGetAccountAPTBalance"; -export * from "./useGetAccountResources"; \ No newline at end of file +export * from "./useGetAccountResources"; +export * from "./useOutsideClick"; \ No newline at end of file diff --git a/packages/nextjs/hooks/scaffold-move/useOutsideClick.ts b/packages/nextjs/hooks/scaffold-move/useOutsideClick.ts new file mode 100644 index 0000000..d7f2e0e --- /dev/null +++ b/packages/nextjs/hooks/scaffold-move/useOutsideClick.ts @@ -0,0 +1,23 @@ +import React, { useEffect } from "react"; + +/** + * Handles clicks outside of passed ref element + * @param ref - react ref of the element + * @param callback - callback function to call when clicked outside + */ +export const useOutsideClick = (ref: React.RefObject, callback: { (): void }) => { + useEffect(() => { + function handleOutsideClick(event: MouseEvent) { + if (!(event.target instanceof Element)) { + return; + } + + if (ref.current && !ref.current.contains(event.target)) { + callback(); + } + } + + document.addEventListener("click", handleOutsideClick); + return () => document.removeEventListener("click", handleOutsideClick); + }, [ref, callback]); +}; From a4ea9d379ee34dbca7678a2119dfc77a4dc7a65d Mon Sep 17 00:00:00 2001 From: arjanjohan Date: Tue, 9 Jul 2024 07:49:58 +0200 Subject: [PATCH 2/4] disconnect works --- packages/nextjs/components/Header.tsx | 1 + .../CustomConnectButton/AddressInfoDropdown.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx index 99ff9f2..a9480da 100644 --- a/packages/nextjs/components/Header.tsx +++ b/packages/nextjs/components/Header.tsx @@ -10,6 +10,7 @@ import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline"; // import { useOutsideClick } from "~~/hooks/scaffold-eth"; import {CustomConnectButton} from "~~/components/scaffold-move"; + type HeaderMenuLink = { label: string; href: string; diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx index a58a1ba..c679c5a 100644 --- a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx @@ -1,7 +1,6 @@ import { useRef, useState } from "react"; import { NetworkOptions } from "./NetworkOptions"; import CopyToClipboard from "react-copy-to-clipboard"; -import { useDisconnect } from "wagmi"; import { ArrowLeftOnRectangleIcon, ArrowTopRightOnSquareIcon, @@ -14,6 +13,9 @@ import { import { BlockieAvatar, isENS } from "~~/components/scaffold-eth"; import { useOutsideClick } from "~~/hooks/scaffold-move"; import { getTargetNetworks } from "~~/utils/scaffold-eth"; +import { + useWallet +} from "@aptos-labs/wallet-adapter-react"; const allowedNetworks = getTargetNetworks(); @@ -30,7 +32,6 @@ export const AddressInfoDropdown = ({ displayName, blockExplorerAddressLink, }: AddressInfoDropdownProps) => { - const { disconnect } = useDisconnect(); const [addressCopied, setAddressCopied] = useState(false); @@ -42,6 +43,8 @@ export const AddressInfoDropdown = ({ }; useOutsideClick(dropdownRef, closeDropdown); + + const { disconnect, wallet } = useWallet(); return ( <>
    From 5313f8d804830104fa8ef36c4ca73fb65ead8727 Mon Sep 17 00:00:00 2001 From: arjanjohan Date: Tue, 9 Jul 2024 08:33:20 +0200 Subject: [PATCH 3/4] update wallet dropdown --- README.md | 7 ++- packages/nextjs/app/page.tsx | 11 ++-- packages/nextjs/components/Header.tsx | 3 - .../AddressInfoDropdown.tsx | 4 +- .../AddressQRCodeModal.tsx | 5 +- .../CustomConnectButton/index.tsx | 60 +++++++------------ .../scaffold-move/WalletContext.tsx | 6 -- .../nextjs/utils/scaffold-eth/networks.ts | 16 ++--- 8 files changed, 46 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 48ddf39..e258bd6 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,11 @@ Visit your app on: `http://localhost:3000`. You can interact with your smart con For this hackathon I kept the scope small due to the time constraints. I build only the most essential and useful features, so that developers can start using Scaffold Move right away. However, there is much more that I want to add to this project after the hackathon. If you have any ideas or suggestions, please reach out and I will add it to this list. -- Styling wallet connect button + add dropdown -- Store network data in scaffold-config +- Wallet button component + - Fix QR code + - Add network switch + - Styling of connect button +- Store Aptos/Movement network data in scaffold-config - Debug page - Msg for no result on view methods - Fix colors for dark mode diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index 1dcdb5f..14dae44 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -2,12 +2,14 @@ import Link from "next/link"; import type { NextPage } from "next"; -import { useAccount } from "wagmi"; import { BugAntIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; -import { Address } from "~~/components/scaffold-eth"; +import { Address } from "~~/components/scaffold-move"; +import { + useWallet, +} from "@aptos-labs/wallet-adapter-react"; const Home: NextPage = () => { - const { address: connectedAddress } = useAccount(); + const { account: connectedAccount } = useWallet(); return ( <> @@ -19,7 +21,8 @@ const Home: NextPage = () => {

    Connected Address:

    -
    + +

    Get started by editing{" "} diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx index a9480da..1290aeb 100644 --- a/packages/nextjs/components/Header.tsx +++ b/packages/nextjs/components/Header.tsx @@ -109,9 +109,6 @@ export const Header = () => {

    -
    - -
    diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx index c679c5a..f9ddc7d 100644 --- a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx @@ -108,7 +108,7 @@ export const AddressInfoDropdown = ({ - {allowedNetworks.length > 1 ? ( + {/* {allowedNetworks.length > 1 ? ( */}
  • - ) : null} + {/* ) : null} */}
  • - ) : chainId !== targetNetwork.id ? ( - - ) : ( */} + {!connected ? ( +
    + +
    + ) + // : chainId !== targetNetwork.id ? ( + // + // ) + : ( <>
    {/* */} {chain ? chain.name : "Loading..."}
    - {/* )} */} + )} ); }; diff --git a/packages/nextjs/components/scaffold-move/WalletContext.tsx b/packages/nextjs/components/scaffold-move/WalletContext.tsx index b10c5ba..494e07d 100644 --- a/packages/nextjs/components/scaffold-move/WalletContext.tsx +++ b/packages/nextjs/components/scaffold-move/WalletContext.tsx @@ -2,8 +2,6 @@ import { ReactNode, createContext, useContext } from "react"; import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react"; import { PetraWallet } from "petra-plugin-wallet-adapter"; -const WalletContext = createContext(null); - export const WalletProvider = ({ children }: { children: ReactNode }) => { const wallets = [new PetraWallet()]; return ( @@ -12,7 +10,3 @@ export const WalletProvider = ({ children }: { children: ReactNode }) => { ); }; - -export const useAptosWallet = () => { - return useContext(WalletContext); -}; diff --git a/packages/nextjs/utils/scaffold-eth/networks.ts b/packages/nextjs/utils/scaffold-eth/networks.ts index 6b8276f..0a658a2 100644 --- a/packages/nextjs/utils/scaffold-eth/networks.ts +++ b/packages/nextjs/utils/scaffold-eth/networks.ts @@ -113,16 +113,16 @@ export function getBlockExplorerTxLink(chainId: number, txnHash: string) { * Defaults to Etherscan if no (wagmi) block explorer is configured for the network. */ export function getBlockExplorerAddressLink(network: chains.Chain, address: string) { - const blockExplorerBaseURL = network.blockExplorers?.default?.url; - if (network.id === chains.hardhat.id) { - return `/blockexplorer/address/${address}`; - } + // const blockExplorerBaseURL = network.blockExplorers?.default?.url; + // if (network.id === chains.hardhat.id) { + // return `/blockexplorer/address/${address}`; + // } - if (!blockExplorerBaseURL) { - return `https://etherscan.io/address/${address}`; - } + // if (!blockExplorerBaseURL) { + return `https://explorer.devnet.m1.movementlabs.xyz/account/${address}?network=devnet`; + // } - return `${blockExplorerBaseURL}/address/${address}`; + // return `${blockExplorerBaseURL}/address/${address}`; } /** From 623f04a2a3346b9a0b38695b69249ca5b54c7cda Mon Sep 17 00:00:00 2001 From: arjanjohan Date: Tue, 9 Jul 2024 08:41:36 +0200 Subject: [PATCH 4/4] minor fix --- .../scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx index f9ddc7d..4f74b5d 100644 --- a/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx +++ b/packages/nextjs/components/scaffold-move/CustomConnectButton/AddressInfoDropdown.tsx @@ -108,7 +108,7 @@ export const AddressInfoDropdown = ({
  • - {/* {allowedNetworks.length > 1 ? ( */} + {allowedNetworks.length > 1 ? (
  • - {/* ) : null} */} + ) : null}