@shortkitsdk/react-native 0.2.34 → 0.2.36

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 (52) hide show
  1. package/android/build.gradle.kts +8 -0
  2. package/android/libs/shortkit-release.aar +0 -0
  3. package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +100 -47
  4. package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +54 -8
  5. package/android/src/main/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHost.kt +240 -27
  6. package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +151 -1
  7. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +135 -6
  8. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +15 -0
  9. package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +21 -11
  10. package/android/src/main/java/com/shortkit/reactnative/ShortKitPackage.kt +0 -2
  11. package/android/src/test/java/com/shortkit/reactnative/ReactCarouselOverlayHostEmitTest.kt +134 -0
  12. package/android/src/test/java/com/shortkit/reactnative/ReactOverlayHostDragTest.kt +45 -0
  13. package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostDragTest.kt +69 -0
  14. package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostEmitTest.kt +144 -0
  15. package/android/src/test/java/com/shortkit/reactnative/ShortKitFeedViewActivePropTest.kt +57 -0
  16. package/ios/ReactOverlayHost.swift +10 -8
  17. package/ios/ReactVideoCarouselOverlayHost.swift +6 -5
  18. package/ios/ShortKitBridge.swift +18 -0
  19. package/ios/ShortKitModule.mm +5 -0
  20. package/ios/ShortKitPlayerNativeView.swift +36 -0
  21. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
  22. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +1252 -82
  23. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +28 -2
  24. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  25. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +28 -2
  26. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  27. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
  28. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
  29. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1252 -82
  30. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +28 -2
  31. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  32. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +28 -2
  33. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +1252 -82
  34. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +28 -2
  35. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  36. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +28 -2
  37. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  38. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
  39. package/ios/ShortKitWidgetNativeView.swift +30 -3
  40. package/ios/ShortKitWidgetNativeViewManager.mm +1 -0
  41. package/package.json +1 -1
  42. package/src/ShortKitCommands.ts +20 -0
  43. package/src/ShortKitFeed.tsx +21 -0
  44. package/src/ShortKitPlayer.tsx +20 -1
  45. package/src/ShortKitWidget.tsx +63 -15
  46. package/src/specs/NativeShortKitModule.ts +10 -0
  47. package/src/specs/ShortKitWidgetViewNativeComponent.ts +15 -3
  48. package/src/types.ts +40 -0
  49. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +0 -149
  50. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerViewManager.kt +0 -35
  51. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +0 -149
  52. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetViewManager.kt +0 -30
package/src/types.ts CHANGED
@@ -32,6 +32,11 @@ export interface FeedConfig {
32
32
  autoplay?: boolean;
33
33
  filter?: FeedFilter;
34
34
  pullToRefreshEnabled?: boolean;
35
+ /** Called when the modal feed presented from `<ShortKitWidget>` /
36
+ * `<ShortKitPlayer>` fetches content items. Use to pre-fetch your own
37
+ * metadata for the items shown in the modal. Mirrors the same callback
38
+ * on `<ShortKitFeed>` props. */
39
+ onDidFetchContentItems?: (items: ContentItem[]) => void;
35
40
  }
36
41
 
37
42
  export type FeedHeight =
@@ -139,6 +144,12 @@ export interface ImageCarouselItem {
139
144
  export interface VideoCarouselItem {
140
145
  id: string;
141
146
  videos: ContentItem[];
147
+ /**
148
+ * Zero-based index of the page the carousel landed on. Mirrors
149
+ * `VideoCarouselInput.initialPageIndex` from the host's input. Available
150
+ * to overlay components for reading the active page on first paint.
151
+ */
152
+ initialPageIndex?: number;
142
153
  title?: string;
143
154
  description?: string;
144
155
  author?: string;
@@ -170,6 +181,18 @@ export interface VideoCarouselVideoInput {
170
181
  export interface VideoCarouselInput {
171
182
  id: string;
172
183
  videos: VideoCarouselVideoInput[];
184
+ /**
185
+ * Zero-based index of the page the carousel should land on the first time
186
+ * it becomes visible (vertical-arrival or initial mount). Defaults to 0.
187
+ * Out-of-range values are clamped to a valid index.
188
+ *
189
+ * **Has no effect after the carousel is first configured.** To move the
190
+ * user imperatively after landing, use
191
+ * `useShortKitCarousel().setActiveIndex(n)`. Re-rendering with a new
192
+ * `initialPageIndex` on the same `id` is ignored — change the `id` to force
193
+ * a re-bind if you genuinely want to land on a different page.
194
+ */
195
+ initialPageIndex?: number;
173
196
  title?: string;
174
197
  description?: string;
175
198
  author?: string;
@@ -421,6 +444,23 @@ export interface ShortKitFeedProps {
421
444
  index: number;
422
445
  pageIndex: number;
423
446
  }) => void;
447
+ /**
448
+ * Fires for the lifecycle of a video-carousel cell long-press: `began`
449
+ * after a short native press delay, `ended` on touch release, and
450
+ * `cancelled` on user finger movement past ~10pt or when the cell is
451
+ * recycled (e.g. by `setFeedItems`).
452
+ *
453
+ * The recognizer runs simultaneously with the carousel's horizontal pan
454
+ * and the feed's vertical pan — it does not block scrolling. The intended
455
+ * use case is Instagram-style hold-to-pause: pause playback and hide
456
+ * overlay chrome on `began`, restore them on `ended` / `cancelled`.
457
+ */
458
+ onVideoCarouselCellLongPress?: (event: {
459
+ id: string;
460
+ index: number;
461
+ pageIndex: number;
462
+ state: 'began' | 'ended' | 'cancelled';
463
+ }) => void;
424
464
  }
425
465
 
426
466
  /**
@@ -1,149 +0,0 @@
1
- package com.shortkit.reactnative
2
-
3
- import android.content.Context
4
- import android.view.MotionEvent
5
- import android.widget.FrameLayout
6
- import com.shortkit.sdk.config.PlayerClickAction
7
- import com.shortkit.sdk.config.FeedConfig
8
- import com.shortkit.sdk.config.PlayerConfig
9
- import com.shortkit.sdk.config.VideoOverlayMode
10
- import com.shortkit.sdk.model.ContentItem
11
- import com.shortkit.sdk.player.ShortKitPlayerView
12
- import org.json.JSONObject
13
-
14
- /**
15
- * Fabric native view wrapping [ShortKitPlayerView] for use as a
16
- * single-video player in React Native.
17
- */
18
- class ShortKitPlayerNativeView(context: Context) : FrameLayout(context) {
19
-
20
- private var playerView: ShortKitPlayerView? = null
21
- private var configJson: String? = null
22
- private var contentItemJson: String? = null
23
-
24
- var config: String?
25
- get() = configJson
26
- set(value) {
27
- if (value == configJson) return
28
- configJson = value
29
- rebuildIfNeeded()
30
- }
31
-
32
- var contentItem: String?
33
- get() = contentItemJson
34
- set(value) {
35
- if (value == contentItemJson) return
36
- contentItemJson = value
37
- applyContentItem()
38
- }
39
-
40
- var active: Boolean = true
41
- set(value) {
42
- if (field == value) return
43
- field = value
44
- applyActive()
45
- }
46
-
47
- override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
48
- return super.onInterceptTouchEvent(ev)
49
- }
50
-
51
- override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
52
- return super.dispatchTouchEvent(ev)
53
- }
54
-
55
- override fun onAttachedToWindow() {
56
- super.onAttachedToWindow()
57
- rebuildIfNeeded()
58
- }
59
-
60
- override fun onDetachedFromWindow() {
61
- playerView?.deactivate()
62
- super.onDetachedFromWindow()
63
- }
64
-
65
- private fun rebuildIfNeeded() {
66
- if (playerView != null) return
67
-
68
- val sdk = ShortKitBridge.shared?.sdk ?: return
69
- val playerConfig = parsePlayerConfig(configJson)
70
-
71
- val view = ShortKitPlayerView(context).apply {
72
- layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
73
- }
74
- view.initialize(sdk, playerConfig)
75
- addView(view)
76
- playerView = view
77
-
78
- applyContentItem()
79
- if (active) view.activate()
80
- }
81
-
82
- private fun applyContentItem() {
83
- val json = contentItemJson ?: return
84
- val view = playerView ?: return
85
- val item = parseContentItem(json) ?: return
86
- view.configure(item)
87
- }
88
-
89
- private fun applyActive() {
90
- val view = playerView ?: return
91
- if (active) view.activate() else view.deactivate()
92
- }
93
-
94
- private fun parsePlayerConfig(json: String?): PlayerConfig {
95
- if (json.isNullOrEmpty()) return PlayerConfig()
96
- return try {
97
- val obj = JSONObject(json)
98
- PlayerConfig(
99
- cornerRadius = obj.optDouble("cornerRadius", 12.0).toFloat(),
100
- clickAction = when (obj.optString("clickAction", "feed")) {
101
- "feed" -> PlayerClickAction.FEED
102
- "mute" -> PlayerClickAction.MUTE
103
- "none" -> PlayerClickAction.NONE
104
- else -> PlayerClickAction.FEED
105
- },
106
- autoplay = obj.optBoolean("autoplay", true),
107
- loop = obj.optBoolean("loop", true),
108
- muteOnStart = obj.optBoolean("muteOnStart", true),
109
- videoOverlay = parseOverlay(obj),
110
- feedConfig = obj.optString("feedConfig", "").let { fcStr ->
111
- if (fcStr.isNotEmpty()) ShortKitBridge.parseFeedConfig(fcStr, context) else FeedConfig()
112
- },
113
- )
114
- } catch (_: Exception) {
115
- PlayerConfig()
116
- }
117
- }
118
-
119
- private fun parseOverlay(obj: JSONObject): VideoOverlayMode {
120
- val overlay = obj.opt("overlay") ?: return VideoOverlayMode.None
121
- if (overlay is JSONObject && overlay.optString("type") == "custom") {
122
- return VideoOverlayMode.Custom {
123
- ReactOverlayHost(context)
124
- }
125
- }
126
- return VideoOverlayMode.None
127
- }
128
-
129
- private fun parseContentItem(json: String): ContentItem? {
130
- return try {
131
- val obj = JSONObject(json)
132
- ContentItem(
133
- id = obj.getString("id"),
134
- title = obj.getString("title"),
135
- description = obj.optString("description", null),
136
- duration = obj.getDouble("duration"),
137
- streamingUrl = obj.getString("streamingUrl"),
138
- thumbnailUrl = obj.getString("thumbnailUrl"),
139
- captionTracks = emptyList(),
140
- customMetadata = null,
141
- author = obj.optString("author", null),
142
- articleUrl = obj.optString("articleUrl", null),
143
- commentCount = if (obj.has("commentCount")) obj.getInt("commentCount") else null,
144
- )
145
- } catch (_: Exception) {
146
- null
147
- }
148
- }
149
- }
@@ -1,35 +0,0 @@
1
- package com.shortkit.reactnative
2
-
3
- import com.facebook.react.module.annotations.ReactModule
4
- import com.facebook.react.uimanager.SimpleViewManager
5
- import com.facebook.react.uimanager.ThemedReactContext
6
- import com.facebook.react.uimanager.annotations.ReactProp
7
-
8
- @ReactModule(name = ShortKitPlayerViewManager.REACT_CLASS)
9
- class ShortKitPlayerViewManager : SimpleViewManager<ShortKitPlayerNativeView>() {
10
-
11
- override fun getName(): String = REACT_CLASS
12
-
13
- override fun createViewInstance(context: ThemedReactContext): ShortKitPlayerNativeView {
14
- return ShortKitPlayerNativeView(context)
15
- }
16
-
17
- @ReactProp(name = "config")
18
- fun setConfig(view: ShortKitPlayerNativeView, config: String?) {
19
- view.config = config
20
- }
21
-
22
- @ReactProp(name = "contentItem")
23
- fun setContentItem(view: ShortKitPlayerNativeView, contentItem: String?) {
24
- view.contentItem = contentItem
25
- }
26
-
27
- @ReactProp(name = "active", defaultBoolean = true)
28
- fun setActive(view: ShortKitPlayerNativeView, active: Boolean) {
29
- view.active = active
30
- }
31
-
32
- companion object {
33
- const val REACT_CLASS = "ShortKitPlayerView"
34
- }
35
- }
@@ -1,149 +0,0 @@
1
- package com.shortkit.reactnative
2
-
3
- import android.content.Context
4
- import android.widget.FrameLayout
5
- import com.shortkit.sdk.config.PlayerClickAction
6
- import com.shortkit.sdk.config.VideoOverlayMode
7
- import com.shortkit.sdk.config.WidgetConfig
8
- import com.shortkit.sdk.widget.ShortKitWidgetView
9
- import com.shortkit.sdk.config.FeedConfig
10
- import com.shortkit.sdk.model.FeedFilter
11
- import com.shortkit.sdk.model.WidgetInput
12
- import org.json.JSONArray
13
- import org.json.JSONObject
14
-
15
- /**
16
- * Fabric native view wrapping [ShortKitWidgetView] for use as a
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)
22
- */
23
- class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
24
-
25
- private var widgetView: ShortKitWidgetView? = null
26
- private var configJson: String? = null
27
- private var itemsJson: String? = null
28
- private var parsedConfig: WidgetConfig = WidgetConfig()
29
- private var parsedItems: List<WidgetInput> = emptyList()
30
-
31
- var config: String?
32
- get() = configJson
33
- set(value) {
34
- if (value == configJson) return
35
- configJson = value
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
- }
42
- }
43
-
44
- var items: String?
45
- get() = itemsJson
46
- set(value) {
47
- if (value == itemsJson) return
48
- itemsJson = value
49
- parsedItems = parseWidgetInputs(value) ?: emptyList()
50
- // Post-mount update on an existing widget.
51
- widgetView?.configure(parsedItems)
52
- }
53
-
54
- override fun onAttachedToWindow() {
55
- super.onAttachedToWindow()
56
- embedWidgetIfNeeded()
57
- }
58
-
59
- override fun onDetachedFromWindow() {
60
- super.onDetachedFromWindow()
61
- removeWidget()
62
- }
63
-
64
- private fun embedWidgetIfNeeded() {
65
- if (widgetView != null) return
66
- val sdk = ShortKitBridge.shared?.sdk ?: return
67
-
68
- // Pass items at initialize time so the widget never races the server
69
- // fetch — analogous to the feed's `feedItems` prop wiring.
70
- val view = ShortKitWidgetView(context).apply {
71
- layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
72
- }
73
- view.initialize(sdk, parsedConfig, parsedItems)
74
- addView(view)
75
- widgetView = view
76
- }
77
-
78
- private fun removeWidget() {
79
- widgetView?.let { removeView(it) }
80
- widgetView = null
81
- }
82
-
83
- private fun parseWidgetConfig(json: String?): WidgetConfig {
84
- if (json.isNullOrEmpty()) return WidgetConfig()
85
- return try {
86
- val obj = JSONObject(json)
87
- WidgetConfig(
88
- cardCount = obj.optInt("cardCount", 3),
89
- cardSpacing = obj.optDouble("cardSpacing", 8.0).toFloat(),
90
- cornerRadius = obj.optDouble("cornerRadius", 12.0).toFloat(),
91
- autoplay = obj.optBoolean("autoplay", true),
92
- muteOnStart = obj.optBoolean("muteOnStart", true),
93
- loop = obj.optBoolean("loop", true),
94
- rotationInterval = obj.optLong("rotationInterval", 10_000L),
95
- clickAction = when (obj.optString("clickAction", "feed")) {
96
- "feed" -> PlayerClickAction.FEED
97
- "mute" -> PlayerClickAction.MUTE
98
- "none" -> PlayerClickAction.NONE
99
- else -> PlayerClickAction.FEED
100
- },
101
- cardOverlay = parseOverlay(obj),
102
- filter = obj.optString("filter", "").let { filterStr ->
103
- if (filterStr.isNotEmpty()) ShortKitBridge.parseFeedFilterToModel(filterStr) else null
104
- },
105
- feedConfig = obj.optString("feedConfig", "").let { fcStr ->
106
- if (fcStr.isNotEmpty()) ShortKitBridge.parseFeedConfig(fcStr, context) else FeedConfig()
107
- },
108
- )
109
- } catch (_: Exception) {
110
- WidgetConfig()
111
- }
112
- }
113
-
114
- private fun parseOverlay(obj: JSONObject): VideoOverlayMode {
115
- val overlay = obj.opt("overlay") ?: return VideoOverlayMode.None
116
- if (overlay is JSONObject && overlay.optString("type") == "custom") {
117
- return VideoOverlayMode.Custom {
118
- ReactOverlayHost(context)
119
- }
120
- }
121
- return VideoOverlayMode.None
122
- }
123
-
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
131
- return try {
132
- val arr = JSONArray(json)
133
- val out = mutableListOf<WidgetInput>()
134
- for (i in 0 until arr.length()) {
135
- val obj = arr.getJSONObject(i)
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))
143
- }
144
- out
145
- } catch (_: Exception) {
146
- null
147
- }
148
- }
149
- }
@@ -1,30 +0,0 @@
1
- package com.shortkit.reactnative
2
-
3
- import com.facebook.react.module.annotations.ReactModule
4
- import com.facebook.react.uimanager.SimpleViewManager
5
- import com.facebook.react.uimanager.ThemedReactContext
6
- import com.facebook.react.uimanager.annotations.ReactProp
7
-
8
- @ReactModule(name = ShortKitWidgetViewManager.REACT_CLASS)
9
- class ShortKitWidgetViewManager : SimpleViewManager<ShortKitWidgetNativeView>() {
10
-
11
- override fun getName(): String = REACT_CLASS
12
-
13
- override fun createViewInstance(context: ThemedReactContext): ShortKitWidgetNativeView {
14
- return ShortKitWidgetNativeView(context)
15
- }
16
-
17
- @ReactProp(name = "config")
18
- fun setConfig(view: ShortKitWidgetNativeView, config: String?) {
19
- view.config = config
20
- }
21
-
22
- @ReactProp(name = "items")
23
- fun setItems(view: ShortKitWidgetNativeView, items: String?) {
24
- view.items = items
25
- }
26
-
27
- companion object {
28
- const val REACT_CLASS = "ShortKitWidgetView"
29
- }
30
- }