@shortkitsdk/react-native 0.2.12 → 0.2.14
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/ReactCarouselOverlayHost.kt +47 -4
- package/android/src/main/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHost.kt +431 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +83 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +2 -0
- package/ios/ReactCarouselOverlayHost.swift +37 -17
- package/ios/ReactOverlayHost.swift +20 -8
- package/ios/ReactVideoCarouselOverlayHost.swift +283 -0
- package/ios/ShortKitBridge.swift +42 -0
- package/ios/ShortKitModule.mm +2 -1
- package/ios/ShortKitSDK.xcframework/Info.plist +4 -4
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +1833 -201
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +51 -1
- 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 +51 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +168 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1833 -201
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +51 -1
- 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 +51 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +168 -0
- package/ios/ShortKitSDK.xcframework.bak2/Info.plist +43 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +31351 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +31351 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/package.json +1 -1
- package/src/ShortKitCarouselOverlaySurface.tsx +57 -2
- package/src/ShortKitFeed.tsx +5 -1
- package/src/ShortKitOverlaySurface.tsx +4 -5
- package/src/ShortKitVideoCarouselOverlaySurface.tsx +156 -0
- package/src/index.ts +4 -1
- package/src/serialization.ts +7 -0
- package/src/specs/NativeShortKitModule.ts +13 -0
- package/src/types.ts +39 -1
package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
2
|
import { AppRegistry } from 'react-native';
|
|
3
3
|
import type { CarouselOverlayProps, ImageCarouselItem } from './types';
|
|
4
|
+
import NativeShortKitModule from './specs/NativeShortKitModule';
|
|
4
5
|
|
|
5
6
|
const _carouselRegistry = new Map<string, React.ComponentType<CarouselOverlayProps>>();
|
|
6
7
|
|
|
@@ -24,7 +25,30 @@ export function registerCarouselOverlayComponent(
|
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
/** Subscribe to a named native event, filtered by surfaceId. */
|
|
29
|
+
function useOverlayEvent<T extends { surfaceId: string }>(
|
|
30
|
+
eventName: string,
|
|
31
|
+
surfaceId: string | undefined,
|
|
32
|
+
handler: (event: T) => void,
|
|
33
|
+
) {
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (!surfaceId) return;
|
|
36
|
+
|
|
37
|
+
let sub: { remove: () => void } | undefined;
|
|
38
|
+
const emitter = NativeShortKitModule?.[eventName as keyof typeof NativeShortKitModule];
|
|
39
|
+
if (typeof emitter === 'function') {
|
|
40
|
+
sub = (emitter as (cb: (e: T) => void) => { remove: () => void })((e: T) => {
|
|
41
|
+
if (e.surfaceId !== surfaceId) return;
|
|
42
|
+
handler(e);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return () => sub?.remove();
|
|
47
|
+
}, [surfaceId]);
|
|
48
|
+
}
|
|
49
|
+
|
|
27
50
|
interface RawCarouselSurfaceProps {
|
|
51
|
+
surfaceId?: string;
|
|
28
52
|
item?: string;
|
|
29
53
|
}
|
|
30
54
|
|
|
@@ -38,6 +62,31 @@ function CarouselSurfaceInner(props: InnerProps) {
|
|
|
38
62
|
return null;
|
|
39
63
|
}
|
|
40
64
|
|
|
65
|
+
const sid = props.surfaceId;
|
|
66
|
+
|
|
67
|
+
const [isActive, setIsActive] = useState(false);
|
|
68
|
+
const [activeImageIndex, setActiveImageIndex] = useState(0);
|
|
69
|
+
|
|
70
|
+
// isActive delivered via onOverlayFullState (same event as video overlay)
|
|
71
|
+
useOverlayEvent<{
|
|
72
|
+
surfaceId: string;
|
|
73
|
+
isActive: boolean;
|
|
74
|
+
playerState: string;
|
|
75
|
+
isMuted: boolean;
|
|
76
|
+
playbackRate: number;
|
|
77
|
+
captionsEnabled: boolean;
|
|
78
|
+
activeCue: string | null;
|
|
79
|
+
feedScrollPhase: string | null;
|
|
80
|
+
}>('onOverlayFullState', sid, (e) => {
|
|
81
|
+
setIsActive(e.isActive);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// activeImageIndex delivered via dedicated event
|
|
85
|
+
useOverlayEvent<{ surfaceId: string; activeImageIndex: number }>(
|
|
86
|
+
'onCarouselActiveImageChanged', sid,
|
|
87
|
+
(e) => setActiveImageIndex(e.activeImageIndex),
|
|
88
|
+
);
|
|
89
|
+
|
|
41
90
|
const item: ImageCarouselItem | null = useMemo(() => {
|
|
42
91
|
if (!props.item) {
|
|
43
92
|
return null;
|
|
@@ -51,5 +100,11 @@ function CarouselSurfaceInner(props: InnerProps) {
|
|
|
51
100
|
|
|
52
101
|
if (!item) return null;
|
|
53
102
|
|
|
54
|
-
return
|
|
103
|
+
return (
|
|
104
|
+
<Component
|
|
105
|
+
item={item}
|
|
106
|
+
isActive={isActive}
|
|
107
|
+
activeImageIndex={activeImageIndex}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
55
110
|
}
|
package/src/ShortKitFeed.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import { ShortKitContext } from './ShortKitContext';
|
|
|
7
7
|
import { deserializeContentItem, serializeFeedConfig, serializeFeedInputs } from './serialization';
|
|
8
8
|
import { registerOverlayComponent } from './ShortKitOverlaySurface';
|
|
9
9
|
import { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurface';
|
|
10
|
+
import { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOverlaySurface';
|
|
10
11
|
|
|
11
12
|
function generateFeedId(): string {
|
|
12
13
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
@@ -103,7 +104,10 @@ export const ShortKitFeed = forwardRef<ShortKitFeedHandle, ShortKitFeedProps>(
|
|
|
103
104
|
if (config?.carouselOverlay && config.carouselOverlay !== 'none') {
|
|
104
105
|
registerCarouselOverlayComponent(config.carouselOverlay.name, config.carouselOverlay.component);
|
|
105
106
|
}
|
|
106
|
-
|
|
107
|
+
if (config?.videoCarouselOverlay && config.videoCarouselOverlay !== 'none') {
|
|
108
|
+
registerVideoCarouselOverlayComponent(config.videoCarouselOverlay.name, config.videoCarouselOverlay.component);
|
|
109
|
+
}
|
|
110
|
+
}, [config?.overlay, config?.carouselOverlay, config?.videoCarouselOverlay]);
|
|
107
111
|
|
|
108
112
|
const serializedConfig = useMemo(
|
|
109
113
|
() => (config ? serializeFeedConfig(config) : '{}'),
|
|
@@ -252,11 +252,10 @@ function ShortKitOverlaySurfaceInner(props: InnerProps) {
|
|
|
252
252
|
},
|
|
253
253
|
);
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// );
|
|
255
|
+
useOverlayEvent<{ surfaceId: string; current: number; duration: number; buffered: number }>(
|
|
256
|
+
'onOverlayTimeUpdate', sid,
|
|
257
|
+
(e) => setTime({ current: e.current, duration: e.duration, buffered: e.buffered }),
|
|
258
|
+
);
|
|
260
259
|
|
|
261
260
|
// Batched full-state sync — replaces 7 individual events on swipe settle.
|
|
262
261
|
// All setState calls within one handler = one React render (auto-batched).
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { AppRegistry } from 'react-native';
|
|
3
|
+
import type { VideoCarouselOverlayProps, VideoCarouselItem, ContentItem, PlayerTime, PlayerState } from './types';
|
|
4
|
+
import NativeShortKitModule from './specs/NativeShortKitModule';
|
|
5
|
+
|
|
6
|
+
const _videoCarouselRegistry = new Map<string, React.ComponentType<VideoCarouselOverlayProps>>();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Register a named video carousel overlay component for rendering inside feed cells.
|
|
10
|
+
* Called by ShortKitFeed on mount via useLayoutEffect.
|
|
11
|
+
* Idempotent — registering the same name twice is a no-op.
|
|
12
|
+
*/
|
|
13
|
+
export function registerVideoCarouselOverlayComponent(
|
|
14
|
+
name: string,
|
|
15
|
+
component: React.ComponentType<VideoCarouselOverlayProps>,
|
|
16
|
+
) {
|
|
17
|
+
if (_videoCarouselRegistry.has(name)) return;
|
|
18
|
+
_videoCarouselRegistry.set(name, component);
|
|
19
|
+
|
|
20
|
+
const moduleName = `ShortKitVideoCarouselOverlay_${name}`;
|
|
21
|
+
AppRegistry.registerComponent(moduleName, () => {
|
|
22
|
+
return function NamedVideoCarouselSurface(props: RawProps) {
|
|
23
|
+
return <VideoCarouselSurfaceInner {...props} overlayName={name} />;
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Subscribe to a named native event, filtered by surfaceId. */
|
|
29
|
+
function useOverlayEvent<T extends { surfaceId: string }>(
|
|
30
|
+
eventName: string,
|
|
31
|
+
surfaceId: string | undefined,
|
|
32
|
+
handler: (event: T) => void,
|
|
33
|
+
) {
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (!surfaceId) return;
|
|
36
|
+
|
|
37
|
+
let sub: { remove: () => void } | undefined;
|
|
38
|
+
const emitter = NativeShortKitModule?.[eventName as keyof typeof NativeShortKitModule];
|
|
39
|
+
if (typeof emitter === 'function') {
|
|
40
|
+
sub = (emitter as (cb: (e: T) => void) => { remove: () => void })((e: T) => {
|
|
41
|
+
if (e.surfaceId !== surfaceId) return;
|
|
42
|
+
handler(e);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return () => sub?.remove();
|
|
47
|
+
}, [surfaceId]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface RawProps {
|
|
51
|
+
surfaceId?: string;
|
|
52
|
+
carouselItem?: string;
|
|
53
|
+
activeVideo?: string;
|
|
54
|
+
activeVideoIndex?: number;
|
|
55
|
+
isActive?: boolean;
|
|
56
|
+
playerState?: string;
|
|
57
|
+
isMuted?: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface InnerProps extends RawProps {
|
|
61
|
+
overlayName: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function VideoCarouselSurfaceInner(props: InnerProps) {
|
|
65
|
+
const Component = _videoCarouselRegistry.get(props.overlayName);
|
|
66
|
+
if (!Component) return null;
|
|
67
|
+
|
|
68
|
+
const sid = props.surfaceId;
|
|
69
|
+
|
|
70
|
+
// Playback state — initialized from surface props, updated via events
|
|
71
|
+
const [isActive, setIsActive] = useState(props.isActive ?? false);
|
|
72
|
+
const [playerState, setPlayerState] = useState<PlayerState>((props.playerState as PlayerState) ?? 'idle');
|
|
73
|
+
const [isMuted, setIsMuted] = useState(props.isMuted ?? true);
|
|
74
|
+
const [time, setTime] = useState<PlayerTime>({ current: 0, duration: 0, buffered: 0 });
|
|
75
|
+
|
|
76
|
+
// Batched full-state sync
|
|
77
|
+
useOverlayEvent<{
|
|
78
|
+
surfaceId: string;
|
|
79
|
+
isActive: boolean;
|
|
80
|
+
playerState: string;
|
|
81
|
+
isMuted: boolean;
|
|
82
|
+
playbackRate: number;
|
|
83
|
+
captionsEnabled: boolean;
|
|
84
|
+
activeCue: string | null;
|
|
85
|
+
feedScrollPhase: string | null;
|
|
86
|
+
}>('onOverlayFullState', sid, (e) => {
|
|
87
|
+
setIsActive(e.isActive);
|
|
88
|
+
setPlayerState(e.playerState as PlayerState);
|
|
89
|
+
setIsMuted(e.isMuted);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Individual state updates
|
|
93
|
+
useOverlayEvent<{ surfaceId: string; playerState: string }>(
|
|
94
|
+
'onOverlayPlayerStateChanged', sid,
|
|
95
|
+
(e) => setPlayerState(e.playerState as PlayerState),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
useOverlayEvent<{ surfaceId: string; isMuted: boolean }>(
|
|
99
|
+
'onOverlayMutedChanged', sid,
|
|
100
|
+
(e) => setIsMuted(e.isMuted),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Time updates (250ms coalesced)
|
|
104
|
+
useOverlayEvent<{ surfaceId: string; current: number; duration: number; buffered: number }>(
|
|
105
|
+
'onOverlayTimeUpdate', sid,
|
|
106
|
+
(e) => setTime({ current: e.current, duration: e.duration, buffered: e.buffered }),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Carousel item — from surface props (set once in configure())
|
|
110
|
+
const carouselItem: VideoCarouselItem | null = useMemo(() => {
|
|
111
|
+
if (!props.carouselItem) return null;
|
|
112
|
+
try { return JSON.parse(props.carouselItem); } catch { return null; }
|
|
113
|
+
}, [props.carouselItem]);
|
|
114
|
+
|
|
115
|
+
// Active video — initialized from surface props, updated via events on swipe
|
|
116
|
+
const initialActiveVideo: ContentItem | null = useMemo(() => {
|
|
117
|
+
if (!props.activeVideo) return null;
|
|
118
|
+
try { return JSON.parse(props.activeVideo); } catch { return null; }
|
|
119
|
+
}, [props.activeVideo]);
|
|
120
|
+
|
|
121
|
+
const [activeVideo, setActiveVideo] = useState<ContentItem | null>(initialActiveVideo);
|
|
122
|
+
const [activeVideoIndex, setActiveVideoIndex] = useState(props.activeVideoIndex ?? 0);
|
|
123
|
+
|
|
124
|
+
// Sync from props when surface is reconfigured (new carousel item)
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
setActiveVideo(initialActiveVideo);
|
|
127
|
+
setActiveVideoIndex(props.activeVideoIndex ?? 0);
|
|
128
|
+
}, [initialActiveVideo]);
|
|
129
|
+
|
|
130
|
+
// Active video changes via event (no Fabric remount)
|
|
131
|
+
useOverlayEvent<{ surfaceId: string; activeVideo: string; activeVideoIndex: number }>(
|
|
132
|
+
'onVideoCarouselActiveVideoChanged', sid,
|
|
133
|
+
(e) => {
|
|
134
|
+
try {
|
|
135
|
+
setActiveVideo(JSON.parse(e.activeVideo));
|
|
136
|
+
} catch {
|
|
137
|
+
// ignore parse errors
|
|
138
|
+
}
|
|
139
|
+
setActiveVideoIndex(e.activeVideoIndex);
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (!carouselItem || !activeVideo) return null;
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Component
|
|
147
|
+
carouselItem={carouselItem}
|
|
148
|
+
activeVideo={activeVideo}
|
|
149
|
+
activeVideoIndex={activeVideoIndex}
|
|
150
|
+
isActive={isActive}
|
|
151
|
+
time={time}
|
|
152
|
+
playerState={playerState}
|
|
153
|
+
isMuted={isMuted}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export type {
|
|
|
11
11
|
FeedSource,
|
|
12
12
|
OverlayConfig,
|
|
13
13
|
CarouselOverlayConfig,
|
|
14
|
+
VideoCarouselOverlayConfig,
|
|
14
15
|
SurveyMode,
|
|
15
16
|
|
|
16
17
|
PlayerConfig,
|
|
@@ -20,6 +21,7 @@ export type {
|
|
|
20
21
|
ContentItem,
|
|
21
22
|
CarouselImage,
|
|
22
23
|
ImageCarouselItem,
|
|
24
|
+
VideoCarouselItem,
|
|
23
25
|
FeedInput,
|
|
24
26
|
JSONValue,
|
|
25
27
|
CaptionTrack,
|
|
@@ -46,4 +48,5 @@ export { ShortKitCommands } from './ShortKitCommands';
|
|
|
46
48
|
export { default as NativeShortKitModule } from './specs/NativeShortKitModule';
|
|
47
49
|
export { registerOverlayComponent } from './ShortKitOverlaySurface';
|
|
48
50
|
export { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurface';
|
|
49
|
-
export
|
|
51
|
+
export { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOverlaySurface';
|
|
52
|
+
export type { OverlayProps, CarouselOverlayProps, VideoCarouselOverlayProps } from './types';
|
package/src/serialization.ts
CHANGED
|
@@ -25,11 +25,18 @@ export function serializeFeedConfig(config: FeedConfig): string {
|
|
|
25
25
|
return JSON.stringify({ type: 'custom', name: raw.name });
|
|
26
26
|
})();
|
|
27
27
|
|
|
28
|
+
const videoCarouselOverlay = (() => {
|
|
29
|
+
const raw = config.videoCarouselOverlay ?? 'none';
|
|
30
|
+
if (typeof raw === 'string') return JSON.stringify(raw);
|
|
31
|
+
return JSON.stringify({ type: 'custom', name: raw.name });
|
|
32
|
+
})();
|
|
33
|
+
|
|
28
34
|
return JSON.stringify({
|
|
29
35
|
feedHeight: JSON.stringify(config.feedHeight ?? { type: 'fullscreen' }),
|
|
30
36
|
scrollAxis: config.scrollAxis ?? 'vertical',
|
|
31
37
|
overlay,
|
|
32
38
|
carouselOverlay,
|
|
39
|
+
videoCarouselOverlay,
|
|
33
40
|
surveyMode: JSON.stringify(config.surveyMode ?? 'none'),
|
|
34
41
|
muteOnStart: config.muteOnStart ?? true,
|
|
35
42
|
feedSource: config.feedSource ?? 'algorithmic',
|
|
@@ -156,6 +156,17 @@ type OverlayTimeUpdateEvent = Readonly<{
|
|
|
156
156
|
buffered: Double;
|
|
157
157
|
}>;
|
|
158
158
|
|
|
159
|
+
type CarouselActiveImageEvent = Readonly<{
|
|
160
|
+
surfaceId: string;
|
|
161
|
+
activeImageIndex: Int32;
|
|
162
|
+
}>;
|
|
163
|
+
|
|
164
|
+
type VideoCarouselActiveVideoEvent = Readonly<{
|
|
165
|
+
surfaceId: string;
|
|
166
|
+
activeVideo: string;
|
|
167
|
+
activeVideoIndex: Int32;
|
|
168
|
+
}>;
|
|
169
|
+
|
|
159
170
|
type OverlayFullStateEvent = Readonly<{
|
|
160
171
|
surfaceId: string;
|
|
161
172
|
isActive: boolean;
|
|
@@ -239,6 +250,8 @@ export interface Spec extends TurboModule {
|
|
|
239
250
|
readonly onOverlayFeedScrollPhaseChanged: EventEmitter<OverlayFeedScrollPhaseEvent>;
|
|
240
251
|
readonly onOverlayTimeUpdate: EventEmitter<OverlayTimeUpdateEvent>;
|
|
241
252
|
readonly onOverlayFullState: EventEmitter<OverlayFullStateEvent>;
|
|
253
|
+
readonly onCarouselActiveImageChanged: EventEmitter<CarouselActiveImageEvent>;
|
|
254
|
+
readonly onVideoCarouselActiveVideoChanged: EventEmitter<VideoCarouselActiveVideoEvent>;
|
|
242
255
|
}
|
|
243
256
|
|
|
244
257
|
export default TurboModuleRegistry.getEnforcing<Spec>('ShortKitModule');
|
package/src/types.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface FeedConfig {
|
|
|
20
20
|
scrollAxis?: ScrollAxis;
|
|
21
21
|
overlay?: OverlayConfig;
|
|
22
22
|
carouselOverlay?: CarouselOverlayConfig;
|
|
23
|
+
videoCarouselOverlay?: VideoCarouselOverlayConfig;
|
|
23
24
|
surveyMode?: SurveyMode;
|
|
24
25
|
muteOnStart?: boolean;
|
|
25
26
|
feedSource?: FeedSource;
|
|
@@ -41,6 +42,10 @@ export type CarouselOverlayConfig =
|
|
|
41
42
|
| 'none'
|
|
42
43
|
| { type: 'custom'; name: string; component: React.ComponentType<CarouselOverlayProps> };
|
|
43
44
|
|
|
45
|
+
export type VideoCarouselOverlayConfig =
|
|
46
|
+
| 'none'
|
|
47
|
+
| { type: 'custom'; name: string; component: React.ComponentType<VideoCarouselOverlayProps> };
|
|
48
|
+
|
|
44
49
|
export type SurveyMode =
|
|
45
50
|
| 'none'
|
|
46
51
|
| { type: 'template'; name: 'default' };
|
|
@@ -81,9 +86,20 @@ export interface ImageCarouselItem {
|
|
|
81
86
|
articleUrl?: string;
|
|
82
87
|
}
|
|
83
88
|
|
|
89
|
+
export interface VideoCarouselItem {
|
|
90
|
+
id: string;
|
|
91
|
+
videos: ContentItem[];
|
|
92
|
+
title?: string;
|
|
93
|
+
description?: string;
|
|
94
|
+
author?: string;
|
|
95
|
+
section?: string;
|
|
96
|
+
articleUrl?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
84
99
|
export type FeedInput =
|
|
85
100
|
| { type: 'video'; playbackId: string; fallbackUrl?: string }
|
|
86
|
-
| { type: 'imageCarousel'; item: ImageCarouselItem }
|
|
101
|
+
| { type: 'imageCarousel'; item: ImageCarouselItem }
|
|
102
|
+
| { type: 'videoCarousel'; item: VideoCarouselItem };
|
|
87
103
|
|
|
88
104
|
export type JSONValue =
|
|
89
105
|
| string
|
|
@@ -195,6 +211,28 @@ export interface OverlayProps {
|
|
|
195
211
|
export interface CarouselOverlayProps {
|
|
196
212
|
/** The carousel item for this cell. */
|
|
197
213
|
item: ImageCarouselItem;
|
|
214
|
+
/** Whether this carousel cell is the active (on-screen) cell. */
|
|
215
|
+
isActive: boolean;
|
|
216
|
+
/** Index of the currently visible image within the carousel. */
|
|
217
|
+
activeImageIndex: number;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Props passed to video carousel overlay components rendered inside feed cells. */
|
|
221
|
+
export interface VideoCarouselOverlayProps {
|
|
222
|
+
/** The video carousel item for this cell. */
|
|
223
|
+
carouselItem: VideoCarouselItem;
|
|
224
|
+
/** The currently active video within the carousel. */
|
|
225
|
+
activeVideo: ContentItem;
|
|
226
|
+
/** Index of the currently active video within the carousel. */
|
|
227
|
+
activeVideoIndex: number;
|
|
228
|
+
/** Whether this carousel cell is the active (on-screen) cell. */
|
|
229
|
+
isActive: boolean;
|
|
230
|
+
/** Current playback time for the active video. */
|
|
231
|
+
time: PlayerTime;
|
|
232
|
+
/** Current player state for the active video. */
|
|
233
|
+
playerState: PlayerState;
|
|
234
|
+
/** Whether audio is muted. */
|
|
235
|
+
isMuted: boolean;
|
|
198
236
|
}
|
|
199
237
|
|
|
200
238
|
// --- Provider Props ---
|