@shortkitsdk/react-native 0.2.32 → 0.2.34
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/libs/shortkit-release.aar +0 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +26 -5
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +19 -5
- package/ios/FeedMaskHostView.swift +190 -0
- package/ios/ShortKitBridge.swift +34 -0
- package/ios/ShortKitPlayerNativeView.swift +31 -0
- package/ios/ShortKitPlayerNativeViewManager.mm +1 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +1919 -106
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +49 -5
- 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 +49 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1919 -106
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +49 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +49 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +1919 -106
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +49 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +49 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
- package/ios/ShortKitWidgetNativeView.swift +159 -6
- package/ios/ShortKitWidgetNativeViewManager.mm +2 -0
- package/package.json +1 -1
- package/src/ShortKitFeedMaskSurface.tsx +132 -0
- package/src/ShortKitPlayer.tsx +15 -2
- package/src/ShortKitWidget.tsx +16 -2
- package/src/index.ts +8 -1
- package/src/serialization.ts +15 -0
- package/src/specs/ShortKitPlayerViewNativeComponent.ts +7 -0
- package/src/specs/ShortKitWidgetViewNativeComponent.ts +15 -0
- package/src/types.ts +95 -0
|
@@ -24,5 +24,7 @@ RCT_EXPORT_MODULE(ShortKitWidgetView)
|
|
|
24
24
|
RCT_EXPORT_VIEW_PROPERTY(config, NSString)
|
|
25
25
|
RCT_EXPORT_VIEW_PROPERTY(items, NSString)
|
|
26
26
|
RCT_EXPORT_VIEW_PROPERTY(widgetId, NSString)
|
|
27
|
+
RCT_EXPORT_VIEW_PROPERTY(active, BOOL)
|
|
28
|
+
RCT_EXPORT_VIEW_PROPERTY(feedItems, NSString)
|
|
27
29
|
|
|
28
30
|
@end
|
package/package.json
CHANGED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AppRegistry, View, Text } from 'react-native';
|
|
3
|
+
import type { FeedMaskProps, ContentItem } from './types';
|
|
4
|
+
import { deserializeContentItem } from './serialization';
|
|
5
|
+
|
|
6
|
+
const SK_MASK_TAG = '[ShortKit:FeedMaskSurface]';
|
|
7
|
+
|
|
8
|
+
// Named registry — supports different mask components per host site.
|
|
9
|
+
const _maskRegistry = new Map<string, React.ComponentType<FeedMaskProps>>();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Register a named feed-mask component for rendering around the
|
|
13
|
+
* expanded feed. Called by `<ShortKitPlayer>` / `<ShortKitWidget>` on
|
|
14
|
+
* mount via `useLayoutEffect` whenever `config.feedMask` is set with
|
|
15
|
+
* `type: 'custom'`. Idempotent — registering the same name twice is
|
|
16
|
+
* a no-op.
|
|
17
|
+
*/
|
|
18
|
+
export function registerFeedMaskComponent(
|
|
19
|
+
name: string,
|
|
20
|
+
component: React.ComponentType<FeedMaskProps>,
|
|
21
|
+
) {
|
|
22
|
+
if (_maskRegistry.has(name)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
_maskRegistry.set(name, component);
|
|
26
|
+
|
|
27
|
+
const moduleName = `ShortKitFeedMask_${name}`;
|
|
28
|
+
AppRegistry.registerComponent(moduleName, () => {
|
|
29
|
+
return function NamedFeedMaskSurface(props: RawFeedMaskSurfaceProps) {
|
|
30
|
+
return <ShortKitFeedMaskSurfaceInner {...props} maskName={name} />;
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Error boundary mirrors the overlay-surface boundary so a crash in
|
|
37
|
+
* the mask component doesn't bring down the SDK's modal presentation.
|
|
38
|
+
*/
|
|
39
|
+
class FeedMaskErrorBoundary extends React.Component<
|
|
40
|
+
{ maskName: string; children: React.ReactNode },
|
|
41
|
+
{ error: Error | null }
|
|
42
|
+
> {
|
|
43
|
+
state: { error: Error | null } = { error: null };
|
|
44
|
+
|
|
45
|
+
static getDerivedStateFromError(error: Error) {
|
|
46
|
+
return { error };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
|
50
|
+
console.error(
|
|
51
|
+
`${SK_MASK_TAG} CRASH in mask '${this.props.maskName}':`,
|
|
52
|
+
error.message,
|
|
53
|
+
'\nComponent stack:',
|
|
54
|
+
info.componentStack,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
render() {
|
|
59
|
+
if (this.state.error) {
|
|
60
|
+
if (__DEV__) {
|
|
61
|
+
return (
|
|
62
|
+
<View style={{ flex: 1, backgroundColor: 'rgba(255,0,0,0.3)', justifyContent: 'center', alignItems: 'center', padding: 20 }}>
|
|
63
|
+
<Text style={{ color: 'white', fontSize: 14, textAlign: 'center' }}>
|
|
64
|
+
{`Mask '${this.props.maskName}' crashed:\n${this.state.error.message}`}
|
|
65
|
+
</Text>
|
|
66
|
+
</View>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
// Production fallback: render an empty view so the SDK still
|
|
70
|
+
// has a usable feedRegion via the embed slot the host inserts
|
|
71
|
+
// (which they no longer do, since the mask crashed). The SDK's
|
|
72
|
+
// placeholder UIView keeps the feed visible.
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return this.props.children;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Raw props pushed in by the native bridge (`FeedMaskHostView`)
|
|
81
|
+
* via `SKFabricSurfaceWrapper.setProperties`. Item arrives serialized
|
|
82
|
+
* as a JSON string; everything else is a primitive.
|
|
83
|
+
*/
|
|
84
|
+
interface RawFeedMaskSurfaceProps {
|
|
85
|
+
/** Serialized `ContentItem`. JSON-stringified by the bridge. */
|
|
86
|
+
item?: string;
|
|
87
|
+
/** Active item's index in the feed, when known. */
|
|
88
|
+
activeIndex?: number;
|
|
89
|
+
/** Total feed length, when known. */
|
|
90
|
+
totalCount?: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface InnerProps extends RawFeedMaskSurfaceProps {
|
|
94
|
+
maskName: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function ShortKitFeedMaskSurfaceInner(props: InnerProps) {
|
|
98
|
+
const Component = _maskRegistry.get(props.maskName);
|
|
99
|
+
if (!Component) {
|
|
100
|
+
if (__DEV__) {
|
|
101
|
+
console.warn(
|
|
102
|
+
`${SK_MASK_TAG} mask '${props.maskName}' is not registered — did you set config.feedMask?`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let item: ContentItem | null = null;
|
|
109
|
+
if (props.item) {
|
|
110
|
+
try {
|
|
111
|
+
item = deserializeContentItem(props.item);
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.warn(`${SK_MASK_TAG} failed to deserialize item:`, e);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// No item to render around → render nothing yet. The SDK will push
|
|
118
|
+
// a configure() with the active item shortly after expansion.
|
|
119
|
+
if (!item) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<FeedMaskErrorBoundary maskName={props.maskName}>
|
|
125
|
+
<Component
|
|
126
|
+
item={item}
|
|
127
|
+
activeIndex={props.activeIndex}
|
|
128
|
+
totalCount={props.totalCount}
|
|
129
|
+
/>
|
|
130
|
+
</FeedMaskErrorBoundary>
|
|
131
|
+
);
|
|
132
|
+
}
|
package/src/ShortKitPlayer.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { serializePlayerConfig } from './serialization';
|
|
|
6
6
|
import { registerOverlayComponent } from './ShortKitOverlaySurface';
|
|
7
7
|
import { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurface';
|
|
8
8
|
import { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOverlaySurface';
|
|
9
|
+
import { registerFeedMaskComponent } from './ShortKitFeedMaskSurface';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Single-video player component. Displays one video with thumbnail fallback
|
|
@@ -19,7 +20,7 @@ import { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOv
|
|
|
19
20
|
* app's responsibility.
|
|
20
21
|
*/
|
|
21
22
|
export function ShortKitPlayer(props: ShortKitPlayerProps) {
|
|
22
|
-
const { config, contentItem, active, style } = props;
|
|
23
|
+
const { config, contentItem, active, feedItems, style } = props;
|
|
23
24
|
|
|
24
25
|
const clickAction = config?.clickAction ?? 'feed';
|
|
25
26
|
|
|
@@ -39,7 +40,10 @@ export function ShortKitPlayer(props: ShortKitPlayerProps) {
|
|
|
39
40
|
registerVideoCarouselOverlayComponent(fc.videoCarouselOverlay.name, fc.videoCarouselOverlay.component);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
+
if (config?.feedMask && config.feedMask !== 'none') {
|
|
44
|
+
registerFeedMaskComponent(config.feedMask.name, config.feedMask.component);
|
|
45
|
+
}
|
|
46
|
+
}, [config?.overlay, config?.feedConfig, config?.feedMask]);
|
|
43
47
|
|
|
44
48
|
const serializedConfig = useMemo(() => {
|
|
45
49
|
return serializePlayerConfig(config ?? {});
|
|
@@ -50,6 +54,14 @@ export function ShortKitPlayer(props: ShortKitPlayerProps) {
|
|
|
50
54
|
return JSON.stringify(contentItem);
|
|
51
55
|
}, [contentItem]);
|
|
52
56
|
|
|
57
|
+
// Serialize the optional expanded-feed seed list. Empty/undefined ⇒
|
|
58
|
+
// pass `undefined` so the native side falls through to the legacy
|
|
59
|
+
// single-item behavior (preload built from `self.items`).
|
|
60
|
+
const serializedFeedItems = useMemo(() => {
|
|
61
|
+
if (!feedItems || feedItems.length === 0) return undefined;
|
|
62
|
+
return JSON.stringify(feedItems);
|
|
63
|
+
}, [feedItems]);
|
|
64
|
+
|
|
53
65
|
// When clickAction is "none", the native view should not participate in
|
|
54
66
|
// Fabric's JS-side hit testing. Without this, Fabric on Android sees the
|
|
55
67
|
// native component as the touch target and never routes the touch to a
|
|
@@ -76,6 +88,7 @@ export function ShortKitPlayer(props: ShortKitPlayerProps) {
|
|
|
76
88
|
config={serializedConfig}
|
|
77
89
|
contentItem={serializedItem}
|
|
78
90
|
active={active}
|
|
91
|
+
feedItems={serializedFeedItems}
|
|
79
92
|
pointerEvents={nativeTouchPassthrough ? 'none' : 'auto'}
|
|
80
93
|
/>
|
|
81
94
|
</View>
|
package/src/ShortKitWidget.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { serializeWidgetConfig } from './serialization';
|
|
|
8
8
|
import { registerOverlayComponent } from './ShortKitOverlaySurface';
|
|
9
9
|
import { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurface';
|
|
10
10
|
import { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOverlaySurface';
|
|
11
|
+
import { registerFeedMaskComponent } from './ShortKitFeedMaskSurface';
|
|
11
12
|
|
|
12
13
|
// Local UUID generator. We don't pull in a runtime dep just for this; a
|
|
13
14
|
// timestamp + random suffix is more than unique enough to disambiguate
|
|
@@ -23,7 +24,7 @@ function generateWidgetId(): string {
|
|
|
23
24
|
* Must be rendered inside a `<ShortKitProvider>`.
|
|
24
25
|
*/
|
|
25
26
|
export function ShortKitWidget(props: ShortKitWidgetProps) {
|
|
26
|
-
const { config, items, onCardTap, style } = props;
|
|
27
|
+
const { config, items, onCardTap, active, feedItems, style } = props;
|
|
27
28
|
|
|
28
29
|
const isInitialized = useContext(ShortKitInitContext);
|
|
29
30
|
if (!isInitialized) {
|
|
@@ -69,7 +70,10 @@ export function ShortKitWidget(props: ShortKitWidgetProps) {
|
|
|
69
70
|
registerVideoCarouselOverlayComponent(fc.videoCarouselOverlay.name, fc.videoCarouselOverlay.component);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
if (config?.feedMask && config.feedMask !== 'none') {
|
|
74
|
+
registerFeedMaskComponent(config.feedMask.name, config.feedMask.component);
|
|
75
|
+
}
|
|
76
|
+
}, [config?.overlay, config?.feedConfig, config?.feedMask]);
|
|
73
77
|
|
|
74
78
|
// Subscribe to the global widget-card-tap event, filtered to this
|
|
75
79
|
// instance's widgetId.
|
|
@@ -93,6 +97,14 @@ export function ShortKitWidget(props: ShortKitWidgetProps) {
|
|
|
93
97
|
return JSON.stringify(items);
|
|
94
98
|
}, [items]);
|
|
95
99
|
|
|
100
|
+
// Serialize the optional expanded-feed seed list. Empty/undefined ⇒
|
|
101
|
+
// pass `undefined` so the native side falls through to the legacy
|
|
102
|
+
// behavior (preload built from `self.items` — the carousel cards).
|
|
103
|
+
const serializedFeedItems = useMemo(() => {
|
|
104
|
+
if (!feedItems || feedItems.length === 0) return undefined;
|
|
105
|
+
return JSON.stringify(feedItems);
|
|
106
|
+
}, [feedItems]);
|
|
107
|
+
|
|
96
108
|
return (
|
|
97
109
|
<View style={[styles.container, style]}>
|
|
98
110
|
<ShortKitWidgetView
|
|
@@ -100,6 +112,8 @@ export function ShortKitWidget(props: ShortKitWidgetProps) {
|
|
|
100
112
|
config={serializedConfig}
|
|
101
113
|
items={serializedItems}
|
|
102
114
|
widgetId={widgetId}
|
|
115
|
+
active={active}
|
|
116
|
+
feedItems={serializedFeedItems}
|
|
103
117
|
/>
|
|
104
118
|
</View>
|
|
105
119
|
);
|
package/src/index.ts
CHANGED
|
@@ -52,4 +52,11 @@ export { default as NativeShortKitModule } from './specs/NativeShortKitModule';
|
|
|
52
52
|
export { registerOverlayComponent } from './ShortKitOverlaySurface';
|
|
53
53
|
export { registerCarouselOverlayComponent } from './ShortKitCarouselOverlaySurface';
|
|
54
54
|
export { registerVideoCarouselOverlayComponent } from './ShortKitVideoCarouselOverlaySurface';
|
|
55
|
-
export
|
|
55
|
+
export { registerFeedMaskComponent } from './ShortKitFeedMaskSurface';
|
|
56
|
+
export type {
|
|
57
|
+
OverlayProps,
|
|
58
|
+
CarouselOverlayProps,
|
|
59
|
+
VideoCarouselOverlayProps,
|
|
60
|
+
FeedMaskProps,
|
|
61
|
+
FeedMaskConfig,
|
|
62
|
+
} from './types';
|
package/src/serialization.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
FeedConfig,
|
|
3
3
|
ContentItem,
|
|
4
4
|
FeedInput,
|
|
5
|
+
FeedMaskConfig,
|
|
5
6
|
PlayerState,
|
|
6
7
|
PlayerTime,
|
|
7
8
|
WidgetConfig,
|
|
@@ -139,6 +140,16 @@ function serializeOverlay(overlay: OverlayConfig | undefined): string | { type:
|
|
|
139
140
|
return { type: 'custom', name: overlay.name };
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
/**
|
|
144
|
+
* FeedMask serialization mirrors the overlay path: only the name
|
|
145
|
+
* crosses the bridge — the JS-side registry maps the name back to
|
|
146
|
+
* the React component at mount time inside `FeedMaskHostView`.
|
|
147
|
+
*/
|
|
148
|
+
function serializeFeedMask(mask: FeedMaskConfig | undefined): string | { type: 'custom'; name: string } {
|
|
149
|
+
if (!mask || mask === 'none') return 'none';
|
|
150
|
+
return { type: 'custom', name: mask.name };
|
|
151
|
+
}
|
|
152
|
+
|
|
142
153
|
export function serializeWidgetConfig(config: WidgetConfig): string {
|
|
143
154
|
const cfg = config ?? {};
|
|
144
155
|
return JSON.stringify({
|
|
@@ -153,6 +164,9 @@ export function serializeWidgetConfig(config: WidgetConfig): string {
|
|
|
153
164
|
overlay: serializeOverlay(cfg.overlay),
|
|
154
165
|
filter: cfg.filter ? JSON.stringify(cfg.filter) : undefined,
|
|
155
166
|
feedConfig: cfg.feedConfig ? serializeFeedConfig(cfg.feedConfig) : undefined,
|
|
167
|
+
feedMask: serializeFeedMask(cfg.feedMask),
|
|
168
|
+
playbackMode: cfg.playbackMode ?? 'singleVisibleRotating',
|
|
169
|
+
previewDuration: cfg.previewDuration ?? 5,
|
|
156
170
|
});
|
|
157
171
|
}
|
|
158
172
|
|
|
@@ -166,5 +180,6 @@ export function serializePlayerConfig(config: PlayerConfig): string {
|
|
|
166
180
|
muteOnStart: cfg.muteOnStart ?? true,
|
|
167
181
|
overlay: serializeOverlay(cfg.overlay),
|
|
168
182
|
feedConfig: cfg.feedConfig ? serializeFeedConfig(cfg.feedConfig) : undefined,
|
|
183
|
+
feedMask: serializeFeedMask(cfg.feedMask),
|
|
169
184
|
});
|
|
170
185
|
}
|
|
@@ -5,6 +5,13 @@ export interface NativeProps extends ViewProps {
|
|
|
5
5
|
config: string;
|
|
6
6
|
contentItem?: string;
|
|
7
7
|
active?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Serialized JSON of the host-provided expanded-feed seed list.
|
|
10
|
+
* See `ShortKitPlayerProps.feedItems` for semantics. Sent as a
|
|
11
|
+
* string to keep the prop primitive (Codegen-friendly) — the
|
|
12
|
+
* native bridge parses + hydrates into `[ContentItem]`.
|
|
13
|
+
*/
|
|
14
|
+
feedItems?: string;
|
|
8
15
|
}
|
|
9
16
|
|
|
10
17
|
export default codegenNativeComponent<NativeProps>(
|
|
@@ -10,6 +10,21 @@ export interface NativeProps extends ViewProps {
|
|
|
10
10
|
* `<ShortKitWidget>` when multiple widgets are mounted.
|
|
11
11
|
*/
|
|
12
12
|
widgetId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Whether the widget is currently allowed to play. Defaults to true on
|
|
15
|
+
* the native side. Hosts use this to release the widget's pool tile
|
|
16
|
+
* when it shouldn't be playing (inactive tab, scrolled off-screen,
|
|
17
|
+
* etc.) so other surfaces can claim the tile cleanly. Mirrors
|
|
18
|
+
* `<ShortKitFeed>`'s `active` prop.
|
|
19
|
+
*/
|
|
20
|
+
active?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Serialized JSON of the host-provided expanded-feed seed list.
|
|
23
|
+
* See `ShortKitWidgetProps.feedItems` for semantics. Sent as a
|
|
24
|
+
* string to keep the prop primitive (Codegen-friendly) — the
|
|
25
|
+
* native bridge parses + hydrates into `[FeedInput]`.
|
|
26
|
+
*/
|
|
27
|
+
feedItems?: string;
|
|
13
28
|
}
|
|
14
29
|
|
|
15
30
|
export default codegenNativeComponent<NativeProps>(
|
package/src/types.ts
CHANGED
|
@@ -52,6 +52,35 @@ export type VideoCarouselOverlayConfig =
|
|
|
52
52
|
| 'none'
|
|
53
53
|
| { type: 'custom'; name: string; component: React.ComponentType<VideoCarouselOverlayProps> };
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Custom feed mask config for `<ShortKitPlayer>` / `<ShortKitWidget>`.
|
|
57
|
+
*
|
|
58
|
+
* When set to `{type: 'custom', name, component}`, the SDK presents the
|
|
59
|
+
* expanded feed embedded inside the host-supplied React component rather
|
|
60
|
+
* than full-screen. The mask renders chrome (e.g. a footer pill) AROUND
|
|
61
|
+
* the feed; the feed itself occupies the `<ShortKitFeedRegion>` placeholder
|
|
62
|
+
* the host puts somewhere in the mask's tree.
|
|
63
|
+
*
|
|
64
|
+
* iOS only. On Android the prop is currently a no-op.
|
|
65
|
+
*/
|
|
66
|
+
export type FeedMaskConfig =
|
|
67
|
+
| 'none'
|
|
68
|
+
| { type: 'custom'; name: string; component: React.ComponentType<FeedMaskProps> };
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Props passed to the host-supplied feed mask component as the user pages
|
|
72
|
+
* through the expanded feed. Mirrors a subset of `OverlayProps` —
|
|
73
|
+
* additional fields (playback state, time, etc.) can be added later.
|
|
74
|
+
*/
|
|
75
|
+
export interface FeedMaskProps {
|
|
76
|
+
/** The currently-active feed item. Re-renders the mask when paging. */
|
|
77
|
+
item: ContentItem;
|
|
78
|
+
/** Active item's index in the feed, when known. */
|
|
79
|
+
activeIndex?: number;
|
|
80
|
+
/** Total feed length, when known (paginated feeds may grow). */
|
|
81
|
+
totalCount?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
55
84
|
// --- Data Models ---
|
|
56
85
|
|
|
57
86
|
export interface ContentItem {
|
|
@@ -438,6 +467,12 @@ export interface PlayerConfig {
|
|
|
438
467
|
muteOnStart?: boolean;
|
|
439
468
|
overlay?: OverlayConfig;
|
|
440
469
|
feedConfig?: FeedConfig;
|
|
470
|
+
/**
|
|
471
|
+
* Optional feed mask. When `{type: 'custom', ...}`, the SDK embeds the
|
|
472
|
+
* expanded feed inside the host's React component instead of presenting
|
|
473
|
+
* full-screen. iOS only. See `FeedMaskConfig` for full semantics.
|
|
474
|
+
*/
|
|
475
|
+
feedMask?: FeedMaskConfig;
|
|
441
476
|
}
|
|
442
477
|
|
|
443
478
|
// --- Widget Config ---
|
|
@@ -454,6 +489,24 @@ export interface WidgetConfig {
|
|
|
454
489
|
overlay?: OverlayConfig;
|
|
455
490
|
filter?: FeedFilter;
|
|
456
491
|
feedConfig?: FeedConfig;
|
|
492
|
+
/**
|
|
493
|
+
* Optional feed mask, same shape as `PlayerConfig.feedMask`. iOS only.
|
|
494
|
+
*/
|
|
495
|
+
feedMask?: FeedMaskConfig;
|
|
496
|
+
/**
|
|
497
|
+
* How the widget schedules preview playback across cells.
|
|
498
|
+
* - `'singleVisibleRotating'` (default): one visible card plays at a
|
|
499
|
+
* time using an HLS Instant Clip; advances to the next visible card
|
|
500
|
+
* on natural EOS, wrapping at the end. Off-screen cards never play.
|
|
501
|
+
* - `'allVisibleSimultaneous'`: every visible card plays simultaneously,
|
|
502
|
+
* hard-capped at 3. Each card loops its own preview in place.
|
|
503
|
+
*/
|
|
504
|
+
playbackMode?: 'singleVisibleRotating' | 'allVisibleSimultaneous';
|
|
505
|
+
/**
|
|
506
|
+
* Length of the looping preview window in seconds. Used to construct the
|
|
507
|
+
* Mux Instant Clip URL for each card. Defaults to 5.
|
|
508
|
+
*/
|
|
509
|
+
previewDuration?: number;
|
|
457
510
|
}
|
|
458
511
|
|
|
459
512
|
/**
|
|
@@ -473,6 +526,22 @@ export interface ShortKitPlayerProps {
|
|
|
473
526
|
* returns to thumbnail-only mode without tearing down the view. Defaults to
|
|
474
527
|
* the value of `config.autoplay` (which itself defaults to `true`). */
|
|
475
528
|
active?: boolean;
|
|
529
|
+
/**
|
|
530
|
+
* Optional list of items used to seed the expanded feed when the user
|
|
531
|
+
* taps the player and `config.clickAction === 'feed'`. The inline
|
|
532
|
+
* player's own `contentItem` is automatically prepended (deduped by
|
|
533
|
+
* playback ID), so the modal-zoom transition lands on the inline
|
|
534
|
+
* clip and the host's items follow.
|
|
535
|
+
*
|
|
536
|
+
* Combine with `config.feedConfig.feedSource`:
|
|
537
|
+
* - `'algorithmic'` (default) → expanded feed plays the seeded
|
|
538
|
+
* items, then continues paginating via `/v1/feed`.
|
|
539
|
+
* - `'custom'` → expanded feed plays only the seeded items and
|
|
540
|
+
* stops at the end (closed list).
|
|
541
|
+
*
|
|
542
|
+
* Has no effect when `clickAction !== 'feed'`.
|
|
543
|
+
*/
|
|
544
|
+
feedItems?: FeedInput[];
|
|
476
545
|
style?: ViewStyle;
|
|
477
546
|
onTap?: () => void;
|
|
478
547
|
}
|
|
@@ -499,6 +568,32 @@ export interface ShortKitWidgetProps {
|
|
|
499
568
|
* regardless (per-cell mute toggle runs alongside `onCardTap`).
|
|
500
569
|
*/
|
|
501
570
|
onCardTap?: (playbackId: string, index: number) => void;
|
|
571
|
+
/**
|
|
572
|
+
* Whether the widget is currently allowed to play. Defaults to `true`.
|
|
573
|
+
* When `false`, the widget stops its rotation timer and deactivates
|
|
574
|
+
* whichever card was active — releasing its tile back to the SDK
|
|
575
|
+
* player pool. Use this for viewport-aware playback orchestration
|
|
576
|
+
* (e.g. tab-focus gating: `active={isFocused}`). Mirrors
|
|
577
|
+
* `<ShortKitFeed>`'s same-named prop.
|
|
578
|
+
*/
|
|
579
|
+
active?: boolean;
|
|
580
|
+
/**
|
|
581
|
+
* Optional list of items used to seed the expanded feed when the user
|
|
582
|
+
* taps a card and `config.clickAction === 'feed'`. The widget's own
|
|
583
|
+
* `items` (the carousel cards) are automatically prepended (deduped
|
|
584
|
+
* by playback ID), so the modal-zoom transition lands on the tapped
|
|
585
|
+
* clip and the host's items follow.
|
|
586
|
+
*
|
|
587
|
+
* Combine with `config.feedConfig.feedSource`:
|
|
588
|
+
* - `'algorithmic'` (default) → expanded feed plays the seeded
|
|
589
|
+
* items, then continues paginating via `/v1/feed`.
|
|
590
|
+
* - `'custom'` → expanded feed plays only the seeded items and
|
|
591
|
+
* stops at the end (closed list).
|
|
592
|
+
*
|
|
593
|
+
* Has no effect when `clickAction !== 'feed'`. Mirrors the
|
|
594
|
+
* `feedItems` prop on `<ShortKitPlayer>`.
|
|
595
|
+
*/
|
|
596
|
+
feedItems?: FeedInput[];
|
|
502
597
|
style?: ViewStyle;
|
|
503
598
|
}
|
|
504
599
|
|