@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.
- package/ShortKitReactNative.podspec +1 -0
- package/android/build.gradle.kts +5 -1
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +319 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactLoadingHost.kt +40 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +559 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +984 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +88 -220
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +12 -3
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +126 -706
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +2 -2
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +2 -2
- package/ios/ReactCarouselOverlayHost.swift +177 -0
- package/ios/ReactLoadingHost.swift +38 -0
- package/ios/ReactOverlayHost.swift +458 -0
- package/ios/SKFabricSurfaceWrapper.h +18 -0
- package/ios/SKFabricSurfaceWrapper.mm +57 -0
- package/ios/ShortKitBridge.swift +266 -65
- package/ios/ShortKitFeedView.swift +63 -207
- package/ios/ShortKitFeedViewManager.mm +3 -2
- package/ios/ShortKitModule.mm +86 -32
- package/ios/ShortKitPlayerNativeView.swift +39 -8
- package/ios/ShortKitReactNative-Bridging-Header.h +2 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +2 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +3998 -962
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +85 -24
- 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 +85 -24
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +2 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +3998 -962
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +85 -24
- 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 +85 -24
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.bak/Info.plist +43 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +28917 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +28917 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +824 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitWidgetNativeView.swift +3 -3
- package/package.json +1 -1
- package/src/ShortKitCarouselOverlaySurface.tsx +55 -0
- package/src/ShortKitCommands.ts +31 -0
- package/src/ShortKitContext.ts +11 -25
- package/src/ShortKitFeed.tsx +110 -41
- package/src/ShortKitLoadingSurface.tsx +24 -0
- package/src/ShortKitOverlaySurface.tsx +205 -0
- package/src/ShortKitPlayer.tsx +6 -7
- package/src/ShortKitProvider.tsx +65 -250
- package/src/index.ts +9 -4
- package/src/serialization.ts +22 -42
- package/src/specs/NativeShortKitModule.ts +67 -53
- package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
- package/src/types.ts +104 -19
- package/src/useShortKit.ts +1 -3
- package/src/useShortKitPlayer.ts +7 -8
- package/android/src/main/java/com/shortkit/reactnative/ShortKitCarouselOverlayBridge.kt +0 -48
- package/android/src/main/java/com/shortkit/reactnative/ShortKitOverlayBridge.kt +0 -128
- package/ios/ShortKitCarouselOverlayBridge.swift +0 -54
- package/ios/ShortKitOverlayBridge.swift +0 -113
- package/src/CarouselOverlayManager.tsx +0 -71
- package/src/OverlayManager.tsx +0 -87
- package/src/useShortKitCarousel.ts +0 -29
package/src/ShortKitProvider.tsx
CHANGED
|
@@ -6,21 +6,23 @@ import type { ShortKitContextValue } from './ShortKitContext';
|
|
|
6
6
|
import type {
|
|
7
7
|
ShortKitProviderProps,
|
|
8
8
|
ContentItem,
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
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 '
|
|
137
|
-
return { ...state,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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.
|
|
404
|
-
|
|
405
|
-
|
|
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
|
|
310
|
+
// Feed scroll phase
|
|
446
311
|
subscriptions.push(
|
|
447
|
-
NativeShortKitModule.
|
|
448
|
-
if (event.phase === '
|
|
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: '
|
|
453
|
-
payload:
|
|
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
|
-
//
|
|
502
|
-
//
|
|
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
|
|
571
|
-
NativeShortKitModule
|
|
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
|
|
575
|
-
NativeShortKitModule?.
|
|
408
|
+
const prefetchStoryboardCmd = useCallback((playbackId: string) => {
|
|
409
|
+
NativeShortKitModule?.prefetchStoryboard(playbackId);
|
|
576
410
|
}, []);
|
|
577
411
|
|
|
578
|
-
const
|
|
579
|
-
if (!NativeShortKitModule) return
|
|
580
|
-
const json = await NativeShortKitModule.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
630
|
-
|
|
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.
|
|
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
|
-
|
|
681
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
44
|
+
export { ShortKitCommands } from './ShortKitCommands';
|
|
45
|
+
export type { OverlayProps, CarouselOverlayProps } from './types';
|
package/src/serialization.ts
CHANGED
|
@@ -1,60 +1,40 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
FeedConfig,
|
|
3
3
|
ContentItem,
|
|
4
|
-
|
|
4
|
+
FeedInput,
|
|
5
5
|
PlayerState,
|
|
6
6
|
PlayerTime,
|
|
7
7
|
} from './types';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
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):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
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
|
-
|
|
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
|
|
85
|
+
* Serialize FeedInput[] to a JSON string for the bridge.
|
|
106
86
|
*/
|
|
107
|
-
export function
|
|
87
|
+
export function serializeFeedInputs(items: FeedInput[]): string {
|
|
108
88
|
return JSON.stringify(items);
|
|
109
89
|
}
|