@shortkitsdk/react-native 0.2.6 → 0.2.12
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 +17 -1
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +379 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactLoadingHost.kt +40 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +570 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +1029 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +212 -219
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +17 -3
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +157 -742
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +11 -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 +444 -0
- package/ios/SKFabricSurfaceWrapper.h +18 -0
- package/ios/SKFabricSurfaceWrapper.mm +57 -0
- package/ios/ShortKitBridge.swift +220 -63
- package/ios/ShortKitFeedView.swift +82 -228
- package/ios/ShortKitFeedViewManager.mm +3 -2
- package/ios/ShortKitModule.mm +69 -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 +3683 -1249
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +56 -15
- 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 +56 -15
- 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 +3683 -1249
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +56 -15
- 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 +56 -15
- 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 -24
- package/src/ShortKitFeed.tsx +124 -41
- package/src/ShortKitLoadingSurface.tsx +24 -0
- package/src/ShortKitOverlaySurface.tsx +313 -0
- package/src/ShortKitPlayer.tsx +30 -9
- package/src/ShortKitProvider.tsx +28 -285
- package/src/index.ts +9 -3
- package/src/serialization.ts +20 -39
- package/src/specs/NativeShortKitModule.ts +74 -45
- package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
- package/src/types.ts +84 -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
|
Binary file
|
|
@@ -127,9 +127,9 @@ import ShortKitSDK
|
|
|
127
127
|
if let overlayObj = obj["overlay"] as? [String: Any],
|
|
128
128
|
overlayObj["type"] as? String == "custom" {
|
|
129
129
|
overlayMode = .custom { @Sendable in
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
return
|
|
130
|
+
let host = ReactOverlayHost()
|
|
131
|
+
host.surfacePresenter = ShortKitBridge.shared?.surfacePresenter
|
|
132
|
+
return host
|
|
133
133
|
}
|
|
134
134
|
} else {
|
|
135
135
|
overlayMode = .none
|
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { AppRegistry } from 'react-native';
|
|
3
|
+
import type { CarouselOverlayProps, ImageCarouselItem } from './types';
|
|
4
|
+
|
|
5
|
+
const _carouselRegistry = new Map<string, React.ComponentType<CarouselOverlayProps>>();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Register a named carousel overlay component for rendering inside feed cells.
|
|
9
|
+
* Called by ShortKitFeed on mount via useLayoutEffect.
|
|
10
|
+
* Idempotent — registering the same name twice is a no-op.
|
|
11
|
+
*/
|
|
12
|
+
export function registerCarouselOverlayComponent(
|
|
13
|
+
name: string,
|
|
14
|
+
component: React.ComponentType<CarouselOverlayProps>,
|
|
15
|
+
) {
|
|
16
|
+
if (_carouselRegistry.has(name)) return;
|
|
17
|
+
_carouselRegistry.set(name, component);
|
|
18
|
+
|
|
19
|
+
const moduleName = `ShortKitCarouselOverlay_${name}`;
|
|
20
|
+
AppRegistry.registerComponent(moduleName, () => {
|
|
21
|
+
return function NamedCarouselSurface(props: RawCarouselSurfaceProps) {
|
|
22
|
+
return <CarouselSurfaceInner {...props} overlayName={name} />;
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface RawCarouselSurfaceProps {
|
|
28
|
+
item?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface InnerProps extends RawCarouselSurfaceProps {
|
|
32
|
+
overlayName: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function CarouselSurfaceInner(props: InnerProps) {
|
|
36
|
+
const Component = _carouselRegistry.get(props.overlayName);
|
|
37
|
+
if (!Component) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const item: ImageCarouselItem | null = useMemo(() => {
|
|
42
|
+
if (!props.item) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(props.item);
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}, [props.item]);
|
|
51
|
+
|
|
52
|
+
if (!item) return null;
|
|
53
|
+
|
|
54
|
+
return <Component item={item} />;
|
|
55
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import NativeShortKitModule from './specs/NativeShortKitModule';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Imperative player commands for use inside overlay components.
|
|
5
|
+
*
|
|
6
|
+
* Overlay components run in isolated React surfaces and cannot access
|
|
7
|
+
* ShortKitProvider context. Use these commands instead of useShortKitPlayer()
|
|
8
|
+
* hooks for player control.
|
|
9
|
+
*/
|
|
10
|
+
export const ShortKitCommands = {
|
|
11
|
+
play: () => NativeShortKitModule?.play(),
|
|
12
|
+
pause: () => NativeShortKitModule?.pause(),
|
|
13
|
+
seek: (seconds: number) => NativeShortKitModule?.seek(seconds),
|
|
14
|
+
seekAndPlay: (seconds: number) => NativeShortKitModule?.seekAndPlay(seconds),
|
|
15
|
+
skipToNext: () => NativeShortKitModule?.skipToNext(),
|
|
16
|
+
skipToPrevious: () => NativeShortKitModule?.skipToPrevious(),
|
|
17
|
+
setMuted: (muted: boolean) => NativeShortKitModule?.setMuted(muted),
|
|
18
|
+
setPlaybackRate: (rate: number) => NativeShortKitModule?.setPlaybackRate(rate),
|
|
19
|
+
setCaptionsEnabled: (enabled: boolean) =>
|
|
20
|
+
NativeShortKitModule?.setCaptionsEnabled(enabled),
|
|
21
|
+
selectCaptionTrack: (language: string) =>
|
|
22
|
+
NativeShortKitModule?.selectCaptionTrack(language),
|
|
23
|
+
sendContentSignal: (signal: 'positive' | 'negative') =>
|
|
24
|
+
NativeShortKitModule?.sendContentSignal(signal),
|
|
25
|
+
setMaxBitrate: (bitrate: number) =>
|
|
26
|
+
NativeShortKitModule?.setMaxBitrate(bitrate),
|
|
27
|
+
prefetchStoryboard: (playbackId: string) =>
|
|
28
|
+
NativeShortKitModule?.prefetchStoryboard(playbackId),
|
|
29
|
+
getStoryboardData: (playbackId: string): Promise<string | null> =>
|
|
30
|
+
NativeShortKitModule?.getStoryboardData(playbackId) ?? Promise.resolve(null),
|
|
31
|
+
} as const;
|
package/src/ShortKitContext.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
import type {
|
|
3
3
|
ContentItem,
|
|
4
|
-
|
|
4
|
+
FeedConfig,
|
|
5
|
+
FeedFilter,
|
|
5
6
|
FeedInput,
|
|
6
7
|
PlayerTime,
|
|
7
8
|
PlayerState,
|
|
8
9
|
CaptionTrack,
|
|
9
10
|
FeedScrollPhase,
|
|
10
11
|
ContentSignal,
|
|
11
|
-
OverlayConfig,
|
|
12
|
-
CarouselOverlayConfig,
|
|
13
12
|
StoryboardData,
|
|
14
13
|
} from './types';
|
|
15
14
|
|
|
@@ -17,7 +16,6 @@ export interface ShortKitContextValue {
|
|
|
17
16
|
// Player state
|
|
18
17
|
playerState: PlayerState;
|
|
19
18
|
currentItem: ContentItem | null;
|
|
20
|
-
nextItem: ContentItem | null;
|
|
21
19
|
time: PlayerTime;
|
|
22
20
|
isMuted: boolean;
|
|
23
21
|
playbackRate: number;
|
|
@@ -25,11 +23,8 @@ export interface ShortKitContextValue {
|
|
|
25
23
|
activeCaptionTrack: CaptionTrack | null;
|
|
26
24
|
activeCue: { text: string; startTime: number; endTime: number } | null;
|
|
27
25
|
prefetchedAheadCount: number;
|
|
28
|
-
remainingContentCount: number;
|
|
29
26
|
isActive: boolean;
|
|
30
27
|
feedScrollPhase: FeedScrollPhase | null;
|
|
31
|
-
lastOverlayTap: number;
|
|
32
|
-
lastOverlayDoubleTap: { x: number; y: number; id: number } | null;
|
|
33
28
|
|
|
34
29
|
// Player commands
|
|
35
30
|
play: () => void;
|
|
@@ -48,27 +43,14 @@ export interface ShortKitContextValue {
|
|
|
48
43
|
// SDK operations
|
|
49
44
|
setUserId: (id: string) => void;
|
|
50
45
|
clearUserId: () => void;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
fetchContent: (limit?: number, filter?: FeedFilter) => Promise<ContentItem[]>;
|
|
47
|
+
|
|
48
|
+
// Preload
|
|
49
|
+
preloadFeed: (config?: Partial<FeedConfig>, items?: FeedInput[]) => Promise<string>;
|
|
54
50
|
|
|
55
51
|
// Storyboard / seek thumbnails
|
|
56
52
|
prefetchStoryboard: (playbackId: string) => void;
|
|
57
53
|
getStoryboardData: (playbackId: string) => Promise<StoryboardData | null>;
|
|
58
|
-
|
|
59
|
-
// Carousel overlay state
|
|
60
|
-
currentCarouselItem: ImageCarouselItem | null;
|
|
61
|
-
nextCarouselItem: ImageCarouselItem | null;
|
|
62
|
-
isCarouselActive: boolean;
|
|
63
|
-
currentCarouselPage: number;
|
|
64
|
-
// Active cell type — used by overlay managers to show/hide
|
|
65
|
-
activeCellType: 'video' | 'carousel' | null;
|
|
66
|
-
|
|
67
|
-
// Internal — used by ShortKitFeed to render custom overlays
|
|
68
|
-
/** @internal */
|
|
69
|
-
_overlayConfig: OverlayConfig;
|
|
70
|
-
/** @internal */
|
|
71
|
-
_carouselOverlayConfig: CarouselOverlayConfig;
|
|
72
54
|
}
|
|
73
55
|
|
|
74
56
|
export const ShortKitContext = createContext<ShortKitContextValue | null>(null);
|
package/src/ShortKitFeed.tsx
CHANGED
|
@@ -1,36 +1,47 @@
|
|
|
1
|
-
import React, { useContext, useEffect } from 'react';
|
|
1
|
+
import React, { useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, forwardRef } from 'react';
|
|
2
2
|
import { View, StyleSheet } from 'react-native';
|
|
3
|
-
import type { ShortKitFeedProps } from './types';
|
|
3
|
+
import type { ShortKitFeedProps, FeedInput, FeedFilter, ShortKitFeedHandle } from './types';
|
|
4
4
|
import ShortKitFeedView from './specs/ShortKitFeedViewNativeComponent';
|
|
5
5
|
import NativeShortKitModule from './specs/NativeShortKitModule';
|
|
6
|
-
import { OverlayManager } from './OverlayManager';
|
|
7
|
-
import { CarouselOverlayManager } from './CarouselOverlayManager';
|
|
8
6
|
import { ShortKitContext } from './ShortKitContext';
|
|
9
|
-
import { deserializeContentItem } from './serialization';
|
|
7
|
+
import { deserializeContentItem, serializeFeedConfig, serializeFeedInputs } from './serialization';
|
|
8
|
+
import { registerOverlayComponent } from './ShortKitOverlaySurface';
|
|
9
|
+
import { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurface';
|
|
10
|
+
|
|
11
|
+
function generateFeedId(): string {
|
|
12
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
13
|
+
const r = (Math.random() * 16) | 0;
|
|
14
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
15
|
+
return v.toString(16);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
10
18
|
|
|
11
19
|
/**
|
|
12
20
|
* Renders the native ShortKit video feed and forwards feed-level events to
|
|
13
21
|
* callback props.
|
|
14
22
|
*
|
|
15
|
-
* Must be rendered inside a `<ShortKitProvider>`.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* When the overlay config is of type `custom`, the `OverlayManager` is
|
|
21
|
-
* rendered alongside the native view to display the developer's React overlay
|
|
22
|
-
* component.
|
|
23
|
+
* Must be rendered inside a `<ShortKitProvider>`. Feed configuration is passed
|
|
24
|
+
* via the `config` prop. Custom overlay components are registered with
|
|
25
|
+
* AppRegistry via the named overlay registry and mounted directly by native
|
|
26
|
+
* per-cell RCTFabricSurface instances.
|
|
23
27
|
*/
|
|
24
|
-
export
|
|
28
|
+
export const ShortKitFeed = forwardRef<ShortKitFeedHandle, ShortKitFeedProps>(
|
|
29
|
+
function ShortKitFeed(props, ref) {
|
|
25
30
|
const {
|
|
31
|
+
config,
|
|
32
|
+
preloadId,
|
|
26
33
|
style,
|
|
27
|
-
|
|
28
|
-
onShareTapped,
|
|
34
|
+
startAtItemId,
|
|
29
35
|
onSurveyResponse,
|
|
30
36
|
onLoop,
|
|
31
37
|
onFeedTransition,
|
|
32
38
|
onFormatChange,
|
|
33
39
|
onContentTapped,
|
|
40
|
+
onDismiss,
|
|
41
|
+
onRefreshRequested,
|
|
42
|
+
onDidFetchContentItems,
|
|
43
|
+
onRemainingContentCountChange,
|
|
44
|
+
onFeedReady,
|
|
34
45
|
} = props;
|
|
35
46
|
|
|
36
47
|
const context = useContext(ShortKitContext);
|
|
@@ -38,8 +49,66 @@ export function ShortKitFeed(props: ShortKitFeedProps) {
|
|
|
38
49
|
throw new Error('ShortKitFeed must be used within a ShortKitProvider');
|
|
39
50
|
}
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
const
|
|
52
|
+
// Stable feed instance ID — survives re-renders and React fast refresh
|
|
53
|
+
const feedIdRef = useRef(generateFeedId());
|
|
54
|
+
const feedId = feedIdRef.current;
|
|
55
|
+
|
|
56
|
+
// Expose per-feed imperative methods on the ref
|
|
57
|
+
useImperativeHandle(ref, () => ({
|
|
58
|
+
setFeedItems: (items: FeedInput[]) => {
|
|
59
|
+
NativeShortKitModule?.setFeedItems(feedId, serializeFeedInputs(items));
|
|
60
|
+
},
|
|
61
|
+
appendFeedItems: (items: FeedInput[]) => {
|
|
62
|
+
NativeShortKitModule?.appendFeedItems(feedId, serializeFeedInputs(items));
|
|
63
|
+
},
|
|
64
|
+
applyFilter: (filter: FeedFilter | null) => {
|
|
65
|
+
NativeShortKitModule?.applyFilter(feedId, filter ? JSON.stringify(filter) : null);
|
|
66
|
+
},
|
|
67
|
+
}), [feedId]);
|
|
68
|
+
|
|
69
|
+
// Subscribe to per-feed remaining content count events
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!NativeShortKitModule || !onRemainingContentCountChange) return;
|
|
72
|
+
|
|
73
|
+
const subscription = NativeShortKitModule.onRemainingContentCountChanged((event) => {
|
|
74
|
+
// Match by feedId, or accept empty feedId as fallback (Android global routing)
|
|
75
|
+
if (event.feedId === feedId || event.feedId === '') {
|
|
76
|
+
onRemainingContentCountChange(event.count);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return () => subscription.remove();
|
|
81
|
+
}, [feedId, onRemainingContentCountChange]);
|
|
82
|
+
|
|
83
|
+
// Subscribe to per-feed ready event
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (!NativeShortKitModule || !onFeedReady) return;
|
|
86
|
+
|
|
87
|
+
const subscription = NativeShortKitModule.onFeedReady((event) => {
|
|
88
|
+
if (event.feedId === feedId) {
|
|
89
|
+
onFeedReady();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return () => subscription.remove();
|
|
94
|
+
}, [feedId, onFeedReady]);
|
|
95
|
+
|
|
96
|
+
// Register overlay components before native view mounts.
|
|
97
|
+
// useLayoutEffect fires after commit but before paint — before
|
|
98
|
+
// didMoveToWindow/onAttachedToWindow on the native view.
|
|
99
|
+
useLayoutEffect(() => {
|
|
100
|
+
if (config?.overlay && config.overlay !== 'none') {
|
|
101
|
+
registerOverlayComponent(config.overlay.name, config.overlay.component);
|
|
102
|
+
}
|
|
103
|
+
if (config?.carouselOverlay && config.carouselOverlay !== 'none') {
|
|
104
|
+
registerCarouselOverlayComponent(config.carouselOverlay.name, config.carouselOverlay.component);
|
|
105
|
+
}
|
|
106
|
+
}, [config?.overlay, config?.carouselOverlay]);
|
|
107
|
+
|
|
108
|
+
const serializedConfig = useMemo(
|
|
109
|
+
() => (config ? serializeFeedConfig(config) : '{}'),
|
|
110
|
+
[config],
|
|
111
|
+
);
|
|
43
112
|
|
|
44
113
|
// ---------------------------------------------------------------------------
|
|
45
114
|
// Subscribe to feed-level events and forward to callback props
|
|
@@ -49,23 +118,6 @@ export function ShortKitFeed(props: ShortKitFeedProps) {
|
|
|
49
118
|
|
|
50
119
|
const subscriptions: Array<{ remove: () => void }> = [];
|
|
51
120
|
|
|
52
|
-
if (onError) {
|
|
53
|
-
subscriptions.push(
|
|
54
|
-
NativeShortKitModule.onError((event) => {
|
|
55
|
-
onError({ code: event.code, message: event.message });
|
|
56
|
-
}),
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (onShareTapped) {
|
|
61
|
-
subscriptions.push(
|
|
62
|
-
NativeShortKitModule.onShareTapped((event) => {
|
|
63
|
-
const item = deserializeContentItem(event.item);
|
|
64
|
-
if (item) onShareTapped(item);
|
|
65
|
-
}),
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
121
|
if (onSurveyResponse) {
|
|
70
122
|
subscriptions.push(
|
|
71
123
|
NativeShortKitModule.onSurveyResponse((event) => {
|
|
@@ -120,19 +172,49 @@ export function ShortKitFeed(props: ShortKitFeedProps) {
|
|
|
120
172
|
);
|
|
121
173
|
}
|
|
122
174
|
|
|
175
|
+
if (onDismiss) {
|
|
176
|
+
subscriptions.push(
|
|
177
|
+
NativeShortKitModule.onDismiss(() => {
|
|
178
|
+
onDismiss();
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (onRefreshRequested) {
|
|
184
|
+
subscriptions.push(
|
|
185
|
+
NativeShortKitModule.onRefreshRequested(() => {
|
|
186
|
+
onRefreshRequested();
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (onDidFetchContentItems) {
|
|
192
|
+
subscriptions.push(
|
|
193
|
+
NativeShortKitModule.onDidFetchContentItems((event) => {
|
|
194
|
+
try {
|
|
195
|
+
const items = JSON.parse(event.items);
|
|
196
|
+
onDidFetchContentItems(items);
|
|
197
|
+
} catch {
|
|
198
|
+
// ignore malformed JSON
|
|
199
|
+
}
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
123
204
|
return () => {
|
|
124
205
|
for (const sub of subscriptions) {
|
|
125
206
|
sub.remove();
|
|
126
207
|
}
|
|
127
208
|
};
|
|
128
209
|
}, [
|
|
129
|
-
onError,
|
|
130
|
-
onShareTapped,
|
|
131
210
|
onSurveyResponse,
|
|
132
211
|
onLoop,
|
|
133
212
|
onFeedTransition,
|
|
134
213
|
onFormatChange,
|
|
135
214
|
onContentTapped,
|
|
215
|
+
onDismiss,
|
|
216
|
+
onRefreshRequested,
|
|
217
|
+
onDidFetchContentItems,
|
|
136
218
|
]);
|
|
137
219
|
|
|
138
220
|
// ---------------------------------------------------------------------------
|
|
@@ -142,13 +224,14 @@ export function ShortKitFeed(props: ShortKitFeedProps) {
|
|
|
142
224
|
<View style={[feedStyles.container, style]}>
|
|
143
225
|
<ShortKitFeedView
|
|
144
226
|
style={feedStyles.feed}
|
|
145
|
-
config=
|
|
227
|
+
config={serializedConfig}
|
|
228
|
+
feedId={feedId}
|
|
229
|
+
startAtItemId={startAtItemId}
|
|
230
|
+
preloadId={preloadId}
|
|
146
231
|
/>
|
|
147
|
-
<OverlayManager overlay={overlayConfig} />
|
|
148
|
-
<CarouselOverlayManager carouselOverlay={carouselOverlayConfig} />
|
|
149
232
|
</View>
|
|
150
233
|
);
|
|
151
|
-
}
|
|
234
|
+
});
|
|
152
235
|
|
|
153
236
|
const feedStyles = StyleSheet.create({
|
|
154
237
|
container: {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AppRegistry, View, StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
const SURFACE_NAME = 'ShortKitLoading';
|
|
5
|
+
let registered = false;
|
|
6
|
+
|
|
7
|
+
export function registerLoadingComponent(
|
|
8
|
+
Component: React.ComponentType<{}>,
|
|
9
|
+
): void {
|
|
10
|
+
if (registered) return;
|
|
11
|
+
registered = true;
|
|
12
|
+
|
|
13
|
+
const LoadingRoot = () => (
|
|
14
|
+
<View style={styles.container}>
|
|
15
|
+
<Component />
|
|
16
|
+
</View>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
AppRegistry.registerComponent(SURFACE_NAME, () => LoadingRoot);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const styles = StyleSheet.create({
|
|
23
|
+
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
|
24
|
+
});
|