@getecho-ai/react-native-sdk 1.0.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/README.md +350 -0
- package/dist/bridge/CallbackManager.d.ts +46 -0
- package/dist/bridge/CallbackManager.js +117 -0
- package/dist/bridge/WebViewBridge.d.ts +67 -0
- package/dist/bridge/WebViewBridge.js +151 -0
- package/dist/components/EchoChat.d.ts +20 -0
- package/dist/components/EchoChat.js +45 -0
- package/dist/components/EchoChatButton.d.ts +15 -0
- package/dist/components/EchoChatButton.js +65 -0
- package/dist/components/EchoChatModal.d.ts +13 -0
- package/dist/components/EchoChatModal.js +473 -0
- package/dist/components/EchoProvider.d.ts +30 -0
- package/dist/components/EchoProvider.js +240 -0
- package/dist/hooks/useSimpleCart.d.ts +76 -0
- package/dist/hooks/useSimpleCart.js +134 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +77 -0
- package/dist/types/index.d.ts +156 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/resolveApiUrl.d.ts +42 -0
- package/dist/utils/resolveApiUrl.js +82 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# Echo React Native SDK
|
|
2
|
+
|
|
3
|
+
AI-powered chat and e-commerce SDK for React Native apps. Add an intelligent shopping assistant to your app in minutes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @getecho-ai/react-native-sdk react-native-webview @react-native-async-storage/async-storage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Expo users:**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx expo install @getecho-ai/react-native-sdk react-native-webview @react-native-async-storage/async-storage
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start (< 5 minutes)
|
|
18
|
+
|
|
19
|
+
The fastest way to get started - uses the built-in `useSimpleCart` hook for automatic cart management:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { EchoProvider, EchoChat, useSimpleCart } from '@getecho-ai/react-native-sdk';
|
|
23
|
+
|
|
24
|
+
function App() {
|
|
25
|
+
const { cart, callbacks } = useSimpleCart();
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<EchoProvider config={{ apiKey: 'your-api-key', callbacks }}>
|
|
29
|
+
<YourApp cart={cart} />
|
|
30
|
+
<EchoChat />
|
|
31
|
+
</EchoProvider>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
That's it! The AI assistant can now add products to cart and show cart contents.
|
|
37
|
+
|
|
38
|
+
## Production Setup (Custom Cart)
|
|
39
|
+
|
|
40
|
+
For production apps with existing cart logic:
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { EchoProvider, EchoChat } from '@getecho-ai/react-native-sdk';
|
|
44
|
+
|
|
45
|
+
function App() {
|
|
46
|
+
return (
|
|
47
|
+
<EchoProvider
|
|
48
|
+
config={{
|
|
49
|
+
apiKey: 'your-api-key',
|
|
50
|
+
callbacks: {
|
|
51
|
+
// Required: Handle add to cart
|
|
52
|
+
onAddToCart: async (product, quantity = 1) => {
|
|
53
|
+
await yourCartService.add(product.id, quantity);
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
cartItemCount: yourCartService.getItemCount()
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// Required: Return current cart
|
|
61
|
+
onGetCart: async () => {
|
|
62
|
+
const cart = await yourCartService.getCart();
|
|
63
|
+
return {
|
|
64
|
+
success: true,
|
|
65
|
+
cart: {
|
|
66
|
+
items: cart.items.map(item => ({
|
|
67
|
+
productId: item.id,
|
|
68
|
+
quantity: item.qty,
|
|
69
|
+
product: item.product,
|
|
70
|
+
})),
|
|
71
|
+
itemCount: cart.totalItems,
|
|
72
|
+
total: cart.totalPrice,
|
|
73
|
+
currency: 'TRY',
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Optional: Navigate to product detail
|
|
79
|
+
onNavigateToProduct: (productId) => {
|
|
80
|
+
navigation.navigate('ProductDetail', { id: productId });
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Optional: Navigate to checkout
|
|
84
|
+
onNavigateToCheckout: () => {
|
|
85
|
+
navigation.navigate('Checkout');
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<YourApp />
|
|
91
|
+
<EchoChat />
|
|
92
|
+
</EchoProvider>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Local Development
|
|
98
|
+
|
|
99
|
+
When developing locally with the Echo backend:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<EchoProvider
|
|
103
|
+
config={{
|
|
104
|
+
apiKey: 'your-api-key',
|
|
105
|
+
apiUrl: 'http://localhost:3000', // Auto-converts to 10.0.2.2 on Android emulator
|
|
106
|
+
callbacks,
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The SDK automatically handles Android emulator's localhost quirk (converts `localhost` to `10.0.2.2`).
|
|
112
|
+
|
|
113
|
+
For real devices, use your machine's local IP:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
apiUrl: 'http://192.168.1.100:3000'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Components
|
|
120
|
+
|
|
121
|
+
### `<EchoProvider>`
|
|
122
|
+
|
|
123
|
+
Wrap your app with this provider. Required props:
|
|
124
|
+
|
|
125
|
+
| Prop | Type | Description |
|
|
126
|
+
|------|------|-------------|
|
|
127
|
+
| `config.apiKey` | `string` | Your Echo API key |
|
|
128
|
+
| `config.callbacks` | `EchoCallbacks` | Cart and navigation callbacks |
|
|
129
|
+
|
|
130
|
+
Optional props:
|
|
131
|
+
|
|
132
|
+
| Prop | Type | Description |
|
|
133
|
+
|------|------|-------------|
|
|
134
|
+
| `config.apiUrl` | `string` | Override API URL (default: production) |
|
|
135
|
+
| `config.userId` | `string` | Pre-set user ID |
|
|
136
|
+
| `config.userEmail` | `string` | User email for personalization |
|
|
137
|
+
| `config.theme` | `EchoTheme` | UI customization |
|
|
138
|
+
| `config.uiSettings` | `UISettings` | Control UI elements visibility |
|
|
139
|
+
|
|
140
|
+
### `<EchoChat>`
|
|
141
|
+
|
|
142
|
+
The main chat component with floating button and modal.
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
<EchoChat
|
|
146
|
+
floating={true} // Show floating button (default: true)
|
|
147
|
+
position="bottom-right" // Button position (default: "bottom-right")
|
|
148
|
+
/>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### `<EchoChatButton>`
|
|
152
|
+
|
|
153
|
+
Standalone chat button for custom layouts:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<EchoChatButton
|
|
157
|
+
onPress={() => setModalVisible(true)}
|
|
158
|
+
position="bottom-left"
|
|
159
|
+
/>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### `<EchoChatModal>`
|
|
163
|
+
|
|
164
|
+
Standalone chat modal:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
<EchoChatModal
|
|
168
|
+
visible={isVisible}
|
|
169
|
+
onClose={() => setIsVisible(false)}
|
|
170
|
+
/>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Hooks
|
|
174
|
+
|
|
175
|
+
### `useSimpleCart(options?)`
|
|
176
|
+
|
|
177
|
+
Built-in cart management for quick prototyping:
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
const {
|
|
181
|
+
cart, // Current cart state
|
|
182
|
+
setCart, // Direct cart setter
|
|
183
|
+
clearCart, // Clear all items
|
|
184
|
+
removeItem, // Remove by productId
|
|
185
|
+
updateQuantity, // Update item quantity
|
|
186
|
+
callbacks, // Ready-to-use EchoCallbacks
|
|
187
|
+
} = useSimpleCart({
|
|
188
|
+
initialCart: { items: [], itemCount: 0 },
|
|
189
|
+
onCartChange: (cart) => console.log('Cart updated:', cart),
|
|
190
|
+
onNavigateToProduct: (id) => navigation.navigate('Product', { id }),
|
|
191
|
+
onNavigateToCheckout: () => navigation.navigate('Checkout'),
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### `useEcho()`
|
|
196
|
+
|
|
197
|
+
Access Echo context anywhere in your app:
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
const {
|
|
201
|
+
config, // Current config
|
|
202
|
+
userId, // Current user ID
|
|
203
|
+
chatId, // Current chat ID
|
|
204
|
+
isReady, // SDK initialized
|
|
205
|
+
} = useEcho();
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Utilities
|
|
209
|
+
|
|
210
|
+
### `resolveApiUrl(url?)`
|
|
211
|
+
|
|
212
|
+
Platform-aware URL resolution for development:
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { resolveApiUrl } from '@getecho-ai/react-native-sdk';
|
|
216
|
+
|
|
217
|
+
const url = resolveApiUrl('http://localhost:3000');
|
|
218
|
+
// Android emulator: 'http://10.0.2.2:3000'
|
|
219
|
+
// iOS simulator: 'http://localhost:3000'
|
|
220
|
+
// Production URL: unchanged
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### `getLocalhostUrl(port?)`
|
|
224
|
+
|
|
225
|
+
Get the correct localhost URL for current platform:
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { getLocalhostUrl } from '@getecho-ai/react-native-sdk';
|
|
229
|
+
|
|
230
|
+
const url = getLocalhostUrl(3000);
|
|
231
|
+
// Android: 'http://10.0.2.2:3000'
|
|
232
|
+
// iOS: 'http://localhost:3000'
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## UI Settings
|
|
236
|
+
|
|
237
|
+
Control visibility of UI elements:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
<EchoProvider
|
|
241
|
+
config={{
|
|
242
|
+
apiKey: 'your-key',
|
|
243
|
+
callbacks,
|
|
244
|
+
uiSettings: {
|
|
245
|
+
showSidebar: false, // Hide conversation history sidebar
|
|
246
|
+
showExpandButton: false, // Hide expand/collapse button
|
|
247
|
+
showCartButton: true, // Show cart button in header
|
|
248
|
+
showHistoryButton: false, // Hide history/new chat button
|
|
249
|
+
showCloseButton: true, // Show close button
|
|
250
|
+
},
|
|
251
|
+
}}
|
|
252
|
+
>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Troubleshooting
|
|
256
|
+
|
|
257
|
+
### "Network request failed" on Android emulator
|
|
258
|
+
|
|
259
|
+
The SDK should auto-convert localhost URLs. If issues persist:
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
// Explicitly use 10.0.2.2
|
|
263
|
+
apiUrl: 'http://10.0.2.2:3000'
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### WebView not loading
|
|
267
|
+
|
|
268
|
+
Ensure peer dependencies are installed:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npm ls react-native-webview @react-native-async-storage/async-storage
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
For Expo, run `npx expo install` to get compatible versions.
|
|
275
|
+
|
|
276
|
+
### Cart not updating
|
|
277
|
+
|
|
278
|
+
1. Verify `onAddToCart` returns `{ success: true, cartItemCount: N }`
|
|
279
|
+
2. Check `onGetCart` returns valid cart structure
|
|
280
|
+
3. Add console.log in callbacks to debug
|
|
281
|
+
|
|
282
|
+
### TypeScript errors
|
|
283
|
+
|
|
284
|
+
Import types explicitly:
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
import type { Product, Cart, EchoCallbacks } from '@getecho-ai/react-native-sdk';
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## API Reference
|
|
291
|
+
|
|
292
|
+
### Types
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
type Product = {
|
|
296
|
+
id: string;
|
|
297
|
+
title: string;
|
|
298
|
+
description?: string;
|
|
299
|
+
priceAmount?: number;
|
|
300
|
+
currency?: string;
|
|
301
|
+
primaryImage?: string;
|
|
302
|
+
images?: string[];
|
|
303
|
+
url?: string;
|
|
304
|
+
category?: string;
|
|
305
|
+
brand?: string;
|
|
306
|
+
inStock?: boolean;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
type Cart = {
|
|
310
|
+
items: CartItem[];
|
|
311
|
+
total?: number;
|
|
312
|
+
currency?: string;
|
|
313
|
+
itemCount: number;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
type CartItem = {
|
|
317
|
+
productId: string;
|
|
318
|
+
quantity: number;
|
|
319
|
+
product?: Product;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
type AddToCartResult = {
|
|
323
|
+
success: boolean;
|
|
324
|
+
cartItemCount?: number;
|
|
325
|
+
error?: string;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
type GetCartResult = {
|
|
329
|
+
success: boolean;
|
|
330
|
+
cart?: Cart;
|
|
331
|
+
error?: string;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
type EchoCallbacks = {
|
|
335
|
+
onAddToCart: (product: Product, quantity?: number) => Promise<AddToCartResult>;
|
|
336
|
+
onGetCart: () => Promise<GetCartResult>;
|
|
337
|
+
onNavigateToProduct?: (productId: string) => void;
|
|
338
|
+
onNavigateToUrl?: (url: string) => void;
|
|
339
|
+
onNavigateToCheckout?: () => void;
|
|
340
|
+
onAuthRequired?: () => void;
|
|
341
|
+
};
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Example App
|
|
345
|
+
|
|
346
|
+
See the `/example` directory for a complete Expo demo app.
|
|
347
|
+
|
|
348
|
+
## License
|
|
349
|
+
|
|
350
|
+
MIT
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CallbackManager - Handles async actions between WebView and Native
|
|
3
|
+
*
|
|
4
|
+
* This implements the manual completion pattern where:
|
|
5
|
+
* 1. Action is initiated with a callbackId
|
|
6
|
+
* 2. Partner app completes the action (e.g., user logs in, adds to cart)
|
|
7
|
+
* 3. Partner calls completeAction(callbackId, result)
|
|
8
|
+
*
|
|
9
|
+
* Includes 30 second timeout to prevent indefinite hangs
|
|
10
|
+
*/
|
|
11
|
+
import type { CallbackResult } from "../types";
|
|
12
|
+
declare class CallbackManager {
|
|
13
|
+
private readonly pendingCallbacks;
|
|
14
|
+
/**
|
|
15
|
+
* Initiate a new action and return a promise that resolves when
|
|
16
|
+
* the partner app completes the action via completeAction()
|
|
17
|
+
* Automatically times out after 30 seconds
|
|
18
|
+
*/
|
|
19
|
+
initiateAction(callbackId: string): Promise<CallbackResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Complete an action with the result from partner app
|
|
22
|
+
* This resolves the pending promise and clears the timeout
|
|
23
|
+
*/
|
|
24
|
+
completeAction(callbackId: string, result: CallbackResult): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Cancel a pending action (e.g., user closes chat)
|
|
27
|
+
* Clears the timeout as well
|
|
28
|
+
*/
|
|
29
|
+
cancelAction(callbackId: string, reason?: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if an action is still pending
|
|
32
|
+
*/
|
|
33
|
+
isPending(callbackId: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Get all pending callback IDs (useful for debugging)
|
|
36
|
+
*/
|
|
37
|
+
getPendingIds(): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Cancel all pending actions (e.g., on unmount)
|
|
40
|
+
* Clears all timeouts as well
|
|
41
|
+
*/
|
|
42
|
+
cancelAll(reason?: string): void;
|
|
43
|
+
}
|
|
44
|
+
export declare const callbackManager: CallbackManager;
|
|
45
|
+
export { CallbackManager };
|
|
46
|
+
export default callbackManager;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CallbackManager - Handles async actions between WebView and Native
|
|
4
|
+
*
|
|
5
|
+
* This implements the manual completion pattern where:
|
|
6
|
+
* 1. Action is initiated with a callbackId
|
|
7
|
+
* 2. Partner app completes the action (e.g., user logs in, adds to cart)
|
|
8
|
+
* 3. Partner calls completeAction(callbackId, result)
|
|
9
|
+
*
|
|
10
|
+
* Includes 30 second timeout to prevent indefinite hangs
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.CallbackManager = exports.callbackManager = void 0;
|
|
14
|
+
const CALLBACK_TIMEOUT_MS = 30000; // 30 seconds
|
|
15
|
+
class CallbackManager {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.pendingCallbacks = new Map();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Initiate a new action and return a promise that resolves when
|
|
21
|
+
* the partner app completes the action via completeAction()
|
|
22
|
+
* Automatically times out after 30 seconds
|
|
23
|
+
*/
|
|
24
|
+
initiateAction(callbackId) {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
const timeoutId = setTimeout(() => {
|
|
27
|
+
const pending = this.pendingCallbacks.get(callbackId);
|
|
28
|
+
if (pending) {
|
|
29
|
+
console.warn(`[EchoSDK] Callback timeout: ${callbackId}`);
|
|
30
|
+
resolve({ success: false, error: "CALLBACK_TIMEOUT" });
|
|
31
|
+
this.pendingCallbacks.delete(callbackId);
|
|
32
|
+
}
|
|
33
|
+
}, CALLBACK_TIMEOUT_MS);
|
|
34
|
+
this.pendingCallbacks.set(callbackId, {
|
|
35
|
+
resolve,
|
|
36
|
+
createdAt: Date.now(),
|
|
37
|
+
timeoutId,
|
|
38
|
+
});
|
|
39
|
+
console.log(`[EchoSDK] Action initiated: ${callbackId}`);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Complete an action with the result from partner app
|
|
44
|
+
* This resolves the pending promise and clears the timeout
|
|
45
|
+
*/
|
|
46
|
+
completeAction(callbackId, result) {
|
|
47
|
+
const pending = this.pendingCallbacks.get(callbackId);
|
|
48
|
+
if (!pending) {
|
|
49
|
+
console.warn(`[EchoSDK] No pending callback found for: ${callbackId}`);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// Clear timeout
|
|
53
|
+
if (pending.timeoutId) {
|
|
54
|
+
clearTimeout(pending.timeoutId);
|
|
55
|
+
}
|
|
56
|
+
console.log(`[EchoSDK] Action completed: ${callbackId}`, result);
|
|
57
|
+
pending.resolve(result);
|
|
58
|
+
this.pendingCallbacks.delete(callbackId);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Cancel a pending action (e.g., user closes chat)
|
|
63
|
+
* Clears the timeout as well
|
|
64
|
+
*/
|
|
65
|
+
cancelAction(callbackId, reason) {
|
|
66
|
+
const pending = this.pendingCallbacks.get(callbackId);
|
|
67
|
+
if (!pending) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// Clear timeout
|
|
71
|
+
if (pending.timeoutId) {
|
|
72
|
+
clearTimeout(pending.timeoutId);
|
|
73
|
+
}
|
|
74
|
+
console.log(`[EchoSDK] Action cancelled: ${callbackId}`, reason);
|
|
75
|
+
pending.resolve({
|
|
76
|
+
success: false,
|
|
77
|
+
error: reason || "ACTION_CANCELLED",
|
|
78
|
+
});
|
|
79
|
+
this.pendingCallbacks.delete(callbackId);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if an action is still pending
|
|
84
|
+
*/
|
|
85
|
+
isPending(callbackId) {
|
|
86
|
+
return this.pendingCallbacks.has(callbackId);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get all pending callback IDs (useful for debugging)
|
|
90
|
+
*/
|
|
91
|
+
getPendingIds() {
|
|
92
|
+
return Array.from(this.pendingCallbacks.keys());
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Cancel all pending actions (e.g., on unmount)
|
|
96
|
+
* Clears all timeouts as well
|
|
97
|
+
*/
|
|
98
|
+
cancelAll(reason) {
|
|
99
|
+
const message = reason || "ALL_ACTIONS_CANCELLED";
|
|
100
|
+
this.pendingCallbacks.forEach((pending, callbackId) => {
|
|
101
|
+
// Clear timeout
|
|
102
|
+
if (pending.timeoutId) {
|
|
103
|
+
clearTimeout(pending.timeoutId);
|
|
104
|
+
}
|
|
105
|
+
pending.resolve({
|
|
106
|
+
success: false,
|
|
107
|
+
error: message,
|
|
108
|
+
});
|
|
109
|
+
console.log(`[EchoSDK] Action cancelled: ${callbackId}`);
|
|
110
|
+
});
|
|
111
|
+
this.pendingCallbacks.clear();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.CallbackManager = CallbackManager;
|
|
115
|
+
// Export singleton instance
|
|
116
|
+
exports.callbackManager = new CallbackManager();
|
|
117
|
+
exports.default = exports.callbackManager;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebViewBridge - Handles communication between WebView and React Native
|
|
3
|
+
*
|
|
4
|
+
* Uses postMessage API for bidirectional communication:
|
|
5
|
+
* - WebView → Native: window.ReactNativeWebView.postMessage()
|
|
6
|
+
* - Native → WebView: webViewRef.injectJavaScript()
|
|
7
|
+
*/
|
|
8
|
+
import type { BridgeMessage, BridgeMessageType } from "../types";
|
|
9
|
+
export type MessageHandler = (message: BridgeMessage) => void;
|
|
10
|
+
declare class WebViewBridge {
|
|
11
|
+
private readonly messageHandlers;
|
|
12
|
+
private webViewRef;
|
|
13
|
+
/**
|
|
14
|
+
* Set the WebView reference for sending messages to WebView
|
|
15
|
+
*/
|
|
16
|
+
setWebViewRef(ref: any): void;
|
|
17
|
+
/**
|
|
18
|
+
* Register a message handler
|
|
19
|
+
*/
|
|
20
|
+
onMessage(handler: MessageHandler): () => void;
|
|
21
|
+
/**
|
|
22
|
+
* Handle incoming message from WebView
|
|
23
|
+
* This is called by the WebView's onMessage prop
|
|
24
|
+
*/
|
|
25
|
+
handleMessage(event: {
|
|
26
|
+
nativeEvent: {
|
|
27
|
+
data: string;
|
|
28
|
+
};
|
|
29
|
+
}): void;
|
|
30
|
+
/**
|
|
31
|
+
* Send message to WebView
|
|
32
|
+
*/
|
|
33
|
+
sendToWebView(type: BridgeMessageType, payload?: any, callbackId?: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Send a raw message to WebView without wrapping payload
|
|
36
|
+
* Used when the web embed expects root-level fields.
|
|
37
|
+
*/
|
|
38
|
+
sendRawToWebView(message: any): void;
|
|
39
|
+
/**
|
|
40
|
+
* Initialize WebView with config
|
|
41
|
+
*/
|
|
42
|
+
initWebView(config: {
|
|
43
|
+
apiKey: string;
|
|
44
|
+
userId?: string;
|
|
45
|
+
userEmail?: string;
|
|
46
|
+
theme?: any;
|
|
47
|
+
}): void;
|
|
48
|
+
/**
|
|
49
|
+
* Complete an action in WebView
|
|
50
|
+
*/
|
|
51
|
+
completeActionInWebView(callbackId: string, result: {
|
|
52
|
+
success: boolean;
|
|
53
|
+
data?: any;
|
|
54
|
+
error?: string;
|
|
55
|
+
}): void;
|
|
56
|
+
/**
|
|
57
|
+
* Handle internal messages (action completion, etc.)
|
|
58
|
+
*/
|
|
59
|
+
private handleInternalMessage;
|
|
60
|
+
/**
|
|
61
|
+
* Clear all handlers (useful on unmount)
|
|
62
|
+
*/
|
|
63
|
+
clearHandlers(): void;
|
|
64
|
+
}
|
|
65
|
+
export declare const webViewBridge: WebViewBridge;
|
|
66
|
+
export { WebViewBridge };
|
|
67
|
+
export default webViewBridge;
|