@shortkitsdk/react-native 0.2.1 → 0.2.3
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/ShortKitReactNative.podspec +8 -1
- package/android/src/main/java/com/shortkit/reactnative/ShortKitCarouselOverlayBridge.kt +48 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +48 -6
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +67 -5
- package/android/src/main/java/com/shortkit/reactnative/ShortKitOverlayBridge.kt +28 -1
- package/ios/ShortKitBridge.swift +47 -2
- package/ios/ShortKitCarouselOverlayBridge.swift +54 -0
- package/ios/ShortKitFeedView.swift +47 -8
- package/ios/ShortKitModule.mm +22 -2
- package/ios/ShortKitOverlayBridge.swift +24 -2
- package/ios/ShortKitPlayerNativeView.swift +1 -1
- package/ios/ShortKitSDK.xcframework/Info.plist +43 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +417 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +27739 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +790 -0
- 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 +790 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +417 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +27739 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +790 -0
- 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 +790 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitWidgetNativeView.swift +1 -1
- package/package.json +1 -1
- package/src/CarouselOverlayManager.tsx +71 -0
- package/src/ShortKitContext.ts +13 -0
- package/src/ShortKitFeed.tsx +3 -0
- package/src/ShortKitProvider.tsx +118 -2
- package/src/index.ts +3 -1
- package/src/serialization.ts +6 -2
- package/src/specs/NativeShortKitModule.ts +19 -0
- package/src/types.ts +4 -3
- package/src/useShortKitCarousel.ts +29 -0
- package/src/useShortKitPlayer.ts +10 -2
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import type { CarouselOverlayConfig } from './types';
|
|
4
|
+
import { ShortKitContext } from './ShortKitContext';
|
|
5
|
+
import type { ShortKitContextValue } from './ShortKitContext';
|
|
6
|
+
|
|
7
|
+
interface CarouselOverlayManagerProps {
|
|
8
|
+
carouselOverlay: CarouselOverlayConfig;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal component that renders TWO instances of the developer's custom
|
|
13
|
+
* carousel overlay component — one for the current cell and one for the next.
|
|
14
|
+
*
|
|
15
|
+
* Works identically to `OverlayManager` but for image carousel cells.
|
|
16
|
+
* The native side finds these views by their `nativeID` and applies
|
|
17
|
+
* scroll-tracking transforms so each overlay moves with its respective
|
|
18
|
+
* carousel cell during swipe transitions.
|
|
19
|
+
*/
|
|
20
|
+
export function CarouselOverlayManager({ carouselOverlay }: CarouselOverlayManagerProps) {
|
|
21
|
+
if (
|
|
22
|
+
carouselOverlay === 'none' ||
|
|
23
|
+
typeof carouselOverlay === 'string' ||
|
|
24
|
+
carouselOverlay.type !== 'custom' ||
|
|
25
|
+
!('component' in carouselOverlay)
|
|
26
|
+
) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const CarouselComponent = carouselOverlay.component;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<View style={StyleSheet.absoluteFill} nativeID="carousel-overlay-current" pointerEvents="box-none">
|
|
35
|
+
<CarouselComponent />
|
|
36
|
+
</View>
|
|
37
|
+
<View style={StyleSheet.absoluteFill} nativeID="carousel-overlay-next" pointerEvents="box-none">
|
|
38
|
+
<NextCarouselOverlayProvider>
|
|
39
|
+
<CarouselComponent />
|
|
40
|
+
</NextCarouselOverlayProvider>
|
|
41
|
+
</View>
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Wraps children with a modified ShortKitContext where `currentCarouselItem`
|
|
48
|
+
* is set to the provider's `nextCarouselItem`.
|
|
49
|
+
*/
|
|
50
|
+
function NextCarouselOverlayProvider({ children }: { children: React.ReactNode }) {
|
|
51
|
+
const context = useContext(ShortKitContext);
|
|
52
|
+
|
|
53
|
+
const nextValue: ShortKitContextValue | null = useMemo(() => {
|
|
54
|
+
if (!context) return null;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...context,
|
|
58
|
+
currentCarouselItem: context.nextCarouselItem,
|
|
59
|
+
isCarouselActive: context.nextCarouselItem != null,
|
|
60
|
+
isCarouselTransitioning: false,
|
|
61
|
+
};
|
|
62
|
+
}, [context]);
|
|
63
|
+
|
|
64
|
+
if (!nextValue) return <>{children}</>;
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<ShortKitContext.Provider value={nextValue}>
|
|
68
|
+
{children}
|
|
69
|
+
</ShortKitContext.Provider>
|
|
70
|
+
);
|
|
71
|
+
}
|
package/src/ShortKitContext.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
import type {
|
|
3
3
|
ContentItem,
|
|
4
|
+
ImageCarouselItem,
|
|
4
5
|
CustomFeedItem,
|
|
5
6
|
PlayerTime,
|
|
6
7
|
PlayerState,
|
|
7
8
|
CaptionTrack,
|
|
8
9
|
ContentSignal,
|
|
9
10
|
OverlayConfig,
|
|
11
|
+
CarouselOverlayConfig,
|
|
10
12
|
} from './types';
|
|
11
13
|
|
|
12
14
|
export interface ShortKitContextValue {
|
|
@@ -48,9 +50,20 @@ export interface ShortKitContextValue {
|
|
|
48
50
|
appendFeedItems: (items: CustomFeedItem[]) => void;
|
|
49
51
|
fetchContent: (limit?: number) => Promise<ContentItem[]>;
|
|
50
52
|
|
|
53
|
+
// Carousel overlay state
|
|
54
|
+
currentCarouselItem: ImageCarouselItem | null;
|
|
55
|
+
nextCarouselItem: ImageCarouselItem | null;
|
|
56
|
+
isCarouselActive: boolean;
|
|
57
|
+
isCarouselTransitioning: boolean;
|
|
58
|
+
|
|
59
|
+
// Active cell type — used by overlay managers to show/hide
|
|
60
|
+
activeCellType: 'video' | 'carousel' | null;
|
|
61
|
+
|
|
51
62
|
// Internal — used by ShortKitFeed to render custom overlays
|
|
52
63
|
/** @internal */
|
|
53
64
|
_overlayConfig: OverlayConfig;
|
|
65
|
+
/** @internal */
|
|
66
|
+
_carouselOverlayConfig: CarouselOverlayConfig;
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
export const ShortKitContext = createContext<ShortKitContextValue | null>(null);
|
package/src/ShortKitFeed.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import type { ShortKitFeedProps } from './types';
|
|
|
4
4
|
import ShortKitFeedView from './specs/ShortKitFeedViewNativeComponent';
|
|
5
5
|
import NativeShortKitModule from './specs/NativeShortKitModule';
|
|
6
6
|
import { OverlayManager } from './OverlayManager';
|
|
7
|
+
import { CarouselOverlayManager } from './CarouselOverlayManager';
|
|
7
8
|
import { ShortKitContext } from './ShortKitContext';
|
|
8
9
|
import { deserializeContentItem } from './serialization';
|
|
9
10
|
|
|
@@ -38,6 +39,7 @@ export function ShortKitFeed(props: ShortKitFeedProps) {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const overlayConfig = context._overlayConfig;
|
|
42
|
+
const carouselOverlayConfig = context._carouselOverlayConfig;
|
|
41
43
|
|
|
42
44
|
// ---------------------------------------------------------------------------
|
|
43
45
|
// Subscribe to feed-level events and forward to callback props
|
|
@@ -143,6 +145,7 @@ export function ShortKitFeed(props: ShortKitFeedProps) {
|
|
|
143
145
|
config="{}"
|
|
144
146
|
/>
|
|
145
147
|
<OverlayManager overlay={overlayConfig} />
|
|
148
|
+
<CarouselOverlayManager carouselOverlay={carouselOverlayConfig} />
|
|
146
149
|
</View>
|
|
147
150
|
);
|
|
148
151
|
}
|
package/src/ShortKitProvider.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import type { ShortKitContextValue } from './ShortKitContext';
|
|
|
6
6
|
import type {
|
|
7
7
|
ShortKitProviderProps,
|
|
8
8
|
ContentItem,
|
|
9
|
+
ImageCarouselItem,
|
|
9
10
|
CustomFeedItem,
|
|
10
11
|
PlayerTime,
|
|
11
12
|
PlayerState,
|
|
@@ -41,6 +42,11 @@ interface State {
|
|
|
41
42
|
isTransitioning: boolean;
|
|
42
43
|
lastOverlayTap: number;
|
|
43
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;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
const initialState: State = {
|
|
@@ -59,6 +65,11 @@ const initialState: State = {
|
|
|
59
65
|
isTransitioning: false,
|
|
60
66
|
lastOverlayTap: 0,
|
|
61
67
|
lastOverlayDoubleTap: null,
|
|
68
|
+
currentCarouselItem: null,
|
|
69
|
+
nextCarouselItem: null,
|
|
70
|
+
isCarouselActive: false,
|
|
71
|
+
isCarouselTransitioning: false,
|
|
72
|
+
activeCellType: null,
|
|
62
73
|
};
|
|
63
74
|
|
|
64
75
|
type Action =
|
|
@@ -78,7 +89,13 @@ type Action =
|
|
|
78
89
|
| { type: 'OVERLAY_FADE_OUT' }
|
|
79
90
|
| { type: 'OVERLAY_RESTORE' }
|
|
80
91
|
| { type: 'OVERLAY_TAP' }
|
|
81
|
-
| { type: 'OVERLAY_DOUBLE_TAP'; payload: { x: number; y: number } }
|
|
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' };
|
|
82
99
|
|
|
83
100
|
function reducer(state: State, action: Action): State {
|
|
84
101
|
switch (action.type) {
|
|
@@ -91,10 +108,13 @@ function reducer(state: State, action: Action): State {
|
|
|
91
108
|
action.payload === 'paused' ||
|
|
92
109
|
action.payload === 'buffering' ||
|
|
93
110
|
action.payload === 'seeking';
|
|
111
|
+
const becameActive = !state.isActive && isPlaybackActive;
|
|
94
112
|
return {
|
|
95
113
|
...state,
|
|
96
114
|
playerState: action.payload,
|
|
97
115
|
isActive: state.isActive || isPlaybackActive,
|
|
116
|
+
// First playback means a video cell is active (initial load)
|
|
117
|
+
activeCellType: becameActive ? 'video' : state.activeCellType,
|
|
98
118
|
};
|
|
99
119
|
}
|
|
100
120
|
case 'CURRENT_ITEM':
|
|
@@ -118,7 +138,20 @@ function reducer(state: State, action: Action): State {
|
|
|
118
138
|
case 'OVERLAY_CONFIGURE':
|
|
119
139
|
return { ...state, nextItem: action.payload };
|
|
120
140
|
case 'OVERLAY_ACTIVATE':
|
|
121
|
-
|
|
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
|
+
};
|
|
122
155
|
case 'OVERLAY_RESET':
|
|
123
156
|
// Don't set isActive = false — the overlay stays mounted during
|
|
124
157
|
// transitions. In the native SDK each cell has its own overlay
|
|
@@ -140,6 +173,18 @@ function reducer(state: State, action: Action): State {
|
|
|
140
173
|
id: (state.lastOverlayDoubleTap?.id ?? 0) + 1,
|
|
141
174
|
},
|
|
142
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 };
|
|
143
188
|
default:
|
|
144
189
|
return state;
|
|
145
190
|
}
|
|
@@ -397,6 +442,61 @@ export function ShortKitProvider({
|
|
|
397
442
|
}),
|
|
398
443
|
);
|
|
399
444
|
|
|
445
|
+
// Feed transition — track active cell type
|
|
446
|
+
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;
|
|
451
|
+
dispatch({
|
|
452
|
+
type: 'ACTIVE_CELL_TYPE',
|
|
453
|
+
payload: isVideo ? 'video' : 'carousel',
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}),
|
|
457
|
+
);
|
|
458
|
+
|
|
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
|
+
|
|
400
500
|
// Note: Feed-level callback events (onDidLoop, onFeedTransition,
|
|
401
501
|
// onShareTapped, etc.) are subscribed by the ShortKitFeed component
|
|
402
502
|
// (Task 11), not here. The provider only manages state-driving events.
|
|
@@ -526,8 +626,18 @@ export function ShortKitProvider({
|
|
|
526
626
|
appendFeedItems: appendFeedItemsCmd,
|
|
527
627
|
fetchContent: fetchContentCmd,
|
|
528
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
|
+
|
|
529
638
|
// Internal — consumed by ShortKitFeed to pass to OverlayManager
|
|
530
639
|
_overlayConfig: config.overlay ?? 'none',
|
|
640
|
+
_carouselOverlayConfig: config.carouselOverlay ?? 'none',
|
|
531
641
|
}),
|
|
532
642
|
[
|
|
533
643
|
state.playerState,
|
|
@@ -545,6 +655,11 @@ export function ShortKitProvider({
|
|
|
545
655
|
state.isTransitioning,
|
|
546
656
|
state.lastOverlayTap,
|
|
547
657
|
state.lastOverlayDoubleTap,
|
|
658
|
+
state.activeCellType,
|
|
659
|
+
state.currentCarouselItem,
|
|
660
|
+
state.nextCarouselItem,
|
|
661
|
+
state.isCarouselActive,
|
|
662
|
+
state.isCarouselTransitioning,
|
|
548
663
|
play,
|
|
549
664
|
pause,
|
|
550
665
|
seek,
|
|
@@ -563,6 +678,7 @@ export function ShortKitProvider({
|
|
|
563
678
|
appendFeedItemsCmd,
|
|
564
679
|
fetchContentCmd,
|
|
565
680
|
config.overlay,
|
|
681
|
+
config.carouselOverlay,
|
|
566
682
|
],
|
|
567
683
|
);
|
|
568
684
|
|
package/src/index.ts
CHANGED
|
@@ -4,12 +4,13 @@ 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';
|
|
7
8
|
export type {
|
|
8
9
|
FeedConfig,
|
|
9
10
|
FeedHeight,
|
|
10
11
|
FeedSource,
|
|
11
12
|
OverlayConfig,
|
|
12
|
-
|
|
13
|
+
CarouselOverlayConfig,
|
|
13
14
|
SurveyMode,
|
|
14
15
|
|
|
15
16
|
PlayerConfig,
|
|
@@ -36,3 +37,4 @@ export type {
|
|
|
36
37
|
ShortKitWidgetProps,
|
|
37
38
|
ShortKitPlayerState,
|
|
38
39
|
} from './types';
|
|
40
|
+
export type { ShortKitCarouselState } from './useShortKitCarousel';
|
package/src/serialization.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
export interface SerializedFeedConfig {
|
|
14
14
|
feedHeight: string;
|
|
15
15
|
overlay: string;
|
|
16
|
-
|
|
16
|
+
carouselOverlay: string;
|
|
17
17
|
surveyMode: string;
|
|
18
18
|
muteOnStart: boolean;
|
|
19
19
|
feedSource: string;
|
|
@@ -37,7 +37,11 @@ export function serializeFeedConfig(config: FeedConfig): SerializedFeedConfig {
|
|
|
37
37
|
return {
|
|
38
38
|
feedHeight: JSON.stringify(config.feedHeight ?? { type: 'fullscreen' }),
|
|
39
39
|
overlay,
|
|
40
|
-
|
|
40
|
+
carouselOverlay: (() => {
|
|
41
|
+
const raw = config.carouselOverlay ?? 'none';
|
|
42
|
+
if (typeof raw === 'string') return JSON.stringify(raw);
|
|
43
|
+
return JSON.stringify({ type: 'custom' });
|
|
44
|
+
})(),
|
|
41
45
|
surveyMode: JSON.stringify(config.surveyMode ?? 'none'),
|
|
42
46
|
muteOnStart: config.muteOnStart ?? true,
|
|
43
47
|
feedSource: config.feedSource ?? 'algorithmic',
|
|
@@ -122,6 +122,20 @@ type ContentTappedEvent = Readonly<{
|
|
|
122
122
|
index: Int32;
|
|
123
123
|
}>;
|
|
124
124
|
|
|
125
|
+
type CarouselOverlayConfigureEvent = Readonly<{
|
|
126
|
+
item: string; // JSON-serialized ImageCarouselItem
|
|
127
|
+
}>;
|
|
128
|
+
|
|
129
|
+
type CarouselOverlayActivateEvent = Readonly<{
|
|
130
|
+
item: string; // JSON-serialized ImageCarouselItem
|
|
131
|
+
}>;
|
|
132
|
+
|
|
133
|
+
type CarouselOverlayResetEvent = Readonly<{}>;
|
|
134
|
+
|
|
135
|
+
type CarouselOverlayFadeOutEvent = Readonly<{}>;
|
|
136
|
+
|
|
137
|
+
type CarouselOverlayRestoreEvent = Readonly<{}>;
|
|
138
|
+
|
|
125
139
|
type OverlayTapEvent = Readonly<{}>;
|
|
126
140
|
|
|
127
141
|
type OverlayDoubleTapEvent = Readonly<{
|
|
@@ -189,6 +203,11 @@ export interface Spec extends TurboModule {
|
|
|
189
203
|
readonly onOverlayTap: EventEmitter<OverlayTapEvent>;
|
|
190
204
|
readonly onOverlayDoubleTap: EventEmitter<OverlayDoubleTapEvent>;
|
|
191
205
|
readonly onContentTapped: EventEmitter<ContentTappedEvent>;
|
|
206
|
+
readonly onCarouselOverlayConfigure: EventEmitter<CarouselOverlayConfigureEvent>;
|
|
207
|
+
readonly onCarouselOverlayActivate: EventEmitter<CarouselOverlayActivateEvent>;
|
|
208
|
+
readonly onCarouselOverlayReset: EventEmitter<CarouselOverlayResetEvent>;
|
|
209
|
+
readonly onCarouselOverlayFadeOut: EventEmitter<CarouselOverlayFadeOutEvent>;
|
|
210
|
+
readonly onCarouselOverlayRestore: EventEmitter<CarouselOverlayRestoreEvent>;
|
|
192
211
|
}
|
|
193
212
|
|
|
194
213
|
export default TurboModuleRegistry.getEnforcing<Spec>('ShortKitModule');
|
package/src/types.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type FeedSource = 'algorithmic' | 'custom';
|
|
|
7
7
|
export interface FeedConfig {
|
|
8
8
|
feedHeight?: FeedHeight;
|
|
9
9
|
overlay?: OverlayConfig;
|
|
10
|
-
|
|
10
|
+
carouselOverlay?: CarouselOverlayConfig;
|
|
11
11
|
surveyMode?: SurveyMode;
|
|
12
12
|
muteOnStart?: boolean;
|
|
13
13
|
feedSource?: FeedSource;
|
|
@@ -21,9 +21,9 @@ export type OverlayConfig =
|
|
|
21
21
|
| 'none'
|
|
22
22
|
| { type: 'custom'; component: React.ComponentType };
|
|
23
23
|
|
|
24
|
-
export type
|
|
24
|
+
export type CarouselOverlayConfig =
|
|
25
25
|
| 'none'
|
|
26
|
-
| { type: '
|
|
26
|
+
| { type: 'custom'; component: React.ComponentType };
|
|
27
27
|
|
|
28
28
|
export type SurveyMode =
|
|
29
29
|
| 'none'
|
|
@@ -212,6 +212,7 @@ export interface ShortKitPlayerState {
|
|
|
212
212
|
playerState: PlayerState;
|
|
213
213
|
currentItem: ContentItem | null;
|
|
214
214
|
nextItem: ContentItem | null;
|
|
215
|
+
activeCellType: 'video' | 'carousel' | null;
|
|
215
216
|
time: PlayerTime;
|
|
216
217
|
isMuted: boolean;
|
|
217
218
|
playbackRate: number;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { ShortKitContext } from './ShortKitContext';
|
|
3
|
+
import type { ImageCarouselItem } from './types';
|
|
4
|
+
|
|
5
|
+
export interface ShortKitCarouselState {
|
|
6
|
+
currentCarouselItem: ImageCarouselItem | null;
|
|
7
|
+
isCarouselActive: boolean;
|
|
8
|
+
isCarouselTransitioning: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hook to access carousel overlay state from the nearest ShortKitProvider.
|
|
13
|
+
*
|
|
14
|
+
* Use this inside a custom carousel overlay component to get the current
|
|
15
|
+
* `ImageCarouselItem` data (images, title, description, etc.).
|
|
16
|
+
*
|
|
17
|
+
* Must be used within a `<ShortKitProvider>`.
|
|
18
|
+
*/
|
|
19
|
+
export function useShortKitCarousel(): ShortKitCarouselState {
|
|
20
|
+
const context = useContext(ShortKitContext);
|
|
21
|
+
if (!context) {
|
|
22
|
+
throw new Error('useShortKitCarousel must be used within a ShortKitProvider');
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
currentCarouselItem: context.currentCarouselItem,
|
|
26
|
+
isCarouselActive: context.isCarouselActive,
|
|
27
|
+
isCarouselTransitioning: context.isCarouselTransitioning,
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/useShortKitPlayer.ts
CHANGED
|
@@ -16,12 +16,20 @@ export function useShortKitPlayer(): ShortKitPlayerState {
|
|
|
16
16
|
throw new Error('useShortKitPlayer must be used within a ShortKitProvider');
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// Return only player-related state and commands (exclude SDK operations
|
|
20
|
-
// and internal fields)
|
|
19
|
+
// Return only player-related state and commands (exclude SDK operations,
|
|
20
|
+
// carousel state, and internal fields)
|
|
21
21
|
const {
|
|
22
22
|
setUserId: _setUserId,
|
|
23
23
|
clearUserId: _clearUserId,
|
|
24
|
+
setFeedItems: _setFeedItems,
|
|
25
|
+
appendFeedItems: _appendFeedItems,
|
|
26
|
+
fetchContent: _fetchContent,
|
|
27
|
+
currentCarouselItem: _currentCarouselItem,
|
|
28
|
+
nextCarouselItem: _nextCarouselItem,
|
|
29
|
+
isCarouselActive: _isCarouselActive,
|
|
30
|
+
isCarouselTransitioning: _isCarouselTransitioning,
|
|
24
31
|
_overlayConfig: _overlay,
|
|
32
|
+
_carouselOverlayConfig: _carouselOverlay,
|
|
25
33
|
...playerState
|
|
26
34
|
} = context;
|
|
27
35
|
|