@octopus-community/react-native 1.0.8 → 1.9.1
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/OctopusReactNativeSdk.podspec +1 -1
- package/README.md +40 -35
- package/android/build.gradle +2 -0
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +2 -1
- package/android/src/main/AndroidManifestNew.xml +2 -1
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusActivity.kt +56 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusContent.kt +396 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusEventEmitter.kt +22 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusEventSerializer.kt +339 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactModule.kt +326 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/{OctopusReactNativeSdkPackage.kt → OctopusReactPackage.kt} +3 -3
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +22 -9
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSSOAuthenticator.kt +5 -15
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIController.kt +17 -2
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIViewManager.kt +63 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/ProfileFieldMapper.kt +2 -2
- package/ios/OctopusEventManager.swift +27 -0
- package/ios/OctopusEventSerializer.swift +271 -0
- package/ios/OctopusReactNativeSdk.mm +26 -1
- package/ios/OctopusReactNativeSdk.swift +216 -2
- package/ios/OctopusSSOAuthenticator.swift +1 -5
- package/ios/OctopusUIManager.swift +83 -0
- package/ios/OctopusUIViewManager.m +7 -0
- package/ios/OctopusUIViewManager.swift +37 -0
- package/lib/module/OctopusUIView.js +39 -0
- package/lib/module/OctopusUIView.js.map +1 -0
- package/lib/module/addHasAccessToCommunityListener.js +33 -0
- package/lib/module/addHasAccessToCommunityListener.js.map +1 -0
- package/lib/module/addNavigateToUrlListener.js +41 -0
- package/lib/module/addNavigateToUrlListener.js.map +1 -0
- package/lib/module/addNotSeenNotificationsCountListener.js +30 -0
- package/lib/module/addNotSeenNotificationsCountListener.js.map +1 -0
- package/lib/module/addSDKEventListener.js +48 -0
- package/lib/module/addSDKEventListener.js.map +1 -0
- package/lib/module/connectUser.js +24 -3
- package/lib/module/connectUser.js.map +1 -1
- package/lib/module/index.js +12 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/initialize.js +9 -12
- package/lib/module/initialize.js.map +1 -1
- package/lib/module/openUI.js +23 -2
- package/lib/module/openUI.js.map +1 -1
- package/lib/module/overrideCommunityAccess.js +36 -0
- package/lib/module/overrideCommunityAccess.js.map +1 -0
- package/lib/module/overrideDefaultLocale.js +75 -0
- package/lib/module/overrideDefaultLocale.js.map +1 -0
- package/lib/module/trackCommunityAccess.js +33 -0
- package/lib/module/trackCommunityAccess.js.map +1 -0
- package/lib/module/trackCustomEvent.js +36 -0
- package/lib/module/trackCustomEvent.js.map +1 -0
- package/lib/module/types/sdkEvents.js +2 -0
- package/lib/module/types/sdkEvents.js.map +1 -0
- package/lib/module/types/urlOpeningStrategy.js +23 -0
- package/lib/module/types/urlOpeningStrategy.js.map +1 -0
- package/lib/module/updateNotSeenNotificationsCount.js +33 -0
- package/lib/module/updateNotSeenNotificationsCount.js.map +1 -0
- package/lib/typescript/src/OctopusUIView.d.ts +32 -0
- package/lib/typescript/src/OctopusUIView.d.ts.map +1 -0
- package/lib/typescript/src/addHasAccessToCommunityListener.d.ts +27 -0
- package/lib/typescript/src/addHasAccessToCommunityListener.d.ts.map +1 -0
- package/lib/typescript/src/addNavigateToUrlListener.d.ts +31 -0
- package/lib/typescript/src/addNavigateToUrlListener.d.ts.map +1 -0
- package/lib/typescript/src/addNotSeenNotificationsCountListener.d.ts +24 -0
- package/lib/typescript/src/addNotSeenNotificationsCountListener.d.ts.map +1 -0
- package/lib/typescript/src/addSDKEventListener.d.ts +43 -0
- package/lib/typescript/src/addSDKEventListener.d.ts.map +1 -0
- package/lib/typescript/src/connectUser.d.ts +24 -8
- package/lib/typescript/src/connectUser.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +13 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/initialize.d.ts +9 -12
- package/lib/typescript/src/initialize.d.ts.map +1 -1
- package/lib/typescript/src/openUI.d.ts +28 -1
- package/lib/typescript/src/openUI.d.ts.map +1 -1
- package/lib/typescript/src/overrideCommunityAccess.d.ts +30 -0
- package/lib/typescript/src/overrideCommunityAccess.d.ts.map +1 -0
- package/lib/typescript/src/overrideDefaultLocale.d.ts +37 -0
- package/lib/typescript/src/overrideDefaultLocale.d.ts.map +1 -0
- package/lib/typescript/src/trackCommunityAccess.d.ts +27 -0
- package/lib/typescript/src/trackCommunityAccess.d.ts.map +1 -0
- package/lib/typescript/src/trackCustomEvent.d.ts +30 -0
- package/lib/typescript/src/trackCustomEvent.d.ts.map +1 -0
- package/lib/typescript/src/types/sdkEvents.d.ts +222 -0
- package/lib/typescript/src/types/sdkEvents.d.ts.map +1 -0
- package/lib/typescript/src/types/urlOpeningStrategy.d.ts +20 -0
- package/lib/typescript/src/types/urlOpeningStrategy.d.ts.map +1 -0
- package/lib/typescript/src/updateNotSeenNotificationsCount.d.ts +27 -0
- package/lib/typescript/src/updateNotSeenNotificationsCount.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/OctopusUIView.tsx +57 -0
- package/src/addHasAccessToCommunityListener.ts +38 -0
- package/src/addNavigateToUrlListener.ts +54 -0
- package/src/addNotSeenNotificationsCountListener.ts +35 -0
- package/src/addSDKEventListener.ts +49 -0
- package/src/connectUser.ts +24 -8
- package/src/index.ts +13 -0
- package/src/initialize.ts +9 -12
- package/src/openUI.ts +32 -2
- package/src/overrideCommunityAccess.ts +33 -0
- package/src/overrideDefaultLocale.ts +88 -0
- package/src/trackCommunityAccess.ts +30 -0
- package/src/trackCustomEvent.ts +36 -0
- package/src/types/sdkEvents.ts +315 -0
- package/src/types/urlOpeningStrategy.ts +20 -0
- package/src/updateNotSeenNotificationsCount.ts +30 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactNativeSdkModule.kt +0 -155
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +0 -434
|
@@ -3,6 +3,7 @@ import OctopusUI
|
|
|
3
3
|
import SwiftUI
|
|
4
4
|
import UIKit
|
|
5
5
|
import React
|
|
6
|
+
import Combine
|
|
6
7
|
|
|
7
8
|
@objc(OctopusReactNativeSdk)
|
|
8
9
|
class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
@@ -18,6 +19,7 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
|
18
19
|
private var logoSource: [String: Any]?
|
|
19
20
|
private var fontConfiguration: [String: Any]?
|
|
20
21
|
private var uiConfiguration: OctopusUIConfiguration?
|
|
22
|
+
private var cancellables = Set<AnyCancellable>()
|
|
21
23
|
|
|
22
24
|
// MARK: - RCTBridgeModule
|
|
23
25
|
|
|
@@ -42,6 +44,10 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
|
42
44
|
self.logoSource = sdkInitializer.getLogoSource(from: options)
|
|
43
45
|
self.fontConfiguration = sdkInitializer.getFontConfiguration(from: options)
|
|
44
46
|
self.uiConfiguration = sdkInitializer.parseUIConfiguration(from: options)
|
|
47
|
+
|
|
48
|
+
// Start observing reactive events after SDK initialization
|
|
49
|
+
startObservingReactiveEvents()
|
|
50
|
+
|
|
45
51
|
resolve(nil)
|
|
46
52
|
} catch {
|
|
47
53
|
reject("INITIALIZE_ERROR", "Failed to initialize Octopus SDK: \(error.localizedDescription)", error)
|
|
@@ -108,15 +114,25 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
|
108
114
|
|
|
109
115
|
// MARK: - UI management
|
|
110
116
|
|
|
111
|
-
@objc(openUI:withRejecter:)
|
|
112
|
-
func openUI(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
117
|
+
@objc(openUI:withResolver:withRejecter:)
|
|
118
|
+
func openUI(options: NSDictionary?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
113
119
|
guard let octopus = octopusSDK else {
|
|
114
120
|
reject("OPEN_UI_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
115
121
|
return
|
|
116
122
|
}
|
|
117
123
|
|
|
124
|
+
let interceptUrls = (options as? [String: Any])?["interceptUrls"] as? Bool ?? false
|
|
125
|
+
|
|
118
126
|
DispatchQueue.main.async {
|
|
119
127
|
do {
|
|
128
|
+
if interceptUrls {
|
|
129
|
+
octopus.set(onNavigateToURLCallback: { [weak self] url in
|
|
130
|
+
self?.eventManager.emitNavigateToUrl(url: url.absoluteString)
|
|
131
|
+
return .handledByApp
|
|
132
|
+
})
|
|
133
|
+
} else {
|
|
134
|
+
octopus.set(onNavigateToURLCallback: nil)
|
|
135
|
+
}
|
|
120
136
|
try self.uiManager.openUI(
|
|
121
137
|
octopus: octopus,
|
|
122
138
|
theme: self.theme,
|
|
@@ -131,6 +147,15 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
|
131
147
|
}
|
|
132
148
|
}
|
|
133
149
|
|
|
150
|
+
@objc(handleUrlStrategy:withStrategy:)
|
|
151
|
+
func handleUrlStrategy(url: String, strategy: String) -> Void {
|
|
152
|
+
guard strategy == "handledByOctopus" else { return }
|
|
153
|
+
guard let urlObj = URL(string: url) else { return }
|
|
154
|
+
DispatchQueue.main.async {
|
|
155
|
+
UIApplication.shared.open(urlObj)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
134
159
|
@objc(closeUI:withRejecter:)
|
|
135
160
|
func closeUI(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
136
161
|
DispatchQueue.main.async {
|
|
@@ -143,6 +168,28 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
|
143
168
|
}
|
|
144
169
|
}
|
|
145
170
|
|
|
171
|
+
/// Called by OctopusUIViewManager's container view to embed the native UI (non-fullscreen).
|
|
172
|
+
@objc func addEmbeddedView(containerView: UIView, interceptUrls: Bool) {
|
|
173
|
+
guard let octopus = octopusSDK else { return }
|
|
174
|
+
if interceptUrls {
|
|
175
|
+
octopus.set(onNavigateToURLCallback: { [weak self] url in
|
|
176
|
+
self?.eventManager.emitNavigateToUrl(url: url.absoluteString)
|
|
177
|
+
return .handledByApp
|
|
178
|
+
})
|
|
179
|
+
} else {
|
|
180
|
+
octopus.set(onNavigateToURLCallback: nil)
|
|
181
|
+
}
|
|
182
|
+
uiManager.addEmbeddedView(
|
|
183
|
+
to: containerView,
|
|
184
|
+
octopus: octopus,
|
|
185
|
+
theme: theme,
|
|
186
|
+
logoSource: logoSource,
|
|
187
|
+
fontConfiguration: fontConfiguration,
|
|
188
|
+
uiConfiguration: uiConfiguration,
|
|
189
|
+
interceptUrls: interceptUrls
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
146
193
|
@objc(updateColorScheme:withResolver:withRejecter:)
|
|
147
194
|
func updateColorScheme(colorScheme: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
148
195
|
// iOS uses adaptive colors that automatically respond to system appearance changes
|
|
@@ -160,6 +207,134 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
|
160
207
|
}
|
|
161
208
|
}
|
|
162
209
|
|
|
210
|
+
// MARK: - Notification management
|
|
211
|
+
|
|
212
|
+
@objc(updateNotSeenNotificationsCount:withRejecter:)
|
|
213
|
+
func updateNotSeenNotificationsCount(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
214
|
+
guard let octopus = octopusSDK else {
|
|
215
|
+
reject("UPDATE_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Task {
|
|
220
|
+
do {
|
|
221
|
+
try await octopus.updateNotSeenNotificationsCount()
|
|
222
|
+
resolve(nil)
|
|
223
|
+
} catch {
|
|
224
|
+
reject("UPDATE_ERROR", error.localizedDescription, error)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// MARK: - Analytics (custom events)
|
|
230
|
+
|
|
231
|
+
@objc(trackCustomEvent:withProperties:withResolver:withRejecter:)
|
|
232
|
+
func trackCustomEvent(
|
|
233
|
+
name: String,
|
|
234
|
+
properties: NSDictionary?,
|
|
235
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
236
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
237
|
+
) -> Void {
|
|
238
|
+
guard let octopus = octopusSDK else {
|
|
239
|
+
reject("TRACK_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
243
|
+
guard !trimmedName.isEmpty else {
|
|
244
|
+
reject("INVALID_ARGS", "name is required and must be non-empty", nil)
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
let props = dictionaryToStringMap(properties) ?? [:]
|
|
248
|
+
let customEvent = CustomEvent(
|
|
249
|
+
name: trimmedName,
|
|
250
|
+
properties: props.mapValues { CustomEvent.PropertyValue(value: $0) }
|
|
251
|
+
)
|
|
252
|
+
Task {
|
|
253
|
+
do {
|
|
254
|
+
try await octopus.track(customEvent: customEvent)
|
|
255
|
+
resolve(nil)
|
|
256
|
+
} catch {
|
|
257
|
+
reject("TRACK_ERROR", error.localizedDescription, error)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/// Converts NSDictionary to [String: String]. Only string values are included.
|
|
263
|
+
private func dictionaryToStringMap(_ dict: NSDictionary?) -> [String: String]? {
|
|
264
|
+
guard let dict = dict as? [String: Any] else { return nil }
|
|
265
|
+
var result: [String: String] = [:]
|
|
266
|
+
for (key, value) in dict {
|
|
267
|
+
if let str = value as? String {
|
|
268
|
+
result[key] = str
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return result
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// MARK: - Community access override
|
|
275
|
+
|
|
276
|
+
@objc(overrideCommunityAccess:withResolver:withRejecter:)
|
|
277
|
+
func overrideCommunityAccess(
|
|
278
|
+
hasAccess: Bool,
|
|
279
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
280
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
281
|
+
) -> Void {
|
|
282
|
+
guard let octopus = octopusSDK else {
|
|
283
|
+
reject("OVERRIDE_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
Task {
|
|
287
|
+
do {
|
|
288
|
+
try await octopus.overrideCommunityAccess(hasAccess)
|
|
289
|
+
resolve(nil)
|
|
290
|
+
} catch {
|
|
291
|
+
reject("OVERRIDE_ERROR", error.localizedDescription, error)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// MARK: - Track community access (analytics only)
|
|
297
|
+
|
|
298
|
+
@objc(trackCommunityAccess:withResolver:withRejecter:)
|
|
299
|
+
func trackCommunityAccess(
|
|
300
|
+
hasAccess: Bool,
|
|
301
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
302
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
303
|
+
) -> Void {
|
|
304
|
+
guard let octopus = octopusSDK else {
|
|
305
|
+
reject("TRACK_ACCESS_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
octopus.track(hasAccessToCommunity: hasAccess)
|
|
309
|
+
resolve(nil)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// MARK: - Locale override
|
|
313
|
+
|
|
314
|
+
@objc(overrideDefaultLocale:withCountryCode:withResolver:withRejecter:)
|
|
315
|
+
func overrideDefaultLocale(
|
|
316
|
+
languageCode: NSString?,
|
|
317
|
+
countryCode: NSString?,
|
|
318
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
319
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
320
|
+
) -> Void {
|
|
321
|
+
guard let octopus = octopusSDK else {
|
|
322
|
+
reject("LOCALE_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
let locale: Locale?
|
|
326
|
+
if let lang = languageCode as String? {
|
|
327
|
+
if let country = countryCode as String? {
|
|
328
|
+
locale = Locale(identifier: "\(lang)-\(country)")
|
|
329
|
+
} else {
|
|
330
|
+
locale = Locale(identifier: lang)
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
locale = nil
|
|
334
|
+
}
|
|
335
|
+
octopus.overrideDefaultLocale(with: locale)
|
|
336
|
+
resolve(nil)
|
|
337
|
+
}
|
|
163
338
|
|
|
164
339
|
// MARK: - Lifecycle management
|
|
165
340
|
|
|
@@ -168,9 +343,48 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
|
|
|
168
343
|
}
|
|
169
344
|
|
|
170
345
|
private func cleanup() {
|
|
346
|
+
cancellables.removeAll()
|
|
171
347
|
uiManager.cleanup()
|
|
172
348
|
octopusSDK = nil
|
|
173
349
|
}
|
|
350
|
+
|
|
351
|
+
private func startObservingReactiveEvents() {
|
|
352
|
+
startObservingNotSeenNotificationsCount()
|
|
353
|
+
startObservingHasAccessToCommunity()
|
|
354
|
+
startObservingEvents()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private func startObservingNotSeenNotificationsCount() {
|
|
358
|
+
guard let octopus = octopusSDK else { return }
|
|
359
|
+
octopus.$notSeenNotificationsCount
|
|
360
|
+
.receive(on: DispatchQueue.main)
|
|
361
|
+
.sink { [weak self] count in
|
|
362
|
+
self?.eventManager.emitNotSeenNotificationsCountChanged(count: count)
|
|
363
|
+
}
|
|
364
|
+
.store(in: &cancellables)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private func startObservingHasAccessToCommunity() {
|
|
368
|
+
guard let octopus = octopusSDK else { return }
|
|
369
|
+
octopus.$hasAccessToCommunity
|
|
370
|
+
.receive(on: DispatchQueue.main)
|
|
371
|
+
.sink { [weak self] hasAccess in
|
|
372
|
+
self?.eventManager.emitHasAccessToCommunityChanged(hasAccess: hasAccess)
|
|
373
|
+
}
|
|
374
|
+
.store(in: &cancellables)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private func startObservingEvents() {
|
|
378
|
+
guard let octopus = octopusSDK else { return }
|
|
379
|
+
octopus.eventPublisher
|
|
380
|
+
.receive(on: DispatchQueue.main)
|
|
381
|
+
.sink { [weak self] event in
|
|
382
|
+
if let eventData = OctopusEventSerializer.serializeEvent(event) {
|
|
383
|
+
self?.eventManager.emitSDKEvent(eventData: eventData)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
.store(in: &cancellables)
|
|
387
|
+
}
|
|
174
388
|
|
|
175
389
|
@objc func invalidate() {
|
|
176
390
|
cleanup()
|
|
@@ -82,9 +82,6 @@ class OctopusSSOAuthenticator {
|
|
|
82
82
|
|
|
83
83
|
let username = profileParams["username"] as? String
|
|
84
84
|
let biography = profileParams["biography"] as? String
|
|
85
|
-
let legalAgeReached = profileParams["legalAgeReached"] as? Bool
|
|
86
|
-
|
|
87
|
-
let ageInformation: ClientUser.AgeInformation? = legalAgeReached != nil ? (legalAgeReached! ? .legalAgeReached : .underaged) : nil
|
|
88
85
|
|
|
89
86
|
var profilePictureData: Data? = nil
|
|
90
87
|
if let pictureUrl = profileParams["profilePicture"] as? String, !pictureUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
@@ -94,8 +91,7 @@ class OctopusSSOAuthenticator {
|
|
|
94
91
|
return ClientUser.Profile(
|
|
95
92
|
nickname: username,
|
|
96
93
|
bio: biography,
|
|
97
|
-
picture: profilePictureData
|
|
98
|
-
ageInformation: ageInformation
|
|
94
|
+
picture: profilePictureData
|
|
99
95
|
)
|
|
100
96
|
}
|
|
101
97
|
|
|
@@ -162,6 +162,76 @@ class OctopusUIManager {
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/// Embeds the Octopus UI into the given container view (for use in ViewManager / non-fullscreen).
|
|
166
|
+
/// Caller is responsible for setting octopus.set(onNavigateToURLCallback:) when interceptUrls is true.
|
|
167
|
+
func addEmbeddedView(
|
|
168
|
+
to containerView: UIView,
|
|
169
|
+
octopus: OctopusSDK,
|
|
170
|
+
theme: OctopusUI.OctopusTheme?,
|
|
171
|
+
logoSource: [String: Any]?,
|
|
172
|
+
fontConfiguration: [String: Any]?,
|
|
173
|
+
uiConfiguration: OctopusUIConfiguration?,
|
|
174
|
+
interceptUrls: Bool
|
|
175
|
+
) {
|
|
176
|
+
let customTheme = createCustomTheme(baseTheme: theme, fontConfiguration: fontConfiguration)
|
|
177
|
+
let bottomSafeAreaInset = uiConfiguration?.bottomSafeAreaInset
|
|
178
|
+
let initialTheme = (theme != nil || fontConfiguration != nil) ? customTheme : nil
|
|
179
|
+
|
|
180
|
+
let hostingController = UIHostingController(
|
|
181
|
+
rootView: makeHomeScreenView(octopus: octopus, theme: initialTheme, bottomSafeAreaInset: bottomSafeAreaInset)
|
|
182
|
+
)
|
|
183
|
+
hostingController.view.backgroundColor = .clear
|
|
184
|
+
containerView.addSubview(hostingController.view)
|
|
185
|
+
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
186
|
+
NSLayoutConstraint.activate([
|
|
187
|
+
hostingController.view.topAnchor.constraint(equalTo: containerView.topAnchor),
|
|
188
|
+
hostingController.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
|
189
|
+
hostingController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
|
190
|
+
hostingController.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
|
191
|
+
])
|
|
192
|
+
|
|
193
|
+
if let parentVC = containerView.findViewController() {
|
|
194
|
+
parentVC.addChild(hostingController)
|
|
195
|
+
hostingController.didMove(toParent: parentVC)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if let _ = theme, let logoSource = logoSource {
|
|
199
|
+
loadLogo(from: logoSource) { [weak hostingController] logoImage in
|
|
200
|
+
DispatchQueue.main.async {
|
|
201
|
+
if let logoImage = logoImage {
|
|
202
|
+
let updatedTheme = OctopusUI.OctopusTheme(
|
|
203
|
+
colors: customTheme.colors,
|
|
204
|
+
fonts: customTheme.fonts,
|
|
205
|
+
assets: OctopusUI.OctopusTheme.Assets(logo: logoImage)
|
|
206
|
+
)
|
|
207
|
+
hostingController?.rootView = self.makeHomeScreenView(
|
|
208
|
+
octopus: octopus,
|
|
209
|
+
theme: updatedTheme,
|
|
210
|
+
bottomSafeAreaInset: bottomSafeAreaInset
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} else if let logoSource = logoSource {
|
|
216
|
+
loadLogo(from: logoSource) { [weak hostingController] logoImage in
|
|
217
|
+
DispatchQueue.main.async {
|
|
218
|
+
if let logoImage = logoImage {
|
|
219
|
+
let logoTheme = OctopusUI.OctopusTheme(
|
|
220
|
+
colors: OctopusUI.OctopusTheme.Colors(),
|
|
221
|
+
fonts: OctopusUI.OctopusTheme.Fonts(),
|
|
222
|
+
assets: OctopusUI.OctopusTheme.Assets(logo: logoImage)
|
|
223
|
+
)
|
|
224
|
+
hostingController?.rootView = self.makeHomeScreenView(
|
|
225
|
+
octopus: octopus,
|
|
226
|
+
theme: logoTheme,
|
|
227
|
+
bottomSafeAreaInset: bottomSafeAreaInset
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
165
235
|
private func makeHomeScreenView(octopus: OctopusSDK, theme: OctopusUI.OctopusTheme?, bottomSafeAreaInset: CGFloat?) -> AnyView {
|
|
166
236
|
if let inset = bottomSafeAreaInset {
|
|
167
237
|
if let theme = theme {
|
|
@@ -235,3 +305,16 @@ class OctopusUIManager {
|
|
|
235
305
|
}
|
|
236
306
|
|
|
237
307
|
}
|
|
308
|
+
|
|
309
|
+
// MARK: - Helper to find parent view controller for embedding
|
|
310
|
+
extension UIView {
|
|
311
|
+
func findViewController() -> UIViewController? {
|
|
312
|
+
if let nextResponder = self.next as? UIViewController {
|
|
313
|
+
return nextResponder
|
|
314
|
+
}
|
|
315
|
+
if let nextResponder = self.next as? UIView {
|
|
316
|
+
return nextResponder.findViewController()
|
|
317
|
+
}
|
|
318
|
+
return nil
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// Container view that embeds the native Octopus UI when added to a window.
|
|
5
|
+
/// The `interceptUrls` property is set by React Native via RCT_EXPORT_VIEW_PROPERTY (KVC on the view).
|
|
6
|
+
@objc final class OctopusEmbeddedContainerView: UIView {
|
|
7
|
+
weak var bridge: RCTBridge?
|
|
8
|
+
@objc var interceptUrls: Bool = false
|
|
9
|
+
private var hasEmbedded = false
|
|
10
|
+
|
|
11
|
+
override func didMoveToWindow() {
|
|
12
|
+
super.didMoveToWindow()
|
|
13
|
+
guard window != nil, !hasEmbedded else { return }
|
|
14
|
+
guard let bridge = bridge else { return }
|
|
15
|
+
|
|
16
|
+
guard let module = bridge.module(for: OctopusReactNativeSdk.self) as? OctopusReactNativeSdk else {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
module.addEmbeddedView(containerView: self, interceptUrls: interceptUrls)
|
|
20
|
+
hasEmbedded = true
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@objc(OctopusUIViewManager)
|
|
25
|
+
class OctopusUIViewManager: RCTViewManager {
|
|
26
|
+
|
|
27
|
+
override func view() -> UIView! {
|
|
28
|
+
let view = OctopusEmbeddedContainerView()
|
|
29
|
+
view.bridge = bridge
|
|
30
|
+
view.backgroundColor = .clear
|
|
31
|
+
return view
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override static func moduleName() -> String! {
|
|
35
|
+
return "OctopusUIView"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { requireNativeComponent, StyleSheet } from 'react-native';
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
const NativeOctopusUIView = requireNativeComponent('OctopusUIView');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Embeds the Octopus Community UI as a native view inside your screen.
|
|
9
|
+
* Use this when you want to keep your app navigation (e.g. bottom tab bar) visible
|
|
10
|
+
* instead of opening the SDK in fullscreen with `openUI()`.
|
|
11
|
+
*
|
|
12
|
+
* You must call `initialize()` before rendering this component.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* function CommunityTab() {
|
|
17
|
+
* return (
|
|
18
|
+
* <View style={{ flex: 1 }}>
|
|
19
|
+
* <OctopusUIView interceptUrls={true} style={StyleSheet.absoluteFill} />
|
|
20
|
+
* </View>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function OctopusUIView({
|
|
26
|
+
interceptUrls = false,
|
|
27
|
+
style
|
|
28
|
+
}) {
|
|
29
|
+
return /*#__PURE__*/_jsx(NativeOctopusUIView, {
|
|
30
|
+
interceptUrls: interceptUrls,
|
|
31
|
+
style: StyleSheet.flatten([styles.default, style])
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const styles = StyleSheet.create({
|
|
35
|
+
default: {
|
|
36
|
+
flex: 1
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=OctopusUIView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["requireNativeComponent","StyleSheet","jsx","_jsx","NativeOctopusUIView","OctopusUIView","interceptUrls","style","flatten","styles","default","create","flex"],"sourceRoot":"../../src","sources":["OctopusUIView.tsx"],"mappings":";;AAAA,SACEA,sBAAsB,EACtBC,UAAU,QAGL,cAAc;AAAC,SAAAC,GAAA,IAAAC,IAAA;AActB,MAAMC,mBAAmB,GACvBJ,sBAAsB,CAAqB,eAAe,CAAC;;AAE7D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASK,aAAaA,CAAC;EAC5BC,aAAa,GAAG,KAAK;EACrBC;AACkB,CAAC,EAAE;EACrB,oBACEJ,IAAA,CAACC,mBAAmB;IAClBE,aAAa,EAAEA,aAAc;IAC7BC,KAAK,EAAEN,UAAU,CAACO,OAAO,CAAC,CAACC,MAAM,CAACC,OAAO,EAAEH,KAAK,CAAC;EAAE,CACpD,CAAC;AAEN;AAEA,MAAME,MAAM,GAAGR,UAAU,CAACU,MAAM,CAAC;EAC/BD,OAAO,EAAE;IACPE,IAAI,EAAE;EACR;AACF,CAAC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { eventEmitter } from "./internals/eventEmitter.js";
|
|
4
|
+
/**
|
|
5
|
+
* Adds a listener for community access changes.
|
|
6
|
+
*
|
|
7
|
+
* This listener receives the **Octopus-managed** access state: the cohort value that determines
|
|
8
|
+
* whether the user has access to the community (when the SDK manages the A/B logic). It is triggered
|
|
9
|
+
* when that state changes — e.g. after you call `overrideCommunityAccess`, or when the cohort is
|
|
10
|
+
* updated by Octopus. Use it to show or hide community entry points in your UI. If your app manages
|
|
11
|
+
* access itself (and only reports it via `trackCommunityAccess`), this listener is less relevant,
|
|
12
|
+
* since the SDK is not the source of the access decision.
|
|
13
|
+
*
|
|
14
|
+
* @param callback - Function called when the access status changes
|
|
15
|
+
* @returns A subscription object with a `remove()` method to unsubscribe
|
|
16
|
+
* @see {@link overrideCommunityAccess} – set the cohort when Octopus manages A/B.
|
|
17
|
+
* @see {@link trackCommunityAccess} – report access for analytics when your app manages access.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const subscription = addHasAccessToCommunityListener((hasAccess) => {
|
|
22
|
+
* console.log(`Has access to community: ${hasAccess}`);
|
|
23
|
+
* // Show or hide community features based on access
|
|
24
|
+
* });
|
|
25
|
+
* subscription.remove();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function addHasAccessToCommunityListener(callback) {
|
|
29
|
+
return eventEmitter.addListener('hasAccessToCommunityChanged', data => {
|
|
30
|
+
callback(data.hasAccess);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=addHasAccessToCommunityListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["eventEmitter","addHasAccessToCommunityListener","callback","addListener","data","hasAccess"],"sourceRoot":"../../src","sources":["addHasAccessToCommunityListener.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,6BAA0B;AAIvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,+BAA+BA,CAC7CC,QAA8C,EAC9C;EACA,OAAOF,YAAY,CAACG,WAAW,CAC7B,6BAA6B,EAC5BC,IAA4B,IAAK;IAChCF,QAAQ,CAACE,IAAI,CAACC,SAAS,CAAC;EAC1B,CACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { eventEmitter } from "./internals/eventEmitter.js";
|
|
4
|
+
import { OctopusReactNativeSdk } from "./internals/nativeModule.js";
|
|
5
|
+
import { UrlOpeningStrategy as UrlOpeningStrategyEnum } from "./types/urlOpeningStrategy.js";
|
|
6
|
+
/**
|
|
7
|
+
* Adds a listener for URL navigation events from the Octopus Community UI.
|
|
8
|
+
*
|
|
9
|
+
* Only has an effect when the UI was opened with `openUI({ interceptUrls: true })`.
|
|
10
|
+
* When the user taps a link, this callback is invoked with the URL. Return
|
|
11
|
+
* `handledByApp` if your app handles the URL (e.g. in-app web view), or
|
|
12
|
+
* `handledByOctopus` to let the SDK open it in the system browser.
|
|
13
|
+
*
|
|
14
|
+
* @param callback - Function called with the tapped URL. Can be async.
|
|
15
|
+
* Return `UrlOpeningStrategy.handledByApp` or `UrlOpeningStrategy.handledByOctopus`.
|
|
16
|
+
* @returns A subscription object with a `remove()` method to unsubscribe.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const subscription = addNavigateToUrlListener(async (url) => {
|
|
21
|
+
* if (url.startsWith('https://myapp.com/')) {
|
|
22
|
+
* // Handle deep link in-app
|
|
23
|
+
* Linking.openURL(url);
|
|
24
|
+
* return UrlOpeningStrategy.handledByApp;
|
|
25
|
+
* }
|
|
26
|
+
* return UrlOpeningStrategy.handledByOctopus; // Open in system browser
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Later, to unsubscribe:
|
|
30
|
+
* subscription.remove();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function addNavigateToUrlListener(callback) {
|
|
34
|
+
return eventEmitter.addListener('navigateToUrl', async data => {
|
|
35
|
+
const strategy = await Promise.resolve(callback(data.url));
|
|
36
|
+
if (strategy === UrlOpeningStrategyEnum.handledByOctopus) {
|
|
37
|
+
OctopusReactNativeSdk.handleUrlStrategy(data.url, UrlOpeningStrategyEnum.handledByOctopus);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=addNavigateToUrlListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["eventEmitter","OctopusReactNativeSdk","UrlOpeningStrategy","UrlOpeningStrategyEnum","addNavigateToUrlListener","callback","addListener","data","strategy","Promise","resolve","url","handledByOctopus","handleUrlStrategy"],"sourceRoot":"../../src","sources":["addNavigateToUrlListener.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,6BAA0B;AACvD,SAASC,qBAAqB,QAAQ,6BAA0B;AAChE,SAEEC,kBAAkB,IAAIC,sBAAsB,QACvC,+BAA4B;AAMnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CACtCC,QAAuC,EACvC;EACA,OAAOL,YAAY,CAACM,WAAW,CAC7B,eAAe,EACf,MAAOC,IAAqB,IAAK;IAC/B,MAAMC,QAAQ,GAAG,MAAMC,OAAO,CAACC,OAAO,CAACL,QAAQ,CAACE,IAAI,CAACI,GAAG,CAAC,CAAC;IAC1D,IAAIH,QAAQ,KAAKL,sBAAsB,CAACS,gBAAgB,EAAE;MACxDX,qBAAqB,CAACY,iBAAiB,CACrCN,IAAI,CAACI,GAAG,EACRR,sBAAsB,CAACS,gBACzB,CAAC;IACH;EACF,CACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { eventEmitter } from "./internals/eventEmitter.js";
|
|
4
|
+
/**
|
|
5
|
+
* Adds a listener for not seen notifications count changes.
|
|
6
|
+
*
|
|
7
|
+
* This listener is triggered whenever the count of unseen notifications changes.
|
|
8
|
+
* The count is automatically updated by the SDK, but can also be manually refreshed
|
|
9
|
+
* using `updateNotSeenNotificationsCount()`.
|
|
10
|
+
*
|
|
11
|
+
* @param callback - Function called when the notification count changes
|
|
12
|
+
* @returns A subscription object with a `remove()` method to unsubscribe
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const subscription = addNotSeenNotificationsCountListener((count) => {
|
|
17
|
+
* console.log(`Unseen notifications: ${count}`);
|
|
18
|
+
* // Update your app's badge or UI
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Later, to unsubscribe:
|
|
22
|
+
* subscription.remove();
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function addNotSeenNotificationsCountListener(callback) {
|
|
26
|
+
return eventEmitter.addListener('notSeenNotificationsCountChanged', data => {
|
|
27
|
+
callback(data.count);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=addNotSeenNotificationsCountListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["eventEmitter","addNotSeenNotificationsCountListener","callback","addListener","data","count"],"sourceRoot":"../../src","sources":["addNotSeenNotificationsCountListener.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,6BAA0B;AAIvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,oCAAoCA,CAClDC,QAAmD,EACnD;EACA,OAAOF,YAAY,CAACG,WAAW,CAC7B,kCAAkC,EACjCC,IAAuB,IAAK;IAC3BF,QAAQ,CAACE,IAAI,CAACC,KAAK,CAAC;EACtB,CACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { eventEmitter } from "./internals/eventEmitter.js";
|
|
4
|
+
/**
|
|
5
|
+
* Adds a listener for SDK events.
|
|
6
|
+
*
|
|
7
|
+
* This listener receives all SDK events including:
|
|
8
|
+
* - Content creation (posts, comments, replies)
|
|
9
|
+
* - Content deletion
|
|
10
|
+
* - Reactions and interactions
|
|
11
|
+
* - Gamification events
|
|
12
|
+
* - Screen navigation
|
|
13
|
+
* - Profile modifications
|
|
14
|
+
* - Session events
|
|
15
|
+
* - And more...
|
|
16
|
+
*
|
|
17
|
+
* Use TypeScript type guards to narrow down specific event types:
|
|
18
|
+
*
|
|
19
|
+
* @param callback - Function called when any SDK event occurs
|
|
20
|
+
* @returns A subscription object with a `remove()` method to unsubscribe
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const subscription = addSDKEventListener((event) => {
|
|
25
|
+
* switch (event.type) {
|
|
26
|
+
* case 'postCreated':
|
|
27
|
+
* console.log(`Post created: ${event.postId}`);
|
|
28
|
+
* break;
|
|
29
|
+
* case 'reactionModified':
|
|
30
|
+
* console.log(`Reaction changed on ${event.contentId}`);
|
|
31
|
+
* break;
|
|
32
|
+
* case 'gamificationPointsGained':
|
|
33
|
+
* console.log(`Gained ${event.points} points for ${event.action}`);
|
|
34
|
+
* break;
|
|
35
|
+
* // ... handle other event types
|
|
36
|
+
* }
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* // Later, to unsubscribe:
|
|
40
|
+
* subscription.remove();
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function addSDKEventListener(callback) {
|
|
44
|
+
return eventEmitter.addListener('sdkEvent', data => {
|
|
45
|
+
callback(data);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=addSDKEventListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["eventEmitter","addSDKEventListener","callback","addListener","data"],"sourceRoot":"../../src","sources":["addSDKEventListener.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,6BAA0B;AAKvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,mBAAmBA,CAACC,QAAkC,EAAE;EACtE,OAAOF,YAAY,CAACG,WAAW,CAAC,UAAU,EAAGC,IAAc,IAAK;IAC9DF,QAAQ,CAACE,IAAI,CAAC;EAChB,CAAC,CAAC;AACJ","ignoreList":[]}
|