@shortkitsdk/react-native 0.2.6 → 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 +123 -741
- 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 +186 -63
- package/ios/ShortKitFeedView.swift +62 -229
- package/ios/ShortKitFeedViewManager.mm +3 -2
- package/ios/ShortKitModule.mm +66 -37
- 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 +1 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +2380 -522
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +39 -12
- 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 +39 -12
- 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 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +2380 -522
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +39 -12
- 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 +39 -12
- 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 +6 -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 +27 -286
- package/src/index.ts +5 -3
- package/src/serialization.ts +19 -39
- package/src/specs/NativeShortKitModule.ts +58 -46
- package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
- package/src/types.ts +78 -16
- package/src/useShortKit.ts +1 -3
- package/src/useShortKitPlayer.ts +7 -7
- 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 -219
- package/ios/ShortKitOverlayBridge.swift +0 -111
- package/src/CarouselOverlayManager.tsx +0 -70
- package/src/OverlayManager.tsx +0 -87
- package/src/useShortKitCarousel.ts +0 -29
package/src/ShortKitProvider.tsx
CHANGED
|
@@ -6,8 +6,8 @@ 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,
|
|
@@ -16,13 +16,13 @@ import type {
|
|
|
16
16
|
StoryboardData,
|
|
17
17
|
} from './types';
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
serializeFeedInputs,
|
|
19
|
+
serializeFeedConfig,
|
|
21
20
|
deserializePlayerState,
|
|
22
21
|
deserializeContentItem,
|
|
23
22
|
deserializePlayerTime,
|
|
24
23
|
} from './serialization';
|
|
25
24
|
import NativeShortKitModule from './specs/NativeShortKitModule';
|
|
25
|
+
import { registerLoadingComponent } from './ShortKitLoadingSurface';
|
|
26
26
|
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
28
28
|
// State & Reducer
|
|
@@ -31,7 +31,6 @@ import NativeShortKitModule from './specs/NativeShortKitModule';
|
|
|
31
31
|
interface State {
|
|
32
32
|
playerState: PlayerState;
|
|
33
33
|
currentItem: ContentItem | null;
|
|
34
|
-
nextItem: ContentItem | null;
|
|
35
34
|
time: PlayerTime;
|
|
36
35
|
isMuted: boolean;
|
|
37
36
|
playbackRate: number;
|
|
@@ -39,22 +38,13 @@ interface State {
|
|
|
39
38
|
activeCaptionTrack: CaptionTrack | null;
|
|
40
39
|
activeCue: { text: string; startTime: number; endTime: number } | null;
|
|
41
40
|
prefetchedAheadCount: number;
|
|
42
|
-
remainingContentCount: number;
|
|
43
41
|
isActive: boolean;
|
|
44
42
|
feedScrollPhase: FeedScrollPhase | null;
|
|
45
|
-
lastOverlayTap: number;
|
|
46
|
-
lastOverlayDoubleTap: { x: number; y: number; id: number } | null;
|
|
47
|
-
currentCarouselItem: ImageCarouselItem | null;
|
|
48
|
-
nextCarouselItem: ImageCarouselItem | null;
|
|
49
|
-
isCarouselActive: boolean;
|
|
50
|
-
currentCarouselPage: number;
|
|
51
|
-
activeCellType: 'video' | 'carousel' | null;
|
|
52
43
|
}
|
|
53
44
|
|
|
54
45
|
const initialState: State = {
|
|
55
46
|
playerState: 'idle',
|
|
56
47
|
currentItem: null,
|
|
57
|
-
nextItem: null,
|
|
58
48
|
time: { current: 0, duration: 0, buffered: 0 },
|
|
59
49
|
isMuted: true,
|
|
60
50
|
playbackRate: 1.0,
|
|
@@ -62,16 +52,8 @@ const initialState: State = {
|
|
|
62
52
|
activeCaptionTrack: null,
|
|
63
53
|
activeCue: null,
|
|
64
54
|
prefetchedAheadCount: 0,
|
|
65
|
-
remainingContentCount: 0,
|
|
66
55
|
isActive: false,
|
|
67
56
|
feedScrollPhase: null,
|
|
68
|
-
lastOverlayTap: 0,
|
|
69
|
-
lastOverlayDoubleTap: null,
|
|
70
|
-
currentCarouselItem: null,
|
|
71
|
-
nextCarouselItem: null,
|
|
72
|
-
isCarouselActive: false,
|
|
73
|
-
currentCarouselPage: 0,
|
|
74
|
-
activeCellType: null,
|
|
75
57
|
};
|
|
76
58
|
|
|
77
59
|
type Action =
|
|
@@ -84,38 +66,20 @@ type Action =
|
|
|
84
66
|
| { type: 'CAPTION_TRACK'; payload: CaptionTrack | null }
|
|
85
67
|
| { type: 'CUE'; payload: { text: string; startTime: number; endTime: number } | null }
|
|
86
68
|
| { type: 'PREFETCH_COUNT'; payload: number }
|
|
87
|
-
| { type: 'REMAINING_COUNT'; payload: number }
|
|
88
|
-
| { type: 'OVERLAY_CONFIGURE'; payload: ContentItem }
|
|
89
|
-
| { type: 'OVERLAY_ACTIVATE'; payload: ContentItem }
|
|
90
|
-
| { type: 'OVERLAY_RESET' }
|
|
91
|
-
| { type: 'OVERLAY_TAP' }
|
|
92
|
-
| { type: 'OVERLAY_DOUBLE_TAP'; payload: { x: number; y: number } }
|
|
93
|
-
| { type: 'ACTIVE_CELL_TYPE'; payload: 'video' | 'carousel' | null }
|
|
94
|
-
| { type: 'FEED_TRANSITION_ENDED'; payload: { isVideo: boolean } }
|
|
95
|
-
| { type: 'CAROUSEL_OVERLAY_CONFIGURE'; payload: ImageCarouselItem }
|
|
96
|
-
| { type: 'CAROUSEL_OVERLAY_ACTIVATE'; payload: ImageCarouselItem }
|
|
97
|
-
| { type: 'CAROUSEL_OVERLAY_RESET' }
|
|
98
|
-
| { type: 'CAROUSEL_PAGE_CHANGED'; payload: number }
|
|
99
69
|
| { type: 'FEED_SCROLL_PHASE'; payload: FeedScrollPhase };
|
|
100
70
|
|
|
101
71
|
function reducer(state: State, action: Action): State {
|
|
102
72
|
switch (action.type) {
|
|
103
73
|
case 'PLAYER_STATE': {
|
|
104
|
-
// Only allow isActive to transition TO true from player state.
|
|
105
|
-
// Once active, the overlay stays mounted — hiding during transient
|
|
106
|
-
// states like "loading" between videos would cause a visible flash.
|
|
107
74
|
const isPlaybackActive =
|
|
108
75
|
action.payload === 'playing' ||
|
|
109
76
|
action.payload === 'paused' ||
|
|
110
77
|
action.payload === 'buffering' ||
|
|
111
78
|
action.payload === 'seeking';
|
|
112
|
-
const becameActive = !state.isActive && isPlaybackActive;
|
|
113
79
|
return {
|
|
114
80
|
...state,
|
|
115
81
|
playerState: action.payload,
|
|
116
82
|
isActive: state.isActive || isPlaybackActive,
|
|
117
|
-
// First playback means a video cell is active (initial load)
|
|
118
|
-
activeCellType: becameActive ? 'video' : state.activeCellType,
|
|
119
83
|
};
|
|
120
84
|
}
|
|
121
85
|
case 'CURRENT_ITEM':
|
|
@@ -134,84 +98,6 @@ function reducer(state: State, action: Action): State {
|
|
|
134
98
|
return { ...state, activeCue: action.payload };
|
|
135
99
|
case 'PREFETCH_COUNT':
|
|
136
100
|
return { ...state, prefetchedAheadCount: action.payload };
|
|
137
|
-
case 'REMAINING_COUNT':
|
|
138
|
-
return { ...state, remainingContentCount: action.payload };
|
|
139
|
-
case 'OVERLAY_CONFIGURE':
|
|
140
|
-
return { ...state, nextItem: action.payload };
|
|
141
|
-
case 'OVERLAY_ACTIVATE':
|
|
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
|
-
return {
|
|
148
|
-
...state,
|
|
149
|
-
currentItem: action.payload,
|
|
150
|
-
isActive: true,
|
|
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,
|
|
157
|
-
};
|
|
158
|
-
case 'OVERLAY_RESET':
|
|
159
|
-
// Don't set isActive = false — the overlay stays mounted during
|
|
160
|
-
// transitions. In the native SDK each cell has its own overlay
|
|
161
|
-
// instance, so there's no gap. We replicate this by keeping the
|
|
162
|
-
// single JS overlay mounted and updating its content on activate.
|
|
163
|
-
console.log('[ShortKitProvider] OVERLAY_RESET (video)');
|
|
164
|
-
return { ...state, feedScrollPhase: null };
|
|
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
|
-
console.log('[ShortKitProvider] ACTIVE_CELL_TYPE:', action.payload);
|
|
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
|
-
}
|
|
204
|
-
case 'CAROUSEL_OVERLAY_CONFIGURE':
|
|
205
|
-
console.log('[ShortKitProvider] CAROUSEL_OVERLAY_CONFIGURE:', action.payload.id);
|
|
206
|
-
return { ...state, nextCarouselItem: action.payload };
|
|
207
|
-
case 'CAROUSEL_OVERLAY_ACTIVATE':
|
|
208
|
-
console.log('[ShortKitProvider] CAROUSEL_OVERLAY_ACTIVATE:', action.payload.id);
|
|
209
|
-
return { ...state, currentCarouselItem: action.payload, isCarouselActive: true, nextCarouselItem: null };
|
|
210
|
-
case 'CAROUSEL_OVERLAY_RESET':
|
|
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
101
|
case 'FEED_SCROLL_PHASE':
|
|
216
102
|
return { ...state, feedScrollPhase: action.payload };
|
|
217
103
|
default:
|
|
@@ -225,11 +111,11 @@ function reducer(state: State, action: Action): State {
|
|
|
225
111
|
|
|
226
112
|
export function ShortKitProvider({
|
|
227
113
|
apiKey,
|
|
228
|
-
config,
|
|
229
114
|
userId,
|
|
230
115
|
clientAppName,
|
|
231
116
|
clientAppVersion,
|
|
232
117
|
customDimensions,
|
|
118
|
+
loadingViewComponent,
|
|
233
119
|
children,
|
|
234
120
|
}: ShortKitProviderProps): React.JSX.Element {
|
|
235
121
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
@@ -241,14 +127,17 @@ export function ShortKitProvider({
|
|
|
241
127
|
useEffect(() => {
|
|
242
128
|
if (!NativeShortKitModule) return;
|
|
243
129
|
|
|
244
|
-
|
|
130
|
+
if (loadingViewComponent) {
|
|
131
|
+
registerLoadingComponent(loadingViewComponent);
|
|
132
|
+
}
|
|
133
|
+
|
|
245
134
|
const serializedDimensions = customDimensions
|
|
246
135
|
? JSON.stringify(customDimensions)
|
|
247
136
|
: undefined;
|
|
248
137
|
|
|
249
138
|
NativeShortKitModule.initialize(
|
|
250
139
|
apiKey,
|
|
251
|
-
|
|
140
|
+
!!loadingViewComponent,
|
|
252
141
|
clientAppName,
|
|
253
142
|
clientAppVersion,
|
|
254
143
|
serializedDimensions,
|
|
@@ -341,6 +230,7 @@ export function ShortKitProvider({
|
|
|
341
230
|
author: event.author,
|
|
342
231
|
articleUrl: event.articleUrl,
|
|
343
232
|
commentCount: event.commentCount,
|
|
233
|
+
fallbackUrl: event.fallbackUrl,
|
|
344
234
|
};
|
|
345
235
|
dispatch({ type: 'CURRENT_ITEM', payload: item });
|
|
346
236
|
}),
|
|
@@ -409,96 +299,11 @@ export function ShortKitProvider({
|
|
|
409
299
|
}),
|
|
410
300
|
);
|
|
411
301
|
|
|
412
|
-
//
|
|
413
|
-
subscriptions.push(
|
|
414
|
-
NativeShortKitModule.onRemainingContentCountChanged((event) => {
|
|
415
|
-
dispatch({ type: 'REMAINING_COUNT', payload: event.count });
|
|
416
|
-
}),
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
// Overlay lifecycle events
|
|
420
|
-
subscriptions.push(
|
|
421
|
-
NativeShortKitModule.onOverlayConfigure((event) => {
|
|
422
|
-
const item = deserializeContentItem(event.item);
|
|
423
|
-
if (item) {
|
|
424
|
-
dispatch({ type: 'OVERLAY_CONFIGURE', payload: item });
|
|
425
|
-
}
|
|
426
|
-
}),
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
subscriptions.push(
|
|
430
|
-
NativeShortKitModule.onOverlayActivate((event) => {
|
|
431
|
-
const item = deserializeContentItem(event.item);
|
|
432
|
-
if (item) {
|
|
433
|
-
dispatch({ type: 'OVERLAY_ACTIVATE', payload: item });
|
|
434
|
-
}
|
|
435
|
-
}),
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
subscriptions.push(
|
|
439
|
-
NativeShortKitModule.onOverlayReset((_event) => {
|
|
440
|
-
dispatch({ type: 'OVERLAY_RESET' });
|
|
441
|
-
}),
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
// Overlay tap events
|
|
445
|
-
subscriptions.push(
|
|
446
|
-
NativeShortKitModule.onOverlayTap((_event) => {
|
|
447
|
-
dispatch({ type: 'OVERLAY_TAP' });
|
|
448
|
-
}),
|
|
449
|
-
);
|
|
450
|
-
|
|
451
|
-
subscriptions.push(
|
|
452
|
-
NativeShortKitModule.onOverlayDoubleTap((event) => {
|
|
453
|
-
dispatch({
|
|
454
|
-
type: 'OVERLAY_DOUBLE_TAP',
|
|
455
|
-
payload: { x: event.x, y: event.y },
|
|
456
|
-
});
|
|
457
|
-
}),
|
|
458
|
-
);
|
|
459
|
-
|
|
460
|
-
// Feed transition — track active cell type
|
|
461
|
-
subscriptions.push(
|
|
462
|
-
NativeShortKitModule.onFeedTransition((event) => {
|
|
463
|
-
console.log('[ShortKitProvider] onFeedTransition:', event.phase, 'toItem:', event.toItem != null ? 'video' : 'non-video');
|
|
464
|
-
if (event.phase === 'ended') {
|
|
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.
|
|
467
|
-
const isVideo = event.toItem != null;
|
|
468
|
-
dispatch({
|
|
469
|
-
type: 'FEED_TRANSITION_ENDED',
|
|
470
|
-
payload: { isVideo },
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
}),
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
// Carousel overlay lifecycle events
|
|
477
|
-
subscriptions.push(
|
|
478
|
-
NativeShortKitModule.onCarouselOverlayConfigure((event) => {
|
|
479
|
-
try {
|
|
480
|
-
const item = JSON.parse(event.item) as ImageCarouselItem;
|
|
481
|
-
dispatch({ type: 'CAROUSEL_OVERLAY_CONFIGURE', payload: item });
|
|
482
|
-
} catch {
|
|
483
|
-
// Ignore malformed JSON
|
|
484
|
-
}
|
|
485
|
-
}),
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
subscriptions.push(
|
|
489
|
-
NativeShortKitModule.onCarouselOverlayActivate((event) => {
|
|
490
|
-
try {
|
|
491
|
-
const item = JSON.parse(event.item) as ImageCarouselItem;
|
|
492
|
-
dispatch({ type: 'CAROUSEL_OVERLAY_ACTIVATE', payload: item });
|
|
493
|
-
} catch {
|
|
494
|
-
// Ignore malformed JSON
|
|
495
|
-
}
|
|
496
|
-
}),
|
|
497
|
-
);
|
|
498
|
-
|
|
302
|
+
// Feed transition
|
|
499
303
|
subscriptions.push(
|
|
500
|
-
NativeShortKitModule.
|
|
501
|
-
|
|
304
|
+
NativeShortKitModule.onFeedTransition((_event) => {
|
|
305
|
+
// Feed transition events are consumed by ShortKitFeed callback props.
|
|
306
|
+
// No state to update here in the simplified provider.
|
|
502
307
|
}),
|
|
503
308
|
);
|
|
504
309
|
|
|
@@ -520,7 +325,7 @@ export function ShortKitProvider({
|
|
|
520
325
|
);
|
|
521
326
|
|
|
522
327
|
// Note: Feed-level callback events (onDidLoop, onFeedTransition,
|
|
523
|
-
//
|
|
328
|
+
// onDismiss, etc.) are subscribed by the ShortKitFeed component
|
|
524
329
|
// not here. The provider only manages state-driving events.
|
|
525
330
|
|
|
526
331
|
return () => {
|
|
@@ -530,40 +335,6 @@ export function ShortKitProvider({
|
|
|
530
335
|
};
|
|
531
336
|
}, []);
|
|
532
337
|
|
|
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
|
-
|
|
567
338
|
// -----------------------------------------------------------------------
|
|
568
339
|
// Commands (stable refs)
|
|
569
340
|
// -----------------------------------------------------------------------
|
|
@@ -623,17 +394,10 @@ export function ShortKitProvider({
|
|
|
623
394
|
NativeShortKitModule?.clearUserId();
|
|
624
395
|
}, []);
|
|
625
396
|
|
|
626
|
-
const
|
|
627
|
-
NativeShortKitModule?.setFeedItems(serializeFeedInputs(items));
|
|
628
|
-
}, []);
|
|
629
|
-
|
|
630
|
-
const appendFeedItemsCmd = useCallback((items: FeedInput[]) => {
|
|
631
|
-
NativeShortKitModule?.appendFeedItems(serializeFeedInputs(items));
|
|
632
|
-
}, []);
|
|
633
|
-
|
|
634
|
-
const fetchContentCmd = useCallback(async (limit: number = 10): Promise<ContentItem[]> => {
|
|
397
|
+
const fetchContentCmd = useCallback(async (limit: number = 10, filter?: FeedFilter): Promise<ContentItem[]> => {
|
|
635
398
|
if (!NativeShortKitModule) return [];
|
|
636
|
-
const
|
|
399
|
+
const filterJSON = filter ? JSON.stringify(filter) : null;
|
|
400
|
+
const json = await NativeShortKitModule.fetchContent(limit, filterJSON);
|
|
637
401
|
try {
|
|
638
402
|
return JSON.parse(json) as ContentItem[];
|
|
639
403
|
} catch {
|
|
@@ -656,6 +420,12 @@ export function ShortKitProvider({
|
|
|
656
420
|
}
|
|
657
421
|
}, []);
|
|
658
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
|
+
|
|
659
429
|
// -----------------------------------------------------------------------
|
|
660
430
|
// Context value (memoized to avoid unnecessary re-renders)
|
|
661
431
|
// -----------------------------------------------------------------------
|
|
@@ -664,7 +434,6 @@ export function ShortKitProvider({
|
|
|
664
434
|
// State
|
|
665
435
|
playerState: state.playerState,
|
|
666
436
|
currentItem: state.currentItem,
|
|
667
|
-
nextItem: state.nextItem,
|
|
668
437
|
time: state.time,
|
|
669
438
|
isMuted: state.isMuted,
|
|
670
439
|
playbackRate: state.playbackRate,
|
|
@@ -672,11 +441,8 @@ export function ShortKitProvider({
|
|
|
672
441
|
activeCaptionTrack: state.activeCaptionTrack,
|
|
673
442
|
activeCue: state.activeCue,
|
|
674
443
|
prefetchedAheadCount: state.prefetchedAheadCount,
|
|
675
|
-
remainingContentCount: state.remainingContentCount,
|
|
676
444
|
isActive: state.isActive,
|
|
677
445
|
feedScrollPhase: state.feedScrollPhase,
|
|
678
|
-
lastOverlayTap: state.lastOverlayTap,
|
|
679
|
-
lastOverlayDoubleTap: state.lastOverlayDoubleTap,
|
|
680
446
|
|
|
681
447
|
// Commands
|
|
682
448
|
play,
|
|
@@ -693,28 +459,14 @@ export function ShortKitProvider({
|
|
|
693
459
|
setMaxBitrate,
|
|
694
460
|
setUserId: setUserIdCmd,
|
|
695
461
|
clearUserId: clearUserIdCmd,
|
|
696
|
-
setFeedItems: setFeedItemsCmd,
|
|
697
|
-
appendFeedItems: appendFeedItemsCmd,
|
|
698
462
|
fetchContent: fetchContentCmd,
|
|
463
|
+
preloadFeed: preloadFeedCmd,
|
|
699
464
|
prefetchStoryboard: prefetchStoryboardCmd,
|
|
700
465
|
getStoryboardData: getStoryboardDataCmd,
|
|
701
|
-
|
|
702
|
-
// Active cell type
|
|
703
|
-
activeCellType: state.activeCellType,
|
|
704
|
-
|
|
705
|
-
// Carousel overlay state
|
|
706
|
-
currentCarouselItem: state.currentCarouselItem,
|
|
707
|
-
nextCarouselItem: state.nextCarouselItem,
|
|
708
|
-
isCarouselActive: state.isCarouselActive,
|
|
709
|
-
currentCarouselPage: state.currentCarouselPage,
|
|
710
|
-
// Internal — consumed by ShortKitFeed to pass to OverlayManager
|
|
711
|
-
_overlayConfig: config.overlay ?? 'none',
|
|
712
|
-
_carouselOverlayConfig: config.carouselOverlay ?? 'none',
|
|
713
466
|
}),
|
|
714
467
|
[
|
|
715
468
|
state.playerState,
|
|
716
469
|
state.currentItem,
|
|
717
|
-
state.nextItem,
|
|
718
470
|
state.time,
|
|
719
471
|
state.isMuted,
|
|
720
472
|
state.playbackRate,
|
|
@@ -722,16 +474,8 @@ export function ShortKitProvider({
|
|
|
722
474
|
state.activeCaptionTrack,
|
|
723
475
|
state.activeCue,
|
|
724
476
|
state.prefetchedAheadCount,
|
|
725
|
-
state.remainingContentCount,
|
|
726
477
|
state.isActive,
|
|
727
478
|
state.feedScrollPhase,
|
|
728
|
-
state.lastOverlayTap,
|
|
729
|
-
state.lastOverlayDoubleTap,
|
|
730
|
-
state.activeCellType,
|
|
731
|
-
state.currentCarouselItem,
|
|
732
|
-
state.nextCarouselItem,
|
|
733
|
-
state.isCarouselActive,
|
|
734
|
-
state.currentCarouselPage,
|
|
735
479
|
play,
|
|
736
480
|
pause,
|
|
737
481
|
seek,
|
|
@@ -746,13 +490,10 @@ export function ShortKitProvider({
|
|
|
746
490
|
setMaxBitrate,
|
|
747
491
|
setUserIdCmd,
|
|
748
492
|
clearUserIdCmd,
|
|
749
|
-
setFeedItemsCmd,
|
|
750
|
-
appendFeedItemsCmd,
|
|
751
493
|
fetchContentCmd,
|
|
494
|
+
preloadFeedCmd,
|
|
752
495
|
prefetchStoryboardCmd,
|
|
753
496
|
getStoryboardDataCmd,
|
|
754
|
-
config.overlay,
|
|
755
|
-
config.carouselOverlay,
|
|
756
497
|
],
|
|
757
498
|
);
|
|
758
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,
|
|
@@ -31,13 +30,16 @@ export type {
|
|
|
31
30
|
FormatChangeEvent,
|
|
32
31
|
ContentSignal,
|
|
33
32
|
SurveyOption,
|
|
34
|
-
|
|
33
|
+
FeedFilter,
|
|
34
|
+
CaptionSource,
|
|
35
35
|
ShortKitProviderProps,
|
|
36
36
|
ShortKitFeedProps,
|
|
37
|
+
ShortKitFeedHandle,
|
|
37
38
|
ShortKitPlayerProps,
|
|
38
39
|
ShortKitWidgetProps,
|
|
39
40
|
ShortKitPlayerState,
|
|
40
41
|
StoryboardData,
|
|
41
42
|
StoryboardTile,
|
|
42
43
|
} from './types';
|
|
43
|
-
export
|
|
44
|
+
export { ShortKitCommands } from './ShortKitCommands';
|
|
45
|
+
export type { OverlayProps, CarouselOverlayProps } from './types';
|
package/src/serialization.ts
CHANGED
|
@@ -7,54 +7,34 @@ import type {
|
|
|
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
|
/**
|