@rematter/pylon-react-native 0.1.4 → 0.1.5
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/ios/RNPylonChatView.swift +99 -80
- package/package.json +1 -1
|
@@ -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
|
}
|