@blazium/ton-connect-mobile 1.2.1 → 1.2.3
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/README.md +316 -18
- package/dist/core/wallets.js +4 -0
- package/dist/index.d.ts +38 -1
- package/dist/index.js +274 -4
- package/dist/react/TonConnectUIProvider.d.ts +21 -2
- package/dist/react/TonConnectUIProvider.js +72 -3
- package/dist/react/WalletSelectionModal.d.ts +1 -0
- package/dist/react/WalletSelectionModal.js +138 -80
- package/dist/react/index.d.ts +1 -0
- package/dist/types/index.d.ts +46 -0
- package/package.json +1 -1
- package/src/core/wallets.ts +4 -0
- package/src/index.ts +337 -6
- package/src/react/TonConnectUIProvider.tsx +109 -4
- package/src/react/WalletSelectionModal.tsx +172 -91
- package/src/react/index.ts +1 -0
- package/src/types/index.ts +52 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WalletSelectionModal component
|
|
3
3
|
* Provides a beautiful wallet selection UI compatible with @tonconnect/ui-react
|
|
4
|
+
* Matches the exact UI/UX of @tonconnect/ui-react wallet selection modal
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import React from 'react';
|
|
@@ -12,6 +13,8 @@ import {
|
|
|
12
13
|
ScrollView,
|
|
13
14
|
StyleSheet,
|
|
14
15
|
Platform,
|
|
16
|
+
Image,
|
|
17
|
+
ActivityIndicator,
|
|
15
18
|
} from 'react-native';
|
|
16
19
|
import { useTonConnectUI, useTonConnectSDK } from './index';
|
|
17
20
|
import type { WalletDefinition } from '../index';
|
|
@@ -41,26 +44,38 @@ export function WalletSelectionModal({
|
|
|
41
44
|
const sdk = useTonConnectSDK();
|
|
42
45
|
const [wallets, setWallets] = React.useState<WalletDefinition[]>([]);
|
|
43
46
|
const [connectingWallet, setConnectingWallet] = React.useState<string | null>(null);
|
|
47
|
+
const [walletAvailability, setWalletAvailability] = React.useState<Record<string, boolean>>({});
|
|
44
48
|
|
|
45
|
-
// Load wallets
|
|
49
|
+
// Load wallets and check availability
|
|
46
50
|
React.useEffect(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const supportedWallets = sdk.getSupportedWallets();
|
|
51
|
-
// CRITICAL FIX: On web, show all wallets with universalLink (they can open in new tab)
|
|
52
|
-
// On mobile, filter by platform
|
|
53
|
-
const platform = Platform.OS === 'ios' ? 'ios' : Platform.OS === 'android' ? 'android' : 'web';
|
|
54
|
-
let platformWallets: WalletDefinition[];
|
|
55
|
-
if (platform === 'web') {
|
|
56
|
-
// On web, show all wallets with universalLink (they can open in new tab)
|
|
57
|
-
platformWallets = supportedWallets.filter((w) => w.platforms.includes('web') || !!w.universalLink);
|
|
51
|
+
const loadWallets = async () => {
|
|
52
|
+
if (customWallets) {
|
|
53
|
+
setWallets(customWallets);
|
|
58
54
|
} else {
|
|
59
|
-
|
|
55
|
+
const supportedWallets = sdk.getSupportedWallets();
|
|
56
|
+
// Show all wallets (like @tonconnect/ui-react does)
|
|
57
|
+
// Availability will be checked and displayed
|
|
58
|
+
setWallets(supportedWallets);
|
|
60
59
|
}
|
|
61
|
-
|
|
60
|
+
|
|
61
|
+
// Check availability for all wallets
|
|
62
|
+
const availability: Record<string, boolean> = {};
|
|
63
|
+
const walletsToCheck = customWallets || sdk.getSupportedWallets();
|
|
64
|
+
for (const wallet of walletsToCheck) {
|
|
65
|
+
try {
|
|
66
|
+
const isAvailable = await sdk.isWalletAvailable(wallet.name);
|
|
67
|
+
availability[wallet.name] = isAvailable;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
availability[wallet.name] = false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
setWalletAvailability(availability);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if (visible) {
|
|
76
|
+
loadWallets();
|
|
62
77
|
}
|
|
63
|
-
}, [sdk, customWallets]);
|
|
78
|
+
}, [sdk, customWallets, visible]);
|
|
64
79
|
|
|
65
80
|
// Handle wallet selection
|
|
66
81
|
const handleSelectWallet = async (wallet: WalletDefinition) => {
|
|
@@ -104,17 +119,23 @@ export function WalletSelectionModal({
|
|
|
104
119
|
>
|
|
105
120
|
<View style={[styles.overlay, style]}>
|
|
106
121
|
<View style={styles.modalContainer}>
|
|
107
|
-
{/* Header */}
|
|
122
|
+
{/* Header - matches @tonconnect/ui-react style */}
|
|
108
123
|
<View style={styles.header}>
|
|
109
|
-
<
|
|
110
|
-
|
|
124
|
+
<TouchableOpacity style={styles.backButton} onPress={onClose}>
|
|
125
|
+
<Text style={styles.backButtonText}>←</Text>
|
|
126
|
+
</TouchableOpacity>
|
|
127
|
+
<Text style={styles.title}>Wallets</Text>
|
|
111
128
|
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
|
|
112
129
|
<Text style={styles.closeButtonText}>✕</Text>
|
|
113
130
|
</TouchableOpacity>
|
|
114
131
|
</View>
|
|
115
132
|
|
|
116
|
-
{/* Wallet
|
|
117
|
-
<ScrollView
|
|
133
|
+
{/* Wallet Grid - matches @tonconnect/ui-react grid layout */}
|
|
134
|
+
<ScrollView
|
|
135
|
+
style={styles.walletList}
|
|
136
|
+
showsVerticalScrollIndicator={false}
|
|
137
|
+
contentContainerStyle={styles.walletGrid}
|
|
138
|
+
>
|
|
118
139
|
{wallets.length === 0 ? (
|
|
119
140
|
<View style={styles.emptyState}>
|
|
120
141
|
<Text style={styles.emptyStateText}>No wallets available</Text>
|
|
@@ -125,29 +146,45 @@ export function WalletSelectionModal({
|
|
|
125
146
|
) : (
|
|
126
147
|
wallets.map((wallet) => {
|
|
127
148
|
const isConnecting = connectingWallet === wallet.name;
|
|
149
|
+
const isAvailable = walletAvailability[wallet.name] !== false;
|
|
128
150
|
return (
|
|
129
151
|
<TouchableOpacity
|
|
130
152
|
key={wallet.name}
|
|
131
|
-
style={[
|
|
132
|
-
|
|
133
|
-
|
|
153
|
+
style={[
|
|
154
|
+
styles.walletCard,
|
|
155
|
+
!isAvailable && styles.walletCardUnavailable,
|
|
156
|
+
isConnecting && styles.walletCardConnecting,
|
|
157
|
+
]}
|
|
158
|
+
onPress={() => isAvailable && !isConnecting && handleSelectWallet(wallet)}
|
|
159
|
+
disabled={!isAvailable || isConnecting}
|
|
134
160
|
>
|
|
135
161
|
<View style={styles.walletIconContainer}>
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
{wallet.iconUrl && Platform.OS !== 'web' ? (
|
|
163
|
+
<Image
|
|
164
|
+
source={{ uri: wallet.iconUrl }}
|
|
165
|
+
style={styles.walletIcon}
|
|
166
|
+
onError={() => {
|
|
167
|
+
// Fallback to placeholder on error
|
|
168
|
+
}}
|
|
169
|
+
resizeMode="cover"
|
|
170
|
+
/>
|
|
171
|
+
) : (
|
|
172
|
+
<View style={styles.walletIconPlaceholder}>
|
|
173
|
+
<Text style={styles.walletIconText}>
|
|
174
|
+
{wallet.name.charAt(0).toUpperCase()}
|
|
175
|
+
</Text>
|
|
176
|
+
</View>
|
|
177
|
+
)}
|
|
146
178
|
</View>
|
|
179
|
+
<Text style={styles.walletName} numberOfLines={1}>
|
|
180
|
+
{wallet.name}
|
|
181
|
+
</Text>
|
|
147
182
|
{isConnecting && (
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
183
|
+
<ActivityIndicator
|
|
184
|
+
size="small"
|
|
185
|
+
color="#0088cc"
|
|
186
|
+
style={styles.connectingSpinner}
|
|
187
|
+
/>
|
|
151
188
|
)}
|
|
152
189
|
</TouchableOpacity>
|
|
153
190
|
);
|
|
@@ -155,11 +192,17 @@ export function WalletSelectionModal({
|
|
|
155
192
|
)}
|
|
156
193
|
</ScrollView>
|
|
157
194
|
|
|
158
|
-
{/* Footer */}
|
|
195
|
+
{/* Footer - matches @tonconnect/ui-react footer */}
|
|
159
196
|
<View style={styles.footer}>
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
197
|
+
<View style={styles.footerContent}>
|
|
198
|
+
<View style={styles.tonConnectLogo}>
|
|
199
|
+
<Text style={styles.tonConnectLogoText}>TON</Text>
|
|
200
|
+
</View>
|
|
201
|
+
<Text style={styles.footerText}>TON Connect</Text>
|
|
202
|
+
</View>
|
|
203
|
+
<TouchableOpacity style={styles.helpButton}>
|
|
204
|
+
<Text style={styles.helpButtonText}>?</Text>
|
|
205
|
+
</TouchableOpacity>
|
|
163
206
|
</View>
|
|
164
207
|
</View>
|
|
165
208
|
</View>
|
|
@@ -177,30 +220,36 @@ const styles = StyleSheet.create({
|
|
|
177
220
|
backgroundColor: '#1a1a1a',
|
|
178
221
|
borderTopLeftRadius: 24,
|
|
179
222
|
borderTopRightRadius: 24,
|
|
180
|
-
maxHeight: '
|
|
223
|
+
maxHeight: '90%',
|
|
181
224
|
paddingBottom: Platform.OS === 'ios' ? 34 : 20,
|
|
182
225
|
},
|
|
183
226
|
header: {
|
|
184
|
-
|
|
227
|
+
flexDirection: 'row',
|
|
228
|
+
alignItems: 'center',
|
|
229
|
+
justifyContent: 'space-between',
|
|
230
|
+
padding: 16,
|
|
185
231
|
borderBottomWidth: 1,
|
|
186
232
|
borderBottomColor: '#2a2a2a',
|
|
187
|
-
|
|
233
|
+
},
|
|
234
|
+
backButton: {
|
|
235
|
+
width: 32,
|
|
236
|
+
height: 32,
|
|
237
|
+
justifyContent: 'center',
|
|
238
|
+
alignItems: 'center',
|
|
239
|
+
},
|
|
240
|
+
backButtonText: {
|
|
241
|
+
color: '#ffffff',
|
|
242
|
+
fontSize: 20,
|
|
243
|
+
fontWeight: '600',
|
|
188
244
|
},
|
|
189
245
|
title: {
|
|
190
|
-
fontSize:
|
|
246
|
+
fontSize: 20,
|
|
191
247
|
fontWeight: 'bold',
|
|
192
248
|
color: '#ffffff',
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
subtitle: {
|
|
196
|
-
fontSize: 15,
|
|
197
|
-
color: '#999999',
|
|
198
|
-
lineHeight: 20,
|
|
249
|
+
flex: 1,
|
|
250
|
+
textAlign: 'center',
|
|
199
251
|
},
|
|
200
252
|
closeButton: {
|
|
201
|
-
position: 'absolute',
|
|
202
|
-
top: 24,
|
|
203
|
-
right: 24,
|
|
204
253
|
width: 32,
|
|
205
254
|
height: 32,
|
|
206
255
|
borderRadius: 16,
|
|
@@ -214,66 +263,65 @@ const styles = StyleSheet.create({
|
|
|
214
263
|
fontWeight: '600',
|
|
215
264
|
},
|
|
216
265
|
walletList: {
|
|
217
|
-
|
|
218
|
-
maxHeight: 400,
|
|
266
|
+
flex: 1,
|
|
219
267
|
},
|
|
220
|
-
|
|
268
|
+
walletGrid: {
|
|
221
269
|
flexDirection: 'row',
|
|
222
|
-
|
|
270
|
+
flexWrap: 'wrap',
|
|
223
271
|
padding: 16,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
272
|
+
justifyContent: 'flex-start',
|
|
273
|
+
},
|
|
274
|
+
walletCard: {
|
|
275
|
+
width: '25%',
|
|
276
|
+
aspectRatio: 1,
|
|
277
|
+
alignItems: 'center',
|
|
278
|
+
justifyContent: 'center',
|
|
279
|
+
padding: 8,
|
|
280
|
+
marginBottom: 16,
|
|
281
|
+
},
|
|
282
|
+
walletCardUnavailable: {
|
|
283
|
+
opacity: 0.5,
|
|
228
284
|
},
|
|
229
|
-
|
|
285
|
+
walletCardConnecting: {
|
|
230
286
|
opacity: 0.7,
|
|
231
287
|
},
|
|
232
288
|
walletIconContainer: {
|
|
233
|
-
|
|
289
|
+
width: 64,
|
|
290
|
+
height: 64,
|
|
291
|
+
marginBottom: 8,
|
|
234
292
|
},
|
|
235
293
|
walletIcon: {
|
|
236
|
-
width:
|
|
237
|
-
height:
|
|
238
|
-
borderRadius:
|
|
294
|
+
width: 64,
|
|
295
|
+
height: 64,
|
|
296
|
+
borderRadius: 16,
|
|
239
297
|
},
|
|
240
298
|
walletIconPlaceholder: {
|
|
241
|
-
width:
|
|
242
|
-
height:
|
|
243
|
-
borderRadius:
|
|
244
|
-
backgroundColor: '#
|
|
299
|
+
width: 64,
|
|
300
|
+
height: 64,
|
|
301
|
+
borderRadius: 16,
|
|
302
|
+
backgroundColor: '#2a2a2a',
|
|
245
303
|
justifyContent: 'center',
|
|
246
304
|
alignItems: 'center',
|
|
247
305
|
},
|
|
248
306
|
walletIconText: {
|
|
249
307
|
color: '#ffffff',
|
|
250
|
-
fontSize:
|
|
308
|
+
fontSize: 24,
|
|
251
309
|
fontWeight: 'bold',
|
|
252
310
|
},
|
|
253
|
-
walletInfo: {
|
|
254
|
-
flex: 1,
|
|
255
|
-
},
|
|
256
311
|
walletName: {
|
|
257
|
-
fontSize:
|
|
258
|
-
fontWeight: '
|
|
312
|
+
fontSize: 12,
|
|
313
|
+
fontWeight: '500',
|
|
259
314
|
color: '#ffffff',
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
walletAppName: {
|
|
263
|
-
fontSize: 14,
|
|
264
|
-
color: '#999999',
|
|
265
|
-
},
|
|
266
|
-
connectingIndicator: {
|
|
267
|
-
marginLeft: 12,
|
|
315
|
+
textAlign: 'center',
|
|
316
|
+
maxWidth: '100%',
|
|
268
317
|
},
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
color: '#0088cc',
|
|
272
|
-
fontWeight: '500',
|
|
318
|
+
connectingSpinner: {
|
|
319
|
+
marginTop: 4,
|
|
273
320
|
},
|
|
274
321
|
emptyState: {
|
|
275
322
|
padding: 40,
|
|
276
323
|
alignItems: 'center',
|
|
324
|
+
width: '100%',
|
|
277
325
|
},
|
|
278
326
|
emptyStateText: {
|
|
279
327
|
fontSize: 18,
|
|
@@ -287,15 +335,48 @@ const styles = StyleSheet.create({
|
|
|
287
335
|
textAlign: 'center',
|
|
288
336
|
},
|
|
289
337
|
footer: {
|
|
338
|
+
flexDirection: 'row',
|
|
339
|
+
alignItems: 'center',
|
|
340
|
+
justifyContent: 'space-between',
|
|
290
341
|
padding: 16,
|
|
291
342
|
borderTopWidth: 1,
|
|
292
343
|
borderTopColor: '#2a2a2a',
|
|
293
344
|
},
|
|
345
|
+
footerContent: {
|
|
346
|
+
flexDirection: 'row',
|
|
347
|
+
alignItems: 'center',
|
|
348
|
+
},
|
|
349
|
+
tonConnectLogo: {
|
|
350
|
+
width: 20,
|
|
351
|
+
height: 20,
|
|
352
|
+
borderRadius: 4,
|
|
353
|
+
backgroundColor: '#0088cc',
|
|
354
|
+
justifyContent: 'center',
|
|
355
|
+
alignItems: 'center',
|
|
356
|
+
marginRight: 8,
|
|
357
|
+
},
|
|
358
|
+
tonConnectLogoText: {
|
|
359
|
+
color: '#ffffff',
|
|
360
|
+
fontSize: 10,
|
|
361
|
+
fontWeight: 'bold',
|
|
362
|
+
},
|
|
294
363
|
footerText: {
|
|
295
364
|
fontSize: 12,
|
|
296
|
-
color: '#
|
|
297
|
-
|
|
298
|
-
|
|
365
|
+
color: '#ffffff',
|
|
366
|
+
fontWeight: '500',
|
|
367
|
+
},
|
|
368
|
+
helpButton: {
|
|
369
|
+
width: 24,
|
|
370
|
+
height: 24,
|
|
371
|
+
borderRadius: 12,
|
|
372
|
+
backgroundColor: '#2a2a2a',
|
|
373
|
+
justifyContent: 'center',
|
|
374
|
+
alignItems: 'center',
|
|
375
|
+
},
|
|
376
|
+
helpButtonText: {
|
|
377
|
+
color: '#999999',
|
|
378
|
+
fontSize: 14,
|
|
379
|
+
fontWeight: '600',
|
|
299
380
|
},
|
|
300
381
|
});
|
|
301
382
|
|
package/src/react/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export type {
|
|
|
19
19
|
SignDataRequest,
|
|
20
20
|
SignDataResponse,
|
|
21
21
|
} from './TonConnectUIProvider';
|
|
22
|
+
export type { Network, BalanceResponse, TransactionStatusResponse, TransactionStatus } from '../types';
|
|
22
23
|
export { TonConnectButton } from './TonConnectButton';
|
|
23
24
|
export type { TonConnectButtonProps } from './TonConnectButton';
|
|
24
25
|
export { WalletSelectionModal } from './WalletSelectionModal';
|
package/src/types/index.ts
CHANGED
|
@@ -187,6 +187,11 @@ export interface PlatformAdapter {
|
|
|
187
187
|
randomBytes(length: number): Promise<Uint8Array>;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Network type
|
|
192
|
+
*/
|
|
193
|
+
export type Network = 'mainnet' | 'testnet';
|
|
194
|
+
|
|
190
195
|
/**
|
|
191
196
|
* SDK configuration
|
|
192
197
|
*/
|
|
@@ -211,6 +216,12 @@ export interface TonConnectMobileConfig {
|
|
|
211
216
|
* Available: 'Tonkeeper', 'MyTonWallet', 'Wallet in Telegram', 'Tonhub'
|
|
212
217
|
*/
|
|
213
218
|
preferredWallet?: string;
|
|
219
|
+
/** Network (mainnet/testnet) - default: 'mainnet' */
|
|
220
|
+
network?: Network;
|
|
221
|
+
/** TON API endpoint for balance checking (optional)
|
|
222
|
+
* Default: 'https://toncenter.com/api/v2' for mainnet, 'https://testnet.toncenter.com/api/v2' for testnet
|
|
223
|
+
*/
|
|
224
|
+
tonApiEndpoint?: string;
|
|
214
225
|
}
|
|
215
226
|
|
|
216
227
|
/**
|
|
@@ -218,3 +229,44 @@ export interface TonConnectMobileConfig {
|
|
|
218
229
|
*/
|
|
219
230
|
export type StatusChangeCallback = (status: ConnectionStatus) => void;
|
|
220
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Event types for TonConnectMobile
|
|
234
|
+
*/
|
|
235
|
+
export type TonConnectEventType = 'connect' | 'disconnect' | 'transaction' | 'error' | 'statusChange';
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Event listener callback
|
|
239
|
+
*/
|
|
240
|
+
export type TonConnectEventListener<T = any> = (data: T) => void;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Transaction status
|
|
244
|
+
*/
|
|
245
|
+
export type TransactionStatus = 'pending' | 'confirmed' | 'failed' | 'unknown';
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Transaction status response
|
|
249
|
+
*/
|
|
250
|
+
export interface TransactionStatusResponse {
|
|
251
|
+
/** Transaction status */
|
|
252
|
+
status: TransactionStatus;
|
|
253
|
+
/** Transaction hash (if available) */
|
|
254
|
+
hash?: string;
|
|
255
|
+
/** Block number (if confirmed) */
|
|
256
|
+
blockNumber?: number;
|
|
257
|
+
/** Error message (if failed) */
|
|
258
|
+
error?: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Balance response
|
|
263
|
+
*/
|
|
264
|
+
export interface BalanceResponse {
|
|
265
|
+
/** Balance in nanotons */
|
|
266
|
+
balance: string;
|
|
267
|
+
/** Balance in TON (formatted) */
|
|
268
|
+
balanceTon: string;
|
|
269
|
+
/** Network */
|
|
270
|
+
network: Network;
|
|
271
|
+
}
|
|
272
|
+
|