@b3dotfun/sdk 0.1.69-alpha.24 → 0.1.69-alpha.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/global-account/react/components/AvatarEditor/AvatarEditor.js +3 -1
- package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +4 -2
- package/dist/cjs/global-account/react/components/ManageAccount/Header.js +36 -4
- package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.js +4 -1
- package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +6 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.js +5 -3
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.js +3 -1
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsProfileCard.js +25 -14
- package/dist/cjs/global-account/react/components/SignInWithB3/SignIn.js +14 -4
- package/dist/cjs/global-account/react/components/UserAvatar/UserAvatar.d.ts +18 -0
- package/dist/cjs/global-account/react/components/UserAvatar/UserAvatar.js +27 -0
- package/dist/cjs/global-account/react/components/index.d.ts +1 -0
- package/dist/cjs/global-account/react/components/index.js +6 -3
- package/dist/cjs/shared/constants/index.d.ts +1 -0
- package/dist/cjs/shared/constants/index.js +2 -1
- package/dist/esm/global-account/react/components/AvatarEditor/AvatarEditor.js +3 -1
- package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +5 -3
- package/dist/esm/global-account/react/components/ManageAccount/Header.js +37 -5
- package/dist/esm/global-account/react/components/ManageAccount/HomeContent.js +4 -1
- package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +7 -1
- package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.js +6 -4
- package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.js +5 -3
- package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.js +25 -14
- package/dist/esm/global-account/react/components/SignInWithB3/SignIn.js +15 -5
- package/dist/esm/global-account/react/components/UserAvatar/UserAvatar.d.ts +18 -0
- package/dist/esm/global-account/react/components/UserAvatar/UserAvatar.js +21 -0
- package/dist/esm/global-account/react/components/index.d.ts +1 -0
- package/dist/esm/global-account/react/components/index.js +2 -0
- package/dist/esm/shared/constants/index.d.ts +1 -0
- package/dist/esm/shared/constants/index.js +1 -0
- package/dist/types/global-account/react/components/UserAvatar/UserAvatar.d.ts +18 -0
- package/dist/types/global-account/react/components/index.d.ts +1 -0
- package/dist/types/shared/constants/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/global-account/react/components/AvatarEditor/AvatarEditor.tsx +3 -1
- package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +18 -14
- package/src/global-account/react/components/ManageAccount/Header.tsx +71 -4
- package/src/global-account/react/components/ManageAccount/HomeContent.tsx +25 -19
- package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +13 -0
- package/src/global-account/react/components/ManageAccount/ProfileSection.tsx +14 -7
- package/src/global-account/react/components/ManageAccount/SettingsContent.tsx +15 -32
- package/src/global-account/react/components/ManageAccount/SettingsProfileCard.tsx +29 -20
- package/src/global-account/react/components/SignInWithB3/SignIn.tsx +42 -13
- package/src/global-account/react/components/UserAvatar/UserAvatar.tsx +45 -0
- package/src/global-account/react/components/index.ts +3 -0
- package/src/shared/constants/index.ts +2 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useAccountWallet,
|
|
3
|
+
useB3Config,
|
|
3
4
|
useModalStore,
|
|
4
5
|
useProfile,
|
|
5
6
|
useSimBalance,
|
|
6
7
|
useUser,
|
|
7
8
|
} from "@b3dotfun/sdk/global-account/react";
|
|
8
9
|
import { validateImageUrl } from "@b3dotfun/sdk/global-account/react/utils/profileDisplay";
|
|
10
|
+
import { AVATAR_COLORS } from "@b3dotfun/sdk/shared/constants";
|
|
9
11
|
import { formatUsername } from "@b3dotfun/sdk/shared/utils";
|
|
10
12
|
import { formatDisplayNumber } from "@b3dotfun/sdk/shared/utils/number";
|
|
11
13
|
import Avatar from "boring-avatars";
|
|
@@ -15,9 +17,9 @@ import { useActiveAccount } from "thirdweb/react";
|
|
|
15
17
|
import { useFirstEOA } from "../../hooks/useFirstEOA";
|
|
16
18
|
import { IPFSMediaRenderer } from "../IPFSMediaRenderer/IPFSMediaRenderer";
|
|
17
19
|
|
|
18
|
-
const AVATAR_COLORS = ["#3368ef", "#272727", "#6366f1", "#06b6d4", "#eeb0d9", "#ba3fbf", "#ff777b", "#dfbb53"];
|
|
19
|
-
|
|
20
20
|
const ProfileSection = () => {
|
|
21
|
+
const { authStrategy } = useB3Config();
|
|
22
|
+
const isBetterAuth = authStrategy === "better-auth";
|
|
21
23
|
const account = useActiveAccount();
|
|
22
24
|
const { address: eoaAddress } = useFirstEOA();
|
|
23
25
|
const { address: smartWalletAddress } = useAccountWallet();
|
|
@@ -60,7 +62,7 @@ const ProfileSection = () => {
|
|
|
60
62
|
}, [avatarSrc]);
|
|
61
63
|
|
|
62
64
|
const currentUsername = user?.username || profile?.displayName || formatUsername(profile?.name || "");
|
|
63
|
-
const avatarSeed = eoaAddress || account?.address || smartWalletAddress || currentUsername || "user";
|
|
65
|
+
const avatarSeed = eoaAddress || account?.address || smartWalletAddress || currentUsername || user?.email || "user";
|
|
64
66
|
|
|
65
67
|
return (
|
|
66
68
|
<div className="flex items-center justify-between px-5 py-6">
|
|
@@ -87,13 +89,18 @@ const ProfileSection = () => {
|
|
|
87
89
|
</button>
|
|
88
90
|
</div>
|
|
89
91
|
<div className="global-account-profile-info flex flex-col gap-1">
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
{!isBetterAuth && (
|
|
93
|
+
<h2 className="text-b3-grey font-neue-montreal-semibold flex h-[38px] items-center gap-1 text-xl">
|
|
94
|
+
<div className="text-b3-foreground-muted"> $</div>
|
|
95
|
+
<div className="text-[30px]">{formatDisplayNumber(totalBalanceUsd, { fractionDigits: 2 })}</div>
|
|
96
|
+
</h2>
|
|
97
|
+
)}
|
|
94
98
|
<div className="b3-modal-username font-neue-montreal-semibold text-base leading-none text-[#0B57C2]">
|
|
95
99
|
{currentUsername}
|
|
96
100
|
</div>
|
|
101
|
+
{isBetterAuth && user?.email && (
|
|
102
|
+
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">{user.email}</div>
|
|
103
|
+
)}
|
|
97
104
|
</div>
|
|
98
105
|
</div>
|
|
99
106
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useAuthentication, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
1
|
+
import { useAuthentication, useB3Config, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
2
2
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
3
3
|
import { getSessionDurationDays, SESSION_DURATION_LABELS } from "@b3dotfun/sdk/shared/utils/session-duration";
|
|
4
|
-
import { Loader2 } from "lucide-react";
|
|
4
|
+
import { Bell, Clock, Link, Loader2 } from "lucide-react";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { Chain } from "thirdweb";
|
|
7
7
|
import { useProfiles } from "thirdweb/react";
|
|
@@ -19,6 +19,8 @@ const SettingsContent = ({
|
|
|
19
19
|
onLogout?: () => void;
|
|
20
20
|
chain: Chain;
|
|
21
21
|
}) => {
|
|
22
|
+
const { authStrategy } = useB3Config();
|
|
23
|
+
const isBetterAuth = authStrategy === "better-auth";
|
|
22
24
|
const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
|
|
23
25
|
const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
|
|
24
26
|
const { logout, user } = useAuthentication(partnerId);
|
|
@@ -58,7 +60,7 @@ const SettingsContent = ({
|
|
|
58
60
|
|
|
59
61
|
return (
|
|
60
62
|
<div className="flex h-[470px] flex-col">
|
|
61
|
-
<ModalHeader showBackButton={false} showCloseButton={false} title="Settings" />
|
|
63
|
+
{!isBetterAuth && <ModalHeader showBackButton={false} showCloseButton={false} title="Settings" />}
|
|
62
64
|
|
|
63
65
|
{/* Profile Section */}
|
|
64
66
|
<div className="p-5">
|
|
@@ -69,41 +71,22 @@ const SettingsContent = ({
|
|
|
69
71
|
|
|
70
72
|
{/* Menu Items */}
|
|
71
73
|
<div className="space-y-3 px-5">
|
|
74
|
+
{!isBetterAuth && (
|
|
75
|
+
<SettingsMenuItem
|
|
76
|
+
icon={<Link size={18} className="text-[#51525c]" />}
|
|
77
|
+
title="Linked Accounts"
|
|
78
|
+
subtitle={`${profiles.length} connected account${profiles.length > 1 ? "s" : ""}`}
|
|
79
|
+
onClick={() => handleNavigate("linkAccount")}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
72
82
|
<SettingsMenuItem
|
|
73
|
-
icon={
|
|
74
|
-
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
75
|
-
<path
|
|
76
|
-
d="M0 12C0 5.37258 5.37258 0 12 0H28C34.6274 0 40 5.37258 40 12V28C40 34.6274 34.6274 40 28 40H12C5.37258 40 0 34.6274 0 28V12Z"
|
|
77
|
-
fill="#F4F4F5"
|
|
78
|
-
/>
|
|
79
|
-
</svg>
|
|
80
|
-
}
|
|
81
|
-
title="Linked Accounts"
|
|
82
|
-
subtitle={`${profiles.length} connected account${profiles.length > 1 ? "s" : ""}`}
|
|
83
|
-
onClick={() => handleNavigate("linkAccount")}
|
|
84
|
-
/>
|
|
85
|
-
<SettingsMenuItem
|
|
86
|
-
icon={
|
|
87
|
-
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
88
|
-
<path
|
|
89
|
-
d="M0 12C0 5.37258 5.37258 0 12 0H28C34.6274 0 40 5.37258 40 12V28C40 34.6274 34.6274 40 28 40H12C5.37258 40 0 34.6274 0 28V12Z"
|
|
90
|
-
fill="#F4F4F5"
|
|
91
|
-
/>
|
|
92
|
-
</svg>
|
|
93
|
-
}
|
|
83
|
+
icon={<Bell size={18} className="text-[#51525c]" />}
|
|
94
84
|
title="Notifications"
|
|
95
85
|
subtitle="Manage your notifications"
|
|
96
86
|
onClick={() => handleNavigate("notifications")}
|
|
97
87
|
/>
|
|
98
88
|
<SettingsMenuItem
|
|
99
|
-
icon={
|
|
100
|
-
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
101
|
-
<path
|
|
102
|
-
d="M0 12C0 5.37258 5.37258 0 12 0H28C34.6274 0 40 5.37258 40 12V28C40 34.6274 34.6274 40 28 40H12C5.37258 40 0 34.6274 0 28V12Z"
|
|
103
|
-
fill="#F4F4F5"
|
|
104
|
-
/>
|
|
105
|
-
</svg>
|
|
106
|
-
}
|
|
89
|
+
icon={<Clock size={18} className="text-[#51525c]" />}
|
|
107
90
|
title="Stay signed in"
|
|
108
91
|
subtitle={SESSION_DURATION_LABELS[sessionDays] ?? `${sessionDays} days`}
|
|
109
92
|
onClick={() => handleNavigate("sessionDuration")}
|
|
@@ -18,7 +18,8 @@ const SettingsProfileCard = () => {
|
|
|
18
18
|
address: eoaAddress || account?.address,
|
|
19
19
|
fresh: true,
|
|
20
20
|
});
|
|
21
|
-
const { partnerId } = useB3Config();
|
|
21
|
+
const { partnerId, authStrategy } = useB3Config();
|
|
22
|
+
const isBetterAuth = authStrategy === "better-auth";
|
|
22
23
|
const { user, setUser } = useAuthentication(partnerId);
|
|
23
24
|
const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
|
|
24
25
|
const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
|
|
@@ -78,25 +79,33 @@ const SettingsProfileCard = () => {
|
|
|
78
79
|
|
|
79
80
|
setIsSaving(true);
|
|
80
81
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
let updatedUser: Users;
|
|
83
|
+
if (isBetterAuth) {
|
|
84
|
+
// Better Auth: register username without wallet signing (DB-only, no ENS)
|
|
85
|
+
// Skip ens_normalize — it rejects underscores/mixed-case that are valid non-ENS usernames
|
|
86
|
+
const sanitizedUsername = editedUsername.trim().toLowerCase();
|
|
87
|
+
// Type assertion needed: b3-mono now accepts message/hash as optional for Better Auth users
|
|
88
|
+
updatedUser = (await app
|
|
89
|
+
.service("users")
|
|
90
|
+
.registerUsername({ username: sanitizedUsername } as any, {} as any)) as unknown as Users;
|
|
91
|
+
} else {
|
|
92
|
+
// Thirdweb: register username with wallet signature + on-chain ENS
|
|
93
|
+
const sanitizedUsername = ens_normalize(editedUsername.trim());
|
|
94
|
+
const b3Username = `${sanitizedUsername}.b3.fun`;
|
|
95
|
+
const usernameSignMessage = `Register "${b3Username}"`;
|
|
96
|
+
const usernameSignature = await account?.signMessage({ message: usernameSignMessage });
|
|
97
|
+
|
|
98
|
+
if (!usernameSignature) {
|
|
99
|
+
throw new Error("Failed to sign username registration message");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
updatedUser = (await app
|
|
103
|
+
.service("users")
|
|
104
|
+
.registerUsername(
|
|
105
|
+
{ username: sanitizedUsername, message: usernameSignMessage, hash: usernameSignature },
|
|
106
|
+
{} as any,
|
|
107
|
+
)) as unknown as Users;
|
|
88
108
|
}
|
|
89
|
-
|
|
90
|
-
console.log("@@usernameSignature", usernameSignature);
|
|
91
|
-
|
|
92
|
-
// Register username with ENS
|
|
93
|
-
// Note: Type assertion needed until @b3dotfun/b3-api package is updated with RegisterUsername type
|
|
94
|
-
const updatedUser = (await app
|
|
95
|
-
.service("users")
|
|
96
|
-
.registerUsername(
|
|
97
|
-
{ username: sanitizedUsername, message: usernameSignMessage, hash: usernameSignature },
|
|
98
|
-
{} as any,
|
|
99
|
-
)) as unknown as Users;
|
|
100
109
|
// Update user state - registerUsername returns an array with single user
|
|
101
110
|
setUser(Array.isArray(updatedUser) ? updatedUser[0] : updatedUser);
|
|
102
111
|
|
|
@@ -179,7 +188,7 @@ const SettingsProfileCard = () => {
|
|
|
179
188
|
<>
|
|
180
189
|
<div className="flex items-center gap-1">
|
|
181
190
|
<p className="b3-modal-username font-neue-montreal-semibold text-lg leading-none text-[#0B57C2]">
|
|
182
|
-
{currentUsername}
|
|
191
|
+
{currentUsername || user?.email}
|
|
183
192
|
</p>
|
|
184
193
|
</div>
|
|
185
194
|
<button
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
SignInWithB3ModalProps,
|
|
5
5
|
StyleRoot,
|
|
6
6
|
useAccountWallet,
|
|
7
|
+
useAuthStore,
|
|
7
8
|
useAuthentication,
|
|
8
9
|
useB3Config,
|
|
9
10
|
useIsMobile,
|
|
@@ -14,6 +15,8 @@ import { cn, truncateAddress } from "@b3dotfun/sdk/shared/utils";
|
|
|
14
15
|
import { Menu, MenuButton, MenuItems, Transition } from "@headlessui/react";
|
|
15
16
|
import { ReactNode, useEffect } from "react";
|
|
16
17
|
import { useConnectedWallets, useSetActiveWallet, useWalletImage } from "thirdweb/react";
|
|
18
|
+
import { useUser } from "../../hooks/useUser";
|
|
19
|
+
import { UserAvatar } from "../UserAvatar/UserAvatar";
|
|
17
20
|
import { ManageAccountButton } from "../custom/ManageAccountButton";
|
|
18
21
|
|
|
19
22
|
type SignInProps = {
|
|
@@ -30,7 +33,9 @@ type SignInWithB3Props = Omit<SignInWithB3ModalProps, "type" | "showBackButton">
|
|
|
30
33
|
|
|
31
34
|
export function SignIn(props: SignInWithB3Props) {
|
|
32
35
|
const { className } = props;
|
|
33
|
-
const { automaticallySetFirstEoa, partnerId } = useB3Config();
|
|
36
|
+
const { automaticallySetFirstEoa, partnerId, authStrategy } = useB3Config();
|
|
37
|
+
const isBetterAuth = authStrategy === "better-auth";
|
|
38
|
+
|
|
34
39
|
const {
|
|
35
40
|
address: globalAddress,
|
|
36
41
|
ensName,
|
|
@@ -53,6 +58,11 @@ export function SignIn(props: SignInWithB3Props) {
|
|
|
53
58
|
|
|
54
59
|
const setActiveWallet = useSetActiveWallet();
|
|
55
60
|
|
|
61
|
+
// Better Auth state
|
|
62
|
+
const isAuthenticated = useAuthStore(state => state.isAuthenticated);
|
|
63
|
+
const { user } = useUser();
|
|
64
|
+
const userDisplayName = user?.username || user?.email || "Account";
|
|
65
|
+
|
|
56
66
|
const handleSetActiveAccount = (selectedWalletId: string | undefined) => {
|
|
57
67
|
if (
|
|
58
68
|
!selectedWalletId ||
|
|
@@ -72,21 +82,29 @@ export function SignIn(props: SignInWithB3Props) {
|
|
|
72
82
|
}
|
|
73
83
|
}, [connectedEOAWallet, isActiveEOAWallet, setActiveWallet, automaticallySetFirstEoa]);
|
|
74
84
|
|
|
85
|
+
const isLoggedIn = isBetterAuth ? isAuthenticated : !!globalAddress;
|
|
86
|
+
|
|
75
87
|
// Desktop version - original dropdown menu
|
|
76
88
|
return (
|
|
77
89
|
<StyleRoot>
|
|
78
90
|
<Menu className={`relative flex items-center ${className || ""}`} as="div">
|
|
79
|
-
{
|
|
91
|
+
{isLoggedIn ? (
|
|
80
92
|
<>
|
|
81
93
|
<MenuButton className="bg-b3-react-background group flex h-10 items-center gap-1 rounded-xl px-3 focus:outline-none">
|
|
82
|
-
{
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
94
|
+
{isBetterAuth ? (
|
|
95
|
+
<UserAvatar avatarUrl={user?.avatar} name={userDisplayName} size={24} />
|
|
96
|
+
) : (
|
|
97
|
+
!!walletImage && (
|
|
98
|
+
<IPFSMediaRenderer
|
|
99
|
+
src={walletImage}
|
|
100
|
+
alt="Wallet Image"
|
|
101
|
+
className="bg-b3-react-primary h-6 w-6 rounded-full object-cover opacity-100"
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
88
104
|
)}
|
|
89
|
-
<div className="text-as-primary">
|
|
105
|
+
<div className="text-as-primary">
|
|
106
|
+
{isBetterAuth ? userDisplayName : ensName ? ensName : truncateAddress(globalAddress ?? "")}
|
|
107
|
+
</div>
|
|
90
108
|
</MenuButton>
|
|
91
109
|
<Transition
|
|
92
110
|
enter="duration-200 ease-out"
|
|
@@ -103,7 +121,16 @@ export function SignIn(props: SignInWithB3Props) {
|
|
|
103
121
|
anchor={isMobile ? "top end" : undefined}
|
|
104
122
|
>
|
|
105
123
|
<div className="bg-b3-react-background">
|
|
106
|
-
{
|
|
124
|
+
{isBetterAuth ? (
|
|
125
|
+
/* Better Auth: show user info instead of wallet switching */
|
|
126
|
+
<div className="flex items-center gap-3 rounded-xl p-3">
|
|
127
|
+
<UserAvatar avatarUrl={user?.avatar} name={userDisplayName} size={48} />
|
|
128
|
+
<div className="flex flex-col gap-0.5">
|
|
129
|
+
{user?.username && <div className="text-b3-react-primary font-semibold">{user.username}</div>}
|
|
130
|
+
{user?.email && <div className="text-b3-react-secondary text-sm">{user.email}</div>}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
) : connectedEOAWallet ? (
|
|
107
134
|
<div
|
|
108
135
|
className={cn(
|
|
109
136
|
"border-b3-react-subtle bg-b3-react-background flex cursor-pointer items-center justify-between rounded-xl p-3",
|
|
@@ -118,7 +145,7 @@ export function SignIn(props: SignInWithB3Props) {
|
|
|
118
145
|
/>
|
|
119
146
|
<div className="ml-4 grow">
|
|
120
147
|
{ensName && <div>{ensName}</div>}
|
|
121
|
-
<div>{truncateAddress(globalAddress)}</div>
|
|
148
|
+
<div>{truncateAddress(globalAddress ?? "")}</div>
|
|
122
149
|
{/* <div>{walletInfo?.name}</div> */}
|
|
123
150
|
</div>
|
|
124
151
|
</div>
|
|
@@ -143,7 +170,7 @@ export function SignIn(props: SignInWithB3Props) {
|
|
|
143
170
|
/>
|
|
144
171
|
<div className="grow pl-4">
|
|
145
172
|
{ensName && <div>{ensName}</div>}
|
|
146
|
-
<div>{truncateAddress(globalAddress)}</div>
|
|
173
|
+
<div>{truncateAddress(globalAddress ?? "")}</div>
|
|
147
174
|
<div>Smart wallet</div>
|
|
148
175
|
</div>
|
|
149
176
|
</div>
|
|
@@ -159,7 +186,9 @@ export function SignIn(props: SignInWithB3Props) {
|
|
|
159
186
|
<button className="mb-2 w-full space-y-1" onClick={onDisconnect}>
|
|
160
187
|
<div className="hover:bg-b3-react-background group flex h-12 items-center rounded-xl px-4 transition-colors">
|
|
161
188
|
<Icon className="fill-b3-react-primary mr-4 shrink-0 transition-colors" name="logout" />
|
|
162
|
-
<div className="text-b3-react-primary mr-auto transition-colors">
|
|
189
|
+
<div className="text-b3-react-primary mr-auto transition-colors">
|
|
190
|
+
{isBetterAuth ? "Sign out" : "Disconnect"}
|
|
191
|
+
</div>
|
|
163
192
|
</div>
|
|
164
193
|
</button>
|
|
165
194
|
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { validateImageUrl } from "@b3dotfun/sdk/global-account/react/utils/profileDisplay";
|
|
2
|
+
import { AVATAR_COLORS } from "@b3dotfun/sdk/shared/constants";
|
|
3
|
+
import Avatar from "boring-avatars";
|
|
4
|
+
import { useCallback, useEffect, useState } from "react";
|
|
5
|
+
import { IPFSMediaRenderer } from "../IPFSMediaRenderer/IPFSMediaRenderer";
|
|
6
|
+
|
|
7
|
+
interface UserAvatarProps {
|
|
8
|
+
/** Direct avatar URL (IPFS or HTTP). Resolved and validated internally. */
|
|
9
|
+
avatarUrl?: string | null;
|
|
10
|
+
/** Seed for the generated fallback avatar + alt text. Use email, username, or address. */
|
|
11
|
+
name?: string;
|
|
12
|
+
/** Avatar size in pixels (square). */
|
|
13
|
+
size?: number;
|
|
14
|
+
/** Additional className for the outer container. */
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Renders a user avatar with IPFS support and boring-avatars fallback.
|
|
20
|
+
*
|
|
21
|
+
* - If `avatarUrl` is provided and valid, renders via IPFSMediaRenderer.
|
|
22
|
+
* - On load failure or missing URL, falls back to a deterministic boring-avatars beam.
|
|
23
|
+
*/
|
|
24
|
+
export function UserAvatar({ avatarUrl, name = "user", size = 40, className }: UserAvatarProps) {
|
|
25
|
+
const resolvedSrc = validateImageUrl(avatarUrl);
|
|
26
|
+
const [imgError, setImgError] = useState(false);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
setImgError(false);
|
|
30
|
+
}, [avatarUrl]);
|
|
31
|
+
|
|
32
|
+
const handleImgError = useCallback(() => setImgError(true), []);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className={className} style={{ width: size, height: size, minWidth: size, minHeight: size }}>
|
|
36
|
+
{resolvedSrc && !imgError ? (
|
|
37
|
+
<div onErrorCapture={handleImgError} className="h-full w-full overflow-hidden rounded-full">
|
|
38
|
+
<IPFSMediaRenderer src={resolvedSrc} alt={name} className="h-full w-full object-cover" />
|
|
39
|
+
</div>
|
|
40
|
+
) : (
|
|
41
|
+
<Avatar name={name} variant="beam" size={size} colors={AVATAR_COLORS} />
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -29,6 +29,9 @@ export { getConnectOptionsFromStrategy, isWalletType, type AllowedStrategy } fro
|
|
|
29
29
|
// ManageAccount Components
|
|
30
30
|
export { ManageAccount } from "./ManageAccount/ManageAccount";
|
|
31
31
|
|
|
32
|
+
// UserAvatar
|
|
33
|
+
export { UserAvatar } from "./UserAvatar/UserAvatar";
|
|
34
|
+
|
|
32
35
|
// Deposit Components
|
|
33
36
|
export { Deposit } from "./Deposit/Deposit";
|
|
34
37
|
|
|
@@ -32,3 +32,5 @@ export const B3_AUTH_COOKIE_NAME = "b3-auth";
|
|
|
32
32
|
export const ENS_GATEWAY_URL = "https://ens-gateway.b3.fun/";
|
|
33
33
|
|
|
34
34
|
export const PUBLIC_BASE_RPC_URL = "https://base-rpc.publicnode.com";
|
|
35
|
+
|
|
36
|
+
export const AVATAR_COLORS = ["#3368ef", "#272727", "#6366f1", "#06b6d4", "#eeb0d9", "#ba3fbf", "#ff777b", "#dfbb53"];
|