@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.
- package/android/libs/shortkit-release.aar +0 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +8 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +10 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +4 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +45 -33
- package/ios/ShortKitBridge.swift +8 -0
- package/ios/ShortKitFeedView.swift +10 -4
- package/ios/ShortKitModule.mm +11 -0
- package/ios/ShortKitPlayerNativeView.swift +7 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +213 -23
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +6 -2
- 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 +6 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +213 -23
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +6 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +6 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +213 -23
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +6 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +6 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
- package/ios/ShortKitWidgetNativeView.swift +33 -12
- package/package.json +1 -1
- package/src/ShortKitFeed.tsx +3 -0
- package/src/ShortKitPlayer.tsx +25 -15
- package/src/ShortKitWidget.tsx +24 -18
- package/src/index.ts +1 -0
- package/src/serialization.ts +38 -0
- package/src/specs/NativeShortKitModule.ts +1 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
embedWidgetIfNeeded()
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
override fun onDetachedFromWindow() {
|
|
46
60
|
super.onDetachedFromWindow()
|
|
61
|
+
removeWidget()
|
|
47
62
|
}
|
|
48
63
|
|
|
49
|
-
private fun
|
|
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,
|
|
73
|
+
view.initialize(sdk, parsedConfig, parsedItems)
|
|
59
74
|
addView(view)
|
|
60
75
|
widgetView = view
|
|
61
|
-
|
|
62
|
-
applyItems()
|
|
63
76
|
}
|
|
64
77
|
|
|
65
|
-
private fun
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
|
133
|
+
val out = mutableListOf<WidgetInput>()
|
|
114
134
|
for (i in 0 until arr.length()) {
|
|
115
135
|
val obj = arr.getJSONObject(i)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
144
|
+
out
|
|
133
145
|
} catch (_: Exception) {
|
|
134
146
|
null
|
|
135
147
|
}
|
package/ios/ShortKitBridge.swift
CHANGED
|
@@ -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
|
-
//
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
package/ios/ShortKitModule.mm
CHANGED
|
@@ -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.
|
|
14
|
+
<string>0.2.28</string>
|
|
15
15
|
<key>CFBundleShortVersionString</key>
|
|
16
|
-
<string>0.2.
|
|
16
|
+
<string>0.2.28</string>
|
|
17
17
|
<key>MinimumOSVersion</key>
|
|
18
18
|
<string>16.0</string>
|
|
19
19
|
</dict>
|