@shortkitsdk/react-native 0.2.5 → 0.2.6
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/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +49 -11
- package/ios/ShortKitBridge.swift +85 -7
- package/ios/ShortKitCarouselOverlayBridge.swift +177 -12
- package/ios/ShortKitFeedView.swift +48 -25
- package/ios/ShortKitModule.mm +29 -4
- package/ios/ShortKitOverlayBridge.swift +2 -4
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +1 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +1635 -457
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +50 -16
- 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 +50 -16
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +1 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1635 -457
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +50 -16
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +50 -16
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/package.json +1 -1
- package/src/CarouselOverlayManager.tsx +0 -1
- package/src/OverlayManager.tsx +1 -1
- package/src/ShortKitContext.ts +11 -6
- package/src/ShortKitProvider.tsx +140 -66
- package/src/index.ts +4 -1
- package/src/serialization.ts +3 -3
- package/src/specs/NativeShortKitModule.ts +18 -16
- package/src/types.ts +26 -3
- package/src/useShortKitCarousel.ts +2 -2
- package/src/useShortKitPlayer.ts +0 -1
package/src/ShortKitProvider.tsx
CHANGED
|
@@ -7,15 +7,17 @@ import type {
|
|
|
7
7
|
ShortKitProviderProps,
|
|
8
8
|
ContentItem,
|
|
9
9
|
ImageCarouselItem,
|
|
10
|
-
|
|
10
|
+
FeedInput,
|
|
11
11
|
PlayerTime,
|
|
12
12
|
PlayerState,
|
|
13
13
|
CaptionTrack,
|
|
14
|
+
FeedScrollPhase,
|
|
14
15
|
ContentSignal,
|
|
16
|
+
StoryboardData,
|
|
15
17
|
} from './types';
|
|
16
18
|
import {
|
|
17
19
|
serializeFeedConfigForModule,
|
|
18
|
-
|
|
20
|
+
serializeFeedInputs,
|
|
19
21
|
deserializePlayerState,
|
|
20
22
|
deserializeContentItem,
|
|
21
23
|
deserializePlayerTime,
|
|
@@ -39,13 +41,13 @@ interface State {
|
|
|
39
41
|
prefetchedAheadCount: number;
|
|
40
42
|
remainingContentCount: number;
|
|
41
43
|
isActive: boolean;
|
|
42
|
-
|
|
44
|
+
feedScrollPhase: FeedScrollPhase | null;
|
|
43
45
|
lastOverlayTap: number;
|
|
44
46
|
lastOverlayDoubleTap: { x: number; y: number; id: number } | null;
|
|
45
47
|
currentCarouselItem: ImageCarouselItem | null;
|
|
46
48
|
nextCarouselItem: ImageCarouselItem | null;
|
|
47
49
|
isCarouselActive: boolean;
|
|
48
|
-
|
|
50
|
+
currentCarouselPage: number;
|
|
49
51
|
activeCellType: 'video' | 'carousel' | null;
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -62,13 +64,13 @@ const initialState: State = {
|
|
|
62
64
|
prefetchedAheadCount: 0,
|
|
63
65
|
remainingContentCount: 0,
|
|
64
66
|
isActive: false,
|
|
65
|
-
|
|
67
|
+
feedScrollPhase: null,
|
|
66
68
|
lastOverlayTap: 0,
|
|
67
69
|
lastOverlayDoubleTap: null,
|
|
68
70
|
currentCarouselItem: null,
|
|
69
71
|
nextCarouselItem: null,
|
|
70
72
|
isCarouselActive: false,
|
|
71
|
-
|
|
73
|
+
currentCarouselPage: 0,
|
|
72
74
|
activeCellType: null,
|
|
73
75
|
};
|
|
74
76
|
|
|
@@ -86,16 +88,15 @@ type Action =
|
|
|
86
88
|
| { type: 'OVERLAY_CONFIGURE'; payload: ContentItem }
|
|
87
89
|
| { type: 'OVERLAY_ACTIVATE'; payload: ContentItem }
|
|
88
90
|
| { type: 'OVERLAY_RESET' }
|
|
89
|
-
| { type: 'OVERLAY_FADE_OUT' }
|
|
90
|
-
| { type: 'OVERLAY_RESTORE' }
|
|
91
91
|
| { type: 'OVERLAY_TAP' }
|
|
92
92
|
| { type: 'OVERLAY_DOUBLE_TAP'; payload: { x: number; y: number } }
|
|
93
|
-
| { type: 'ACTIVE_CELL_TYPE'; payload: 'video' | 'carousel' }
|
|
93
|
+
| { type: 'ACTIVE_CELL_TYPE'; payload: 'video' | 'carousel' | null }
|
|
94
|
+
| { type: 'FEED_TRANSITION_ENDED'; payload: { isVideo: boolean } }
|
|
94
95
|
| { type: 'CAROUSEL_OVERLAY_CONFIGURE'; payload: ImageCarouselItem }
|
|
95
96
|
| { type: 'CAROUSEL_OVERLAY_ACTIVATE'; payload: ImageCarouselItem }
|
|
96
97
|
| { type: 'CAROUSEL_OVERLAY_RESET' }
|
|
97
|
-
| { type: '
|
|
98
|
-
| { type: '
|
|
98
|
+
| { type: 'CAROUSEL_PAGE_CHANGED'; payload: number }
|
|
99
|
+
| { type: 'FEED_SCROLL_PHASE'; payload: FeedScrollPhase };
|
|
99
100
|
|
|
100
101
|
function reducer(state: State, action: Action): State {
|
|
101
102
|
switch (action.type) {
|
|
@@ -138,30 +139,29 @@ function reducer(state: State, action: Action): State {
|
|
|
138
139
|
case 'OVERLAY_CONFIGURE':
|
|
139
140
|
return { ...state, nextItem: action.payload };
|
|
140
141
|
case 'OVERLAY_ACTIVATE':
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
// the page settle swaps overlays, creating a visible dim-layer gap.
|
|
142
|
+
// Clear nextItem so overlay-next doesn't show stale data from the
|
|
143
|
+
// handleSwipe re-configure. The next OVERLAY_CONFIGURE (from UIKit
|
|
144
|
+
// prefetching cell N+2) will set it to the correct upcoming item.
|
|
145
|
+
// Also clear carousel state — a video cell is now active.
|
|
146
|
+
console.log('[ShortKitProvider] OVERLAY_ACTIVATE (video):', action.payload.playbackId);
|
|
147
147
|
return {
|
|
148
148
|
...state,
|
|
149
149
|
currentItem: action.payload,
|
|
150
150
|
isActive: true,
|
|
151
|
-
time: { current: 0, duration: action.payload.duration, buffered: 0 },
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
time: { current: 0, duration: action.payload.duration ?? 0, buffered: 0 },
|
|
152
|
+
nextItem: null,
|
|
153
|
+
currentCarouselItem: null,
|
|
154
|
+
isCarouselActive: false,
|
|
155
|
+
nextCarouselItem: null,
|
|
156
|
+
currentCarouselPage: 0,
|
|
154
157
|
};
|
|
155
158
|
case 'OVERLAY_RESET':
|
|
156
159
|
// Don't set isActive = false — the overlay stays mounted during
|
|
157
160
|
// transitions. In the native SDK each cell has its own overlay
|
|
158
161
|
// instance, so there's no gap. We replicate this by keeping the
|
|
159
162
|
// single JS overlay mounted and updating its content on activate.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return { ...state, isTransitioning: true };
|
|
163
|
-
case 'OVERLAY_RESTORE':
|
|
164
|
-
return { ...state, isTransitioning: false };
|
|
163
|
+
console.log('[ShortKitProvider] OVERLAY_RESET (video)');
|
|
164
|
+
return { ...state, feedScrollPhase: null };
|
|
165
165
|
case 'OVERLAY_TAP':
|
|
166
166
|
return { ...state, lastOverlayTap: state.lastOverlayTap + 1 };
|
|
167
167
|
case 'OVERLAY_DOUBLE_TAP':
|
|
@@ -174,17 +174,46 @@ function reducer(state: State, action: Action): State {
|
|
|
174
174
|
},
|
|
175
175
|
};
|
|
176
176
|
case 'ACTIVE_CELL_TYPE':
|
|
177
|
+
console.log('[ShortKitProvider] ACTIVE_CELL_TYPE:', action.payload);
|
|
177
178
|
return { ...state, activeCellType: action.payload };
|
|
179
|
+
case 'FEED_TRANSITION_ENDED': {
|
|
180
|
+
if (action.payload.isVideo) {
|
|
181
|
+
return {
|
|
182
|
+
...state,
|
|
183
|
+
activeCellType: 'video',
|
|
184
|
+
currentCarouselItem: null,
|
|
185
|
+
isCarouselActive: false,
|
|
186
|
+
nextCarouselItem: null,
|
|
187
|
+
currentCarouselPage: 0,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Non-video destination. If a carousel configure was received, activate it.
|
|
191
|
+
if (state.nextCarouselItem) {
|
|
192
|
+
console.log('[ShortKitProvider] FEED_TRANSITION_ENDED → carousel activate:', state.nextCarouselItem.id);
|
|
193
|
+
return {
|
|
194
|
+
...state,
|
|
195
|
+
activeCellType: 'carousel',
|
|
196
|
+
currentCarouselItem: state.nextCarouselItem,
|
|
197
|
+
isCarouselActive: true,
|
|
198
|
+
nextCarouselItem: null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Unknown non-video type (survey, ad, etc.)
|
|
202
|
+
return { ...state, activeCellType: null };
|
|
203
|
+
}
|
|
178
204
|
case 'CAROUSEL_OVERLAY_CONFIGURE':
|
|
205
|
+
console.log('[ShortKitProvider] CAROUSEL_OVERLAY_CONFIGURE:', action.payload.id);
|
|
179
206
|
return { ...state, nextCarouselItem: action.payload };
|
|
180
207
|
case 'CAROUSEL_OVERLAY_ACTIVATE':
|
|
181
|
-
|
|
208
|
+
console.log('[ShortKitProvider] CAROUSEL_OVERLAY_ACTIVATE:', action.payload.id);
|
|
209
|
+
return { ...state, currentCarouselItem: action.payload, isCarouselActive: true, nextCarouselItem: null };
|
|
182
210
|
case 'CAROUSEL_OVERLAY_RESET':
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
211
|
+
console.log('[ShortKitProvider] CAROUSEL_OVERLAY_RESET');
|
|
212
|
+
return { ...state, currentCarouselItem: null, isCarouselActive: false, nextCarouselItem: null, currentCarouselPage: 0 };
|
|
213
|
+
case 'CAROUSEL_PAGE_CHANGED':
|
|
214
|
+
return { ...state, currentCarouselPage: action.payload };
|
|
215
|
+
case 'FEED_SCROLL_PHASE':
|
|
216
|
+
return { ...state, feedScrollPhase: action.payload };
|
|
188
217
|
default:
|
|
189
218
|
return state;
|
|
190
219
|
}
|
|
@@ -197,7 +226,6 @@ function reducer(state: State, action: Action): State {
|
|
|
197
226
|
export function ShortKitProvider({
|
|
198
227
|
apiKey,
|
|
199
228
|
config,
|
|
200
|
-
embedId,
|
|
201
229
|
userId,
|
|
202
230
|
clientAppName,
|
|
203
231
|
clientAppVersion,
|
|
@@ -221,7 +249,6 @@ export function ShortKitProvider({
|
|
|
221
249
|
NativeShortKitModule.initialize(
|
|
222
250
|
apiKey,
|
|
223
251
|
serializedConfig,
|
|
224
|
-
embedId,
|
|
225
252
|
clientAppName,
|
|
226
253
|
clientAppVersion,
|
|
227
254
|
serializedDimensions,
|
|
@@ -414,18 +441,6 @@ export function ShortKitProvider({
|
|
|
414
441
|
}),
|
|
415
442
|
);
|
|
416
443
|
|
|
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
444
|
// Overlay tap events
|
|
430
445
|
subscriptions.push(
|
|
431
446
|
NativeShortKitModule.onOverlayTap((_event) => {
|
|
@@ -445,12 +460,14 @@ export function ShortKitProvider({
|
|
|
445
460
|
// Feed transition — track active cell type
|
|
446
461
|
subscriptions.push(
|
|
447
462
|
NativeShortKitModule.onFeedTransition((event) => {
|
|
463
|
+
console.log('[ShortKitProvider] onFeedTransition:', event.phase, 'toItem:', event.toItem != null ? 'video' : 'non-video');
|
|
448
464
|
if (event.phase === 'ended') {
|
|
449
|
-
// toItem is null when the destination cell is non-video (carousel, survey, ad)
|
|
465
|
+
// toItem is null when the destination cell is non-video (carousel, survey, ad).
|
|
466
|
+
// The reducer checks nextCarouselItem to distinguish carousel from other types.
|
|
450
467
|
const isVideo = event.toItem != null;
|
|
451
468
|
dispatch({
|
|
452
|
-
type: '
|
|
453
|
-
payload: isVideo
|
|
469
|
+
type: 'FEED_TRANSITION_ENDED',
|
|
470
|
+
payload: { isVideo },
|
|
454
471
|
});
|
|
455
472
|
}
|
|
456
473
|
}),
|
|
@@ -485,21 +502,26 @@ export function ShortKitProvider({
|
|
|
485
502
|
}),
|
|
486
503
|
);
|
|
487
504
|
|
|
505
|
+
// Feed scroll phase
|
|
488
506
|
subscriptions.push(
|
|
489
|
-
NativeShortKitModule.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
507
|
+
NativeShortKitModule.onFeedScrollPhase((event) => {
|
|
508
|
+
if (event.phase === 'dragging' && event.fromId) {
|
|
509
|
+
dispatch({
|
|
510
|
+
type: 'FEED_SCROLL_PHASE',
|
|
511
|
+
payload: { phase: 'dragging', fromId: event.fromId },
|
|
512
|
+
});
|
|
513
|
+
} else if (event.phase === 'settled') {
|
|
514
|
+
dispatch({
|
|
515
|
+
type: 'FEED_SCROLL_PHASE',
|
|
516
|
+
payload: { phase: 'settled' },
|
|
517
|
+
});
|
|
518
|
+
}
|
|
497
519
|
}),
|
|
498
520
|
);
|
|
499
521
|
|
|
500
522
|
// Note: Feed-level callback events (onDidLoop, onFeedTransition,
|
|
501
523
|
// onShareTapped, etc.) are subscribed by the ShortKitFeed component
|
|
502
|
-
//
|
|
524
|
+
// not here. The provider only manages state-driving events.
|
|
503
525
|
|
|
504
526
|
return () => {
|
|
505
527
|
for (const sub of subscriptions) {
|
|
@@ -508,6 +530,40 @@ export function ShortKitProvider({
|
|
|
508
530
|
};
|
|
509
531
|
}, []);
|
|
510
532
|
|
|
533
|
+
// -----------------------------------------------------------------------
|
|
534
|
+
// Overlay ready signal — tell native it's safe to swap overlay transforms.
|
|
535
|
+
// This fires after React has re-rendered overlay-current with the new
|
|
536
|
+
// content item (and the overlay component has reset its opacity).
|
|
537
|
+
// Child effects (e.g. NewsOverlay's content-change reset) fire before
|
|
538
|
+
// this parent effect, guaranteeing the overlay is visually ready.
|
|
539
|
+
// -----------------------------------------------------------------------
|
|
540
|
+
const currentItemIdRef = useRef<string | null>(null);
|
|
541
|
+
useEffect(() => {
|
|
542
|
+
const newId = state.currentItem?.id ?? null;
|
|
543
|
+
if (newId && newId !== currentItemIdRef.current) {
|
|
544
|
+
currentItemIdRef.current = newId;
|
|
545
|
+
NativeShortKitModule?.notifyOverlayReady();
|
|
546
|
+
}
|
|
547
|
+
}, [state.currentItem?.id]);
|
|
548
|
+
|
|
549
|
+
// Carousel overlay ready signal — same mechanism as video overlays.
|
|
550
|
+
const currentCarouselIdRef = useRef<string | null>(null);
|
|
551
|
+
useEffect(() => {
|
|
552
|
+
const newId = state.currentCarouselItem?.id ?? null;
|
|
553
|
+
if (newId && newId !== currentCarouselIdRef.current) {
|
|
554
|
+
currentCarouselIdRef.current = newId;
|
|
555
|
+
NativeShortKitModule?.notifyOverlayReady();
|
|
556
|
+
}
|
|
557
|
+
}, [state.currentCarouselItem?.id]);
|
|
558
|
+
|
|
559
|
+
// Safety net: if a carousel configure arrives before any feed transition
|
|
560
|
+
// (e.g. the feed starts on a carousel cell), activate after one render cycle.
|
|
561
|
+
useEffect(() => {
|
|
562
|
+
if (state.nextCarouselItem && !state.currentCarouselItem && !state.activeCellType) {
|
|
563
|
+
dispatch({ type: 'CAROUSEL_OVERLAY_ACTIVATE', payload: state.nextCarouselItem });
|
|
564
|
+
}
|
|
565
|
+
}, [state.nextCarouselItem, state.currentCarouselItem, state.activeCellType]);
|
|
566
|
+
|
|
511
567
|
// -----------------------------------------------------------------------
|
|
512
568
|
// Commands (stable refs)
|
|
513
569
|
// -----------------------------------------------------------------------
|
|
@@ -567,12 +623,12 @@ export function ShortKitProvider({
|
|
|
567
623
|
NativeShortKitModule?.clearUserId();
|
|
568
624
|
}, []);
|
|
569
625
|
|
|
570
|
-
const setFeedItemsCmd = useCallback((items:
|
|
571
|
-
NativeShortKitModule?.setFeedItems(
|
|
626
|
+
const setFeedItemsCmd = useCallback((items: FeedInput[]) => {
|
|
627
|
+
NativeShortKitModule?.setFeedItems(serializeFeedInputs(items));
|
|
572
628
|
}, []);
|
|
573
629
|
|
|
574
|
-
const appendFeedItemsCmd = useCallback((items:
|
|
575
|
-
NativeShortKitModule?.appendFeedItems(
|
|
630
|
+
const appendFeedItemsCmd = useCallback((items: FeedInput[]) => {
|
|
631
|
+
NativeShortKitModule?.appendFeedItems(serializeFeedInputs(items));
|
|
576
632
|
}, []);
|
|
577
633
|
|
|
578
634
|
const fetchContentCmd = useCallback(async (limit: number = 10): Promise<ContentItem[]> => {
|
|
@@ -585,6 +641,21 @@ export function ShortKitProvider({
|
|
|
585
641
|
}
|
|
586
642
|
}, []);
|
|
587
643
|
|
|
644
|
+
const prefetchStoryboardCmd = useCallback((playbackId: string) => {
|
|
645
|
+
NativeShortKitModule?.prefetchStoryboard(playbackId);
|
|
646
|
+
}, []);
|
|
647
|
+
|
|
648
|
+
const getStoryboardDataCmd = useCallback(async (playbackId: string): Promise<StoryboardData | null> => {
|
|
649
|
+
if (!NativeShortKitModule) return null;
|
|
650
|
+
const json = await NativeShortKitModule.getStoryboardData(playbackId);
|
|
651
|
+
if (!json) return null;
|
|
652
|
+
try {
|
|
653
|
+
return JSON.parse(json) as StoryboardData;
|
|
654
|
+
} catch {
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
}, []);
|
|
658
|
+
|
|
588
659
|
// -----------------------------------------------------------------------
|
|
589
660
|
// Context value (memoized to avoid unnecessary re-renders)
|
|
590
661
|
// -----------------------------------------------------------------------
|
|
@@ -603,7 +674,7 @@ export function ShortKitProvider({
|
|
|
603
674
|
prefetchedAheadCount: state.prefetchedAheadCount,
|
|
604
675
|
remainingContentCount: state.remainingContentCount,
|
|
605
676
|
isActive: state.isActive,
|
|
606
|
-
|
|
677
|
+
feedScrollPhase: state.feedScrollPhase,
|
|
607
678
|
lastOverlayTap: state.lastOverlayTap,
|
|
608
679
|
lastOverlayDoubleTap: state.lastOverlayDoubleTap,
|
|
609
680
|
|
|
@@ -625,6 +696,8 @@ export function ShortKitProvider({
|
|
|
625
696
|
setFeedItems: setFeedItemsCmd,
|
|
626
697
|
appendFeedItems: appendFeedItemsCmd,
|
|
627
698
|
fetchContent: fetchContentCmd,
|
|
699
|
+
prefetchStoryboard: prefetchStoryboardCmd,
|
|
700
|
+
getStoryboardData: getStoryboardDataCmd,
|
|
628
701
|
|
|
629
702
|
// Active cell type
|
|
630
703
|
activeCellType: state.activeCellType,
|
|
@@ -633,8 +706,7 @@ export function ShortKitProvider({
|
|
|
633
706
|
currentCarouselItem: state.currentCarouselItem,
|
|
634
707
|
nextCarouselItem: state.nextCarouselItem,
|
|
635
708
|
isCarouselActive: state.isCarouselActive,
|
|
636
|
-
|
|
637
|
-
|
|
709
|
+
currentCarouselPage: state.currentCarouselPage,
|
|
638
710
|
// Internal — consumed by ShortKitFeed to pass to OverlayManager
|
|
639
711
|
_overlayConfig: config.overlay ?? 'none',
|
|
640
712
|
_carouselOverlayConfig: config.carouselOverlay ?? 'none',
|
|
@@ -652,14 +724,14 @@ export function ShortKitProvider({
|
|
|
652
724
|
state.prefetchedAheadCount,
|
|
653
725
|
state.remainingContentCount,
|
|
654
726
|
state.isActive,
|
|
655
|
-
state.
|
|
727
|
+
state.feedScrollPhase,
|
|
656
728
|
state.lastOverlayTap,
|
|
657
729
|
state.lastOverlayDoubleTap,
|
|
658
730
|
state.activeCellType,
|
|
659
731
|
state.currentCarouselItem,
|
|
660
732
|
state.nextCarouselItem,
|
|
661
733
|
state.isCarouselActive,
|
|
662
|
-
state.
|
|
734
|
+
state.currentCarouselPage,
|
|
663
735
|
play,
|
|
664
736
|
pause,
|
|
665
737
|
seek,
|
|
@@ -677,6 +749,8 @@ export function ShortKitProvider({
|
|
|
677
749
|
setFeedItemsCmd,
|
|
678
750
|
appendFeedItemsCmd,
|
|
679
751
|
fetchContentCmd,
|
|
752
|
+
prefetchStoryboardCmd,
|
|
753
|
+
getStoryboardDataCmd,
|
|
680
754
|
config.overlay,
|
|
681
755
|
config.carouselOverlay,
|
|
682
756
|
],
|
package/src/index.ts
CHANGED
|
@@ -20,13 +20,14 @@ export type {
|
|
|
20
20
|
ContentItem,
|
|
21
21
|
CarouselImage,
|
|
22
22
|
ImageCarouselItem,
|
|
23
|
-
|
|
23
|
+
FeedInput,
|
|
24
24
|
JSONValue,
|
|
25
25
|
CaptionTrack,
|
|
26
26
|
PlayerTime,
|
|
27
27
|
PlayerState,
|
|
28
28
|
LoopEvent,
|
|
29
29
|
FeedTransitionEvent,
|
|
30
|
+
FeedScrollPhase,
|
|
30
31
|
FormatChangeEvent,
|
|
31
32
|
ContentSignal,
|
|
32
33
|
SurveyOption,
|
|
@@ -36,5 +37,7 @@ export type {
|
|
|
36
37
|
ShortKitPlayerProps,
|
|
37
38
|
ShortKitWidgetProps,
|
|
38
39
|
ShortKitPlayerState,
|
|
40
|
+
StoryboardData,
|
|
41
|
+
StoryboardTile,
|
|
39
42
|
} from './types';
|
|
40
43
|
export type { ShortKitCarouselState } from './useShortKitCarousel';
|
package/src/serialization.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
FeedConfig,
|
|
3
3
|
ContentItem,
|
|
4
|
-
|
|
4
|
+
FeedInput,
|
|
5
5
|
PlayerState,
|
|
6
6
|
PlayerTime,
|
|
7
7
|
} from './types';
|
|
@@ -102,8 +102,8 @@ export function deserializePlayerTime(event: {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* Serialize
|
|
105
|
+
* Serialize FeedInput[] to a JSON string for the bridge.
|
|
106
106
|
*/
|
|
107
|
-
export function
|
|
107
|
+
export function serializeFeedInputs(items: FeedInput[]): string {
|
|
108
108
|
return JSON.stringify(items);
|
|
109
109
|
}
|
|
@@ -66,6 +66,11 @@ type FeedTransitionEvent = Readonly<{
|
|
|
66
66
|
direction: string;
|
|
67
67
|
}>;
|
|
68
68
|
|
|
69
|
+
type FeedScrollPhaseEvent = Readonly<{
|
|
70
|
+
phase: string;
|
|
71
|
+
fromId?: string;
|
|
72
|
+
}>;
|
|
73
|
+
|
|
69
74
|
type FormatChangeEvent = Readonly<{
|
|
70
75
|
contentId: string;
|
|
71
76
|
fromBitrate: Double;
|
|
@@ -109,14 +114,6 @@ type OverlayResetEvent = Readonly<{
|
|
|
109
114
|
item: string; // JSON-serialized ContentItem
|
|
110
115
|
}>;
|
|
111
116
|
|
|
112
|
-
type OverlayFadeOutEvent = Readonly<{
|
|
113
|
-
item: string; // JSON-serialized ContentItem
|
|
114
|
-
}>;
|
|
115
|
-
|
|
116
|
-
type OverlayRestoreEvent = Readonly<{
|
|
117
|
-
item: string; // JSON-serialized ContentItem
|
|
118
|
-
}>;
|
|
119
|
-
|
|
120
117
|
type ContentTappedEvent = Readonly<{
|
|
121
118
|
contentId: string;
|
|
122
119
|
index: Int32;
|
|
@@ -132,9 +129,10 @@ type CarouselOverlayActivateEvent = Readonly<{
|
|
|
132
129
|
|
|
133
130
|
type CarouselOverlayResetEvent = Readonly<{}>;
|
|
134
131
|
|
|
135
|
-
type
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
type CarouselPageChangedEvent = Readonly<{
|
|
133
|
+
page: Int32;
|
|
134
|
+
pageCount: Int32;
|
|
135
|
+
}>;
|
|
138
136
|
|
|
139
137
|
type OverlayTapEvent = Readonly<{}>;
|
|
140
138
|
|
|
@@ -148,7 +146,6 @@ export interface Spec extends TurboModule {
|
|
|
148
146
|
initialize(
|
|
149
147
|
apiKey: string,
|
|
150
148
|
config: string, // JSON-serialized FeedConfig
|
|
151
|
-
embedId?: string,
|
|
152
149
|
clientAppName?: string,
|
|
153
150
|
clientAppVersion?: string,
|
|
154
151
|
customDimensions?: string, // JSON-serialized Record<string, string>
|
|
@@ -178,6 +175,13 @@ export interface Spec extends TurboModule {
|
|
|
178
175
|
appendFeedItems(items: string): void;
|
|
179
176
|
fetchContent(limit: Int32): Promise<string>;
|
|
180
177
|
|
|
178
|
+
// --- Storyboard / seek thumbnails ---
|
|
179
|
+
prefetchStoryboard(playbackId: string): void;
|
|
180
|
+
getStoryboardData(playbackId: string): Promise<string>;
|
|
181
|
+
|
|
182
|
+
// --- Overlay lifecycle ---
|
|
183
|
+
notifyOverlayReady(): void;
|
|
184
|
+
|
|
181
185
|
// --- Event emitters ---
|
|
182
186
|
readonly onPlayerStateChanged: EventEmitter<PlayerStateEvent>;
|
|
183
187
|
readonly onCurrentItemChanged: EventEmitter<CurrentItemEvent>;
|
|
@@ -189,6 +193,7 @@ export interface Spec extends TurboModule {
|
|
|
189
193
|
readonly onActiveCueChanged: EventEmitter<CueEvent>;
|
|
190
194
|
readonly onDidLoop: EventEmitter<LoopEvent>;
|
|
191
195
|
readonly onFeedTransition: EventEmitter<FeedTransitionEvent>;
|
|
196
|
+
readonly onFeedScrollPhase: EventEmitter<FeedScrollPhaseEvent>;
|
|
192
197
|
readonly onFormatChange: EventEmitter<FormatChangeEvent>;
|
|
193
198
|
readonly onPrefetchedAheadCountChanged: EventEmitter<PrefetchedAheadCountEvent>;
|
|
194
199
|
readonly onRemainingContentCountChanged: EventEmitter<RemainingContentCountEvent>;
|
|
@@ -198,16 +203,13 @@ export interface Spec extends TurboModule {
|
|
|
198
203
|
readonly onOverlayConfigure: EventEmitter<OverlayConfigureEvent>;
|
|
199
204
|
readonly onOverlayActivate: EventEmitter<OverlayActivateEvent>;
|
|
200
205
|
readonly onOverlayReset: EventEmitter<OverlayResetEvent>;
|
|
201
|
-
readonly onOverlayFadeOut: EventEmitter<OverlayFadeOutEvent>;
|
|
202
|
-
readonly onOverlayRestore: EventEmitter<OverlayRestoreEvent>;
|
|
203
206
|
readonly onOverlayTap: EventEmitter<OverlayTapEvent>;
|
|
204
207
|
readonly onOverlayDoubleTap: EventEmitter<OverlayDoubleTapEvent>;
|
|
205
208
|
readonly onContentTapped: EventEmitter<ContentTappedEvent>;
|
|
206
209
|
readonly onCarouselOverlayConfigure: EventEmitter<CarouselOverlayConfigureEvent>;
|
|
207
210
|
readonly onCarouselOverlayActivate: EventEmitter<CarouselOverlayActivateEvent>;
|
|
208
211
|
readonly onCarouselOverlayReset: EventEmitter<CarouselOverlayResetEvent>;
|
|
209
|
-
readonly
|
|
210
|
-
readonly onCarouselOverlayRestore: EventEmitter<CarouselOverlayRestoreEvent>;
|
|
212
|
+
readonly onCarouselPageChanged: EventEmitter<CarouselPageChangedEvent>;
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
export default TurboModuleRegistry.getEnforcing<Spec>('ShortKitModule');
|
package/src/types.ts
CHANGED
|
@@ -65,7 +65,7 @@ export interface ImageCarouselItem {
|
|
|
65
65
|
articleUrl?: string;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export type
|
|
68
|
+
export type FeedInput =
|
|
69
69
|
| { type: 'video'; playbackId: string }
|
|
70
70
|
| { type: 'imageCarousel'; item: ImageCarouselItem };
|
|
71
71
|
|
|
@@ -76,6 +76,24 @@ export type JSONValue =
|
|
|
76
76
|
| null
|
|
77
77
|
| { [key: string]: JSONValue };
|
|
78
78
|
|
|
79
|
+
// --- Storyboard / Seek Thumbnails ---
|
|
80
|
+
|
|
81
|
+
export interface StoryboardTile {
|
|
82
|
+
start: number;
|
|
83
|
+
x: number;
|
|
84
|
+
y: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface StoryboardData {
|
|
88
|
+
url: string;
|
|
89
|
+
tileWidth: number;
|
|
90
|
+
tileHeight: number;
|
|
91
|
+
duration: number;
|
|
92
|
+
imageWidth: number;
|
|
93
|
+
imageHeight: number;
|
|
94
|
+
tiles: StoryboardTile[];
|
|
95
|
+
}
|
|
96
|
+
|
|
79
97
|
export interface CaptionTrack {
|
|
80
98
|
language: string;
|
|
81
99
|
label: string;
|
|
@@ -111,6 +129,10 @@ export interface FeedTransitionEvent {
|
|
|
111
129
|
direction: 'forward' | 'backward';
|
|
112
130
|
}
|
|
113
131
|
|
|
132
|
+
export type FeedScrollPhase =
|
|
133
|
+
| { phase: 'dragging'; fromId: string }
|
|
134
|
+
| { phase: 'settled' };
|
|
135
|
+
|
|
114
136
|
export interface FormatChangeEvent {
|
|
115
137
|
contentId: string;
|
|
116
138
|
fromBitrate: number;
|
|
@@ -136,7 +158,6 @@ export interface ShortKitError {
|
|
|
136
158
|
export interface ShortKitProviderProps {
|
|
137
159
|
apiKey: string;
|
|
138
160
|
config: FeedConfig;
|
|
139
|
-
embedId?: string;
|
|
140
161
|
userId?: string;
|
|
141
162
|
|
|
142
163
|
clientAppName?: string;
|
|
@@ -222,7 +243,7 @@ export interface ShortKitPlayerState {
|
|
|
222
243
|
prefetchedAheadCount: number;
|
|
223
244
|
remainingContentCount: number;
|
|
224
245
|
isActive: boolean;
|
|
225
|
-
|
|
246
|
+
feedScrollPhase: FeedScrollPhase | null;
|
|
226
247
|
lastOverlayTap: number;
|
|
227
248
|
lastOverlayDoubleTap: { x: number; y: number; id: number } | null;
|
|
228
249
|
|
|
@@ -238,4 +259,6 @@ export interface ShortKitPlayerState {
|
|
|
238
259
|
selectCaptionTrack: (language: string) => void;
|
|
239
260
|
sendContentSignal: (signal: ContentSignal) => void;
|
|
240
261
|
setMaxBitrate: (bitrate: number) => void;
|
|
262
|
+
prefetchStoryboard: (playbackId: string) => void;
|
|
263
|
+
getStoryboardData: (playbackId: string) => Promise<StoryboardData | null>;
|
|
241
264
|
}
|
|
@@ -5,7 +5,7 @@ import type { ImageCarouselItem } from './types';
|
|
|
5
5
|
export interface ShortKitCarouselState {
|
|
6
6
|
currentCarouselItem: ImageCarouselItem | null;
|
|
7
7
|
isCarouselActive: boolean;
|
|
8
|
-
|
|
8
|
+
currentCarouselPage: number;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -24,6 +24,6 @@ export function useShortKitCarousel(): ShortKitCarouselState {
|
|
|
24
24
|
return {
|
|
25
25
|
currentCarouselItem: context.currentCarouselItem,
|
|
26
26
|
isCarouselActive: context.isCarouselActive,
|
|
27
|
-
|
|
27
|
+
currentCarouselPage: context.currentCarouselPage,
|
|
28
28
|
};
|
|
29
29
|
}
|
package/src/useShortKitPlayer.ts
CHANGED
|
@@ -27,7 +27,6 @@ export function useShortKitPlayer(): ShortKitPlayerState {
|
|
|
27
27
|
currentCarouselItem: _currentCarouselItem,
|
|
28
28
|
nextCarouselItem: _nextCarouselItem,
|
|
29
29
|
isCarouselActive: _isCarouselActive,
|
|
30
|
-
isCarouselTransitioning: _isCarouselTransitioning,
|
|
31
30
|
_overlayConfig: _overlay,
|
|
32
31
|
_carouselOverlayConfig: _carouselOverlay,
|
|
33
32
|
...playerState
|