@shortkitsdk/react-native 0.2.34 → 0.2.35

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 (35) hide show
  1. package/android/libs/shortkit-release.aar +0 -0
  2. package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +6 -1
  3. package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +8 -1
  4. package/android/src/main/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHost.kt +7 -0
  5. package/android/src/main/java/com/shortkit/reactnative/ShortKitPackage.kt +0 -2
  6. package/ios/ShortKitSDK.xcframework/Info.plist +5 -5
  7. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
  8. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +357 -35
  9. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +2 -0
  10. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  11. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +2 -0
  12. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  13. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
  14. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
  15. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +357 -35
  16. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +2 -0
  17. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  18. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +2 -0
  19. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +357 -35
  20. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +2 -0
  21. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  22. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +2 -0
  23. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  24. package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
  25. package/ios/ShortKitWidgetNativeView.swift +30 -3
  26. package/ios/ShortKitWidgetNativeViewManager.mm +1 -0
  27. package/package.json +1 -1
  28. package/src/ShortKitPlayer.tsx +20 -1
  29. package/src/ShortKitWidget.tsx +63 -15
  30. package/src/specs/ShortKitWidgetViewNativeComponent.ts +15 -3
  31. package/src/types.ts +5 -0
  32. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +0 -149
  33. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerViewManager.kt +0 -35
  34. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +0 -149
  35. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetViewManager.kt +0 -30
@@ -10,39 +10,39 @@
10
10
  </data>
11
11
  <key>Info.plist</key>
12
12
  <data>
13
- kXOjURx+Z1d6/6VN46mtXJY4tGs=
13
+ OvrkhBvYgEeQNGBIL8Sk+zwmd+8=
14
14
  </data>
15
15
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json</key>
16
16
  <data>
17
- IFMuyXGMCXuNezRhYjKHLUu9xA4=
17
+ gUMbcAXcwCbvhUG1oUpN35PTUFU=
18
18
  </data>
19
19
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
20
20
  <data>
21
- v3GW9Gub1dJiosTgJ/z5NwmgWnY=
21
+ h6Cg/BD9N7YWvWXoaZDHIvsBs+Q=
22
22
  </data>
23
23
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
24
24
  <data>
25
- 0oQnp4oNo+Ztwgww53FIZhz0zfM=
25
+ WN66xa1ykIuY1q8/q3xX14rO1qU=
26
26
  </data>
27
27
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface</key>
28
28
  <data>
29
- v3GW9Gub1dJiosTgJ/z5NwmgWnY=
29
+ h6Cg/BD9N7YWvWXoaZDHIvsBs+Q=
30
30
  </data>
31
31
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
32
32
  <data>
33
- IFMuyXGMCXuNezRhYjKHLUu9xA4=
33
+ gUMbcAXcwCbvhUG1oUpN35PTUFU=
34
34
  </data>
35
35
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
36
36
  <data>
37
- Duv2bgxehsG2ZV4wnlh7s8/WzFk=
37
+ MiQorgvuaKDH14aSEh9ZJVYQfjo=
38
38
  </data>
39
39
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
40
40
  <data>
41
- Cu/J3KUQmcbxz8bCf9NQZ5aVBTM=
41
+ K085KBZpASu5rTlrMsWTnd618bw=
42
42
  </data>
43
43
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
44
44
  <data>
45
- Duv2bgxehsG2ZV4wnlh7s8/WzFk=
45
+ MiQorgvuaKDH14aSEh9ZJVYQfjo=
46
46
  </data>
47
47
  <key>Modules/module.modulemap</key>
48
48
  <data>
@@ -66,56 +66,56 @@
66
66
  <dict>
67
67
  <key>hash2</key>
68
68
  <data>
69
- N/7VIAsF7mG+qU52BV03Pbvk+uQA/UoDg7z3aLRvEm4=
69
+ 98/sM6XrtDCNV+236cKUe+xeL8df0JeaP/P+lEE01ew=
70
70
  </data>
71
71
  </dict>
72
72
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
73
73
  <dict>
74
74
  <key>hash2</key>
75
75
  <data>
76
- HHpbkQ91Av7qOwHoz6asjSQ4d9TzPj7Lg+sxPVOhA30=
76
+ znlbvvD4pnPCRu9Aei74T4NdRxjdD2cIswo6AC4CsTA=
77
77
  </data>
78
78
  </dict>
79
79
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
80
80
  <dict>
81
81
  <key>hash2</key>
82
82
  <data>
83
- pbR9S11lEANUEhT1e5NkK796Tecps3q6SosOoeuApuQ=
83
+ Bn3ZIMIwiUnv4XCjNMicV7dy8dh3SrBhja0tyxB9BhU=
84
84
  </data>
85
85
  </dict>
86
86
  <key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface</key>
87
87
  <dict>
88
88
  <key>hash2</key>
89
89
  <data>
90
- HHpbkQ91Av7qOwHoz6asjSQ4d9TzPj7Lg+sxPVOhA30=
90
+ znlbvvD4pnPCRu9Aei74T4NdRxjdD2cIswo6AC4CsTA=
91
91
  </data>
92
92
  </dict>
93
93
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
94
94
  <dict>
95
95
  <key>hash2</key>
96
96
  <data>
97
- N/7VIAsF7mG+qU52BV03Pbvk+uQA/UoDg7z3aLRvEm4=
97
+ 98/sM6XrtDCNV+236cKUe+xeL8df0JeaP/P+lEE01ew=
98
98
  </data>
99
99
  </dict>
100
100
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
101
101
  <dict>
102
102
  <key>hash2</key>
103
103
  <data>
104
- 6nFBIiTjaP5xmdLc7I/x9j4ASiT2UjNnxKmRYUGmLXw=
104
+ xoM3qxlgST57eK/vJqzmI4rFvll9ZmfMr4+El60Pq/c=
105
105
  </data>
106
106
  </dict>
107
107
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
108
108
  <dict>
109
109
  <key>hash2</key>
110
110
  <data>
111
- Yx9LmEqOq+aeF+mSiKZyfw0FW/BySAPRrQMVzDyEiUU=
111
+ IjhLI8nlyrN3P1GlQbXjVHfr4vkxgKw5OqJNmlk0Fv4=
112
112
  </data>
113
113
  </dict>
114
114
  <key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
115
115
  <dict>
116
116
  <key>hash2</key>
117
117
  <data>
118
- 6nFBIiTjaP5xmdLc7I/x9j4ASiT2UjNnxKmRYUGmLXw=
118
+ xoM3qxlgST57eK/vJqzmI4rFvll9ZmfMr4+El60Pq/c=
119
119
  </data>
120
120
  </dict>
121
121
  <key>Modules/module.modulemap</key>
@@ -25,12 +25,24 @@ import ShortKitSDK
25
25
  }
26
26
  }
27
27
 
28
- /// Stable per-instance widget identifier set by the JS wrapper. Echoed
28
+ /// Stable per-instance widget identifier set by the JS wrapper when the
29
+ /// host has wired `onCardTap` and wants to handle taps itself. Echoed
29
30
  /// back on `onWidgetCardTap` events so the JS wrapper can route the
30
- /// global emit to the correct `<ShortKitWidget>` instance when more than
31
- /// one is mounted simultaneously.
31
+ /// global emit to the correct `<ShortKitWidget>` instance when more
32
+ /// than one is mounted simultaneously. Presence of this id signals
33
+ /// "host takes over taps" — the native side wires `vc.onCardTap`
34
+ /// which makes the SDK skip its built-in modal-present path.
32
35
  @objc public var widgetId: String?
33
36
 
37
+ /// Stable per-instance modal-feed identifier set by the JS wrapper when
38
+ /// the host has wired per-feed callbacks (e.g.
39
+ /// `feedConfig.onDidFetchContentItems`) and wants to receive scoped
40
+ /// events from the SDK's built-in modal feed. Independent from
41
+ /// `widgetId` — the host can wire modal callbacks WITHOUT taking over
42
+ /// taps. Used as the `feedId` under which the modal feed VC is
43
+ /// registered with the bridge on present.
44
+ @objc public var modalFeedId: String?
45
+
34
46
  /// Mirrors `ShortKitWidgetViewController.active`. Hosts use this to
35
47
  /// release the widget's pool tile when it shouldn't be playing
36
48
  /// (e.g. inactive tab, scrolled off-screen). Defaults to `true` so
@@ -172,6 +184,21 @@ import ShortKitSDK
172
184
  }
173
185
  }
174
186
 
187
+ // Register the SDK's built-in modal feed VC with the bridge under
188
+ // the JS-supplied `modalFeedId` so per-feed callbacks wired on the
189
+ // widget's `feedConfig` (e.g. `onDidFetchContentItems`) get scoped
190
+ // attribution. The JS wrapper supplies this id only when at least
191
+ // one such callback is set; absent it, the modal still presents
192
+ // and runs normally — events just emit with empty feedId.
193
+ if let id = modalFeedId {
194
+ vc.onModalFeedPresented = { feedVC in
195
+ ShortKitBridge.shared?.registerFeed(id: id, viewController: feedVC)
196
+ }
197
+ vc.onModalFeedDismissed = {
198
+ ShortKitBridge.shared?.unregisterFeed(id: id)
199
+ }
200
+ }
201
+
175
202
  parentVC.addChild(vc)
176
203
  vc.view.frame = bounds
177
204
  vc.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
@@ -24,6 +24,7 @@ RCT_EXPORT_MODULE(ShortKitWidgetView)
24
24
  RCT_EXPORT_VIEW_PROPERTY(config, NSString)
25
25
  RCT_EXPORT_VIEW_PROPERTY(items, NSString)
26
26
  RCT_EXPORT_VIEW_PROPERTY(widgetId, NSString)
27
+ RCT_EXPORT_VIEW_PROPERTY(modalFeedId, NSString)
27
28
  RCT_EXPORT_VIEW_PROPERTY(active, BOOL)
28
29
  RCT_EXPORT_VIEW_PROPERTY(feedItems, NSString)
29
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shortkitsdk/react-native",
3
- "version": "0.2.34",
3
+ "version": "0.2.35",
4
4
  "description": "ShortKit React Native SDK — short-form video feed",
5
5
  "react-native": "src/index",
6
6
  "source": "src/index",
@@ -1,5 +1,5 @@
1
1
  import React, { useLayoutEffect, useMemo } from 'react';
2
- import { View, StyleSheet } from 'react-native';
2
+ import { Platform, View, StyleSheet } from 'react-native';
3
3
  import type { ShortKitPlayerProps } from './types';
4
4
  import ShortKitPlayerView from './specs/ShortKitPlayerViewNativeComponent';
5
5
  import { serializePlayerConfig } from './serialization';
@@ -8,6 +8,11 @@ import { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurfa
8
8
  import { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOverlaySurface';
9
9
  import { registerFeedMaskComponent } from './ShortKitFeedMaskSurface';
10
10
 
11
+ // One-time dev warning when ShortKitPlayer is rendered on Android. The
12
+ // Android SDK was scoped down to feed-only in #246; the JS surface stays
13
+ // for iOS but renders an empty container on Android.
14
+ let didWarnAndroidPlayerUnavailable = false;
15
+
11
16
  /**
12
17
  * Single-video player component. Displays one video with thumbnail fallback
13
18
  * and optional overlay. Wraps a native Fabric view.
@@ -75,6 +80,20 @@ export function ShortKitPlayer(props: ShortKitPlayerProps) {
75
80
  // is fully invisible to hit testing.
76
81
  const nativeTouchPassthrough = clickAction === 'none';
77
82
 
83
+ // Android: the native ViewManager backing ShortKitPlayer was removed in
84
+ // #246 (feed-only scope-down). The JS surface stays so cross-platform
85
+ // host code compiles, but renders an empty container.
86
+ if (Platform.OS === 'android') {
87
+ if (__DEV__ && !didWarnAndroidPlayerUnavailable) {
88
+ didWarnAndroidPlayerUnavailable = true;
89
+ // eslint-disable-next-line no-console
90
+ console.warn(
91
+ '[ShortKit] <ShortKitPlayer> is not supported on Android — rendering an empty container.',
92
+ );
93
+ }
94
+ return <View style={[styles.container, style]} />;
95
+ }
96
+
78
97
  return (
79
98
  <View
80
99
  style={[styles.container, style]}
@@ -1,5 +1,5 @@
1
1
  import React, { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
2
- import { View, StyleSheet } from 'react-native';
2
+ import { Platform, View, StyleSheet } from 'react-native';
3
3
  import type { ShortKitWidgetProps } from './types';
4
4
  import ShortKitWidgetView from './specs/ShortKitWidgetViewNativeComponent';
5
5
  import NativeShortKitModule from './specs/NativeShortKitModule';
@@ -10,6 +10,11 @@ import { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurfa
10
10
  import { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOverlaySurface';
11
11
  import { registerFeedMaskComponent } from './ShortKitFeedMaskSurface';
12
12
 
13
+ // One-time dev warning when ShortKitWidget is rendered on Android. The
14
+ // Android SDK was scoped down to feed-only in #246; the JS surface stays
15
+ // for iOS but renders an empty container on Android.
16
+ let didWarnAndroidWidgetUnavailable = false;
17
+
13
18
  // Local UUID generator. We don't pull in a runtime dep just for this; a
14
19
  // timestamp + random suffix is more than unique enough to disambiguate
15
20
  // concurrently-mounted widgets within a single app session.
@@ -31,28 +36,36 @@ export function ShortKitWidget(props: ShortKitWidgetProps) {
31
36
  throw new Error('ShortKitWidget must be used within a ShortKitProvider');
32
37
  }
33
38
 
34
- // Stable widget ID for the lifetime of this component, ONLY when the host
35
- // wires `onCardTap`. Skipping it when unset means the native side leaves
36
- // `widgetVC.onCardTap` nil, which means the SDK's existing
37
- // `clickAction: 'feed'` modal-presentation path stays intact for hosts
38
- // that don't opt in. Backwards compatible.
39
+ // Stable widget ID, generated only when the host wires `onCardTap`.
40
+ // Presence on the native side signals "host handles taps" and the SDK
41
+ // skips its built-in modal-presentation path. Backwards compatible —
42
+ // hosts who don't wire `onCardTap` see no behavior change.
39
43
  const widgetId = useMemo(
40
44
  () => (onCardTap ? generateWidgetId() : undefined),
41
- // We deliberately compute this once per "host wired callback" lifetime,
42
- // not per render. The boolean test on first render is what matters; if
43
- // the host swaps onCardTap from set → unset later we keep the same
44
- // widgetId (harmless — no events will fire because the JS subscription
45
- // below tears down). Going the other way (unset → set), we generate a
46
- // fresh id then.
47
45
  // eslint-disable-next-line react-hooks/exhaustive-deps
48
46
  [Boolean(onCardTap)],
49
47
  );
50
48
 
51
- // Keep a stable ref to the latest onCardTap so the subscription doesn't
52
- // need to tear down + rebuild every time the host re-renders with a new
53
- // closure identity.
49
+ // Stable modal-feed ID, generated only when the host wires per-feed
50
+ // callbacks on the expanded modal feed (currently
51
+ // `feedConfig.onDidFetchContentItems`). Independent from `widgetId` —
52
+ // hosts can subscribe to modal events without taking over taps. The
53
+ // native side registers the modal feed VC under this id so emits get
54
+ // scoped back to this widget's callbacks.
55
+ const onDidFetchContentItems = config?.feedConfig?.onDidFetchContentItems;
56
+ const modalFeedId = useMemo(
57
+ () => (onDidFetchContentItems ? generateWidgetId() : undefined),
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ [Boolean(onDidFetchContentItems)],
60
+ );
61
+
62
+ // Keep stable refs to the latest callbacks so subscriptions don't need to
63
+ // tear down + rebuild every time the host re-renders with a new closure
64
+ // identity.
54
65
  const onCardTapRef = useRef(onCardTap);
55
66
  onCardTapRef.current = onCardTap;
67
+ const onDidFetchContentItemsRef = useRef(onDidFetchContentItems);
68
+ onDidFetchContentItemsRef.current = onDidFetchContentItems;
56
69
 
57
70
  useLayoutEffect(() => {
58
71
  if (config?.overlay && config.overlay !== 'none') {
@@ -88,6 +101,26 @@ export function ShortKitWidget(props: ShortKitWidgetProps) {
88
101
  return () => subscription.remove();
89
102
  }, [widgetId, onCardTap]);
90
103
 
104
+ // Subscribe to `onDidFetchContentItems` scoped to this widget's modal
105
+ // feed. Native side registers the modal feed VC under `modalFeedId` on
106
+ // present and emits with that feedId; filter to it and route to the
107
+ // host callback wired on `feedConfig.onDidFetchContentItems`.
108
+ useEffect(() => {
109
+ if (!NativeShortKitModule || !modalFeedId || !onDidFetchContentItems) return;
110
+
111
+ const subscription = NativeShortKitModule.onDidFetchContentItems((event) => {
112
+ if (event.feedId !== modalFeedId) return;
113
+ try {
114
+ const items = JSON.parse(event.items);
115
+ onDidFetchContentItemsRef.current?.(items);
116
+ } catch {
117
+ // ignore malformed JSON
118
+ }
119
+ });
120
+
121
+ return () => subscription.remove();
122
+ }, [modalFeedId, onDidFetchContentItems]);
123
+
91
124
  const serializedConfig = useMemo(() => {
92
125
  return serializeWidgetConfig(config ?? {});
93
126
  }, [config]);
@@ -105,6 +138,20 @@ export function ShortKitWidget(props: ShortKitWidgetProps) {
105
138
  return JSON.stringify(feedItems);
106
139
  }, [feedItems]);
107
140
 
141
+ // Android: the native ViewManager backing ShortKitWidget was removed in
142
+ // #246 (feed-only scope-down). The JS surface stays so cross-platform
143
+ // host code compiles, but renders an empty container.
144
+ if (Platform.OS === 'android') {
145
+ if (__DEV__ && !didWarnAndroidWidgetUnavailable) {
146
+ didWarnAndroidWidgetUnavailable = true;
147
+ // eslint-disable-next-line no-console
148
+ console.warn(
149
+ '[ShortKit] <ShortKitWidget> is not supported on Android — rendering an empty container.',
150
+ );
151
+ }
152
+ return <View style={[styles.container, style]} />;
153
+ }
154
+
108
155
  return (
109
156
  <View style={[styles.container, style]}>
110
157
  <ShortKitWidgetView
@@ -112,6 +159,7 @@ export function ShortKitWidget(props: ShortKitWidgetProps) {
112
159
  config={serializedConfig}
113
160
  items={serializedItems}
114
161
  widgetId={widgetId}
162
+ modalFeedId={modalFeedId}
115
163
  active={active}
116
164
  feedItems={serializedFeedItems}
117
165
  />
@@ -5,11 +5,23 @@ export interface NativeProps extends ViewProps {
5
5
  config: string;
6
6
  items?: string;
7
7
  /**
8
- * Stable per-instance widget identifier. Used to route the global
9
- * `NativeShortKitModule.onWidgetCardTap` event back to the originating
10
- * `<ShortKitWidget>` when multiple widgets are mounted.
8
+ * Stable per-instance widget identifier. Set only when the host has
9
+ * wired `onCardTap` its presence signals "host handles taps" to the
10
+ * native side. Echoed back on `onWidgetCardTap` events so the JS
11
+ * wrapper can route the global emit to the correct `<ShortKitWidget>`
12
+ * instance when more than one is mounted.
11
13
  */
12
14
  widgetId?: string;
15
+ /**
16
+ * Stable per-instance modal-feed identifier. Set only when the host
17
+ * has wired per-feed callbacks (e.g.
18
+ * `feedConfig.onDidFetchContentItems`). The native side registers the
19
+ * SDK's built-in modal feed VC with the bridge under this id so events
20
+ * fired by the modal can be scoped back to the correct widget's
21
+ * callbacks. Independent from `widgetId` — a host can wire modal
22
+ * callbacks without taking over taps.
23
+ */
24
+ modalFeedId?: string;
13
25
  /**
14
26
  * Whether the widget is currently allowed to play. Defaults to true on
15
27
  * the native side. Hosts use this to release the widget's pool tile
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 =
@@ -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
- }