@shortkitsdk/react-native 0.2.26 → 0.2.28

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.
Files changed (36) hide show
  1. package/android/libs/shortkit-release.aar +0 -0
  2. package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +8 -0
  3. package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +10 -0
  4. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +4 -0
  5. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +45 -33
  6. package/ios/ShortKitBridge.swift +8 -0
  7. package/ios/ShortKitFeedView.swift +10 -4
  8. package/ios/ShortKitModule.mm +11 -0
  9. package/ios/ShortKitPlayerNativeView.swift +7 -1
  10. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
  11. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +213 -23
  12. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +6 -2
  13. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  14. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +6 -2
  15. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  16. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
  17. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
  18. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +213 -23
  19. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +6 -2
  20. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  21. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +6 -2
  22. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +213 -23
  23. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +6 -2
  24. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  25. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +6 -2
  26. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  27. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
  28. package/ios/ShortKitWidgetNativeView.swift +33 -12
  29. package/package.json +1 -1
  30. package/src/ShortKitFeed.tsx +3 -0
  31. package/src/ShortKitPlayer.tsx +25 -15
  32. package/src/ShortKitWidget.tsx +24 -18
  33. package/src/index.ts +1 -0
  34. package/src/serialization.ts +38 -0
  35. package/src/specs/NativeShortKitModule.ts +1 -0
  36. package/src/types.ts +19 -1
Binary file
@@ -835,6 +835,14 @@ class ShortKitBridge(
835
835
  }
836
836
  }
837
837
 
838
+ fun refresh(feedId: String) {
839
+ val fragment = feedRegistry[feedId]?.get()
840
+ if (fragment != null) {
841
+ Handler(Looper.getMainLooper()).post { fragment.refresh() }
842
+ }
843
+ // No pending ops — refresh on unregistered feed is a no-op
844
+ }
845
+
838
846
  // ------------------------------------------------------------------
839
847
  // Fetch content
840
848
  // ------------------------------------------------------------------
@@ -230,6 +230,16 @@ class ShortKitModule(reactContext: ReactApplicationContext) :
230
230
  }
231
231
  }
232
232
 
233
+ @ReactMethod
234
+ override fun refresh(feedId: String) {
235
+ val b = bridge
236
+ if (b != null) {
237
+ b.refresh(feedId)
238
+ } else {
239
+ // No buffering — refresh before bridge is ready is a no-op
240
+ }
241
+ }
242
+
233
243
  @ReactMethod
234
244
  override fun preloadFeed(configJSON: String, itemsJSON: String?, promise: Promise) {
235
245
  val b = bridge
@@ -4,6 +4,7 @@ import android.content.Context
4
4
  import android.view.MotionEvent
5
5
  import android.widget.FrameLayout
6
6
  import com.shortkit.sdk.config.PlayerClickAction
7
+ import com.shortkit.sdk.config.FeedConfig
7
8
  import com.shortkit.sdk.config.PlayerConfig
8
9
  import com.shortkit.sdk.config.VideoOverlayMode
9
10
  import com.shortkit.sdk.model.ContentItem
@@ -106,6 +107,9 @@ class ShortKitPlayerNativeView(context: Context) : FrameLayout(context) {
106
107
  loop = obj.optBoolean("loop", true),
107
108
  muteOnStart = obj.optBoolean("muteOnStart", true),
108
109
  videoOverlay = parseOverlay(obj),
110
+ feedConfig = obj.optString("feedConfig", "").let { fcStr ->
111
+ if (fcStr.isNotEmpty()) ShortKitBridge.parseFeedConfig(fcStr, context) else FeedConfig()
112
+ },
109
113
  )
110
114
  } catch (_: Exception) {
111
115
  PlayerConfig()
@@ -5,28 +5,40 @@ import android.widget.FrameLayout
5
5
  import com.shortkit.sdk.config.PlayerClickAction
6
6
  import com.shortkit.sdk.config.VideoOverlayMode
7
7
  import com.shortkit.sdk.config.WidgetConfig
8
- import com.shortkit.sdk.model.ContentItem
9
8
  import com.shortkit.sdk.widget.ShortKitWidgetView
9
+ import com.shortkit.sdk.config.FeedConfig
10
10
  import com.shortkit.sdk.model.FeedFilter
11
+ import com.shortkit.sdk.model.WidgetInput
11
12
  import org.json.JSONArray
12
13
  import org.json.JSONObject
13
14
 
14
15
  /**
15
16
  * Fabric native view wrapping [ShortKitWidgetView] for use as a
16
17
  * horizontal video carousel in React Native.
18
+ *
19
+ * Props:
20
+ * - `config`: JSON string with WidgetConfig values
21
+ * - `items`: JSON string with WidgetInput array (compact playback-ID form)
17
22
  */
18
23
  class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
19
24
 
20
25
  private var widgetView: ShortKitWidgetView? = null
21
26
  private var configJson: String? = null
22
27
  private var itemsJson: String? = null
28
+ private var parsedConfig: WidgetConfig = WidgetConfig()
29
+ private var parsedItems: List<WidgetInput> = emptyList()
23
30
 
24
31
  var config: String?
25
32
  get() = configJson
26
33
  set(value) {
27
34
  if (value == configJson) return
28
35
  configJson = value
29
- rebuildIfNeeded()
36
+ parsedConfig = parseWidgetConfig(value)
37
+ // If widget is already built, rebuild to pick up new config.
38
+ if (widgetView != null) {
39
+ removeWidget()
40
+ embedWidgetIfNeeded()
41
+ }
30
42
  }
31
43
 
32
44
  var items: String?
@@ -34,39 +46,38 @@ class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
34
46
  set(value) {
35
47
  if (value == itemsJson) return
36
48
  itemsJson = value
37
- applyItems()
49
+ parsedItems = parseWidgetInputs(value) ?: emptyList()
50
+ // Post-mount update on an existing widget.
51
+ widgetView?.configure(parsedItems)
38
52
  }
39
53
 
40
54
  override fun onAttachedToWindow() {
41
55
  super.onAttachedToWindow()
42
- rebuildIfNeeded()
56
+ embedWidgetIfNeeded()
43
57
  }
44
58
 
45
59
  override fun onDetachedFromWindow() {
46
60
  super.onDetachedFromWindow()
61
+ removeWidget()
47
62
  }
48
63
 
49
- private fun rebuildIfNeeded() {
64
+ private fun embedWidgetIfNeeded() {
50
65
  if (widgetView != null) return
51
-
52
66
  val sdk = ShortKitBridge.shared?.sdk ?: return
53
- val widgetConfig = parseWidgetConfig(configJson)
54
67
 
68
+ // Pass items at initialize time so the widget never races the server
69
+ // fetch — analogous to the feed's `feedItems` prop wiring.
55
70
  val view = ShortKitWidgetView(context).apply {
56
71
  layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
57
72
  }
58
- view.initialize(sdk, widgetConfig)
73
+ view.initialize(sdk, parsedConfig, parsedItems)
59
74
  addView(view)
60
75
  widgetView = view
61
-
62
- applyItems()
63
76
  }
64
77
 
65
- private fun applyItems() {
66
- val json = itemsJson ?: return
67
- val view = widgetView ?: return
68
- val contentItems = parseContentItems(json) ?: return
69
- view.configure(contentItems)
78
+ private fun removeWidget() {
79
+ widgetView?.let { removeView(it) }
80
+ widgetView = null
70
81
  }
71
82
 
72
83
  private fun parseWidgetConfig(json: String?): WidgetConfig {
@@ -91,6 +102,9 @@ class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
91
102
  filter = obj.optString("filter", "").let { filterStr ->
92
103
  if (filterStr.isNotEmpty()) ShortKitBridge.parseFeedFilterToModel(filterStr) else null
93
104
  },
105
+ feedConfig = obj.optString("feedConfig", "").let { fcStr ->
106
+ if (fcStr.isNotEmpty()) ShortKitBridge.parseFeedConfig(fcStr, context) else FeedConfig()
107
+ },
94
108
  )
95
109
  } catch (_: Exception) {
96
110
  WidgetConfig()
@@ -107,29 +121,27 @@ class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
107
121
  return VideoOverlayMode.None
108
122
  }
109
123
 
110
- private fun parseContentItems(json: String): List<ContentItem>? {
124
+ /**
125
+ * Parse a JSON array of `WidgetInput` values (compact playback-ID form).
126
+ * Mirrors the JS `WidgetInput` type:
127
+ * `{ type: 'video'; playbackId: string; fallbackUrl?: string }`.
128
+ */
129
+ private fun parseWidgetInputs(json: String?): List<WidgetInput>? {
130
+ if (json.isNullOrEmpty()) return null
111
131
  return try {
112
132
  val arr = JSONArray(json)
113
- val items = mutableListOf<ContentItem>()
133
+ val out = mutableListOf<WidgetInput>()
114
134
  for (i in 0 until arr.length()) {
115
135
  val obj = arr.getJSONObject(i)
116
- items.add(
117
- ContentItem(
118
- id = obj.getString("id"),
119
- title = obj.getString("title"),
120
- description = obj.optString("description", null),
121
- duration = obj.getDouble("duration"),
122
- streamingUrl = obj.getString("streamingUrl"),
123
- thumbnailUrl = obj.getString("thumbnailUrl"),
124
- captionTracks = emptyList(),
125
- customMetadata = null,
126
- author = obj.optString("author", null),
127
- articleUrl = obj.optString("articleUrl", null),
128
- commentCount = if (obj.has("commentCount")) obj.getInt("commentCount") else null,
129
- )
130
- )
136
+ if (obj.optString("type") != "video") continue
137
+ val playbackId = obj.optString("playbackId", "")
138
+ if (playbackId.isEmpty()) continue
139
+ val fallbackUrl = if (obj.has("fallbackUrl") && !obj.isNull("fallbackUrl")) {
140
+ obj.getString("fallbackUrl")
141
+ } else null
142
+ out.add(WidgetInput.Video(playbackId = playbackId, fallbackUrl = fallbackUrl))
131
143
  }
132
- items
144
+ out
133
145
  } catch (_: Exception) {
134
146
  null
135
147
  }
@@ -404,6 +404,14 @@ import ShortKitSDK
404
404
  }
405
405
  }
406
406
 
407
+ @objc public func refresh(_ feedId: String) {
408
+ DispatchQueue.main.async { [weak self] in
409
+ guard let self else { return }
410
+ self.feedViewController(for: feedId)?.refresh()
411
+ // No pending ops buffer — refresh on a not-yet-registered feed is a no-op
412
+ }
413
+ }
414
+
407
415
  @objc public func fetchContent(_ limit: Int, filterJSON: String?, completion: @escaping (String) -> Void) {
408
416
  let filter = filterJSON.flatMap { Self.parseFeedFilter($0) }
409
417
  Task {
@@ -191,10 +191,16 @@ import ShortKitSDK
191
191
  addSubview(feedVC.view)
192
192
  feedVC.didMove(toParent: parentVC)
193
193
 
194
- // Fabric sets props BEFORE didMoveToWindow, so active=false may have
195
- // fired its didSet while feedViewController was still nil. Apply now.
196
- if isActiveManagedByProp && !active, let feedVC = feedViewController {
197
- feedVC.deactivate()
194
+ // With FVC.viewDidAppear no longer self-claiming for bridge-managed
195
+ // surfaces, the bridge is the sole authority on claim timing. Mirror
196
+ // the reattach path: explicitly activate iff the RN `active` prop
197
+ // is true. If the prop is false (e.g. user is on a different tab
198
+ // while this FVC was created to handle a delayed API response), the
199
+ // FVC stays idle — no claim, no pool mutation, no hijack. A later
200
+ // prop change to active=true triggers `feedVC.activate()` via the
201
+ // prop's didSet.
202
+ if active {
203
+ feedVC.activate()
198
204
  }
199
205
  }
200
206
 
@@ -308,6 +308,17 @@ RCT_EXPORT_METHOD(applyFilter:(NSString *)feedId filterJSON:(NSString *)filterJS
308
308
  }
309
309
  }
310
310
 
311
+ RCT_EXPORT_METHOD(refresh:(NSString *)feedId) {
312
+ if (_shortKitBridge) {
313
+ [_shortKitBridge refresh:feedId];
314
+ } else {
315
+ if (!_pendingBridgeOps) _pendingBridgeOps = [NSMutableArray new];
316
+ [_pendingBridgeOps addObject:^{
317
+ [self->_shortKitBridge refresh:feedId];
318
+ }];
319
+ }
320
+ }
321
+
311
322
  RCT_EXPORT_METHOD(preloadFeed:(NSString *)configJSON
312
323
  itemsJSON:(NSString *)itemsJSON
313
324
  resolve:(RCTPromiseResolveBlock)resolve
@@ -187,13 +187,19 @@ import ShortKitSDK
187
187
  overlayMode = .none
188
188
  }
189
189
 
190
+ let feedConfig: FeedConfig = {
191
+ guard let feedConfigStr = obj["feedConfig"] as? String else { return FeedConfig() }
192
+ return ShortKitBridge.parseFeedConfig(feedConfigStr)
193
+ }()
194
+
190
195
  return PlayerConfig(
191
196
  cornerRadius: cornerRadius,
192
197
  clickAction: clickAction,
193
198
  autoplay: autoplay,
194
199
  loop: loop,
195
200
  muteOnStart: muteOnStart,
196
- videoOverlay: overlayMode
201
+ videoOverlay: overlayMode,
202
+ feedConfig: feedConfig
197
203
  )
198
204
  }
199
205
 
@@ -11,9 +11,9 @@
11
11
  <key>CFBundlePackageType</key>
12
12
  <string>FMWK</string>
13
13
  <key>CFBundleVersion</key>
14
- <string>0.2.26</string>
14
+ <string>0.2.28</string>
15
15
  <key>CFBundleShortVersionString</key>
16
- <string>0.2.26</string>
16
+ <string>0.2.28</string>
17
17
  <key>MinimumOSVersion</key>
18
18
  <string>16.0</string>
19
19
  </dict>