Skip to content

Commit

Permalink
Merge pull request #4 from arjanjohan/wallet-dropdown
Browse files Browse the repository at this point in the history
Wallet dropdown
  • Loading branch information
arjanjohan authored Jul 9, 2024
2 parents 26cec8e + 623f04a commit aaa7f79
Show file tree
Hide file tree
Showing 13 changed files with 347 additions and 25 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions packages/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
Expand All @@ -19,7 +21,8 @@ const Home: NextPage = () => {
</h1>
<div className="flex justify-center items-center space-x-2 flex-col sm:flex-row">
<p className="my-2 font-medium">Connected Address:</p>
<Address address={connectedAddress} />

<Address address={connectedAccount?.address} />
</div>
<p className="text-center text-lg">
Get started by editing{" "}
Expand Down
6 changes: 3 additions & 3 deletions packages/nextjs/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ 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;
Expand Down Expand Up @@ -107,9 +109,7 @@ export const Header = () => {
</ul>
</div>
<div className="navbar-end flex-grow mr-4">
<div className="flex flex-col items-center mr-1 btn btn-primary">
<WalletSelector />
</div>
<CustomConnectButton />
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { useRef, useState } from "react";
import { NetworkOptions } from "./NetworkOptions";
import CopyToClipboard from "react-copy-to-clipboard";
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";
import {
useWallet
} from "@aptos-labs/wallet-adapter-react";

const allowedNetworks = getTargetNetworks();

type AddressInfoDropdownProps = {
address: string;
blockExplorerAddressLink: string | undefined;
displayName: string;
ensAvatar?: string;
};

export const AddressInfoDropdown = ({
address,
ensAvatar,
displayName,
blockExplorerAddressLink,
}: AddressInfoDropdownProps) => {

const [addressCopied, setAddressCopied] = useState(false);

const [selectingNetwork, setSelectingNetwork] = useState(false);
const dropdownRef = useRef<HTMLDetailsElement>(null);
const closeDropdown = () => {
setSelectingNetwork(false);
dropdownRef.current?.removeAttribute("open");
};
useOutsideClick(dropdownRef, closeDropdown);


const { disconnect, wallet } = useWallet();
return (
<>
<details ref={dropdownRef} className="dropdown dropdown-end leading-3">
<summary tabIndex={0} className="btn btn-secondary btn-sm pl-0 pr-2 shadow-md dropdown-toggle gap-0 !h-auto">
<BlockieAvatar address={address} size={30} ensImage={ensAvatar} />
<span className="ml-2 mr-1">
{isENS(displayName) ? displayName : address?.slice(0, 6) + "..." + address?.slice(-4)}
</span>
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
</summary>
<ul
tabIndex={0}
className="dropdown-content menu z-[2] p-2 mt-2 shadow-center shadow-accent bg-base-200 rounded-box gap-1"
>
<NetworkOptions hidden={!selectingNetwork} />
<li className={selectingNetwork ? "hidden" : ""}>
{addressCopied ? (
<div className="btn-sm !rounded-xl flex gap-3 py-3">
<CheckCircleIcon
className="text-xl font-normal h-6 w-4 cursor-pointer ml-2 sm:ml-0"
aria-hidden="true"
/>
<span className=" whitespace-nowrap">Copy address</span>
</div>
) : (
<CopyToClipboard
text={address}
onCopy={() => {
setAddressCopied(true);
setTimeout(() => {
setAddressCopied(false);
}, 800);
}}
>
<div className="btn-sm !rounded-xl flex gap-3 py-3">
<DocumentDuplicateIcon
className="text-xl font-normal h-6 w-4 cursor-pointer ml-2 sm:ml-0"
aria-hidden="true"
/>
<span className=" whitespace-nowrap">Copy address</span>
</div>
</CopyToClipboard>
)}
</li>
<li className={selectingNetwork ? "hidden" : ""}>
<label htmlFor="qrcode-modal" className="btn-sm !rounded-xl flex gap-3 py-3">
<QrCodeIcon className="h-6 w-4 ml-2 sm:ml-0" />
<span className="whitespace-nowrap">View QR Code</span>
</label>
</li>
<li className={selectingNetwork ? "hidden" : ""}>
<button className="menu-item btn-sm !rounded-xl flex gap-3 py-3" type="button">
<ArrowTopRightOnSquareIcon className="h-6 w-4 ml-2 sm:ml-0" />
<a
target="_blank"
href={blockExplorerAddressLink}
rel="noopener noreferrer"
className="whitespace-nowrap"
>
View on Block Explorer
</a>
</button>
</li>
{allowedNetworks.length > 1 ? (
<li className={selectingNetwork ? "hidden" : ""}>
<button
className="btn-sm !rounded-xl flex gap-3 py-3"
type="button"
onClick={() => {
setSelectingNetwork(true);
}}
>
<ArrowsRightLeftIcon className="h-6 w-4 ml-2 sm:ml-0" /> <span>Switch Network</span>
</button>
</li>
) : null}
<li className={selectingNetwork ? "hidden" : ""}>
<button
className="menu-item text-error btn-sm !rounded-xl flex gap-3 py-3"
type="button"
onClick={() => disconnect()}
>
<ArrowLeftOnRectangleIcon className="h-6 w-4 ml-2 sm:ml-0" /> <span>Disconnect</span>
</button>
</li>
</ul>
</details>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { QRCodeSVG } from "qrcode.react";
import { Address } from "~~/components/scaffold-move";

type AddressQRCodeModalProps = {
address: string;
modalId: string;
};

export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps) => {
return (
<>
<div>
<input type="checkbox" id={`${modalId}`} className="modal-toggle" />
<label htmlFor={`${modalId}`} className="modal cursor-pointer">
<label className="modal-box relative">
{/* dummy input to capture event onclick on modal box */}
<input className="h-0 w-0 absolute top-0 left-0" />
<label htmlFor={`${modalId}`} className="btn btn-ghost btn-sm btn-circle absolute right-3 top-3">
</label>
<div className="space-y-3 py-6">
<div className="flex flex-col items-center gap-6">
<QRCodeSVG value={address} size={256} />
<Address address={address} format="long" disableAddressLink />
</div>
</div>
</label>
</label>
</div>
</>
);
};
Original file line number Diff line number Diff line change
@@ -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 => (
<li key={allowedNetwork.id} className={hidden ? "hidden" : ""}>
<button
className="menu-item btn-sm !rounded-xl flex gap-3 py-3 whitespace-nowrap"
type="button"
onClick={() => {
switchChain?.({ chainId: allowedNetwork.id });
}}
>
<ArrowsRightLeftIcon className="h-6 w-4 ml-2 sm:ml-0" />
<span>
Switch to{" "}
<span>
{allowedNetwork.name}
</span>
</span>
</button>
</li>
))}
</>
);
};
Original file line number Diff line number Diff line change
@@ -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 (
<div className="dropdown dropdown-end mr-2">
<label tabIndex={0} className="btn btn-error btn-sm dropdown-toggle gap-1">
<span>Wrong network</span>
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
</label>
<ul
tabIndex={0}
className="dropdown-content menu p-2 mt-1 shadow-center shadow-accent bg-base-200 rounded-box gap-1"
>
<NetworkOptions />
<li>
<button
className="menu-item text-error btn-sm !rounded-xl flex gap-3 py-3"
type="button"
onClick={() => disconnect()}
>
<ArrowLeftOnRectangleIcon className="h-6 w-4 ml-2 sm:ml-0" />
<span>Disconnect</span>
</button>
</li>
</ul>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"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";
import { WalletSelector } from "@aptos-labs/wallet-adapter-ant-design";


import {
useWallet
} from "@aptos-labs/wallet-adapter-react";

export const CustomConnectButton = () => {
const { targetNetwork } = useTargetNetwork();
// const [account, setAccount] = useState(null);
const chain = {name: "devnet"}; // TODO: replace


const { account, connected} = useWallet();


const blockExplorerAddressLink = account ? getBlockExplorerAddressLink(targetNetwork, account?.address) : undefined;

return (
<>
{!connected ? (
<div className="flex flex-col items-center mr-1 btn btn-primary">
<WalletSelector />
</div>
)
// : chainId !== targetNetwork.id ? (
// <WrongNetworkDropdown />
// )
: (
<>
<div className="flex flex-col items-center mr-1">
{/* <Balance address={account} className="min-h-0 h-auto" /> */}
<span className="text-xs">{chain ? chain.name : "Loading..."}</span>
</div>
<AddressInfoDropdown
address={account?.address || ""}
displayName={account?.address || ""}
ensAvatar={""} // Update this with ENS Avatar if available
blockExplorerAddressLink={blockExplorerAddressLink}
/>
</>
)}
</>
);
};
6 changes: 0 additions & 6 deletions packages/nextjs/components/scaffold-move/WalletContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -12,7 +10,3 @@ export const WalletProvider = ({ children }: { children: ReactNode }) => {
</AptosWalletAdapterProvider>
);
};

export const useAptosWallet = () => {
return useContext(WalletContext);
};
3 changes: 2 additions & 1 deletion packages/nextjs/components/scaffold-move/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./Address";
export * from "./Balance";
export * from "./Balance";
export * from "./CustomConnectButton";
3 changes: 2 additions & 1 deletion packages/nextjs/hooks/scaffold-move/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./useDeployedContractInfo";
export * from "./useGetAccountAPTBalance";
export * from "./useGetAccountResources";
export * from "./useGetAccountResources";
export * from "./useOutsideClick";
Loading

0 comments on commit aaa7f79

Please sign in to comment.