@shortkitsdk/react-native 0.2.5 → 0.2.11
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 +5 -1
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +319 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactLoadingHost.kt +40 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +559 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +984 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +88 -220
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +12 -3
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +126 -706
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +2 -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 +458 -0
- package/ios/SKFabricSurfaceWrapper.h +18 -0
- package/ios/SKFabricSurfaceWrapper.mm +57 -0
- package/ios/ShortKitBridge.swift +266 -65
- package/ios/ShortKitFeedView.swift +63 -207
- package/ios/ShortKitFeedViewManager.mm +3 -2
- package/ios/ShortKitModule.mm +86 -32
- 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 +2 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +3998 -962
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +85 -24
- 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 +85 -24
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +2 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +3998 -962
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +85 -24
- 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 +85 -24
- 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 +11 -25
- package/src/ShortKitFeed.tsx +110 -41
- package/src/ShortKitLoadingSurface.tsx +24 -0
- package/src/ShortKitOverlaySurface.tsx +205 -0
- package/src/ShortKitPlayer.tsx +6 -7
- package/src/ShortKitProvider.tsx +65 -250
- package/src/index.ts +9 -4
- package/src/serialization.ts +22 -42
- package/src/specs/NativeShortKitModule.ts +67 -53
- package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
- package/src/types.ts +104 -19
- package/src/useShortKit.ts +1 -3
- package/src/useShortKitPlayer.ts +7 -8
- 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 -54
- package/ios/ShortKitOverlayBridge.swift +0 -113
- package/src/CarouselOverlayManager.tsx +0 -71
- package/src/OverlayManager.tsx +0 -87
- package/src/useShortKitCarousel.ts +0 -29
package/ios/ShortKitBridge.swift
CHANGED
|
@@ -18,27 +18,78 @@ 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
|
+
// MARK: - Feed Instance Registry
|
|
34
|
+
|
|
35
|
+
private struct WeakFeedRef {
|
|
36
|
+
weak var vc: ShortKitFeedViewController?
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private var feedRegistry: [String: WeakFeedRef] = [:]
|
|
40
|
+
private var pendingOps: [String: [(ShortKitFeedViewController) -> Void]] = [:]
|
|
41
|
+
|
|
42
|
+
public func registerFeed(id: String, viewController vc: ShortKitFeedViewController) {
|
|
43
|
+
feedRegistry[id] = WeakFeedRef(vc: vc)
|
|
44
|
+
|
|
45
|
+
// Wire per-feed remaining content count callback
|
|
46
|
+
vc.onRemainingContentCountChange = { [weak self] count in
|
|
47
|
+
self?.emit("onRemainingContentCountChanged", body: [
|
|
48
|
+
"feedId": id,
|
|
49
|
+
"count": count
|
|
50
|
+
])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Replay buffered operations on the next run-loop tick so the VC's
|
|
54
|
+
// view hierarchy is fully set up after didMoveToWindow returns.
|
|
55
|
+
if let ops = pendingOps.removeValue(forKey: id) {
|
|
56
|
+
DispatchQueue.main.async { [weak vc] in
|
|
57
|
+
guard let vc = vc else { return }
|
|
58
|
+
for op in ops {
|
|
59
|
+
op(vc)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public func unregisterFeed(id: String) {
|
|
66
|
+
feedRegistry.removeValue(forKey: id)
|
|
67
|
+
// Note: pendingOps are preserved for this ID to survive detach/reattach cycles
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private func feedViewController(for id: String) -> ShortKitFeedViewController? {
|
|
71
|
+
return feedRegistry[id]?.vc
|
|
72
|
+
}
|
|
73
|
+
|
|
21
74
|
// MARK: - Init
|
|
22
75
|
|
|
23
76
|
@objc public init(
|
|
24
77
|
apiKey: String,
|
|
25
|
-
|
|
26
|
-
embedId: String?,
|
|
78
|
+
hasLoadingView: Bool,
|
|
27
79
|
clientAppName: String?,
|
|
28
80
|
clientAppVersion: String?,
|
|
29
81
|
customDimensions customDimensionsJSON: String?,
|
|
30
|
-
delegate: ShortKitBridgeDelegateProtocol
|
|
82
|
+
delegate: ShortKitBridgeDelegateProtocol,
|
|
83
|
+
surfacePresenter: AnyObject?
|
|
31
84
|
) {
|
|
32
85
|
self.delegate = delegate
|
|
86
|
+
self.surfacePresenter = surfacePresenter
|
|
33
87
|
super.init()
|
|
34
88
|
|
|
35
|
-
let feedConfig = Self.parseFeedConfig(configJSON)
|
|
36
89
|
let dimensions = Self.parseCustomDimensions(customDimensionsJSON)
|
|
37
90
|
|
|
38
91
|
let sdk = ShortKit(
|
|
39
92
|
apiKey: apiKey,
|
|
40
|
-
config: feedConfig,
|
|
41
|
-
embedId: embedId,
|
|
42
93
|
clientAppName: clientAppName,
|
|
43
94
|
clientAppVersion: clientAppVersion,
|
|
44
95
|
customDimensions: dimensions
|
|
@@ -47,6 +98,14 @@ import ShortKitSDK
|
|
|
47
98
|
|
|
48
99
|
ShortKitBridge.shared = self
|
|
49
100
|
|
|
101
|
+
if hasLoadingView {
|
|
102
|
+
sdk.loadingViewProvider = { [weak self] in
|
|
103
|
+
let host = ReactLoadingHost()
|
|
104
|
+
host.surfacePresenter = self?.surfacePresenter
|
|
105
|
+
return host
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
50
109
|
subscribeToPublishers(sdk.player)
|
|
51
110
|
sdk.delegate = self
|
|
52
111
|
}
|
|
@@ -55,6 +114,9 @@ import ShortKitSDK
|
|
|
55
114
|
|
|
56
115
|
@objc public func teardown() {
|
|
57
116
|
cancellables.removeAll()
|
|
117
|
+
preloadHandles.removeAll()
|
|
118
|
+
feedRegistry.removeAll()
|
|
119
|
+
pendingOps.removeAll()
|
|
58
120
|
shortKit = nil
|
|
59
121
|
if ShortKitBridge.shared === self {
|
|
60
122
|
ShortKitBridge.shared = nil
|
|
@@ -145,24 +207,105 @@ import ShortKitSDK
|
|
|
145
207
|
|
|
146
208
|
// MARK: - Custom Feed
|
|
147
209
|
|
|
148
|
-
@objc public func setFeedItems(_ json: String) {
|
|
149
|
-
guard let items = Self.
|
|
150
|
-
|
|
151
|
-
self
|
|
210
|
+
@objc public func setFeedItems(_ feedId: String, items json: String) {
|
|
211
|
+
guard let items = Self.parseFeedInputs(json) else { return }
|
|
212
|
+
DispatchQueue.main.async { [weak self] in
|
|
213
|
+
guard let self else { return }
|
|
214
|
+
if let vc = self.feedViewController(for: feedId) {
|
|
215
|
+
vc.setFeedItems(items)
|
|
216
|
+
} else {
|
|
217
|
+
self.pendingOps[feedId, default: []].append { vc in
|
|
218
|
+
vc.setFeedItems(items)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@objc public func appendFeedItems(_ feedId: String, items json: String) {
|
|
225
|
+
guard let items = Self.parseFeedInputs(json) else { return }
|
|
226
|
+
DispatchQueue.main.async { [weak self] in
|
|
227
|
+
guard let self else { return }
|
|
228
|
+
if let vc = self.feedViewController(for: feedId) {
|
|
229
|
+
vc.appendFeedItems(items)
|
|
230
|
+
} else {
|
|
231
|
+
self.pendingOps[feedId, default: []].append { vc in
|
|
232
|
+
vc.appendFeedItems(items)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
152
235
|
}
|
|
153
236
|
}
|
|
154
237
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
238
|
+
// MARK: - Storyboard / Seek Thumbnails
|
|
239
|
+
|
|
240
|
+
@objc public func prefetchStoryboard(_ playbackId: String) {
|
|
241
|
+
Task {
|
|
242
|
+
_ = await StoryboardProvider.shared.fetch(playbackId: playbackId)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@objc public func getStoryboardData(_ playbackId: String, completion: @escaping (String?) -> Void) {
|
|
247
|
+
Task {
|
|
248
|
+
// Try cache first, otherwise fetch
|
|
249
|
+
let storyboard: CachedStoryboard?
|
|
250
|
+
if let cached = StoryboardProvider.shared.cached(for: playbackId) {
|
|
251
|
+
storyboard = cached
|
|
252
|
+
} else {
|
|
253
|
+
storyboard = await StoryboardProvider.shared.fetch(playbackId: playbackId)
|
|
254
|
+
}
|
|
255
|
+
guard let meta = storyboard?.metadata else {
|
|
256
|
+
completion(nil)
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
// Compute sprite sheet dimensions from tile positions
|
|
260
|
+
var maxX = 0, maxY = 0
|
|
261
|
+
for tile in meta.tiles {
|
|
262
|
+
if tile.x > maxX { maxX = tile.x }
|
|
263
|
+
if tile.y > maxY { maxY = tile.y }
|
|
264
|
+
}
|
|
265
|
+
let imageWidth = maxX + meta.tileWidth
|
|
266
|
+
let imageHeight = maxY + meta.tileHeight
|
|
267
|
+
// Build JSON matching the shape the JS side expects
|
|
268
|
+
var tilesArr: [[String: Any]] = []
|
|
269
|
+
for tile in meta.tiles {
|
|
270
|
+
tilesArr.append(["start": tile.start, "x": tile.x, "y": tile.y])
|
|
271
|
+
}
|
|
272
|
+
let result: [String: Any] = [
|
|
273
|
+
"url": meta.url,
|
|
274
|
+
"tileWidth": meta.tileWidth,
|
|
275
|
+
"tileHeight": meta.tileHeight,
|
|
276
|
+
"duration": meta.duration,
|
|
277
|
+
"imageWidth": imageWidth,
|
|
278
|
+
"imageHeight": imageHeight,
|
|
279
|
+
"tiles": tilesArr,
|
|
280
|
+
]
|
|
281
|
+
if let data = try? JSONSerialization.data(withJSONObject: result),
|
|
282
|
+
let json = String(data: data, encoding: .utf8) {
|
|
283
|
+
completion(json)
|
|
284
|
+
} else {
|
|
285
|
+
completion(nil)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@objc public func applyFilter(_ feedId: String, filterJSON: String?) {
|
|
291
|
+
let filter = filterJSON.flatMap { Self.parseFeedFilter($0) }
|
|
292
|
+
DispatchQueue.main.async { [weak self] in
|
|
293
|
+
guard let self else { return }
|
|
294
|
+
if let vc = self.feedViewController(for: feedId) {
|
|
295
|
+
vc.applyFilter(filter)
|
|
296
|
+
} else {
|
|
297
|
+
self.pendingOps[feedId, default: []].append { vc in
|
|
298
|
+
vc.applyFilter(filter)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
159
301
|
}
|
|
160
302
|
}
|
|
161
303
|
|
|
162
|
-
@objc public func fetchContent(_ limit: Int, completion: @escaping (String) -> Void) {
|
|
304
|
+
@objc public func fetchContent(_ limit: Int, filterJSON: String?, completion: @escaping (String) -> Void) {
|
|
305
|
+
let filter = filterJSON.flatMap { Self.parseFeedFilter($0) }
|
|
163
306
|
Task {
|
|
164
307
|
do {
|
|
165
|
-
let items = try await self.shortKit?.fetchContent(limit: limit) ?? []
|
|
308
|
+
let items = try await self.shortKit?.fetchContent(limit: limit, filter: filter) ?? []
|
|
166
309
|
let data = try JSONEncoder().encode(items)
|
|
167
310
|
let json = String(data: data, encoding: .utf8) ?? "[]"
|
|
168
311
|
completion(json)
|
|
@@ -172,6 +315,22 @@ import ShortKitSDK
|
|
|
172
315
|
}
|
|
173
316
|
}
|
|
174
317
|
|
|
318
|
+
@objc public func preloadFeed(_ configJSON: String, completion: @escaping (String) -> Void) {
|
|
319
|
+
let config = Self.parseFeedConfig(configJSON)
|
|
320
|
+
guard let preload = shortKit?.preloadFeed(filter: config.filter) else {
|
|
321
|
+
completion("")
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
let uuid = UUID().uuidString
|
|
325
|
+
preloadHandles[uuid] = preload
|
|
326
|
+
completion(uuid)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/// Consume a preload handle by ID. Returns and removes the handle.
|
|
330
|
+
public func consumePreload(id: String) -> FeedPreload? {
|
|
331
|
+
return preloadHandles.removeValue(forKey: id)
|
|
332
|
+
}
|
|
333
|
+
|
|
175
334
|
// MARK: - Combine Subscriptions
|
|
176
335
|
|
|
177
336
|
private func subscribeToPublishers(_ player: ShortKitPlayer) {
|
|
@@ -288,6 +447,24 @@ import ShortKitSDK
|
|
|
288
447
|
}
|
|
289
448
|
.store(in: &cancellables)
|
|
290
449
|
|
|
450
|
+
// Feed scroll phase
|
|
451
|
+
player.feedScrollPhase
|
|
452
|
+
.receive(on: DispatchQueue.main)
|
|
453
|
+
.sink { [weak self] phase in
|
|
454
|
+
switch phase {
|
|
455
|
+
case .dragging(let fromId):
|
|
456
|
+
self?.emit("onFeedScrollPhase", body: [
|
|
457
|
+
"phase": "dragging",
|
|
458
|
+
"fromId": fromId
|
|
459
|
+
])
|
|
460
|
+
case .settled:
|
|
461
|
+
self?.emit("onFeedScrollPhase", body: [
|
|
462
|
+
"phase": "settled"
|
|
463
|
+
])
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
.store(in: &cancellables)
|
|
467
|
+
|
|
291
468
|
// Format change
|
|
292
469
|
player.formatChange
|
|
293
470
|
.receive(on: DispatchQueue.main)
|
|
@@ -310,22 +487,16 @@ import ShortKitSDK
|
|
|
310
487
|
}
|
|
311
488
|
.store(in: &cancellables)
|
|
312
489
|
|
|
313
|
-
// Remaining content count
|
|
314
|
-
player.remainingContentCount
|
|
315
|
-
.receive(on: DispatchQueue.main)
|
|
316
|
-
.sink { [weak self] count in
|
|
317
|
-
self?.emit("onRemainingContentCountChanged", body: ["count": count])
|
|
318
|
-
}
|
|
319
|
-
.store(in: &cancellables)
|
|
490
|
+
// Remaining content count — now handled per-feed via onRemainingContentCountChange callback
|
|
320
491
|
}
|
|
321
492
|
|
|
322
493
|
// MARK: - Event Emission Helpers
|
|
323
494
|
|
|
324
|
-
|
|
495
|
+
func emit(_ name: String, body: [String: Any]) {
|
|
325
496
|
delegate?.emitEvent(name, body: body)
|
|
326
497
|
}
|
|
327
498
|
|
|
328
|
-
|
|
499
|
+
func emitOnMain(_ name: String, body: [String: Any]) {
|
|
329
500
|
if Thread.isMainThread {
|
|
330
501
|
emit(name, body: body)
|
|
331
502
|
} else {
|
|
@@ -335,32 +506,6 @@ import ShortKitSDK
|
|
|
335
506
|
}
|
|
336
507
|
}
|
|
337
508
|
|
|
338
|
-
// MARK: - Overlay Lifecycle Events (called by Fabric view in Task 13)
|
|
339
|
-
|
|
340
|
-
/// Emit overlay lifecycle events from the Fabric view's overlay container.
|
|
341
|
-
public func emitOverlayEvent(_ name: String, item: ContentItem) {
|
|
342
|
-
emitOnMain(name, body: ["item": serializeContentItemToJSON(item)])
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/// Emit a raw overlay event with an arbitrary body.
|
|
346
|
-
public func emitOverlayEvent(_ name: String, body: [String: Any]) {
|
|
347
|
-
emitOnMain(name, body: body)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// MARK: - Carousel Overlay Lifecycle Events
|
|
351
|
-
|
|
352
|
-
/// Emit carousel overlay lifecycle events with an ImageCarouselItem.
|
|
353
|
-
public func emitCarouselOverlayEvent(_ name: String, item: ImageCarouselItem) {
|
|
354
|
-
guard let data = try? JSONEncoder().encode(item),
|
|
355
|
-
let json = String(data: data, encoding: .utf8) else { return }
|
|
356
|
-
emitOnMain(name, body: ["item": json])
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/// Emit a raw carousel overlay event with an arbitrary body.
|
|
360
|
-
public func emitCarouselOverlayEvent(_ name: String, body: [String: Any]) {
|
|
361
|
-
emitOnMain(name, body: body)
|
|
362
|
-
}
|
|
363
|
-
|
|
364
509
|
// MARK: - Content Item Serialization
|
|
365
510
|
|
|
366
511
|
/// Serialize a ContentItem to a JSON string for bridge transport.
|
|
@@ -415,6 +560,9 @@ import ShortKitSDK
|
|
|
415
560
|
if let commentCount = item.commentCount {
|
|
416
561
|
dict["commentCount"] = commentCount
|
|
417
562
|
}
|
|
563
|
+
if let fallbackUrl = item.fallbackUrl {
|
|
564
|
+
dict["fallbackUrl"] = fallbackUrl
|
|
565
|
+
}
|
|
418
566
|
|
|
419
567
|
return dict
|
|
420
568
|
}
|
|
@@ -458,7 +606,7 @@ import ShortKitSDK
|
|
|
458
606
|
/// {"feedHeight":"{\"type\":\"fullscreen\"}","overlay":"\"none\"",
|
|
459
607
|
/// "carouselMode":"\"none\"","surveyMode":"\"none\"","muteOnStart":true}
|
|
460
608
|
/// ```
|
|
461
|
-
|
|
609
|
+
public static func parseFeedConfig(_ json: String) -> FeedConfig {
|
|
462
610
|
guard let data = json.data(using: .utf8),
|
|
463
611
|
let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
464
612
|
return FeedConfig()
|
|
@@ -466,6 +614,7 @@ import ShortKitSDK
|
|
|
466
614
|
|
|
467
615
|
let feedHeight = parseFeedHeight(obj["feedHeight"] as? String)
|
|
468
616
|
let muteOnStart = obj["muteOnStart"] as? Bool ?? true
|
|
617
|
+
let autoplay = obj["autoplay"] as? Bool ?? true
|
|
469
618
|
let videoOverlay = parseVideoOverlay(obj["overlay"] as? String)
|
|
470
619
|
|
|
471
620
|
let feedSourceStr = obj["feedSource"] as? String ?? "algorithmic"
|
|
@@ -473,6 +622,8 @@ import ShortKitSDK
|
|
|
473
622
|
|
|
474
623
|
let carouselOverlay = parseCarouselOverlay(obj["carouselOverlay"] as? String)
|
|
475
624
|
|
|
625
|
+
let filter = parseFeedFilter(obj["filter"] as? String)
|
|
626
|
+
|
|
476
627
|
return FeedConfig(
|
|
477
628
|
feedHeight: feedHeight,
|
|
478
629
|
videoOverlay: videoOverlay,
|
|
@@ -480,7 +631,9 @@ import ShortKitSDK
|
|
|
480
631
|
surveyOverlay: .none,
|
|
481
632
|
adOverlay: .none,
|
|
482
633
|
muteOnStart: muteOnStart,
|
|
483
|
-
|
|
634
|
+
autoplay: autoplay,
|
|
635
|
+
feedSource: feedSource,
|
|
636
|
+
filter: filter
|
|
484
637
|
)
|
|
485
638
|
}
|
|
486
639
|
|
|
@@ -488,7 +641,7 @@ import ShortKitSDK
|
|
|
488
641
|
///
|
|
489
642
|
/// Examples:
|
|
490
643
|
/// - `"\"none\""` → `.none`
|
|
491
|
-
/// - `"{\"type\":\"custom\"}"` → `.custom {
|
|
644
|
+
/// - `"{\"type\":\"custom\"}"` → `.custom { ReactOverlayHost() }`
|
|
492
645
|
private static func parseVideoOverlay(_ json: String?) -> VideoOverlayMode {
|
|
493
646
|
guard let json,
|
|
494
647
|
let data = json.data(using: .utf8),
|
|
@@ -505,10 +658,13 @@ import ShortKitSDK
|
|
|
505
658
|
if let obj = parsed as? [String: Any],
|
|
506
659
|
let type = obj["type"] as? String,
|
|
507
660
|
type == "custom" {
|
|
661
|
+
let name = obj["name"] as? String ?? "Default"
|
|
508
662
|
return .custom { @Sendable in
|
|
509
|
-
let
|
|
510
|
-
|
|
511
|
-
|
|
663
|
+
let host = ReactOverlayHost()
|
|
664
|
+
host.surfacePresenter = ShortKitBridge.shared?.surfacePresenter
|
|
665
|
+
host.overlayModuleName = "ShortKitOverlay_\(name)"
|
|
666
|
+
host.bridge = ShortKitBridge.shared
|
|
667
|
+
return host
|
|
512
668
|
}
|
|
513
669
|
}
|
|
514
670
|
|
|
@@ -519,7 +675,7 @@ import ShortKitSDK
|
|
|
519
675
|
///
|
|
520
676
|
/// Examples:
|
|
521
677
|
/// - `"\"none\""` → `.none`
|
|
522
|
-
/// - `"{\"type\":\"custom\"}"` → `.custom {
|
|
678
|
+
/// - `"{\"type\":\"custom\",\"name\":\"news\"}"` → `.custom { ReactCarouselOverlayHost() }`
|
|
523
679
|
private static func parseCarouselOverlay(_ json: String?) -> CarouselOverlayMode {
|
|
524
680
|
guard let json,
|
|
525
681
|
let data = json.data(using: .utf8),
|
|
@@ -534,10 +690,16 @@ import ShortKitSDK
|
|
|
534
690
|
if let obj = parsed as? [String: Any],
|
|
535
691
|
let type = obj["type"] as? String,
|
|
536
692
|
type == "custom" {
|
|
693
|
+
let name = obj["name"] as? String ?? "Default"
|
|
537
694
|
return .custom { @Sendable in
|
|
538
|
-
let
|
|
539
|
-
|
|
540
|
-
|
|
695
|
+
let host = ReactCarouselOverlayHost()
|
|
696
|
+
host.surfacePresenter = ShortKitBridge.shared?.surfacePresenter
|
|
697
|
+
host.bridge = ShortKitBridge.shared
|
|
698
|
+
host.carouselOverlayModuleName = "ShortKitCarouselOverlay_\(name)"
|
|
699
|
+
// Eagerly create the RN surface so it's mounted and ready before
|
|
700
|
+
// the cell scrolls into view, matching video overlay behaviour.
|
|
701
|
+
host.prepareSurface()
|
|
702
|
+
return host
|
|
541
703
|
}
|
|
542
704
|
}
|
|
543
705
|
|
|
@@ -565,20 +727,38 @@ import ShortKitSDK
|
|
|
565
727
|
}
|
|
566
728
|
}
|
|
567
729
|
|
|
568
|
-
/// Parse a JSON string of
|
|
569
|
-
private static func
|
|
730
|
+
/// Parse a JSON string of FeedFilter from the JS bridge.
|
|
731
|
+
private static func parseFeedFilter(_ json: String?) -> FeedFilter? {
|
|
732
|
+
guard let json,
|
|
733
|
+
let data = json.data(using: .utf8),
|
|
734
|
+
let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
735
|
+
return nil
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return FeedFilter(
|
|
739
|
+
tags: obj["tags"] as? [String],
|
|
740
|
+
section: obj["section"] as? String,
|
|
741
|
+
author: obj["author"] as? String,
|
|
742
|
+
contentType: obj["contentType"] as? String,
|
|
743
|
+
metadata: obj["metadata"] as? [String: String]
|
|
744
|
+
)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/// Parse a JSON string of FeedInput[] from the JS bridge.
|
|
748
|
+
private static func parseFeedInputs(_ json: String) -> [FeedInput]? {
|
|
570
749
|
guard let data = json.data(using: .utf8),
|
|
571
750
|
let arr = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] else {
|
|
572
751
|
return nil
|
|
573
752
|
}
|
|
574
753
|
|
|
575
|
-
var result: [
|
|
754
|
+
var result: [FeedInput] = []
|
|
576
755
|
for obj in arr {
|
|
577
756
|
guard let type = obj["type"] as? String else { continue }
|
|
578
757
|
switch type {
|
|
579
758
|
case "video":
|
|
580
759
|
guard let playbackId = obj["playbackId"] as? String else { continue }
|
|
581
|
-
|
|
760
|
+
let fallbackUrl = obj["fallbackUrl"] as? String
|
|
761
|
+
result.append(.video(playbackId: playbackId, fallbackUrl: fallbackUrl))
|
|
582
762
|
case "imageCarousel":
|
|
583
763
|
guard let itemData = obj["item"],
|
|
584
764
|
let itemJSON = try? JSONSerialization.data(withJSONObject: itemData),
|
|
@@ -613,4 +793,25 @@ extension ShortKitBridge: ShortKitDelegate {
|
|
|
613
793
|
"index": index
|
|
614
794
|
])
|
|
615
795
|
}
|
|
796
|
+
|
|
797
|
+
public func shortKitDidRequestRefresh(_ shortKit: ShortKit) {
|
|
798
|
+
emitOnMain("onRefreshRequested", body: [:])
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
public func shortKit(_ shortKit: ShortKit, didFetchContentItems items: [ContentItem]) {
|
|
802
|
+
Task {
|
|
803
|
+
let data = try? JSONEncoder().encode(items)
|
|
804
|
+
let json = data.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
805
|
+
self.emitOnMain("onDidFetchContentItems", body: ["items": json])
|
|
806
|
+
}
|
|
807
|
+
}
|
|
616
808
|
}
|
|
809
|
+
|
|
810
|
+
// MARK: - Dismiss Emission
|
|
811
|
+
|
|
812
|
+
extension ShortKitBridge {
|
|
813
|
+
@objc public func emitDismiss() {
|
|
814
|
+
emitOnMain("onDismiss", body: [:])
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|