@shortkitsdk/react-native 0.1.0

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.
Files changed (35) hide show
  1. package/ShortKitReactNative.podspec +19 -0
  2. package/android/build.gradle.kts +34 -0
  3. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +249 -0
  4. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +32 -0
  5. package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +769 -0
  6. package/android/src/main/java/com/shortkit/reactnative/ShortKitOverlayBridge.kt +101 -0
  7. package/android/src/main/java/com/shortkit/reactnative/ShortKitPackage.kt +40 -0
  8. package/app.plugin.js +1 -0
  9. package/ios/ShortKitBridge.swift +537 -0
  10. package/ios/ShortKitFeedView.swift +207 -0
  11. package/ios/ShortKitFeedViewManager.mm +29 -0
  12. package/ios/ShortKitModule.h +25 -0
  13. package/ios/ShortKitModule.mm +204 -0
  14. package/ios/ShortKitOverlayBridge.swift +91 -0
  15. package/ios/ShortKitReactNative-Bridging-Header.h +3 -0
  16. package/ios/ShortKitReactNative.podspec +19 -0
  17. package/package.json +50 -0
  18. package/plugin/build/index.d.ts +3 -0
  19. package/plugin/build/index.js +13 -0
  20. package/plugin/build/withShortKitAndroid.d.ts +8 -0
  21. package/plugin/build/withShortKitAndroid.js +32 -0
  22. package/plugin/build/withShortKitIOS.d.ts +8 -0
  23. package/plugin/build/withShortKitIOS.js +29 -0
  24. package/react-native.config.js +8 -0
  25. package/src/OverlayManager.tsx +87 -0
  26. package/src/ShortKitContext.ts +51 -0
  27. package/src/ShortKitFeed.tsx +203 -0
  28. package/src/ShortKitProvider.tsx +526 -0
  29. package/src/index.ts +26 -0
  30. package/src/serialization.ts +95 -0
  31. package/src/specs/NativeShortKitModule.ts +201 -0
  32. package/src/specs/ShortKitFeedViewNativeComponent.ts +13 -0
  33. package/src/types.ts +167 -0
  34. package/src/useShortKit.ts +20 -0
  35. package/src/useShortKitPlayer.ts +29 -0
@@ -0,0 +1,201 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+ import type { Double, Int32, EventEmitter } from 'react-native/Libraries/Types/CodegenTypes';
4
+
5
+ // --- Event payload types (codegen-compatible inline object types) ---
6
+
7
+ type PlayerStateEvent = Readonly<{
8
+ state: string;
9
+ errorMessage?: string;
10
+ }>;
11
+
12
+ type CurrentItemEvent = Readonly<{
13
+ id: string;
14
+ title: string;
15
+ description?: string;
16
+ duration: Double;
17
+ streamingUrl: string;
18
+ thumbnailUrl: string;
19
+ captionTracks: string; // JSON-serialized CaptionTrack[]
20
+ customMetadata?: string; // JSON-serialized Record<string, JSONValue>
21
+ author?: string;
22
+ articleUrl?: string;
23
+ commentCount?: Int32;
24
+ }>;
25
+
26
+ type TimeUpdateEvent = Readonly<{
27
+ current: Double;
28
+ duration: Double;
29
+ buffered: Double;
30
+ }>;
31
+
32
+ type MutedEvent = Readonly<{
33
+ isMuted: boolean;
34
+ }>;
35
+
36
+ type PlaybackRateEvent = Readonly<{
37
+ rate: Double;
38
+ }>;
39
+
40
+ type CaptionsEnabledEvent = Readonly<{
41
+ enabled: boolean;
42
+ }>;
43
+
44
+ type CaptionTrackEvent = Readonly<{
45
+ language: string;
46
+ label: string;
47
+ sourceUrl: string;
48
+ }>;
49
+
50
+ type CueEvent = Readonly<{
51
+ text: string;
52
+ startTime: Double;
53
+ endTime: Double;
54
+ }>;
55
+
56
+ type LoopEvent = Readonly<{
57
+ contentId: string;
58
+ loopCount: Int32;
59
+ }>;
60
+
61
+ type FeedTransitionEvent = Readonly<{
62
+ phase: string;
63
+ fromItem?: string; // JSON-serialized ContentItem
64
+ toItem?: string; // JSON-serialized ContentItem
65
+ direction: string;
66
+ }>;
67
+
68
+ type FormatChangeEvent = Readonly<{
69
+ contentId: string;
70
+ fromBitrate: Double;
71
+ toBitrate: Double;
72
+ fromResolution: string;
73
+ toResolution: string;
74
+ }>;
75
+
76
+ type PrefetchedAheadCountEvent = Readonly<{
77
+ count: Int32;
78
+ }>;
79
+
80
+ type ErrorEvent = Readonly<{
81
+ code: string;
82
+ message: string;
83
+ }>;
84
+
85
+ type ShareTappedEvent = Readonly<{
86
+ item: string; // JSON-serialized ContentItem
87
+ }>;
88
+
89
+ type SurveyResponseEvent = Readonly<{
90
+ surveyId: string;
91
+ optionId: string;
92
+ optionText: string;
93
+ }>;
94
+
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
+ type OverlayConfigureEvent = Readonly<{
116
+ item: string; // JSON-serialized ContentItem
117
+ }>;
118
+
119
+ type OverlayActivateEvent = Readonly<{
120
+ item: string; // JSON-serialized ContentItem
121
+ }>;
122
+
123
+ type OverlayResetEvent = Readonly<{
124
+ item: string; // JSON-serialized ContentItem
125
+ }>;
126
+
127
+ type OverlayFadeOutEvent = Readonly<{
128
+ item: string; // JSON-serialized ContentItem
129
+ }>;
130
+
131
+ type OverlayRestoreEvent = Readonly<{
132
+ item: string; // JSON-serialized ContentItem
133
+ }>;
134
+
135
+ type OverlayTapEvent = Readonly<{}>;
136
+
137
+ type OverlayDoubleTapEvent = Readonly<{
138
+ x: Double;
139
+ y: Double;
140
+ }>;
141
+
142
+ export interface Spec extends TurboModule {
143
+ // --- Lifecycle ---
144
+ initialize(
145
+ apiKey: string,
146
+ config: string, // JSON-serialized FeedConfig
147
+ clientAppName?: string,
148
+ clientAppVersion?: string,
149
+ customDimensions?: string, // JSON-serialized Record<string, string>
150
+ ): void;
151
+ setUserId(userId: string): void;
152
+ clearUserId(): void;
153
+ onPause(): void;
154
+ onResume(): void;
155
+ destroy(): void;
156
+
157
+ // --- Player controls ---
158
+ play(): void;
159
+ pause(): void;
160
+ seek(seconds: Double): void;
161
+ seekAndPlay(seconds: Double): void;
162
+ skipToNext(): void;
163
+ skipToPrevious(): void;
164
+ setMuted(muted: boolean): void;
165
+ setPlaybackRate(rate: Double): void;
166
+ setCaptionsEnabled(enabled: boolean): void;
167
+ selectCaptionTrack(language: string): void;
168
+ sendContentSignal(signal: string): void;
169
+ setMaxBitrate(bitrate: Double): void;
170
+
171
+ // --- Event emitters ---
172
+ readonly onPlayerStateChanged: EventEmitter<PlayerStateEvent>;
173
+ readonly onCurrentItemChanged: EventEmitter<CurrentItemEvent>;
174
+ readonly onTimeUpdate: EventEmitter<TimeUpdateEvent>;
175
+ readonly onMutedChanged: EventEmitter<MutedEvent>;
176
+ readonly onPlaybackRateChanged: EventEmitter<PlaybackRateEvent>;
177
+ readonly onCaptionsEnabledChanged: EventEmitter<CaptionsEnabledEvent>;
178
+ readonly onActiveCaptionTrackChanged: EventEmitter<CaptionTrackEvent>;
179
+ readonly onActiveCueChanged: EventEmitter<CueEvent>;
180
+ readonly onDidLoop: EventEmitter<LoopEvent>;
181
+ readonly onFeedTransition: EventEmitter<FeedTransitionEvent>;
182
+ readonly onFormatChange: EventEmitter<FormatChangeEvent>;
183
+ readonly onPrefetchedAheadCountChanged: EventEmitter<PrefetchedAheadCountEvent>;
184
+ readonly onError: EventEmitter<ErrorEvent>;
185
+ readonly onShareTapped: EventEmitter<ShareTappedEvent>;
186
+ 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
+ readonly onOverlayConfigure: EventEmitter<OverlayConfigureEvent>;
193
+ readonly onOverlayActivate: EventEmitter<OverlayActivateEvent>;
194
+ readonly onOverlayReset: EventEmitter<OverlayResetEvent>;
195
+ readonly onOverlayFadeOut: EventEmitter<OverlayFadeOutEvent>;
196
+ readonly onOverlayRestore: EventEmitter<OverlayRestoreEvent>;
197
+ readonly onOverlayTap: EventEmitter<OverlayTapEvent>;
198
+ readonly onOverlayDoubleTap: EventEmitter<OverlayDoubleTapEvent>;
199
+ }
200
+
201
+ 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
+ import type { WithDefault } from 'react-native/Libraries/Types/CodegenTypes';
4
+
5
+ export interface NativeProps extends ViewProps {
6
+ config: string;
7
+ overlayType?: WithDefault<'none' | 'custom', 'none'>;
8
+ }
9
+
10
+ export default codegenNativeComponent<NativeProps>(
11
+ 'ShortKitFeedView',
12
+ {},
13
+ ) as HostComponent<NativeProps>;
package/src/types.ts ADDED
@@ -0,0 +1,167 @@
1
+ import type { ViewStyle } from 'react-native';
2
+
3
+ // --- Configuration ---
4
+
5
+ export interface FeedConfig {
6
+ feedHeight?: FeedHeight;
7
+ overlay?: OverlayConfig;
8
+ carouselMode?: CarouselMode;
9
+ surveyMode?: SurveyMode;
10
+ muteOnStart?: boolean;
11
+ }
12
+
13
+ export type FeedHeight =
14
+ | { type: 'fullscreen' }
15
+ | { type: 'percentage'; value: number };
16
+
17
+ export type OverlayConfig =
18
+ | 'none'
19
+ | { type: 'custom'; component: React.ComponentType };
20
+
21
+ export type CarouselMode =
22
+ | 'none'
23
+ | { type: 'template'; name: 'default' };
24
+
25
+ export type SurveyMode =
26
+ | 'none'
27
+ | { type: 'template'; name: 'default' };
28
+
29
+ // --- Data Models ---
30
+
31
+ export interface ContentItem {
32
+ id: string;
33
+ title: string;
34
+ description?: string;
35
+ duration: number;
36
+ streamingUrl: string;
37
+ thumbnailUrl: string;
38
+ captionTracks: CaptionTrack[];
39
+ customMetadata?: Record<string, JSONValue>;
40
+ author?: string;
41
+ articleUrl?: string;
42
+ commentCount?: number;
43
+ }
44
+
45
+ export type JSONValue =
46
+ | string
47
+ | number
48
+ | boolean
49
+ | null
50
+ | { [key: string]: JSONValue };
51
+
52
+ export interface CaptionTrack {
53
+ language: string;
54
+ label: string;
55
+ sourceUrl: string;
56
+ }
57
+
58
+ export interface PlayerTime {
59
+ current: number;
60
+ duration: number;
61
+ buffered: number;
62
+ }
63
+
64
+ export type PlayerState =
65
+ | 'idle'
66
+ | 'loading'
67
+ | 'ready'
68
+ | 'playing'
69
+ | 'paused'
70
+ | 'seeking'
71
+ | 'buffering'
72
+ | 'ended'
73
+ | { error: string };
74
+
75
+ export interface LoopEvent {
76
+ contentId: string;
77
+ loopCount: number;
78
+ }
79
+
80
+ export interface FeedTransitionEvent {
81
+ phase: 'began' | 'ended';
82
+ from: ContentItem | null;
83
+ to: ContentItem | null;
84
+ direction: 'forward' | 'backward';
85
+ }
86
+
87
+ export interface FormatChangeEvent {
88
+ contentId: string;
89
+ fromBitrate: number;
90
+ toBitrate: number;
91
+ fromResolution: string;
92
+ toResolution: string;
93
+ }
94
+
95
+ export type ContentSignal = 'positive' | 'negative';
96
+
97
+ export interface SurveyOption {
98
+ id: string;
99
+ text: string;
100
+ }
101
+
102
+ export interface ShortKitError {
103
+ code: string;
104
+ message: string;
105
+ }
106
+
107
+ // --- Provider Props ---
108
+
109
+ export interface ShortKitProviderProps {
110
+ apiKey: string;
111
+ config: FeedConfig;
112
+ userId?: string;
113
+
114
+ clientAppName?: string;
115
+ clientAppVersion?: string;
116
+ customDimensions?: Record<string, string>;
117
+ children: React.ReactNode;
118
+ }
119
+
120
+ // --- Feed Component Props ---
121
+
122
+ export interface ShortKitFeedProps {
123
+ style?: ViewStyle;
124
+ onError?: (error: ShortKitError) => void;
125
+ onShareTapped?: (item: ContentItem) => void;
126
+ onSurveyResponse?: (surveyId: string, option: SurveyOption) => void;
127
+ onLoop?: (event: LoopEvent) => void;
128
+ onFeedTransition?: (event: FeedTransitionEvent) => void;
129
+ onFormatChange?: (event: FormatChangeEvent) => void;
130
+ onArticleTapped?: (item: ContentItem) => void;
131
+ onCommentTapped?: (item: ContentItem) => void;
132
+ onOverlayShareTapped?: (item: ContentItem) => void;
133
+ onSaveTapped?: (item: ContentItem) => void;
134
+ onLikeTapped?: (item: ContentItem) => void;
135
+ }
136
+
137
+ // --- Hook Return Types ---
138
+
139
+ export interface ShortKitPlayerState {
140
+ playerState: PlayerState;
141
+ currentItem: ContentItem | null;
142
+ nextItem: ContentItem | null;
143
+ time: PlayerTime;
144
+ isMuted: boolean;
145
+ playbackRate: number;
146
+ captionsEnabled: boolean;
147
+ activeCaptionTrack: CaptionTrack | null;
148
+ activeCue: { text: string; startTime: number; endTime: number } | null;
149
+ prefetchedAheadCount: number;
150
+ isActive: boolean;
151
+ isTransitioning: boolean;
152
+ lastOverlayTap: number;
153
+ lastOverlayDoubleTap: { x: number; y: number; id: number } | null;
154
+
155
+ play: () => void;
156
+ pause: () => void;
157
+ seek: (seconds: number) => void;
158
+ seekAndPlay: (seconds: number) => void;
159
+ skipToNext: () => void;
160
+ skipToPrevious: () => void;
161
+ setMuted: (muted: boolean) => void;
162
+ setPlaybackRate: (rate: number) => void;
163
+ setCaptionsEnabled: (enabled: boolean) => void;
164
+ selectCaptionTrack: (language: string) => void;
165
+ sendContentSignal: (signal: ContentSignal) => void;
166
+ setMaxBitrate: (bitrate: number) => void;
167
+ }
@@ -0,0 +1,20 @@
1
+ import { useContext } from 'react';
2
+ import { ShortKitContext } from './ShortKitContext';
3
+
4
+ /**
5
+ * Hook to access SDK-level operations from the nearest ShortKitProvider.
6
+ *
7
+ * Must be used within a `<ShortKitProvider>`.
8
+ *
9
+ * @returns SDK operations: `setUserId` and `clearUserId`.
10
+ */
11
+ export function useShortKit() {
12
+ const context = useContext(ShortKitContext);
13
+ if (!context) {
14
+ throw new Error('useShortKit must be used within a ShortKitProvider');
15
+ }
16
+ return {
17
+ setUserId: context.setUserId,
18
+ clearUserId: context.clearUserId,
19
+ };
20
+ }
@@ -0,0 +1,29 @@
1
+ import { useContext } from 'react';
2
+ import { ShortKitContext } from './ShortKitContext';
3
+ import type { ShortKitPlayerState } from './types';
4
+
5
+ /**
6
+ * Hook to access player state and commands from the nearest ShortKitProvider.
7
+ *
8
+ * Must be used within a `<ShortKitProvider>`.
9
+ *
10
+ * @returns Player state (playerState, currentItem, time, etc.) and
11
+ * command functions (play, pause, seek, etc.).
12
+ */
13
+ export function useShortKitPlayer(): ShortKitPlayerState {
14
+ const context = useContext(ShortKitContext);
15
+ if (!context) {
16
+ throw new Error('useShortKitPlayer must be used within a ShortKitProvider');
17
+ }
18
+
19
+ // Return only player-related state and commands (exclude SDK operations
20
+ // and internal fields)
21
+ const {
22
+ setUserId: _setUserId,
23
+ clearUserId: _clearUserId,
24
+ _overlayConfig: _overlay,
25
+ ...playerState
26
+ } = context;
27
+
28
+ return playerState;
29
+ }