@blazium/ton-connect-mobile 1.1.1 → 1.1.2

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.
@@ -0,0 +1,103 @@
1
+ /**
2
+ * TonConnectButton component
3
+ * Compatible with @tonconnect/ui-react TonConnectButton
4
+ */
5
+
6
+ import React, { useState } from 'react';
7
+ import { TouchableOpacity, Text, StyleSheet, ActivityIndicator, ViewStyle, TextStyle } from 'react-native';
8
+ import { useTonConnectUI, useTonWallet } from './index';
9
+
10
+ export interface TonConnectButtonProps {
11
+ /** Button text when disconnected */
12
+ text?: string;
13
+ /** Button text when connected */
14
+ connectedText?: string;
15
+ /** Custom styles */
16
+ style?: ViewStyle;
17
+ /** Custom text styles */
18
+ textStyle?: TextStyle;
19
+ /** Callback when button is pressed */
20
+ onPress?: () => void;
21
+ }
22
+
23
+ /**
24
+ * TonConnectButton - Button component for connecting/disconnecting wallet
25
+ * Compatible with @tonconnect/ui-react TonConnectButton
26
+ */
27
+ export function TonConnectButton({
28
+ text = 'Connect Wallet',
29
+ connectedText = 'Disconnect',
30
+ style,
31
+ textStyle,
32
+ onPress,
33
+ }: TonConnectButtonProps): JSX.Element {
34
+ const tonConnectUI = useTonConnectUI();
35
+ const wallet = useTonWallet();
36
+ const isConnected = wallet?.connected || false;
37
+ const [isLoading, setIsLoading] = useState(false);
38
+
39
+ const handlePress = async () => {
40
+ // CRITICAL FIX: Prevent multiple simultaneous presses
41
+ if (isLoading) {
42
+ return;
43
+ }
44
+
45
+ if (onPress) {
46
+ onPress();
47
+ return;
48
+ }
49
+
50
+ setIsLoading(true);
51
+ try {
52
+ if (isConnected) {
53
+ await tonConnectUI.disconnect();
54
+ } else {
55
+ await tonConnectUI.openModal();
56
+ await tonConnectUI.connectWallet();
57
+ }
58
+ } catch (error) {
59
+ // CRITICAL FIX: Handle errors gracefully
60
+ console.error('TonConnectButton error:', error);
61
+ // Error is already handled by the SDK/UI, just reset loading state
62
+ } finally {
63
+ setIsLoading(false);
64
+ }
65
+ };
66
+
67
+ return (
68
+ <TouchableOpacity
69
+ style={[styles.button, style, isLoading && styles.buttonDisabled]}
70
+ onPress={handlePress}
71
+ disabled={isLoading}
72
+ >
73
+ {isLoading ? (
74
+ <ActivityIndicator color="#ffffff" />
75
+ ) : (
76
+ <Text style={[styles.buttonText, textStyle]}>
77
+ {isConnected ? connectedText : text}
78
+ </Text>
79
+ )}
80
+ </TouchableOpacity>
81
+ );
82
+ }
83
+
84
+ const styles = StyleSheet.create({
85
+ button: {
86
+ backgroundColor: '#0088cc',
87
+ paddingHorizontal: 24,
88
+ paddingVertical: 12,
89
+ borderRadius: 8,
90
+ alignItems: 'center',
91
+ justifyContent: 'center',
92
+ minHeight: 44,
93
+ },
94
+ buttonDisabled: {
95
+ opacity: 0.6,
96
+ },
97
+ buttonText: {
98
+ color: '#ffffff',
99
+ fontSize: 16,
100
+ fontWeight: '600',
101
+ },
102
+ });
103
+
@@ -0,0 +1,290 @@
1
+ /**
2
+ * React integration layer for @tonconnect/ui-react compatibility
3
+ * Provides TonConnectUIProvider, hooks, and components compatible with @tonconnect/ui-react API
4
+ */
5
+
6
+ import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
7
+ import { TonConnectMobile, ConnectionStatus, WalletInfo, SendTransactionRequest } from '../index';
8
+ import type { TonConnectMobileConfig } from '../types';
9
+
10
+ /**
11
+ * Account information (compatible with @tonconnect/ui-react)
12
+ */
13
+ export interface Account {
14
+ address: string;
15
+ chain: number;
16
+ publicKey?: string;
17
+ }
18
+
19
+ /**
20
+ * Wallet state (compatible with @tonconnect/ui-react)
21
+ */
22
+ export interface WalletState {
23
+ account: Account | null;
24
+ wallet: WalletInfo | null;
25
+ connected: boolean;
26
+ }
27
+
28
+ /**
29
+ * Transaction response (compatible with @tonconnect/ui-react)
30
+ */
31
+ export interface TransactionResponse {
32
+ boc: string;
33
+ signature: string;
34
+ }
35
+
36
+ /**
37
+ * Sign data request
38
+ */
39
+ export interface SignDataRequest {
40
+ /** Data to sign (will be base64 encoded) */
41
+ data: string | Uint8Array;
42
+ /** Optional version */
43
+ version?: string;
44
+ }
45
+
46
+ /**
47
+ * Sign data response
48
+ */
49
+ export interface SignDataResponse {
50
+ signature: string;
51
+ timestamp: number;
52
+ }
53
+
54
+ /**
55
+ * TonConnect UI instance interface (compatible with @tonconnect/ui-react)
56
+ */
57
+ export interface TonConnectUI {
58
+ /** Open connection modal */
59
+ openModal: () => Promise<void>;
60
+ /** Close connection modal */
61
+ closeModal: () => void;
62
+ /** Connect to wallet */
63
+ connectWallet: () => Promise<void>;
64
+ /** Disconnect from wallet */
65
+ disconnect: () => Promise<void>;
66
+ /** Send transaction */
67
+ sendTransaction: (transaction: SendTransactionRequest) => Promise<TransactionResponse>;
68
+ /** Sign data */
69
+ signData: (request: SignDataRequest) => Promise<SignDataResponse>;
70
+ /** Current wallet state */
71
+ wallet: WalletState | null;
72
+ /** Modal open state */
73
+ modalState: {
74
+ open: boolean;
75
+ };
76
+ /** UI kit version */
77
+ uiVersion: string;
78
+ }
79
+
80
+ /**
81
+ * Context value
82
+ */
83
+ interface TonConnectUIContextValue {
84
+ tonConnectUI: TonConnectUI;
85
+ sdk: TonConnectMobile;
86
+ }
87
+
88
+ const TonConnectUIContext = createContext<TonConnectUIContextValue | null>(null);
89
+
90
+ /**
91
+ * TonConnectUIProvider props
92
+ */
93
+ export interface TonConnectUIProviderProps {
94
+ /** SDK configuration */
95
+ config: TonConnectMobileConfig;
96
+ /** Children */
97
+ children: ReactNode;
98
+ /** Optional SDK instance (for testing or custom instances) */
99
+ sdkInstance?: TonConnectMobile;
100
+ }
101
+
102
+ /**
103
+ * TonConnectUIProvider - React context provider for TON Connect
104
+ * Compatible with @tonconnect/ui-react API
105
+ */
106
+ export function TonConnectUIProvider({
107
+ config,
108
+ children,
109
+ sdkInstance,
110
+ }: TonConnectUIProviderProps): JSX.Element {
111
+ const [sdk] = useState<TonConnectMobile>(() => sdkInstance || new TonConnectMobile(config));
112
+ const [walletState, setWalletState] = useState<WalletState | null>(null);
113
+ const [modalOpen, setModalOpen] = useState(false);
114
+ const [isConnecting, setIsConnecting] = useState(false);
115
+
116
+ // Update wallet state from SDK status
117
+ const updateWalletState = useCallback((status: ConnectionStatus) => {
118
+ if (status.connected && status.wallet) {
119
+ setWalletState({
120
+ account: {
121
+ address: status.wallet.address,
122
+ chain: -239, // TON mainnet chain ID
123
+ publicKey: status.wallet.publicKey,
124
+ },
125
+ wallet: status.wallet,
126
+ connected: true,
127
+ });
128
+ } else {
129
+ setWalletState({
130
+ account: null,
131
+ wallet: null,
132
+ connected: false,
133
+ });
134
+ }
135
+ }, []);
136
+
137
+ // Subscribe to SDK status changes
138
+ useEffect(() => {
139
+ // Set initial state
140
+ const initialStatus = sdk.getStatus();
141
+ updateWalletState(initialStatus);
142
+
143
+ // Subscribe to changes
144
+ const unsubscribe = sdk.onStatusChange((status) => {
145
+ updateWalletState(status);
146
+ // Close modal when connected
147
+ if (status.connected) {
148
+ setModalOpen(false);
149
+ setIsConnecting(false);
150
+ }
151
+ });
152
+
153
+ return () => {
154
+ unsubscribe();
155
+ // CRITICAL FIX: Cleanup SDK on unmount to prevent memory leaks
156
+ // Note: SDK has its own cleanup via destroy(), but we don't call it here
157
+ // to allow SDK to persist across component remounts (e.g., navigation)
158
+ };
159
+ }, [sdk, updateWalletState]);
160
+
161
+ // Open modal
162
+ const openModal = useCallback(async () => {
163
+ setModalOpen(true);
164
+ }, []);
165
+
166
+ // Close modal
167
+ const closeModal = useCallback(() => {
168
+ setModalOpen(false);
169
+ setIsConnecting(false);
170
+ }, []);
171
+
172
+ // Connect wallet
173
+ const connectWallet = useCallback(async () => {
174
+ // CRITICAL FIX: Use functional update to avoid race condition
175
+ setIsConnecting((prev) => {
176
+ if (prev) {
177
+ // Already connecting, return early
178
+ return prev;
179
+ }
180
+ return true;
181
+ });
182
+
183
+ // Wait for connection
184
+ try {
185
+ await sdk.connect();
186
+ // Status update will be handled by the subscription
187
+ } catch (error) {
188
+ setIsConnecting(false);
189
+ throw error;
190
+ }
191
+ }, [sdk]);
192
+
193
+ // Disconnect
194
+ const disconnect = useCallback(async () => {
195
+ await sdk.disconnect();
196
+ setModalOpen(false);
197
+ }, [sdk]);
198
+
199
+ // Send transaction
200
+ const sendTransaction = useCallback(
201
+ async (transaction: SendTransactionRequest): Promise<TransactionResponse> => {
202
+ const response = await sdk.sendTransaction(transaction);
203
+ return {
204
+ boc: response.boc,
205
+ signature: response.signature,
206
+ };
207
+ },
208
+ [sdk]
209
+ );
210
+
211
+ // Sign data
212
+ const signData = useCallback(
213
+ async (request: SignDataRequest): Promise<SignDataResponse> => {
214
+ const response = await sdk.signData(request.data, request.version);
215
+ return {
216
+ signature: response.signature,
217
+ timestamp: response.timestamp,
218
+ };
219
+ },
220
+ [sdk]
221
+ );
222
+
223
+ // Create TonConnectUI instance
224
+ const tonConnectUI: TonConnectUI = {
225
+ openModal,
226
+ closeModal,
227
+ connectWallet,
228
+ disconnect,
229
+ sendTransaction,
230
+ signData,
231
+ wallet: walletState,
232
+ modalState: {
233
+ open: modalOpen,
234
+ },
235
+ uiVersion: '1.0.0',
236
+ };
237
+
238
+ const contextValue: TonConnectUIContextValue = {
239
+ tonConnectUI,
240
+ sdk,
241
+ };
242
+
243
+ return <TonConnectUIContext.Provider value={contextValue}>{children}</TonConnectUIContext.Provider>;
244
+ }
245
+
246
+ /**
247
+ * Hook to access TonConnectUI instance
248
+ * Compatible with @tonconnect/ui-react useTonConnectUI hook
249
+ */
250
+ export function useTonConnectUI(): TonConnectUI {
251
+ const context = useContext(TonConnectUIContext);
252
+ if (!context) {
253
+ throw new Error('useTonConnectUI must be used within TonConnectUIProvider');
254
+ }
255
+ return context.tonConnectUI;
256
+ }
257
+
258
+ /**
259
+ * Hook to access wallet state
260
+ * Compatible with @tonconnect/ui-react useTonWallet hook
261
+ */
262
+ export function useTonWallet(): WalletState | null {
263
+ const tonConnectUI = useTonConnectUI();
264
+ return tonConnectUI.wallet;
265
+ }
266
+
267
+ /**
268
+ * Hook to access modal state
269
+ * Compatible with @tonconnect/ui-react useTonConnectModal hook
270
+ */
271
+ export function useTonConnectModal(): { open: boolean; close: () => void; openModal: () => Promise<void> } {
272
+ const tonConnectUI = useTonConnectUI();
273
+ return {
274
+ open: tonConnectUI.modalState.open,
275
+ close: tonConnectUI.closeModal,
276
+ openModal: tonConnectUI.openModal,
277
+ };
278
+ }
279
+
280
+ /**
281
+ * Hook to access SDK instance (for advanced usage)
282
+ */
283
+ export function useTonConnectSDK(): TonConnectMobile {
284
+ const context = useContext(TonConnectUIContext);
285
+ if (!context) {
286
+ throw new Error('useTonConnectSDK must be used within TonConnectUIProvider');
287
+ }
288
+ return context.sdk;
289
+ }
290
+
@@ -0,0 +1,24 @@
1
+ /**
2
+ * React integration exports
3
+ * Re-export all React components and hooks
4
+ */
5
+
6
+ export {
7
+ TonConnectUIProvider,
8
+ useTonConnectUI,
9
+ useTonWallet,
10
+ useTonConnectModal,
11
+ useTonConnectSDK,
12
+ } from './TonConnectUIProvider';
13
+ export type {
14
+ TonConnectUIProviderProps,
15
+ TonConnectUI,
16
+ WalletState,
17
+ Account,
18
+ TransactionResponse,
19
+ SignDataRequest,
20
+ SignDataResponse,
21
+ } from './TonConnectUIProvider';
22
+ export { TonConnectButton } from './TonConnectButton';
23
+ export type { TonConnectButtonProps } from './TonConnectButton';
24
+
@@ -82,9 +82,9 @@ export interface ConnectionRequestPayload {
82
82
  name: 'ton_addr';
83
83
  }>;
84
84
  /** Return strategy - how wallet should return to the app */
85
- returnStrategy?: 'back' | 'none';
86
- // NOTE: returnScheme is NOT part of the official TON Connect protocol
87
- // For mobile apps, wallets should handle callbacks based on the manifest URL
85
+ returnStrategy?: 'back' | 'post_redirect' | 'none';
86
+ /** Return scheme for mobile apps (required by many wallets for proper callback handling) */
87
+ returnScheme?: string;
88
88
  }
89
89
 
90
90
  /**
@@ -143,7 +143,7 @@ export interface TransactionRequestPayload {
143
143
  /** Return URL scheme (for mobile apps) */
144
144
  returnScheme?: string;
145
145
  /** Return strategy */
146
- returnStrategy?: 'back' | 'none';
146
+ returnStrategy?: 'back' | 'post_redirect' | 'none';
147
147
  }
148
148
 
149
149
  /**