@b3dotfun/sdk 0.1.69-alpha.23 → 0.1.69-alpha.25
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 +37 -4
- package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.js +4 -1
- 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/BetterAuthSignIn.d.ts +6 -1
- package/dist/cjs/global-account/react/components/SignInWithB3/BetterAuthSignIn.js +2 -2
- package/dist/cjs/global-account/react/components/SignInWithB3/BetterAuthVerifyEmail.d.ts +37 -0
- package/dist/cjs/global-account/react/components/SignInWithB3/BetterAuthVerifyEmail.js +85 -0
- package/dist/cjs/global-account/react/components/SignInWithB3/SignIn.js +14 -4
- package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.d.ts +1 -1
- package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +2 -2
- package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.d.ts +3 -1
- package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.js +2 -2
- package/dist/cjs/global-account/react/components/index.d.ts +1 -0
- package/dist/cjs/global-account/react/components/index.js +5 -3
- package/dist/cjs/global-account/react/hooks/useBetterAuth.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/useBetterAuth.js +5 -4
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +2 -0
- 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 +38 -5
- package/dist/esm/global-account/react/components/ManageAccount/HomeContent.js +4 -1
- package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.js +6 -4
- package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.js +4 -2
- package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.js +25 -14
- package/dist/esm/global-account/react/components/SignInWithB3/BetterAuthSignIn.d.ts +6 -1
- package/dist/esm/global-account/react/components/SignInWithB3/BetterAuthSignIn.js +2 -2
- package/dist/esm/global-account/react/components/SignInWithB3/BetterAuthVerifyEmail.d.ts +37 -0
- package/dist/esm/global-account/react/components/SignInWithB3/BetterAuthVerifyEmail.js +82 -0
- package/dist/esm/global-account/react/components/SignInWithB3/SignIn.js +16 -6
- package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.d.ts +1 -1
- package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +2 -2
- package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.d.ts +3 -1
- package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.js +2 -2
- package/dist/esm/global-account/react/components/index.d.ts +1 -0
- package/dist/esm/global-account/react/components/index.js +1 -0
- package/dist/esm/global-account/react/hooks/useBetterAuth.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/useBetterAuth.js +5 -4
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +2 -0
- package/dist/esm/shared/constants/index.d.ts +1 -0
- package/dist/esm/shared/constants/index.js +1 -0
- package/dist/styles/index.css +1 -1
- package/dist/types/global-account/react/components/SignInWithB3/BetterAuthSignIn.d.ts +6 -1
- package/dist/types/global-account/react/components/SignInWithB3/BetterAuthVerifyEmail.d.ts +37 -0
- package/dist/types/global-account/react/components/SignInWithB3/SignInWithB3Flow.d.ts +1 -1
- package/dist/types/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.d.ts +3 -1
- package/dist/types/global-account/react/components/index.d.ts +1 -0
- package/dist/types/global-account/react/hooks/useBetterAuth.d.ts +1 -1
- package/dist/types/global-account/react/stores/useModalStore.d.ts +2 -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 +74 -4
- package/src/global-account/react/components/ManageAccount/HomeContent.tsx +25 -19
- package/src/global-account/react/components/ManageAccount/ProfileSection.tsx +14 -7
- package/src/global-account/react/components/ManageAccount/SettingsContent.tsx +18 -14
- package/src/global-account/react/components/ManageAccount/SettingsProfileCard.tsx +29 -20
- package/src/global-account/react/components/SignInWithB3/BetterAuthSignIn.tsx +7 -1
- package/src/global-account/react/components/SignInWithB3/BetterAuthVerifyEmail.tsx +155 -0
- package/src/global-account/react/components/SignInWithB3/SignIn.tsx +43 -14
- package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +8 -1
- package/src/global-account/react/components/SignInWithB3/steps/LoginStepBetterAuth.tsx +4 -2
- package/src/global-account/react/components/index.ts +5 -0
- package/src/global-account/react/hooks/useBetterAuth.ts +5 -4
- package/src/global-account/react/stores/useModalStore.ts +2 -0
- package/src/shared/constants/index.ts +2 -0
|
@@ -8,6 +8,7 @@ export { useB3Config } from "./B3Provider/useB3Config";
|
|
|
8
8
|
export { StyleRoot } from "./StyleRoot";
|
|
9
9
|
export { BetterAuthResetPassword, type BetterAuthResetPasswordProps } from "./SignInWithB3/BetterAuthResetPassword";
|
|
10
10
|
export { BetterAuthSignIn, type BetterAuthSignInProps } from "./SignInWithB3/BetterAuthSignIn";
|
|
11
|
+
export { BetterAuthVerifyEmail, type BetterAuthVerifyEmailProps, type BetterAuthVerifyEmailState, } from "./SignInWithB3/BetterAuthVerifyEmail";
|
|
11
12
|
export { AuthButton } from "./SignInWithB3/components/AuthButton";
|
|
12
13
|
export { PermissionItem } from "./SignInWithB3/components/PermissionItem";
|
|
13
14
|
export { WalletRow } from "./SignInWithB3/components/WalletRow";
|
|
@@ -13,7 +13,7 @@ export declare class EmailVerificationRequiredError extends Error {
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function useBetterAuth(): {
|
|
15
15
|
signInWithEmail: (email: string, password: string) => Promise<import("@feathersjs/authentication").AuthenticationResult>;
|
|
16
|
-
signUpWithEmail: (email: string, password: string, name: string) => Promise<import("@feathersjs/authentication").AuthenticationResult>;
|
|
16
|
+
signUpWithEmail: (email: string, password: string, name: string, verifyCallbackURL?: string) => Promise<import("@feathersjs/authentication").AuthenticationResult>;
|
|
17
17
|
signInWithSocial: (provider: BetterAuthSocialProvider) => Promise<void>;
|
|
18
18
|
requestPasswordReset: (email: string, redirectTo?: string) => Promise<{
|
|
19
19
|
data: {
|
|
@@ -41,6 +41,8 @@ export interface SignInWithB3ModalProps extends BaseModalProps {
|
|
|
41
41
|
source?: "signInWithB3Button" | "requestPermissions";
|
|
42
42
|
/** Whether to show the signers enabled modal */
|
|
43
43
|
signersEnabled?: boolean;
|
|
44
|
+
/** URL Better Auth redirects to after server-side email verification. */
|
|
45
|
+
verifyEmailRedirectTo?: string;
|
|
44
46
|
}
|
|
45
47
|
/**
|
|
46
48
|
* Props for the Request Permissions modal
|
|
@@ -16,3 +16,4 @@ export declare const CLIENT_APP_BUNDLE_ID: string;
|
|
|
16
16
|
export declare const B3_AUTH_COOKIE_NAME = "b3-auth";
|
|
17
17
|
export declare const ENS_GATEWAY_URL = "https://ens-gateway.b3.fun/";
|
|
18
18
|
export declare const PUBLIC_BASE_RPC_URL = "https://base-rpc.publicnode.com";
|
|
19
|
+
export declare const AVATAR_COLORS: string[];
|
package/package.json
CHANGED
|
@@ -62,6 +62,8 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
|
|
|
62
62
|
const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
|
|
63
63
|
const contentType = useModalStore(state => state.contentType);
|
|
64
64
|
|
|
65
|
+
const { authStrategy } = useB3Config();
|
|
66
|
+
const isBetterAuth = authStrategy === "better-auth";
|
|
65
67
|
const account = useActiveAccount();
|
|
66
68
|
const { data: profile, refetch: refreshProfile } = useProfile({
|
|
67
69
|
address: account?.address,
|
|
@@ -162,7 +164,7 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
|
|
|
162
164
|
};
|
|
163
165
|
|
|
164
166
|
const handleSaveChanges = async () => {
|
|
165
|
-
if (!account?.address) {
|
|
167
|
+
if (!isBetterAuth && !account?.address) {
|
|
166
168
|
toast.error("No account connected");
|
|
167
169
|
return;
|
|
168
170
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TabsListPrimitive, TabTriggerPrimitive, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
1
|
+
import { TabsListPrimitive, TabTriggerPrimitive, useB3Config, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
2
2
|
|
|
3
3
|
const HomeIcon = () => {
|
|
4
4
|
return (
|
|
@@ -42,6 +42,8 @@ const SettingsIcon = () => {
|
|
|
42
42
|
|
|
43
43
|
const BottomNavigation = () => {
|
|
44
44
|
const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
|
|
45
|
+
const { authStrategy } = useB3Config();
|
|
46
|
+
const isBetterAuth = authStrategy === "better-auth";
|
|
45
47
|
|
|
46
48
|
return (
|
|
47
49
|
<div className="b3-modal-bottom-navigation sticky bottom-0 left-0 w-full rounded-b-xl border-t border-gray-200 bg-[#FAFAFA]">
|
|
@@ -54,19 +56,21 @@ const BottomNavigation = () => {
|
|
|
54
56
|
<span className="text-b3-grey font-neue-montreal-semibold text-xs">Home</span>
|
|
55
57
|
</TabTriggerPrimitive>
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
{!isBetterAuth && (
|
|
60
|
+
<TabTriggerPrimitive
|
|
61
|
+
value="swap"
|
|
62
|
+
className="data-[state=active]:border-b3-primary-blue group flex flex-initial flex-col items-center gap-1 border-r-0 border-t-0 px-6 pb-2 pt-2.5 text-[#a0a0ab] data-[state=active]:border-t-4 data-[state=active]:text-[#18181B] dark:data-[state=active]:text-white"
|
|
63
|
+
onClick={() => {
|
|
64
|
+
setB3ModalContentType({
|
|
65
|
+
type: "anySpend",
|
|
66
|
+
showBackButton: true,
|
|
67
|
+
});
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<SwapIcon />
|
|
71
|
+
<span className="text-b3-grey font-neue-montreal-semibold text-xs">Swap</span>
|
|
72
|
+
</TabTriggerPrimitive>
|
|
73
|
+
)}
|
|
70
74
|
|
|
71
75
|
<TabTriggerPrimitive
|
|
72
76
|
value="settings"
|
|
@@ -2,10 +2,13 @@ import {
|
|
|
2
2
|
CopyToClipboard,
|
|
3
3
|
ManageAccountModalProps,
|
|
4
4
|
useAuthentication,
|
|
5
|
+
useB3Config,
|
|
5
6
|
useModalStore,
|
|
6
7
|
} from "@b3dotfun/sdk/global-account/react";
|
|
8
|
+
import { AVATAR_COLORS } from "@b3dotfun/sdk/shared/constants";
|
|
7
9
|
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
8
10
|
import { AnimatePresence, motion } from "framer-motion";
|
|
11
|
+
import Avatar from "boring-avatars";
|
|
9
12
|
import { Loader2 } from "lucide-react";
|
|
10
13
|
import { useState } from "react";
|
|
11
14
|
import { useActiveWallet, useConnectedWallets, useSetActiveWallet, useWalletImage } from "thirdweb/react";
|
|
@@ -89,7 +92,71 @@ function WalletItem({ wallet, isActive, onClick }: { wallet: Wallet; isActive: b
|
|
|
89
92
|
);
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
function BetterAuthHeader({ onLogout }: { onLogout?: () => void }) {
|
|
96
|
+
const contentType = useModalStore(state => state.contentType) as ManageAccountModalProps;
|
|
97
|
+
const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
|
|
98
|
+
const partnerId = contentType?.partnerId;
|
|
99
|
+
const { logout, user } = useAuthentication(partnerId);
|
|
100
|
+
const [logoutLoading, setLogoutLoading] = useState(false);
|
|
101
|
+
|
|
102
|
+
const displayName = user?.username || user?.email || "Account";
|
|
103
|
+
|
|
104
|
+
const onLogoutEnhanced = async () => {
|
|
105
|
+
setLogoutLoading(true);
|
|
106
|
+
try {
|
|
107
|
+
await logout();
|
|
108
|
+
onLogout?.();
|
|
109
|
+
} finally {
|
|
110
|
+
setB3ModalOpen(false);
|
|
111
|
+
setLogoutLoading(false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div className="bg-b3-background border-b3-line flex items-center justify-between border-b px-5 py-3">
|
|
117
|
+
<div className="flex items-center gap-2">
|
|
118
|
+
<div className="size-10 shrink-0 overflow-hidden rounded-full">
|
|
119
|
+
<Avatar name={displayName} variant="beam" size={40} colors={AVATAR_COLORS} />
|
|
120
|
+
</div>
|
|
121
|
+
<div className="flex flex-col gap-0.5">
|
|
122
|
+
{user?.username && (
|
|
123
|
+
<p className="text-b3-grey font-neue-montreal-semibold text-left text-sm">{user.username}</p>
|
|
124
|
+
)}
|
|
125
|
+
{user?.email && (
|
|
126
|
+
<div className="flex items-center gap-1">
|
|
127
|
+
<p className="text-b3-foreground-muted font-neue-montreal-medium text-sm">{user.email}</p>
|
|
128
|
+
<CopyToClipboard text={user.email} />
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<button
|
|
134
|
+
className="border-b3-line hover:bg-b3-line flex items-center justify-center gap-1.5 rounded-xl border border-solid px-3 py-2 transition-colors"
|
|
135
|
+
onClick={onLogoutEnhanced}
|
|
136
|
+
disabled={logoutLoading}
|
|
137
|
+
>
|
|
138
|
+
{logoutLoading ? (
|
|
139
|
+
<Loader2 className="animate-spin" size={16} />
|
|
140
|
+
) : (
|
|
141
|
+
<SignOutIcon size={16} className="text-b3-grey" />
|
|
142
|
+
)}
|
|
143
|
+
<p className="text-b3-grey font-neue-montreal-semibold text-sm">Sign out</p>
|
|
144
|
+
</button>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
92
149
|
export function Header({ onLogout }: { onLogout?: () => void }) {
|
|
150
|
+
const { authStrategy } = useB3Config();
|
|
151
|
+
|
|
152
|
+
if (authStrategy === "better-auth") {
|
|
153
|
+
return <BetterAuthHeader onLogout={onLogout} />;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return <WalletHeader onLogout={onLogout} />;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function WalletHeader({ onLogout }: { onLogout?: () => void }) {
|
|
93
160
|
const activeWallet = useActiveWallet();
|
|
94
161
|
|
|
95
162
|
const connectedWallets = useConnectedWallets();
|
|
@@ -110,10 +177,13 @@ export function Header({ onLogout }: { onLogout?: () => void }) {
|
|
|
110
177
|
|
|
111
178
|
const onLogoutEnhanced = async () => {
|
|
112
179
|
setLogoutLoading(true);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
180
|
+
try {
|
|
181
|
+
await logout();
|
|
182
|
+
onLogout?.();
|
|
183
|
+
} finally {
|
|
184
|
+
setB3ModalOpen(false);
|
|
185
|
+
setLogoutLoading(false);
|
|
186
|
+
}
|
|
117
187
|
};
|
|
118
188
|
|
|
119
189
|
const handleWalletSwitch = (wallet: Wallet) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useB3Config } from "@b3dotfun/sdk/global-account/react";
|
|
1
2
|
import { Tabs, TabsContent, TabsList, TabTrigger } from "../ui/Tabs";
|
|
2
3
|
import { Header } from "./Header";
|
|
3
4
|
import HomeActions from "./HomeActions";
|
|
@@ -11,31 +12,36 @@ interface HomeContentProps {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function HomeContent({ showDeposit = false, showSwap = true }: HomeContentProps) {
|
|
15
|
+
const { authStrategy } = useB3Config();
|
|
16
|
+
const isBetterAuth = authStrategy === "better-auth";
|
|
17
|
+
|
|
14
18
|
return (
|
|
15
19
|
<div className="flex flex-col">
|
|
16
20
|
<Header />
|
|
17
21
|
<div className="flex flex-col">
|
|
18
22
|
<ProfileSection />
|
|
19
23
|
|
|
20
|
-
<HomeActions showDeposit={showDeposit} showSwap={showSwap} />
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
{!isBetterAuth && <HomeActions showDeposit={showDeposit} showSwap={showSwap} />}
|
|
25
|
+
{!isBetterAuth && (
|
|
26
|
+
<div className="b3-modal-balance-content space-y-2 p-5">
|
|
27
|
+
<Tabs defaultValue={"balance"}>
|
|
28
|
+
<TabsList className="b3-modal-balance-tabs-list">
|
|
29
|
+
<TabTrigger value="balance" className="font-neue-montreal-semibold p-0 pr-3">
|
|
30
|
+
Balance
|
|
31
|
+
</TabTrigger>
|
|
32
|
+
<TabTrigger value="nfts" className="font-neue-montreal-semibold p-0 pr-3">
|
|
33
|
+
NFTs
|
|
34
|
+
</TabTrigger>
|
|
35
|
+
</TabsList>
|
|
36
|
+
<TabsContent value="balance" className="px-0 pb-4 pt-2">
|
|
37
|
+
<TokenContent />
|
|
38
|
+
</TabsContent>
|
|
39
|
+
<TabsContent value="nfts" className="px-0 pb-4 pt-2">
|
|
40
|
+
<NFTContent />
|
|
41
|
+
</TabsContent>
|
|
42
|
+
</Tabs>
|
|
43
|
+
</div>
|
|
44
|
+
)}
|
|
39
45
|
</div>
|
|
40
46
|
</div>
|
|
41
47
|
);
|
|
@@ -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,4 +1,4 @@
|
|
|
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
4
|
import { Loader2 } from "lucide-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);
|
|
@@ -69,19 +71,21 @@ const SettingsContent = ({
|
|
|
69
71
|
|
|
70
72
|
{/* Menu Items */}
|
|
71
73
|
<div className="space-y-3 px-5">
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
{!isBetterAuth && (
|
|
75
|
+
<SettingsMenuItem
|
|
76
|
+
icon={
|
|
77
|
+
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
78
|
+
<path
|
|
79
|
+
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"
|
|
80
|
+
fill="#F4F4F5"
|
|
81
|
+
/>
|
|
82
|
+
</svg>
|
|
83
|
+
}
|
|
84
|
+
title="Linked Accounts"
|
|
85
|
+
subtitle={`${profiles.length} connected account${profiles.length > 1 ? "s" : ""}`}
|
|
86
|
+
onClick={() => handleNavigate("linkAccount")}
|
|
87
|
+
/>
|
|
88
|
+
)}
|
|
85
89
|
<SettingsMenuItem
|
|
86
90
|
icon={
|
|
87
91
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -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
|
|
@@ -33,6 +33,11 @@ export interface BetterAuthSignInProps {
|
|
|
33
33
|
showEmail?: boolean;
|
|
34
34
|
/** URL to redirect to after password reset link is clicked. Token is appended as ?token=... */
|
|
35
35
|
passwordResetRedirectTo?: string;
|
|
36
|
+
/**
|
|
37
|
+
* URL Better Auth redirects to after server-side email verification. Render
|
|
38
|
+
* `BetterAuthVerifyEmail` at this route so the user gets a confirmation page.
|
|
39
|
+
*/
|
|
40
|
+
verifyEmailRedirectTo?: string;
|
|
36
41
|
/** Called after successful authentication */
|
|
37
42
|
onSuccess?: () => void;
|
|
38
43
|
/** Called on authentication error */
|
|
@@ -61,6 +66,7 @@ export function BetterAuthSignIn({
|
|
|
61
66
|
socialProviders = DEFAULT_SOCIAL_PROVIDERS.map(p => p.id),
|
|
62
67
|
showEmail = true,
|
|
63
68
|
passwordResetRedirectTo,
|
|
69
|
+
verifyEmailRedirectTo,
|
|
64
70
|
onSuccess,
|
|
65
71
|
onError,
|
|
66
72
|
className,
|
|
@@ -153,7 +159,7 @@ export function BetterAuthSignIn({
|
|
|
153
159
|
setError(null);
|
|
154
160
|
|
|
155
161
|
if (mode === "sign-up") {
|
|
156
|
-
await signUpWithEmail(normalizedEmail, password, name.trim());
|
|
162
|
+
await signUpWithEmail(normalizedEmail, password, name.trim(), verifyEmailRedirectTo);
|
|
157
163
|
} else {
|
|
158
164
|
await signInWithEmail(normalizedEmail, password);
|
|
159
165
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Button } from "@b3dotfun/sdk/global-account/react";
|
|
2
|
+
import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
3
|
+
|
|
4
|
+
const debug = debugB3React("BetterAuthVerifyEmail");
|
|
5
|
+
|
|
6
|
+
export type BetterAuthVerifyEmailState = "success" | "expired" | "invalid" | "already-verified" | "error";
|
|
7
|
+
|
|
8
|
+
export interface BetterAuthVerifyEmailProps {
|
|
9
|
+
/**
|
|
10
|
+
* Error code from the callback URL's `?error=` query param. Pass `null` /
|
|
11
|
+
* `undefined` when the user landed here cleanly (successful verification).
|
|
12
|
+
* Better Auth appends this param when server-side verification fails.
|
|
13
|
+
*/
|
|
14
|
+
errorCode?: string | null;
|
|
15
|
+
/** Called when the user clicks the "Go to sign in" button. */
|
|
16
|
+
onGoToSignIn?: () => void;
|
|
17
|
+
/** Fallback href used when `onGoToSignIn` is not provided. Defaults to "/login". */
|
|
18
|
+
signInHref?: string;
|
|
19
|
+
/** Optional override for the success headline. */
|
|
20
|
+
successTitle?: string;
|
|
21
|
+
/** Optional override for the success body text. */
|
|
22
|
+
successMessage?: string;
|
|
23
|
+
/** Optional class name for the root container. */
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function classifyError(code: string | null | undefined): BetterAuthVerifyEmailState {
|
|
28
|
+
if (!code) return "success";
|
|
29
|
+
const normalized = code.toLowerCase();
|
|
30
|
+
|
|
31
|
+
// Exact matches for Better Auth's documented verification error codes.
|
|
32
|
+
if (normalized === "expired_token") return "expired";
|
|
33
|
+
if (normalized === "invalid_token") return "invalid";
|
|
34
|
+
if (normalized === "already_verified" || normalized === "email_already_verified") return "already-verified";
|
|
35
|
+
|
|
36
|
+
// Loose fallbacks for close variants. Order matters — check "already" before
|
|
37
|
+
// "verified" so `email_already_verified` maps to already-verified, not invalid.
|
|
38
|
+
if (normalized.includes("expired")) return "expired";
|
|
39
|
+
if (normalized.includes("already")) return "already-verified";
|
|
40
|
+
if (normalized.includes("invalid")) return "invalid";
|
|
41
|
+
return "error";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const COPY: Record<BetterAuthVerifyEmailState, { title: string; message: string }> = {
|
|
45
|
+
success: {
|
|
46
|
+
title: "Email verified",
|
|
47
|
+
message: "Your email is confirmed. You can now sign in to your account.",
|
|
48
|
+
},
|
|
49
|
+
expired: {
|
|
50
|
+
title: "Link expired",
|
|
51
|
+
message: "This verification link has expired. Request a new one from the sign-in page.",
|
|
52
|
+
},
|
|
53
|
+
invalid: {
|
|
54
|
+
title: "Invalid link",
|
|
55
|
+
message: "This verification link is invalid or has already been used. Try signing in or request a new link.",
|
|
56
|
+
},
|
|
57
|
+
"already-verified": {
|
|
58
|
+
title: "Already verified",
|
|
59
|
+
message: "Your email was already confirmed. You can sign in now.",
|
|
60
|
+
},
|
|
61
|
+
error: {
|
|
62
|
+
title: "Verification failed",
|
|
63
|
+
message: "We couldn't verify your email. Request a new link from the sign-in page.",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Standalone email-verification confirmation page. Render on the route you
|
|
69
|
+
* set as `callbackURL` when calling `betterAuthClient.sendVerificationEmail`
|
|
70
|
+
* (or the `verifyCallbackURL` arg on `useBetterAuth().signUpWithEmail`).
|
|
71
|
+
*
|
|
72
|
+
* Better Auth verifies the token server-side before redirecting here. This
|
|
73
|
+
* component only displays the outcome based on the `?error=` query param.
|
|
74
|
+
*
|
|
75
|
+
* Usage:
|
|
76
|
+
* ```tsx
|
|
77
|
+
* const error = new URLSearchParams(window.location.search).get("error");
|
|
78
|
+
* <BetterAuthVerifyEmail
|
|
79
|
+
* errorCode={error}
|
|
80
|
+
* onGoToSignIn={() => router.push("/login")}
|
|
81
|
+
* />
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function BetterAuthVerifyEmail({
|
|
85
|
+
errorCode,
|
|
86
|
+
onGoToSignIn,
|
|
87
|
+
signInHref = "/login",
|
|
88
|
+
successTitle,
|
|
89
|
+
successMessage,
|
|
90
|
+
className,
|
|
91
|
+
}: BetterAuthVerifyEmailProps) {
|
|
92
|
+
const state = classifyError(errorCode);
|
|
93
|
+
const isSuccess = state === "success" || state === "already-verified";
|
|
94
|
+
const copy = COPY[state];
|
|
95
|
+
const title = isSuccess && successTitle ? successTitle : copy.title;
|
|
96
|
+
const message = isSuccess && successMessage ? successMessage : copy.message;
|
|
97
|
+
|
|
98
|
+
debug("Rendering verify-email state", { state, errorCode });
|
|
99
|
+
|
|
100
|
+
const handleClick = () => {
|
|
101
|
+
if (onGoToSignIn) {
|
|
102
|
+
onGoToSignIn();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (typeof window !== "undefined") {
|
|
106
|
+
window.location.href = signInHref;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div className={`w-full max-w-[400px] px-6 ${className || ""}`}>
|
|
112
|
+
<div className="space-y-6 text-center">
|
|
113
|
+
<div
|
|
114
|
+
className={`mx-auto flex h-12 w-12 items-center justify-center rounded-full ${
|
|
115
|
+
isSuccess ? "bg-green-100" : "bg-red-100"
|
|
116
|
+
}`}
|
|
117
|
+
>
|
|
118
|
+
{isSuccess ? (
|
|
119
|
+
<svg
|
|
120
|
+
className="h-6 w-6 text-green-600"
|
|
121
|
+
fill="none"
|
|
122
|
+
viewBox="0 0 24 24"
|
|
123
|
+
stroke="currentColor"
|
|
124
|
+
aria-hidden="true"
|
|
125
|
+
>
|
|
126
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
127
|
+
</svg>
|
|
128
|
+
) : (
|
|
129
|
+
<svg
|
|
130
|
+
className="h-6 w-6 text-red-600"
|
|
131
|
+
fill="none"
|
|
132
|
+
viewBox="0 0 24 24"
|
|
133
|
+
stroke="currentColor"
|
|
134
|
+
aria-hidden="true"
|
|
135
|
+
>
|
|
136
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
137
|
+
</svg>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div>
|
|
142
|
+
<h1 className="text-[28px] font-semibold tracking-tight text-gray-900 dark:text-gray-100">{title}</h1>
|
|
143
|
+
<p className="mt-3 text-[15px] text-gray-500 dark:text-gray-400">{message}</p>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<Button
|
|
147
|
+
onClick={handleClick}
|
|
148
|
+
className="h-11 w-full bg-gray-900 text-[15px] font-medium text-white hover:bg-gray-800 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-100"
|
|
149
|
+
>
|
|
150
|
+
{isSuccess ? "Go to sign in" : "Back to sign in"}
|
|
151
|
+
</Button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|