@b3dotfun/sdk 0.0.77 → 0.0.78-alpha.0
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/B3DynamicModal.js +18 -4
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.d.ts +4 -2
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +6 -3
- package/dist/cjs/global-account/react/components/B3Provider/types.d.ts +1 -0
- package/dist/cjs/global-account/react/components/B3Provider/types.js +1 -0
- package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +2 -2
- package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +162 -46
- package/dist/cjs/global-account/react/components/TurnkeyAuthModal.d.ts +8 -0
- package/dist/cjs/global-account/react/components/TurnkeyAuthModal.js +84 -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/global-account/react/hooks/index.d.ts +1 -0
- package/dist/cjs/global-account/react/hooks/index.js +3 -1
- package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +7 -0
- package/dist/cjs/global-account/react/hooks/useTurnkeyAuth.d.ts +20 -0
- package/dist/cjs/global-account/react/hooks/useTurnkeyAuth.js +112 -0
- package/dist/cjs/global-account/react/hooks/useUserQuery.d.ts +7 -0
- package/dist/cjs/global-account/react/stores/useAuthStore.d.ts +2 -0
- package/dist/cjs/global-account/react/stores/useAuthStore.js +2 -0
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +21 -1
- package/dist/esm/global-account/react/components/B3DynamicModal.js +18 -4
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.d.ts +4 -2
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +6 -3
- package/dist/esm/global-account/react/components/B3Provider/types.d.ts +1 -0
- package/dist/esm/global-account/react/components/B3Provider/types.js +1 -0
- package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +2 -2
- package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +162 -46
- package/dist/esm/global-account/react/components/TurnkeyAuthModal.d.ts +8 -0
- package/dist/esm/global-account/react/components/TurnkeyAuthModal.js +81 -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/global-account/react/hooks/index.d.ts +1 -0
- package/dist/esm/global-account/react/hooks/index.js +1 -0
- package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +7 -0
- package/dist/esm/global-account/react/hooks/useTurnkeyAuth.d.ts +20 -0
- package/dist/esm/global-account/react/hooks/useTurnkeyAuth.js +106 -0
- package/dist/esm/global-account/react/hooks/useUserQuery.d.ts +7 -0
- package/dist/esm/global-account/react/stores/useAuthStore.d.ts +2 -0
- package/dist/esm/global-account/react/stores/useAuthStore.js +2 -0
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +21 -1
- package/dist/styles/index.css +1 -1
- package/dist/types/global-account/react/components/B3Provider/B3Provider.d.ts +4 -2
- package/dist/types/global-account/react/components/B3Provider/types.d.ts +1 -0
- package/dist/types/global-account/react/components/TurnkeyAuthModal.d.ts +8 -0
- package/dist/types/global-account/react/components/index.d.ts +1 -0
- package/dist/types/global-account/react/hooks/index.d.ts +1 -0
- package/dist/types/global-account/react/hooks/useAuthentication.d.ts +7 -0
- package/dist/types/global-account/react/hooks/useTurnkeyAuth.d.ts +20 -0
- package/dist/types/global-account/react/hooks/useUserQuery.d.ts +7 -0
- package/dist/types/global-account/react/stores/useAuthStore.d.ts +2 -0
- package/dist/types/global-account/react/stores/useModalStore.d.ts +21 -1
- package/package.json +2 -2
- package/src/global-account/react/components/B3DynamicModal.tsx +26 -3
- package/src/global-account/react/components/B3Provider/B3Provider.tsx +9 -0
- package/src/global-account/react/components/B3Provider/types.ts +2 -0
- package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +3 -3
- package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +170 -48
- package/src/global-account/react/components/TurnkeyAuthModal.tsx +240 -0
- package/src/global-account/react/components/index.ts +3 -0
- package/src/global-account/react/hooks/index.ts +1 -0
- package/src/global-account/react/hooks/useTurnkeyAuth.ts +138 -0
- package/src/global-account/react/stores/useAuthStore.ts +4 -0
- package/src/global-account/react/stores/useModalStore.ts +22 -0
|
@@ -27,6 +27,8 @@ interface AuthState {
|
|
|
27
27
|
setIsAuthenticating: (isAuthenticating: boolean) => void;
|
|
28
28
|
hasStartedConnecting: boolean;
|
|
29
29
|
setHasStartedConnecting: (hasStartedConnecting: boolean) => void;
|
|
30
|
+
justCompletedLogin: boolean;
|
|
31
|
+
setJustCompletedLogin: (justCompletedLogin: boolean) => void;
|
|
30
32
|
}
|
|
31
33
|
export declare const useAuthStore: import("zustand").UseBoundStore<import("zustand").StoreApi<AuthState>>;
|
|
32
34
|
export {};
|
|
@@ -10,6 +10,8 @@ import { Account } from "thirdweb/wallets";
|
|
|
10
10
|
interface BaseModalProps {
|
|
11
11
|
/** Whether to show a back button in the modal header */
|
|
12
12
|
showBackButton?: boolean;
|
|
13
|
+
/** Whether the modal can be closed by clicking outside or pressing escape. Defaults to true */
|
|
14
|
+
closable?: boolean;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Props for the Sign In With B3 modal
|
|
@@ -39,6 +41,24 @@ export interface SignInWithB3ModalProps extends BaseModalProps {
|
|
|
39
41
|
/** Whether to show the signers enabled modal */
|
|
40
42
|
signersEnabled?: boolean;
|
|
41
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Props for the Turnkey Authentication modal
|
|
46
|
+
* Handles Turnkey email/OTP authentication flow
|
|
47
|
+
*/
|
|
48
|
+
export interface TurnkeyAuthModalProps extends BaseModalProps {
|
|
49
|
+
/** Modal type identifier */
|
|
50
|
+
type: "turnkeyAuth";
|
|
51
|
+
/** Callback function called when authentication is successful */
|
|
52
|
+
onSuccess: (_user: any) => void;
|
|
53
|
+
/** Callback function called when modal is closed */
|
|
54
|
+
onClose: () => void;
|
|
55
|
+
/** Initial email to pre-fill */
|
|
56
|
+
initialEmail?: string;
|
|
57
|
+
/** Whether to skip directly to OTP step */
|
|
58
|
+
skipToOtp?: boolean;
|
|
59
|
+
/** Whether the modal can be closed - defaults to false for Turnkey */
|
|
60
|
+
closable?: boolean;
|
|
61
|
+
}
|
|
42
62
|
/**
|
|
43
63
|
* Props for the Request Permissions modal
|
|
44
64
|
* Used to request permission for session keys to interact with contracts
|
|
@@ -451,7 +471,7 @@ export interface AnySpendCollectorClubPurchaseProps extends BaseModalProps {
|
|
|
451
471
|
/**
|
|
452
472
|
* Union type of all possible modal content types
|
|
453
473
|
*/
|
|
454
|
-
export type ModalContentType = SignInWithB3ModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeB3ExactInProps | AnySpendStakeUpsideProps | AnySpendStakeUpsideExactInProps | AnySpendDepositUpsideProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | LinkNewAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps | DepositModalProps | SendModalProps | NotificationsModalProps | AnySpendCollectorClubPurchaseProps;
|
|
474
|
+
export type ModalContentType = SignInWithB3ModalProps | TurnkeyAuthModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeB3ExactInProps | AnySpendStakeUpsideProps | AnySpendStakeUpsideExactInProps | AnySpendDepositUpsideProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | LinkNewAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps | DepositModalProps | SendModalProps | NotificationsModalProps | AnySpendCollectorClubPurchaseProps;
|
|
455
475
|
/**
|
|
456
476
|
* State interface for the modal store
|
|
457
477
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b3dotfun/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.78-alpha.0",
|
|
4
4
|
"source": "src/index.ts",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"react-native": "./dist/cjs/index.native.js",
|
|
@@ -283,7 +283,7 @@
|
|
|
283
283
|
],
|
|
284
284
|
"dependencies": {
|
|
285
285
|
"@adraffy/ens-normalize": "1.11.1",
|
|
286
|
-
"@b3dotfun/b3-api": "0.0.
|
|
286
|
+
"@b3dotfun/b3-api": "0.0.89",
|
|
287
287
|
"@b3dotfun/basement-api": "0.0.11",
|
|
288
288
|
"@feathersjs/authentication-client": "5.0.33",
|
|
289
289
|
"@feathersjs/feathers": "5.0.33",
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
OrderHistory,
|
|
12
12
|
} from "@b3dotfun/sdk/anyspend/react";
|
|
13
13
|
import { AnySpendDepositHype } from "@b3dotfun/sdk/anyspend/react/components/AnyspendDepositHype";
|
|
14
|
+
import { AnySpendDepositUpside } from "@b3dotfun/sdk/anyspend/react/components/AnySpendDepositUpside";
|
|
14
15
|
import { AnySpendStakeUpside } from "@b3dotfun/sdk/anyspend/react/components/AnySpendStakeUpside";
|
|
15
16
|
import { AnySpendStakeUpsideExactIn } from "@b3dotfun/sdk/anyspend/react/components/AnySpendStakeUpsideExactIn";
|
|
16
17
|
import { useGlobalAccount, useIsMobile, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
@@ -29,10 +30,10 @@ import NotificationsContent from "./ManageAccount/NotificationsContent";
|
|
|
29
30
|
import { RequestPermissions } from "./RequestPermissions/RequestPermissions";
|
|
30
31
|
import { Send } from "./Send/Send";
|
|
31
32
|
import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow";
|
|
33
|
+
import { TurnkeyAuthModal } from "./TurnkeyAuthModal";
|
|
32
34
|
import { ToastContainer, useToastContext } from "./Toast/index";
|
|
33
35
|
import { Dialog, DialogContent, DialogDescription, DialogTitle } from "./ui/dialog";
|
|
34
36
|
import { Drawer, DrawerContent, DrawerDescription, DrawerTitle } from "./ui/drawer";
|
|
35
|
-
import { AnySpendDepositUpside } from "@b3dotfun/sdk/anyspend/react/components/AnySpendDepositUpside";
|
|
36
37
|
|
|
37
38
|
const debug = debugB3React("B3DynamicModal");
|
|
38
39
|
|
|
@@ -72,6 +73,7 @@ export function B3DynamicModal() {
|
|
|
72
73
|
"anySpendBuySpin",
|
|
73
74
|
"anySpendOrderHistory",
|
|
74
75
|
"signInWithB3",
|
|
76
|
+
"turnkeyAuth",
|
|
75
77
|
"anySpendSignatureMint",
|
|
76
78
|
"anySpendBondKit",
|
|
77
79
|
"linkAccount",
|
|
@@ -97,6 +99,8 @@ export function B3DynamicModal() {
|
|
|
97
99
|
|
|
98
100
|
// Check if current content type is in freestyle types
|
|
99
101
|
const isFreestyleType = freestyleTypes.includes(contentType?.type as string);
|
|
102
|
+
// Determine if modal should be closable - defaults to true unless explicitly set to false
|
|
103
|
+
const isClosable = contentType?.closable !== false;
|
|
100
104
|
const hideCloseButton = true;
|
|
101
105
|
|
|
102
106
|
// Build content class using cn utility
|
|
@@ -107,6 +111,7 @@ export function B3DynamicModal() {
|
|
|
107
111
|
fullWidthTypes.includes(contentType?.type as string) && "w-full",
|
|
108
112
|
isFreestyleType && "b3-modal-freestyle",
|
|
109
113
|
contentType?.type === "signInWithB3" && "p-0",
|
|
114
|
+
contentType?.type === "turnkeyAuth" && "p-0",
|
|
110
115
|
contentType?.type === "anySpend" && "md:p-0",
|
|
111
116
|
contentType?.type === "send" && "p-0",
|
|
112
117
|
contentType?.type === "manageAccount" && " md:p-0 md:pt-2",
|
|
@@ -123,6 +128,8 @@ export function B3DynamicModal() {
|
|
|
123
128
|
switch (contentType.type) {
|
|
124
129
|
case "signInWithB3":
|
|
125
130
|
return <SignInWithB3Flow {...contentType} />;
|
|
131
|
+
case "turnkeyAuth":
|
|
132
|
+
return <TurnkeyAuthModal {...contentType} />;
|
|
126
133
|
case "requestPermissions":
|
|
127
134
|
return <RequestPermissions {...contentType} />;
|
|
128
135
|
case "manageAccount":
|
|
@@ -182,8 +189,17 @@ export function B3DynamicModal() {
|
|
|
182
189
|
const ModalTitle = isMobile ? DrawerTitle : DialogTitle;
|
|
183
190
|
const ModalDescription = isMobile ? DrawerDescription : DialogDescription;
|
|
184
191
|
|
|
192
|
+
// Create a wrapper for onOpenChange that respects closable property
|
|
193
|
+
const handleOpenChange = (open: boolean) => {
|
|
194
|
+
// Only allow closing if the modal is closable
|
|
195
|
+
if (!open && !isClosable) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
setB3ModalOpen(open);
|
|
199
|
+
};
|
|
200
|
+
|
|
185
201
|
return (
|
|
186
|
-
<ModalComponent open={isOpen} onOpenChange={
|
|
202
|
+
<ModalComponent open={isOpen} onOpenChange={handleOpenChange}>
|
|
187
203
|
<ModalContent
|
|
188
204
|
className={cn(
|
|
189
205
|
contentClass,
|
|
@@ -198,11 +214,18 @@ export function B3DynamicModal() {
|
|
|
198
214
|
"mx-auto w-full max-w-md sm:max-w-lg",
|
|
199
215
|
)}
|
|
200
216
|
hideCloseButton={hideCloseButton}
|
|
217
|
+
onEscapeKeyDown={!isClosable ? e => e.preventDefault() : undefined}
|
|
218
|
+
onPointerDownOutside={!isClosable ? e => e.preventDefault() : undefined}
|
|
219
|
+
onInteractOutside={!isClosable ? e => e.preventDefault() : undefined}
|
|
201
220
|
>
|
|
202
221
|
<ModalTitle className="sr-only hidden">{contentType?.type || "Modal"}</ModalTitle>
|
|
203
222
|
<ModalDescription className="sr-only hidden">{contentType?.type || "Modal Body"}</ModalDescription>
|
|
204
223
|
|
|
205
|
-
<div
|
|
224
|
+
<div
|
|
225
|
+
className={cn(
|
|
226
|
+
"b3-modal-content no-scrollbar dark:bg-b3-background flex max-h-[90dvh] flex-col overflow-auto sm:max-h-[80dvh]",
|
|
227
|
+
)}
|
|
228
|
+
>
|
|
206
229
|
{!hideCloseButton && (
|
|
207
230
|
<button
|
|
208
231
|
onClick={navigateBack}
|
|
@@ -62,6 +62,7 @@ export function B3Provider({
|
|
|
62
62
|
connectors,
|
|
63
63
|
overrideDefaultConnectors = false,
|
|
64
64
|
createClientReferenceId,
|
|
65
|
+
enableTurnkey = false,
|
|
65
66
|
}: {
|
|
66
67
|
theme: "light" | "dark";
|
|
67
68
|
children: React.ReactNode;
|
|
@@ -80,6 +81,7 @@ export function B3Provider({
|
|
|
80
81
|
connectors?: CreateConnectorFn[];
|
|
81
82
|
overrideDefaultConnectors?: boolean;
|
|
82
83
|
createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
|
|
84
|
+
enableTurnkey?: boolean;
|
|
83
85
|
}) {
|
|
84
86
|
// Initialize Google Analytics on mount
|
|
85
87
|
useEffect(() => {
|
|
@@ -107,6 +109,7 @@ export function B3Provider({
|
|
|
107
109
|
clientType={clientType}
|
|
108
110
|
partnerId={partnerId}
|
|
109
111
|
createClientReferenceId={createClientReferenceId}
|
|
112
|
+
enableTurnkey={enableTurnkey}
|
|
110
113
|
>
|
|
111
114
|
<ToastContextConnector />
|
|
112
115
|
<RelayKitProviderWrapper simDuneApiKey={simDuneApiKey}>
|
|
@@ -137,6 +140,7 @@ export function InnerProvider({
|
|
|
137
140
|
clientType = "socket",
|
|
138
141
|
partnerId,
|
|
139
142
|
createClientReferenceId,
|
|
143
|
+
enableTurnkey,
|
|
140
144
|
}: {
|
|
141
145
|
children: React.ReactNode;
|
|
142
146
|
accountOverride?: Account;
|
|
@@ -147,17 +151,21 @@ export function InnerProvider({
|
|
|
147
151
|
clientType?: ClientType;
|
|
148
152
|
partnerId: string;
|
|
149
153
|
createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
|
|
154
|
+
enableTurnkey?: boolean;
|
|
150
155
|
}) {
|
|
151
156
|
const activeAccount = useActiveAccount();
|
|
152
157
|
const [manuallySelectedWallet, setManuallySelectedWallet] = useState<Wallet | undefined>(undefined);
|
|
153
158
|
const wallets = useConnectedWallets();
|
|
154
159
|
const isAuthenticated = useAuthStore(state => state.isAuthenticated);
|
|
155
160
|
const isConnected = useAuthStore(state => state.isConnected);
|
|
161
|
+
const justCompletedLogin = useAuthStore(state => state.justCompletedLogin);
|
|
156
162
|
const setActiveWallet = useSetActiveWallet();
|
|
157
163
|
const { user, setUser, refetchUser } = useAuthentication(partnerId);
|
|
164
|
+
|
|
158
165
|
debug("@@B3Provider:isConnected", isConnected);
|
|
159
166
|
debug("@@wallets", wallets);
|
|
160
167
|
debug("@@B3Provider:user", user);
|
|
168
|
+
debug("@@B3Provider:justCompletedLogin", justCompletedLogin);
|
|
161
169
|
|
|
162
170
|
// Use given accountOverride or activeAccount from thirdweb
|
|
163
171
|
const effectiveAccount = isAuthenticated ? accountOverride || activeAccount : undefined;
|
|
@@ -215,6 +223,7 @@ export function InnerProvider({
|
|
|
215
223
|
clientType,
|
|
216
224
|
partnerId: partnerId,
|
|
217
225
|
createClientReferenceId,
|
|
226
|
+
enableTurnkey,
|
|
218
227
|
}}
|
|
219
228
|
>
|
|
220
229
|
<InnerProvider2>{children}</InnerProvider2>
|
|
@@ -25,6 +25,7 @@ export interface B3ContextType {
|
|
|
25
25
|
clientType: ClientType;
|
|
26
26
|
partnerId: string;
|
|
27
27
|
createClientReferenceId?: (params: CreateOrderParams | CreateOnrampOrderParams) => Promise<string>;
|
|
28
|
+
enableTurnkey?: boolean;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -45,4 +46,5 @@ export const B3Context = createContext<B3ContextType>({
|
|
|
45
46
|
clientType: "rest",
|
|
46
47
|
partnerId: "",
|
|
47
48
|
createClientReferenceId: undefined,
|
|
49
|
+
enableTurnkey: false,
|
|
48
50
|
});
|
|
@@ -48,7 +48,7 @@ const BottomNavigation = () => {
|
|
|
48
48
|
<TabsListPrimitive className="flex h-[68px] w-full items-center justify-center gap-4 border-none bg-transparent">
|
|
49
49
|
<TabTriggerPrimitive
|
|
50
50
|
value="home"
|
|
51
|
-
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]"
|
|
51
|
+
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"
|
|
52
52
|
>
|
|
53
53
|
<HomeIcon />
|
|
54
54
|
<span className="text-b3-grey font-neue-montreal-semibold text-xs">Home</span>
|
|
@@ -56,7 +56,7 @@ const BottomNavigation = () => {
|
|
|
56
56
|
|
|
57
57
|
<TabTriggerPrimitive
|
|
58
58
|
value="swap"
|
|
59
|
-
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]"
|
|
59
|
+
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"
|
|
60
60
|
onClick={() => {
|
|
61
61
|
setB3ModalContentType({
|
|
62
62
|
type: "anySpend",
|
|
@@ -70,7 +70,7 @@ const BottomNavigation = () => {
|
|
|
70
70
|
|
|
71
71
|
<TabTriggerPrimitive
|
|
72
72
|
value="settings"
|
|
73
|
-
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]"
|
|
73
|
+
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"
|
|
74
74
|
>
|
|
75
75
|
<SettingsIcon />
|
|
76
76
|
<span className="text-b3-grey font-neue-montreal-semibold text-xs">Settings</span>
|
|
@@ -33,16 +33,18 @@ export function SignInWithB3Flow({
|
|
|
33
33
|
source = "signInWithB3Button",
|
|
34
34
|
signersEnabled = false,
|
|
35
35
|
}: SignInWithB3ModalProps) {
|
|
36
|
-
const { automaticallySetFirstEoa } = useB3();
|
|
36
|
+
const { automaticallySetFirstEoa, user, refetchUser, enableTurnkey } = useB3();
|
|
37
37
|
const [step, setStep] = useState<"login" | "permissions" | null>(source === "requestPermissions" ? null : "login");
|
|
38
38
|
const [sessionKeyAdded, setSessionKeyAdded] = useState(source === "requestPermissions" ? true : false);
|
|
39
|
-
const { setB3ModalContentType, setB3ModalOpen, isOpen } = useModalStore();
|
|
39
|
+
const { setB3ModalContentType, setB3ModalOpen, isOpen, contentType } = useModalStore();
|
|
40
40
|
const account = useActiveAccount();
|
|
41
41
|
const isAuthenticating = useAuthStore(state => state.isAuthenticating);
|
|
42
42
|
const isAuthenticated = useAuthStore(state => state.isAuthenticated);
|
|
43
43
|
const isConnected = useAuthStore(state => state.isConnected);
|
|
44
|
+
const setJustCompletedLogin = useAuthStore(state => state.setJustCompletedLogin);
|
|
44
45
|
const [refetchCount, setRefetchCount] = useState(0);
|
|
45
46
|
const [refetchError, setRefetchError] = useState<string | null>(null);
|
|
47
|
+
const [turnkeyAuthCompleted, setTurnkeyAuthCompleted] = useState(false);
|
|
46
48
|
const {
|
|
47
49
|
data: signers,
|
|
48
50
|
refetch: refetchSigners,
|
|
@@ -82,6 +84,109 @@ export function SignInWithB3Flow({
|
|
|
82
84
|
}, backoffDelay);
|
|
83
85
|
}, [refetchCount, refetchSigners, onError, setRefetchQueued, refetchQueued]);
|
|
84
86
|
|
|
87
|
+
// Extract the completion flow logic to be reused
|
|
88
|
+
const handlePostTurnkeyFlow = useCallback(() => {
|
|
89
|
+
debug("Running post-Turnkey flow logic");
|
|
90
|
+
|
|
91
|
+
// Check if we already have a signer for this partner
|
|
92
|
+
const hasExistingSigner = signers?.some(signer => signer.partner.id === partnerId);
|
|
93
|
+
|
|
94
|
+
if (hasExistingSigner) {
|
|
95
|
+
// Path 1: User already has a signer for this partner
|
|
96
|
+
setSessionKeyAdded(true);
|
|
97
|
+
onSessionKeySuccess?.();
|
|
98
|
+
if (closeAfterLogin) {
|
|
99
|
+
setB3ModalOpen(false);
|
|
100
|
+
} else {
|
|
101
|
+
setB3ModalContentType({
|
|
102
|
+
type: "manageAccount",
|
|
103
|
+
chain,
|
|
104
|
+
partnerId,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
} else if (signersEnabled) {
|
|
108
|
+
// Path 2: No existing signer, but signers are enabled
|
|
109
|
+
if (source !== "requestPermissions") {
|
|
110
|
+
// Navigate to permissions step to request new signer
|
|
111
|
+
setStep("permissions");
|
|
112
|
+
} else {
|
|
113
|
+
// Already in request permissions flow, retry fetching signers
|
|
114
|
+
handleRefetchSigners();
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
// Path 3: No existing signer and signers are not enabled
|
|
118
|
+
// Default handling for when no signer exists and signers are not enabled
|
|
119
|
+
if (closeAfterLogin) {
|
|
120
|
+
setB3ModalOpen(false);
|
|
121
|
+
} else {
|
|
122
|
+
// if not closed, default to manage account
|
|
123
|
+
setB3ModalContentType({
|
|
124
|
+
type: "manageAccount",
|
|
125
|
+
chain,
|
|
126
|
+
partnerId,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, [
|
|
131
|
+
signers,
|
|
132
|
+
partnerId,
|
|
133
|
+
onSessionKeySuccess,
|
|
134
|
+
closeAfterLogin,
|
|
135
|
+
setB3ModalOpen,
|
|
136
|
+
setB3ModalContentType,
|
|
137
|
+
chain,
|
|
138
|
+
source,
|
|
139
|
+
signersEnabled,
|
|
140
|
+
handleRefetchSigners,
|
|
141
|
+
setSessionKeyAdded,
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
// Define handleTurnkeySuccess before the useEffect that uses it
|
|
145
|
+
const handleTurnkeySuccess = useCallback(
|
|
146
|
+
async (user: any) => {
|
|
147
|
+
debug("Turnkey authentication successful - setting completed flag", { user });
|
|
148
|
+
|
|
149
|
+
// Set completed flag FIRST before any async operations
|
|
150
|
+
setTurnkeyAuthCompleted(true);
|
|
151
|
+
|
|
152
|
+
// Refetch user to update the user state with Turnkey ID
|
|
153
|
+
debug("Refetching user after Turnkey success...");
|
|
154
|
+
await refetchUser();
|
|
155
|
+
debug("User refetched successfully");
|
|
156
|
+
|
|
157
|
+
// After user data is refreshed, close Turnkey modal and go back to sign-in flow
|
|
158
|
+
debug("Switching back to signInWithB3 modal");
|
|
159
|
+
setB3ModalContentType({
|
|
160
|
+
type: "signInWithB3",
|
|
161
|
+
strategies,
|
|
162
|
+
onLoginSuccess,
|
|
163
|
+
onSessionKeySuccess,
|
|
164
|
+
onError,
|
|
165
|
+
chain,
|
|
166
|
+
sessionKeyAddress,
|
|
167
|
+
partnerId,
|
|
168
|
+
closeAfterLogin,
|
|
169
|
+
source,
|
|
170
|
+
signersEnabled,
|
|
171
|
+
});
|
|
172
|
+
// The useEffect will re-run with updated user data to complete the sign-in process
|
|
173
|
+
},
|
|
174
|
+
[
|
|
175
|
+
refetchUser,
|
|
176
|
+
setB3ModalContentType,
|
|
177
|
+
strategies,
|
|
178
|
+
onLoginSuccess,
|
|
179
|
+
onSessionKeySuccess,
|
|
180
|
+
onError,
|
|
181
|
+
chain,
|
|
182
|
+
sessionKeyAddress,
|
|
183
|
+
partnerId,
|
|
184
|
+
closeAfterLogin,
|
|
185
|
+
source,
|
|
186
|
+
signersEnabled,
|
|
187
|
+
],
|
|
188
|
+
);
|
|
189
|
+
|
|
85
190
|
// Handle post-login flow after signers are loaded
|
|
86
191
|
useEffect(() => {
|
|
87
192
|
debug("@@SignInWithB3Flow:useEffect", {
|
|
@@ -93,38 +198,51 @@ export function SignInWithB3Flow({
|
|
|
93
198
|
source,
|
|
94
199
|
});
|
|
95
200
|
|
|
96
|
-
if (isConnected && isAuthenticated) {
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
onSessionKeySuccess?.();
|
|
102
|
-
if (closeAfterLogin) {
|
|
103
|
-
setB3ModalOpen(false);
|
|
104
|
-
} else {
|
|
105
|
-
setB3ModalContentType({
|
|
106
|
-
type: "manageAccount",
|
|
107
|
-
chain,
|
|
108
|
-
partnerId,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
} else if (source !== "requestPermissions") {
|
|
112
|
-
if (signersEnabled) setStep("permissions");
|
|
113
|
-
} else {
|
|
114
|
-
if (signersEnabled) handleRefetchSigners();
|
|
201
|
+
if (isConnected && isAuthenticated && user) {
|
|
202
|
+
// Mark that login just completed BEFORE opening manage account or closing modal
|
|
203
|
+
// This allows Turnkey modal to show (if enableTurnkey is true)
|
|
204
|
+
if (closeAfterLogin) {
|
|
205
|
+
setJustCompletedLogin(true);
|
|
115
206
|
}
|
|
116
207
|
|
|
117
|
-
//
|
|
118
|
-
if
|
|
119
|
-
|
|
208
|
+
// Check if we should show Turnkey login form
|
|
209
|
+
// Show if enableTurnkey is true AND user just logged in AND hasn't completed Turnkey auth in this session
|
|
210
|
+
// For new users (!turnkeyId): Show email form
|
|
211
|
+
// For returning users (turnkeyId && turnkeyEmail): Auto-skip to OTP
|
|
212
|
+
// Also check that we're not already showing the Turnkey modal
|
|
213
|
+
const hasTurnkeyId = user?.partnerIds?.turnkeyId;
|
|
214
|
+
const hasTurnkeyEmail = !!user?.email;
|
|
215
|
+
const isTurnkeyModalCurrentlyOpen = contentType?.type === "turnkeyAuth";
|
|
216
|
+
const shouldShowTurnkeyModal =
|
|
217
|
+
enableTurnkey &&
|
|
218
|
+
user &&
|
|
219
|
+
!turnkeyAuthCompleted &&
|
|
220
|
+
!isTurnkeyModalCurrentlyOpen &&
|
|
221
|
+
(!hasTurnkeyId || (hasTurnkeyId && hasTurnkeyEmail));
|
|
222
|
+
|
|
223
|
+
if (shouldShowTurnkeyModal) {
|
|
224
|
+
// Extract email from user object - check partnerIds.turnkeyEmail first, then twProfiles, then user.email
|
|
225
|
+
const email = user?.email || user?.twProfiles?.find((profile: any) => profile.details?.email)?.details?.email;
|
|
226
|
+
|
|
227
|
+
// Open Turnkey modal through the modal store
|
|
228
|
+
setB3ModalContentType({
|
|
229
|
+
type: "turnkeyAuth",
|
|
230
|
+
onSuccess: handleTurnkeySuccess,
|
|
231
|
+
onClose: () => {
|
|
232
|
+
// After closing Turnkey modal, continue with the rest of the flow
|
|
233
|
+
setTurnkeyAuthCompleted(true);
|
|
234
|
+
debug("Turnkey modal closed, running post-Turnkey flow");
|
|
235
|
+
handlePostTurnkeyFlow();
|
|
236
|
+
},
|
|
237
|
+
initialEmail: email,
|
|
238
|
+
skipToOtp: !!(hasTurnkeyId && hasTurnkeyEmail),
|
|
239
|
+
closable: false, // Turnkey modal cannot be closed until auth is complete
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
120
242
|
}
|
|
121
243
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
type: "manageAccount",
|
|
125
|
-
chain,
|
|
126
|
-
partnerId,
|
|
127
|
-
});
|
|
244
|
+
// Normal flow continues after Turnkey auth is complete (or if not needed)
|
|
245
|
+
handlePostTurnkeyFlow();
|
|
128
246
|
}
|
|
129
247
|
}, [
|
|
130
248
|
signers,
|
|
@@ -142,6 +260,13 @@ export function SignInWithB3Flow({
|
|
|
142
260
|
isAuthenticating,
|
|
143
261
|
isAuthenticated,
|
|
144
262
|
isOpen,
|
|
263
|
+
setJustCompletedLogin,
|
|
264
|
+
user,
|
|
265
|
+
enableTurnkey,
|
|
266
|
+
turnkeyAuthCompleted,
|
|
267
|
+
handleTurnkeySuccess,
|
|
268
|
+
contentType,
|
|
269
|
+
handlePostTurnkeyFlow,
|
|
145
270
|
]);
|
|
146
271
|
|
|
147
272
|
debug("render", {
|
|
@@ -205,34 +330,31 @@ export function SignInWithB3Flow({
|
|
|
205
330
|
}
|
|
206
331
|
}, [chain, onError, onSessionKeySuccessEnhanced, sessionKeyAddress, setB3ModalContentType, step]);
|
|
207
332
|
|
|
333
|
+
// Render content based on current step/state
|
|
334
|
+
let content = null;
|
|
335
|
+
|
|
208
336
|
// Display error if refetch limit exceeded
|
|
209
337
|
if (refetchError) {
|
|
210
|
-
|
|
338
|
+
content = (
|
|
211
339
|
<LoginStepContainer partnerId={partnerId}>
|
|
212
340
|
<div className="p-4 text-center text-red-500">{refetchError}</div>
|
|
213
341
|
</LoginStepContainer>
|
|
214
342
|
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (isAuthenticating || (isFetchingSigners && step === "login") || source === "requestPermissions") {
|
|
218
|
-
return (
|
|
343
|
+
} else if (isAuthenticating || (isFetchingSigners && step === "login") || source === "requestPermissions") {
|
|
344
|
+
content = (
|
|
219
345
|
<LoginStepContainer partnerId={partnerId}>
|
|
220
346
|
<div className="my-8 flex min-h-[350px] items-center justify-center">
|
|
221
347
|
<Loading variant="white" size="lg" />
|
|
222
348
|
</div>
|
|
223
349
|
</LoginStepContainer>
|
|
224
350
|
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (step === "login") {
|
|
351
|
+
} else if (step === "login") {
|
|
228
352
|
// Custom strategy
|
|
229
353
|
if (strategies?.[0] === "privy") {
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (strategies) {
|
|
235
|
-
return (
|
|
354
|
+
content = <SignInWithB3Privy onSuccess={handleLoginSuccess} chain={chain} />;
|
|
355
|
+
} else if (strategies) {
|
|
356
|
+
// Strategies are explicitly provided
|
|
357
|
+
content = (
|
|
236
358
|
<LoginStepCustom
|
|
237
359
|
strategies={strategies}
|
|
238
360
|
chain={chain}
|
|
@@ -241,11 +363,11 @@ export function SignInWithB3Flow({
|
|
|
241
363
|
automaticallySetFirstEoa={!!automaticallySetFirstEoa}
|
|
242
364
|
/>
|
|
243
365
|
);
|
|
366
|
+
} else {
|
|
367
|
+
// Default to handle all strategies we support
|
|
368
|
+
content = <LoginStep chain={chain} onSuccess={handleLoginSuccess} onError={onError} />;
|
|
244
369
|
}
|
|
245
|
-
|
|
246
|
-
// Default to handle all strategies we support
|
|
247
|
-
return <LoginStep chain={chain} onSuccess={handleLoginSuccess} onError={onError} />;
|
|
248
370
|
}
|
|
249
371
|
|
|
250
|
-
return
|
|
372
|
+
return content;
|
|
251
373
|
}
|