@b3dotfun/sdk 0.0.30 → 0.0.31-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/AnySpend.js +1 -1
- package/dist/cjs/anyspend/react/components/AnySpendBuySpin.js +2 -1
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3.js +2 -1
- package/dist/cjs/anyspend/react/components/AnyspendDepositHype.d.ts +4 -0
- package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +6 -1
- package/dist/cjs/anyspend/react/components/common/ChainTokenIcon.d.ts +1 -1
- package/dist/cjs/anyspend/react/components/common/ChainTokenIcon.js +2 -1
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +23 -28
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +2 -2
- package/dist/cjs/anyspend/react/components/common/OrderDetails.js +5 -5
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +1 -1
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.d.ts +4 -1
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +3 -3
- package/dist/cjs/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +2 -5
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +5 -0
- package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.js +1 -0
- package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.d.ts +6 -0
- package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.js +94 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ContentTokens.d.ts +14 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ContentTokens.js +272 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +9 -51
- package/dist/cjs/global-account/react/components/ManageAccount/TokenBalanceRow.d.ts +10 -0
- package/dist/cjs/global-account/react/components/ManageAccount/TokenBalanceRow.js +8 -0
- package/dist/cjs/global-account/react/components/TokenIcon.d.ts +11 -0
- package/dist/cjs/global-account/react/components/TokenIcon.js +43 -0
- package/dist/cjs/global-account/react/components/ui/accordion.d.ts +7 -0
- package/dist/cjs/global-account/react/components/ui/accordion.js +53 -0
- package/dist/cjs/global-account/react/components/ui/dialog.js +1 -1
- package/dist/cjs/global-account/react/hooks/index.d.ts +2 -0
- package/dist/cjs/global-account/react/hooks/index.js +5 -1
- package/dist/cjs/global-account/react/hooks/useAnalytics.d.ts +7 -0
- package/dist/cjs/global-account/react/hooks/useAnalytics.js +29 -0
- package/dist/cjs/global-account/react/hooks/useB3BalanceFromAddresses.js +2 -1
- package/dist/cjs/global-account/react/hooks/useNativeBalance.js +2 -1
- package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +24 -0
- package/dist/cjs/global-account/react/hooks/useSimBalance.js +29 -0
- package/dist/cjs/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +2 -1
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +2 -2
- package/dist/cjs/global-account/react/utils/profileDisplay.js +9 -0
- package/dist/cjs/global-account/utils/analytics.d.ts +16 -0
- package/dist/cjs/global-account/utils/analytics.js +55 -0
- package/dist/cjs/shared/constants/index.d.ts +1 -0
- package/dist/cjs/shared/constants/index.js +2 -1
- package/dist/cjs/shared/generated/chain-networks.json +185 -17
- package/dist/esm/anyspend/react/components/AnySpend.js +1 -1
- package/dist/esm/anyspend/react/components/AnySpendBuySpin.js +2 -1
- package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +2 -1
- package/dist/esm/anyspend/react/components/AnyspendDepositHype.d.ts +4 -0
- package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +5 -1
- package/dist/esm/anyspend/react/components/common/ChainTokenIcon.d.ts +1 -1
- package/dist/esm/anyspend/react/components/common/ChainTokenIcon.js +2 -1
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +22 -27
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +2 -2
- package/dist/esm/anyspend/react/components/common/OrderDetails.js +5 -5
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +1 -1
- package/dist/esm/anyspend/react/components/common/PanelOnramp.d.ts +4 -1
- package/dist/esm/anyspend/react/components/common/PanelOnramp.js +4 -4
- package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/esm/global-account/react/components/B3DynamicModal.js +2 -5
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +5 -0
- package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.js +1 -0
- package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.d.ts +6 -0
- package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.js +88 -0
- package/dist/esm/global-account/react/components/ManageAccount/ContentTokens.d.ts +14 -0
- package/dist/esm/global-account/react/components/ManageAccount/ContentTokens.js +266 -0
- package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +12 -51
- package/dist/esm/global-account/react/components/ManageAccount/TokenBalanceRow.d.ts +10 -0
- package/dist/esm/global-account/react/components/ManageAccount/TokenBalanceRow.js +5 -0
- package/dist/esm/global-account/react/components/TokenIcon.d.ts +11 -0
- package/dist/esm/global-account/react/components/TokenIcon.js +37 -0
- package/dist/esm/global-account/react/components/ui/accordion.d.ts +7 -0
- package/dist/esm/global-account/react/components/ui/accordion.js +14 -0
- package/dist/esm/global-account/react/components/ui/dialog.js +1 -1
- package/dist/esm/global-account/react/hooks/index.d.ts +2 -0
- package/dist/esm/global-account/react/hooks/index.js +2 -0
- package/dist/esm/global-account/react/hooks/useAnalytics.d.ts +7 -0
- package/dist/esm/global-account/react/hooks/useAnalytics.js +26 -0
- package/dist/esm/global-account/react/hooks/useB3BalanceFromAddresses.js +2 -1
- package/dist/esm/global-account/react/hooks/useNativeBalance.js +2 -1
- package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +24 -0
- package/dist/esm/global-account/react/hooks/useSimBalance.js +26 -0
- package/dist/esm/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +2 -1
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +2 -2
- package/dist/esm/global-account/react/utils/profileDisplay.js +9 -0
- package/dist/esm/global-account/utils/analytics.d.ts +16 -0
- package/dist/esm/global-account/utils/analytics.js +50 -0
- package/dist/esm/shared/constants/index.d.ts +1 -0
- package/dist/esm/shared/constants/index.js +1 -0
- package/dist/esm/shared/generated/chain-networks.json +185 -17
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/AnyspendDepositHype.d.ts +4 -0
- package/dist/types/anyspend/react/components/common/ChainTokenIcon.d.ts +1 -1
- package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
- package/dist/types/anyspend/react/components/common/PanelOnramp.d.ts +4 -1
- package/dist/types/global-account/react/components/ManageAccount/BalanceContent.d.ts +6 -0
- package/dist/types/global-account/react/components/ManageAccount/ContentTokens.d.ts +14 -0
- package/dist/types/global-account/react/components/ManageAccount/TokenBalanceRow.d.ts +10 -0
- package/dist/types/global-account/react/components/TokenIcon.d.ts +11 -0
- package/dist/types/global-account/react/components/ui/accordion.d.ts +7 -0
- package/dist/types/global-account/react/hooks/index.d.ts +2 -0
- package/dist/types/global-account/react/hooks/useAnalytics.d.ts +7 -0
- package/dist/types/global-account/react/hooks/useSimBalance.d.ts +24 -0
- package/dist/types/global-account/react/stores/useModalStore.d.ts +2 -2
- package/dist/types/global-account/utils/analytics.d.ts +16 -0
- package/dist/types/shared/constants/index.d.ts +1 -0
- package/package.json +10 -18
- package/src/anyspend/react/components/AnySpend.tsx +1 -0
- package/src/anyspend/react/components/AnySpendBuySpin.tsx +2 -1
- package/src/anyspend/react/components/AnySpendStakeB3.tsx +3 -2
- package/src/anyspend/react/components/AnyspendDepositHype.tsx +10 -0
- package/src/anyspend/react/components/AnyspendSignatureMint.tsx +4 -4
- package/src/anyspend/react/components/common/ChainTokenIcon.tsx +8 -2
- package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +56 -107
- package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +12 -3
- package/src/anyspend/react/components/common/OrderDetails.tsx +5 -5
- package/src/anyspend/react/components/common/OrderTokenAmount.tsx +2 -2
- package/src/anyspend/react/components/common/PanelOnramp.tsx +11 -5
- package/src/anyspend/react/components/common/PaySection.tsx +1 -1
- package/src/global-account/react/components/B3DynamicModal.tsx +8 -7
- package/src/global-account/react/components/B3Provider/B3Provider.tsx +6 -0
- package/src/global-account/react/components/LinkAccount/LinkAccount.tsx +2 -1
- package/src/global-account/react/components/ManageAccount/BalanceContent.tsx +228 -0
- package/src/global-account/react/components/ManageAccount/ContentTokens.tsx +568 -0
- package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +86 -341
- package/src/global-account/react/components/ManageAccount/TokenBalanceRow.tsx +46 -0
- package/src/global-account/react/components/TokenIcon.tsx +87 -0
- package/src/global-account/react/components/ui/accordion.tsx +53 -0
- package/src/global-account/react/components/ui/dialog.tsx +1 -1
- package/src/global-account/react/hooks/index.ts +2 -0
- package/src/global-account/react/hooks/useAccountAssets.ts +1 -0
- package/src/global-account/react/hooks/useAnalytics.tsx +30 -0
- package/src/global-account/react/hooks/useB3BalanceFromAddresses.ts +3 -2
- package/src/global-account/react/hooks/useNativeBalance.tsx +2 -1
- package/src/global-account/react/hooks/useSimBalance.ts +56 -0
- package/src/global-account/react/hooks/useUnifiedChainSwitchAndExecute.ts +3 -1
- package/src/global-account/react/stores/useModalStore.ts +2 -2
- package/src/global-account/react/utils/profileDisplay.ts +9 -0
- package/src/global-account/utils/analytics.ts +64 -0
- package/src/shared/constants/index.ts +2 -0
- package/src/shared/generated/chain-networks.json +185 -17
- package/src/{anyspend/types → types}/window.d.ts +5 -1
- package/dist/cjs/index.d.ts +0 -0
- package/dist/cjs/index.js +0 -2
- package/dist/esm/index.d.ts +0 -0
- package/dist/esm/index.js +0 -2
- package/dist/types/index.d.ts +0 -0
- package/src/index.ts +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Button,
|
|
3
|
-
CopyToClipboard,
|
|
4
3
|
ManageAccountModalProps,
|
|
5
4
|
TabsContentPrimitive,
|
|
6
5
|
TabsListPrimitive,
|
|
@@ -9,29 +8,27 @@ import {
|
|
|
9
8
|
TWSignerWithMetadata,
|
|
10
9
|
useAccountAssets,
|
|
11
10
|
useAuthentication,
|
|
12
|
-
useB3BalanceFromAddresses,
|
|
13
11
|
useGetAllTWSigners,
|
|
14
12
|
useModalStore,
|
|
15
|
-
useNativeBalance,
|
|
16
|
-
useProfile,
|
|
17
13
|
useRemoveSessionKey,
|
|
18
14
|
} from "@b3dotfun/sdk/global-account/react";
|
|
19
|
-
import { BankIcon } from "@b3dotfun/sdk/global-account/react/components/icons/BankIcon";
|
|
20
15
|
import { SignOutIcon } from "@b3dotfun/sdk/global-account/react/components/icons/SignOutIcon";
|
|
21
|
-
import { SwapIcon } from "@b3dotfun/sdk/global-account/react/components/icons/SwapIcon";
|
|
22
|
-
import { formatUsername } from "@b3dotfun/sdk/shared/utils";
|
|
23
16
|
import { formatNumber } from "@b3dotfun/sdk/shared/utils/formatNumber";
|
|
17
|
+
|
|
24
18
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
25
|
-
import { LinkIcon, Loader2,
|
|
19
|
+
import { BarChart3, Coins, Image, LinkIcon, Loader2, Settings, UnlinkIcon } from "lucide-react";
|
|
26
20
|
import { useState } from "react";
|
|
27
21
|
import { Chain } from "thirdweb";
|
|
28
22
|
import { useActiveAccount, useProfiles, useUnlinkProfile } from "thirdweb/react";
|
|
29
23
|
import { formatUnits } from "viem";
|
|
30
|
-
|
|
24
|
+
|
|
31
25
|
import { getProfileDisplayInfo } from "../../utils/profileDisplay";
|
|
32
26
|
import { AccountAssets } from "../AccountAssets/AccountAssets";
|
|
27
|
+
import { ContentTokens } from "./ContentTokens";
|
|
33
28
|
|
|
34
|
-
|
|
29
|
+
import { BalanceContent } from "./BalanceContent";
|
|
30
|
+
|
|
31
|
+
type TabValue = "overview" | "tokens" | "nfts" | "apps" | "settings";
|
|
35
32
|
|
|
36
33
|
interface ManageAccountProps {
|
|
37
34
|
onLogout?: () => void;
|
|
@@ -43,11 +40,6 @@ interface ManageAccountProps {
|
|
|
43
40
|
containerClassName?: string;
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
function centerTruncate(str: string, length = 4) {
|
|
47
|
-
if (str.length <= length * 2) return str;
|
|
48
|
-
return `${str.slice(0, length)}...${str.slice(-length)}`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
43
|
export function ManageAccount({
|
|
52
44
|
onLogout,
|
|
53
45
|
onSwap: _onSwap,
|
|
@@ -57,28 +49,17 @@ export function ManageAccount({
|
|
|
57
49
|
}: ManageAccountProps) {
|
|
58
50
|
const [revokingSignerId, setRevokingSignerId] = useState<string | null>(null);
|
|
59
51
|
const account = useActiveAccount();
|
|
60
|
-
const { data:
|
|
61
|
-
|
|
62
|
-
const { data: nativeBalance } = useNativeBalance(account?.address);
|
|
63
|
-
const { address: eoaAddress } = useFirstEOA();
|
|
64
|
-
const { data: profile } = useProfile({
|
|
65
|
-
address: eoaAddress || account?.address,
|
|
66
|
-
fresh: true,
|
|
67
|
-
});
|
|
68
|
-
const { data: eoaNativeBalance } = useNativeBalance(eoaAddress);
|
|
69
|
-
const { data: eoaB3Balance } = useB3BalanceFromAddresses(eoaAddress);
|
|
52
|
+
const { data: nfts, isLoading } = useAccountAssets(account?.address);
|
|
53
|
+
|
|
70
54
|
const { data: signers, refetch: refetchSigners } = useGetAllTWSigners({
|
|
71
55
|
chain,
|
|
72
56
|
accountAddress: account?.address,
|
|
73
57
|
});
|
|
74
|
-
const { setB3ModalOpen,
|
|
75
|
-
const { activeTab = "
|
|
58
|
+
const { setB3ModalOpen, contentType } = useModalStore();
|
|
59
|
+
const { activeTab = "overview", setActiveTab } = contentType as ManageAccountModalProps;
|
|
76
60
|
const { logout } = useAuthentication(partnerId);
|
|
77
61
|
const [logoutLoading, setLogoutLoading] = useState(false);
|
|
78
62
|
|
|
79
|
-
console.log("account", account);
|
|
80
|
-
console.log("eoaAddress", eoaAddress);
|
|
81
|
-
|
|
82
63
|
const { removeSessionKey } = useRemoveSessionKey({
|
|
83
64
|
chain,
|
|
84
65
|
onSuccess: tx => {
|
|
@@ -105,275 +86,6 @@ export function ManageAccount({
|
|
|
105
86
|
setLogoutLoading(false);
|
|
106
87
|
};
|
|
107
88
|
|
|
108
|
-
const BalanceContent = () => {
|
|
109
|
-
const { info: eoaInfo } = useFirstEOA();
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<div className="flex flex-col gap-6">
|
|
113
|
-
{/* Profile Section */}
|
|
114
|
-
<div className="flex items-center justify-between">
|
|
115
|
-
<div className="flex items-center gap-4">
|
|
116
|
-
<div className="relative">
|
|
117
|
-
{profile?.avatar ? (
|
|
118
|
-
<img src={profile?.avatar} alt="Profile" className="size-24 rounded-full" />
|
|
119
|
-
) : (
|
|
120
|
-
<div className="bg-b3-primary-wash size-24 rounded-full" />
|
|
121
|
-
)}
|
|
122
|
-
<div className="bg-b3-grey border-b3-background absolute -bottom-1 -right-1 flex size-8 items-center justify-center rounded-full border-4">
|
|
123
|
-
<Pencil size={16} className="text-b3-background" />
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
<div>
|
|
127
|
-
<h2 className="text-b3-grey text-xl font-semibold">
|
|
128
|
-
{profile?.displayName || formatUsername(profile?.name || "")}
|
|
129
|
-
</h2>
|
|
130
|
-
<span className="text-b3-foreground-muted">{formatUsername(profile?.name || "")}</span>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
134
|
-
<div className="manage-account-address bg-b3-line flex h-11 items-center gap-2 rounded-full px-4">
|
|
135
|
-
<span className="text-b3-grey font-neue-montreal-semibold">{centerTruncate(account?.address || "")}</span>
|
|
136
|
-
<CopyToClipboard text={account?.address || ""} />
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
{/* Quick Actions */}
|
|
140
|
-
<div className="grid grid-cols-2 gap-3">
|
|
141
|
-
<Button
|
|
142
|
-
className="manage-account-deposit bg-b3-primary-wash hover:bg-b3-primary-wash/70 h-[84px] w-full flex-col items-start gap-2 rounded-2xl"
|
|
143
|
-
onClick={() => {
|
|
144
|
-
setB3ModalOpen(true);
|
|
145
|
-
setB3ModalContentType({
|
|
146
|
-
type: "anySpend",
|
|
147
|
-
defaultActiveTab: "fiat",
|
|
148
|
-
showBackButton: true,
|
|
149
|
-
});
|
|
150
|
-
}}
|
|
151
|
-
>
|
|
152
|
-
<BankIcon size={24} className="text-b3-primary-blue shrink-0" />
|
|
153
|
-
<div className="text-b3-grey font-neue-montreal-semibold">Deposit</div>
|
|
154
|
-
</Button>
|
|
155
|
-
<Button
|
|
156
|
-
className="manage-account-swap bg-b3-primary-wash hover:bg-b3-primary-wash/70 flex h-[84px] w-full flex-col items-start gap-2 rounded-2xl"
|
|
157
|
-
onClick={() => {
|
|
158
|
-
setB3ModalOpen(true);
|
|
159
|
-
setB3ModalContentType({
|
|
160
|
-
type: "anySpend",
|
|
161
|
-
showBackButton: true,
|
|
162
|
-
});
|
|
163
|
-
}}
|
|
164
|
-
>
|
|
165
|
-
<SwapIcon size={24} className="text-b3-primary-blue" />
|
|
166
|
-
<div className="text-b3-grey font-neue-montreal-semibold">Swap</div>
|
|
167
|
-
</Button>
|
|
168
|
-
</div>
|
|
169
|
-
|
|
170
|
-
{/* Balance Section */}
|
|
171
|
-
<div className="space-y-4">
|
|
172
|
-
<h3 className="text-b3-grey font-neue-montreal-semibold">Balance</h3>
|
|
173
|
-
|
|
174
|
-
{/* B3 Balance */}
|
|
175
|
-
<div className="flex items-center justify-between">
|
|
176
|
-
<div className="flex items-center gap-3">
|
|
177
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-full">
|
|
178
|
-
<img src="https://cdn.b3.fun/b3-coin-3d.png" alt="B3" className="size-10" />
|
|
179
|
-
</div>
|
|
180
|
-
<div>
|
|
181
|
-
<div className="flex items-center gap-2">
|
|
182
|
-
<span className="text-b3-grey font-neue-montreal-semibold">B3</span>
|
|
183
|
-
</div>
|
|
184
|
-
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
|
|
185
|
-
{b3Balance?.formattedTotal || "0.00"} B3
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
<div className="text-right">
|
|
190
|
-
<div className="text-b3-grey font-neue-montreal-semibold">
|
|
191
|
-
${b3Balance?.balanceUsdFormatted || "0.00"}
|
|
192
|
-
</div>
|
|
193
|
-
<div className="flex items-center gap-1">
|
|
194
|
-
{b3Balance?.priceChange24h !== null && b3Balance?.priceChange24h !== undefined ? (
|
|
195
|
-
<>
|
|
196
|
-
<Triangle
|
|
197
|
-
className={`size-3 ${b3Balance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
|
|
198
|
-
/>
|
|
199
|
-
<span
|
|
200
|
-
className={`font-neue-montreal-medium text-sm ${b3Balance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
|
|
201
|
-
>
|
|
202
|
-
{b3Balance.priceChange24h >= 0 ? "+" : ""}
|
|
203
|
-
{b3Balance.priceChange24h.toFixed(2)}%
|
|
204
|
-
</span>
|
|
205
|
-
</>
|
|
206
|
-
) : (
|
|
207
|
-
<span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
|
|
208
|
-
)}
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
|
|
213
|
-
{/* ETH Balance */}
|
|
214
|
-
<div className="flex items-center justify-between">
|
|
215
|
-
<div className="flex items-center gap-3">
|
|
216
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-full">
|
|
217
|
-
<img src="https://cdn.b3.fun/ethereum.svg" alt="ETH" className="size-10" />
|
|
218
|
-
</div>
|
|
219
|
-
<div>
|
|
220
|
-
<div className="flex items-center gap-2">
|
|
221
|
-
<span className="text-b3-grey font-neue-montreal-semibold">Ethereum</span>
|
|
222
|
-
</div>
|
|
223
|
-
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
|
|
224
|
-
{nativeBalance?.formattedTotal || "0.00"} ETH
|
|
225
|
-
</div>
|
|
226
|
-
</div>
|
|
227
|
-
</div>
|
|
228
|
-
<div className="text-right">
|
|
229
|
-
<div className="text-b3-grey font-neue-montreal-semibold">
|
|
230
|
-
${nativeBalance?.formattedTotalUsd || "0.00"}
|
|
231
|
-
</div>
|
|
232
|
-
<div className="flex items-center gap-2">
|
|
233
|
-
{nativeBalance?.priceChange24h !== null && nativeBalance?.priceChange24h !== undefined ? (
|
|
234
|
-
<>
|
|
235
|
-
<Triangle
|
|
236
|
-
className={`size-3 ${nativeBalance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
|
|
237
|
-
/>
|
|
238
|
-
<span
|
|
239
|
-
className={`font-neue-montreal-medium text-sm ${nativeBalance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
|
|
240
|
-
>
|
|
241
|
-
{nativeBalance.priceChange24h >= 0 ? "+" : ""}
|
|
242
|
-
{nativeBalance.priceChange24h.toFixed(2)}%
|
|
243
|
-
</span>
|
|
244
|
-
</>
|
|
245
|
-
) : (
|
|
246
|
-
<span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
|
|
247
|
-
)}
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
|
|
253
|
-
{/* EOA Account Balance Section - matching global balance styling */}
|
|
254
|
-
{eoaAddress && (
|
|
255
|
-
<div className="space-y-4">
|
|
256
|
-
<h3 className="text-b3-grey font-neue-montreal-semibold">Connected {eoaInfo?.data?.name || "Wallet"}</h3>
|
|
257
|
-
|
|
258
|
-
{/* EOA Address */}
|
|
259
|
-
<div className="manage-account-address bg-b3-line flex h-11 items-center gap-2 rounded-full px-4">
|
|
260
|
-
<span className="text-b3-grey font-neue-montreal-semibold">{centerTruncate(eoaAddress)}</span>
|
|
261
|
-
<CopyToClipboard text={eoaAddress} />
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
{/* EOA B3 Balance */}
|
|
265
|
-
<div className="flex items-center justify-between">
|
|
266
|
-
<div className="flex items-center gap-3">
|
|
267
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-full">
|
|
268
|
-
<img src="https://cdn.b3.fun/b3-coin-3d.png" alt="B3" className="size-10" />
|
|
269
|
-
</div>
|
|
270
|
-
<div>
|
|
271
|
-
<div className="flex items-center gap-2">
|
|
272
|
-
<span className="text-b3-grey font-neue-montreal-semibold">B3</span>
|
|
273
|
-
</div>
|
|
274
|
-
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
|
|
275
|
-
{eoaB3Balance?.formattedTotal || "0.00"} B3
|
|
276
|
-
</div>
|
|
277
|
-
</div>
|
|
278
|
-
</div>
|
|
279
|
-
<div className="text-right">
|
|
280
|
-
<div className="text-b3-grey font-neue-montreal-semibold">
|
|
281
|
-
${eoaB3Balance?.balanceUsdFormatted || "0.00"}
|
|
282
|
-
</div>
|
|
283
|
-
<div className="flex items-center gap-1">
|
|
284
|
-
{eoaB3Balance?.priceChange24h !== null && eoaB3Balance?.priceChange24h !== undefined ? (
|
|
285
|
-
<>
|
|
286
|
-
<Triangle
|
|
287
|
-
className={`size-3 ${eoaB3Balance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
|
|
288
|
-
/>
|
|
289
|
-
<span
|
|
290
|
-
className={`font-neue-montreal-medium text-sm ${eoaB3Balance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
|
|
291
|
-
>
|
|
292
|
-
{eoaB3Balance.priceChange24h >= 0 ? "+" : ""}
|
|
293
|
-
{eoaB3Balance.priceChange24h.toFixed(2)}%
|
|
294
|
-
</span>
|
|
295
|
-
</>
|
|
296
|
-
) : (
|
|
297
|
-
<span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
|
|
298
|
-
)}
|
|
299
|
-
</div>
|
|
300
|
-
</div>
|
|
301
|
-
</div>
|
|
302
|
-
|
|
303
|
-
{/* EOA ETH Balance */}
|
|
304
|
-
<div className="flex items-center justify-between">
|
|
305
|
-
<div className="flex items-center gap-3">
|
|
306
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-full">
|
|
307
|
-
<img src="https://cdn.b3.fun/ethereum.svg" alt="ETH" className="size-10" />
|
|
308
|
-
</div>
|
|
309
|
-
<div>
|
|
310
|
-
<div className="flex items-center gap-2">
|
|
311
|
-
<span className="text-b3-grey font-neue-montreal-semibold">Ethereum</span>
|
|
312
|
-
</div>
|
|
313
|
-
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
|
|
314
|
-
{eoaNativeBalance?.formattedTotal || "0.00"} ETH
|
|
315
|
-
</div>
|
|
316
|
-
</div>
|
|
317
|
-
</div>
|
|
318
|
-
<div className="text-right">
|
|
319
|
-
<div className="text-b3-grey font-neue-montreal-semibold">
|
|
320
|
-
${eoaNativeBalance?.formattedTotalUsd || "0.00"}
|
|
321
|
-
</div>
|
|
322
|
-
<div className="flex items-center gap-2">
|
|
323
|
-
{eoaNativeBalance?.priceChange24h !== null && eoaNativeBalance?.priceChange24h !== undefined ? (
|
|
324
|
-
<>
|
|
325
|
-
<Triangle
|
|
326
|
-
className={`size-3 ${eoaNativeBalance.priceChange24h >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
|
|
327
|
-
/>
|
|
328
|
-
<span
|
|
329
|
-
className={`font-neue-montreal-medium text-sm ${eoaNativeBalance.priceChange24h >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
|
|
330
|
-
>
|
|
331
|
-
{eoaNativeBalance.priceChange24h >= 0 ? "+" : ""}
|
|
332
|
-
{eoaNativeBalance.priceChange24h.toFixed(2)}%
|
|
333
|
-
</span>
|
|
334
|
-
</>
|
|
335
|
-
) : (
|
|
336
|
-
<span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
|
|
337
|
-
)}
|
|
338
|
-
</div>
|
|
339
|
-
</div>
|
|
340
|
-
</div>
|
|
341
|
-
</div>
|
|
342
|
-
)}
|
|
343
|
-
|
|
344
|
-
{/* Global Account Info */}
|
|
345
|
-
<div className="border-b3-line flex items-center justify-between rounded-2xl border p-4">
|
|
346
|
-
<div className="">
|
|
347
|
-
<div className="flex items-center gap-2">
|
|
348
|
-
<img src="https://cdn.b3.fun/b3_logo.svg" alt="B3" className="h-4" />
|
|
349
|
-
<h3 className="font-neue-montreal-semibold text-b3-grey">Global Account</h3>
|
|
350
|
-
</div>
|
|
351
|
-
|
|
352
|
-
<p className="text-b3-foreground-muted font-neue-montreal-medium mt-2 text-sm">
|
|
353
|
-
Your universal account for all B3-powered apps
|
|
354
|
-
</p>
|
|
355
|
-
</div>
|
|
356
|
-
<button
|
|
357
|
-
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"
|
|
358
|
-
onClick={onLogoutEnhanced}
|
|
359
|
-
>
|
|
360
|
-
{logoutLoading ? <Loader2 className="animate-spin" /> : <SignOutIcon size={16} className="text-b3-grey" />}
|
|
361
|
-
</button>
|
|
362
|
-
</div>
|
|
363
|
-
</div>
|
|
364
|
-
);
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
const AssetsContent = () => (
|
|
368
|
-
<div className="grid grid-cols-3 gap-4">
|
|
369
|
-
{assets?.nftResponse ? (
|
|
370
|
-
<AccountAssets nfts={assets.nftResponse} isLoading={isLoading} />
|
|
371
|
-
) : (
|
|
372
|
-
<div className="col-span-3 py-12 text-center text-gray-500">No NFTs found</div>
|
|
373
|
-
)}
|
|
374
|
-
</div>
|
|
375
|
-
);
|
|
376
|
-
|
|
377
89
|
const AppsContent = () => (
|
|
378
90
|
<div className="space-y-4">
|
|
379
91
|
{signers?.map((signer: TWSignerWithMetadata) => (
|
|
@@ -429,10 +141,7 @@ export function ManageAccount({
|
|
|
429
141
|
const handleUnlink = async (profile: any) => {
|
|
430
142
|
setUnlinkingAccountId(profile.title);
|
|
431
143
|
try {
|
|
432
|
-
|
|
433
|
-
client,
|
|
434
|
-
profileToUnlink: profile.originalProfile,
|
|
435
|
-
});
|
|
144
|
+
unlinkProfile({ client, profileToUnlink: profile.originalProfile });
|
|
436
145
|
} catch (error) {
|
|
437
146
|
console.error("Error unlinking account:", error);
|
|
438
147
|
} finally {
|
|
@@ -558,7 +267,7 @@ export function ManageAccount({
|
|
|
558
267
|
</div>
|
|
559
268
|
|
|
560
269
|
<p className="text-b3-foreground-muted font-neue-montreal-medium mt-2 text-sm">
|
|
561
|
-
Your universal account for all B3
|
|
270
|
+
Your universal account for all B3 apps
|
|
562
271
|
</p>
|
|
563
272
|
</div>
|
|
564
273
|
<button
|
|
@@ -579,55 +288,91 @@ export function ManageAccount({
|
|
|
579
288
|
defaultValue={activeTab}
|
|
580
289
|
onValueChange={value => {
|
|
581
290
|
const tab = value as TabValue;
|
|
582
|
-
if (["
|
|
291
|
+
if (["overview", "tokens", "nfts", "apps", "settings"].includes(tab)) {
|
|
583
292
|
setActiveTab?.(tab);
|
|
584
293
|
}
|
|
585
294
|
}}
|
|
586
295
|
>
|
|
587
|
-
<
|
|
588
|
-
<
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
296
|
+
<div className="px-4">
|
|
297
|
+
<TabsListPrimitive className="grid h-auto grid-cols-2 grid-rows-2 gap-3 rounded-none border-none bg-transparent">
|
|
298
|
+
<TabTriggerPrimitive
|
|
299
|
+
value="overview"
|
|
300
|
+
className="data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg"
|
|
301
|
+
>
|
|
302
|
+
<BarChart3 size={20} className="text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" />
|
|
303
|
+
<span className="text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white">
|
|
304
|
+
Overview
|
|
305
|
+
</span>
|
|
306
|
+
</TabTriggerPrimitive>
|
|
307
|
+
<TabTriggerPrimitive
|
|
308
|
+
value="tokens"
|
|
309
|
+
className="data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg"
|
|
310
|
+
>
|
|
311
|
+
<Coins size={20} className="text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" />
|
|
312
|
+
<span className="text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white">
|
|
313
|
+
Tokens
|
|
314
|
+
</span>
|
|
315
|
+
</TabTriggerPrimitive>
|
|
316
|
+
<TabTriggerPrimitive
|
|
317
|
+
value="nfts"
|
|
318
|
+
className="data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg"
|
|
319
|
+
>
|
|
320
|
+
<Image size={20} className="text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" />
|
|
321
|
+
<span className="text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white">
|
|
322
|
+
NFTs
|
|
323
|
+
</span>
|
|
324
|
+
</TabTriggerPrimitive>
|
|
325
|
+
{/*
|
|
326
|
+
// TODO: Apps is a remnant of session key flow. Moving forward, we should find a way to properly associate apps from linked partners that a user has logged in with
|
|
327
|
+
// https://linear.app/npclabs/issue/B3-2318/find-a-way-to-properly-display-which-partner-apps-a-user-has-logged-in
|
|
328
|
+
<TabTriggerPrimitive
|
|
329
|
+
value="apps"
|
|
330
|
+
className="data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-16 w-full flex-col items-start justify-between rounded-xl border border-gray-200 bg-white p-3 text-left shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg col-start-1 col-end-2"
|
|
331
|
+
>
|
|
332
|
+
<div className="flex w-full items-center justify-between">
|
|
333
|
+
<Grid3X3 size={20} className="text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" />
|
|
334
|
+
<span className="text-b3-grey font-neue-montreal-bold text-lg group-data-[state=active]:text-white">4</span>
|
|
335
|
+
</div>
|
|
336
|
+
<span className="text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white">
|
|
337
|
+
Apps
|
|
338
|
+
</span>
|
|
339
|
+
</TabTriggerPrimitive>
|
|
340
|
+
*/}
|
|
341
|
+
<TabTriggerPrimitive
|
|
342
|
+
value="settings"
|
|
343
|
+
className="data-[state=active]:bg-b3-primary-blue data-[state=active]:hover:bg-b3-primary-blue data-[state=active]:border-b3-primary-blue group flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white p-2 text-center shadow-sm transition-all duration-200 hover:bg-gray-50 hover:shadow-md data-[state=active]:shadow-lg"
|
|
344
|
+
>
|
|
345
|
+
<Settings size={20} className="text-b3-primary-blue shrink-0 group-data-[state=active]:text-white" />
|
|
346
|
+
<span className="text-b3-grey font-neue-montreal-semibold text-sm group-data-[state=active]:text-white">
|
|
347
|
+
Settings
|
|
348
|
+
</span>
|
|
349
|
+
</TabTriggerPrimitive>
|
|
350
|
+
</TabsListPrimitive>
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<TabsContentPrimitive value="overview" className="px-4 pb-4 pt-2">
|
|
354
|
+
<BalanceContent onLogout={onLogout} partnerId={partnerId} />
|
|
355
|
+
</TabsContentPrimitive>
|
|
617
356
|
|
|
618
|
-
<TabsContentPrimitive value="
|
|
619
|
-
<
|
|
357
|
+
<TabsContentPrimitive value="tokens" className="px-4 pb-4 pt-2">
|
|
358
|
+
<ContentTokens activeTab={activeTab} />
|
|
620
359
|
</TabsContentPrimitive>
|
|
621
360
|
|
|
622
|
-
<TabsContentPrimitive value="
|
|
623
|
-
<
|
|
361
|
+
<TabsContentPrimitive value="nfts" className="px-4 pb-4 pt-2">
|
|
362
|
+
<div className="grid grid-cols-3 gap-4">
|
|
363
|
+
{nfts?.nftResponse ? (
|
|
364
|
+
<AccountAssets nfts={nfts.nftResponse} isLoading={isLoading} />
|
|
365
|
+
) : (
|
|
366
|
+
<div className="col-span-3 py-12 text-center text-gray-500">No NFTs found</div>
|
|
367
|
+
)}
|
|
368
|
+
</div>
|
|
624
369
|
</TabsContentPrimitive>
|
|
625
370
|
|
|
626
|
-
<TabsContentPrimitive value="apps" className="
|
|
371
|
+
<TabsContentPrimitive value="apps" className="px-4 pb-4 pt-2">
|
|
627
372
|
<AppsContent />
|
|
628
373
|
</TabsContentPrimitive>
|
|
629
374
|
|
|
630
|
-
<TabsContentPrimitive value="settings" className="
|
|
375
|
+
<TabsContentPrimitive value="settings" className="px-4 pb-4 pt-2">
|
|
631
376
|
<SettingsContent />
|
|
632
377
|
</TabsContentPrimitive>
|
|
633
378
|
</TabsPrimitive>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Triangle } from "lucide-react";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
interface TokenBalanceRowProps {
|
|
5
|
+
icon: ReactNode;
|
|
6
|
+
name: string;
|
|
7
|
+
balance: string;
|
|
8
|
+
usdValue: string;
|
|
9
|
+
priceChange?: number | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function TokenBalanceRow({ icon, name, balance, usdValue, priceChange }: TokenBalanceRowProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex items-center justify-between">
|
|
15
|
+
<div className="flex items-center gap-3">
|
|
16
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-full">{icon}</div>
|
|
17
|
+
<div>
|
|
18
|
+
<div className="flex items-center gap-2">
|
|
19
|
+
<span className="text-b3-grey font-neue-montreal-semibold">{name}</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div className="text-b3-foreground-muted font-neue-montreal-medium text-sm">{balance}</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="text-right">
|
|
25
|
+
<div className="text-b3-grey font-neue-montreal-semibold">${usdValue}</div>
|
|
26
|
+
<div className="flex items-center gap-1">
|
|
27
|
+
{priceChange !== null && priceChange !== undefined ? (
|
|
28
|
+
<>
|
|
29
|
+
<Triangle
|
|
30
|
+
className={`size-3 ${priceChange >= 0 ? "text-b3-positive fill-b3-positive" : "text-b3-negative fill-b3-negative rotate-180"}`}
|
|
31
|
+
/>
|
|
32
|
+
<span
|
|
33
|
+
className={`font-neue-montreal-medium text-sm ${priceChange >= 0 ? "text-b3-positive" : "text-b3-negative"}`}
|
|
34
|
+
>
|
|
35
|
+
{priceChange >= 0 ? "+" : ""}
|
|
36
|
+
{priceChange.toFixed(2)}%
|
|
37
|
+
</span>
|
|
38
|
+
</>
|
|
39
|
+
) : (
|
|
40
|
+
<span className="text-b3-foreground-muted font-neue-montreal-medium text-sm">--</span>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { cn } from "@b3dotfun/sdk/shared/utils";
|
|
2
|
+
import React, { useCallback, useMemo, useState } from "react";
|
|
3
|
+
|
|
4
|
+
interface TokenIconProps {
|
|
5
|
+
src: string;
|
|
6
|
+
alt: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
size?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Create a global image cache to prevent re-loading
|
|
12
|
+
const imageCache = new Map<string, boolean>();
|
|
13
|
+
|
|
14
|
+
export const TokenIcon: React.FC<TokenIconProps> = ({ src, alt, className, size = 40 }) => {
|
|
15
|
+
const [isLoaded, setIsLoaded] = useState(() => imageCache.get(src) || false);
|
|
16
|
+
const [hasError, setHasError] = useState(false);
|
|
17
|
+
|
|
18
|
+
const handleLoad = useCallback(() => {
|
|
19
|
+
imageCache.set(src, true);
|
|
20
|
+
setIsLoaded(true);
|
|
21
|
+
}, [src]);
|
|
22
|
+
|
|
23
|
+
const handleError = useCallback(() => {
|
|
24
|
+
setHasError(true);
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
// Memoize the image element to prevent unnecessary re-renders
|
|
28
|
+
const imageElement = useMemo(
|
|
29
|
+
() => (
|
|
30
|
+
<img
|
|
31
|
+
src={src}
|
|
32
|
+
alt={alt}
|
|
33
|
+
className={cn("transition-opacity duration-200", className, {
|
|
34
|
+
"opacity-100": isLoaded && !hasError,
|
|
35
|
+
"opacity-0": !isLoaded || hasError,
|
|
36
|
+
})}
|
|
37
|
+
onLoad={handleLoad}
|
|
38
|
+
onError={handleError}
|
|
39
|
+
loading="eager" // Load immediately since these are critical UI elements
|
|
40
|
+
decoding="async"
|
|
41
|
+
style={{
|
|
42
|
+
width: size,
|
|
43
|
+
height: size,
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
),
|
|
47
|
+
[src, alt, className, isLoaded, hasError, handleLoad, handleError, size],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Show a placeholder while loading or if there's an error
|
|
51
|
+
const placeholder = useMemo(
|
|
52
|
+
() => (
|
|
53
|
+
<div
|
|
54
|
+
className={cn(
|
|
55
|
+
"bg-b3-primary-wash flex items-center justify-center rounded-full transition-opacity duration-200",
|
|
56
|
+
{
|
|
57
|
+
"opacity-0": isLoaded && !hasError,
|
|
58
|
+
"opacity-100": !isLoaded || hasError,
|
|
59
|
+
},
|
|
60
|
+
)}
|
|
61
|
+
style={{
|
|
62
|
+
width: size,
|
|
63
|
+
height: size,
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<span className="text-b3-grey font-neue-montreal-semibold text-xs">{alt.charAt(0).toUpperCase()}</span>
|
|
67
|
+
</div>
|
|
68
|
+
),
|
|
69
|
+
[alt, isLoaded, hasError, size],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div className="relative inline-block" style={{ width: size, height: size }}>
|
|
74
|
+
{placeholder}
|
|
75
|
+
<div className="absolute inset-0">{imageElement}</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Pre-defined token icons for common tokens
|
|
81
|
+
export const B3TokenIcon: React.FC<Omit<TokenIconProps, "src" | "alt">> = props => (
|
|
82
|
+
<TokenIcon src="https://cdn.b3.fun/b3-coin-3d.png" alt="B3" {...props} />
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
export const EthereumTokenIcon: React.FC<Omit<TokenIconProps, "src" | "alt">> = props => (
|
|
86
|
+
<TokenIcon src="https://cdn.b3.fun/ethereum.svg" alt="ETH" {...props} />
|
|
87
|
+
);
|