|
|
@@ -1,21 +1,18 @@
|
|
|
import { WalletName, WalletReadyState } from '@solana/wallet-adapter-base';
|
|
|
import { useWallet, Wallet } from '@solana/wallet-adapter-react';
|
|
|
-import React, { FC, MouseEvent, ReactNode, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
|
+import React, { FC, MouseEvent, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
|
import { createPortal } from 'react-dom';
|
|
|
-import { WalletUIAlt } from './WalletUIAlt';
|
|
|
-import { Button } from './Button';
|
|
|
import { Collapse } from './Collapse';
|
|
|
import { useWalletModal } from './useWalletModal';
|
|
|
import { WalletListItem } from './WalletListItem';
|
|
|
-import { WalletUIMain } from './WalletUIMain';
|
|
|
+import { WalletSVG } from './WalletSVG';
|
|
|
|
|
|
export interface WalletModalProps {
|
|
|
className?: string;
|
|
|
- featuredWallets?: number;
|
|
|
container?: string;
|
|
|
}
|
|
|
|
|
|
-export const WalletModal: FC<WalletModalProps> = ({ className = '', featuredWallets = 3, container = 'body' }) => {
|
|
|
+export const WalletModal: FC<WalletModalProps> = ({ className = '', container = 'body' }) => {
|
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
const { wallets, select } = useWallet();
|
|
|
const { setVisible } = useWalletModal();
|
|
|
@@ -23,52 +20,33 @@ export const WalletModal: FC<WalletModalProps> = ({ className = '', featuredWall
|
|
|
const [fadeIn, setFadeIn] = useState(false);
|
|
|
const [portal, setPortal] = useState<Element | null>(null);
|
|
|
|
|
|
- const [featured, more] = useMemo(() => {
|
|
|
- const installedWallets: Wallet[] = [];
|
|
|
- const undetectedWallets: Wallet[] = [];
|
|
|
- const loadableWallets: Wallet[] = [];
|
|
|
- wallets.forEach((wallet: Wallet) => {
|
|
|
- if (wallet.readyState === WalletReadyState.Installed) {
|
|
|
- installedWallets.push(wallet);
|
|
|
- } else if (wallet.readyState === WalletReadyState.NotDetected) {
|
|
|
- undetectedWallets.push(wallet);
|
|
|
+ const [installedWallets, otherWallets] = useMemo(() => {
|
|
|
+ const installed: Wallet[] = [];
|
|
|
+ const notDetected: Wallet[] = [];
|
|
|
+ const loadable: Wallet[] = [];
|
|
|
+
|
|
|
+ for (const wallet of wallets) {
|
|
|
+ if (wallet.readyState === WalletReadyState.NotDetected) {
|
|
|
+ notDetected.push(wallet);
|
|
|
} else if (wallet.readyState === WalletReadyState.Loadable) {
|
|
|
- loadableWallets.push(wallet);
|
|
|
+ loadable.push(wallet);
|
|
|
+ } else if (wallet.readyState === WalletReadyState.Installed) {
|
|
|
+ installed.push(wallet);
|
|
|
}
|
|
|
- });
|
|
|
- const installableWallets = installedWallets.concat(undetectedWallets);
|
|
|
- const remainingFeaturedSpots = Math.max(
|
|
|
- 0,
|
|
|
- featuredWallets - (installableWallets.length && 1) - (loadableWallets.length && 1)
|
|
|
- );
|
|
|
- const otherWallets = installableWallets.slice(1).concat(loadableWallets.slice(1));
|
|
|
- return [
|
|
|
- [
|
|
|
- ...installableWallets.slice(0, 1),
|
|
|
- ...loadableWallets.slice(0, 1),
|
|
|
- ...otherWallets.slice(0, remainingFeaturedSpots),
|
|
|
- ],
|
|
|
- otherWallets.slice(remainingFeaturedSpots),
|
|
|
- ];
|
|
|
- }, [wallets, featuredWallets]);
|
|
|
+ }
|
|
|
|
|
|
- const getStartedWallet = useMemo(() => {
|
|
|
- const torusWallet = wallets.find(
|
|
|
- (wallet: { adapter: { name: WalletName } }) => wallet.adapter.name === 'Torus'
|
|
|
- );
|
|
|
- if (torusWallet) return torusWallet.adapter.name;
|
|
|
-
|
|
|
- const loadable = wallets.filter(
|
|
|
- (wallet: { readyState: any }) => wallet.readyState === WalletReadyState.Loadable
|
|
|
- );
|
|
|
- return loadable[0]?.adapter.name || featured[0]?.adapter.name;
|
|
|
- }, [wallets, featured]);
|
|
|
-
|
|
|
- const installedWalletDetected = useMemo(() => {
|
|
|
- if (wallets.some((wallet) => wallet.readyState === WalletReadyState.Installed)) return true;
|
|
|
- return false;
|
|
|
+ return [installed, [...loadable, ...notDetected]];
|
|
|
}, [wallets]);
|
|
|
|
|
|
+ const getStartedWallet = useMemo(() => {
|
|
|
+ return installedWallets.length
|
|
|
+ ? installedWallets[0]
|
|
|
+ : wallets.find((wallet: { adapter: { name: WalletName } }) => wallet.adapter.name === 'Torus') ||
|
|
|
+ wallets.find((wallet: { adapter: { name: WalletName } }) => wallet.adapter.name === 'Phantom') ||
|
|
|
+ wallets.find((wallet: { readyState: any }) => wallet.readyState === WalletReadyState.Loadable) ||
|
|
|
+ otherWallets[0];
|
|
|
+ }, [installedWallets, wallets, otherWallets]);
|
|
|
+
|
|
|
const hideModal = useCallback(() => {
|
|
|
setFadeIn(false);
|
|
|
setTimeout(() => setVisible(false), 150);
|
|
|
@@ -90,9 +68,7 @@ export const WalletModal: FC<WalletModalProps> = ({ className = '', featuredWall
|
|
|
[select, handleClose]
|
|
|
);
|
|
|
|
|
|
- const handleCollapseClick = useCallback(() => {
|
|
|
- setExpanded(!expanded);
|
|
|
- }, [expanded]);
|
|
|
+ const handleCollapseClick = useCallback(() => setExpanded(!expanded), [expanded]);
|
|
|
|
|
|
const handleTabKey = useCallback(
|
|
|
(event: KeyboardEvent) => {
|
|
|
@@ -121,33 +97,6 @@ export const WalletModal: FC<WalletModalProps> = ({ className = '', featuredWall
|
|
|
[ref]
|
|
|
);
|
|
|
|
|
|
- const uiProps = useMemo(
|
|
|
- () => ({
|
|
|
- className,
|
|
|
- fadeIn,
|
|
|
- wallets,
|
|
|
- featured,
|
|
|
- getStartedWallet,
|
|
|
- more,
|
|
|
- expanded,
|
|
|
- handleClose,
|
|
|
- handleWalletClick,
|
|
|
- handleCollapseClick,
|
|
|
- }),
|
|
|
- [
|
|
|
- className,
|
|
|
- fadeIn,
|
|
|
- wallets,
|
|
|
- featured,
|
|
|
- getStartedWallet,
|
|
|
- more,
|
|
|
- expanded,
|
|
|
- handleClose,
|
|
|
- handleWalletClick,
|
|
|
- handleCollapseClick,
|
|
|
- ]
|
|
|
- );
|
|
|
-
|
|
|
useLayoutEffect(() => {
|
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
|
if (event.key === 'Escape') {
|
|
|
@@ -173,12 +122,136 @@ export const WalletModal: FC<WalletModalProps> = ({ className = '', featuredWall
|
|
|
};
|
|
|
}, [hideModal, handleTabKey]);
|
|
|
|
|
|
- useLayoutEffect(() => {
|
|
|
- setPortal(document.querySelector(container));
|
|
|
- }, [container]);
|
|
|
+ useLayoutEffect(() => setPortal(document.querySelector(container)), [container]);
|
|
|
|
|
|
return (
|
|
|
portal &&
|
|
|
- createPortal(installedWalletDetected ? <WalletUIMain {...uiProps} /> : <WalletUIAlt {...uiProps} />, portal)
|
|
|
+ createPortal(
|
|
|
+ <div
|
|
|
+ aria-labelledby="wallet-adapter-modal-title"
|
|
|
+ aria-modal="true"
|
|
|
+ className={`wallet-adapter-modal ${fadeIn && 'wallet-adapter-modal-fade-in'} ${className}`}
|
|
|
+ ref={ref}
|
|
|
+ role="dialog"
|
|
|
+ >
|
|
|
+ <div className="wallet-adapter-modal-container">
|
|
|
+ <div className="wallet-adapter-modal-wrapper">
|
|
|
+ <button onClick={handleClose} className="wallet-adapter-modal-button-close">
|
|
|
+ <svg width="14" height="14">
|
|
|
+ <path d="M14 12.461 8.3 6.772l5.234-5.233L12.006 0 6.772 5.234 1.54 0 0 1.539l5.234 5.233L0 12.006l1.539 1.528L6.772 8.3l5.69 5.7L14 12.461z" />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ {installedWallets.length ? (
|
|
|
+ <>
|
|
|
+ <h1 className="wallet-adapter-modal-title">Connect a wallet on Solana to continue</h1>
|
|
|
+ <ul className="wallet-adapter-modal-list">
|
|
|
+ {installedWallets.map((wallet) => (
|
|
|
+ <WalletListItem
|
|
|
+ key={wallet.adapter.name}
|
|
|
+ handleClick={(event) => handleWalletClick(event, wallet.adapter.name)}
|
|
|
+ wallet={wallet}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ {otherWallets.length ? (
|
|
|
+ <Collapse expanded={expanded} id="wallet-adapter-modal-collapse">
|
|
|
+ {otherWallets.map((wallet) => (
|
|
|
+ <WalletListItem
|
|
|
+ key={wallet.adapter.name}
|
|
|
+ handleClick={(event) =>
|
|
|
+ handleWalletClick(event, wallet.adapter.name)
|
|
|
+ }
|
|
|
+ tabIndex={expanded ? 0 : -1}
|
|
|
+ wallet={wallet}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ </Collapse>
|
|
|
+ ) : null}
|
|
|
+ </ul>
|
|
|
+ {otherWallets.length ? (
|
|
|
+ <button
|
|
|
+ className="wallet-adapter-modal-list-more"
|
|
|
+ onClick={handleCollapseClick}
|
|
|
+ tabIndex={0}
|
|
|
+ >
|
|
|
+ <span>{expanded ? 'Less ' : 'More '}options</span>
|
|
|
+ <svg
|
|
|
+ width="13"
|
|
|
+ height="7"
|
|
|
+ viewBox="0 0 13 7"
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
+ className={`${
|
|
|
+ expanded ? 'wallet-adapter-modal-list-more-icon-rotate' : ''
|
|
|
+ }`}
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ d="M0.71418 1.626L5.83323 6.26188C5.91574 6.33657 6.0181 6.39652 6.13327 6.43762C6.24844 6.47872 6.37371 6.5 6.50048 6.5C6.62725 6.5 6.75252 6.47872 6.8677 6.43762C6.98287 6.39652 7.08523 6.33657 7.16774 6.26188L12.2868 1.626C12.7753 1.1835 12.3703 0.5 11.6195 0.5H1.37997C0.629216 0.5 0.224175 1.1835 0.71418 1.626Z"
|
|
|
+ fill="wallet-adapter-modal-button-icon-fill"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ ) : null}
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
+ <h1 className="wallet-adapter-modal-title">
|
|
|
+ You'll need a wallet on Solana to continue
|
|
|
+ </h1>
|
|
|
+ <div className="wallet-adapter-modal-middle">
|
|
|
+ <WalletSVG />
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ className="wallet-adapter-modal-middle-button"
|
|
|
+ onClick={(event) => handleWalletClick(event, getStartedWallet.adapter.name)}
|
|
|
+ >
|
|
|
+ Get started
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ {otherWallets.length ? (
|
|
|
+ <>
|
|
|
+ <h2 className="wallet-adapter-modal-list-header">
|
|
|
+ Already have a wallet?
|
|
|
+ <button
|
|
|
+ className="wallet-adapter-modal-list-more"
|
|
|
+ onClick={handleCollapseClick}
|
|
|
+ tabIndex={0}
|
|
|
+ >
|
|
|
+ <span>{expanded ? 'Hide ' : 'View '}options</span>
|
|
|
+ <svg
|
|
|
+ width="13"
|
|
|
+ height="7"
|
|
|
+ viewBox="0 0 13 7"
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
+ className={`${
|
|
|
+ expanded ? 'wallet-adapter-modal-list-more-icon-rotate' : ''
|
|
|
+ }`}
|
|
|
+ >
|
|
|
+ <path d="M0.71418 1.626L5.83323 6.26188C5.91574 6.33657 6.0181 6.39652 6.13327 6.43762C6.24844 6.47872 6.37371 6.5 6.50048 6.5C6.62725 6.5 6.75252 6.47872 6.8677 6.43762C6.98287 6.39652 7.08523 6.33657 7.16774 6.26188L12.2868 1.626C12.7753 1.1835 12.3703 0.5 11.6195 0.5H1.37997C0.629216 0.5 0.224175 1.1835 0.71418 1.626Z" />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </h2>
|
|
|
+ <Collapse expanded={expanded} id="wallet-adapter-modal-collapse">
|
|
|
+ <ul className="wallet-adapter-modal-list wallet-modal-mt-24">
|
|
|
+ {otherWallets.map((wallet) => (
|
|
|
+ <WalletListItem
|
|
|
+ key={wallet.adapter.name}
|
|
|
+ handleClick={(event) =>
|
|
|
+ handleWalletClick(event, wallet.adapter.name)
|
|
|
+ }
|
|
|
+ tabIndex={expanded ? 0 : -1}
|
|
|
+ wallet={wallet}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ </ul>
|
|
|
+ </Collapse>
|
|
|
+ </>
|
|
|
+ ) : null}
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="wallet-adapter-modal-overlay" onMouseDown={handleClose} />
|
|
|
+ </div>,
|
|
|
+ portal
|
|
|
+ )
|
|
|
);
|
|
|
};
|