@shortkitsdk/react-native 0.1.0 → 0.2.1
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/ShortKitFeedView.kt +74 -19
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +107 -95
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPackage.kt +5 -1
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +136 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerViewManager.kt +35 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +133 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetViewManager.kt +30 -0
- package/ios/ShortKitBridge.swift +81 -47
- package/ios/ShortKitFeedView.swift +67 -20
- package/ios/ShortKitModule.mm +22 -5
- package/ios/ShortKitPlayerNativeView.swift +186 -0
- package/ios/ShortKitPlayerNativeViewManager.mm +28 -0
- package/ios/ShortKitWidgetNativeView.swift +168 -0
- package/ios/ShortKitWidgetNativeViewManager.mm +27 -0
- package/package.json +1 -1
- package/src/ShortKitContext.ts +5 -0
- package/src/ShortKitFeed.tsx +5 -50
- package/src/ShortKitPlayer.tsx +61 -0
- package/src/ShortKitProvider.tsx +50 -4
- package/src/ShortKitWidget.tsx +63 -0
- package/src/index.ts +12 -0
- package/src/serialization.ts +10 -0
- package/src/specs/NativeShortKitModule.ts +18 -25
- package/src/specs/ShortKitPlayerViewNativeComponent.ts +13 -0
- package/src/specs/ShortKitWidgetViewNativeComponent.ts +12 -0
- package/src/types.ts +78 -5
- package/src/useShortKit.ts +5 -1
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
|
+
CustomFeedItem,
|
|
9
10
|
PlayerTime,
|
|
10
11
|
PlayerState,
|
|
11
12
|
CaptionTrack,
|
|
@@ -13,6 +14,7 @@ import type {
|
|
|
13
14
|
} from './types';
|
|
14
15
|
import {
|
|
15
16
|
serializeFeedConfigForModule,
|
|
17
|
+
serializeCustomFeedItems,
|
|
16
18
|
deserializePlayerState,
|
|
17
19
|
deserializeContentItem,
|
|
18
20
|
deserializePlayerTime,
|
|
@@ -34,6 +36,7 @@ interface State {
|
|
|
34
36
|
activeCaptionTrack: CaptionTrack | null;
|
|
35
37
|
activeCue: { text: string; startTime: number; endTime: number } | null;
|
|
36
38
|
prefetchedAheadCount: number;
|
|
39
|
+
remainingContentCount: number;
|
|
37
40
|
isActive: boolean;
|
|
38
41
|
isTransitioning: boolean;
|
|
39
42
|
lastOverlayTap: number;
|
|
@@ -51,6 +54,7 @@ const initialState: State = {
|
|
|
51
54
|
activeCaptionTrack: null,
|
|
52
55
|
activeCue: null,
|
|
53
56
|
prefetchedAheadCount: 0,
|
|
57
|
+
remainingContentCount: 0,
|
|
54
58
|
isActive: false,
|
|
55
59
|
isTransitioning: false,
|
|
56
60
|
lastOverlayTap: 0,
|
|
@@ -67,8 +71,9 @@ type Action =
|
|
|
67
71
|
| { type: 'CAPTION_TRACK'; payload: CaptionTrack | null }
|
|
68
72
|
| { type: 'CUE'; payload: { text: string; startTime: number; endTime: number } | null }
|
|
69
73
|
| { type: 'PREFETCH_COUNT'; payload: number }
|
|
74
|
+
| { type: 'REMAINING_COUNT'; payload: number }
|
|
70
75
|
| { type: 'OVERLAY_CONFIGURE'; payload: ContentItem }
|
|
71
|
-
| { type: 'OVERLAY_ACTIVATE' }
|
|
76
|
+
| { type: 'OVERLAY_ACTIVATE'; payload: ContentItem }
|
|
72
77
|
| { type: 'OVERLAY_RESET' }
|
|
73
78
|
| { type: 'OVERLAY_FADE_OUT' }
|
|
74
79
|
| { type: 'OVERLAY_RESTORE' }
|
|
@@ -108,10 +113,12 @@ function reducer(state: State, action: Action): State {
|
|
|
108
113
|
return { ...state, activeCue: action.payload };
|
|
109
114
|
case 'PREFETCH_COUNT':
|
|
110
115
|
return { ...state, prefetchedAheadCount: action.payload };
|
|
116
|
+
case 'REMAINING_COUNT':
|
|
117
|
+
return { ...state, remainingContentCount: action.payload };
|
|
111
118
|
case 'OVERLAY_CONFIGURE':
|
|
112
119
|
return { ...state, nextItem: action.payload };
|
|
113
120
|
case 'OVERLAY_ACTIVATE':
|
|
114
|
-
return { ...state, currentItem:
|
|
121
|
+
return { ...state, currentItem: action.payload, isActive: true };
|
|
115
122
|
case 'OVERLAY_RESET':
|
|
116
123
|
// Don't set isActive = false — the overlay stays mounted during
|
|
117
124
|
// transitions. In the native SDK each cell has its own overlay
|
|
@@ -145,6 +152,7 @@ function reducer(state: State, action: Action): State {
|
|
|
145
152
|
export function ShortKitProvider({
|
|
146
153
|
apiKey,
|
|
147
154
|
config,
|
|
155
|
+
embedId,
|
|
148
156
|
userId,
|
|
149
157
|
clientAppName,
|
|
150
158
|
clientAppVersion,
|
|
@@ -168,6 +176,7 @@ export function ShortKitProvider({
|
|
|
168
176
|
NativeShortKitModule.initialize(
|
|
169
177
|
apiKey,
|
|
170
178
|
serializedConfig,
|
|
179
|
+
embedId,
|
|
171
180
|
clientAppName,
|
|
172
181
|
clientAppVersion,
|
|
173
182
|
serializedDimensions,
|
|
@@ -245,6 +254,7 @@ export function ShortKitProvider({
|
|
|
245
254
|
NativeShortKitModule.onCurrentItemChanged((event) => {
|
|
246
255
|
const item: ContentItem = {
|
|
247
256
|
id: event.id,
|
|
257
|
+
playbackId: event.playbackId,
|
|
248
258
|
title: event.title,
|
|
249
259
|
description: event.description,
|
|
250
260
|
duration: event.duration,
|
|
@@ -327,6 +337,13 @@ export function ShortKitProvider({
|
|
|
327
337
|
}),
|
|
328
338
|
);
|
|
329
339
|
|
|
340
|
+
// Remaining content count
|
|
341
|
+
subscriptions.push(
|
|
342
|
+
NativeShortKitModule.onRemainingContentCountChanged((event) => {
|
|
343
|
+
dispatch({ type: 'REMAINING_COUNT', payload: event.count });
|
|
344
|
+
}),
|
|
345
|
+
);
|
|
346
|
+
|
|
330
347
|
// Overlay lifecycle events
|
|
331
348
|
subscriptions.push(
|
|
332
349
|
NativeShortKitModule.onOverlayConfigure((event) => {
|
|
@@ -338,8 +355,11 @@ export function ShortKitProvider({
|
|
|
338
355
|
);
|
|
339
356
|
|
|
340
357
|
subscriptions.push(
|
|
341
|
-
NativeShortKitModule.onOverlayActivate((
|
|
342
|
-
|
|
358
|
+
NativeShortKitModule.onOverlayActivate((event) => {
|
|
359
|
+
const item = deserializeContentItem(event.item);
|
|
360
|
+
if (item) {
|
|
361
|
+
dispatch({ type: 'OVERLAY_ACTIVATE', payload: item });
|
|
362
|
+
}
|
|
343
363
|
}),
|
|
344
364
|
);
|
|
345
365
|
|
|
@@ -447,6 +467,24 @@ export function ShortKitProvider({
|
|
|
447
467
|
NativeShortKitModule?.clearUserId();
|
|
448
468
|
}, []);
|
|
449
469
|
|
|
470
|
+
const setFeedItemsCmd = useCallback((items: CustomFeedItem[]) => {
|
|
471
|
+
NativeShortKitModule?.setFeedItems(serializeCustomFeedItems(items));
|
|
472
|
+
}, []);
|
|
473
|
+
|
|
474
|
+
const appendFeedItemsCmd = useCallback((items: CustomFeedItem[]) => {
|
|
475
|
+
NativeShortKitModule?.appendFeedItems(serializeCustomFeedItems(items));
|
|
476
|
+
}, []);
|
|
477
|
+
|
|
478
|
+
const fetchContentCmd = useCallback(async (limit: number = 10): Promise<ContentItem[]> => {
|
|
479
|
+
if (!NativeShortKitModule) return [];
|
|
480
|
+
const json = await NativeShortKitModule.fetchContent(limit);
|
|
481
|
+
try {
|
|
482
|
+
return JSON.parse(json) as ContentItem[];
|
|
483
|
+
} catch {
|
|
484
|
+
return [];
|
|
485
|
+
}
|
|
486
|
+
}, []);
|
|
487
|
+
|
|
450
488
|
// -----------------------------------------------------------------------
|
|
451
489
|
// Context value (memoized to avoid unnecessary re-renders)
|
|
452
490
|
// -----------------------------------------------------------------------
|
|
@@ -463,6 +501,7 @@ export function ShortKitProvider({
|
|
|
463
501
|
activeCaptionTrack: state.activeCaptionTrack,
|
|
464
502
|
activeCue: state.activeCue,
|
|
465
503
|
prefetchedAheadCount: state.prefetchedAheadCount,
|
|
504
|
+
remainingContentCount: state.remainingContentCount,
|
|
466
505
|
isActive: state.isActive,
|
|
467
506
|
isTransitioning: state.isTransitioning,
|
|
468
507
|
lastOverlayTap: state.lastOverlayTap,
|
|
@@ -483,6 +522,9 @@ export function ShortKitProvider({
|
|
|
483
522
|
setMaxBitrate,
|
|
484
523
|
setUserId: setUserIdCmd,
|
|
485
524
|
clearUserId: clearUserIdCmd,
|
|
525
|
+
setFeedItems: setFeedItemsCmd,
|
|
526
|
+
appendFeedItems: appendFeedItemsCmd,
|
|
527
|
+
fetchContent: fetchContentCmd,
|
|
486
528
|
|
|
487
529
|
// Internal — consumed by ShortKitFeed to pass to OverlayManager
|
|
488
530
|
_overlayConfig: config.overlay ?? 'none',
|
|
@@ -498,6 +540,7 @@ export function ShortKitProvider({
|
|
|
498
540
|
state.activeCaptionTrack,
|
|
499
541
|
state.activeCue,
|
|
500
542
|
state.prefetchedAheadCount,
|
|
543
|
+
state.remainingContentCount,
|
|
501
544
|
state.isActive,
|
|
502
545
|
state.isTransitioning,
|
|
503
546
|
state.lastOverlayTap,
|
|
@@ -516,6 +559,9 @@ export function ShortKitProvider({
|
|
|
516
559
|
setMaxBitrate,
|
|
517
560
|
setUserIdCmd,
|
|
518
561
|
clearUserIdCmd,
|
|
562
|
+
setFeedItemsCmd,
|
|
563
|
+
appendFeedItemsCmd,
|
|
564
|
+
fetchContentCmd,
|
|
519
565
|
config.overlay,
|
|
520
566
|
],
|
|
521
567
|
);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import type { ShortKitWidgetProps } from './types';
|
|
4
|
+
import ShortKitWidgetView from './specs/ShortKitWidgetViewNativeComponent';
|
|
5
|
+
import { ShortKitContext } from './ShortKitContext';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Horizontal carousel widget component. Displays a row of video cards
|
|
9
|
+
* with automatic rotation. Wraps a native Fabric view.
|
|
10
|
+
*
|
|
11
|
+
* Must be rendered inside a `<ShortKitProvider>`.
|
|
12
|
+
*/
|
|
13
|
+
export function ShortKitWidget(props: ShortKitWidgetProps) {
|
|
14
|
+
const { config, items, style } = props;
|
|
15
|
+
|
|
16
|
+
const context = useContext(ShortKitContext);
|
|
17
|
+
if (!context) {
|
|
18
|
+
throw new Error('ShortKitWidget must be used within a ShortKitProvider');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const serializedConfig = useMemo(() => {
|
|
22
|
+
const cfg = config ?? {};
|
|
23
|
+
return JSON.stringify({
|
|
24
|
+
cardCount: cfg.cardCount ?? 3,
|
|
25
|
+
cardSpacing: cfg.cardSpacing ?? 8,
|
|
26
|
+
cornerRadius: cfg.cornerRadius ?? 12,
|
|
27
|
+
autoplay: cfg.autoplay ?? true,
|
|
28
|
+
muteOnStart: cfg.muteOnStart ?? true,
|
|
29
|
+
loop: cfg.loop ?? true,
|
|
30
|
+
rotationInterval: cfg.rotationInterval ?? 10000,
|
|
31
|
+
clickAction: cfg.clickAction ?? 'feed',
|
|
32
|
+
overlay: cfg.overlay
|
|
33
|
+
? typeof cfg.overlay === 'string'
|
|
34
|
+
? cfg.overlay
|
|
35
|
+
: { type: 'custom' }
|
|
36
|
+
: 'none',
|
|
37
|
+
});
|
|
38
|
+
}, [config]);
|
|
39
|
+
|
|
40
|
+
const serializedItems = useMemo(() => {
|
|
41
|
+
if (!items || items.length === 0) return undefined;
|
|
42
|
+
return JSON.stringify(items);
|
|
43
|
+
}, [items]);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<View style={[styles.container, style]}>
|
|
47
|
+
<ShortKitWidgetView
|
|
48
|
+
style={styles.widget}
|
|
49
|
+
config={serializedConfig}
|
|
50
|
+
items={serializedItems}
|
|
51
|
+
/>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const styles = StyleSheet.create({
|
|
57
|
+
container: {
|
|
58
|
+
overflow: 'hidden',
|
|
59
|
+
},
|
|
60
|
+
widget: {
|
|
61
|
+
flex: 1,
|
|
62
|
+
},
|
|
63
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
export { ShortKitProvider } from './ShortKitProvider';
|
|
2
2
|
export { ShortKitFeed } from './ShortKitFeed';
|
|
3
|
+
export { ShortKitPlayer } from './ShortKitPlayer';
|
|
4
|
+
export { ShortKitWidget } from './ShortKitWidget';
|
|
3
5
|
export { useShortKitPlayer } from './useShortKitPlayer';
|
|
4
6
|
export { useShortKit } from './useShortKit';
|
|
5
7
|
export type {
|
|
6
8
|
FeedConfig,
|
|
7
9
|
FeedHeight,
|
|
10
|
+
FeedSource,
|
|
8
11
|
OverlayConfig,
|
|
9
12
|
CarouselMode,
|
|
10
13
|
SurveyMode,
|
|
11
14
|
|
|
15
|
+
PlayerConfig,
|
|
16
|
+
PlayerClickAction,
|
|
17
|
+
WidgetConfig,
|
|
18
|
+
|
|
12
19
|
ContentItem,
|
|
20
|
+
CarouselImage,
|
|
21
|
+
ImageCarouselItem,
|
|
22
|
+
CustomFeedItem,
|
|
13
23
|
JSONValue,
|
|
14
24
|
CaptionTrack,
|
|
15
25
|
PlayerTime,
|
|
@@ -22,5 +32,7 @@ export type {
|
|
|
22
32
|
ShortKitError,
|
|
23
33
|
ShortKitProviderProps,
|
|
24
34
|
ShortKitFeedProps,
|
|
35
|
+
ShortKitPlayerProps,
|
|
36
|
+
ShortKitWidgetProps,
|
|
25
37
|
ShortKitPlayerState,
|
|
26
38
|
} from './types';
|
package/src/serialization.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
FeedConfig,
|
|
3
3
|
ContentItem,
|
|
4
|
+
CustomFeedItem,
|
|
4
5
|
PlayerState,
|
|
5
6
|
PlayerTime,
|
|
6
7
|
} from './types';
|
|
@@ -15,6 +16,7 @@ export interface SerializedFeedConfig {
|
|
|
15
16
|
carouselMode: string;
|
|
16
17
|
surveyMode: string;
|
|
17
18
|
muteOnStart: boolean;
|
|
19
|
+
feedSource: string;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -38,6 +40,7 @@ export function serializeFeedConfig(config: FeedConfig): SerializedFeedConfig {
|
|
|
38
40
|
carouselMode: JSON.stringify(config.carouselMode ?? 'none'),
|
|
39
41
|
surveyMode: JSON.stringify(config.surveyMode ?? 'none'),
|
|
40
42
|
muteOnStart: config.muteOnStart ?? true,
|
|
43
|
+
feedSource: config.feedSource ?? 'algorithmic',
|
|
41
44
|
};
|
|
42
45
|
}
|
|
43
46
|
|
|
@@ -93,3 +96,10 @@ export function deserializePlayerTime(event: {
|
|
|
93
96
|
buffered: event.buffered,
|
|
94
97
|
};
|
|
95
98
|
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Serialize CustomFeedItem[] to a JSON string for the bridge.
|
|
102
|
+
*/
|
|
103
|
+
export function serializeCustomFeedItems(items: CustomFeedItem[]): string {
|
|
104
|
+
return JSON.stringify(items);
|
|
105
|
+
}
|
|
@@ -11,6 +11,7 @@ type PlayerStateEvent = Readonly<{
|
|
|
11
11
|
|
|
12
12
|
type CurrentItemEvent = Readonly<{
|
|
13
13
|
id: string;
|
|
14
|
+
playbackId?: string;
|
|
14
15
|
title: string;
|
|
15
16
|
description?: string;
|
|
16
17
|
duration: Double;
|
|
@@ -77,6 +78,10 @@ type PrefetchedAheadCountEvent = Readonly<{
|
|
|
77
78
|
count: Int32;
|
|
78
79
|
}>;
|
|
79
80
|
|
|
81
|
+
type RemainingContentCountEvent = Readonly<{
|
|
82
|
+
count: Int32;
|
|
83
|
+
}>;
|
|
84
|
+
|
|
80
85
|
type ErrorEvent = Readonly<{
|
|
81
86
|
code: string;
|
|
82
87
|
message: string;
|
|
@@ -92,26 +97,6 @@ type SurveyResponseEvent = Readonly<{
|
|
|
92
97
|
optionText: string;
|
|
93
98
|
}>;
|
|
94
99
|
|
|
95
|
-
type ArticleTappedEvent = Readonly<{
|
|
96
|
-
item: string; // JSON-serialized ContentItem
|
|
97
|
-
}>;
|
|
98
|
-
|
|
99
|
-
type CommentTappedEvent = Readonly<{
|
|
100
|
-
item: string; // JSON-serialized ContentItem
|
|
101
|
-
}>;
|
|
102
|
-
|
|
103
|
-
type OverlayShareTappedEvent = Readonly<{
|
|
104
|
-
item: string; // JSON-serialized ContentItem
|
|
105
|
-
}>;
|
|
106
|
-
|
|
107
|
-
type SaveTappedEvent = Readonly<{
|
|
108
|
-
item: string; // JSON-serialized ContentItem
|
|
109
|
-
}>;
|
|
110
|
-
|
|
111
|
-
type LikeTappedEvent = Readonly<{
|
|
112
|
-
item: string; // JSON-serialized ContentItem
|
|
113
|
-
}>;
|
|
114
|
-
|
|
115
100
|
type OverlayConfigureEvent = Readonly<{
|
|
116
101
|
item: string; // JSON-serialized ContentItem
|
|
117
102
|
}>;
|
|
@@ -132,6 +117,11 @@ type OverlayRestoreEvent = Readonly<{
|
|
|
132
117
|
item: string; // JSON-serialized ContentItem
|
|
133
118
|
}>;
|
|
134
119
|
|
|
120
|
+
type ContentTappedEvent = Readonly<{
|
|
121
|
+
contentId: string;
|
|
122
|
+
index: Int32;
|
|
123
|
+
}>;
|
|
124
|
+
|
|
135
125
|
type OverlayTapEvent = Readonly<{}>;
|
|
136
126
|
|
|
137
127
|
type OverlayDoubleTapEvent = Readonly<{
|
|
@@ -144,6 +134,7 @@ export interface Spec extends TurboModule {
|
|
|
144
134
|
initialize(
|
|
145
135
|
apiKey: string,
|
|
146
136
|
config: string, // JSON-serialized FeedConfig
|
|
137
|
+
embedId?: string,
|
|
147
138
|
clientAppName?: string,
|
|
148
139
|
clientAppVersion?: string,
|
|
149
140
|
customDimensions?: string, // JSON-serialized Record<string, string>
|
|
@@ -168,6 +159,11 @@ export interface Spec extends TurboModule {
|
|
|
168
159
|
sendContentSignal(signal: string): void;
|
|
169
160
|
setMaxBitrate(bitrate: Double): void;
|
|
170
161
|
|
|
162
|
+
// --- Custom feed ---
|
|
163
|
+
setFeedItems(items: string): void;
|
|
164
|
+
appendFeedItems(items: string): void;
|
|
165
|
+
fetchContent(limit: Int32): Promise<string>;
|
|
166
|
+
|
|
171
167
|
// --- Event emitters ---
|
|
172
168
|
readonly onPlayerStateChanged: EventEmitter<PlayerStateEvent>;
|
|
173
169
|
readonly onCurrentItemChanged: EventEmitter<CurrentItemEvent>;
|
|
@@ -181,14 +177,10 @@ export interface Spec extends TurboModule {
|
|
|
181
177
|
readonly onFeedTransition: EventEmitter<FeedTransitionEvent>;
|
|
182
178
|
readonly onFormatChange: EventEmitter<FormatChangeEvent>;
|
|
183
179
|
readonly onPrefetchedAheadCountChanged: EventEmitter<PrefetchedAheadCountEvent>;
|
|
180
|
+
readonly onRemainingContentCountChanged: EventEmitter<RemainingContentCountEvent>;
|
|
184
181
|
readonly onError: EventEmitter<ErrorEvent>;
|
|
185
182
|
readonly onShareTapped: EventEmitter<ShareTappedEvent>;
|
|
186
183
|
readonly onSurveyResponse: EventEmitter<SurveyResponseEvent>;
|
|
187
|
-
readonly onArticleTapped: EventEmitter<ArticleTappedEvent>;
|
|
188
|
-
readonly onCommentTapped: EventEmitter<CommentTappedEvent>;
|
|
189
|
-
readonly onOverlayShareTapped: EventEmitter<OverlayShareTappedEvent>;
|
|
190
|
-
readonly onSaveTapped: EventEmitter<SaveTappedEvent>;
|
|
191
|
-
readonly onLikeTapped: EventEmitter<LikeTappedEvent>;
|
|
192
184
|
readonly onOverlayConfigure: EventEmitter<OverlayConfigureEvent>;
|
|
193
185
|
readonly onOverlayActivate: EventEmitter<OverlayActivateEvent>;
|
|
194
186
|
readonly onOverlayReset: EventEmitter<OverlayResetEvent>;
|
|
@@ -196,6 +188,7 @@ export interface Spec extends TurboModule {
|
|
|
196
188
|
readonly onOverlayRestore: EventEmitter<OverlayRestoreEvent>;
|
|
197
189
|
readonly onOverlayTap: EventEmitter<OverlayTapEvent>;
|
|
198
190
|
readonly onOverlayDoubleTap: EventEmitter<OverlayDoubleTapEvent>;
|
|
191
|
+
readonly onContentTapped: EventEmitter<ContentTappedEvent>;
|
|
199
192
|
}
|
|
200
193
|
|
|
201
194
|
export default TurboModuleRegistry.getEnforcing<Spec>('ShortKitModule');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HostComponent, ViewProps } from 'react-native';
|
|
2
|
+
import { codegenNativeComponent } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface NativeProps extends ViewProps {
|
|
5
|
+
config: string;
|
|
6
|
+
contentItem?: string;
|
|
7
|
+
active?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default codegenNativeComponent<NativeProps>(
|
|
11
|
+
'ShortKitPlayerView',
|
|
12
|
+
{},
|
|
13
|
+
) as HostComponent<NativeProps>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HostComponent, ViewProps } from 'react-native';
|
|
2
|
+
import { codegenNativeComponent } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface NativeProps extends ViewProps {
|
|
5
|
+
config: string;
|
|
6
|
+
items?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default codegenNativeComponent<NativeProps>(
|
|
10
|
+
'ShortKitWidgetView',
|
|
11
|
+
{},
|
|
12
|
+
) as HostComponent<NativeProps>;
|
package/src/types.ts
CHANGED
|
@@ -2,12 +2,15 @@ import type { ViewStyle } from 'react-native';
|
|
|
2
2
|
|
|
3
3
|
// --- Configuration ---
|
|
4
4
|
|
|
5
|
+
export type FeedSource = 'algorithmic' | 'custom';
|
|
6
|
+
|
|
5
7
|
export interface FeedConfig {
|
|
6
8
|
feedHeight?: FeedHeight;
|
|
7
9
|
overlay?: OverlayConfig;
|
|
8
10
|
carouselMode?: CarouselMode;
|
|
9
11
|
surveyMode?: SurveyMode;
|
|
10
12
|
muteOnStart?: boolean;
|
|
13
|
+
feedSource?: FeedSource;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export type FeedHeight =
|
|
@@ -30,6 +33,7 @@ export type SurveyMode =
|
|
|
30
33
|
|
|
31
34
|
export interface ContentItem {
|
|
32
35
|
id: string;
|
|
36
|
+
playbackId?: string;
|
|
33
37
|
title: string;
|
|
34
38
|
description?: string;
|
|
35
39
|
duration: number;
|
|
@@ -42,6 +46,29 @@ export interface ContentItem {
|
|
|
42
46
|
commentCount?: number;
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
// --- Custom Feed Types ---
|
|
50
|
+
|
|
51
|
+
export interface CarouselImage {
|
|
52
|
+
url: string;
|
|
53
|
+
alt?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ImageCarouselItem {
|
|
57
|
+
id: string;
|
|
58
|
+
images: CarouselImage[];
|
|
59
|
+
autoScrollInterval?: number;
|
|
60
|
+
caption?: string;
|
|
61
|
+
title?: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
author?: string;
|
|
64
|
+
section?: string;
|
|
65
|
+
articleUrl?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type CustomFeedItem =
|
|
69
|
+
| { type: 'video'; playbackId: string }
|
|
70
|
+
| { type: 'imageCarousel'; item: ImageCarouselItem };
|
|
71
|
+
|
|
45
72
|
export type JSONValue =
|
|
46
73
|
| string
|
|
47
74
|
| number
|
|
@@ -109,6 +136,7 @@ export interface ShortKitError {
|
|
|
109
136
|
export interface ShortKitProviderProps {
|
|
110
137
|
apiKey: string;
|
|
111
138
|
config: FeedConfig;
|
|
139
|
+
embedId?: string;
|
|
112
140
|
userId?: string;
|
|
113
141
|
|
|
114
142
|
clientAppName?: string;
|
|
@@ -127,11 +155,55 @@ export interface ShortKitFeedProps {
|
|
|
127
155
|
onLoop?: (event: LoopEvent) => void;
|
|
128
156
|
onFeedTransition?: (event: FeedTransitionEvent) => void;
|
|
129
157
|
onFormatChange?: (event: FormatChangeEvent) => void;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
158
|
+
onContentTapped?: (contentId: string, index: number) => void;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// --- Single Player Config ---
|
|
162
|
+
|
|
163
|
+
export type PlayerClickAction = 'feed' | 'mute' | 'none';
|
|
164
|
+
|
|
165
|
+
export interface PlayerConfig {
|
|
166
|
+
cornerRadius?: number;
|
|
167
|
+
clickAction?: PlayerClickAction;
|
|
168
|
+
autoplay?: boolean;
|
|
169
|
+
loop?: boolean;
|
|
170
|
+
muteOnStart?: boolean;
|
|
171
|
+
overlay?: OverlayConfig;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// --- Widget Config ---
|
|
175
|
+
|
|
176
|
+
export interface WidgetConfig {
|
|
177
|
+
cardCount?: number;
|
|
178
|
+
cardSpacing?: number;
|
|
179
|
+
cornerRadius?: number;
|
|
180
|
+
autoplay?: boolean;
|
|
181
|
+
muteOnStart?: boolean;
|
|
182
|
+
loop?: boolean;
|
|
183
|
+
rotationInterval?: number;
|
|
184
|
+
clickAction?: PlayerClickAction;
|
|
185
|
+
overlay?: OverlayConfig;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// --- Single Player Props ---
|
|
189
|
+
|
|
190
|
+
export interface ShortKitPlayerProps {
|
|
191
|
+
config?: PlayerConfig;
|
|
192
|
+
contentItem?: ContentItem;
|
|
193
|
+
/** Controls whether the player is actively playing. When `false` the player
|
|
194
|
+
* returns to thumbnail-only mode without tearing down the view. Defaults to
|
|
195
|
+
* the value of `config.autoplay` (which itself defaults to `true`). */
|
|
196
|
+
active?: boolean;
|
|
197
|
+
style?: ViewStyle;
|
|
198
|
+
onTap?: () => void;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// --- Widget Props ---
|
|
202
|
+
|
|
203
|
+
export interface ShortKitWidgetProps {
|
|
204
|
+
config?: WidgetConfig;
|
|
205
|
+
items?: ContentItem[];
|
|
206
|
+
style?: ViewStyle;
|
|
135
207
|
}
|
|
136
208
|
|
|
137
209
|
// --- Hook Return Types ---
|
|
@@ -147,6 +219,7 @@ export interface ShortKitPlayerState {
|
|
|
147
219
|
activeCaptionTrack: CaptionTrack | null;
|
|
148
220
|
activeCue: { text: string; startTime: number; endTime: number } | null;
|
|
149
221
|
prefetchedAheadCount: number;
|
|
222
|
+
remainingContentCount: number;
|
|
150
223
|
isActive: boolean;
|
|
151
224
|
isTransitioning: boolean;
|
|
152
225
|
lastOverlayTap: number;
|
package/src/useShortKit.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ShortKitContext } from './ShortKitContext';
|
|
|
6
6
|
*
|
|
7
7
|
* Must be used within a `<ShortKitProvider>`.
|
|
8
8
|
*
|
|
9
|
-
* @returns SDK operations
|
|
9
|
+
* @returns SDK operations and state.
|
|
10
10
|
*/
|
|
11
11
|
export function useShortKit() {
|
|
12
12
|
const context = useContext(ShortKitContext);
|
|
@@ -16,5 +16,9 @@ export function useShortKit() {
|
|
|
16
16
|
return {
|
|
17
17
|
setUserId: context.setUserId,
|
|
18
18
|
clearUserId: context.clearUserId,
|
|
19
|
+
setFeedItems: context.setFeedItems,
|
|
20
|
+
appendFeedItems: context.appendFeedItems,
|
|
21
|
+
fetchContent: context.fetchContent,
|
|
22
|
+
remainingContentCount: context.remainingContentCount,
|
|
19
23
|
};
|
|
20
24
|
}
|