Skip to content

Commit 151127d

Browse files
committed
[PRO-144] SDK: Update in-app wallet icon in wide connect ui (#8556)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on enhancing the in-app wallet feature by updating icons, adding new components for different authentication methods, and improving the UI for wallet connection options. ### Detailed summary - Updated the in-app wallet icon in the wide connect UI. - Changed `defaultAuthOptions` from a constant to an exported variable. - Modified `EmailIcon` component for better styling and structure. - Added new functions for various modal configurations (`ConfiguredInAppWalletWideModal`, `GoogleLoginWideModal`, `GithubLoginWideModal`). - Introduced `InAppWalletIcon` component to handle multiple authentication methods visually. - Created comprehensive storybook examples for `InAppWalletIcon` with various authentication options. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an in-app wallet icon that displays available authentication methods and adapts layout for single vs. multiple options. * **UI/UX Improvements** * Updated wallet icon visuals and dynamic color handling for more consistent rendering in the connect modal. * Simplified email icon visuals for clearer strokes. * **Stories** * Added Storybook examples showcasing the in-app wallet icon across themes and auth-option permutations. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 759f41b commit 151127d

File tree

8 files changed

+455
-22
lines changed

8 files changed

+455
-22
lines changed

.changeset/sixty-clubs-fix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Update in-app wallet icon in wide connect ui

packages/thirdweb/src/react/web/ui/ConnectWallet/WalletEntryButton.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Skeleton } from "../components/Skeleton.js";
1717
import { Text } from "../components/text.js";
1818
import { WalletImage } from "../components/WalletImage.js";
1919
import { StyledButton } from "../design-system/elements.js";
20+
import { InAppWalletIcon } from "./in-app-wallet-icon.js";
2021
import type { ConnectLocale } from "./locale/types.js";
2122

2223
/**
@@ -52,9 +53,20 @@ export function WalletEntryButton(props: {
5253
wallet && walletId === "inApp"
5354
? (wallet.getConfig() as InAppWalletCreationOptions)?.metadata
5455
: undefined;
55-
const nameOverride = customMeta?.name || walletName;
56+
let nameOverride = customMeta?.name || walletName;
5657
const iconOverride = customMeta?.icon;
5758

59+
// change "Social Login" to name of the login method if only 1 method is enabled
60+
if (wallet.id === "inApp") {
61+
const config = wallet.getConfig() as InAppWalletCreationOptions;
62+
if (config?.auth?.options.length === 1) {
63+
const name = config.auth?.options[0];
64+
if (name) {
65+
nameOverride = uppercaseFirstLetter({ text: name });
66+
}
67+
}
68+
}
69+
5870
return (
5971
<WalletButtonEl
6072
className={props.className}
@@ -64,12 +76,17 @@ export function WalletEntryButton(props: {
6476
>
6577
{iconOverride ? (
6678
<Img
67-
alt={nameOverride}
79+
alt=""
6880
client={props.client}
6981
height={`${iconSize.xl}`}
7082
src={iconOverride}
7183
width={`${iconSize.xl}`}
7284
/>
85+
) : wallet.id === "inApp" ? (
86+
<InAppWalletIcon
87+
client={props.client}
88+
wallet={wallet as Wallet<"inApp">}
89+
/>
7390
) : (
7491
<WalletImage client={props.client} id={walletId} size={iconSize.xl} />
7592
)}
@@ -119,3 +136,7 @@ export const WalletButtonEl = /* @__PURE__ */ StyledButton((_) => {
119136
width: "100%",
120137
};
121138
});
139+
140+
function uppercaseFirstLetter(props: { text: string }) {
141+
return props.text.charAt(0).toUpperCase() + props.text.slice(1);
142+
}
Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
import type { IconFC } from "./types.js";
22

3-
/**
4-
* @internal
5-
*/
63
export const EmailIcon: IconFC = (props) => {
74
return (
85
<svg
9-
fill="none"
6+
xmlns="http://www.w3.org/2000/svg"
7+
width={props.size}
108
height={props.size}
9+
viewBox="0 0 24 24"
1110
role="presentation"
12-
viewBox="0 0 16 16"
13-
width={props.size}
14-
xmlns="http://www.w3.org/2000/svg"
11+
fill="none"
12+
stroke="currentColor"
13+
strokeWidth="2"
14+
strokeLinecap="round"
15+
strokeLinejoin="round"
16+
className="lucide lucide-mail-icon lucide-mail"
17+
style={{ color: props.color }}
1518
>
16-
<path
17-
d="M13.3335 2.6665H2.66683C1.93045 2.6665 1.3335 3.26346 1.3335 3.99984V11.9998C1.3335 12.7362 1.93045 13.3332 2.66683 13.3332H13.3335C14.0699 13.3332 14.6668 12.7362 14.6668 11.9998V3.99984C14.6668 3.26346 14.0699 2.6665 13.3335 2.6665Z"
18-
stroke={props.color ?? "currentColor"}
19-
strokeLinecap="round"
20-
strokeLinejoin="round"
21-
/>
22-
<path
23-
d="M14.6668 4.6665L8.68683 8.4665C8.48101 8.59545 8.24304 8.66384 8.00016 8.66384C7.75728 8.66384 7.51931 8.59545 7.3135 8.4665L1.3335 4.6665"
24-
stroke={props.color ?? "currentColor"}
25-
strokeLinecap="round"
26-
strokeLinejoin="round"
27-
/>
19+
<path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" />
20+
<rect x="2" y="4" width="20" height="16" rx="2" />
2821
</svg>
2922
);
3023
};

packages/thirdweb/src/react/web/ui/ConnectWallet/icons/WalletDotIcon.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const WalletDotIcon: IconFC = (props) => {
1111
viewBox="0 0 18 18"
1212
width={props.size}
1313
height={props.size}
14+
style={{ color: props.color }}
1415
role="presentation"
1516
>
1617
<path
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
"use client";
2+
import type { ThirdwebClient } from "../../../../client/client.js";
3+
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
4+
import type { AuthOption } from "../../../../wallets/types.js";
5+
import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
6+
import {
7+
iconSize,
8+
radius,
9+
spacing,
10+
} from "../../../core/design-system/index.js";
11+
import { socialIcons } from "../../../core/utils/walletIcon.js";
12+
import { defaultAuthOptions } from "../../wallets/shared/ConnectWalletSocialOptions.js";
13+
import { Img } from "../components/Img.js";
14+
import { EmailIcon } from "./icons/EmailIcon.js";
15+
import { FingerPrintIcon } from "./icons/FingerPrintIcon.js";
16+
import { GuestIcon } from "./icons/GuestIcon.js";
17+
import { PhoneIcon } from "./icons/PhoneIcon.js";
18+
19+
export function InAppWalletIcon(props: {
20+
client: ThirdwebClient;
21+
wallet: Wallet<"inApp">;
22+
}) {
23+
const enabledAuthMethods = (
24+
props.wallet.getConfig()?.auth?.options || defaultAuthOptions
25+
)
26+
.slice() // clone
27+
.sort((a, b) => {
28+
if (a in socialIcons && !(b in socialIcons)) {
29+
return -1;
30+
}
31+
if (!(a in socialIcons) && b in socialIcons) {
32+
return 1;
33+
}
34+
return 0;
35+
});
36+
37+
const theme = useCustomTheme();
38+
39+
const firstMethod = enabledAuthMethods[0];
40+
const secondMethod = enabledAuthMethods[1];
41+
const thirdMethod = enabledAuthMethods[2];
42+
const fourthMethod = enabledAuthMethods[3];
43+
44+
const offset = "4px";
45+
const offset2 = "6px";
46+
const smallIconSize = "20";
47+
const extraIconSize = "12";
48+
49+
if (firstMethod && secondMethod) {
50+
return (
51+
<div
52+
style={{
53+
width: `${iconSize.xl}px`,
54+
height: `${iconSize.xl}px`,
55+
position: "relative",
56+
gap: spacing["3xs"],
57+
border: `1px solid ${theme.colors.borderColor}`,
58+
borderRadius: radius.md,
59+
backgroundColor: theme.colors.tertiaryBg,
60+
}}
61+
>
62+
<div
63+
style={{
64+
position: "absolute",
65+
top: offset,
66+
left: offset,
67+
display: "flex",
68+
}}
69+
>
70+
<AuthOptionIcon
71+
authOption={firstMethod}
72+
client={props.client}
73+
size={smallIconSize}
74+
/>
75+
</div>
76+
77+
<div
78+
style={{
79+
position: "absolute",
80+
bottom: offset,
81+
right: offset,
82+
display: "flex",
83+
}}
84+
>
85+
<AuthOptionIcon
86+
authOption={secondMethod}
87+
client={props.client}
88+
size={smallIconSize}
89+
/>
90+
</div>
91+
92+
<div>
93+
{thirdMethod && (
94+
<div
95+
style={{
96+
position: "absolute",
97+
top: offset2,
98+
right: offset2,
99+
display: "flex",
100+
}}
101+
>
102+
<AuthOptionIcon
103+
authOption={thirdMethod}
104+
client={props.client}
105+
size={extraIconSize}
106+
/>
107+
</div>
108+
)}
109+
110+
{fourthMethod && (
111+
<div
112+
style={{
113+
position: "absolute",
114+
bottom: offset2,
115+
left: offset2,
116+
display: "flex",
117+
}}
118+
>
119+
<AuthOptionIcon
120+
authOption={fourthMethod}
121+
client={props.client}
122+
size={extraIconSize}
123+
/>
124+
</div>
125+
)}
126+
</div>
127+
</div>
128+
);
129+
}
130+
131+
if (firstMethod) {
132+
return (
133+
<div
134+
style={{
135+
width: `${iconSize.xl}px`,
136+
height: `${iconSize.xl}px`,
137+
display: "flex",
138+
justifyContent: "center",
139+
alignItems: "center",
140+
border: `1px solid ${theme.colors.borderColor}`,
141+
borderRadius: radius.md,
142+
backgroundColor: theme.colors.tertiaryBg,
143+
}}
144+
>
145+
<AuthOptionIcon
146+
authOption={firstMethod}
147+
client={props.client}
148+
key={firstMethod}
149+
size={iconSize.lg}
150+
/>
151+
</div>
152+
);
153+
}
154+
155+
return null;
156+
}
157+
158+
function AuthOptionIcon(props: {
159+
authOption: AuthOption;
160+
client: ThirdwebClient;
161+
size: string;
162+
}) {
163+
const theme = useCustomTheme();
164+
if (props.authOption in socialIcons) {
165+
const icon = socialIcons[props.authOption as keyof typeof socialIcons];
166+
return (
167+
<Img
168+
src={icon}
169+
width={props.size}
170+
height={props.size}
171+
client={props.client}
172+
/>
173+
);
174+
}
175+
176+
if (props.authOption === "phone") {
177+
return <PhoneIcon size={props.size} color={theme.colors.secondaryText} />;
178+
}
179+
180+
if (props.authOption === "email") {
181+
return <EmailIcon size={props.size} color={theme.colors.secondaryText} />;
182+
}
183+
184+
if (props.authOption === "passkey") {
185+
return (
186+
<FingerPrintIcon size={props.size} color={theme.colors.secondaryText} />
187+
);
188+
}
189+
190+
if (props.authOption === "guest") {
191+
return <GuestIcon size={props.size} color={theme.colors.secondaryText} />;
192+
}
193+
194+
return null;
195+
}

packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export type ConnectWalletSelectUIState =
6161
};
6262
};
6363

64-
const defaultAuthOptions: AuthOption[] = [
64+
export const defaultAuthOptions: AuthOption[] = [
6565
"email",
6666
"phone",
6767
"google",

packages/thirdweb/src/stories/ConnectEmbed.stories.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Meta } from "@storybook/react";
22
import { ConnectButton } from "../react/web/ui/ConnectWallet/ConnectButton.js";
33
import { ConnectEmbed } from "../react/web/ui/ConnectWallet/Modal/ConnectEmbed.js";
4+
import { createWallet } from "../wallets/create-wallet.js";
45
import { ecosystemWallet } from "../wallets/in-app/web/ecosystem.js";
56
import { inAppWallet } from "../wallets/in-app/web/in-app.js";
67
import { storyClient } from "./utils.js";
@@ -110,6 +111,60 @@ export function AllInAppWalletAuthMethods() {
110111
);
111112
}
112113

114+
export function ConfiguredInAppWalletWideModal() {
115+
return (
116+
<ConnectEmbed
117+
client={storyClient}
118+
className="foo-bar"
119+
modalSize="wide"
120+
wallets={[
121+
createWallet("io.metamask"),
122+
inAppWallet({
123+
auth: {
124+
options: ["google", "github", "email"],
125+
},
126+
}),
127+
]}
128+
/>
129+
);
130+
}
131+
132+
export function GoogleLoginWideModal() {
133+
return (
134+
<ConnectEmbed
135+
client={storyClient}
136+
className="foo-bar"
137+
modalSize="wide"
138+
wallets={[
139+
createWallet("io.metamask"),
140+
inAppWallet({
141+
auth: {
142+
options: ["google"],
143+
},
144+
}),
145+
]}
146+
/>
147+
);
148+
}
149+
150+
export function GithubLoginWideModal() {
151+
return (
152+
<ConnectEmbed
153+
client={storyClient}
154+
className="foo-bar"
155+
modalSize="wide"
156+
wallets={[
157+
createWallet("io.metamask"),
158+
inAppWallet({
159+
auth: {
160+
options: ["github"],
161+
},
162+
}),
163+
]}
164+
/>
165+
);
166+
}
167+
113168
export function EcosystemWallet() {
114169
return (
115170
<ConnectEmbed

0 commit comments

Comments
 (0)