@rematter/pylon-react-native 0.1.4 → 0.1.6
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.
|
@@ -18,96 +18,107 @@ import com.pylon.chatwidget.PylonUser
|
|
|
18
18
|
* This is kept as minimal as possible to avoid interfering with touch pass-through.
|
|
19
19
|
*/
|
|
20
20
|
class RNPylonChatView(context: Context) : FrameLayout(context) {
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
private var pylonChatView: PylonChatView? = null
|
|
23
23
|
private var config: PylonConfig? = null
|
|
24
24
|
private var user: PylonUser? = null
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
// Config properties
|
|
27
27
|
var appId: String? = null
|
|
28
28
|
set(value) {
|
|
29
29
|
field = value
|
|
30
30
|
updateConfig()
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
var widgetBaseUrl: String? = null
|
|
34
34
|
set(value) {
|
|
35
35
|
field = value
|
|
36
36
|
updateConfig()
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
var widgetScriptUrl: String? = null
|
|
40
40
|
set(value) {
|
|
41
41
|
field = value
|
|
42
42
|
updateConfig()
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
var enableLogging: Boolean = true
|
|
46
46
|
set(value) {
|
|
47
47
|
field = value
|
|
48
48
|
updateConfig()
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
var debugMode: Boolean = false
|
|
52
52
|
set(value) {
|
|
53
53
|
field = value
|
|
54
54
|
updateConfig()
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
var primaryColor: String? = null
|
|
58
58
|
set(value) {
|
|
59
59
|
field = value
|
|
60
60
|
updateConfig()
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
// User properties
|
|
64
64
|
var userEmail: String? = null
|
|
65
65
|
set(value) {
|
|
66
66
|
field = value
|
|
67
67
|
updateUser()
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
var userName: String? = null
|
|
71
71
|
set(value) {
|
|
72
72
|
field = value
|
|
73
73
|
updateUser()
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
var userAvatarUrl: String? = null
|
|
77
77
|
set(value) {
|
|
78
78
|
field = value
|
|
79
79
|
updateUser()
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
var userEmailHash: String? = null
|
|
83
83
|
set(value) {
|
|
84
84
|
field = value
|
|
85
85
|
updateUser()
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
var userAccountId: String? = null
|
|
89
89
|
set(value) {
|
|
90
90
|
field = value
|
|
91
91
|
updateUser()
|
|
92
92
|
}
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
var userAccountExternalId: String? = null
|
|
95
95
|
set(value) {
|
|
96
96
|
field = value
|
|
97
97
|
updateUser()
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
// Safe area top inset for coordinate space adjustment
|
|
101
|
+
var topInset: Float = 0f
|
|
102
|
+
set(value) {
|
|
103
|
+
field = value
|
|
104
|
+
// Apply to pylonChatView if it exists
|
|
105
|
+
pylonChatView?.let {
|
|
106
|
+
// Note: Android PylonChat doesn't have topInset support yet,
|
|
107
|
+
// but we store it here for future use or coordinate adjustments
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
init {
|
|
101
112
|
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
102
113
|
}
|
|
103
|
-
|
|
114
|
+
|
|
104
115
|
// Track pointer events setting
|
|
105
116
|
private var pointerEventsMode = "auto"
|
|
106
|
-
|
|
117
|
+
|
|
107
118
|
fun setPointerEventsMode(mode: String) {
|
|
108
119
|
pointerEventsMode = mode
|
|
109
120
|
}
|
|
110
|
-
|
|
121
|
+
|
|
111
122
|
/**
|
|
112
123
|
* This is the CRITICAL method for pointerEvents.
|
|
113
124
|
* By returning false here when pointerEvents="none", we tell React Native's
|
|
@@ -139,7 +150,7 @@ class RNPylonChatView(context: Context) : FrameLayout(context) {
|
|
|
139
150
|
|
|
140
151
|
private fun updateConfig() {
|
|
141
152
|
val id = appId ?: return
|
|
142
|
-
|
|
153
|
+
|
|
143
154
|
config = PylonConfig.build(id) {
|
|
144
155
|
this.enableLogging = this@RNPylonChatView.enableLogging
|
|
145
156
|
this.primaryColor = this@RNPylonChatView.primaryColor
|
|
@@ -147,14 +158,14 @@ class RNPylonChatView(context: Context) : FrameLayout(context) {
|
|
|
147
158
|
this@RNPylonChatView.widgetBaseUrl?.let { this.widgetBaseUrl = it }
|
|
148
159
|
this@RNPylonChatView.widgetScriptUrl?.let { this.widgetScriptUrl = it }
|
|
149
160
|
}
|
|
150
|
-
|
|
161
|
+
|
|
151
162
|
recreatePylonView()
|
|
152
163
|
}
|
|
153
|
-
|
|
164
|
+
|
|
154
165
|
private fun updateUser() {
|
|
155
166
|
val email = userEmail ?: return
|
|
156
167
|
val name = userName ?: return
|
|
157
|
-
|
|
168
|
+
|
|
158
169
|
user = PylonUser(
|
|
159
170
|
email = email,
|
|
160
171
|
name = name,
|
|
@@ -163,48 +174,48 @@ class RNPylonChatView(context: Context) : FrameLayout(context) {
|
|
|
163
174
|
accountId = userAccountId,
|
|
164
175
|
accountExternalId = userAccountExternalId
|
|
165
176
|
)
|
|
166
|
-
|
|
177
|
+
|
|
167
178
|
recreatePylonView()
|
|
168
179
|
}
|
|
169
|
-
|
|
180
|
+
|
|
170
181
|
private fun recreatePylonView() {
|
|
171
182
|
val cfg = config ?: return
|
|
172
183
|
val usr = user ?: return
|
|
173
|
-
|
|
184
|
+
|
|
174
185
|
// Remove old view
|
|
175
186
|
pylonChatView?.let { removeView(it) }
|
|
176
|
-
|
|
187
|
+
|
|
177
188
|
// Create new PylonChatView
|
|
178
189
|
val newView = PylonChatView(context, cfg, usr)
|
|
179
190
|
newView.setListener(object : PylonChatListener {
|
|
180
191
|
override fun onPylonLoaded() {
|
|
181
192
|
sendEvent("onPylonLoaded", Arguments.createMap())
|
|
182
193
|
}
|
|
183
|
-
|
|
194
|
+
|
|
184
195
|
override fun onPylonInitialized() {
|
|
185
196
|
sendEvent("onPylonInitialized", Arguments.createMap())
|
|
186
197
|
}
|
|
187
|
-
|
|
198
|
+
|
|
188
199
|
override fun onPylonReady() {
|
|
189
200
|
sendEvent("onPylonReady", Arguments.createMap())
|
|
190
201
|
}
|
|
191
|
-
|
|
202
|
+
|
|
192
203
|
override fun onMessageReceived(message: String) {
|
|
193
204
|
val params = Arguments.createMap()
|
|
194
205
|
params.putString("message", message)
|
|
195
206
|
sendEvent("onMessageReceived", params)
|
|
196
207
|
}
|
|
197
|
-
|
|
208
|
+
|
|
198
209
|
override fun onChatOpened() {
|
|
199
210
|
sendEvent("onChatOpened", Arguments.createMap())
|
|
200
211
|
}
|
|
201
|
-
|
|
212
|
+
|
|
202
213
|
override fun onChatClosed() {
|
|
203
214
|
val params = Arguments.createMap()
|
|
204
215
|
params.putBoolean("wasOpen", true)
|
|
205
216
|
sendEvent("onChatClosed", params)
|
|
206
217
|
}
|
|
207
|
-
|
|
218
|
+
|
|
208
219
|
override fun onInteractiveBoundsChanged(selector: String, left: Float, top: Float, right: Float, bottom: Float) {
|
|
209
220
|
// Convert pixels to density-independent pixels (dp) for React Native.
|
|
210
221
|
val density = resources.displayMetrics.density
|
|
@@ -216,57 +227,57 @@ class RNPylonChatView(context: Context) : FrameLayout(context) {
|
|
|
216
227
|
params.putDouble("bottom", (bottom / density).toDouble())
|
|
217
228
|
sendEvent("onInteractiveBoundsChanged", params)
|
|
218
229
|
}
|
|
219
|
-
|
|
230
|
+
|
|
220
231
|
override fun onPylonError(error: String) {
|
|
221
232
|
val params = Arguments.createMap()
|
|
222
233
|
params.putString("error", error)
|
|
223
234
|
sendEvent("onPylonError", params)
|
|
224
235
|
}
|
|
225
|
-
|
|
236
|
+
|
|
226
237
|
override fun onUnreadCountChanged(count: Int) {
|
|
227
238
|
val params = Arguments.createMap()
|
|
228
239
|
params.putInt("count", count)
|
|
229
240
|
sendEvent("onUnreadCountChanged", params)
|
|
230
241
|
}
|
|
231
|
-
|
|
242
|
+
|
|
232
243
|
override fun onFileChooserLaunched(requestCode: Int) {
|
|
233
244
|
// Handle file chooser if needed
|
|
234
245
|
}
|
|
235
246
|
})
|
|
236
|
-
|
|
247
|
+
|
|
237
248
|
newView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
238
249
|
addView(newView)
|
|
239
250
|
pylonChatView = newView
|
|
240
251
|
}
|
|
241
|
-
|
|
252
|
+
|
|
242
253
|
private fun sendEvent(eventName: String, params: WritableMap) {
|
|
243
254
|
val reactContext = context as ReactContext
|
|
244
255
|
reactContext
|
|
245
256
|
.getJSModule(RCTEventEmitter::class.java)
|
|
246
257
|
.receiveEvent(id, eventName, params)
|
|
247
258
|
}
|
|
248
|
-
|
|
259
|
+
|
|
249
260
|
// Imperative methods
|
|
250
261
|
fun openChat() {
|
|
251
262
|
pylonChatView?.openChat()
|
|
252
263
|
}
|
|
253
|
-
|
|
264
|
+
|
|
254
265
|
fun closeChat() {
|
|
255
266
|
pylonChatView?.closeChat()
|
|
256
267
|
}
|
|
257
|
-
|
|
268
|
+
|
|
258
269
|
fun showChatBubble() {
|
|
259
270
|
pylonChatView?.showChatBubble()
|
|
260
271
|
}
|
|
261
|
-
|
|
272
|
+
|
|
262
273
|
fun hideChatBubble() {
|
|
263
274
|
pylonChatView?.hideChatBubble()
|
|
264
275
|
}
|
|
265
|
-
|
|
276
|
+
|
|
266
277
|
fun showNewMessage(message: String, isHtml: Boolean) {
|
|
267
278
|
pylonChatView?.showNewMessage(message, isHtml)
|
|
268
279
|
}
|
|
269
|
-
|
|
280
|
+
|
|
270
281
|
fun setNewIssueCustomFields(fields: Map<String, Any?>) {
|
|
271
282
|
@Suppress("UNCHECKED_CAST")
|
|
272
283
|
pylonChatView?.setNewIssueCustomFields(fields as Map<String, Any>)
|
|
@@ -276,19 +287,19 @@ class RNPylonChatView(context: Context) : FrameLayout(context) {
|
|
|
276
287
|
@Suppress("UNCHECKED_CAST")
|
|
277
288
|
pylonChatView?.setTicketFormFields(fields as Map<String, Any>)
|
|
278
289
|
}
|
|
279
|
-
|
|
290
|
+
|
|
280
291
|
fun updateEmailHash(emailHash: String?) {
|
|
281
292
|
pylonChatView?.setEmailHash(emailHash)
|
|
282
293
|
}
|
|
283
|
-
|
|
294
|
+
|
|
284
295
|
fun showTicketForm(slug: String) {
|
|
285
296
|
pylonChatView?.showTicketForm(slug)
|
|
286
297
|
}
|
|
287
|
-
|
|
298
|
+
|
|
288
299
|
fun showKnowledgeBaseArticle(articleId: String) {
|
|
289
300
|
pylonChatView?.showKnowledgeBaseArticle(articleId)
|
|
290
301
|
}
|
|
291
|
-
|
|
302
|
+
|
|
292
303
|
fun clickElementAtSelector(selector: String) {
|
|
293
304
|
// Trigger a click on the element with the given ID selector.
|
|
294
305
|
// This is used for Android's proxy-based touch pass-through system.
|
|
@@ -15,7 +15,7 @@ import com.pylon.chatwidget.PylonConfig
|
|
|
15
15
|
import com.pylon.chatwidget.PylonUser
|
|
16
16
|
|
|
17
17
|
class RNPylonChatViewManager : SimpleViewManager<RNPylonChatView>() {
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
companion object {
|
|
20
20
|
const val REACT_CLASS = "RNPylonChatView"
|
|
21
21
|
const val COMMAND_OPEN_CHAT = 1
|
|
@@ -67,7 +67,7 @@ class RNPylonChatViewManager : SimpleViewManager<RNPylonChatView>() {
|
|
|
67
67
|
fun setPointerEvents(view: RNPylonChatView, pointerEvents: String?) {
|
|
68
68
|
val mode = pointerEvents ?: "auto"
|
|
69
69
|
view.setPointerEventsMode(mode)
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
when (mode) {
|
|
72
72
|
"none" -> {
|
|
73
73
|
// Don't handle any touches - let them pass through
|
|
@@ -128,6 +128,12 @@ class RNPylonChatViewManager : SimpleViewManager<RNPylonChatView>() {
|
|
|
128
128
|
view.userAccountExternalId = id
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// Coordinate space adjustment
|
|
132
|
+
@ReactProp(name = "topInset", defaultFloat = 0f)
|
|
133
|
+
fun setTopInset(view: RNPylonChatView, inset: Float) {
|
|
134
|
+
view.topInset = inset
|
|
135
|
+
}
|
|
136
|
+
|
|
131
137
|
// Event names
|
|
132
138
|
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
|
|
133
139
|
return MapBuilder.builder<String, Any>()
|
|
@@ -14,61 +14,61 @@ import WebKit
|
|
|
14
14
|
// Note: PylonChat files will be added to Xcode project from ../../ios/PylonChat/
|
|
15
15
|
|
|
16
16
|
class RNPylonChatView: UIView {
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
private var pylonChatView: PylonChatView?
|
|
19
19
|
private var config: PylonConfig?
|
|
20
20
|
private var user: PylonUser?
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
// Config properties
|
|
23
23
|
@objc var appId: NSString = "" {
|
|
24
24
|
didSet { updateConfig() }
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
@objc var widgetBaseUrl: NSString? {
|
|
28
28
|
didSet { updateConfig() }
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
@objc var widgetScriptUrl: NSString? {
|
|
32
32
|
didSet { updateConfig() }
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
@objc var enableLogging: Bool = true {
|
|
36
36
|
didSet { updateConfig() }
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
@objc var debugMode: Bool = false {
|
|
40
40
|
didSet { updateConfig() }
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
@objc var primaryColor: NSString? {
|
|
44
44
|
didSet { updateConfig() }
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
// User properties
|
|
48
48
|
@objc var userEmail: NSString? {
|
|
49
49
|
didSet { updateUser() }
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
@objc var userName: NSString? {
|
|
53
53
|
didSet { updateUser() }
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
@objc var userAvatarUrl: NSString? {
|
|
57
57
|
didSet { updateUser() }
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
@objc var userEmailHash: NSString? {
|
|
61
61
|
didSet { updateUser() }
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
@objc var userAccountId: NSString? {
|
|
65
65
|
didSet { updateUser() }
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
@objc var userAccountExternalId: NSString? {
|
|
69
69
|
didSet { updateUser() }
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
// Safe area top inset for coordinate space adjustment
|
|
73
73
|
@objc var topInset: NSNumber = 0 {
|
|
74
74
|
didSet {
|
|
@@ -77,7 +77,7 @@ class RNPylonChatView: UIView {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
// Event callbacks - renamed to avoid collision with PylonChatListener methods
|
|
82
82
|
@objc var rctOnPylonLoaded: RCTBubblingEventBlock?
|
|
83
83
|
@objc var rctOnPylonInitialized: RCTBubblingEventBlock?
|
|
@@ -87,28 +87,28 @@ class RNPylonChatView: UIView {
|
|
|
87
87
|
@objc var rctOnUnreadCountChanged: RCTBubblingEventBlock?
|
|
88
88
|
@objc var rctOnMessageReceived: RCTBubblingEventBlock?
|
|
89
89
|
@objc var rctOnPylonError: RCTBubblingEventBlock?
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
override init(frame: CGRect) {
|
|
92
92
|
super.init(frame: frame)
|
|
93
93
|
setupView()
|
|
94
94
|
}
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
required init?(coder: NSCoder) {
|
|
97
97
|
super.init(coder: coder)
|
|
98
98
|
setupView()
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
private func setupView() {
|
|
102
102
|
backgroundColor = .clear
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
// Override pointInside to make React Native call hitTest
|
|
106
106
|
public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
|
107
107
|
// Always return true so React Native will call hitTest
|
|
108
108
|
// The actual hit detection happens in hitTest
|
|
109
109
|
return true
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
// Forward hit testing to the embedded PylonChatView
|
|
113
113
|
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
114
114
|
// If we have a PylonChatView, let it handle hit testing
|
|
@@ -117,14 +117,14 @@ class RNPylonChatView: UIView {
|
|
|
117
117
|
let convertedPoint = convert(point, to: pylonView)
|
|
118
118
|
return pylonView.hitTest(convertedPoint, with: event)
|
|
119
119
|
}
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
// If no PylonChatView yet, pass through (return nil)
|
|
122
122
|
return nil
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
private func updateConfig() {
|
|
126
126
|
guard (appId as String).isEmpty == false else { return }
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
config = PylonConfig(
|
|
129
129
|
appId: appId as String,
|
|
130
130
|
enableLogging: enableLogging,
|
|
@@ -133,14 +133,14 @@ class RNPylonChatView: UIView {
|
|
|
133
133
|
widgetBaseUrl: widgetBaseUrl as String?,
|
|
134
134
|
widgetScriptUrl: widgetScriptUrl as String?
|
|
135
135
|
)
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
recreatePylonView()
|
|
138
138
|
}
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
private func updateUser() {
|
|
141
141
|
guard let email = userEmail as String?,
|
|
142
142
|
let name = userName as String? else { return }
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
user = PylonUser(
|
|
145
145
|
email: email,
|
|
146
146
|
name: name,
|
|
@@ -149,75 +149,75 @@ class RNPylonChatView: UIView {
|
|
|
149
149
|
accountId: userAccountId as String?,
|
|
150
150
|
accountExternalId: userAccountExternalId as String?
|
|
151
151
|
)
|
|
152
|
-
|
|
152
|
+
|
|
153
153
|
recreatePylonView()
|
|
154
154
|
}
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
private func recreatePylonView() {
|
|
157
157
|
guard let config = config, let user = user else { return }
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
// Remove old view
|
|
160
160
|
pylonChatView?.removeFromSuperview()
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
// Create new PylonChatView
|
|
163
163
|
let newView = PylonChatView(config: config, user: user)
|
|
164
164
|
newView.listener = self
|
|
165
165
|
newView.topInset = CGFloat(truncating: topInset)
|
|
166
166
|
newView.translatesAutoresizingMaskIntoConstraints = false
|
|
167
|
-
|
|
167
|
+
|
|
168
168
|
addSubview(newView)
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
NSLayoutConstraint.activate([
|
|
171
171
|
newView.topAnchor.constraint(equalTo: topAnchor),
|
|
172
172
|
newView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
173
173
|
newView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
174
174
|
newView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
|
175
175
|
])
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
pylonChatView = newView
|
|
178
|
-
|
|
178
|
+
|
|
179
179
|
// Force layout
|
|
180
180
|
setNeedsLayout()
|
|
181
181
|
layoutIfNeeded()
|
|
182
182
|
}
|
|
183
|
-
|
|
183
|
+
|
|
184
184
|
// Imperative methods (called from React Native)
|
|
185
185
|
func openChat() {
|
|
186
186
|
pylonChatView?.openChat()
|
|
187
187
|
}
|
|
188
|
-
|
|
188
|
+
|
|
189
189
|
func closeChat() {
|
|
190
190
|
pylonChatView?.closeChat()
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
func showChatBubble() {
|
|
194
194
|
pylonChatView?.showChatBubble()
|
|
195
195
|
}
|
|
196
|
-
|
|
196
|
+
|
|
197
197
|
func hideChatBubble() {
|
|
198
198
|
pylonChatView?.hideChatBubble()
|
|
199
199
|
}
|
|
200
|
-
|
|
200
|
+
|
|
201
201
|
func showNewMessage(_ message: String, isHtml: Bool) {
|
|
202
202
|
pylonChatView?.showNewMessage(message, isHtml: isHtml)
|
|
203
203
|
}
|
|
204
|
-
|
|
204
|
+
|
|
205
205
|
func setNewIssueCustomFields(_ fields: [String: Any]) {
|
|
206
206
|
pylonChatView?.setNewIssueCustomFields(fields)
|
|
207
207
|
}
|
|
208
|
-
|
|
208
|
+
|
|
209
209
|
func setTicketFormFields(_ fields: [String: Any]) {
|
|
210
210
|
pylonChatView?.setTicketFormFields(fields)
|
|
211
211
|
}
|
|
212
|
-
|
|
212
|
+
|
|
213
213
|
func updateEmailHash(_ emailHash: String?) {
|
|
214
214
|
pylonChatView?.updateEmailHash(emailHash)
|
|
215
215
|
}
|
|
216
|
-
|
|
216
|
+
|
|
217
217
|
func showTicketForm(_ slug: String) {
|
|
218
218
|
pylonChatView?.showTicketForm(slug)
|
|
219
219
|
}
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
func showKnowledgeBaseArticle(_ articleId: String) {
|
|
222
222
|
pylonChatView?.showKnowledgeBaseArticle(articleId)
|
|
223
223
|
}
|
|
@@ -228,31 +228,31 @@ extension RNPylonChatView: PylonChatListener {
|
|
|
228
228
|
func onPylonLoaded() {
|
|
229
229
|
rctOnPylonLoaded?([:])
|
|
230
230
|
}
|
|
231
|
-
|
|
231
|
+
|
|
232
232
|
func onPylonInitialized() {
|
|
233
233
|
rctOnPylonInitialized?([:])
|
|
234
234
|
}
|
|
235
|
-
|
|
235
|
+
|
|
236
236
|
func onPylonReady() {
|
|
237
237
|
rctOnPylonReady?([:])
|
|
238
238
|
}
|
|
239
|
-
|
|
239
|
+
|
|
240
240
|
func onMessageReceived(message: String) {
|
|
241
241
|
rctOnMessageReceived?(["message": message])
|
|
242
242
|
}
|
|
243
|
-
|
|
243
|
+
|
|
244
244
|
func onChatOpened() {
|
|
245
245
|
rctOnChatOpened?([:])
|
|
246
246
|
}
|
|
247
|
-
|
|
247
|
+
|
|
248
248
|
func onChatClosed(wasOpen: Bool) {
|
|
249
249
|
rctOnChatClosed?(["wasOpen": wasOpen])
|
|
250
250
|
}
|
|
251
|
-
|
|
251
|
+
|
|
252
252
|
func onPylonError(error: String) {
|
|
253
253
|
rctOnPylonError?(["error": error])
|
|
254
254
|
}
|
|
255
|
-
|
|
255
|
+
|
|
256
256
|
func onUnreadCountChanged(count: Int) {
|
|
257
257
|
rctOnUnreadCountChanged?(["count": count])
|
|
258
258
|
}
|
|
@@ -261,71 +261,90 @@ extension RNPylonChatView: PylonChatListener {
|
|
|
261
261
|
// MARK: - Imperative method helpers
|
|
262
262
|
extension RNPylonChatViewManager {
|
|
263
263
|
@objc func openChat(_ reactTag: NSNumber) {
|
|
264
|
-
bridge.uiManager.addUIBlock {
|
|
265
|
-
|
|
264
|
+
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
|
|
265
|
+
// Use uiManager.view(forReactTag:) instead of viewRegistry
|
|
266
|
+
guard let uiManager = uiManager else {
|
|
267
|
+
print("⚠️ RNPylonChat: uiManager is nil")
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
guard let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else {
|
|
272
|
+
print("⚠️ RNPylonChat: Could not find view for reactTag: \(reactTag)")
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
266
276
|
view.openChat()
|
|
267
277
|
}
|
|
268
278
|
}
|
|
269
|
-
|
|
279
|
+
|
|
270
280
|
@objc func closeChat(_ reactTag: NSNumber) {
|
|
271
|
-
bridge.uiManager.addUIBlock {
|
|
272
|
-
guard let
|
|
281
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
282
|
+
guard let uiManager = uiManager,
|
|
283
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
273
284
|
view.closeChat()
|
|
274
285
|
}
|
|
275
286
|
}
|
|
276
|
-
|
|
287
|
+
|
|
277
288
|
@objc func showChatBubble(_ reactTag: NSNumber) {
|
|
278
|
-
bridge.uiManager.addUIBlock {
|
|
279
|
-
guard let
|
|
289
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
290
|
+
guard let uiManager = uiManager,
|
|
291
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
280
292
|
view.showChatBubble()
|
|
281
293
|
}
|
|
282
294
|
}
|
|
283
|
-
|
|
295
|
+
|
|
284
296
|
@objc func hideChatBubble(_ reactTag: NSNumber) {
|
|
285
|
-
bridge.uiManager.addUIBlock {
|
|
286
|
-
guard let
|
|
297
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
298
|
+
guard let uiManager = uiManager,
|
|
299
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
287
300
|
view.hideChatBubble()
|
|
288
301
|
}
|
|
289
302
|
}
|
|
290
|
-
|
|
303
|
+
|
|
291
304
|
@objc func showNewMessage(_ reactTag: NSNumber, message: NSString, isHtml: Bool) {
|
|
292
|
-
bridge.uiManager.addUIBlock {
|
|
293
|
-
guard let
|
|
305
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
306
|
+
guard let uiManager = uiManager,
|
|
307
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
294
308
|
view.showNewMessage(message as String, isHtml: isHtml)
|
|
295
309
|
}
|
|
296
310
|
}
|
|
297
|
-
|
|
311
|
+
|
|
298
312
|
@objc func setNewIssueCustomFields(_ reactTag: NSNumber, fields: NSDictionary) {
|
|
299
|
-
bridge.uiManager.addUIBlock {
|
|
300
|
-
guard let
|
|
313
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
314
|
+
guard let uiManager = uiManager,
|
|
315
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
301
316
|
view.setNewIssueCustomFields(fields as! [String: Any])
|
|
302
317
|
}
|
|
303
318
|
}
|
|
304
|
-
|
|
319
|
+
|
|
305
320
|
@objc func setTicketFormFields(_ reactTag: NSNumber, fields: NSDictionary) {
|
|
306
|
-
bridge.uiManager.addUIBlock {
|
|
307
|
-
guard let
|
|
321
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
322
|
+
guard let uiManager = uiManager,
|
|
323
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
308
324
|
view.setTicketFormFields(fields as! [String: Any])
|
|
309
325
|
}
|
|
310
326
|
}
|
|
311
|
-
|
|
327
|
+
|
|
312
328
|
@objc func updateEmailHash(_ reactTag: NSNumber, emailHash: NSString?) {
|
|
313
|
-
bridge.uiManager.addUIBlock {
|
|
314
|
-
guard let
|
|
329
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
330
|
+
guard let uiManager = uiManager,
|
|
331
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
315
332
|
view.updateEmailHash(emailHash as String?)
|
|
316
333
|
}
|
|
317
334
|
}
|
|
318
|
-
|
|
335
|
+
|
|
319
336
|
@objc func showTicketForm(_ reactTag: NSNumber, slug: NSString) {
|
|
320
|
-
bridge.uiManager.addUIBlock {
|
|
321
|
-
guard let
|
|
337
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
338
|
+
guard let uiManager = uiManager,
|
|
339
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
322
340
|
view.showTicketForm(slug as String)
|
|
323
341
|
}
|
|
324
342
|
}
|
|
325
|
-
|
|
343
|
+
|
|
326
344
|
@objc func showKnowledgeBaseArticle(_ reactTag: NSNumber, articleId: NSString) {
|
|
327
|
-
bridge.uiManager.addUIBlock {
|
|
328
|
-
guard let
|
|
345
|
+
self.bridge.uiManager.addUIBlock { uiManager, _ in
|
|
346
|
+
guard let uiManager = uiManager,
|
|
347
|
+
let view = uiManager.view(forReactTag: reactTag) as? RNPylonChatView else { return }
|
|
329
348
|
view.showKnowledgeBaseArticle(articleId as String)
|
|
330
349
|
}
|
|
331
350
|
}
|