@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 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;