@shortkitsdk/react-native 0.2.6 → 0.2.12
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/ShortKitReactNative.podspec +1 -0
- package/android/build.gradle.kts +17 -1
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +379 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactLoadingHost.kt +40 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +570 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +1029 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +212 -219
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +17 -3
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +157 -742
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +11 -2
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +2 -2
- package/ios/ReactCarouselOverlayHost.swift +177 -0
- package/ios/ReactLoadingHost.swift +38 -0
- package/ios/ReactOverlayHost.swift +444 -0
- package/ios/SKFabricSurfaceWrapper.h +18 -0
- package/ios/SKFabricSurfaceWrapper.mm +57 -0
- package/ios/ShortKitBridge.swift +220 -63
- package/ios/ShortKitFeedView.swift +82 -228
- package/ios/ShortKitFeedViewManager.mm +3 -2
- package/ios/ShortKitModule.mm +69 -37
- package/ios/ShortKitPlayerNativeView.swift +39 -8
- package/ios/ShortKitReactNative-Bridging-Header.h +2 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +1 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +3683 -1249
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +56 -15
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +56 -15
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +1 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +3683 -1249
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +56 -15
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +56 -15
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.bak/Info.plist +43 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +28917 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +28917 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitWidgetNativeView.swift +3 -3
- package/package.json +1 -1
- package/src/ShortKitCarouselOverlaySurface.tsx +55 -0
- package/src/ShortKitCommands.ts +31 -0
- package/src/ShortKitContext.ts +6 -24
- package/src/ShortKitFeed.tsx +124 -41
- package/src/ShortKitLoadingSurface.tsx +24 -0
- package/src/ShortKitOverlaySurface.tsx +313 -0
- package/src/ShortKitPlayer.tsx +30 -9
- package/src/ShortKitProvider.tsx +28 -285
- package/src/index.ts +9 -3
- package/src/serialization.ts +20 -39
- package/src/specs/NativeShortKitModule.ts +74 -45
- package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
- package/src/types.ts +84 -16
- package/src/useShortKit.ts +1 -3
- package/src/useShortKitPlayer.ts +7 -7
- package/android/src/main/java/com/shortkit/reactnative/ShortKitCarouselOverlayBridge.kt +0 -48
- package/android/src/main/java/com/shortkit/reactnative/ShortKitOverlayBridge.kt +0 -128
- package/ios/ShortKitCarouselOverlayBridge.swift +0 -219
- package/ios/ShortKitOverlayBridge.swift +0 -111
- package/src/CarouselOverlayManager.tsx +0 -70
- package/src/OverlayManager.tsx +0 -87
- package/src/useShortKitCarousel.ts +0 -29
package/ios/ShortKitBridge.swift
CHANGED
|
@@ -18,25 +18,87 @@ import ShortKitSDK
|
|
|
18
18
|
private var cancellables = Set<AnyCancellable>()
|
|
19
19
|
private weak var delegate: ShortKitBridgeDelegateProtocol?
|
|
20
20
|
|
|
21
|
+
/// Surface presenter for creating RCTFabricSurface instances in overlay hosts.
|
|
22
|
+
/// Set from ShortKitModule.mm via setSurfacePresenter: (called by RCTInstance).
|
|
23
|
+
public var surfacePresenter: AnyObject?
|
|
24
|
+
|
|
25
|
+
/// Called from ShortKitModule when RCTInstance provides the surface presenter.
|
|
26
|
+
@objc public func updateSurfacePresenter(_ presenter: AnyObject?) {
|
|
27
|
+
self.surfacePresenter = presenter
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Preload handles keyed by UUID, consumed by feed views via preloadId prop.
|
|
31
|
+
public var preloadHandles: [String: FeedPreload] = [:]
|
|
32
|
+
|
|
33
|
+
/// Tracks the most recently created preload or registered feed ID for feedReady routing.
|
|
34
|
+
private var activeFeedId: String = ""
|
|
35
|
+
|
|
36
|
+
// MARK: - Feed Instance Registry
|
|
37
|
+
|
|
38
|
+
private struct WeakFeedRef {
|
|
39
|
+
weak var vc: ShortKitFeedViewController?
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private var feedRegistry: [String: WeakFeedRef] = [:]
|
|
43
|
+
private var pendingOps: [String: [(ShortKitFeedViewController) -> Void]] = [:]
|
|
44
|
+
|
|
45
|
+
public func registerFeed(id: String, viewController vc: ShortKitFeedViewController) {
|
|
46
|
+
feedRegistry[id] = WeakFeedRef(vc: vc)
|
|
47
|
+
activeFeedId = id
|
|
48
|
+
|
|
49
|
+
// Wire per-feed remaining content count callback
|
|
50
|
+
vc.onRemainingContentCountChange = { [weak self] count in
|
|
51
|
+
self?.emit("onRemainingContentCountChanged", body: [
|
|
52
|
+
"feedId": id,
|
|
53
|
+
"count": count
|
|
54
|
+
])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Wire per-feed ready callback
|
|
58
|
+
vc.onFeedReady = { [weak self] in
|
|
59
|
+
self?.emitOnMain("onFeedReady", body: ["feedId": id])
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Replay buffered operations on the next run-loop tick so the VC's
|
|
63
|
+
// view hierarchy is fully set up after didMoveToWindow returns.
|
|
64
|
+
if let ops = pendingOps.removeValue(forKey: id) {
|
|
65
|
+
DispatchQueue.main.async { [weak vc] in
|
|
66
|
+
guard let vc = vc else { return }
|
|
67
|
+
for op in ops {
|
|
68
|
+
op(vc)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public func unregisterFeed(id: String) {
|
|
75
|
+
feedRegistry.removeValue(forKey: id)
|
|
76
|
+
// Note: pendingOps are preserved for this ID to survive detach/reattach cycles
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private func feedViewController(for id: String) -> ShortKitFeedViewController? {
|
|
80
|
+
return feedRegistry[id]?.vc
|
|
81
|
+
}
|
|
82
|
+
|
|
21
83
|
// MARK: - Init
|
|
22
84
|
|
|
23
85
|
@objc public init(
|
|
24
86
|
apiKey: String,
|
|
25
|
-
|
|
87
|
+
hasLoadingView: Bool,
|
|
26
88
|
clientAppName: String?,
|
|
27
89
|
clientAppVersion: String?,
|
|
28
90
|
customDimensions customDimensionsJSON: String?,
|
|
29
|
-
delegate: ShortKitBridgeDelegateProtocol
|
|
91
|
+
delegate: ShortKitBridgeDelegateProtocol,
|
|
92
|
+
surfacePresenter: AnyObject?
|
|
30
93
|
) {
|
|
31
94
|
self.delegate = delegate
|
|
95
|
+
self.surfacePresenter = surfacePresenter
|
|
32
96
|
super.init()
|
|
33
97
|
|
|
34
|
-
let feedConfig = Self.parseFeedConfig(configJSON)
|
|
35
98
|
let dimensions = Self.parseCustomDimensions(customDimensionsJSON)
|
|
36
99
|
|
|
37
100
|
let sdk = ShortKit(
|
|
38
101
|
apiKey: apiKey,
|
|
39
|
-
config: feedConfig,
|
|
40
102
|
clientAppName: clientAppName,
|
|
41
103
|
clientAppVersion: clientAppVersion,
|
|
42
104
|
customDimensions: dimensions
|
|
@@ -45,6 +107,14 @@ import ShortKitSDK
|
|
|
45
107
|
|
|
46
108
|
ShortKitBridge.shared = self
|
|
47
109
|
|
|
110
|
+
if hasLoadingView {
|
|
111
|
+
sdk.loadingViewProvider = { [weak self] in
|
|
112
|
+
let host = ReactLoadingHost()
|
|
113
|
+
host.surfacePresenter = self?.surfacePresenter
|
|
114
|
+
return host
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
48
118
|
subscribeToPublishers(sdk.player)
|
|
49
119
|
sdk.delegate = self
|
|
50
120
|
}
|
|
@@ -53,6 +123,9 @@ import ShortKitSDK
|
|
|
53
123
|
|
|
54
124
|
@objc public func teardown() {
|
|
55
125
|
cancellables.removeAll()
|
|
126
|
+
preloadHandles.removeAll()
|
|
127
|
+
feedRegistry.removeAll()
|
|
128
|
+
pendingOps.removeAll()
|
|
56
129
|
shortKit = nil
|
|
57
130
|
if ShortKitBridge.shared === self {
|
|
58
131
|
ShortKitBridge.shared = nil
|
|
@@ -143,17 +216,31 @@ import ShortKitSDK
|
|
|
143
216
|
|
|
144
217
|
// MARK: - Custom Feed
|
|
145
218
|
|
|
146
|
-
@objc public func setFeedItems(_ json: String) {
|
|
219
|
+
@objc public func setFeedItems(_ feedId: String, items json: String) {
|
|
147
220
|
guard let items = Self.parseFeedInputs(json) else { return }
|
|
148
|
-
|
|
149
|
-
self
|
|
221
|
+
DispatchQueue.main.async { [weak self] in
|
|
222
|
+
guard let self else { return }
|
|
223
|
+
if let vc = self.feedViewController(for: feedId) {
|
|
224
|
+
vc.setFeedItems(items)
|
|
225
|
+
} else {
|
|
226
|
+
self.pendingOps[feedId, default: []].append { vc in
|
|
227
|
+
vc.setFeedItems(items)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
150
230
|
}
|
|
151
231
|
}
|
|
152
232
|
|
|
153
|
-
@objc public func appendFeedItems(_ json: String) {
|
|
233
|
+
@objc public func appendFeedItems(_ feedId: String, items json: String) {
|
|
154
234
|
guard let items = Self.parseFeedInputs(json) else { return }
|
|
155
|
-
|
|
156
|
-
self
|
|
235
|
+
DispatchQueue.main.async { [weak self] in
|
|
236
|
+
guard let self else { return }
|
|
237
|
+
if let vc = self.feedViewController(for: feedId) {
|
|
238
|
+
vc.appendFeedItems(items)
|
|
239
|
+
} else {
|
|
240
|
+
self.pendingOps[feedId, default: []].append { vc in
|
|
241
|
+
vc.appendFeedItems(items)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
157
244
|
}
|
|
158
245
|
}
|
|
159
246
|
|
|
@@ -209,14 +296,25 @@ import ShortKitSDK
|
|
|
209
296
|
}
|
|
210
297
|
}
|
|
211
298
|
|
|
212
|
-
@objc public func
|
|
213
|
-
|
|
299
|
+
@objc public func applyFilter(_ feedId: String, filterJSON: String?) {
|
|
300
|
+
let filter = filterJSON.flatMap { Self.parseFeedFilter($0) }
|
|
301
|
+
DispatchQueue.main.async { [weak self] in
|
|
302
|
+
guard let self else { return }
|
|
303
|
+
if let vc = self.feedViewController(for: feedId) {
|
|
304
|
+
vc.applyFilter(filter)
|
|
305
|
+
} else {
|
|
306
|
+
self.pendingOps[feedId, default: []].append { vc in
|
|
307
|
+
vc.applyFilter(filter)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
214
311
|
}
|
|
215
312
|
|
|
216
|
-
@objc public func fetchContent(_ limit: Int, completion: @escaping (String) -> Void) {
|
|
313
|
+
@objc public func fetchContent(_ limit: Int, filterJSON: String?, completion: @escaping (String) -> Void) {
|
|
314
|
+
let filter = filterJSON.flatMap { Self.parseFeedFilter($0) }
|
|
217
315
|
Task {
|
|
218
316
|
do {
|
|
219
|
-
let items = try await self.shortKit?.fetchContent(limit: limit) ?? []
|
|
317
|
+
let items = try await self.shortKit?.fetchContent(limit: limit, filter: filter) ?? []
|
|
220
318
|
let data = try JSONEncoder().encode(items)
|
|
221
319
|
let json = String(data: data, encoding: .utf8) ?? "[]"
|
|
222
320
|
completion(json)
|
|
@@ -226,6 +324,43 @@ import ShortKitSDK
|
|
|
226
324
|
}
|
|
227
325
|
}
|
|
228
326
|
|
|
327
|
+
@objc public func preloadFeed(_ configJSON: String, itemsJSON: String?, completion: @escaping (String) -> Void) {
|
|
328
|
+
let config = Self.parseFeedConfig(configJSON)
|
|
329
|
+
let preload: FeedPreload?
|
|
330
|
+
|
|
331
|
+
NSLog("[ShortKit Bridge] preloadFeed called — feedSource: %@, hasItemsJSON: %@",
|
|
332
|
+
config.feedSource == .custom ? "custom" : "algorithmic",
|
|
333
|
+
itemsJSON != nil ? "yes (\(itemsJSON!.prefix(100))...)" : "no")
|
|
334
|
+
|
|
335
|
+
if config.feedSource == .custom {
|
|
336
|
+
guard let json = itemsJSON, let items = Self.parseFeedInputs(json) else {
|
|
337
|
+
NSLog("[ShortKit Bridge] ❌ preloadFeed: feedSource=custom but no valid items — returning empty")
|
|
338
|
+
completion("")
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
NSLog("[ShortKit Bridge] preloadFeed: creating custom preload with %d items", items.count)
|
|
342
|
+
preload = shortKit?.preloadFeed(items: items)
|
|
343
|
+
} else {
|
|
344
|
+
preload = shortKit?.preloadFeed(filter: config.filter)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
guard let preload else {
|
|
348
|
+
NSLog("[ShortKit Bridge] ❌ preloadFeed: shortKit?.preloadFeed returned nil")
|
|
349
|
+
completion("")
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
let uuid = UUID().uuidString
|
|
353
|
+
NSLog("[ShortKit Bridge] ✅ preloadFeed: created handle %@", uuid)
|
|
354
|
+
preloadHandles[uuid] = preload
|
|
355
|
+
activeFeedId = uuid
|
|
356
|
+
completion(uuid)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/// Consume a preload handle by ID. Returns and removes the handle.
|
|
360
|
+
public func consumePreload(id: String) -> FeedPreload? {
|
|
361
|
+
return preloadHandles.removeValue(forKey: id)
|
|
362
|
+
}
|
|
363
|
+
|
|
229
364
|
// MARK: - Combine Subscriptions
|
|
230
365
|
|
|
231
366
|
private func subscribeToPublishers(_ player: ShortKitPlayer) {
|
|
@@ -382,22 +517,16 @@ import ShortKitSDK
|
|
|
382
517
|
}
|
|
383
518
|
.store(in: &cancellables)
|
|
384
519
|
|
|
385
|
-
// Remaining content count
|
|
386
|
-
player.remainingContentCount
|
|
387
|
-
.receive(on: DispatchQueue.main)
|
|
388
|
-
.sink { [weak self] count in
|
|
389
|
-
self?.emit("onRemainingContentCountChanged", body: ["count": count])
|
|
390
|
-
}
|
|
391
|
-
.store(in: &cancellables)
|
|
520
|
+
// Remaining content count — now handled per-feed via onRemainingContentCountChange callback
|
|
392
521
|
}
|
|
393
522
|
|
|
394
523
|
// MARK: - Event Emission Helpers
|
|
395
524
|
|
|
396
|
-
|
|
525
|
+
func emit(_ name: String, body: [String: Any]) {
|
|
397
526
|
delegate?.emitEvent(name, body: body)
|
|
398
527
|
}
|
|
399
528
|
|
|
400
|
-
|
|
529
|
+
func emitOnMain(_ name: String, body: [String: Any]) {
|
|
401
530
|
if Thread.isMainThread {
|
|
402
531
|
emit(name, body: body)
|
|
403
532
|
} else {
|
|
@@ -407,32 +536,6 @@ import ShortKitSDK
|
|
|
407
536
|
}
|
|
408
537
|
}
|
|
409
538
|
|
|
410
|
-
// MARK: - Overlay Lifecycle Events (called by Fabric view in Task 13)
|
|
411
|
-
|
|
412
|
-
/// Emit overlay lifecycle events from the Fabric view's overlay container.
|
|
413
|
-
public func emitOverlayEvent(_ name: String, item: ContentItem) {
|
|
414
|
-
emitOnMain(name, body: ["item": serializeContentItemToJSON(item)])
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/// Emit a raw overlay event with an arbitrary body.
|
|
418
|
-
public func emitOverlayEvent(_ name: String, body: [String: Any]) {
|
|
419
|
-
emitOnMain(name, body: body)
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// MARK: - Carousel Overlay Lifecycle Events
|
|
423
|
-
|
|
424
|
-
/// Emit carousel overlay lifecycle events with an ImageCarouselItem.
|
|
425
|
-
public func emitCarouselOverlayEvent(_ name: String, item: ImageCarouselItem) {
|
|
426
|
-
guard let data = try? JSONEncoder().encode(item),
|
|
427
|
-
let json = String(data: data, encoding: .utf8) else { return }
|
|
428
|
-
emitOnMain(name, body: ["item": json])
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/// Emit a raw carousel overlay event with an arbitrary body.
|
|
432
|
-
public func emitCarouselOverlayEvent(_ name: String, body: [String: Any]) {
|
|
433
|
-
emitOnMain(name, body: body)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
539
|
// MARK: - Content Item Serialization
|
|
437
540
|
|
|
438
541
|
/// Serialize a ContentItem to a JSON string for bridge transport.
|
|
@@ -487,6 +590,9 @@ import ShortKitSDK
|
|
|
487
590
|
if let commentCount = item.commentCount {
|
|
488
591
|
dict["commentCount"] = commentCount
|
|
489
592
|
}
|
|
593
|
+
if let fallbackUrl = item.fallbackUrl {
|
|
594
|
+
dict["fallbackUrl"] = fallbackUrl
|
|
595
|
+
}
|
|
490
596
|
|
|
491
597
|
return dict
|
|
492
598
|
}
|
|
@@ -530,7 +636,7 @@ import ShortKitSDK
|
|
|
530
636
|
/// {"feedHeight":"{\"type\":\"fullscreen\"}","overlay":"\"none\"",
|
|
531
637
|
/// "carouselMode":"\"none\"","surveyMode":"\"none\"","muteOnStart":true}
|
|
532
638
|
/// ```
|
|
533
|
-
|
|
639
|
+
public static func parseFeedConfig(_ json: String) -> FeedConfig {
|
|
534
640
|
guard let data = json.data(using: .utf8),
|
|
535
641
|
let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
536
642
|
return FeedConfig()
|
|
@@ -538,6 +644,7 @@ import ShortKitSDK
|
|
|
538
644
|
|
|
539
645
|
let feedHeight = parseFeedHeight(obj["feedHeight"] as? String)
|
|
540
646
|
let muteOnStart = obj["muteOnStart"] as? Bool ?? true
|
|
647
|
+
let autoplay = obj["autoplay"] as? Bool ?? true
|
|
541
648
|
let videoOverlay = parseVideoOverlay(obj["overlay"] as? String)
|
|
542
649
|
|
|
543
650
|
let feedSourceStr = obj["feedSource"] as? String ?? "algorithmic"
|
|
@@ -545,14 +652,22 @@ import ShortKitSDK
|
|
|
545
652
|
|
|
546
653
|
let carouselOverlay = parseCarouselOverlay(obj["carouselOverlay"] as? String)
|
|
547
654
|
|
|
655
|
+
let filter = parseFeedFilter(obj["filter"] as? String)
|
|
656
|
+
|
|
657
|
+
let scrollAxisStr = obj["scrollAxis"] as? String ?? "vertical"
|
|
658
|
+
let scrollAxis: ScrollAxis = scrollAxisStr == "horizontal" ? .horizontal : .vertical
|
|
659
|
+
|
|
548
660
|
return FeedConfig(
|
|
549
661
|
feedHeight: feedHeight,
|
|
662
|
+
scrollAxis: scrollAxis,
|
|
550
663
|
videoOverlay: videoOverlay,
|
|
551
664
|
carouselOverlay: carouselOverlay,
|
|
552
665
|
surveyOverlay: .none,
|
|
553
666
|
adOverlay: .none,
|
|
554
667
|
muteOnStart: muteOnStart,
|
|
555
|
-
|
|
668
|
+
autoplay: autoplay,
|
|
669
|
+
feedSource: feedSource,
|
|
670
|
+
filter: filter
|
|
556
671
|
)
|
|
557
672
|
}
|
|
558
673
|
|
|
@@ -560,7 +675,7 @@ import ShortKitSDK
|
|
|
560
675
|
///
|
|
561
676
|
/// Examples:
|
|
562
677
|
/// - `"\"none\""` → `.none`
|
|
563
|
-
/// - `"{\"type\":\"custom\"}"` → `.custom {
|
|
678
|
+
/// - `"{\"type\":\"custom\"}"` → `.custom { ReactOverlayHost() }`
|
|
564
679
|
private static func parseVideoOverlay(_ json: String?) -> VideoOverlayMode {
|
|
565
680
|
guard let json,
|
|
566
681
|
let data = json.data(using: .utf8),
|
|
@@ -577,10 +692,13 @@ import ShortKitSDK
|
|
|
577
692
|
if let obj = parsed as? [String: Any],
|
|
578
693
|
let type = obj["type"] as? String,
|
|
579
694
|
type == "custom" {
|
|
695
|
+
let name = obj["name"] as? String ?? "Default"
|
|
580
696
|
return .custom { @Sendable in
|
|
581
|
-
let
|
|
582
|
-
|
|
583
|
-
|
|
697
|
+
let host = ReactOverlayHost()
|
|
698
|
+
host.surfacePresenter = ShortKitBridge.shared?.surfacePresenter
|
|
699
|
+
host.overlayModuleName = "ShortKitOverlay_\(name)"
|
|
700
|
+
host.bridge = ShortKitBridge.shared
|
|
701
|
+
return host
|
|
584
702
|
}
|
|
585
703
|
}
|
|
586
704
|
|
|
@@ -591,7 +709,7 @@ import ShortKitSDK
|
|
|
591
709
|
///
|
|
592
710
|
/// Examples:
|
|
593
711
|
/// - `"\"none\""` → `.none`
|
|
594
|
-
/// - `"{\"type\":\"custom\"}"` → `.custom {
|
|
712
|
+
/// - `"{\"type\":\"custom\",\"name\":\"news\"}"` → `.custom { ReactCarouselOverlayHost() }`
|
|
595
713
|
private static func parseCarouselOverlay(_ json: String?) -> CarouselOverlayMode {
|
|
596
714
|
guard let json,
|
|
597
715
|
let data = json.data(using: .utf8),
|
|
@@ -606,10 +724,16 @@ import ShortKitSDK
|
|
|
606
724
|
if let obj = parsed as? [String: Any],
|
|
607
725
|
let type = obj["type"] as? String,
|
|
608
726
|
type == "custom" {
|
|
727
|
+
let name = obj["name"] as? String ?? "Default"
|
|
609
728
|
return .custom { @Sendable in
|
|
610
|
-
let
|
|
611
|
-
|
|
612
|
-
|
|
729
|
+
let host = ReactCarouselOverlayHost()
|
|
730
|
+
host.surfacePresenter = ShortKitBridge.shared?.surfacePresenter
|
|
731
|
+
host.bridge = ShortKitBridge.shared
|
|
732
|
+
host.carouselOverlayModuleName = "ShortKitCarouselOverlay_\(name)"
|
|
733
|
+
// Eagerly create the RN surface so it's mounted and ready before
|
|
734
|
+
// the cell scrolls into view, matching video overlay behaviour.
|
|
735
|
+
host.prepareSurface()
|
|
736
|
+
return host
|
|
613
737
|
}
|
|
614
738
|
}
|
|
615
739
|
|
|
@@ -637,6 +761,23 @@ import ShortKitSDK
|
|
|
637
761
|
}
|
|
638
762
|
}
|
|
639
763
|
|
|
764
|
+
/// Parse a JSON string of FeedFilter from the JS bridge.
|
|
765
|
+
private static func parseFeedFilter(_ json: String?) -> FeedFilter? {
|
|
766
|
+
guard let json,
|
|
767
|
+
let data = json.data(using: .utf8),
|
|
768
|
+
let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
769
|
+
return nil
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return FeedFilter(
|
|
773
|
+
tags: obj["tags"] as? [String],
|
|
774
|
+
section: obj["section"] as? String,
|
|
775
|
+
author: obj["author"] as? String,
|
|
776
|
+
contentType: obj["contentType"] as? String,
|
|
777
|
+
metadata: obj["metadata"] as? [String: String]
|
|
778
|
+
)
|
|
779
|
+
}
|
|
780
|
+
|
|
640
781
|
/// Parse a JSON string of FeedInput[] from the JS bridge.
|
|
641
782
|
private static func parseFeedInputs(_ json: String) -> [FeedInput]? {
|
|
642
783
|
guard let data = json.data(using: .utf8),
|
|
@@ -650,7 +791,8 @@ import ShortKitSDK
|
|
|
650
791
|
switch type {
|
|
651
792
|
case "video":
|
|
652
793
|
guard let playbackId = obj["playbackId"] as? String else { continue }
|
|
653
|
-
|
|
794
|
+
let fallbackUrl = obj["fallbackUrl"] as? String
|
|
795
|
+
result.append(.video(playbackId: playbackId, fallbackUrl: fallbackUrl))
|
|
654
796
|
case "imageCarousel":
|
|
655
797
|
guard let itemData = obj["item"],
|
|
656
798
|
let itemJSON = try? JSONSerialization.data(withJSONObject: itemData),
|
|
@@ -685,10 +827,25 @@ extension ShortKitBridge: ShortKitDelegate {
|
|
|
685
827
|
"index": index
|
|
686
828
|
])
|
|
687
829
|
}
|
|
830
|
+
|
|
831
|
+
public func shortKitDidRequestRefresh(_ shortKit: ShortKit) {
|
|
832
|
+
emitOnMain("onRefreshRequested", body: [:])
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
public func shortKit(_ shortKit: ShortKit, didFetchContentItems items: [ContentItem]) {
|
|
836
|
+
Task {
|
|
837
|
+
let data = try? JSONEncoder().encode(items)
|
|
838
|
+
let json = data.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
839
|
+
self.emitOnMain("onDidFetchContentItems", body: ["items": json])
|
|
840
|
+
}
|
|
841
|
+
}
|
|
688
842
|
}
|
|
689
843
|
|
|
690
|
-
// MARK: -
|
|
844
|
+
// MARK: - Dismiss Emission
|
|
691
845
|
|
|
692
|
-
extension
|
|
693
|
-
|
|
846
|
+
extension ShortKitBridge {
|
|
847
|
+
@objc public func emitDismiss() {
|
|
848
|
+
emitOnMain("onDismiss", body: [:])
|
|
849
|
+
}
|
|
694
850
|
}
|
|
851
|
+
|