Skip to content

Commit b7d73c7

Browse files
authored
Added updated balance visualization in Connect modal (#2680)
* Updated connect component with multiple tokens visualization * Fully refactored balance visualization on connect modal * Put modal size as before * Added a little bit of emulator support to connect demo card * Remove breakdown for cleaner UI * Added flow token emulator support to connect button * Added USDF (PYUSD) as example in connect card * Added flowscan utils and refactored its usage * Enforced vaultidentifier or erc20 address * Change back displayBalance to localestring * Fixed first load problem * Improved tolocalestring with params --------- Co-authored-by: mfbz <mfbz@users.noreply.github.com>
1 parent 8ef9cc6 commit b7d73c7

File tree

8 files changed

+484
-94
lines changed

8 files changed

+484
-94
lines changed

.changeset/young-terms-agree.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@onflow/react-sdk": minor
3+
---
4+
5+
Enhanced the Connect component to enable visualizing different tokens like USDC and other stablecoins within the Connect modal.

packages/demo/src/components/component-cards/connect-card.tsx

Lines changed: 191 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
1+
import {useState} from "react"
12
import {Connect, useFlowChainId} from "@onflow/react-sdk"
23
import {useDarkMode} from "../flow-provider-wrapper"
34
import {DemoCard, type PropDefinition} from "../ui/demo-card"
45
import {PlusGridIcon} from "../ui/plus-grid"
6+
import {CONTRACT_ADDRESSES} from "../../constants"
57

68
const IMPLEMENTATION_CODE = `import { Connect } from "@onflow/react-sdk"
79
8-
<Connect />`
10+
// Basic usage - shows FLOW by default
11+
<Connect />
12+
13+
// With multiple tokens - dropdown selector, first token is default
14+
// Note: Provide only ONE identifier per token (the bridge derives the other)
15+
<Connect
16+
balanceTokens={[
17+
{
18+
symbol: "FLOW",
19+
name: "Flow Token",
20+
vaultIdentifier: "A.1654653399040a61.FlowToken.Vault",
21+
},
22+
{
23+
symbol: "USDF",
24+
name: "USDF (PYUSD)",
25+
vaultIdentifier: "A.1e4aa0b87d10b141.EVMVMBridgedToken_2aabea2058b5ac2d339b163c6ab6f2b6d53aabed.Vault",
26+
},
27+
]}
28+
balanceType="combined"
29+
/>`
930

1031
const PROPS: PropDefinition[] = [
1132
{
@@ -29,11 +50,19 @@ const PROPS: PropDefinition[] = [
2950
},
3051
{
3152
name: "balanceType",
32-
type: '"cadence" | "evm" | "vault"',
53+
type: '"cadence" | "evm" | "combined"',
3354
required: false,
34-
description: "Type of balance to display (from cross-VM token balance)",
55+
description:
56+
"Type of balance to display: cadence (Cadence VM only), evm (EVM only), or combined (sum of both)",
3557
defaultValue: '"cadence"',
3658
},
59+
{
60+
name: "balanceTokens",
61+
type: "TokenConfig[]",
62+
required: false,
63+
description:
64+
"Array of tokens with dropdown selector (first token is default). Each token needs symbol, name, and EXACTLY ONE of: vaultIdentifier OR erc20Address (the bridge derives the other automatically)",
65+
},
3766
{
3867
name: "modalConfig",
3968
type: "ConnectModalConfig",
@@ -47,6 +76,41 @@ export function ConnectCard() {
4776
const {darkMode} = useDarkMode()
4877
const {data: chainId, isLoading} = useFlowChainId()
4978
const isEmulator = chainId === "emulator" || chainId === "local"
79+
const [showMultiToken, setShowMultiToken] = useState(false)
80+
const [balanceType, setBalanceType] = useState<
81+
"cadence" | "evm" | "combined"
82+
>("cadence")
83+
84+
const getFlowTokenAddress = () => {
85+
if (chainId === "emulator" || chainId === "local") {
86+
return CONTRACT_ADDRESSES.FlowToken.emulator
87+
}
88+
return chainId === "testnet"
89+
? CONTRACT_ADDRESSES.FlowToken.testnet
90+
: CONTRACT_ADDRESSES.FlowToken.mainnet
91+
}
92+
93+
const multiTokens = [
94+
{
95+
symbol: "FLOW",
96+
name: "Flow Token",
97+
vaultIdentifier: `A.${getFlowTokenAddress().replace("0x", "")}.FlowToken.Vault`,
98+
},
99+
// Only show USDF (PYUSD) on testnet and mainnet
100+
// Note: Only vaultIdentifier provided - EVM address is derived by the bridge
101+
...(!isEmulator
102+
? [
103+
{
104+
symbol: "USDF",
105+
name: "USDF (PYUSD)",
106+
vaultIdentifier:
107+
chainId === "testnet"
108+
? "A.dfc20aee650fcbdf.EVMVMBridgedToken_f2e5a325f7d678da511e66b1c0ad7d5ba4df93d3.Vault"
109+
: "A.1e4aa0b87d10b141.EVMVMBridgedToken_2aabea2058b5ac2d339b163c6ab6f2b6d53aabed.Vault",
110+
},
111+
]
112+
: []),
113+
]
50114

51115
return (
52116
<DemoCard
@@ -58,7 +122,7 @@ export function ConnectCard() {
58122
docsUrl="https://developers.flow.com/build/tools/react-sdk/components#connect"
59123
>
60124
<div className="space-y-6">
61-
<div className="grid grid-cols-2 gap-4">
125+
<div className="grid grid-cols-3 gap-4">
62126
<div
63127
className={`relative p-4 rounded-lg border ${
64128
darkMode
@@ -78,7 +142,7 @@ export function ConnectCard() {
78142
</div>
79143

80144
<div
81-
className={`relative p-4 rounded-lg border ${
145+
className={`relative col-span-2 p-4 rounded-lg border ${
82146
darkMode
83147
? "bg-gray-900/50 border-white/10"
84148
: "bg-gray-50 border-black/5"
@@ -88,63 +152,136 @@ export function ConnectCard() {
88152
<h4
89153
className={`text-xs font-medium mb-1 ${darkMode ? "text-gray-500" : "text-gray-500"}`}
90154
>
91-
Customizable
155+
Multi-Token Cross-VM
92156
</h4>
93-
<p className={`text-sm ${darkMode ? "text-white" : "text-black"}`}>
94-
Style variants
157+
<p
158+
className={`text-xs ${darkMode ? "text-gray-400" : "text-gray-600"}`}
159+
>
160+
Multi-token support with cross-VM bridge integration. Provide only
161+
a vaultIdentifier or an erc20Address and the bridge derives the
162+
other automatically.
95163
</p>
96164
</div>
97165
</div>
98166

99-
<div
100-
className={`relative p-8 rounded-lg border ${
101-
darkMode
102-
? "bg-gray-900/50 border-white/10"
103-
: "bg-gray-50 border-black/5"
104-
}`}
105-
>
106-
{isLoading ? (
107-
<div className="text-center">
167+
<div className="flex gap-4">
168+
<div
169+
className={`relative flex-1 p-8 rounded-lg border flex items-center justify-center
170+
min-h-[200px] ${
171+
darkMode
172+
? "bg-gray-900/50 border-white/10"
173+
: "bg-gray-50 border-black/5"
174+
}`}
175+
>
176+
{isLoading ? (
177+
<div className="text-center">
178+
<div
179+
className={`inline-block animate-spin rounded-full h-8 w-8 border-b-2 ${
180+
darkMode ? "border-white" : "border-black" }`}
181+
></div>
182+
</div>
183+
) : isEmulator ? (
108184
<div
109-
className={`inline-block animate-spin rounded-full h-8 w-8 border-b-2 ${
110-
darkMode ? "border-white" : "border-black" }`}
111-
></div>
112-
</div>
113-
) : isEmulator ? (
185+
className={`text-center py-4 px-6 rounded-lg border ${
186+
darkMode
187+
? "bg-orange-900/20 border-orange-800/50"
188+
: "bg-orange-50 border-orange-200"
189+
}`}
190+
>
191+
<svg
192+
className="w-8 h-8 mx-auto mb-2 text-orange-500"
193+
fill="none"
194+
viewBox="0 0 24 24"
195+
stroke="currentColor"
196+
>
197+
<path
198+
strokeLinecap="round"
199+
strokeLinejoin="round"
200+
strokeWidth={2}
201+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
202+
/>
203+
</svg>
204+
<p
205+
className={`text-sm font-medium ${darkMode ? "text-orange-400" : "text-orange-600"}`}
206+
>
207+
Emulator Network Detected
208+
</p>
209+
<p
210+
className={`text-xs mt-1 ${darkMode ? "text-orange-400/70" : "text-orange-600/70"}`}
211+
>
212+
Connect component requires testnet or mainnet
213+
</p>
214+
</div>
215+
) : (
216+
<div>
217+
{showMultiToken ? (
218+
<Connect
219+
balanceType={balanceType}
220+
balanceTokens={multiTokens}
221+
/>
222+
) : (
223+
<Connect balanceType={balanceType} />
224+
)}
225+
</div>
226+
)}
227+
</div>
228+
229+
{!isEmulator && (
114230
<div
115-
className={`text-center py-4 px-6 rounded-lg border ${
116-
darkMode
117-
? "bg-orange-900/20 border-orange-800/50"
118-
: "bg-orange-50 border-orange-200"
119-
}`}
231+
className={`w-56 p-4 rounded-lg border space-y-4 ${
232+
darkMode
233+
? "bg-gray-900/50 border-white/10"
234+
: "bg-gray-50 border-black/5"
235+
}`}
120236
>
121-
<svg
122-
className="w-8 h-8 mx-auto mb-2 text-orange-500"
123-
fill="none"
124-
viewBox="0 0 24 24"
125-
stroke="currentColor"
126-
>
127-
<path
128-
strokeLinecap="round"
129-
strokeLinejoin="round"
130-
strokeWidth={2}
131-
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
132-
/>
133-
</svg>
134-
<p
135-
className={`text-sm font-medium ${darkMode ? "text-orange-400" : "text-orange-600"}`}
136-
>
137-
Emulator Network Detected
138-
</p>
139-
<p
140-
className={`text-xs mt-1 ${darkMode ? "text-orange-400/70" : "text-orange-600/70"}`}
141-
>
142-
Connect component requires testnet or mainnet
143-
</p>
144-
</div>
145-
) : (
146-
<div className="flex justify-center">
147-
<Connect />
237+
<div>
238+
<label
239+
className={`text-xs font-medium mb-2 block ${darkMode ? "text-gray-500" : "text-gray-500"}`}
240+
>
241+
Token Mode
242+
</label>
243+
<button
244+
onClick={() => setShowMultiToken(!showMultiToken)}
245+
className={`w-full text-sm px-3 py-2 rounded-lg transition-colors ${
246+
showMultiToken
247+
? darkMode
248+
? "bg-blue-600 hover:bg-blue-700 text-white"
249+
: "bg-blue-500 hover:bg-blue-600 text-white"
250+
: darkMode
251+
? "bg-gray-800 hover:bg-gray-700 text-white"
252+
: "bg-white hover:bg-gray-100 text-black border border-black/10"
253+
}`}
254+
>
255+
{showMultiToken ? "Multi-Token" : "Basic"}
256+
</button>
257+
</div>
258+
259+
<div>
260+
<label
261+
className={`text-xs font-medium mb-2 block ${darkMode ? "text-gray-500" : "text-gray-500"}`}
262+
>
263+
Balance Type
264+
</label>
265+
<div className="space-y-1.5">
266+
{(["cadence", "evm", "combined"] as const).map(type => (
267+
<button
268+
key={type}
269+
onClick={() => setBalanceType(type)}
270+
className={`w-full text-sm px-3 py-2 rounded-lg transition-colors text-left ${
271+
balanceType === type
272+
? darkMode
273+
? "bg-blue-600 hover:bg-blue-700 text-white"
274+
: "bg-blue-500 hover:bg-blue-600 text-white"
275+
: darkMode
276+
? "bg-gray-800 hover:bg-gray-700 text-white"
277+
: "bg-white hover:bg-gray-100 text-black border border-black/10"
278+
}`}
279+
>
280+
{type.charAt(0).toUpperCase() + type.slice(1)}
281+
</button>
282+
))}
283+
</div>
284+
</div>
148285
</div>
149286
)}
150287
</div>

0 commit comments

Comments
 (0)