@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.
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * WalletSelectionModal component
4
4
  * Provides a beautiful wallet selection UI compatible with @tonconnect/ui-react
5
+ * Matches the exact UI/UX of @tonconnect/ui-react wallet selection modal
5
6
  */
6
7
  var __importDefault = (this && this.__importDefault) || function (mod) {
7
8
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -20,27 +21,37 @@ function WalletSelectionModal({ visible, onClose, wallets: customWallets, style,
20
21
  const sdk = (0, index_1.useTonConnectSDK)();
21
22
  const [wallets, setWallets] = react_1.default.useState([]);
22
23
  const [connectingWallet, setConnectingWallet] = react_1.default.useState(null);
23
- // Load wallets
24
+ const [walletAvailability, setWalletAvailability] = react_1.default.useState({});
25
+ // Load wallets and check availability
24
26
  react_1.default.useEffect(() => {
25
- if (customWallets) {
26
- setWallets(customWallets);
27
- }
28
- else {
29
- const supportedWallets = sdk.getSupportedWallets();
30
- // CRITICAL FIX: On web, show all wallets with universalLink (they can open in new tab)
31
- // On mobile, filter by platform
32
- const platform = react_native_1.Platform.OS === 'ios' ? 'ios' : react_native_1.Platform.OS === 'android' ? 'android' : 'web';
33
- let platformWallets;
34
- if (platform === 'web') {
35
- // On web, show all wallets with universalLink (they can open in new tab)
36
- platformWallets = supportedWallets.filter((w) => w.platforms.includes('web') || !!w.universalLink);
27
+ const loadWallets = async () => {
28
+ if (customWallets) {
29
+ setWallets(customWallets);
37
30
  }
38
31
  else {
39
- platformWallets = supportedWallets.filter((w) => w.platforms.includes(platform));
32
+ const supportedWallets = sdk.getSupportedWallets();
33
+ // Show all wallets (like @tonconnect/ui-react does)
34
+ // Availability will be checked and displayed
35
+ setWallets(supportedWallets);
36
+ }
37
+ // Check availability for all wallets
38
+ const availability = {};
39
+ const walletsToCheck = customWallets || sdk.getSupportedWallets();
40
+ for (const wallet of walletsToCheck) {
41
+ try {
42
+ const isAvailable = await sdk.isWalletAvailable(wallet.name);
43
+ availability[wallet.name] = isAvailable;
44
+ }
45
+ catch (error) {
46
+ availability[wallet.name] = false;
47
+ }
40
48
  }
41
- setWallets(platformWallets);
49
+ setWalletAvailability(availability);
50
+ };
51
+ if (visible) {
52
+ loadWallets();
42
53
  }
43
- }, [sdk, customWallets]);
54
+ }, [sdk, customWallets, visible]);
44
55
  // Handle wallet selection
45
56
  const handleSelectWallet = async (wallet) => {
46
57
  // Prevent multiple simultaneous connection attempts
@@ -74,26 +85,35 @@ function WalletSelectionModal({ visible, onClose, wallets: customWallets, style,
74
85
  react_1.default.createElement(react_native_1.View, { style: [styles.overlay, style] },
75
86
  react_1.default.createElement(react_native_1.View, { style: styles.modalContainer },
76
87
  react_1.default.createElement(react_native_1.View, { style: styles.header },
77
- react_1.default.createElement(react_native_1.Text, { style: styles.title }, "Connect your TON wallet"),
78
- react_1.default.createElement(react_native_1.Text, { style: styles.subtitle }, "Choose a wallet to connect to this app"),
88
+ react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.backButton, onPress: onClose },
89
+ react_1.default.createElement(react_native_1.Text, { style: styles.backButtonText }, "\u2190")),
90
+ react_1.default.createElement(react_native_1.Text, { style: styles.title }, "Wallets"),
79
91
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.closeButton, onPress: onClose },
80
92
  react_1.default.createElement(react_native_1.Text, { style: styles.closeButtonText }, "\u2715"))),
81
- react_1.default.createElement(react_native_1.ScrollView, { style: styles.walletList, showsVerticalScrollIndicator: false }, wallets.length === 0 ? (react_1.default.createElement(react_native_1.View, { style: styles.emptyState },
93
+ react_1.default.createElement(react_native_1.ScrollView, { style: styles.walletList, showsVerticalScrollIndicator: false, contentContainerStyle: styles.walletGrid }, wallets.length === 0 ? (react_1.default.createElement(react_native_1.View, { style: styles.emptyState },
82
94
  react_1.default.createElement(react_native_1.Text, { style: styles.emptyStateText }, "No wallets available"),
83
95
  react_1.default.createElement(react_native_1.Text, { style: styles.emptyStateSubtext }, "Please install a TON wallet app to continue"))) : (wallets.map((wallet) => {
84
96
  const isConnecting = connectingWallet === wallet.name;
85
- return (react_1.default.createElement(react_native_1.TouchableOpacity, { key: wallet.name, style: [styles.walletItem, isConnecting && styles.walletItemConnecting], onPress: () => handleSelectWallet(wallet), disabled: isConnecting },
86
- react_1.default.createElement(react_native_1.View, { style: styles.walletIconContainer },
87
- react_1.default.createElement(react_native_1.View, { style: styles.walletIconPlaceholder },
88
- react_1.default.createElement(react_native_1.Text, { style: styles.walletIconText }, wallet.name.charAt(0).toUpperCase()))),
89
- react_1.default.createElement(react_native_1.View, { style: styles.walletInfo },
90
- react_1.default.createElement(react_native_1.Text, { style: styles.walletName }, wallet.name),
91
- react_1.default.createElement(react_native_1.Text, { style: styles.walletAppName }, wallet.appName)),
92
- isConnecting && (react_1.default.createElement(react_native_1.View, { style: styles.connectingIndicator },
93
- react_1.default.createElement(react_native_1.Text, { style: styles.connectingText }, "Connecting...")))));
97
+ const isAvailable = walletAvailability[wallet.name] !== false;
98
+ return (react_1.default.createElement(react_native_1.TouchableOpacity, { key: wallet.name, style: [
99
+ styles.walletCard,
100
+ !isAvailable && styles.walletCardUnavailable,
101
+ isConnecting && styles.walletCardConnecting,
102
+ ], onPress: () => isAvailable && !isConnecting && handleSelectWallet(wallet), disabled: !isAvailable || isConnecting },
103
+ react_1.default.createElement(react_native_1.View, { style: styles.walletIconContainer }, wallet.iconUrl && react_native_1.Platform.OS !== 'web' ? (react_1.default.createElement(react_native_1.Image, { source: { uri: wallet.iconUrl }, style: styles.walletIcon, onError: () => {
104
+ // Fallback to placeholder on error
105
+ }, resizeMode: "cover" })) : (react_1.default.createElement(react_native_1.View, { style: styles.walletIconPlaceholder },
106
+ react_1.default.createElement(react_native_1.Text, { style: styles.walletIconText }, wallet.name.charAt(0).toUpperCase())))),
107
+ react_1.default.createElement(react_native_1.Text, { style: styles.walletName, numberOfLines: 1 }, wallet.name),
108
+ isConnecting && (react_1.default.createElement(react_native_1.ActivityIndicator, { size: "small", color: "#0088cc", style: styles.connectingSpinner }))));
94
109
  }))),
95
110
  react_1.default.createElement(react_native_1.View, { style: styles.footer },
96
- react_1.default.createElement(react_native_1.Text, { style: styles.footerText }, "By connecting, you agree to the app's Terms of Service and Privacy Policy"))))));
111
+ react_1.default.createElement(react_native_1.View, { style: styles.footerContent },
112
+ react_1.default.createElement(react_native_1.View, { style: styles.tonConnectLogo },
113
+ react_1.default.createElement(react_native_1.Text, { style: styles.tonConnectLogoText }, "TON")),
114
+ react_1.default.createElement(react_native_1.Text, { style: styles.footerText }, "TON Connect")),
115
+ react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.helpButton },
116
+ react_1.default.createElement(react_native_1.Text, { style: styles.helpButtonText }, "?")))))));
97
117
  }
98
118
  const styles = react_native_1.StyleSheet.create({
99
119
  overlay: {
@@ -105,30 +125,36 @@ const styles = react_native_1.StyleSheet.create({
105
125
  backgroundColor: '#1a1a1a',
106
126
  borderTopLeftRadius: 24,
107
127
  borderTopRightRadius: 24,
108
- maxHeight: '85%',
128
+ maxHeight: '90%',
109
129
  paddingBottom: react_native_1.Platform.OS === 'ios' ? 34 : 20,
110
130
  },
111
131
  header: {
112
- padding: 24,
132
+ flexDirection: 'row',
133
+ alignItems: 'center',
134
+ justifyContent: 'space-between',
135
+ padding: 16,
113
136
  borderBottomWidth: 1,
114
137
  borderBottomColor: '#2a2a2a',
115
- position: 'relative',
138
+ },
139
+ backButton: {
140
+ width: 32,
141
+ height: 32,
142
+ justifyContent: 'center',
143
+ alignItems: 'center',
144
+ },
145
+ backButtonText: {
146
+ color: '#ffffff',
147
+ fontSize: 20,
148
+ fontWeight: '600',
116
149
  },
117
150
  title: {
118
- fontSize: 28,
151
+ fontSize: 20,
119
152
  fontWeight: 'bold',
120
153
  color: '#ffffff',
121
- marginBottom: 8,
122
- },
123
- subtitle: {
124
- fontSize: 15,
125
- color: '#999999',
126
- lineHeight: 20,
154
+ flex: 1,
155
+ textAlign: 'center',
127
156
  },
128
157
  closeButton: {
129
- position: 'absolute',
130
- top: 24,
131
- right: 24,
132
158
  width: 32,
133
159
  height: 32,
134
160
  borderRadius: 16,
@@ -142,66 +168,65 @@ const styles = react_native_1.StyleSheet.create({
142
168
  fontWeight: '600',
143
169
  },
144
170
  walletList: {
145
- padding: 16,
146
- maxHeight: 400,
171
+ flex: 1,
147
172
  },
148
- walletItem: {
173
+ walletGrid: {
149
174
  flexDirection: 'row',
150
- alignItems: 'center',
175
+ flexWrap: 'wrap',
151
176
  padding: 16,
152
- backgroundColor: '#2a2a2a',
153
- borderRadius: 16,
154
- marginBottom: 12,
155
- minHeight: 72,
177
+ justifyContent: 'flex-start',
178
+ },
179
+ walletCard: {
180
+ width: '25%',
181
+ aspectRatio: 1,
182
+ alignItems: 'center',
183
+ justifyContent: 'center',
184
+ padding: 8,
185
+ marginBottom: 16,
186
+ },
187
+ walletCardUnavailable: {
188
+ opacity: 0.5,
156
189
  },
157
- walletItemConnecting: {
190
+ walletCardConnecting: {
158
191
  opacity: 0.7,
159
192
  },
160
193
  walletIconContainer: {
161
- marginRight: 16,
194
+ width: 64,
195
+ height: 64,
196
+ marginBottom: 8,
162
197
  },
163
198
  walletIcon: {
164
- width: 48,
165
- height: 48,
166
- borderRadius: 12,
199
+ width: 64,
200
+ height: 64,
201
+ borderRadius: 16,
167
202
  },
168
203
  walletIconPlaceholder: {
169
- width: 48,
170
- height: 48,
171
- borderRadius: 12,
172
- backgroundColor: '#3a3a3a',
204
+ width: 64,
205
+ height: 64,
206
+ borderRadius: 16,
207
+ backgroundColor: '#2a2a2a',
173
208
  justifyContent: 'center',
174
209
  alignItems: 'center',
175
210
  },
176
211
  walletIconText: {
177
212
  color: '#ffffff',
178
- fontSize: 20,
213
+ fontSize: 24,
179
214
  fontWeight: 'bold',
180
215
  },
181
- walletInfo: {
182
- flex: 1,
183
- },
184
216
  walletName: {
185
- fontSize: 17,
186
- fontWeight: '600',
217
+ fontSize: 12,
218
+ fontWeight: '500',
187
219
  color: '#ffffff',
188
- marginBottom: 4,
189
- },
190
- walletAppName: {
191
- fontSize: 14,
192
- color: '#999999',
193
- },
194
- connectingIndicator: {
195
- marginLeft: 12,
220
+ textAlign: 'center',
221
+ maxWidth: '100%',
196
222
  },
197
- connectingText: {
198
- fontSize: 14,
199
- color: '#0088cc',
200
- fontWeight: '500',
223
+ connectingSpinner: {
224
+ marginTop: 4,
201
225
  },
202
226
  emptyState: {
203
227
  padding: 40,
204
228
  alignItems: 'center',
229
+ width: '100%',
205
230
  },
206
231
  emptyStateText: {
207
232
  fontSize: 18,
@@ -215,14 +240,47 @@ const styles = react_native_1.StyleSheet.create({
215
240
  textAlign: 'center',
216
241
  },
217
242
  footer: {
243
+ flexDirection: 'row',
244
+ alignItems: 'center',
245
+ justifyContent: 'space-between',
218
246
  padding: 16,
219
247
  borderTopWidth: 1,
220
248
  borderTopColor: '#2a2a2a',
221
249
  },
250
+ footerContent: {
251
+ flexDirection: 'row',
252
+ alignItems: 'center',
253
+ },
254
+ tonConnectLogo: {
255
+ width: 20,
256
+ height: 20,
257
+ borderRadius: 4,
258
+ backgroundColor: '#0088cc',
259
+ justifyContent: 'center',
260
+ alignItems: 'center',
261
+ marginRight: 8,
262
+ },
263
+ tonConnectLogoText: {
264
+ color: '#ffffff',
265
+ fontSize: 10,
266
+ fontWeight: 'bold',
267
+ },
222
268
  footerText: {
223
269
  fontSize: 12,
224
- color: '#666666',
225
- textAlign: 'center',
226
- lineHeight: 16,
270
+ color: '#ffffff',
271
+ fontWeight: '500',
272
+ },
273
+ helpButton: {
274
+ width: 24,
275
+ height: 24,
276
+ borderRadius: 12,
277
+ backgroundColor: '#2a2a2a',
278
+ justifyContent: 'center',
279
+ alignItems: 'center',
280
+ },
281
+ helpButtonText: {
282
+ color: '#999999',
283
+ fontSize: 14,
284
+ fontWeight: '600',
227
285
  },
228
286
  });
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { TonConnectUIProvider, useTonConnectUI, useTonWallet, useTonConnectModal, useTonConnectSDK, } from './TonConnectUIProvider';
6
6
  export type { TonConnectUIProviderProps, TonConnectUI, WalletState, Account, TransactionResponse, SignDataRequest, SignDataResponse, } from './TonConnectUIProvider';
7
+ export type { Network, BalanceResponse, TransactionStatusResponse, TransactionStatus } from '../types';
7
8
  export { TonConnectButton } from './TonConnectButton';
8
9
  export type { TonConnectButtonProps } from './TonConnectButton';
9
10
  export { WalletSelectionModal } from './WalletSelectionModal';
@@ -175,6 +175,10 @@ export interface PlatformAdapter {
175
175
  /** Generate random bytes */
176
176
  randomBytes(length: number): Promise<Uint8Array>;
177
177
  }
178
+ /**
179
+ * Network type
180
+ */
181
+ export type Network = 'mainnet' | 'testnet';
178
182
  /**
179
183
  * SDK configuration
180
184
  */
@@ -199,8 +203,50 @@ export interface TonConnectMobileConfig {
199
203
  * Available: 'Tonkeeper', 'MyTonWallet', 'Wallet in Telegram', 'Tonhub'
200
204
  */
201
205
  preferredWallet?: string;
206
+ /** Network (mainnet/testnet) - default: 'mainnet' */
207
+ network?: Network;
208
+ /** TON API endpoint for balance checking (optional)
209
+ * Default: 'https://toncenter.com/api/v2' for mainnet, 'https://testnet.toncenter.com/api/v2' for testnet
210
+ */
211
+ tonApiEndpoint?: string;
202
212
  }
203
213
  /**
204
214
  * Event listener callback type
205
215
  */
206
216
  export type StatusChangeCallback = (status: ConnectionStatus) => void;
217
+ /**
218
+ * Event types for TonConnectMobile
219
+ */
220
+ export type TonConnectEventType = 'connect' | 'disconnect' | 'transaction' | 'error' | 'statusChange';
221
+ /**
222
+ * Event listener callback
223
+ */
224
+ export type TonConnectEventListener<T = any> = (data: T) => void;
225
+ /**
226
+ * Transaction status
227
+ */
228
+ export type TransactionStatus = 'pending' | 'confirmed' | 'failed' | 'unknown';
229
+ /**
230
+ * Transaction status response
231
+ */
232
+ export interface TransactionStatusResponse {
233
+ /** Transaction status */
234
+ status: TransactionStatus;
235
+ /** Transaction hash (if available) */
236
+ hash?: string;
237
+ /** Block number (if confirmed) */
238
+ blockNumber?: number;
239
+ /** Error message (if failed) */
240
+ error?: string;
241
+ }
242
+ /**
243
+ * Balance response
244
+ */
245
+ export interface BalanceResponse {
246
+ /** Balance in nanotons */
247
+ balance: string;
248
+ /** Balance in TON (formatted) */
249
+ balanceTon: string;
250
+ /** Network */
251
+ network: Network;
252
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazium/ton-connect-mobile",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Production-ready TON Connect Mobile SDK for React Native and Expo. Implements the real TonConnect protocol for mobile applications using deep links and callbacks.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -31,6 +31,7 @@ export const SUPPORTED_WALLETS: WalletDefinition[] = [
31
31
  appName: 'Tonkeeper',
32
32
  universalLink: 'https://app.tonkeeper.com/ton-connect',
33
33
  deepLink: 'tonkeeper://',
34
+ iconUrl: 'https://tonkeeper.com/assets/tonconnect-icon.png',
34
35
  platforms: ['ios', 'android', 'web'], // CRITICAL FIX: Tonkeeper Web is supported
35
36
  preferredReturnStrategy: 'post_redirect', // CRITICAL FIX: 'back' strategy may not send callback properly, use 'post_redirect'
36
37
  requiresReturnScheme: true, // CRITICAL FIX: Mobile apps need returnScheme for proper callback handling
@@ -40,6 +41,7 @@ export const SUPPORTED_WALLETS: WalletDefinition[] = [
40
41
  appName: 'MyTonWallet',
41
42
  universalLink: 'https://connect.mytonwallet.org',
42
43
  deepLink: 'mytonwallet://',
44
+ iconUrl: 'https://static.mytonwallet.io/icon-256.png',
43
45
  platforms: ['ios', 'android', 'web'],
44
46
  preferredReturnStrategy: 'post_redirect',
45
47
  requiresReturnScheme: true, // MyTonWallet requires explicit returnScheme
@@ -49,6 +51,7 @@ export const SUPPORTED_WALLETS: WalletDefinition[] = [
49
51
  appName: 'Wallet',
50
52
  universalLink: 'https://wallet.tonapi.io/ton-connect',
51
53
  deepLink: 'tg://',
54
+ iconUrl: 'https://wallet.tonapi.io/icon.png',
52
55
  platforms: ['ios', 'android'],
53
56
  preferredReturnStrategy: 'post_redirect',
54
57
  requiresReturnScheme: true, // Telegram Wallet requires explicit returnScheme
@@ -58,6 +61,7 @@ export const SUPPORTED_WALLETS: WalletDefinition[] = [
58
61
  appName: 'Tonhub',
59
62
  universalLink: 'https://tonhub.com/ton-connect',
60
63
  deepLink: 'tonhub://',
64
+ iconUrl: 'https://tonhub.com/tonconnect_logo.png',
61
65
  platforms: ['ios', 'android'],
62
66
  preferredReturnStrategy: 'post_redirect',
63
67
  requiresReturnScheme: true, // Tonhub requires explicit returnScheme for proper callback