@b3dotfun/sdk 0.0.31 → 0.0.32-alpha.1
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/AnyspendDepositHype.js +2 -2
- package/dist/cjs/anyspend/react/components/common/OrderDetails.d.ts +1 -0
- package/dist/cjs/anyspend/react/components/common/OrderDetails.js +7 -3
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +4 -1
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +47 -17
- package/dist/cjs/anyspend/react/hooks/useSigMint.d.ts +1 -1
- package/dist/cjs/anyspend/types/api.d.ts +26 -7
- package/dist/cjs/global-account/react/components/B3Provider/types.d.ts +4 -5
- package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +53 -1
- package/dist/cjs/global-account/react/hooks/useSiwe.js +5 -5
- package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +2 -2
- package/dist/esm/anyspend/react/components/common/OrderDetails.d.ts +1 -0
- package/dist/esm/anyspend/react/components/common/OrderDetails.js +7 -3
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +4 -1
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +49 -19
- package/dist/esm/anyspend/react/hooks/useSigMint.d.ts +1 -1
- package/dist/esm/anyspend/types/api.d.ts +26 -7
- package/dist/esm/global-account/react/components/B3Provider/types.d.ts +4 -5
- package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +54 -5
- package/dist/esm/global-account/react/hooks/useSiwe.js +5 -5
- package/dist/types/anyspend/react/components/common/OrderDetails.d.ts +1 -0
- package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +4 -1
- package/dist/types/anyspend/react/hooks/useSigMint.d.ts +1 -1
- package/dist/types/anyspend/types/api.d.ts +26 -7
- package/dist/types/global-account/react/components/B3Provider/types.d.ts +4 -5
- package/package.json +2 -2
- package/src/anyspend/react/components/AnyspendDepositHype.tsx +2 -1
- package/src/anyspend/react/components/common/OrderDetails.tsx +6 -2
- package/src/anyspend/react/hooks/useAnyspendFlow.ts +53 -16
- package/src/anyspend/types/api.ts +26 -7
- package/src/global-account/react/components/B3Provider/B3Provider.native.tsx +2 -2
- package/src/global-account/react/components/B3Provider/B3Provider.tsx +2 -2
- package/src/global-account/react/components/B3Provider/types.ts +4 -5
- package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +147 -3
- package/src/global-account/react/hooks/useSiwe.tsx +5 -5
|
@@ -7,12 +7,19 @@ import {
|
|
|
7
7
|
useGeoOnrampOptions,
|
|
8
8
|
} from "@b3dotfun/sdk/anyspend/react";
|
|
9
9
|
import { anyspendService } from "@b3dotfun/sdk/anyspend/services/anyspend";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useAccountWallet,
|
|
12
|
+
useProfile,
|
|
13
|
+
useRouter,
|
|
14
|
+
useSearchParamsSSR,
|
|
15
|
+
useTokenBalance,
|
|
16
|
+
} from "@b3dotfun/sdk/global-account/react";
|
|
11
17
|
import { formatTokenAmount, formatUnits } from "@b3dotfun/sdk/shared/utils/number";
|
|
12
|
-
import { useEffect, useState } from "react";
|
|
18
|
+
import { useEffect, useMemo, useState } from "react";
|
|
13
19
|
import { toast } from "sonner";
|
|
14
20
|
import { parseUnits } from "viem";
|
|
15
21
|
import { base, mainnet } from "viem/chains";
|
|
22
|
+
import { useAccount } from "wagmi";
|
|
16
23
|
import { components } from "../../types/api";
|
|
17
24
|
import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod";
|
|
18
25
|
import { FiatPaymentMethod } from "../components/common/FiatPaymentMethod";
|
|
@@ -36,6 +43,7 @@ interface UseAnyspendFlowProps {
|
|
|
36
43
|
sourceTokenAddress?: string;
|
|
37
44
|
sourceTokenChainId?: number;
|
|
38
45
|
slippage?: number;
|
|
46
|
+
disableUrlParamManagement?: boolean;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
export function useAnyspendFlow({
|
|
@@ -48,6 +56,7 @@ export function useAnyspendFlow({
|
|
|
48
56
|
sourceTokenAddress,
|
|
49
57
|
sourceTokenChainId,
|
|
50
58
|
slippage = 0,
|
|
59
|
+
disableUrlParamManagement = false,
|
|
51
60
|
}: UseAnyspendFlowProps) {
|
|
52
61
|
const searchParams = useSearchParamsSSR();
|
|
53
62
|
const router = useRouter();
|
|
@@ -76,6 +85,7 @@ export function useAnyspendFlow({
|
|
|
76
85
|
|
|
77
86
|
// Recipient state
|
|
78
87
|
const { address: globalAddress } = useAccountWallet();
|
|
88
|
+
const { address: wagmiAddress } = useAccount();
|
|
79
89
|
const [selectedRecipientAddress, setSelectedRecipientAddress] = useState<string | undefined>(recipientAddress);
|
|
80
90
|
const recipientProfile = useProfile({ address: selectedRecipientAddress, fresh: true });
|
|
81
91
|
const recipientName = recipientProfile.data?.name;
|
|
@@ -87,11 +97,33 @@ export function useAnyspendFlow({
|
|
|
87
97
|
}
|
|
88
98
|
}, [selectedRecipientAddress, globalAddress]);
|
|
89
99
|
|
|
100
|
+
// Check token balance for crypto payments
|
|
101
|
+
const { rawBalance, isLoading: isBalanceLoading } = useTokenBalance({
|
|
102
|
+
token: selectedSrcToken,
|
|
103
|
+
address: wagmiAddress,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Check if user has enough balance
|
|
107
|
+
const hasEnoughBalance = useMemo(() => {
|
|
108
|
+
if (!rawBalance || isBalanceLoading || paymentType !== "crypto") return false;
|
|
109
|
+
try {
|
|
110
|
+
const requiredAmount = parseUnits(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
|
|
111
|
+
return rawBalance >= requiredAmount;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}, [rawBalance, srcAmount, selectedSrcToken.decimals, isBalanceLoading, paymentType]);
|
|
116
|
+
|
|
117
|
+
// Auto-set crypto payment method based on balance
|
|
90
118
|
useEffect(() => {
|
|
91
|
-
if (paymentType === "crypto") {
|
|
92
|
-
|
|
119
|
+
if (paymentType === "crypto" && !isBalanceLoading) {
|
|
120
|
+
if (hasEnoughBalance) {
|
|
121
|
+
setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
|
|
122
|
+
} else {
|
|
123
|
+
setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.TRANSFER_CRYPTO);
|
|
124
|
+
}
|
|
93
125
|
}
|
|
94
|
-
}, [paymentType]);
|
|
126
|
+
}, [paymentType, hasEnoughBalance, isBalanceLoading]);
|
|
95
127
|
|
|
96
128
|
// Fetch specific token when sourceTokenAddress and sourceTokenChainId are provided
|
|
97
129
|
useEffect(() => {
|
|
@@ -159,14 +191,14 @@ export function useAnyspendFlow({
|
|
|
159
191
|
|
|
160
192
|
// Update useEffect for URL parameter to not override loadOrder
|
|
161
193
|
useEffect(() => {
|
|
162
|
-
if (loadOrder) return; // Skip if we have a loadOrder
|
|
194
|
+
if (loadOrder || disableUrlParamManagement) return; // Skip if we have a loadOrder or URL param management is disabled
|
|
163
195
|
|
|
164
196
|
const orderIdParam = searchParams.get("orderId");
|
|
165
197
|
if (orderIdParam) {
|
|
166
198
|
setOrderId(orderIdParam);
|
|
167
199
|
setActivePanel(PanelView.ORDER_DETAILS);
|
|
168
200
|
}
|
|
169
|
-
}, [searchParams, loadOrder]);
|
|
201
|
+
}, [searchParams, loadOrder, disableUrlParamManagement]);
|
|
170
202
|
|
|
171
203
|
// Order creation hooks
|
|
172
204
|
const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
|
|
@@ -177,16 +209,18 @@ export function useAnyspendFlow({
|
|
|
177
209
|
onOrderSuccess?.(newOrderId);
|
|
178
210
|
|
|
179
211
|
// Add orderId and payment method to URL for persistence
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
212
|
+
if (!disableUrlParamManagement) {
|
|
213
|
+
const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
|
|
214
|
+
params.set("orderId", newOrderId);
|
|
215
|
+
if (selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
|
|
216
|
+
console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
|
|
217
|
+
params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
|
|
218
|
+
} else {
|
|
219
|
+
console.log("Payment method is NONE, not setting in URL");
|
|
220
|
+
}
|
|
221
|
+
console.log("Final URL params:", params.toString());
|
|
222
|
+
router.push(`${window.location.pathname}?${params.toString()}`);
|
|
187
223
|
}
|
|
188
|
-
console.log("Final URL params:", params.toString());
|
|
189
|
-
router.push(`${window.location.pathname}?${params.toString()}`);
|
|
190
224
|
},
|
|
191
225
|
onError: error => {
|
|
192
226
|
console.error(error);
|
|
@@ -245,6 +279,9 @@ export function useAnyspendFlow({
|
|
|
245
279
|
setSelectedRecipientAddress,
|
|
246
280
|
recipientName,
|
|
247
281
|
globalAddress,
|
|
282
|
+
// Balance check
|
|
283
|
+
hasEnoughBalance,
|
|
284
|
+
isBalanceLoading,
|
|
248
285
|
// Quote data
|
|
249
286
|
anyspendQuote,
|
|
250
287
|
isLoadingAnyspendQuote,
|
|
@@ -51,7 +51,7 @@ export interface paths {
|
|
|
51
51
|
decimals: number;
|
|
52
52
|
metadata: {
|
|
53
53
|
/** @description Token logo URI */
|
|
54
|
-
logoURI
|
|
54
|
+
logoURI?: string;
|
|
55
55
|
};
|
|
56
56
|
}[];
|
|
57
57
|
/** @example 200 */
|
|
@@ -880,28 +880,47 @@ export interface components {
|
|
|
880
880
|
* @description Country code
|
|
881
881
|
* @example US
|
|
882
882
|
*/
|
|
883
|
-
country
|
|
883
|
+
country: string;
|
|
884
884
|
/**
|
|
885
885
|
* @description Onramp vendor used
|
|
886
886
|
* @example stripe-web2
|
|
887
887
|
* @enum {string}
|
|
888
888
|
*/
|
|
889
|
-
vendor
|
|
889
|
+
vendor: "coinbase" | "stripe" | "stripe-web2";
|
|
890
890
|
/**
|
|
891
891
|
* @description Payment method used
|
|
892
892
|
* @example
|
|
893
893
|
*/
|
|
894
|
-
paymentMethod
|
|
894
|
+
paymentMethod: string;
|
|
895
895
|
/**
|
|
896
896
|
* @description Redirect URL after payment
|
|
897
897
|
* @example https://www.anyspend.com
|
|
898
898
|
*/
|
|
899
|
-
redirectUrl
|
|
899
|
+
redirectUrl: string;
|
|
900
900
|
/**
|
|
901
901
|
* @description Stripe payment amount in cents
|
|
902
902
|
* @example 9900
|
|
903
903
|
*/
|
|
904
904
|
stripeAmountInCents?: number;
|
|
905
|
+
/**
|
|
906
|
+
* Format: ipv4
|
|
907
|
+
* @description Optional IP address for location detection
|
|
908
|
+
* @example 192.168.1.1
|
|
909
|
+
*/
|
|
910
|
+
ipAddress?: string;
|
|
911
|
+
/** @description Optional fingerprint data for fraud detection */
|
|
912
|
+
fingerprint?: {
|
|
913
|
+
/**
|
|
914
|
+
* @description Fingerprint request ID
|
|
915
|
+
* @example fp_req_12345
|
|
916
|
+
*/
|
|
917
|
+
requestId: string;
|
|
918
|
+
/**
|
|
919
|
+
* @description Fingerprint visitor ID
|
|
920
|
+
* @example fp_visitor_67890
|
|
921
|
+
*/
|
|
922
|
+
visitorId: string;
|
|
923
|
+
};
|
|
905
924
|
};
|
|
906
925
|
/** @description Optional onramp configuration */
|
|
907
926
|
Onramp: {
|
|
@@ -1522,10 +1541,10 @@ export interface components {
|
|
|
1522
1541
|
*/
|
|
1523
1542
|
chain: number;
|
|
1524
1543
|
/**
|
|
1525
|
-
* @description Sender address
|
|
1544
|
+
* @description Sender address (can be null)
|
|
1526
1545
|
* @example 0xa7539e73700B1726aBA29526606442A491Ef5747
|
|
1527
1546
|
*/
|
|
1528
|
-
from
|
|
1547
|
+
from?: string | null;
|
|
1529
1548
|
/**
|
|
1530
1549
|
* @description Transaction hash
|
|
1531
1550
|
* @example 0x60ece99a645201668d20db6775a6b3d30967433ff0750b356cdad46d3e13f9c8
|
|
@@ -4,7 +4,7 @@ import { useState } from "react";
|
|
|
4
4
|
import { ThirdwebProvider, useActiveAccount } from "thirdweb/react";
|
|
5
5
|
import { Account } from "thirdweb/wallets";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Users } from "@b3dotfun/b3-api";
|
|
8
8
|
import { B3Context, B3ContextType } from "./types";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -64,7 +64,7 @@ export function InnerProvider({
|
|
|
64
64
|
theme: "light" | "dark";
|
|
65
65
|
}) {
|
|
66
66
|
const activeAccount = useActiveAccount();
|
|
67
|
-
const [user, setUser] = useState<
|
|
67
|
+
const [user, setUser] = useState<Users | undefined>(undefined);
|
|
68
68
|
|
|
69
69
|
// Use given accountOverride or activeAccount from thirdweb
|
|
70
70
|
const effectiveAccount = accountOverride || activeAccount;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RelayKitProviderWrapper, TooltipProvider, useAuthStore } from "@b3dotfun/sdk/global-account/react";
|
|
2
|
-
import { User } from "@b3dotfun/sdk/global-account/types/b3-api.types";
|
|
3
2
|
import { PermissionsConfig } from "@b3dotfun/sdk/global-account/types/permissions";
|
|
4
3
|
import { loadGA4Script } from "@b3dotfun/sdk/global-account/utils/analytics";
|
|
5
4
|
import { supportedChains } from "@b3dotfun/sdk/shared/constants/chains/supported";
|
|
@@ -18,6 +17,7 @@ import { createConfig, http, WagmiProvider } from "wagmi";
|
|
|
18
17
|
import { StyleRoot } from "../StyleRoot";
|
|
19
18
|
import { B3Context, B3ContextType } from "./types";
|
|
20
19
|
|
|
20
|
+
import { Users } from "@b3dotfun/b3-api";
|
|
21
21
|
import "@reservoir0x/relay-kit-ui/styles.css";
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -115,7 +115,7 @@ export function InnerProvider({
|
|
|
115
115
|
const setActiveWallet = useSetActiveWallet();
|
|
116
116
|
const isAuthenticated = useAuthStore(state => state.isAuthenticated);
|
|
117
117
|
|
|
118
|
-
const [user, setUser] = useState<
|
|
118
|
+
const [user, setUser] = useState<Users | undefined>(undefined);
|
|
119
119
|
|
|
120
120
|
// Use given accountOverride or activeAccount from thirdweb
|
|
121
121
|
const effectiveAccount = isAuthenticated ? accountOverride || activeAccount : undefined;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { User } from "@b3dotfun/sdk/global-account/types/b3-api.types";
|
|
3
|
-
import { Wallet } from "thirdweb/wallets";
|
|
1
|
+
import { Users } from "@b3dotfun/b3-api";
|
|
4
2
|
import { PermissionsConfig } from "@b3dotfun/sdk/global-account/types/permissions";
|
|
5
3
|
import { createContext } from "react";
|
|
4
|
+
import { Account, Wallet } from "thirdweb/wallets";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Context type for B3Provider
|
|
@@ -10,10 +9,10 @@ import { createContext } from "react";
|
|
|
10
9
|
export interface B3ContextType {
|
|
11
10
|
account?: Account;
|
|
12
11
|
automaticallySetFirstEoa: boolean;
|
|
13
|
-
user?:
|
|
12
|
+
user?: Users;
|
|
14
13
|
setWallet: (wallet: Wallet) => void;
|
|
15
14
|
wallet?: Wallet;
|
|
16
|
-
setUser: (user?:
|
|
15
|
+
setUser: (user?: Users) => void;
|
|
17
16
|
initialized: boolean;
|
|
18
17
|
ready: boolean;
|
|
19
18
|
environment?: "development" | "production";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import app from "@b3dotfun/sdk/global-account/app";
|
|
1
2
|
import {
|
|
2
3
|
Button,
|
|
3
4
|
ManageAccountModalProps,
|
|
@@ -8,16 +9,18 @@ import {
|
|
|
8
9
|
TWSignerWithMetadata,
|
|
9
10
|
useAccountAssets,
|
|
10
11
|
useAuthentication,
|
|
12
|
+
useB3,
|
|
11
13
|
useGetAllTWSigners,
|
|
12
14
|
useModalStore,
|
|
15
|
+
useQueryB3,
|
|
13
16
|
useRemoveSessionKey,
|
|
14
17
|
} from "@b3dotfun/sdk/global-account/react";
|
|
15
18
|
import { SignOutIcon } from "@b3dotfun/sdk/global-account/react/components/icons/SignOutIcon";
|
|
16
19
|
import { formatNumber } from "@b3dotfun/sdk/shared/utils/formatNumber";
|
|
17
|
-
|
|
18
20
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
19
|
-
import { BarChart3, Coins, Image, LinkIcon, Loader2, Settings, UnlinkIcon } from "lucide-react";
|
|
20
|
-
import { useState } from "react";
|
|
21
|
+
import { BarChart3, Coins, Copy, Image, LinkIcon, Loader2, Pencil, Settings, UnlinkIcon } from "lucide-react";
|
|
22
|
+
import { useRef, useState } from "react";
|
|
23
|
+
import { toast } from "sonner";
|
|
21
24
|
import { Chain } from "thirdweb";
|
|
22
25
|
import { useActiveAccount, useProfiles, useUnlinkProfile } from "thirdweb/react";
|
|
23
26
|
import { formatUnits } from "viem";
|
|
@@ -26,6 +29,7 @@ import { getProfileDisplayInfo } from "../../utils/profileDisplay";
|
|
|
26
29
|
import { AccountAssets } from "../AccountAssets/AccountAssets";
|
|
27
30
|
import { ContentTokens } from "./ContentTokens";
|
|
28
31
|
|
|
32
|
+
import { Referrals, Users } from "@b3dotfun/b3-api";
|
|
29
33
|
import { BalanceContent } from "./BalanceContent";
|
|
30
34
|
|
|
31
35
|
type TabValue = "overview" | "tokens" | "nfts" | "apps" | "settings";
|
|
@@ -130,6 +134,50 @@ export function ManageAccount({
|
|
|
130
134
|
const { data: profilesRaw = [], isLoading: isLoadingProfiles } = useProfiles({ client });
|
|
131
135
|
const { mutate: unlinkProfile, isPending: isUnlinking } = useUnlinkProfile();
|
|
132
136
|
const { setB3ModalOpen, setB3ModalContentType, isLinking } = useModalStore();
|
|
137
|
+
const { user, setUser } = useB3();
|
|
138
|
+
const [isUpdatingCode, setIsUpdatingCode] = useState(false);
|
|
139
|
+
const [newReferralCode, setNewReferralCode] = useState("");
|
|
140
|
+
const [isEditingCode, setIsEditingCode] = useState(false);
|
|
141
|
+
const referallCodeRef = useRef<HTMLInputElement>(null);
|
|
142
|
+
const { data: referrals, isLoading: isLoadingReferrals } = useQueryB3(
|
|
143
|
+
"referrals",
|
|
144
|
+
"find",
|
|
145
|
+
{ query: { referrerId: user?.userId } },
|
|
146
|
+
!!user?.userId,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Fetch referred users
|
|
150
|
+
const currentReferralCode = user?.referralCode || user?.userId || "";
|
|
151
|
+
|
|
152
|
+
const handleCopyCode = async () => {
|
|
153
|
+
try {
|
|
154
|
+
await navigator.clipboard.writeText(currentReferralCode);
|
|
155
|
+
toast.success("Referral code copied to clipboard!");
|
|
156
|
+
} catch (error) {
|
|
157
|
+
toast.error("Failed to copy referral code");
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const handleUpdateReferralCode = async () => {
|
|
162
|
+
if (!newReferralCode) return;
|
|
163
|
+
|
|
164
|
+
setIsUpdatingCode(true);
|
|
165
|
+
try {
|
|
166
|
+
// @ts-expect-error - setReferralCode is not typed for some reason
|
|
167
|
+
const newUser = await app.service("users").setReferralCode({
|
|
168
|
+
userId: user?.userId,
|
|
169
|
+
referralCode: newReferralCode,
|
|
170
|
+
});
|
|
171
|
+
setUser(newUser as unknown as Users);
|
|
172
|
+
toast.success("Referral code updated successfully!");
|
|
173
|
+
setIsEditingCode(false);
|
|
174
|
+
setNewReferralCode("");
|
|
175
|
+
} catch (error) {
|
|
176
|
+
toast.error("Failed to update referral code");
|
|
177
|
+
} finally {
|
|
178
|
+
setIsUpdatingCode(false);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
133
181
|
|
|
134
182
|
const profiles = profilesRaw
|
|
135
183
|
.filter((profile: any) => !["custom_auth_endpoint", "siwe"].includes(profile.type))
|
|
@@ -241,6 +289,102 @@ export function ManageAccount({
|
|
|
241
289
|
)}
|
|
242
290
|
</div>
|
|
243
291
|
|
|
292
|
+
{/* Referral Section */}
|
|
293
|
+
<div className="space-y-4">
|
|
294
|
+
<h3 className="text-b3-grey font-neue-montreal-semibold text-xl">Referrals</h3>
|
|
295
|
+
|
|
296
|
+
{/* Referral Code */}
|
|
297
|
+
<div className="bg-b3-line rounded-xl p-4">
|
|
298
|
+
{isEditingCode && (
|
|
299
|
+
<div>
|
|
300
|
+
<div className="text-b3-grey font-neue-montreal-semibold">Your Referral Code</div>
|
|
301
|
+
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
|
|
302
|
+
Share this code with friends to earn rewards
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
<div className="flex items-center justify-between">
|
|
307
|
+
{!isEditingCode && (
|
|
308
|
+
<div>
|
|
309
|
+
<div className="text-b3-grey font-neue-montreal-semibold">Your Referral Code</div>
|
|
310
|
+
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
|
|
311
|
+
Share this code with friends to earn rewards
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
)}
|
|
315
|
+
<div className="flex items-center gap-2">
|
|
316
|
+
{isEditingCode ? (
|
|
317
|
+
<div className="flex items-center gap-2">
|
|
318
|
+
<input
|
|
319
|
+
type="text"
|
|
320
|
+
value={newReferralCode}
|
|
321
|
+
onChange={e => setNewReferralCode(e.target.value)}
|
|
322
|
+
className="rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm"
|
|
323
|
+
placeholder="Enter new code"
|
|
324
|
+
ref={referallCodeRef}
|
|
325
|
+
/>
|
|
326
|
+
<Button size="sm" onClick={handleUpdateReferralCode} disabled={isUpdatingCode || !newReferralCode}>
|
|
327
|
+
{isUpdatingCode ? <Loader2 className="h-4 w-4 animate-spin" /> : "Save"}
|
|
328
|
+
</Button>
|
|
329
|
+
<Button
|
|
330
|
+
size="sm"
|
|
331
|
+
variant="ghost"
|
|
332
|
+
onClick={() => {
|
|
333
|
+
setIsEditingCode(false);
|
|
334
|
+
setNewReferralCode("");
|
|
335
|
+
}}
|
|
336
|
+
>
|
|
337
|
+
Cancel
|
|
338
|
+
</Button>
|
|
339
|
+
</div>
|
|
340
|
+
) : (
|
|
341
|
+
<>
|
|
342
|
+
<div className="rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm">
|
|
343
|
+
{currentReferralCode}
|
|
344
|
+
</div>
|
|
345
|
+
<Button size="icon" variant="ghost" onClick={handleCopyCode}>
|
|
346
|
+
<Copy className="h-4 w-4" />
|
|
347
|
+
</Button>
|
|
348
|
+
<Button
|
|
349
|
+
size="icon"
|
|
350
|
+
variant="ghost"
|
|
351
|
+
onClick={() => {
|
|
352
|
+
setIsEditingCode(true);
|
|
353
|
+
setTimeout(() => {
|
|
354
|
+
referallCodeRef.current?.focus();
|
|
355
|
+
}, 100);
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
<Pencil className="h-4 w-4" />
|
|
359
|
+
</Button>
|
|
360
|
+
</>
|
|
361
|
+
)}
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
{/* Referred Users */}
|
|
367
|
+
<div className="bg-b3-line rounded-xl p-4">
|
|
368
|
+
<div className="text-b3-grey font-neue-montreal-semibold mb-4">Referred Users</div>
|
|
369
|
+
{isLoadingReferrals ? (
|
|
370
|
+
<div className="flex justify-center py-4">
|
|
371
|
+
<Loader2 className="h-6 w-6 animate-spin text-gray-400" />
|
|
372
|
+
</div>
|
|
373
|
+
) : referrals?.data?.length ? (
|
|
374
|
+
<div className="space-y-3">
|
|
375
|
+
{referrals.data.map((referral: Referrals) => (
|
|
376
|
+
<div key={String(referral._id)} className="flex items-center justify-between rounded-lg bg-white p-3">
|
|
377
|
+
<div className="text-sm font-medium">{referral.referreeId}</div>
|
|
378
|
+
<div className="text-sm text-gray-500">{new Date(referral.createdAt).toLocaleDateString()}</div>
|
|
379
|
+
</div>
|
|
380
|
+
))}
|
|
381
|
+
</div>
|
|
382
|
+
) : (
|
|
383
|
+
<div className="py-4 text-center text-gray-500">No referred users yet</div>
|
|
384
|
+
)}
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
|
|
244
388
|
{/* Additional Settings Sections */}
|
|
245
389
|
<div className="space-y-4">
|
|
246
390
|
<h3 className="text-b3-grey font-neue-montreal-semibold text-xl">Account Preferences</h3>
|
|
@@ -5,13 +5,13 @@ import { Account } from "thirdweb/wallets";
|
|
|
5
5
|
import { useSearchParam } from "./useSearchParamsSSR";
|
|
6
6
|
|
|
7
7
|
export function useSiwe() {
|
|
8
|
-
const
|
|
8
|
+
const referralCode = useSearchParam("referralCode");
|
|
9
9
|
|
|
10
10
|
const authenticate = useCallback(
|
|
11
11
|
async (account: Account, partnerId: string) => {
|
|
12
12
|
if (!account || !account.signMessage) throw new Error("Account not found");
|
|
13
13
|
|
|
14
|
-
console.log("@@useAuthenticate:
|
|
14
|
+
console.log("@@useAuthenticate:referralCode", referralCode);
|
|
15
15
|
// generate challenge
|
|
16
16
|
const challenge = await app.service("global-accounts-challenge").create({
|
|
17
17
|
address: account.address,
|
|
@@ -32,15 +32,15 @@ export function useSiwe() {
|
|
|
32
32
|
signature,
|
|
33
33
|
serverSignature: challenge.serverSignature,
|
|
34
34
|
nonce: challenge.nonce,
|
|
35
|
-
// http://localhost:5173/?
|
|
36
|
-
|
|
35
|
+
// http://localhost:5173/?referralCode=GIO2
|
|
36
|
+
referralCode,
|
|
37
37
|
partnerId: partnerId,
|
|
38
38
|
});
|
|
39
39
|
debug("@@useAuthenticate:response", response);
|
|
40
40
|
|
|
41
41
|
return response;
|
|
42
42
|
},
|
|
43
|
-
[
|
|
43
|
+
[referralCode],
|
|
44
44
|
);
|
|
45
45
|
|
|
46
46
|
return {
|