Skip to content

Commit 6ffc779

Browse files
committed
- Adds metamask button
- Adds chainnamespace filters and select chainnamespace screen. - Fixes multiple re render.
1 parent 5098f84 commit 6ffc779

File tree

17 files changed

+227
-101
lines changed

17 files changed

+227
-101
lines changed

packages/modal/src/ui/components/ConnectWallet/ConnectWallet.tsx

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
import { WALLET_CONNECTORS } from "@web3auth/no-modal";
1+
import { ChainNamespaceType, WALLET_CONNECTORS } from "@web3auth/no-modal";
22
import { FormEvent, useContext, useEffect, useMemo, useState } from "react";
33

44
import { CONNECT_WALLET_PAGES } from "../../constants";
55
import { RootContext } from "../../context/RootContext";
66
import { ExternalButton } from "../../interfaces";
77
import { ConnectWalletProps } from "./ConnectWallet.type";
8+
import ConnectWalletChainFilter from "./ConnectWalletChainFilter";
89
import ConnectWalletHeader from "./ConnectWalletHeader";
910
import ConnectWalletList from "./ConnectWalletList";
1011
import ConnectWalletQrCode from "./ConnectWalletQrCode";
1112
import ConnectWalletSearch from "./ConnectWalletSearch";
1213

14+
const WALLET_LIMIT_COUNT = 10;
15+
1316
function ConnectWallet(props: ConnectWalletProps) {
1417
const {
1518
isDark,
@@ -24,6 +27,7 @@ function ConnectWallet(props: ConnectWalletProps) {
2427
adapterVisibilityMap,
2528
deviceDetails,
2629
handleWalletDetailsHeight,
30+
chainNamespace,
2731
} = props;
2832

2933
const { bodyState, setBodyState } = useContext(RootContext);
@@ -35,7 +39,7 @@ function ConnectWallet(props: ConnectWalletProps) {
3539
const [selectedButton, setSelectedButton] = useState<ExternalButton>(null);
3640
const [walletSearch, setWalletSearch] = useState<string>("");
3741
const [isLoading, setIsLoading] = useState<boolean>(true);
38-
// const [selectedChain, setSelectedChain] = useState<string>("all");
42+
const [selectedChain, setSelectedChain] = useState<string>("all");
3943
const [initialWalletCount, setInitialWalletCount] = useState<number>(0);
4044

4145
const handleBack = () => {
@@ -111,13 +115,14 @@ function ConnectWallet(props: ConnectWalletProps) {
111115
}, [walletDiscoverySupported, sortedButtons, visibleButtons, totalExternalWallets]);
112116

113117
const handleWalletClick = (button: ExternalButton) => {
114-
// if has injected wallet, connect to injected wallet
115-
// if doesn't have wallet connect & doesn't have install links, must be a custom adapter
116-
// TODO: handle multiple chain namespaces with ui
117-
// TODO: fix more wallets not shown in UI + scroll issue
118-
if (button.hasInjectedWallet || (!button.hasWalletConnect && !button.hasInstallLinks)) {
119-
handleExternalWalletClick({ connector: button.name });
120-
} else if (button.hasWalletConnect) {
118+
const isInjectedConnectorAndSingleChainNamespace = button.hasInjectedWallet && button.chainNamespaces?.length === 1;
119+
// if doesn't have wallet connect & doesn't have install links, must be a custom connector
120+
const isCustomConnector = !button.hasInjectedWallet && !button.hasWalletConnect && !button.hasInstallLinks;
121+
if (isInjectedConnectorAndSingleChainNamespace || isCustomConnector) {
122+
return handleExternalWalletClick({ connector: button.name });
123+
}
124+
125+
if (button.hasWalletConnect || !isInjectedConnectorAndSingleChainNamespace) {
121126
setSelectedButton(button);
122127
setSelectedWallet(true);
123128
setCurrentPage(CONNECT_WALLET_PAGES.SELECTED_WALLET);
@@ -132,11 +137,23 @@ function ConnectWallet(props: ConnectWalletProps) {
132137
};
133138

134139
const handleMoreWallets = () => {
135-
// setIsLoading(true);
136-
setInitialWalletCount((prev) => prev + 10);
137140
const allButtons = [...allExternalButtons, ...customAdapterButtons];
138-
const buttons = allButtons.slice(initialWalletCount, initialWalletCount + 10);
141+
const buttons = allButtons.slice(initialWalletCount, initialWalletCount + WALLET_LIMIT_COUNT);
139142
setExternalButtons((prev) => [...prev, ...buttons]);
143+
setInitialWalletCount((prev) => prev + WALLET_LIMIT_COUNT);
144+
};
145+
146+
const handleChainFilterChange = (chain: string) => {
147+
setInitialWalletCount(0);
148+
setSelectedChain(chain);
149+
if (chain === "all") {
150+
setExternalButtons(sortedButtons.slice(0, WALLET_LIMIT_COUNT));
151+
setTotalExternalWalletsCount(sortedButtons.length);
152+
} else {
153+
const filteredButtons = sortedButtons.filter((button) => button.chainNamespaces.includes(chain as ChainNamespaceType));
154+
setExternalButtons(filteredButtons.slice(0, WALLET_LIMIT_COUNT));
155+
setTotalExternalWalletsCount(filteredButtons.length);
156+
}
140157
};
141158

142159
return (
@@ -149,22 +166,22 @@ function ConnectWallet(props: ConnectWalletProps) {
149166
walletConnectUri={walletConnectUri}
150167
isDark={isDark}
151168
selectedButton={selectedButton}
152-
setBodyState={setBodyState}
153169
bodyState={bodyState}
170+
setBodyState={setBodyState}
171+
handleExternalWalletClick={handleExternalWalletClick}
154172
/>
155173
) : (
156174
<div className="w3a--flex w3a--flex-col w3a--gap-y-2">
157-
{/* TODO: To be implemented */}
158-
{/* <ConnectWalletChainFilter
175+
<ConnectWalletChainFilter
159176
isDark={isDark}
160177
isLoading={isLoading}
161178
selectedChain={selectedChain}
162-
setSelectedChain={setSelectedChain}
163-
chains={CHAIN_LIST}
164-
/> */}
179+
setSelectedChain={handleChainFilterChange}
180+
chainNamespace={chainNamespace}
181+
/>
165182
{/* Search Input */}
166183
<ConnectWalletSearch
167-
totalExternalWallets={totalExternalWalletsCount}
184+
totalExternalWalletCount={totalExternalWalletsCount}
168185
isLoading={isLoading}
169186
walletSearch={walletSearch}
170187
handleWalletSearch={handleWalletSearch}
@@ -173,7 +190,7 @@ function ConnectWallet(props: ConnectWalletProps) {
173190
<ConnectWalletList
174191
externalButtons={externalButtons}
175192
isLoading={isLoading}
176-
totalExternalWallets={totalExternalWalletsCount}
193+
totalExternalWalletsCount={totalExternalWalletsCount}
177194
initialWalletCount={initialWalletCount}
178195
handleWalletClick={handleWalletClick}
179196
handleMoreWallets={handleMoreWallets}

packages/modal/src/ui/components/ConnectWallet/ConnectWallet.type.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { BaseConnectorConfig, WalletRegistry } from "@web3auth/no-modal";
1+
import type { BaseConnectorConfig, ChainNamespaceType, WalletRegistry } from "@web3auth/no-modal";
22

33
import type { browser, ExternalButton, os, platform } from "../../interfaces";
44

@@ -12,7 +12,8 @@ export interface ConnectWalletProps {
1212
customAdapterButtons: ExternalButton[];
1313
adapterVisibilityMap: Record<string, boolean>;
1414
deviceDetails: { platform: platform; browser: browser; os: os };
15+
chainNamespace: ChainNamespaceType[];
1516
onBackClick?: (flag: boolean) => void;
16-
handleExternalWalletClick: (params: { connector: string }) => void;
17+
handleExternalWalletClick: (params: { connector: string; chainNamespace?: ChainNamespaceType }) => void;
1718
handleWalletDetailsHeight: () => void;
1819
}

packages/modal/src/ui/components/ConnectWallet/ConnectWalletChainFilter/ConnectWalletChainFilter.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
1-
import { useId } from "react";
1+
import { useMemo } from "react";
22

33
import { cn, getIcons } from "../../../utils";
44
import { ConnectWalletChainFilterProps } from "./ConnectWalletChainFilter.type";
55

66
function ConnectWalletChainFilter(props: ConnectWalletChainFilterProps) {
7-
const { isDark, isLoading, selectedChain, setSelectedChain, chains } = props;
8-
const uuid = useId();
7+
const { isDark, isLoading, selectedChain, setSelectedChain, chainNamespace } = props;
8+
9+
const chains = useMemo(() => {
10+
const chains = [{ id: "all", name: "All Chains", icon: "" }];
11+
for (const chain of chainNamespace) {
12+
chains.push({
13+
id: chain,
14+
name: chain === "eip155" ? "Ethereum" : chain,
15+
icon: chain === "eip155" ? "ethereum" : chain,
16+
});
17+
}
18+
return chains;
19+
}, [chainNamespace]);
920

1021
if (isLoading) {
1122
return (
1223
<div className="w3a--flex w3a--items-center w3a--justify-between w3a--gap-x-2">
13-
{Array.from({ length: chains.length }).map(() => (
14-
<div key={uuid} className="w3a--h-12 w3a--w-[100px] w3a--animate-pulse w3a--rounded-2xl w3a--bg-app-gray-200 dark:w3a--bg-app-gray-700" />
24+
{Array.from({ length: chains.length }).map((_, index) => (
25+
<div
26+
key={`chain-loader-${index}`}
27+
className="w3a--h-12 w3a--w-[100px] w3a--animate-pulse w3a--rounded-2xl w3a--bg-app-gray-200 dark:w3a--bg-app-gray-700"
28+
/>
1529
))}
1630
</div>
1731
);
@@ -31,6 +45,7 @@ function ConnectWalletChainFilter(props: ConnectWalletChainFilterProps) {
3145
)}
3246
onClick={() => setSelectedChain(chain.id)}
3347
>
48+
{chain.id === "all" && chain.name}
3449
{chain.icon && <img src={getIcons(isDark ? `${chain.icon}-dark` : `${chain.icon}-light`)} alt={chain.name} />}
3550
</button>
3651
))}

packages/modal/src/ui/components/ConnectWallet/ConnectWalletChainFilter/ConnectWalletChainFilter.type.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { ChainNamespaceType } from "@web3auth/no-modal";
2+
13
export interface Chain {
24
id: string;
35
name: string;
@@ -8,6 +10,6 @@ export interface ConnectWalletChainFilterProps {
810
isDark: boolean;
911
isLoading: boolean;
1012
selectedChain: string;
13+
chainNamespace: ChainNamespaceType[];
1114
setSelectedChain: (chain: string) => void;
12-
chains: Chain[];
1315
}

packages/modal/src/ui/components/ConnectWallet/ConnectWalletList/ConnectWalletList.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ function WalletsFound(props: WalletsFoundProps) {
5151
}
5252

5353
function MoreWalletsButton(props: MoreWalletsButtonProps) {
54-
const { totalExternalWallets, initialWalletCount, handleMoreWallets, isLoading, isDark } = props;
54+
const { totalExternalWalletsCount, initialWalletCount, handleMoreWallets, isLoading, isDark } = props;
5555

5656
const onMoreWalletsClick = () => {
5757
if (handleMoreWallets) {
5858
handleMoreWallets();
5959
}
6060
};
6161

62-
if (isLoading && initialWalletCount < totalExternalWallets) {
62+
if (isLoading && initialWalletCount < totalExternalWalletsCount) {
6363
return <div className="w3a--h-12 w3a--w-full w3a--animate-pulse w3a--rounded-full w3a--bg-app-gray-200 dark:w3a--bg-app-gray-700" />;
6464
}
6565

@@ -75,7 +75,7 @@ function MoreWalletsButton(props: MoreWalletsButtonProps) {
7575
className="w3a--inline-flex w3a--items-center w3a--rounded-full w3a--bg-app-primary-100 w3a--px-2 w3a--py-1 w3a--text-xs w3a--font-medium w3a--text-app-primary-800
7676
dark:w3a--border dark:w3a--border-app-primary-400 dark:w3a--bg-transparent dark:w3a--text-app-primary-400"
7777
>
78-
{totalExternalWallets - initialWalletCount}
78+
{totalExternalWalletsCount - initialWalletCount}
7979
</span>
8080
</button>
8181
);
@@ -85,7 +85,7 @@ function ConnectWalletList(props: ConnectWalletListProps) {
8585
const {
8686
externalButtons,
8787
isLoading,
88-
totalExternalWallets,
88+
totalExternalWalletsCount,
8989
initialWalletCount,
9090
handleWalletClick,
9191
handleMoreWallets,
@@ -109,9 +109,9 @@ function ConnectWalletList(props: ConnectWalletListProps) {
109109
/>
110110
)}
111111
</ul>
112-
{totalExternalWallets > 15 && !isLoading && initialWalletCount < totalExternalWallets && (
112+
{totalExternalWalletsCount > 15 && !isLoading && initialWalletCount < totalExternalWalletsCount && (
113113
<MoreWalletsButton
114-
totalExternalWallets={totalExternalWallets}
114+
totalExternalWalletsCount={totalExternalWalletsCount}
115115
initialWalletCount={initialWalletCount}
116116
handleMoreWallets={handleMoreWallets}
117117
isLoading={isLoading}

packages/modal/src/ui/components/ConnectWallet/ConnectWalletList/ConnectWalletList.type.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { browser, ExternalButton, os, platform } from "../../../interfaces";
33
export interface ConnectWalletListProps {
44
externalButtons: ExternalButton[];
55
isLoading: boolean;
6-
totalExternalWallets: number;
6+
totalExternalWalletsCount: number;
77
initialWalletCount: number;
8-
handleWalletClick: (button: ExternalButton) => void;
9-
handleMoreWallets: () => void;
108
isDark: boolean;
119
deviceDetails: { platform: platform; browser: browser; os: os };
1210
walletConnectUri: string;
11+
handleWalletClick: (button: ExternalButton) => void;
12+
handleMoreWallets: () => void;
1313
}
1414

1515
export type WalletsFoundProps = Pick<
@@ -19,5 +19,5 @@ export type WalletsFoundProps = Pick<
1919

2020
export type MoreWalletsButtonProps = Pick<
2121
ConnectWalletListProps,
22-
"totalExternalWallets" | "initialWalletCount" | "handleMoreWallets" | "isLoading" | "isDark"
22+
"totalExternalWalletsCount" | "initialWalletCount" | "handleMoreWallets" | "isLoading" | "isDark"
2323
>;

packages/modal/src/ui/components/ConnectWallet/ConnectWalletQrCode/ConnectWalletQrCode.tsx

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,69 @@ import { QRCode } from "react-qrcode-logo";
66
import { WALLET_CONNECT_LOGO } from "../../../interfaces";
77
import i18n from "../../../localeImport";
88
import Image from "../../Image";
9-
import { ConnectWalletQrCodeProps } from "./ConnectWalletQrCode.type";
9+
import { ConnectWalletQrCodeProps, SelectWalletChainNamespaceProps } from "./ConnectWalletQrCode.type";
10+
11+
const SelectWalletChainNamespace = (props: SelectWalletChainNamespaceProps) => {
12+
const { handleExternalWalletClick, selectedButton } = props;
13+
const [t] = useTranslation(undefined, { i18n });
14+
15+
const chainNamespaces = selectedButton.chainNamespaces!.map((chainNamespace) => {
16+
const imageId = chainNamespace === "eip155" ? "evm" : chainNamespace;
17+
const displayName = chainNamespace === "eip155" ? "EVM" : chainNamespace;
18+
return {
19+
chainNamespace,
20+
displayName,
21+
imageId: `chain-${imageId}`,
22+
};
23+
});
24+
25+
return (
26+
<div>
27+
{/* Wallet image */}
28+
<div className="w3a--my-6 w3a--flex w3a--justify-center">
29+
<Image
30+
imageId={`login-${selectedButton.name}`}
31+
hoverImageId={`login-${selectedButton.name}`}
32+
fallbackImageId="wallet"
33+
height="100"
34+
width="100"
35+
isButton
36+
extension={selectedButton.imgExtension}
37+
/>
38+
</div>
39+
40+
{/* Description */}
41+
<p className="w3a--my-6 w3a--text-center w3a--text-sm w3a--text-app-gray-500">
42+
{t("modal.external.select-chain-description", { wallet: selectedButton.displayName })}
43+
</p>
44+
45+
{/* Chain namespace buttons */}
46+
<ul className="w3a--flex w3a--flex-col w3a--gap-3">
47+
{chainNamespaces.map(({ chainNamespace, displayName, imageId }) => (
48+
<li key={chainNamespace}>
49+
<button
50+
type="button"
51+
className="w3a--btn w3a--size-xl w3a--w-full w3a--items-center !w3a--justify-between w3a--rounded-full"
52+
onClick={() => handleExternalWalletClick({ connector: selectedButton.name, chainNamespace })}
53+
>
54+
<div className="w3a--flex w3a--items-center">
55+
<Image imageId={imageId} hoverImageId={imageId} fallbackImageId="wallet" height="24" width="24" isButton extension="svg" />
56+
<p className="w3a--ml-2 w3a--text-left w3a--text-sm first-letter:w3a--capitalize">{displayName}</p>
57+
</div>
58+
<span className="w3a--inline-flex w3a--items-center w3a--rounded-lg w3a--bg-app-primary-100 w3a--px-2 w3a--py-1 w3a--text-xs w3a--font-medium w3a--text-app-primary-800">
59+
{t("modal.external.installed")}
60+
</span>
61+
</button>
62+
</li>
63+
))}
64+
</ul>
65+
</div>
66+
);
67+
};
1068

1169
function ConnectWalletQrCode(props: ConnectWalletQrCodeProps) {
1270
const [t] = useTranslation(undefined, { i18n });
13-
const { walletConnectUri, isDark, selectedButton, setBodyState, bodyState, logoImage, primaryColor } = props;
71+
const { walletConnectUri, isDark, selectedButton, setBodyState, bodyState, logoImage, primaryColor, handleExternalWalletClick } = props;
1472

1573
const isDesktop = useMemo<boolean>(() => {
1674
const browser = Bowser.getParser(window.navigator.userAgent);
@@ -21,7 +79,11 @@ function ConnectWalletQrCode(props: ConnectWalletQrCodeProps) {
2179
const whiteColor = "#FFFFFF";
2280
const blackColor = "#000000";
2381
const modalColor = getComputedStyle(root)?.getPropertyValue("--app-gray-800")?.trim() || "#1f2a37";
24-
const qrColor = primaryColor.toLowerCase() === "#ffffff" ? "#000000" : primaryColor;
82+
const qrColor = primaryColor && primaryColor.toLowerCase() === "#ffffff" ? "#000000" : primaryColor;
83+
84+
if (selectedButton.hasInjectedWallet) {
85+
return <SelectWalletChainNamespace handleExternalWalletClick={handleExternalWalletClick} selectedButton={selectedButton} />;
86+
}
2587

2688
return (
2789
<div className="w3a--contents">

packages/modal/src/ui/components/ConnectWallet/ConnectWalletQrCode/ConnectWalletQrCode.type.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ChainNamespaceType } from "@web3auth/no-modal";
2+
13
import { RootContextType } from "../../../context/RootContext";
24
import { ExternalButton } from "../../../interfaces";
35

@@ -7,4 +9,7 @@ export interface ConnectWalletQrCodeProps extends RootContextType {
79
selectedButton: ExternalButton;
810
logoImage?: string;
911
primaryColor?: string;
12+
handleExternalWalletClick: (params: { connector: string; chainNamespace?: ChainNamespaceType }) => void;
1013
}
14+
15+
export type SelectWalletChainNamespaceProps = Pick<ConnectWalletQrCodeProps, "handleExternalWalletClick" | "selectedButton">;

0 commit comments

Comments
 (0)