@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.
Files changed (75) hide show
  1. package/ShortKitReactNative.podspec +1 -0
  2. package/android/build.gradle.kts +5 -1
  3. package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +319 -0
  4. package/android/src/main/java/com/shortkit/reactnative/ReactLoadingHost.kt +40 -0
  5. package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +559 -0
  6. package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +984 -0
  7. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +88 -220
  8. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +12 -3
  9. package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +126 -706
  10. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +2 -2
  11. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +2 -2
  12. package/ios/ReactCarouselOverlayHost.swift +177 -0
  13. package/ios/ReactLoadingHost.swift +38 -0
  14. package/ios/ReactOverlayHost.swift +458 -0
  15. package/ios/SKFabricSurfaceWrapper.h +18 -0
  16. package/ios/SKFabricSurfaceWrapper.mm +57 -0
  17. package/ios/ShortKitBridge.swift +266 -65
  18. package/ios/ShortKitFeedView.swift +63 -207
  19. package/ios/ShortKitFeedViewManager.mm +3 -2
  20. package/ios/ShortKitModule.mm +86 -32
  21. package/ios/ShortKitPlayerNativeView.swift +39 -8
  22. package/ios/ShortKitReactNative-Bridging-Header.h +2 -0
  23. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +2 -1
  24. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +3998 -962
  25. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +85 -24
  26. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  27. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +85 -24
  28. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  29. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +2 -1
  30. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +3998 -962
  31. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +85 -24
  32. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  33. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +85 -24
  34. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  35. package/ios/ShortKitSDK.xcframework.bak/Info.plist +43 -0
  36. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
  37. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Info.plist +16 -0
  38. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +28917 -0
  39. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +824 -0
  40. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  41. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +824 -0
  42. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +4 -0
  43. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  44. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
  45. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +16 -0
  46. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +28917 -0
  47. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +824 -0
  48. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  49. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +824 -0
  50. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +4 -0
  51. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  52. package/ios/ShortKitWidgetNativeView.swift +3 -3
  53. package/package.json +1 -1
  54. package/src/ShortKitCarouselOverlaySurface.tsx +55 -0
  55. package/src/ShortKitCommands.ts +31 -0
  56. package/src/ShortKitContext.ts +11 -25
  57. package/src/ShortKitFeed.tsx +110 -41
  58. package/src/ShortKitLoadingSurface.tsx +24 -0
  59. package/src/ShortKitOverlaySurface.tsx +205 -0
  60. package/src/ShortKitPlayer.tsx +6 -7
  61. package/src/ShortKitProvider.tsx +65 -250
  62. package/src/index.ts +9 -4
  63. package/src/serialization.ts +22 -42
  64. package/src/specs/NativeShortKitModule.ts +67 -53
  65. package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
  66. package/src/types.ts +104 -19
  67. package/src/useShortKit.ts +1 -3
  68. package/src/useShortKitPlayer.ts +7 -8
  69. package/android/src/main/java/com/shortkit/reactnative/ShortKitCarouselOverlayBridge.kt +0 -48
  70. package/android/src/main/java/com/shortkit/reactnative/ShortKitOverlayBridge.kt +0 -128
  71. package/ios/ShortKitCarouselOverlayBridge.swift +0 -54
  72. package/ios/ShortKitOverlayBridge.swift +0 -113
  73. package/src/CarouselOverlayManager.tsx +0 -71
  74. package/src/OverlayManager.tsx +0 -87
  75. package/src/useShortKitCarousel.ts +0 -29
@@ -6,21 +6,23 @@ import type { ShortKitContextValue } from './ShortKitContext';
6
6
  import type {
7
7
  ShortKitProviderProps,
8
8
  ContentItem,
9
- ImageCarouselItem,
10
- CustomFeedItem,
9
+ FeedConfig,
10
+ FeedFilter,
11
11
  PlayerTime,
12
12
  PlayerState,
13
13
  CaptionTrack,
14
+ FeedScrollPhase,
14
15
  ContentSignal,
16
+ StoryboardData,
15
17
  } from './types';
16
18
  import {
17
- serializeFeedConfigForModule,
18
- serializeCustomFeedItems,
19
+ serializeFeedConfig,
19
20
  deserializePlayerState,
20
21
  deserializeContentItem,
21
22
  deserializePlayerTime,
22
23
  } from './serialization';
23
24
  import NativeShortKitModule from './specs/NativeShortKitModule';
25
+ import { registerLoadingComponent } from './ShortKitLoadingSurface';
24
26
 
25
27
  // ---------------------------------------------------------------------------
26
28
  // State & Reducer
@@ -29,7 +31,6 @@ import NativeShortKitModule from './specs/NativeShortKitModule';
29
31
  interface State {
30
32
  playerState: PlayerState;
31
33
  currentItem: ContentItem | null;
32
- nextItem: ContentItem | null;
33
34
  time: PlayerTime;
34
35
  isMuted: boolean;
35
36
  playbackRate: number;
@@ -37,22 +38,13 @@ interface State {
37
38
  activeCaptionTrack: CaptionTrack | null;
38
39
  activeCue: { text: string; startTime: number; endTime: number } | null;
39
40
  prefetchedAheadCount: number;
40
- remainingContentCount: number;
41
41
  isActive: boolean;
42
- isTransitioning: boolean;
43
- lastOverlayTap: number;
44
- lastOverlayDoubleTap: { x: number; y: number; id: number } | null;
45
- currentCarouselItem: ImageCarouselItem | null;
46
- nextCarouselItem: ImageCarouselItem | null;
47
- isCarouselActive: boolean;
48
- isCarouselTransitioning: boolean;
49
- activeCellType: 'video' | 'carousel' | null;
42
+ feedScrollPhase: FeedScrollPhase | null;
50
43
  }
51
44
 
52
45
  const initialState: State = {
53
46
  playerState: 'idle',
54
47
  currentItem: null,
55
- nextItem: null,
56
48
  time: { current: 0, duration: 0, buffered: 0 },
57
49
  isMuted: true,
58
50
  playbackRate: 1.0,
@@ -60,16 +52,8 @@ const initialState: State = {
60
52
  activeCaptionTrack: null,
61
53
  activeCue: null,
62
54
  prefetchedAheadCount: 0,
63
- remainingContentCount: 0,
64
55
  isActive: false,
65
- isTransitioning: false,
66
- lastOverlayTap: 0,
67
- lastOverlayDoubleTap: null,
68
- currentCarouselItem: null,
69
- nextCarouselItem: null,
70
- isCarouselActive: false,
71
- isCarouselTransitioning: false,
72
- activeCellType: null,
56
+ feedScrollPhase: null,
73
57
  };
74
58
 
75
59
  type Action =
@@ -82,39 +66,20 @@ type Action =
82
66
  | { type: 'CAPTION_TRACK'; payload: CaptionTrack | null }
83
67
  | { type: 'CUE'; payload: { text: string; startTime: number; endTime: number } | null }
84
68
  | { type: 'PREFETCH_COUNT'; payload: number }
85
- | { type: 'REMAINING_COUNT'; payload: number }
86
- | { type: 'OVERLAY_CONFIGURE'; payload: ContentItem }
87
- | { type: 'OVERLAY_ACTIVATE'; payload: ContentItem }
88
- | { type: 'OVERLAY_RESET' }
89
- | { type: 'OVERLAY_FADE_OUT' }
90
- | { type: 'OVERLAY_RESTORE' }
91
- | { type: 'OVERLAY_TAP' }
92
- | { type: 'OVERLAY_DOUBLE_TAP'; payload: { x: number; y: number } }
93
- | { type: 'ACTIVE_CELL_TYPE'; payload: 'video' | 'carousel' }
94
- | { type: 'CAROUSEL_OVERLAY_CONFIGURE'; payload: ImageCarouselItem }
95
- | { type: 'CAROUSEL_OVERLAY_ACTIVATE'; payload: ImageCarouselItem }
96
- | { type: 'CAROUSEL_OVERLAY_RESET' }
97
- | { type: 'CAROUSEL_OVERLAY_FADE_OUT' }
98
- | { type: 'CAROUSEL_OVERLAY_RESTORE' };
69
+ | { type: 'FEED_SCROLL_PHASE'; payload: FeedScrollPhase };
99
70
 
100
71
  function reducer(state: State, action: Action): State {
101
72
  switch (action.type) {
102
73
  case 'PLAYER_STATE': {
103
- // Only allow isActive to transition TO true from player state.
104
- // Once active, the overlay stays mounted — hiding during transient
105
- // states like "loading" between videos would cause a visible flash.
106
74
  const isPlaybackActive =
107
75
  action.payload === 'playing' ||
108
76
  action.payload === 'paused' ||
109
77
  action.payload === 'buffering' ||
110
78
  action.payload === 'seeking';
111
- const becameActive = !state.isActive && isPlaybackActive;
112
79
  return {
113
80
  ...state,
114
81
  playerState: action.payload,
115
82
  isActive: state.isActive || isPlaybackActive,
116
- // First playback means a video cell is active (initial load)
117
- activeCellType: becameActive ? 'video' : state.activeCellType,
118
83
  };
119
84
  }
120
85
  case 'CURRENT_ITEM':
@@ -133,58 +98,8 @@ function reducer(state: State, action: Action): State {
133
98
  return { ...state, activeCue: action.payload };
134
99
  case 'PREFETCH_COUNT':
135
100
  return { ...state, prefetchedAheadCount: action.payload };
136
- case 'REMAINING_COUNT':
137
- return { ...state, remainingContentCount: action.payload };
138
- case 'OVERLAY_CONFIGURE':
139
- return { ...state, nextItem: action.payload };
140
- case 'OVERLAY_ACTIVATE':
141
- // Reset time/cue/transition state to prevent stale playback data from
142
- // the previous cell flashing on the new cell's overlay.
143
- // Note: nextItem is NOT cleared here — the native-side deferred-configure
144
- // (ShortKitOverlayBridge) already prevents handleSwipe from polluting it.
145
- // Clearing it would cause overlay-next to unmount its content ~80ms before
146
- // the page settle swaps overlays, creating a visible dim-layer gap.
147
- return {
148
- ...state,
149
- currentItem: action.payload,
150
- isActive: true,
151
- time: { current: 0, duration: action.payload.duration, buffered: 0 },
152
- activeCue: null,
153
- isTransitioning: false,
154
- };
155
- case 'OVERLAY_RESET':
156
- // Don't set isActive = false — the overlay stays mounted during
157
- // transitions. In the native SDK each cell has its own overlay
158
- // instance, so there's no gap. We replicate this by keeping the
159
- // single JS overlay mounted and updating its content on activate.
160
- return { ...state, isTransitioning: false };
161
- case 'OVERLAY_FADE_OUT':
162
- return { ...state, isTransitioning: true };
163
- case 'OVERLAY_RESTORE':
164
- return { ...state, isTransitioning: false };
165
- case 'OVERLAY_TAP':
166
- return { ...state, lastOverlayTap: state.lastOverlayTap + 1 };
167
- case 'OVERLAY_DOUBLE_TAP':
168
- return {
169
- ...state,
170
- lastOverlayDoubleTap: {
171
- x: action.payload.x,
172
- y: action.payload.y,
173
- id: (state.lastOverlayDoubleTap?.id ?? 0) + 1,
174
- },
175
- };
176
- case 'ACTIVE_CELL_TYPE':
177
- return { ...state, activeCellType: action.payload };
178
- case 'CAROUSEL_OVERLAY_CONFIGURE':
179
- return { ...state, nextCarouselItem: action.payload };
180
- case 'CAROUSEL_OVERLAY_ACTIVATE':
181
- return { ...state, currentCarouselItem: action.payload, isCarouselActive: true };
182
- case 'CAROUSEL_OVERLAY_RESET':
183
- return { ...state, isCarouselTransitioning: false };
184
- case 'CAROUSEL_OVERLAY_FADE_OUT':
185
- return { ...state, isCarouselTransitioning: true };
186
- case 'CAROUSEL_OVERLAY_RESTORE':
187
- return { ...state, isCarouselTransitioning: false };
101
+ case 'FEED_SCROLL_PHASE':
102
+ return { ...state, feedScrollPhase: action.payload };
188
103
  default:
189
104
  return state;
190
105
  }
@@ -196,12 +111,11 @@ function reducer(state: State, action: Action): State {
196
111
 
197
112
  export function ShortKitProvider({
198
113
  apiKey,
199
- config,
200
- embedId,
201
114
  userId,
202
115
  clientAppName,
203
116
  clientAppVersion,
204
117
  customDimensions,
118
+ loadingViewComponent,
205
119
  children,
206
120
  }: ShortKitProviderProps): React.JSX.Element {
207
121
  const [state, dispatch] = useReducer(reducer, initialState);
@@ -213,15 +127,17 @@ export function ShortKitProvider({
213
127
  useEffect(() => {
214
128
  if (!NativeShortKitModule) return;
215
129
 
216
- const serializedConfig = serializeFeedConfigForModule(config);
130
+ if (loadingViewComponent) {
131
+ registerLoadingComponent(loadingViewComponent);
132
+ }
133
+
217
134
  const serializedDimensions = customDimensions
218
135
  ? JSON.stringify(customDimensions)
219
136
  : undefined;
220
137
 
221
138
  NativeShortKitModule.initialize(
222
139
  apiKey,
223
- serializedConfig,
224
- embedId,
140
+ !!loadingViewComponent,
225
141
  clientAppName,
226
142
  clientAppVersion,
227
143
  serializedDimensions,
@@ -314,6 +230,7 @@ export function ShortKitProvider({
314
230
  author: event.author,
315
231
  articleUrl: event.articleUrl,
316
232
  commentCount: event.commentCount,
233
+ fallbackUrl: event.fallbackUrl,
317
234
  };
318
235
  dispatch({ type: 'CURRENT_ITEM', payload: item });
319
236
  }),
@@ -382,124 +299,34 @@ export function ShortKitProvider({
382
299
  }),
383
300
  );
384
301
 
385
- // Remaining content count
386
- subscriptions.push(
387
- NativeShortKitModule.onRemainingContentCountChanged((event) => {
388
- dispatch({ type: 'REMAINING_COUNT', payload: event.count });
389
- }),
390
- );
391
-
392
- // Overlay lifecycle events
393
- subscriptions.push(
394
- NativeShortKitModule.onOverlayConfigure((event) => {
395
- const item = deserializeContentItem(event.item);
396
- if (item) {
397
- dispatch({ type: 'OVERLAY_CONFIGURE', payload: item });
398
- }
399
- }),
400
- );
401
-
302
+ // Feed transition
402
303
  subscriptions.push(
403
- NativeShortKitModule.onOverlayActivate((event) => {
404
- const item = deserializeContentItem(event.item);
405
- if (item) {
406
- dispatch({ type: 'OVERLAY_ACTIVATE', payload: item });
407
- }
408
- }),
409
- );
410
-
411
- subscriptions.push(
412
- NativeShortKitModule.onOverlayReset((_event) => {
413
- dispatch({ type: 'OVERLAY_RESET' });
414
- }),
415
- );
416
-
417
- subscriptions.push(
418
- NativeShortKitModule.onOverlayFadeOut((_event) => {
419
- dispatch({ type: 'OVERLAY_FADE_OUT' });
420
- }),
421
- );
422
-
423
- subscriptions.push(
424
- NativeShortKitModule.onOverlayRestore((_event) => {
425
- dispatch({ type: 'OVERLAY_RESTORE' });
426
- }),
427
- );
428
-
429
- // Overlay tap events
430
- subscriptions.push(
431
- NativeShortKitModule.onOverlayTap((_event) => {
432
- dispatch({ type: 'OVERLAY_TAP' });
433
- }),
434
- );
435
-
436
- subscriptions.push(
437
- NativeShortKitModule.onOverlayDoubleTap((event) => {
438
- dispatch({
439
- type: 'OVERLAY_DOUBLE_TAP',
440
- payload: { x: event.x, y: event.y },
441
- });
304
+ NativeShortKitModule.onFeedTransition((_event) => {
305
+ // Feed transition events are consumed by ShortKitFeed callback props.
306
+ // No state to update here in the simplified provider.
442
307
  }),
443
308
  );
444
309
 
445
- // Feed transition — track active cell type
310
+ // Feed scroll phase
446
311
  subscriptions.push(
447
- NativeShortKitModule.onFeedTransition((event) => {
448
- if (event.phase === 'ended') {
449
- // toItem is null when the destination cell is non-video (carousel, survey, ad)
450
- const isVideo = event.toItem != null;
312
+ NativeShortKitModule.onFeedScrollPhase((event) => {
313
+ if (event.phase === 'dragging' && event.fromId) {
451
314
  dispatch({
452
- type: 'ACTIVE_CELL_TYPE',
453
- payload: isVideo ? 'video' : 'carousel',
315
+ type: 'FEED_SCROLL_PHASE',
316
+ payload: { phase: 'dragging', fromId: event.fromId },
317
+ });
318
+ } else if (event.phase === 'settled') {
319
+ dispatch({
320
+ type: 'FEED_SCROLL_PHASE',
321
+ payload: { phase: 'settled' },
454
322
  });
455
323
  }
456
324
  }),
457
325
  );
458
326
 
459
- // Carousel overlay lifecycle events
460
- subscriptions.push(
461
- NativeShortKitModule.onCarouselOverlayConfigure((event) => {
462
- try {
463
- const item = JSON.parse(event.item) as ImageCarouselItem;
464
- dispatch({ type: 'CAROUSEL_OVERLAY_CONFIGURE', payload: item });
465
- } catch {
466
- // Ignore malformed JSON
467
- }
468
- }),
469
- );
470
-
471
- subscriptions.push(
472
- NativeShortKitModule.onCarouselOverlayActivate((event) => {
473
- try {
474
- const item = JSON.parse(event.item) as ImageCarouselItem;
475
- dispatch({ type: 'CAROUSEL_OVERLAY_ACTIVATE', payload: item });
476
- } catch {
477
- // Ignore malformed JSON
478
- }
479
- }),
480
- );
481
-
482
- subscriptions.push(
483
- NativeShortKitModule.onCarouselOverlayReset((_event) => {
484
- dispatch({ type: 'CAROUSEL_OVERLAY_RESET' });
485
- }),
486
- );
487
-
488
- subscriptions.push(
489
- NativeShortKitModule.onCarouselOverlayFadeOut((_event) => {
490
- dispatch({ type: 'CAROUSEL_OVERLAY_FADE_OUT' });
491
- }),
492
- );
493
-
494
- subscriptions.push(
495
- NativeShortKitModule.onCarouselOverlayRestore((_event) => {
496
- dispatch({ type: 'CAROUSEL_OVERLAY_RESTORE' });
497
- }),
498
- );
499
-
500
327
  // Note: Feed-level callback events (onDidLoop, onFeedTransition,
501
- // onShareTapped, etc.) are subscribed by the ShortKitFeed component
502
- // (Task 11), not here. The provider only manages state-driving events.
328
+ // onDismiss, etc.) are subscribed by the ShortKitFeed component
329
+ // not here. The provider only manages state-driving events.
503
330
 
504
331
  return () => {
505
332
  for (const sub of subscriptions) {
@@ -567,24 +394,38 @@ export function ShortKitProvider({
567
394
  NativeShortKitModule?.clearUserId();
568
395
  }, []);
569
396
 
570
- const setFeedItemsCmd = useCallback((items: CustomFeedItem[]) => {
571
- NativeShortKitModule?.setFeedItems(serializeCustomFeedItems(items));
397
+ const fetchContentCmd = useCallback(async (limit: number = 10, filter?: FeedFilter): Promise<ContentItem[]> => {
398
+ if (!NativeShortKitModule) return [];
399
+ const filterJSON = filter ? JSON.stringify(filter) : null;
400
+ const json = await NativeShortKitModule.fetchContent(limit, filterJSON);
401
+ try {
402
+ return JSON.parse(json) as ContentItem[];
403
+ } catch {
404
+ return [];
405
+ }
572
406
  }, []);
573
407
 
574
- const appendFeedItemsCmd = useCallback((items: CustomFeedItem[]) => {
575
- NativeShortKitModule?.appendFeedItems(serializeCustomFeedItems(items));
408
+ const prefetchStoryboardCmd = useCallback((playbackId: string) => {
409
+ NativeShortKitModule?.prefetchStoryboard(playbackId);
576
410
  }, []);
577
411
 
578
- const fetchContentCmd = useCallback(async (limit: number = 10): Promise<ContentItem[]> => {
579
- if (!NativeShortKitModule) return [];
580
- const json = await NativeShortKitModule.fetchContent(limit);
412
+ const getStoryboardDataCmd = useCallback(async (playbackId: string): Promise<StoryboardData | null> => {
413
+ if (!NativeShortKitModule) return null;
414
+ const json = await NativeShortKitModule.getStoryboardData(playbackId);
415
+ if (!json) return null;
581
416
  try {
582
- return JSON.parse(json) as ContentItem[];
417
+ return JSON.parse(json) as StoryboardData;
583
418
  } catch {
584
- return [];
419
+ return null;
585
420
  }
586
421
  }, []);
587
422
 
423
+ const preloadFeedCmd = useCallback(async (config?: Partial<FeedConfig>): Promise<string> => {
424
+ if (!NativeShortKitModule) return '';
425
+ const configJSON = config ? serializeFeedConfig(config as FeedConfig) : '{}';
426
+ return NativeShortKitModule.preloadFeed(configJSON);
427
+ }, []);
428
+
588
429
  // -----------------------------------------------------------------------
589
430
  // Context value (memoized to avoid unnecessary re-renders)
590
431
  // -----------------------------------------------------------------------
@@ -593,7 +434,6 @@ export function ShortKitProvider({
593
434
  // State
594
435
  playerState: state.playerState,
595
436
  currentItem: state.currentItem,
596
- nextItem: state.nextItem,
597
437
  time: state.time,
598
438
  isMuted: state.isMuted,
599
439
  playbackRate: state.playbackRate,
@@ -601,11 +441,8 @@ export function ShortKitProvider({
601
441
  activeCaptionTrack: state.activeCaptionTrack,
602
442
  activeCue: state.activeCue,
603
443
  prefetchedAheadCount: state.prefetchedAheadCount,
604
- remainingContentCount: state.remainingContentCount,
605
444
  isActive: state.isActive,
606
- isTransitioning: state.isTransitioning,
607
- lastOverlayTap: state.lastOverlayTap,
608
- lastOverlayDoubleTap: state.lastOverlayDoubleTap,
445
+ feedScrollPhase: state.feedScrollPhase,
609
446
 
610
447
  // Commands
611
448
  play,
@@ -622,27 +459,14 @@ export function ShortKitProvider({
622
459
  setMaxBitrate,
623
460
  setUserId: setUserIdCmd,
624
461
  clearUserId: clearUserIdCmd,
625
- setFeedItems: setFeedItemsCmd,
626
- appendFeedItems: appendFeedItemsCmd,
627
462
  fetchContent: fetchContentCmd,
628
-
629
- // Active cell type
630
- activeCellType: state.activeCellType,
631
-
632
- // Carousel overlay state
633
- currentCarouselItem: state.currentCarouselItem,
634
- nextCarouselItem: state.nextCarouselItem,
635
- isCarouselActive: state.isCarouselActive,
636
- isCarouselTransitioning: state.isCarouselTransitioning,
637
-
638
- // Internal — consumed by ShortKitFeed to pass to OverlayManager
639
- _overlayConfig: config.overlay ?? 'none',
640
- _carouselOverlayConfig: config.carouselOverlay ?? 'none',
463
+ preloadFeed: preloadFeedCmd,
464
+ prefetchStoryboard: prefetchStoryboardCmd,
465
+ getStoryboardData: getStoryboardDataCmd,
641
466
  }),
642
467
  [
643
468
  state.playerState,
644
469
  state.currentItem,
645
- state.nextItem,
646
470
  state.time,
647
471
  state.isMuted,
648
472
  state.playbackRate,
@@ -650,16 +474,8 @@ export function ShortKitProvider({
650
474
  state.activeCaptionTrack,
651
475
  state.activeCue,
652
476
  state.prefetchedAheadCount,
653
- state.remainingContentCount,
654
477
  state.isActive,
655
- state.isTransitioning,
656
- state.lastOverlayTap,
657
- state.lastOverlayDoubleTap,
658
- state.activeCellType,
659
- state.currentCarouselItem,
660
- state.nextCarouselItem,
661
- state.isCarouselActive,
662
- state.isCarouselTransitioning,
478
+ state.feedScrollPhase,
663
479
  play,
664
480
  pause,
665
481
  seek,
@@ -674,11 +490,10 @@ export function ShortKitProvider({
674
490
  setMaxBitrate,
675
491
  setUserIdCmd,
676
492
  clearUserIdCmd,
677
- setFeedItemsCmd,
678
- appendFeedItemsCmd,
679
493
  fetchContentCmd,
680
- config.overlay,
681
- config.carouselOverlay,
494
+ preloadFeedCmd,
495
+ prefetchStoryboardCmd,
496
+ getStoryboardDataCmd,
682
497
  ],
683
498
  );
684
499
 
package/src/index.ts CHANGED
@@ -4,7 +4,6 @@ export { ShortKitPlayer } from './ShortKitPlayer';
4
4
  export { ShortKitWidget } from './ShortKitWidget';
5
5
  export { useShortKitPlayer } from './useShortKitPlayer';
6
6
  export { useShortKit } from './useShortKit';
7
- export { useShortKitCarousel } from './useShortKitCarousel';
8
7
  export type {
9
8
  FeedConfig,
10
9
  FeedHeight,
@@ -20,21 +19,27 @@ export type {
20
19
  ContentItem,
21
20
  CarouselImage,
22
21
  ImageCarouselItem,
23
- CustomFeedItem,
22
+ FeedInput,
24
23
  JSONValue,
25
24
  CaptionTrack,
26
25
  PlayerTime,
27
26
  PlayerState,
28
27
  LoopEvent,
29
28
  FeedTransitionEvent,
29
+ FeedScrollPhase,
30
30
  FormatChangeEvent,
31
31
  ContentSignal,
32
32
  SurveyOption,
33
- ShortKitError,
33
+ FeedFilter,
34
+ CaptionSource,
34
35
  ShortKitProviderProps,
35
36
  ShortKitFeedProps,
37
+ ShortKitFeedHandle,
36
38
  ShortKitPlayerProps,
37
39
  ShortKitWidgetProps,
38
40
  ShortKitPlayerState,
41
+ StoryboardData,
42
+ StoryboardTile,
39
43
  } from './types';
40
- export type { ShortKitCarouselState } from './useShortKitCarousel';
44
+ export { ShortKitCommands } from './ShortKitCommands';
45
+ export type { OverlayProps, CarouselOverlayProps } from './types';
@@ -1,60 +1,40 @@
1
1
  import type {
2
2
  FeedConfig,
3
3
  ContentItem,
4
- CustomFeedItem,
4
+ FeedInput,
5
5
  PlayerState,
6
6
  PlayerTime,
7
7
  } from './types';
8
8
 
9
9
  /**
10
- * Serialized FeedConfig with JSON strings for complex fields.
11
- * Used by ShortKitFeedView native component props.
12
- */
13
- export interface SerializedFeedConfig {
14
- feedHeight: string;
15
- overlay: string;
16
- carouselOverlay: string;
17
- surveyMode: string;
18
- muteOnStart: boolean;
19
- feedSource: string;
20
- }
21
-
22
- /**
23
- * Serialize FeedConfig for the bridge.
10
+ * Serialize FeedConfig into a JSON string for the native Fabric view prop.
24
11
  * Complex union types are JSON-stringified; the `component` field on custom
25
12
  * overlays is stripped because React component references cannot cross the bridge.
13
+ * The overlay `name` is included so native can look up the correct Fabric surface.
26
14
  */
27
- export function serializeFeedConfig(config: FeedConfig): SerializedFeedConfig {
28
- let overlay: string;
29
- const rawOverlay = config.overlay ?? 'none';
30
- if (typeof rawOverlay === 'string') {
31
- overlay = JSON.stringify(rawOverlay);
32
- } else {
33
- // Strip the component ref — it stays on the JS side for OverlayManager
34
- overlay = JSON.stringify({ type: 'custom' });
35
- }
15
+ export function serializeFeedConfig(config: FeedConfig): string {
16
+ const overlay = (() => {
17
+ const raw = config.overlay ?? 'none';
18
+ if (typeof raw === 'string') return JSON.stringify(raw);
19
+ return JSON.stringify({ type: 'custom', name: raw.name });
20
+ })();
36
21
 
37
- return {
22
+ const carouselOverlay = (() => {
23
+ const raw = config.carouselOverlay ?? 'none';
24
+ if (typeof raw === 'string') return JSON.stringify(raw);
25
+ return JSON.stringify({ type: 'custom', name: raw.name });
26
+ })();
27
+
28
+ return JSON.stringify({
38
29
  feedHeight: JSON.stringify(config.feedHeight ?? { type: 'fullscreen' }),
39
30
  overlay,
40
- carouselOverlay: (() => {
41
- const raw = config.carouselOverlay ?? 'none';
42
- if (typeof raw === 'string') return JSON.stringify(raw);
43
- return JSON.stringify({ type: 'custom' });
44
- })(),
31
+ carouselOverlay,
45
32
  surveyMode: JSON.stringify(config.surveyMode ?? 'none'),
46
33
  muteOnStart: config.muteOnStart ?? true,
47
34
  feedSource: config.feedSource ?? 'algorithmic',
48
- };
49
- }
50
-
51
- /**
52
- * Serialize a FeedConfig into a single JSON string for the TurboModule
53
- * `initialize(config:)` parameter.
54
- */
55
- export function serializeFeedConfigForModule(config: FeedConfig): string {
56
- const serialized = serializeFeedConfig(config);
57
- return JSON.stringify(serialized);
35
+ autoplay: config.autoplay ?? true,
36
+ filter: config.filter ? JSON.stringify(config.filter) : undefined,
37
+ });
58
38
  }
59
39
 
60
40
  /**
@@ -102,8 +82,8 @@ export function deserializePlayerTime(event: {
102
82
  }
103
83
 
104
84
  /**
105
- * Serialize CustomFeedItem[] to a JSON string for the bridge.
85
+ * Serialize FeedInput[] to a JSON string for the bridge.
106
86
  */
107
- export function serializeCustomFeedItems(items: CustomFeedItem[]): string {
87
+ export function serializeFeedInputs(items: FeedInput[]): string {
108
88
  return JSON.stringify(items);
109
89
  }