@b3dotfun/sdk 0.0.26 → 0.0.27-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/anyspend/react/components/AnySpendCustom.d.ts +1 -0
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +3 -2
- package/dist/cjs/anyspend/react/components/AnySpendNFT.js +2 -2
- package/dist/cjs/anyspend/react/components/common/PaymentStripeWeb2.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/common/PaymentStripeWeb2.js +3 -3
- package/dist/cjs/anyspend/react/components/common/PaymentVendorUI.js +2 -2
- package/dist/cjs/anyspend/utils/chain.js +2 -2
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +4 -0
- package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.d.ts +2 -0
- package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.js +228 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +56 -3
- package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStep.js +1 -1
- package/dist/cjs/global-account/react/components/custom/Button.d.ts +1 -1
- package/dist/cjs/global-account/react/components/ui/button.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +8 -2
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +20 -1
- package/dist/cjs/global-account/react/stores/useModalStore.js +3 -0
- package/dist/cjs/global-account/react/utils/profileDisplay.d.ts +21 -0
- package/dist/cjs/global-account/react/utils/profileDisplay.js +63 -0
- package/dist/esm/anyspend/react/components/AnySpendCustom.d.ts +1 -0
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +3 -2
- package/dist/esm/anyspend/react/components/AnySpendNFT.js +2 -2
- package/dist/esm/anyspend/react/components/common/PaymentStripeWeb2.d.ts +2 -1
- package/dist/esm/anyspend/react/components/common/PaymentStripeWeb2.js +3 -3
- package/dist/esm/anyspend/react/components/common/PaymentVendorUI.js +2 -2
- package/dist/esm/anyspend/utils/chain.js +2 -2
- package/dist/esm/global-account/react/components/B3DynamicModal.js +4 -0
- package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.d.ts +2 -0
- package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.js +225 -0
- package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +58 -5
- package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStep.js +1 -1
- package/dist/esm/global-account/react/components/custom/Button.d.ts +1 -1
- package/dist/esm/global-account/react/components/ui/button.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +8 -2
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +20 -1
- package/dist/esm/global-account/react/stores/useModalStore.js +3 -0
- package/dist/esm/global-account/react/utils/profileDisplay.d.ts +21 -0
- package/dist/esm/global-account/react/utils/profileDisplay.js +60 -0
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/AnySpendCustom.d.ts +1 -0
- package/dist/types/anyspend/react/components/common/PaymentStripeWeb2.d.ts +2 -1
- package/dist/types/global-account/react/components/LinkAccount/LinkAccount.d.ts +2 -0
- package/dist/types/global-account/react/components/custom/Button.d.ts +1 -1
- package/dist/types/global-account/react/components/ui/button.d.ts +1 -1
- package/dist/types/global-account/react/stores/useModalStore.d.ts +20 -1
- package/dist/types/global-account/react/utils/profileDisplay.d.ts +21 -0
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpendCustom.tsx +7 -3
- package/src/anyspend/react/components/AnySpendNFT.tsx +2 -1
- package/src/anyspend/react/components/common/PaymentStripeWeb2.tsx +5 -28
- package/src/anyspend/react/components/common/PaymentVendorUI.tsx +2 -2
- package/src/anyspend/utils/chain.ts +2 -2
- package/src/global-account/react/components/B3DynamicModal.tsx +4 -0
- package/src/global-account/react/components/LinkAccount/LinkAccount.tsx +369 -0
- package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +187 -5
- package/src/global-account/react/components/SignInWithB3/steps/LoginStep.tsx +3 -1
- package/src/global-account/react/hooks/useUnifiedChainSwitchAndExecute.ts +9 -2
- package/src/global-account/react/stores/useModalStore.ts +26 -1
- package/src/global-account/react/utils/profileDisplay.ts +87 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Profile } from "thirdweb/wallets";
|
|
2
|
+
export interface ExtendedProfileDetails {
|
|
3
|
+
id?: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
phone?: string;
|
|
6
|
+
address?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
username?: string;
|
|
9
|
+
profileImageUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ExtendedProfile extends Omit<Profile, "details"> {
|
|
12
|
+
details: ExtendedProfileDetails;
|
|
13
|
+
}
|
|
14
|
+
export interface ProfileDisplayInfo {
|
|
15
|
+
title: string;
|
|
16
|
+
subtitle: string;
|
|
17
|
+
imageUrl: string | null;
|
|
18
|
+
initial: string;
|
|
19
|
+
type: Profile["type"];
|
|
20
|
+
}
|
|
21
|
+
export declare function getProfileDisplayInfo(profile: ExtendedProfile): ProfileDisplayInfo;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getProfileDisplayInfo = getProfileDisplayInfo;
|
|
4
|
+
function getProfileDisplayInfo(profile) {
|
|
5
|
+
const { type, details } = profile;
|
|
6
|
+
// Default display info
|
|
7
|
+
let displayInfo = {
|
|
8
|
+
title: details.email || details.phone || details.address || "Unknown",
|
|
9
|
+
subtitle: `Connected with ${type}`,
|
|
10
|
+
imageUrl: null,
|
|
11
|
+
initial: (type.charAt(0) || "U").toUpperCase(),
|
|
12
|
+
type,
|
|
13
|
+
};
|
|
14
|
+
// Handle specific providers
|
|
15
|
+
switch (type) {
|
|
16
|
+
case "x":
|
|
17
|
+
displayInfo = {
|
|
18
|
+
title: details.name || details.username || "Unknown",
|
|
19
|
+
subtitle: details.username ? `@${details.username}` : "X Account",
|
|
20
|
+
imageUrl: details.profileImageUrl || null,
|
|
21
|
+
initial: "X",
|
|
22
|
+
type,
|
|
23
|
+
};
|
|
24
|
+
break;
|
|
25
|
+
case "google":
|
|
26
|
+
displayInfo = {
|
|
27
|
+
title: details.name || details.email || "Unknown",
|
|
28
|
+
subtitle: details.email || "Google Account",
|
|
29
|
+
imageUrl: details.profileImageUrl || null,
|
|
30
|
+
initial: "G",
|
|
31
|
+
type,
|
|
32
|
+
};
|
|
33
|
+
break;
|
|
34
|
+
case "discord":
|
|
35
|
+
displayInfo = {
|
|
36
|
+
title: details.username || details.name || "Unknown",
|
|
37
|
+
subtitle: "Discord Account",
|
|
38
|
+
imageUrl: details.profileImageUrl || null,
|
|
39
|
+
initial: "D",
|
|
40
|
+
type,
|
|
41
|
+
};
|
|
42
|
+
break;
|
|
43
|
+
case "email":
|
|
44
|
+
displayInfo = {
|
|
45
|
+
title: details.email || "Unknown",
|
|
46
|
+
subtitle: "Email Account",
|
|
47
|
+
imageUrl: null,
|
|
48
|
+
initial: "E",
|
|
49
|
+
type,
|
|
50
|
+
};
|
|
51
|
+
break;
|
|
52
|
+
case "phone":
|
|
53
|
+
displayInfo = {
|
|
54
|
+
title: details.phone || "Unknown",
|
|
55
|
+
subtitle: "Phone Number",
|
|
56
|
+
imageUrl: null,
|
|
57
|
+
initial: "P",
|
|
58
|
+
type,
|
|
59
|
+
};
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return displayInfo;
|
|
63
|
+
}
|
|
@@ -34,6 +34,7 @@ var PanelView;
|
|
|
34
34
|
function generateGetRelayQuoteRequest({ orderType, srcChainId, srcToken, dstChainId, dstToken, dstAmount, contractAddress, tokenId, contractType, encodedData, spenderAddress, }) {
|
|
35
35
|
switch (orderType) {
|
|
36
36
|
case "mint_nft": {
|
|
37
|
+
invariant(contractType, "Contract type is required");
|
|
37
38
|
return {
|
|
38
39
|
type: "mint_nft",
|
|
39
40
|
srcChain: srcChainId,
|
|
@@ -92,12 +93,12 @@ export function AnySpendCustom(props) {
|
|
|
92
93
|
const fingerprintConfig = getFingerprintConfig();
|
|
93
94
|
return (_jsx(AnySpendFingerprintWrapper, { fingerprint: fingerprintConfig, children: _jsx(AnySpendCustomInner, { ...props }) }));
|
|
94
95
|
}
|
|
95
|
-
function AnySpendCustomInner({ loadOrder, mode = "modal", recipientAddress: recipientAddressProps, spenderAddress, orderType, dstChainId, dstToken, dstAmount, contractAddress, encodedData, metadata, header, onSuccess, showRecipient = true, }) {
|
|
96
|
+
function AnySpendCustomInner({ loadOrder, mode = "modal", activeTab: activeTabProps = "crypto", recipientAddress: recipientAddressProps, spenderAddress, orderType, dstChainId, dstToken, dstAmount, contractAddress, encodedData, metadata, header, onSuccess, showRecipient = true, }) {
|
|
96
97
|
const hasMounted = useHasMounted();
|
|
97
98
|
const searchParams = useSearchParamsSSR();
|
|
98
99
|
const router = useRouter();
|
|
99
100
|
const [activePanel, setActivePanel] = useState(loadOrder ? PanelView.ORDER_DETAILS : PanelView.CONFIRM_ORDER);
|
|
100
|
-
const [activeTab, setActiveTab] = useState(
|
|
101
|
+
const [activeTab, setActiveTab] = useState(activeTabProps);
|
|
101
102
|
// Add state for selected payment methods
|
|
102
103
|
const [selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod] = useState(CryptoPaymentMethodType.NONE);
|
|
103
104
|
const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState(FiatPaymentMethod.NONE);
|
|
@@ -79,11 +79,11 @@ export function AnySpendNFT({ loadOrder, mode = "modal", recipientAddress, nftCo
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
fetchContractMetadata();
|
|
82
|
-
}, [nftContract.contractAddress, nftContract.chainId, nftContract.imageUrl, nftContract.tokenId]);
|
|
82
|
+
}, [nftContract.contractAddress, nftContract.chainId, nftContract.imageUrl, nftContract.tokenId, isLoadingFallback]);
|
|
83
83
|
const header = ({ anyspendPrice, isLoadingAnyspendPrice, }) => (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative size-[200px]", children: [_jsx("div", { className: "absolute inset-0 scale-95 bg-black/30 blur-md" }), _jsxs(GlareCard, { className: "overflow-hidden", children: [imageUrlWithFallback && (_jsx("img", { src: imageUrlWithFallback, alt: nftContract.name, className: "size-full object-cover" })), _jsx("div", { className: "absolute inset-0 rounded-xl border border-white/10" })] }), _jsx(DropdownMenu, { nftContract: nftContract })] }), _jsxs("div", { className: "from-b3-react-background to-as-on-surface-1 -mb-5 mt-[-100px] w-full rounded-t-lg bg-gradient-to-t", children: [_jsx("div", { className: "h-[100px] w-full" }), _jsxs("div", { className: "mb-1 flex w-full flex-col items-center gap-2 p-5", children: [_jsx("span", { className: "font-sf-rounded text-2xl font-semibold", children: nftContract.name }), _jsx("div", { className: "flex w-fit items-center gap-1", children: anyspendPrice ? (_jsx(AnimatePresence, { mode: "wait", children: _jsx("div", { className: cn("text-as-primary group flex items-center text-3xl font-semibold transition-all", {
|
|
84
84
|
"opacity-0": isLoadingAnyspendPrice,
|
|
85
85
|
}), children: formatDisplayNumber(anyspendPrice?.data?.currencyIn?.amountUsd, { style: "currency" }) }) })) : (_jsx("div", { className: "h-[36px] w-full" })) })] })] })] }));
|
|
86
|
-
return (_jsx(AnySpendCustom, { loadOrder: loadOrder, mode: mode, recipientAddress: recipientAddress, orderType: "mint_nft", dstChainId: nftContract.chainId, dstToken: nftContract.currency, dstAmount: nftContract.price, contractAddress: nftContract.contractAddress, encodedData: "0x", metadata: {
|
|
86
|
+
return (_jsx(AnySpendCustom, { loadOrder: loadOrder, mode: mode, activeTab: "fiat", recipientAddress: recipientAddress, orderType: "mint_nft", dstChainId: nftContract.chainId, dstToken: nftContract.currency, dstAmount: nftContract.price, contractAddress: nftContract.contractAddress, encodedData: "0x", metadata: {
|
|
87
87
|
type: "mint_nft",
|
|
88
88
|
nftContract: nftContract,
|
|
89
89
|
}, header: header, onSuccess: onSuccess }));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { components } from "../../../../anyspend/types/api";
|
|
2
2
|
interface PaymentStripeWeb2Props {
|
|
3
3
|
order: components["schemas"]["Order"];
|
|
4
|
+
stripePaymentIntentId: string;
|
|
4
5
|
onPaymentSuccess?: (paymentIntent: any) => void;
|
|
5
6
|
}
|
|
6
|
-
export default function PaymentStripeWeb2({ order, onPaymentSuccess }: PaymentStripeWeb2Props): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default function PaymentStripeWeb2({ order, stripePaymentIntentId, onPaymentSuccess }: PaymentStripeWeb2Props): import("react/jsx-runtime").JSX.Element;
|
|
7
8
|
export {};
|
|
@@ -12,10 +12,10 @@ import { AnySpendFingerprintWrapper, getFingerprintConfig } from "../AnySpendFin
|
|
|
12
12
|
import HowItWorks from "./HowItWorks.js";
|
|
13
13
|
import PaymentMethodIcons from "./PaymentMethodIcons.js";
|
|
14
14
|
const stripePromise = loadStripe(STRIPE_CONFIG.publishableKey);
|
|
15
|
-
export default function PaymentStripeWeb2({ order, onPaymentSuccess }) {
|
|
15
|
+
export default function PaymentStripeWeb2({ order, stripePaymentIntentId, onPaymentSuccess }) {
|
|
16
16
|
const { theme } = useB3();
|
|
17
17
|
const fingerprintConfig = getFingerprintConfig();
|
|
18
|
-
const { clientSecret, isLoadingStripeClientSecret, stripeClientSecretError } = useStripeClientSecret(
|
|
18
|
+
const { clientSecret, isLoadingStripeClientSecret, stripeClientSecretError } = useStripeClientSecret(stripePaymentIntentId);
|
|
19
19
|
if (isLoadingStripeClientSecret) {
|
|
20
20
|
return _jsx(StripeLoadingState, {});
|
|
21
21
|
}
|
|
@@ -164,7 +164,7 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
|
|
|
164
164
|
// Validation
|
|
165
165
|
validation: {
|
|
166
166
|
phone: {
|
|
167
|
-
required: "
|
|
167
|
+
required: "always", // or 'always', 'never'
|
|
168
168
|
},
|
|
169
169
|
},
|
|
170
170
|
} }))] }), message && (_jsxs("div", { className: "bg-as-red/10 border-as-red/20 flex w-full items-center gap-3 rounded-2xl border p-4", children: [_jsx("div", { className: "bg-as-red flex h-6 w-6 shrink-0 items-center justify-center rounded-full", children: _jsx("svg", { className: "h-4 w-4 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), _jsx("div", { className: "text-as-red text-sm font-medium", children: message })] })), _jsx(ShinyButton, { type: "submit", accentColor: "hsl(var(--as-brand))", disabled: !stripe || !elements || loading, className: "relative w-full py-4 text-lg font-semibold", children: loading ? (_jsxs("div", { className: "flex items-center justify-center gap-3", children: [_jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-current border-t-transparent" }), _jsx("span", { className: "text-white", children: "Processing Payment..." })] })) : (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx("span", { className: "text-white", children: "Complete Payment" }), amount && _jsxs("span", { className: "text-white/90", children: ["$", Number(amount).toFixed(2)] })] })) })] }), showHowItWorks && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: _jsxs("div", { className: "bg-as-on-surface-1 relative max-h-[80vh] w-full max-w-2xl overflow-y-auto rounded-2xl p-6", children: [_jsxs("div", { className: "mb-6 flex items-center justify-between", children: [_jsx("h2", { className: "text-as-primary text-xl font-semibold", children: "How it works" }), _jsx("button", { onClick: () => setShowHowItWorks(false), className: "text-as-primary/60 hover:text-as-primary transition-colors", children: _jsx(X, { className: "h-6 w-6" }) })] }), _jsxs("div", { className: "space-y-6", children: [_jsx(PaymentMethodIcons, {}), _jsx(HowItWorks, { steps: howItWorksSteps })] })] }) }))] }));
|
|
@@ -8,8 +8,8 @@ export default function PaymentVendorUI({ order, dstTokenSymbol }) {
|
|
|
8
8
|
return _jsx(PaymentOneClick, { order: order, dstTokenSymbol: dstTokenSymbol });
|
|
9
9
|
}
|
|
10
10
|
// Handle Stripe Web2 payment flow
|
|
11
|
-
if (vendor === "stripe-web2") {
|
|
12
|
-
return _jsx(PaymentStripeWeb2, { order: order });
|
|
11
|
+
if (vendor === "stripe-web2" && order.stripePaymentIntentId) {
|
|
12
|
+
return _jsx(PaymentStripeWeb2, { order: order, stripePaymentIntentId: order.stripePaymentIntentId });
|
|
13
13
|
}
|
|
14
14
|
// Return null for unsupported vendors
|
|
15
15
|
return null;
|
|
@@ -90,7 +90,7 @@ export const EVM_MAINNET = {
|
|
|
90
90
|
name: avalanche.name,
|
|
91
91
|
logoUrl: "https://assets.relay.link/icons/square/43114/light.png",
|
|
92
92
|
type: ChainType.EVM,
|
|
93
|
-
nativeRequired: parseEther("0.
|
|
93
|
+
nativeRequired: parseEther("0.01"),
|
|
94
94
|
canDepositNative: true,
|
|
95
95
|
defaultToken: getAvaxToken(),
|
|
96
96
|
nativeToken: getAvaxToken(),
|
|
@@ -104,7 +104,7 @@ export const EVM_MAINNET = {
|
|
|
104
104
|
name: bsc.name,
|
|
105
105
|
logoUrl: "https://avatars.githubusercontent.com/u/45615063?s=280&v=4",
|
|
106
106
|
type: ChainType.EVM,
|
|
107
|
-
nativeRequired: parseEther("0.
|
|
107
|
+
nativeRequired: parseEther("0.000025"),
|
|
108
108
|
canDepositNative: true,
|
|
109
109
|
defaultToken: getBnbToken(),
|
|
110
110
|
nativeToken: getBnbToken(),
|
|
@@ -4,6 +4,7 @@ import { useIsMobile, useModalStore } from "../../../global-account/react/index.
|
|
|
4
4
|
import { cn } from "../../../shared/utils/cn.js";
|
|
5
5
|
import { debugB3React } from "../../../shared/utils/debug.js";
|
|
6
6
|
import { useB3 } from "./B3Provider/useB3.js";
|
|
7
|
+
import { LinkAccount } from "./LinkAccount/LinkAccount.js";
|
|
7
8
|
import { ManageAccount } from "./ManageAccount/ManageAccount.js";
|
|
8
9
|
import { RequestPermissions } from "./RequestPermissions/RequestPermissions.js";
|
|
9
10
|
import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow.js";
|
|
@@ -28,6 +29,7 @@ export function B3DynamicModal() {
|
|
|
28
29
|
"signInWithB3",
|
|
29
30
|
"anySpendSignatureMint",
|
|
30
31
|
"anySpendBondKit",
|
|
32
|
+
"linkAccount",
|
|
31
33
|
];
|
|
32
34
|
const freestyleTypes = [
|
|
33
35
|
"anySpendNft",
|
|
@@ -79,6 +81,8 @@ export function B3DynamicModal() {
|
|
|
79
81
|
return _jsx(AnyspendSignatureMint, { ...contentType, mode: "modal" });
|
|
80
82
|
case "anySpendBondKit":
|
|
81
83
|
return _jsx(AnySpendBondKit, { ...contentType });
|
|
84
|
+
case "linkAccount":
|
|
85
|
+
return _jsx(LinkAccount, { ...contentType });
|
|
82
86
|
// Add other modal types here
|
|
83
87
|
default:
|
|
84
88
|
return null;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { client } from "../../../../shared/utils/thirdweb.js";
|
|
3
|
+
import { Loader2 } from "lucide-react";
|
|
4
|
+
import { useCallback, useEffect, useState } from "react";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
import { useLinkProfile, useProfiles } from "thirdweb/react";
|
|
7
|
+
import { preAuthenticate } from "thirdweb/wallets";
|
|
8
|
+
import { useModalStore } from "../../stores/useModalStore.js";
|
|
9
|
+
import { getProfileDisplayInfo } from "../../utils/profileDisplay.js";
|
|
10
|
+
import { useB3 } from "../B3Provider/useB3.js";
|
|
11
|
+
import { Button } from "../ui/button.js";
|
|
12
|
+
const AUTH_METHODS = [
|
|
13
|
+
{ id: "email", label: "Email", enabled: true },
|
|
14
|
+
{ id: "phone", label: "Phone", enabled: true },
|
|
15
|
+
{ id: "google", label: "Google", enabled: true },
|
|
16
|
+
{ id: "x", label: "X (Twitter)", enabled: true },
|
|
17
|
+
{ id: "discord", label: "Discord", enabled: true },
|
|
18
|
+
{ id: "apple", label: "Apple", enabled: true },
|
|
19
|
+
];
|
|
20
|
+
export function LinkAccount({ onSuccess: onSuccessCallback, onError, onClose, chain, partnerId, }) {
|
|
21
|
+
const { isLinking, linkingMethod, setLinkingState, navigateBack, setB3ModalContentType } = useModalStore();
|
|
22
|
+
const [selectedMethod, setSelectedMethod] = useState(null);
|
|
23
|
+
const [email, setEmail] = useState("");
|
|
24
|
+
const [phone, setPhone] = useState("");
|
|
25
|
+
const [otp, setOtp] = useState("");
|
|
26
|
+
const [otpSent, setOtpSent] = useState(false);
|
|
27
|
+
const [error, setError] = useState(null);
|
|
28
|
+
const { data: profilesRaw = [] } = useProfiles({ client });
|
|
29
|
+
// Get connected auth methods
|
|
30
|
+
const connectedAuthMethods = profilesRaw
|
|
31
|
+
.filter((profile) => !["custom_auth_endpoint", "siwe"].includes(profile.type))
|
|
32
|
+
.map((profile) => profile.type);
|
|
33
|
+
// Filter available auth methods
|
|
34
|
+
const availableAuthMethods = AUTH_METHODS.filter(method => !connectedAuthMethods.includes(method.id) && method.enabled);
|
|
35
|
+
const profiles = profilesRaw
|
|
36
|
+
.filter((profile) => !["custom_auth_endpoint", "siwe"].includes(profile.type))
|
|
37
|
+
.map((profile) => ({
|
|
38
|
+
...getProfileDisplayInfo(profile),
|
|
39
|
+
originalProfile: profile,
|
|
40
|
+
}));
|
|
41
|
+
const { account } = useB3();
|
|
42
|
+
const { mutate: linkProfile } = useLinkProfile();
|
|
43
|
+
const onSuccess = useCallback(async () => {
|
|
44
|
+
await onSuccessCallback?.();
|
|
45
|
+
}, [onSuccessCallback]);
|
|
46
|
+
// Reset linking state when component unmounts
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
return () => {
|
|
49
|
+
if (isLinking) {
|
|
50
|
+
setLinkingState(false);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}, [isLinking, setLinkingState]);
|
|
54
|
+
const mutationOptions = {
|
|
55
|
+
onError: (error) => {
|
|
56
|
+
console.error("Error linking account:", error);
|
|
57
|
+
toast.error(error.message);
|
|
58
|
+
setLinkingState(false);
|
|
59
|
+
onError?.(error);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const validateInput = () => {
|
|
63
|
+
if (selectedMethod === "email") {
|
|
64
|
+
if (!email) {
|
|
65
|
+
setError("Please enter your email address");
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
69
|
+
setError("Please enter a valid email address");
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (selectedMethod === "phone") {
|
|
74
|
+
if (!phone) {
|
|
75
|
+
setError("Please enter your phone number");
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (!/^\+?[\d\s-]{10,}$/.test(phone)) {
|
|
79
|
+
setError("Please enter a valid phone number");
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
setError(null);
|
|
84
|
+
return true;
|
|
85
|
+
};
|
|
86
|
+
const handleSendOTP = async () => {
|
|
87
|
+
if (!validateInput())
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
setLinkingState(true, selectedMethod);
|
|
91
|
+
setError(null);
|
|
92
|
+
if (selectedMethod === "email") {
|
|
93
|
+
await preAuthenticate({
|
|
94
|
+
client,
|
|
95
|
+
strategy: "email",
|
|
96
|
+
email,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else if (selectedMethod === "phone") {
|
|
100
|
+
await preAuthenticate({
|
|
101
|
+
client,
|
|
102
|
+
strategy: "phone",
|
|
103
|
+
phoneNumber: phone,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
setOtpSent(true);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error("Error sending OTP:", error);
|
|
110
|
+
setError(error instanceof Error ? error.message : "Failed to send OTP");
|
|
111
|
+
onError?.(error);
|
|
112
|
+
setLinkingState(false);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const handleLinkAccount = async () => {
|
|
116
|
+
if (!otp) {
|
|
117
|
+
setError("Please enter the verification code");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
setLinkingState(true, selectedMethod);
|
|
122
|
+
setError(null);
|
|
123
|
+
if (selectedMethod === "email") {
|
|
124
|
+
await linkProfile({
|
|
125
|
+
client,
|
|
126
|
+
strategy: "email",
|
|
127
|
+
email,
|
|
128
|
+
verificationCode: otp,
|
|
129
|
+
}, mutationOptions);
|
|
130
|
+
}
|
|
131
|
+
else if (selectedMethod === "phone") {
|
|
132
|
+
await linkProfile({
|
|
133
|
+
client,
|
|
134
|
+
strategy: "phone",
|
|
135
|
+
phoneNumber: phone,
|
|
136
|
+
verificationCode: otp,
|
|
137
|
+
}, mutationOptions);
|
|
138
|
+
}
|
|
139
|
+
onSuccess?.();
|
|
140
|
+
onClose?.();
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error("Error linking account:", error);
|
|
144
|
+
setError(error instanceof Error ? error.message : "Failed to link account");
|
|
145
|
+
onError?.(error);
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
setLinkingState(false);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const handleSocialLink = async (strategy) => {
|
|
152
|
+
try {
|
|
153
|
+
console.log("handleSocialLink", strategy);
|
|
154
|
+
setLinkingState(true, strategy);
|
|
155
|
+
setError(null);
|
|
156
|
+
const result = await linkProfile({
|
|
157
|
+
client,
|
|
158
|
+
strategy,
|
|
159
|
+
}, mutationOptions);
|
|
160
|
+
console.log("result", result);
|
|
161
|
+
// Don't close the modal yet, wait for auth to complete
|
|
162
|
+
onSuccess?.();
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error("Error linking with social:", error);
|
|
166
|
+
setError(error instanceof Error ? error.message : "Failed to link social account");
|
|
167
|
+
onError?.(error);
|
|
168
|
+
setLinkingState(false);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
// Add effect to handle social auth completion
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (isLinking && linkingMethod && !selectedMethod) {
|
|
174
|
+
// This means we're in a social auth flow
|
|
175
|
+
const checkAuthStatus = async () => {
|
|
176
|
+
try {
|
|
177
|
+
// Wait a bit to ensure auth is complete
|
|
178
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
179
|
+
onClose?.();
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.error("Error checking auth status:", error);
|
|
183
|
+
setLinkingState(false);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
checkAuthStatus();
|
|
187
|
+
}
|
|
188
|
+
}, [isLinking, linkingMethod, selectedMethod, onClose, setLinkingState]);
|
|
189
|
+
const handleBack = useCallback(() => {
|
|
190
|
+
if (isLinking)
|
|
191
|
+
return;
|
|
192
|
+
setSelectedMethod(null);
|
|
193
|
+
setEmail("");
|
|
194
|
+
setPhone("");
|
|
195
|
+
setOtp("");
|
|
196
|
+
setOtpSent(false);
|
|
197
|
+
setError(null);
|
|
198
|
+
setLinkingState(false);
|
|
199
|
+
}, [isLinking, setSelectedMethod, setEmail, setPhone, setOtp, setOtpSent, setError, setLinkingState]);
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
if (isLinking) {
|
|
202
|
+
setLinkingState(false);
|
|
203
|
+
navigateBack();
|
|
204
|
+
setB3ModalContentType({
|
|
205
|
+
type: "manageAccount",
|
|
206
|
+
activeTab: "settings",
|
|
207
|
+
setActiveTab: () => { },
|
|
208
|
+
chain,
|
|
209
|
+
partnerId,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
213
|
+
}, [profiles.length]);
|
|
214
|
+
if (!account) {
|
|
215
|
+
return _jsx("div", { className: "text-b3-foreground-muted py-8 text-center", children: "Please connect your account first" });
|
|
216
|
+
}
|
|
217
|
+
return (_jsxs("div", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h2", { className: "text-b3-grey font-neue-montreal-semibold text-2xl", children: "Link New Account" }), selectedMethod && (_jsx(Button, { variant: "ghost", className: "text-b3-grey hover:text-b3-grey/80", onClick: handleBack, children: "Backs" }))] }), !selectedMethod ? (_jsxs("div", { className: "grid gap-3", children: [availableAuthMethods.map(method => (_jsx(Button, { className: "bg-b3-primary-wash hover:bg-b3-primary-wash/70 text-b3-grey font-neue-montreal-semibold h-16 justify-start px-6 text-lg", onClick: () => {
|
|
218
|
+
if (method.id === "email" || method.id === "phone") {
|
|
219
|
+
setSelectedMethod(method.id);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
handleSocialLink(method.id);
|
|
223
|
+
}
|
|
224
|
+
}, disabled: linkingMethod === method.id, children: isLinking && linkingMethod === method.id ? _jsx(Loader2, { className: "animate-spin" }) : method.label }, method.id))), availableAuthMethods.length === 0 && (_jsx("div", { className: "text-b3-foreground-muted py-8 text-center", children: "All available authentication methods have been connected" }))] })) : (_jsxs("div", { className: "space-y-4", children: [selectedMethod === "email" && (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-b3-grey font-neue-montreal-medium text-sm", children: "Email Address" }), _jsx("input", { type: "email", placeholder: "Enter your email", className: "bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2", value: email, onChange: e => setEmail(e.target.value), disabled: otpSent || (isLinking && linkingMethod === "email") })] })), selectedMethod === "phone" && (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-b3-grey font-neue-montreal-medium text-sm", children: "Phone Number" }), _jsx("input", { type: "tel", placeholder: "Enter your phone number", className: "bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2", value: phone, onChange: e => setPhone(e.target.value), disabled: otpSent || (isLinking && linkingMethod === "phone") }), _jsx("p", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "Include country code (e.g., +1 for US)" })] })), error && _jsx("div", { className: "text-b3-negative font-neue-montreal-medium py-2 text-sm", children: error }), otpSent ? (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-b3-grey font-neue-montreal-medium text-sm", children: "Verification Code" }), _jsx("input", { type: "text", placeholder: "Enter verification code", className: "bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2", value: otp, onChange: e => setOtp(e.target.value), disabled: isLinking && linkingMethod === selectedMethod })] }), _jsx(Button, { className: "bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white", onClick: handleLinkAccount, disabled: !otp || (isLinking && linkingMethod === selectedMethod), children: isLinking && linkingMethod === selectedMethod ? _jsx(Loader2, { className: "animate-spin" }) : "Link Account" })] })) : (_jsx(Button, { className: "bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white", onClick: handleSendOTP, disabled: (!email && !phone) || (isLinking && linkingMethod === selectedMethod), children: isLinking && linkingMethod === selectedMethod ? (_jsx(Loader2, { className: "animate-spin" })) : ("Send Verification Code") }))] }))] }));
|
|
225
|
+
}
|
|
@@ -5,11 +5,13 @@ import { SignOutIcon } from "../../../../global-account/react/components/icons/S
|
|
|
5
5
|
import { SwapIcon } from "../../../../global-account/react/components/icons/SwapIcon.js";
|
|
6
6
|
import { formatUsername } from "../../../../shared/utils/index.js";
|
|
7
7
|
import { formatNumber } from "../../../../shared/utils/formatNumber.js";
|
|
8
|
-
import {
|
|
8
|
+
import { client } from "../../../../shared/utils/thirdweb.js";
|
|
9
|
+
import { LinkIcon, Loader2, Pencil, Triangle, UnlinkIcon } from "lucide-react";
|
|
9
10
|
import { useState } from "react";
|
|
10
|
-
import { useActiveAccount } from "thirdweb/react";
|
|
11
|
+
import { useActiveAccount, useProfiles, useUnlinkProfile } from "thirdweb/react";
|
|
11
12
|
import { formatUnits } from "viem";
|
|
12
13
|
import useFirstEOA from "../../hooks/useFirstEOA.js";
|
|
14
|
+
import { getProfileDisplayInfo } from "../../utils/profileDisplay.js";
|
|
13
15
|
import { AccountAssets } from "../AccountAssets/AccountAssets.js";
|
|
14
16
|
function centerTruncate(str, length = 4) {
|
|
15
17
|
if (str.length <= length * 2)
|
|
@@ -17,7 +19,6 @@ function centerTruncate(str, length = 4) {
|
|
|
17
19
|
return `${str.slice(0, length)}...${str.slice(-length)}`;
|
|
18
20
|
}
|
|
19
21
|
export function ManageAccount({ onLogout, onSwap: _onSwap, onDeposit: _onDeposit, chain, partnerId, }) {
|
|
20
|
-
const [activeTab, setActiveTab] = useState("balance");
|
|
21
22
|
const [revokingSignerId, setRevokingSignerId] = useState(null);
|
|
22
23
|
const account = useActiveAccount();
|
|
23
24
|
const { data: assets, isLoading } = useAccountAssets(account?.address);
|
|
@@ -34,7 +35,8 @@ export function ManageAccount({ onLogout, onSwap: _onSwap, onDeposit: _onDeposit
|
|
|
34
35
|
chain,
|
|
35
36
|
accountAddress: account?.address,
|
|
36
37
|
});
|
|
37
|
-
const { setB3ModalOpen, setB3ModalContentType } = useModalStore();
|
|
38
|
+
const { setB3ModalOpen, setB3ModalContentType, contentType } = useModalStore();
|
|
39
|
+
const { activeTab = "balance", setActiveTab } = contentType;
|
|
38
40
|
const { logout } = useAuthentication(partnerId);
|
|
39
41
|
const [logoutLoading, setLogoutLoading] = useState(false);
|
|
40
42
|
console.log("account", account);
|
|
@@ -81,5 +83,56 @@ export function ManageAccount({ onLogout, onSwap: _onSwap, onDeposit: _onDeposit
|
|
|
81
83
|
};
|
|
82
84
|
const AssetsContent = () => (_jsx("div", { className: "grid grid-cols-3 gap-4", children: assets?.nftResponse ? (_jsx(AccountAssets, { nfts: assets.nftResponse, isLoading: isLoading })) : (_jsx("div", { className: "col-span-3 py-12 text-center text-gray-500", children: "No NFTs found" })) }));
|
|
83
85
|
const AppsContent = () => (_jsxs("div", { className: "space-y-4", children: [signers?.map((signer) => (_jsx("div", { className: "rounded-xl border border-gray-200 p-4 dark:border-gray-800", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 dark:bg-gray-800", children: _jsx("span", { className: "text-xs font-medium text-gray-600 dark:text-gray-400", children: "App" }) }), _jsxs("div", { children: [_jsx("h3", { className: "font-medium text-gray-900 dark:text-white", children: signer.partner.name }), _jsxs("div", { className: "mt-2 space-y-1", children: [_jsxs("p", { className: "text-xs text-gray-500", children: ["Added ", new Date(signer.createdAt).toLocaleDateString()] }), _jsxs("p", { className: "text-xs text-gray-500", children: ["Expires ", new Date(Number(signer.endTimestamp) * 1000).toLocaleDateString()] }), _jsxs("p", { className: "text-xs text-gray-500", children: ["Max spend: ", formatNumber(Number(formatUnits(signer.nativeTokenLimitPerTransaction, 18))), " ETH"] })] })] })] }), _jsx(Button, { variant: "outline", size: "sm", className: "border-red-200 text-red-500 hover:border-red-300 hover:text-red-600", onClick: () => handleRevoke(signer), disabled: revokingSignerId === signer.id, children: revokingSignerId === signer.id ? "Revoking..." : "Revoke" })] }) }, signer.id))), !signers?.length && _jsx("div", { className: "py-12 text-center text-gray-500", children: "No connected apps" })] }));
|
|
84
|
-
|
|
86
|
+
const SettingsContent = () => {
|
|
87
|
+
const [unlinkingAccountId, setUnlinkingAccountId] = useState(null);
|
|
88
|
+
const { data: profilesRaw = [], isLoading: isLoadingProfiles } = useProfiles({ client });
|
|
89
|
+
const { mutate: unlinkProfile, isPending: isUnlinking } = useUnlinkProfile();
|
|
90
|
+
const { setB3ModalOpen, setB3ModalContentType, isLinking } = useModalStore();
|
|
91
|
+
const profiles = profilesRaw
|
|
92
|
+
.filter((profile) => !["custom_auth_endpoint", "siwe"].includes(profile.type))
|
|
93
|
+
.map((profile) => ({
|
|
94
|
+
...getProfileDisplayInfo(profile),
|
|
95
|
+
originalProfile: profile,
|
|
96
|
+
}));
|
|
97
|
+
const handleUnlink = async (profile) => {
|
|
98
|
+
setUnlinkingAccountId(profile.title);
|
|
99
|
+
try {
|
|
100
|
+
await unlinkProfile({
|
|
101
|
+
client,
|
|
102
|
+
profileToUnlink: profile.originalProfile,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error("Error unlinking account:", error);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
setUnlinkingAccountId(null);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const handleOpenLinkModal = () => {
|
|
113
|
+
setB3ModalOpen(true);
|
|
114
|
+
setB3ModalContentType({
|
|
115
|
+
type: "linkAccount",
|
|
116
|
+
showBackButton: true,
|
|
117
|
+
partnerId,
|
|
118
|
+
chain,
|
|
119
|
+
onSuccess: async () => {
|
|
120
|
+
// Let the LinkAccount component handle modal closing
|
|
121
|
+
},
|
|
122
|
+
onError: () => {
|
|
123
|
+
// Let the LinkAccount component handle errors
|
|
124
|
+
},
|
|
125
|
+
onClose: () => {
|
|
126
|
+
// Let the LinkAccount component handle closing
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
return (_jsxs("div", { className: "space-y-8", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-b3-grey font-neue-montreal-semibold text-xl", children: "Linked Accounts" }), _jsxs(Button, { className: "bg-b3-primary-wash hover:bg-b3-primary-wash/70 flex items-center gap-2 rounded-full px-4 py-2", onClick: handleOpenLinkModal, disabled: isLinking, children: [isLinking ? (_jsx(Loader2, { className: "text-b3-primary-blue animate-spin", size: 16 })) : (_jsx(LinkIcon, { size: 16, className: "text-b3-primary-blue" })), _jsx("span", { className: "text-b3-grey font-neue-montreal-semibold", children: isLinking ? "Linking..." : "Link New Account" })] })] }), isLoadingProfiles ? (_jsx("div", { className: "flex justify-center py-8", children: _jsx(Loader2, { className: "text-b3-grey animate-spin" }) })) : profiles.length > 0 ? (_jsx("div", { className: "space-y-4", children: profiles.map(profile => (_jsxs("div", { className: "bg-b3-line flex items-center justify-between rounded-xl p-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [profile.imageUrl ? (_jsx("img", { src: profile.imageUrl, alt: profile.title, className: "size-10 rounded-full" })) : (_jsx("div", { className: "bg-b3-primary-wash flex h-10 w-10 items-center justify-center rounded-full", children: _jsx("span", { className: "text-b3-grey font-neue-montreal-semibold text-sm uppercase", children: profile.initial }) })), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-b3-grey font-neue-montreal-semibold", children: profile.title }), _jsx("span", { className: "text-b3-foreground-muted font-neue-montreal-medium bg-b3-primary-wash rounded px-2 py-0.5 text-xs", children: profile.type.toUpperCase() })] }), _jsx("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: profile.subtitle })] })] }), _jsx(Button, { variant: "ghost", size: "icon", className: "text-b3-grey hover:text-b3-negative", onClick: () => handleUnlink(profile), disabled: unlinkingAccountId === profile.title || isUnlinking, children: unlinkingAccountId === profile.title || isUnlinking ? (_jsx(Loader2, { className: "animate-spin" })) : (_jsx(UnlinkIcon, { size: 16 })) })] }, profile.title))) })) : (_jsx("div", { className: "text-b3-foreground-muted py-8 text-center", children: "No linked accounts found" }))] }), _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-b3-grey font-neue-montreal-semibold text-xl", children: "Account Preferences" }), _jsx("div", { className: "bg-b3-line rounded-xl p-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-b3-grey font-neue-montreal-semibold", children: "Dark Mode" }), _jsx("div", { className: "text-b3-foreground-muted font-neue-montreal-medium text-sm", children: "Switch between light and dark theme" })] }), _jsx("div", { className: "bg-b3-primary-wash h-6 w-12 rounded-full" })] }) })] }), _jsxs("div", { className: "border-b3-line flex items-center justify-between rounded-2xl border p-4", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("img", { src: "https://cdn.b3.fun/b3_logo.svg", alt: "B3", className: "h-4" }), _jsx("h3", { className: "font-neue-montreal-semibold text-b3-grey", children: "Global Account" })] }), _jsx("p", { className: "text-b3-foreground-muted font-neue-montreal-medium mt-2 text-sm", children: "Your universal account for all B3-powered apps" })] }), _jsx("button", { className: "text-b3-grey hover:text-b3-grey/80 hover:bg-b3-line border-b3-line flex size-12 items-center justify-center rounded-full border", onClick: onLogoutEnhanced, children: logoutLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(SignOutIcon, { size: 16, className: "text-b3-grey" }) })] })] }));
|
|
131
|
+
};
|
|
132
|
+
return (_jsx("div", { className: "b3-manage-account bg-b3-background flex flex-col rounded-xl", children: _jsx("div", { className: "flex-1", children: _jsxs(TabsPrimitive, { defaultValue: activeTab, onValueChange: value => {
|
|
133
|
+
const tab = value;
|
|
134
|
+
if (["balance", "assets", "apps", "settings"].includes(tab)) {
|
|
135
|
+
setActiveTab?.(tab);
|
|
136
|
+
}
|
|
137
|
+
}, children: [_jsxs(TabsListPrimitive, { className: "font-neue-montreal-semibold text-b3-grey flex h-8 w-full items-start justify-start gap-8 border-0 text-xl md:p-4", children: [_jsx(TabTriggerPrimitive, { value: "balance", className: "data-[state=active]:text-b3-primary-blue data-[state=active]:border-b-b3-primary-blue flex-none rounded-none border-0 p-0 pb-1 text-xl leading-none tracking-wide transition-colors data-[state=active]:border-b data-[state=active]:bg-white md:pb-4", children: "Overview" }), _jsx(TabTriggerPrimitive, { value: "assets", className: "data-[state=active]:text-b3-primary-blue data-[state=active]:border-b-b3-primary-blue flex-none rounded-none border-0 p-0 pb-1 text-xl leading-none tracking-wide transition-colors data-[state=active]:border-b data-[state=active]:bg-white md:pb-4", children: "Mints" }), _jsx(TabTriggerPrimitive, { value: "apps", className: "data-[state=active]:text-b3-primary-blue data-[state=active]:border-b-b3-primary-blue flex-none rounded-none border-0 p-0 pb-1 text-xl leading-none tracking-wide transition-colors data-[state=active]:border-b data-[state=active]:bg-white md:pb-4", children: "Apps" }), _jsx(TabTriggerPrimitive, { value: "settings", className: "data-[state=active]:text-b3-primary-blue data-[state=active]:border-b-b3-primary-blue flex-none rounded-none border-0 p-0 pb-1 text-xl leading-none tracking-wide transition-colors data-[state=active]:border-b data-[state=active]:bg-white md:pb-4", children: "Settings" })] }), _jsx(TabsContentPrimitive, { value: "balance", className: "pt-4 md:p-4", children: _jsx(BalanceContent, {}) }), _jsx(TabsContentPrimitive, { value: "assets", className: "pt-4 md:p-4", children: _jsx(AssetsContent, {}) }), _jsx(TabsContentPrimitive, { value: "apps", className: "pt-4 md:p-4", children: _jsx(AppsContent, {}) }), _jsx(TabsContentPrimitive, { value: "settings", className: "pt-4 md:p-4", children: _jsx(SettingsContent, {}) })] }) }) }));
|
|
85
138
|
}
|
|
@@ -13,7 +13,7 @@ export function LoginStepContainer({ children, partnerId }) {
|
|
|
13
13
|
},
|
|
14
14
|
}, !!partnerId);
|
|
15
15
|
const partnerLogo = partner?.data?.[0]?.loginCustomization?.logoUrl;
|
|
16
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center", children: [partnerLogo && _jsx("img", { src: partnerLogo, alt: "Partner Logo", className: "mb-6 mt-6 h-12 w-auto object-contain" }), children, _jsxs("h2", { className: "mt-6 flex items-center gap-2 text-lg font-bold", children: ["Powered by", _jsx("img", { alt: "B3 Logo", className: "h-5", src: "https://cdn.b3.fun/b3_logo.svg" }), "Connect"] })] }));
|
|
16
|
+
return (_jsxs("div", { className: "flex flex-col items-center justify-center", children: [partnerLogo && (_jsx("img", { src: partnerLogo, alt: "Partner Logo", className: "partner-logo mb-6 mt-6 h-12 w-auto object-contain" })), children, _jsxs("h2", { className: "mt-6 flex items-center gap-2 text-lg font-bold", children: ["Powered by", _jsx("img", { alt: "B3 Logo", className: "h-5", src: "https://cdn.b3.fun/b3_logo.svg" }), "Connect"] })] }));
|
|
17
17
|
}
|
|
18
18
|
export function LoginStep({ onSuccess, onError, partnerId, chain }) {
|
|
19
19
|
const wallet = ecosystemWallet(ecosystemWalletId, {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
declare const buttonVariants: (props?: ({
|
|
3
3
|
variant?: "link" | "default" | "b3" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
4
|
-
size?: "default" | "
|
|
4
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
6
6
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
7
7
|
variant?: "default" | "outline";
|
|
@@ -2,7 +2,7 @@ import { type VariantProps } from "class-variance-authority";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
4
|
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
5
|
-
size?: "default" | "
|
|
5
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
8
8
|
asChild?: boolean;
|
|
@@ -34,9 +34,14 @@ export function useUnifiedChainSwitchAndExecute() {
|
|
|
34
34
|
if (!signer) {
|
|
35
35
|
throw new Error("No account connected");
|
|
36
36
|
}
|
|
37
|
+
// Get the target chain configuration instead of using potentially stale walletClient.chain
|
|
38
|
+
const targetChain = supportedChains.find(chain => chain.id === targetChainId);
|
|
39
|
+
if (!targetChain) {
|
|
40
|
+
throw new Error(`Chain ${targetChainId} is not supported`);
|
|
41
|
+
}
|
|
37
42
|
const hash = await walletClient.sendTransaction({
|
|
38
43
|
account: signer,
|
|
39
|
-
chain:
|
|
44
|
+
chain: targetChain,
|
|
40
45
|
to: params.to,
|
|
41
46
|
data: params.data,
|
|
42
47
|
value: params.value,
|
|
@@ -49,7 +54,7 @@ export function useUnifiedChainSwitchAndExecute() {
|
|
|
49
54
|
if (onCorrectChain) {
|
|
50
55
|
return await executeTransaction();
|
|
51
56
|
}
|
|
52
|
-
toast.info(`Switching to ${getChainName(targetChainId)}…`);
|
|
57
|
+
const switchingToastId = toast.info(`Switching to ${getChainName(targetChainId)}…`);
|
|
53
58
|
const targetChain = supportedChains.find(chain => chain.id === targetChainId);
|
|
54
59
|
if (!targetChain) {
|
|
55
60
|
toast.error(`Chain ${targetChainId} is not supported`);
|
|
@@ -71,6 +76,7 @@ export function useUnifiedChainSwitchAndExecute() {
|
|
|
71
76
|
},
|
|
72
77
|
},
|
|
73
78
|
});
|
|
79
|
+
toast.dismiss(switchingToastId);
|
|
74
80
|
return await executeTransaction();
|
|
75
81
|
}
|
|
76
82
|
catch (e) {
|