@rematter/pylon-react-native 0.1.4

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.
Files changed (40) hide show
  1. package/README.md +503 -0
  2. package/RNPylonChat.podspec +33 -0
  3. package/android/build.gradle +74 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/com/pylon/chatwidget/Pylon.kt +149 -0
  7. package/android/src/main/java/com/pylon/chatwidget/PylonChat.kt +715 -0
  8. package/android/src/main/java/com/pylon/chatwidget/PylonChatController.kt +63 -0
  9. package/android/src/main/java/com/pylon/chatwidget/PylonChatListener.kt +76 -0
  10. package/android/src/main/java/com/pylon/chatwidget/PylonChatView.kt +7 -0
  11. package/android/src/main/java/com/pylon/chatwidget/PylonConfig.kt +62 -0
  12. package/android/src/main/java/com/pylon/chatwidget/PylonDebugView.kt +76 -0
  13. package/android/src/main/java/com/pylon/chatwidget/PylonUser.kt +41 -0
  14. package/android/src/main/java/com/pylonchat/reactnative/RNPylonChatPackage.kt +17 -0
  15. package/android/src/main/java/com/pylonchat/reactnative/RNPylonChatView.kt +298 -0
  16. package/android/src/main/java/com/pylonchat/reactnative/RNPylonChatViewManager.kt +201 -0
  17. package/ios/PylonChat/PylonChat.swift +865 -0
  18. package/ios/RNPylonChatView.swift +332 -0
  19. package/ios/RNPylonChatViewManager.m +55 -0
  20. package/ios/RNPylonChatViewManager.swift +23 -0
  21. package/lib/PylonChatView.d.ts +27 -0
  22. package/lib/PylonChatView.js +78 -0
  23. package/lib/PylonChatWidget.android.d.ts +19 -0
  24. package/lib/PylonChatWidget.android.js +144 -0
  25. package/lib/PylonChatWidget.ios.d.ts +14 -0
  26. package/lib/PylonChatWidget.ios.js +79 -0
  27. package/lib/PylonModule.d.ts +32 -0
  28. package/lib/PylonModule.js +44 -0
  29. package/lib/index.d.ts +5 -0
  30. package/lib/index.js +15 -0
  31. package/lib/types.d.ts +34 -0
  32. package/lib/types.js +2 -0
  33. package/package.json +39 -0
  34. package/src/PylonChatView.tsx +170 -0
  35. package/src/PylonChatWidget.android.tsx +165 -0
  36. package/src/PylonChatWidget.d.ts +15 -0
  37. package/src/PylonChatWidget.ios.tsx +79 -0
  38. package/src/PylonModule.ts +52 -0
  39. package/src/index.ts +15 -0
  40. package/src/types.ts +37 -0
package/README.md ADDED
@@ -0,0 +1,503 @@
1
+ # Pylon Chat SDK for React Native
2
+
3
+ Add Pylon's chat widget to your React Native application to enable in-app customer support.
4
+
5
+ ## Requirements
6
+
7
+ - React Native >= 0.70.0
8
+ - iOS >= 13.0
9
+ - Android minSdkVersion >= 24
10
+
11
+ **Note:** This package uses native modules. For Expo projects, you'll need a [development build](https://docs.expo.dev/develop/development-builds/introduction/).
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ This SDK is distributed as source code and installed from a local directory.
18
+
19
+ ### Step 1: Clone the Repository
20
+
21
+ Clone the SDK repository anywhere on your machine, but preferably forked into your own repository:
22
+
23
+ ```bash
24
+ git clone https://github.com/pylon-labs/sdk-mobile.git
25
+ ```
26
+
27
+ ### Step 2: Install the Package
28
+
29
+ From your React Native project root, install from the local path:
30
+
31
+ ```bash
32
+ npm install /path/to/sdk-mobile/react-native
33
+ # or
34
+ yarn add file:/path/to/sdk-mobile/react-native
35
+ ```
36
+
37
+ **What happens automatically:**
38
+
39
+ - npm runs the `prepare` script (compiles TypeScript, copies native code)
40
+ - Package is installed to `node_modules/@pylon/react-native-chat/`
41
+
42
+ ### Step 3: Link Native Code
43
+
44
+ The SDK includes native Swift (iOS) and Kotlin (Android) code that needs to be linked to your app.
45
+
46
+ **For Expo projects:**
47
+
48
+ Expo apps don't have native folders by default. Generate them:
49
+
50
+ ```bash
51
+ npx expo install expo-dev-client
52
+ npx expo prebuild
53
+ ```
54
+
55
+ This creates `ios/` and `android/` folders with the SDK's native code linked.
56
+
57
+ **For bare React Native projects:**
58
+
59
+ iOS requires CocoaPods linking:
60
+
61
+ ```bash
62
+ cd ios && pod install && cd ..
63
+ ```
64
+
65
+ Android auto-links automatically (no action needed).
66
+
67
+ ### Step 4: Get Your Pylon App ID
68
+
69
+ 1. Login to [app.usepylon.com](https://app.usepylon.com)
70
+ 2. Go to Settings → Chat Widget
71
+ 3. Copy your App ID
72
+
73
+ ---
74
+
75
+ ## Quick Start
76
+
77
+ ```tsx
78
+ import { PylonChatView, type PylonChatViewRef } from "@pylon/react-native-chat";
79
+ import { useRef } from "react";
80
+ import { StyleSheet, View } from "react-native";
81
+
82
+ export default function App() {
83
+ const pylonRef = useRef<PylonChatViewRef>(null);
84
+
85
+ const config = {
86
+ appId: "YOUR_APP_ID", // Get from app.usepylon.com/settings/chat-widget
87
+ widgetBaseUrl: "https://widget.usepylon.com",
88
+ };
89
+
90
+ const user = {
91
+ email: "user@example.com",
92
+ name: "User Name",
93
+ };
94
+
95
+ return (
96
+ <View style={styles.container}>
97
+ {/* Your app content */}
98
+ <Text>My App Content</Text>
99
+
100
+ {/* Pylon Chat Widget - renders as overlay */}
101
+ <PylonChatView
102
+ ref={pylonRef}
103
+ config={config}
104
+ user={user}
105
+ style={styles.chat}
106
+ />
107
+ </View>
108
+ );
109
+ }
110
+
111
+ const styles = StyleSheet.create({
112
+ container: {
113
+ flex: 1,
114
+ },
115
+ chat: {
116
+ position: "absolute",
117
+ top: 0,
118
+ left: 0,
119
+ right: 0,
120
+ bottom: 0,
121
+ },
122
+ });
123
+ ```
124
+
125
+ **Identity Verification (Strongly Recommended):**
126
+
127
+ We strongly recommend setting up [Identity Verification](https://docs.usepylon.com/pylon-docs/chat-widget/identity-verification). The `emailHash` needs to be generated by the server responsible for authorizing your mobile app.
128
+
129
+ ```tsx
130
+ const user = {
131
+ email: "user@example.com",
132
+ name: "User Name",
133
+ emailHash: "sha256_hash_generated_by_server", // Add this for identity verification
134
+ };
135
+ ```
136
+
137
+ ---
138
+
139
+ ## API Reference
140
+
141
+ ### PylonChatView Props
142
+
143
+ #### `config` (required)
144
+
145
+ ```tsx
146
+ {
147
+ appId: string; // Your Pylon app ID (required)
148
+ widgetBaseUrl?: string; // Default: "https://widget.usepylon.com"
149
+ enableLogging?: boolean; // Enable debug logs (default: false)
150
+ debugMode?: boolean; // Show debug overlay (default: false)
151
+ }
152
+ ```
153
+
154
+ #### `user` (required)
155
+
156
+ ```tsx
157
+ {
158
+ email: string; // User's email (required)
159
+ name: string; // User's name (required)
160
+ avatarUrl?: string; // User's avatar URL
161
+ emailHash?: string; // SHA-256 hash for identity verification
162
+ accountId?: string; // Your internal account ID
163
+ accountExternalId?: string; // External system account ID
164
+ }
165
+ ```
166
+
167
+ #### `listener` (optional)
168
+
169
+ Event callbacks for chat interactions:
170
+
171
+ ```tsx
172
+ {
173
+ onPylonLoaded?: () => void;
174
+ onPylonInitialized?: () => void;
175
+ onPylonReady?: () => void;
176
+ onChatOpened?: () => void;
177
+ onChatClosed?: (wasOpen: boolean) => void;
178
+ onUnreadCountChanged?: (count: number) => void;
179
+ onMessageReceived?: (message: string) => void;
180
+ onPylonError?: (error: string) => void;
181
+ }
182
+ ```
183
+
184
+ #### `style` (optional)
185
+
186
+ Standard React Native `ViewStyle`. Typically used to position the widget:
187
+
188
+ ```tsx
189
+ style={{
190
+ position: "absolute",
191
+ top: 0,
192
+ left: 0,
193
+ right: 0,
194
+ bottom: 0,
195
+ }}
196
+ ```
197
+
198
+ ---
199
+
200
+ ### Imperative Methods (via ref)
201
+
202
+ ```tsx
203
+ const pylonRef = useRef<PylonChatViewRef>(null);
204
+
205
+ // Control chat window
206
+ pylonRef.current?.openChat();
207
+ pylonRef.current?.closeChat();
208
+
209
+ // Control chat bubble
210
+ pylonRef.current?.showChatBubble();
211
+ pylonRef.current?.hideChatBubble();
212
+
213
+ // Send messages programmatically
214
+ pylonRef.current?.showNewMessage("Hello from the app!", false);
215
+ pylonRef.current?.showNewMessage("<p><strong>HTML</strong> message</p>", true);
216
+
217
+ // Set custom fields for new issues
218
+ pylonRef.current?.setNewIssueCustomFields({
219
+ source: "mobile-app",
220
+ version: "1.2.3",
221
+ platform: Platform.OS,
222
+ });
223
+
224
+ // Show specific forms
225
+ pylonRef.current?.showTicketForm("form-id");
226
+ pylonRef.current?.showKnowledgeBaseArticle("article-id");
227
+
228
+ // Pre-fill ticket form fields
229
+ pylonRef.current?.setTicketFormFields({
230
+ subject: "Issue from mobile app",
231
+ description: "User reported...",
232
+ });
233
+
234
+ // Identity verification
235
+ pylonRef.current?.updateEmailHash("sha256_hash");
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Usage Patterns
241
+
242
+ ### Basic Setup with Event Listeners
243
+
244
+ ```tsx
245
+ export default function App() {
246
+ const pylonRef = useRef<PylonChatViewRef>(null);
247
+ const [unreadCount, setUnreadCount] = useState(0);
248
+
249
+ const config = {
250
+ appId: "YOUR_APP_ID",
251
+ };
252
+
253
+ const user = {
254
+ email: "user@example.com",
255
+ name: "User Name",
256
+ };
257
+
258
+ const listener = {
259
+ onChatOpened: () => console.log("Chat opened"),
260
+ onChatClosed: (wasOpen) => console.log("Chat closed", wasOpen),
261
+ onUnreadCountChanged: (count) => setUnreadCount(count),
262
+ onMessageReceived: (msg) => console.log("New message:", msg),
263
+ onPylonError: (error) => console.error("Pylon error:", error),
264
+ };
265
+
266
+ return (
267
+ <View style={{ flex: 1 }}>
268
+ <MyAppContent />
269
+
270
+ {unreadCount > 0 && <Badge count={unreadCount} />}
271
+
272
+ <PylonChatView
273
+ ref={pylonRef}
274
+ config={config}
275
+ user={user}
276
+ listener={listener}
277
+ style={StyleSheet.absoluteFill}
278
+ />
279
+ </View>
280
+ );
281
+ }
282
+ ```
283
+
284
+ ### With Safe Area Insets
285
+
286
+ ```tsx
287
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
288
+
289
+ export default function App() {
290
+ const insets = useSafeAreaInsets();
291
+
292
+ return (
293
+ <View style={{ flex: 1 }}>
294
+ <MyAppContent />
295
+
296
+ <PylonChatView
297
+ config={config}
298
+ user={user}
299
+ style={{
300
+ position: "absolute",
301
+ top: 0,
302
+ left: 0,
303
+ right: 0,
304
+ bottom: 0,
305
+ paddingTop: insets.top,
306
+ paddingBottom: insets.bottom,
307
+ }}
308
+ />
309
+ </View>
310
+ );
311
+ }
312
+ ```
313
+
314
+ ### Conditional Rendering (User Login)
315
+
316
+ ```tsx
317
+ export default function App() {
318
+ const [currentUser, setCurrentUser] = useState(null);
319
+
320
+ return (
321
+ <View style={{ flex: 1 }}>
322
+ <MyAppContent />
323
+
324
+ {currentUser && (
325
+ <PylonChatView
326
+ config={{ appId: "YOUR_APP_ID" }}
327
+ user={{
328
+ email: currentUser.email,
329
+ name: currentUser.name,
330
+ }}
331
+ style={StyleSheet.absoluteFill}
332
+ />
333
+ )}
334
+ </View>
335
+ );
336
+ }
337
+ ```
338
+
339
+ ### Custom Fields and Metadata
340
+
341
+ ```tsx
342
+ export default function App() {
343
+ const pylonRef = useRef<PylonChatViewRef>(null);
344
+
345
+ useEffect(() => {
346
+ // Set custom fields when component mounts
347
+ pylonRef.current?.setNewIssueCustomFields({
348
+ app_version: "1.2.3",
349
+ platform: Platform.OS,
350
+ device_model: DeviceInfo.getModel(),
351
+ user_tier: "premium",
352
+ });
353
+ }, []);
354
+
355
+ return (
356
+ <View style={{ flex: 1 }}>
357
+ <MyAppContent />
358
+ <PylonChatView
359
+ ref={pylonRef}
360
+ config={config}
361
+ user={user}
362
+ style={StyleSheet.absoluteFill}
363
+ />
364
+ </View>
365
+ );
366
+ }
367
+ ```
368
+
369
+ ---
370
+
371
+ ## Known Limitations
372
+
373
+ ### Android: Clickable Elements in Chat Widget
374
+
375
+ **Issue:** On Android React Native specifically, clickable elements associated with an unopened chat widget (such as surveys) besides the chat bubble do not currently work. The chat bubble itself functions correctly for opening and closing the widget.
376
+
377
+ **Workaround:** This is a known platform limitation related to touch event handling between React Native and Android.
378
+
379
+ **Need this functionality?** Please contact the Pylon team if your application requires interactive elements like surveys in the Android React Native chat widget. We'd love to hear about your use case and prioritize a smarter React Native Android touch handler.
380
+
381
+ ---
382
+
383
+ ## Troubleshooting
384
+
385
+ ### Android: Java Version Issues
386
+
387
+ **Error:** `Unsupported class file major version 69`
388
+
389
+ **Solution:** Install Java 17:
390
+
391
+ ```bash
392
+ brew install openjdk@17
393
+ export JAVA_HOME=$(/usr/libexec/java_home -v 17)
394
+ ```
395
+
396
+ ### iOS: CocoaPods Encoding Issues
397
+
398
+ **Error:** `Unicode Normalization not appropriate for ASCII-8BIT`
399
+
400
+ **Solution:**
401
+
402
+ ```bash
403
+ export LANG=en_US.UTF-8
404
+ cd ios && pod install
405
+ ```
406
+
407
+ ### Expo: Development Build Required
408
+
409
+ **Error:** `No development build installed`
410
+
411
+ **Solution:** This package uses native modules and cannot run in Expo Go:
412
+
413
+ ```bash
414
+ npx expo install expo-dev-client
415
+ npx expo prebuild
416
+ npx expo run:ios # or expo run:android
417
+ ```
418
+
419
+ ### Module Not Found
420
+
421
+ **Error:** `Invariant Violation: View config not found for component RNPylonChatView`
422
+
423
+ **Solution:** Clean and rebuild:
424
+
425
+ ```bash
426
+ # Expo
427
+ rm -rf ios android .expo
428
+ npx expo prebuild --clean
429
+ npx expo run:ios
430
+
431
+ # Bare React Native
432
+ cd ios && pod install && cd ..
433
+ npx react-native run-ios
434
+ ```
435
+
436
+ ### Touch Events Not Working
437
+
438
+ If touches aren't passing through to views behind the chat bubble:
439
+
440
+ 1. Use `position: "absolute"` and cover the full screen
441
+ 2. Don't wrap `PylonChatView` in additional Views that intercept touches
442
+ 3. The native SDK handles touch pass-through automatically
443
+
444
+ ```tsx
445
+ // ✅ Correct
446
+ <View style={{ flex: 1 }}>
447
+ <ScrollView>{/* Your content */}</ScrollView>
448
+ <PylonChatView style={StyleSheet.absoluteFill} {...props} />
449
+ </View>
450
+
451
+ // ❌ Wrong - extra wrapper intercepts touches
452
+ <View style={{ flex: 1 }}>
453
+ <ScrollView>{/* Your content */}</ScrollView>
454
+ <View style={StyleSheet.absoluteFill}>
455
+ <PylonChatView style={{ flex: 1 }} {...props} />
456
+ </View>
457
+ </View>
458
+ ```
459
+
460
+ ---
461
+
462
+ ## Architecture
463
+
464
+ This package wraps the native iOS and Android Pylon Chat SDKs using React Native's native module system:
465
+
466
+ ```
467
+ ┌─────────────────────────────────────────────────────────────┐
468
+ │ React Native (JavaScript/TypeScript) │
469
+ │ <PylonChatView /> │
470
+ └───────────────────────┬─────────────────────────────────────┘
471
+ │ Native Bridge
472
+ ┌───────────────────────▼─────────────────────────────────────┐
473
+ │ Native Module (iOS: Swift, Android: Kotlin) │
474
+ │ - RNPylonChatView.swift / RNPylonChatView.kt │
475
+ │ - Props → Native config conversion │
476
+ │ - Event forwarding to JS │
477
+ └───────────────────────┬─────────────────────────────────────┘
478
+
479
+ ┌───────────────────────▼─────────────────────────────────────┐
480
+ │ Native SDK (PylonChat) │
481
+ │ ┌─────────────────────────────────────────────────────────┐ │
482
+ │ │ WebView (Pylon Chat Widget) │ │
483
+ │ │ - Loads from widget.usepylon.com │ │
484
+ │ │ - JavaScript ↔ Native bridge │ │
485
+ │ │ - Touch pass-through when collapsed │ │
486
+ │ │ - Real-time messaging │ │
487
+ │ └─────────────────────────────────────────────────────────┘ │
488
+ └─────────────────────────────────────────────────────────────┘
489
+ ```
490
+
491
+ The widget is WebView-based, providing consistent functionality with the web SDK while native code handles platform-specific integration.
492
+
493
+ ---
494
+
495
+ ## Demo App
496
+
497
+ See [`demo-app/README.md`](./demo-app/README.md) for a complete example application demonstrating all SDK features.
498
+
499
+ ---
500
+
501
+ ## Support
502
+
503
+ For issues or questions, please reach out to the Pylon team.
@@ -0,0 +1,33 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "RNPylonChat"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => "13.0" }
14
+ s.source = { :git => "https://github.com/usepylon/pylon-chat-sdk.git", :tag => "#{s.version}" }
15
+
16
+ # Bridge files + Copied iOS SDK
17
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
18
+
19
+ # Exclude demo apps
20
+ s.exclude_files = [
21
+ "demo-app/**/*",
22
+ "../ios/DemoApp/**/*"
23
+ ]
24
+
25
+ s.requires_arc = true
26
+ s.swift_version = "5.0"
27
+
28
+ s.dependency "React-Core"
29
+
30
+ # WebKit is required by PylonChat SDK
31
+ s.frameworks = "WebKit"
32
+ end
33
+
@@ -0,0 +1,74 @@
1
+ buildscript {
2
+ ext.kotlin_version = '1.9.0'
3
+ repositories {
4
+ google()
5
+ mavenCentral()
6
+ }
7
+ dependencies {
8
+ classpath 'com.android.tools.build:gradle:8.0.2'
9
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
10
+ }
11
+ }
12
+
13
+ apply plugin: 'com.android.library'
14
+ apply plugin: 'kotlin-android'
15
+
16
+ def getExtOrDefault(name) {
17
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['RNPylonChat_' + name]
18
+ }
19
+
20
+ def getExtOrIntegerDefault(name) {
21
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['RNPylonChat_' + name]).toInteger()
22
+ }
23
+
24
+ android {
25
+ namespace "com.pylonchat.reactnative"
26
+ compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
27
+
28
+ defaultConfig {
29
+ minSdkVersion getExtOrIntegerDefault('minSdkVersion')
30
+ targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
31
+ versionCode 1
32
+ versionName "1.0"
33
+ }
34
+
35
+ buildTypes {
36
+ release {
37
+ minifyEnabled false
38
+ }
39
+ }
40
+
41
+ lintOptions {
42
+ disable 'GradleCompatible'
43
+ }
44
+
45
+ compileOptions {
46
+ sourceCompatibility JavaVersion.VERSION_17
47
+ targetCompatibility JavaVersion.VERSION_17
48
+ }
49
+
50
+ kotlinOptions {
51
+ jvmTarget = '17'
52
+ }
53
+
54
+ // Source sets now include copied SDK files
55
+ sourceSets {
56
+ main {
57
+ java.srcDirs = ['src/main/java']
58
+ }
59
+ }
60
+ }
61
+
62
+ repositories {
63
+ mavenCentral()
64
+ google()
65
+ }
66
+
67
+ dependencies {
68
+ implementation 'com.facebook.react:react-android'
69
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
70
+
71
+ // WebView dependency (same as parent SDK)
72
+ implementation 'androidx.webkit:webkit:1.8.0'
73
+ }
74
+
@@ -0,0 +1,5 @@
1
+ RNPylonChat_kotlinVersion=1.9.0
2
+ RNPylonChat_compileSdkVersion=34
3
+ RNPylonChat_targetSdkVersion=34
4
+ RNPylonChat_minSdkVersion=24
5
+
@@ -0,0 +1,4 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <uses-permission android:name="android.permission.INTERNET" />
3
+ </manifest>
4
+