@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.
Files changed (75) hide show
  1. package/ShortKitReactNative.podspec +1 -0
  2. package/android/build.gradle.kts +17 -1
  3. package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +379 -0
  4. package/android/src/main/java/com/shortkit/reactnative/ReactLoadingHost.kt +40 -0
  5. package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +570 -0
  6. package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +1029 -0
  7. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +212 -219
  8. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +17 -3
  9. package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +157 -742
  10. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +11 -2
  11. package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +2 -2
  12. package/ios/ReactCarouselOverlayHost.swift +177 -0
  13. package/ios/ReactLoadingHost.swift +38 -0
  14. package/ios/ReactOverlayHost.swift +444 -0
  15. package/ios/SKFabricSurfaceWrapper.h +18 -0
  16. package/ios/SKFabricSurfaceWrapper.mm +57 -0
  17. package/ios/ShortKitBridge.swift +220 -63
  18. package/ios/ShortKitFeedView.swift +82 -228
  19. package/ios/ShortKitFeedViewManager.mm +3 -2
  20. package/ios/ShortKitModule.mm +69 -37
  21. package/ios/ShortKitPlayerNativeView.swift +39 -8
  22. package/ios/ShortKitReactNative-Bridging-Header.h +2 -0
  23. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +1 -1
  24. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +3683 -1249
  25. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +56 -15
  26. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  27. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +56 -15
  28. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  29. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +1 -1
  30. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +3683 -1249
  31. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +56 -15
  32. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  33. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +56 -15
  34. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  35. package/ios/ShortKitSDK.xcframework.bak/Info.plist +43 -0
  36. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
  37. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Info.plist +16 -0
  38. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +28917 -0
  39. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +824 -0
  40. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  41. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +824 -0
  42. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +4 -0
  43. package/ios/ShortKitSDK.xcframework.bak/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
  44. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
  45. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +16 -0
  46. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +28917 -0
  47. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +824 -0
  48. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  49. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +824 -0
  50. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +4 -0
  51. package/ios/ShortKitSDK.xcframework.bak/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
  52. package/ios/ShortKitWidgetNativeView.swift +3 -3
  53. package/package.json +1 -1
  54. package/src/ShortKitCarouselOverlaySurface.tsx +55 -0
  55. package/src/ShortKitCommands.ts +31 -0
  56. package/src/ShortKitContext.ts +6 -24
  57. package/src/ShortKitFeed.tsx +124 -41
  58. package/src/ShortKitLoadingSurface.tsx +24 -0
  59. package/src/ShortKitOverlaySurface.tsx +313 -0
  60. package/src/ShortKitPlayer.tsx +30 -9
  61. package/src/ShortKitProvider.tsx +28 -285
  62. package/src/index.ts +9 -3
  63. package/src/serialization.ts +20 -39
  64. package/src/specs/NativeShortKitModule.ts +74 -45
  65. package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
  66. package/src/types.ts +84 -16
  67. package/src/useShortKit.ts +1 -3
  68. package/src/useShortKitPlayer.ts +7 -7
  69. package/android/src/main/java/com/shortkit/reactnative/ShortKitCarouselOverlayBridge.kt +0 -48
  70. package/android/src/main/java/com/shortkit/reactnative/ShortKitOverlayBridge.kt +0 -128
  71. package/ios/ShortKitCarouselOverlayBridge.swift +0 -219
  72. package/ios/ShortKitOverlayBridge.swift +0 -111
  73. package/src/CarouselOverlayManager.tsx +0 -70
  74. package/src/OverlayManager.tsx +0 -87
  75. package/src/useShortKitCarousel.ts +0 -29
@@ -1,219 +0,0 @@
1
- import UIKit
2
- import ShortKitSDK
3
-
4
- /// A UIView that conforms to `CarouselOverlay` and renders carousel images
5
- /// natively inside the feed cell using a horizontal paging UIScrollView.
6
- ///
7
- /// This matches the Swift SDK's `DefaultCarouselView` architecture: images
8
- /// are rendered INSIDE the cell (part of the feed scroll view), so vertical
9
- /// feed scrolling and horizontal image swiping work naturally through UIKit's
10
- /// gesture conflict resolution.
11
- ///
12
- /// The RN side only renders metadata (title, description, page dots, buttons)
13
- /// on top via the `CarouselOverlayManager`.
14
- @objc public class ShortKitCarouselOverlayBridge: UIView, @unchecked Sendable, CarouselOverlay {
15
-
16
- // MARK: - Bridge Reference
17
-
18
- weak var bridge: ShortKitBridge?
19
-
20
- // MARK: - CarouselOverlay
21
-
22
- public var cachedImage: ((String) -> UIImage?)?
23
-
24
- // MARK: - State
25
-
26
- private var currentItem: ImageCarouselItem?
27
- private var loadTasks: [Task<Void, Never>] = []
28
- private var currentPage: Int = 0
29
- private var autoScrollTimer: Timer?
30
-
31
- // MARK: - UI
32
-
33
- private let scrollView = UIScrollView()
34
- private let pageControl = UIPageControl()
35
- private var imageViews: [UIImageView] = []
36
-
37
- // MARK: - Init
38
-
39
- override init(frame: CGRect) {
40
- super.init(frame: frame)
41
- backgroundColor = .black
42
- isUserInteractionEnabled = true
43
- setupScrollView()
44
- setupPageControl()
45
- }
46
-
47
- required init?(coder: NSCoder) {
48
- fatalError("init(coder:) is not supported")
49
- }
50
-
51
- private func setupScrollView() {
52
- scrollView.isPagingEnabled = true
53
- scrollView.showsHorizontalScrollIndicator = false
54
- scrollView.bounces = false
55
- scrollView.delegate = self
56
- scrollView.translatesAutoresizingMaskIntoConstraints = false
57
- addSubview(scrollView)
58
- NSLayoutConstraint.activate([
59
- scrollView.topAnchor.constraint(equalTo: topAnchor),
60
- scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
61
- scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
62
- scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
63
- ])
64
- }
65
-
66
- private func setupPageControl() {
67
- pageControl.isUserInteractionEnabled = false
68
- pageControl.translatesAutoresizingMaskIntoConstraints = false
69
- addSubview(pageControl)
70
- NSLayoutConstraint.activate([
71
- pageControl.centerXAnchor.constraint(equalTo: centerXAnchor),
72
- pageControl.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -40),
73
- ])
74
- }
75
-
76
- // MARK: - Layout
77
-
78
- public override func layoutSubviews() {
79
- super.layoutSubviews()
80
- let w = bounds.width
81
- let h = bounds.height
82
- guard w > 0, h > 0 else { return }
83
- scrollView.contentSize = CGSize(width: w * CGFloat(imageViews.count), height: h)
84
- for (i, iv) in imageViews.enumerated() {
85
- iv.frame = CGRect(x: w * CGFloat(i), y: 0, width: w, height: h)
86
- }
87
- }
88
-
89
- // MARK: - CarouselOverlay
90
-
91
- public func configure(with item: ImageCarouselItem) {
92
- NSLog("[CarouselBridge] configure id=\(item.id) images=\(item.images.count)")
93
- currentItem = item
94
-
95
- // Cancel previous loads & auto-scroll
96
- cancelLoads()
97
- autoScrollTimer?.invalidate()
98
- autoScrollTimer = nil
99
-
100
- // Remove old image views
101
- for iv in imageViews { iv.removeFromSuperview() }
102
- imageViews.removeAll()
103
-
104
- // Create image views and load images
105
- for image in item.images {
106
- let iv = UIImageView()
107
- iv.contentMode = .scaleAspectFill
108
- iv.clipsToBounds = true
109
- iv.backgroundColor = .black
110
- scrollView.addSubview(iv)
111
- imageViews.append(iv)
112
-
113
- if let cached = cachedImage?(image.url) {
114
- iv.image = cached
115
- } else {
116
- let task = Task { [weak iv] in
117
- guard let url = URL(string: image.url),
118
- let (data, _) = try? await URLSession.shared.data(from: url),
119
- !Task.isCancelled,
120
- let uiImage = UIImage(data: data) else { return }
121
- await MainActor.run { iv?.image = uiImage }
122
- }
123
- loadTasks.append(task)
124
- }
125
- }
126
-
127
- // Reset scroll position and page
128
- currentPage = 0
129
- scrollView.contentOffset = .zero
130
- setNeedsLayout()
131
-
132
- // Update page control
133
- pageControl.numberOfPages = item.images.count
134
- pageControl.currentPage = 0
135
- pageControl.isHidden = item.images.count <= 1
136
-
137
- // Start auto-scroll if configured
138
- let interval = item.autoScrollInterval ?? 0
139
- if interval > 0 && item.images.count > 1 {
140
- startAutoScroll(interval: interval)
141
- }
142
-
143
- // Emit configure to JS so the metadata overlay updates
144
- bridge?.emitCarouselOverlayEvent("onCarouselOverlayConfigure", item: item)
145
- }
146
-
147
- public func resetState() {
148
- NSLog("[CarouselBridge] resetState")
149
- cancelLoads()
150
- autoScrollTimer?.invalidate()
151
- autoScrollTimer = nil
152
- for iv in imageViews { iv.removeFromSuperview() }
153
- imageViews.removeAll()
154
- scrollView.contentOffset = .zero
155
- currentPage = 0
156
- currentItem = nil
157
- bridge?.emitCarouselOverlayEvent("onCarouselOverlayReset", body: [:])
158
- }
159
-
160
- public func fadeOutForTransition() {
161
- // No-op — native images are inside the cell, transitions are handled
162
- // by the feed's collection view.
163
- }
164
-
165
- public func restoreFromTransition() {
166
- // No-op
167
- }
168
-
169
- // MARK: - Auto-Scroll
170
-
171
- private func startAutoScroll(interval: Double) {
172
- autoScrollTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
173
- guard let self, !self.imageViews.isEmpty else { return }
174
- let nextPage = (self.currentPage + 1) % self.imageViews.count
175
- let offset = CGFloat(nextPage) * self.scrollView.bounds.width
176
- self.scrollView.setContentOffset(CGPoint(x: offset, y: 0), animated: true)
177
- self.currentPage = nextPage
178
- self.pageControl.currentPage = nextPage
179
- }
180
- }
181
-
182
- // MARK: - Helpers
183
-
184
- private func cancelLoads() {
185
- for task in loadTasks { task.cancel() }
186
- loadTasks.removeAll()
187
- }
188
-
189
- }
190
-
191
- // MARK: - UIScrollViewDelegate
192
-
193
- extension ShortKitCarouselOverlayBridge: UIScrollViewDelegate {
194
- public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
195
- guard scrollView.bounds.width > 0 else { return }
196
- let page = Int(round(scrollView.contentOffset.x / scrollView.bounds.width))
197
- currentPage = page
198
- pageControl.currentPage = page
199
-
200
- // Restart auto-scroll after user interaction
201
- if let interval = currentItem?.autoScrollInterval, interval > 0, imageViews.count > 1 {
202
- autoScrollTimer?.invalidate()
203
- startAutoScroll(interval: interval)
204
- }
205
- }
206
-
207
- public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
208
- guard scrollView.bounds.width > 0 else { return }
209
- let page = Int(round(scrollView.contentOffset.x / scrollView.bounds.width))
210
- currentPage = page
211
- pageControl.currentPage = page
212
- }
213
-
214
- public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
215
- // Pause auto-scroll during user interaction
216
- autoScrollTimer?.invalidate()
217
- autoScrollTimer = nil
218
- }
219
- }
@@ -1,111 +0,0 @@
1
- import UIKit
2
- import ShortKitSDK
3
-
4
- /// A transparent UIView that conforms to `FeedOverlay` and bridges overlay
5
- /// lifecycle calls to JS events via `ShortKitBridge`.
6
- ///
7
- /// The actual overlay UI is rendered by React Native on the JS side through
8
- /// the `OverlayManager` component. This view simply relays the SDK lifecycle
9
- /// events so the JS overlay knows when to configure, activate, reset, etc.
10
- @objc public class ShortKitOverlayBridge: UIView, @unchecked Sendable, FeedOverlay {
11
-
12
- // MARK: - Bridge Reference
13
-
14
- /// Weak reference to the bridge, set by the factory closure in `parseFeedConfig`.
15
- weak var bridge: ShortKitBridge?
16
-
17
- // MARK: - State
18
-
19
- /// Stores the last configured ContentItem so we can pass it with lifecycle
20
- /// events that don't receive the item as a parameter.
21
- private var currentItem: ContentItem?
22
-
23
- /// Deferred configure emission — cancelled if `activatePlayback()` fires
24
- /// on the same run-loop iteration (meaning this was a handleSwipe
25
- /// re-configure of the active cell, not a prefetch for the next cell).
26
- private var pendingConfigureWorkItem: DispatchWorkItem?
27
-
28
- // MARK: - Init
29
-
30
- override init(frame: CGRect) {
31
- super.init(frame: frame)
32
- backgroundColor = .clear
33
- isUserInteractionEnabled = true
34
- setupGestures()
35
- }
36
-
37
- required init?(coder: NSCoder) {
38
- fatalError("init(coder:) is not supported")
39
- }
40
-
41
- // MARK: - Gestures
42
-
43
- private func setupGestures() {
44
- let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:)))
45
- doubleTap.numberOfTapsRequired = 2
46
- addGestureRecognizer(doubleTap)
47
-
48
- let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap))
49
- singleTap.require(toFail: doubleTap)
50
- addGestureRecognizer(singleTap)
51
- }
52
-
53
- @objc private func handleSingleTap() {
54
- bridge?.emitOverlayEvent("onOverlayTap", body: [:])
55
- }
56
-
57
- @objc private func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
58
- let location = gesture.location(in: self)
59
- bridge?.emitOverlayEvent("onOverlayDoubleTap", body: [
60
- "x": location.x,
61
- "y": location.y,
62
- ])
63
- }
64
-
65
- // MARK: - FeedOverlay
66
-
67
- public func attach(player: ShortKitPlayer) {
68
- // No-op on the native side. The JS overlay subscribes to player
69
- // state via the TurboModule's Combine publishers.
70
- }
71
-
72
- public func configure(with item: ContentItem) {
73
- currentItem = item
74
-
75
- // Defer the configure event by one run-loop tick. If activatePlayback()
76
- // fires before then (handleSwipe sequence: configure → reset → activate),
77
- // the event is cancelled — preventing nextItem from being overwritten
78
- // with the current cell's data.
79
- pendingConfigureWorkItem?.cancel()
80
- let workItem = DispatchWorkItem { [weak self] in
81
- guard let self, let item = self.currentItem else { return }
82
- self.bridge?.emitOverlayEvent("onOverlayConfigure", item: item)
83
- self.pendingConfigureWorkItem = nil
84
- }
85
- pendingConfigureWorkItem = workItem
86
- DispatchQueue.main.async(execute: workItem)
87
- }
88
-
89
- public func resetPlaybackProgress() {
90
- guard let item = currentItem else { return }
91
- bridge?.emitOverlayEvent("onOverlayReset", item: item)
92
- }
93
-
94
- public func activatePlayback() {
95
- // Cancel the pending configure — it was for the current cell (part of
96
- // handleSwipe), not a prefetch for the next cell.
97
- pendingConfigureWorkItem?.cancel()
98
- pendingConfigureWorkItem = nil
99
-
100
- guard let item = currentItem else { return }
101
- bridge?.emitOverlayEvent("onOverlayActivate", item: item)
102
- }
103
-
104
- public func fadeOutForTransition() {
105
- // No-op: JS overlays handle fading via feedScrollPhase
106
- }
107
-
108
- public func restoreFromTransition() {
109
- // No-op: JS overlays handle fading via feedScrollPhase
110
- }
111
- }
@@ -1,70 +0,0 @@
1
- import React, { useContext, useMemo } from 'react';
2
- import { View, StyleSheet } from 'react-native';
3
- import type { CarouselOverlayConfig } from './types';
4
- import { ShortKitContext } from './ShortKitContext';
5
- import type { ShortKitContextValue } from './ShortKitContext';
6
-
7
- interface CarouselOverlayManagerProps {
8
- carouselOverlay: CarouselOverlayConfig;
9
- }
10
-
11
- /**
12
- * Internal component that renders TWO instances of the developer's custom
13
- * carousel overlay component — one for the current cell and one for the next.
14
- *
15
- * Works identically to `OverlayManager` but for image carousel cells.
16
- * The native side finds these views by their `nativeID` and applies
17
- * scroll-tracking transforms so each overlay moves with its respective
18
- * carousel cell during swipe transitions.
19
- */
20
- export function CarouselOverlayManager({ carouselOverlay }: CarouselOverlayManagerProps) {
21
- if (
22
- carouselOverlay === 'none' ||
23
- typeof carouselOverlay === 'string' ||
24
- carouselOverlay.type !== 'custom' ||
25
- !('component' in carouselOverlay)
26
- ) {
27
- return null;
28
- }
29
-
30
- const CarouselComponent = carouselOverlay.component;
31
-
32
- return (
33
- <>
34
- <View style={StyleSheet.absoluteFill} nativeID="carousel-overlay-current" pointerEvents="box-none">
35
- <CarouselComponent />
36
- </View>
37
- <View style={StyleSheet.absoluteFill} nativeID="carousel-overlay-next" pointerEvents="box-none">
38
- <NextCarouselOverlayProvider>
39
- <CarouselComponent />
40
- </NextCarouselOverlayProvider>
41
- </View>
42
- </>
43
- );
44
- }
45
-
46
- /**
47
- * Wraps children with a modified ShortKitContext where `currentCarouselItem`
48
- * is set to the provider's `nextCarouselItem`.
49
- */
50
- function NextCarouselOverlayProvider({ children }: { children: React.ReactNode }) {
51
- const context = useContext(ShortKitContext);
52
-
53
- const nextValue: ShortKitContextValue | null = useMemo(() => {
54
- if (!context) return null;
55
-
56
- return {
57
- ...context,
58
- currentCarouselItem: context.nextCarouselItem,
59
- isCarouselActive: context.nextCarouselItem != null,
60
- };
61
- }, [context]);
62
-
63
- if (!nextValue) return <>{children}</>;
64
-
65
- return (
66
- <ShortKitContext.Provider value={nextValue}>
67
- {children}
68
- </ShortKitContext.Provider>
69
- );
70
- }
@@ -1,87 +0,0 @@
1
- import React, { useContext, useMemo } from 'react';
2
- import { View, StyleSheet } from 'react-native';
3
- import type { OverlayConfig, PlayerState } from './types';
4
- import { ShortKitContext } from './ShortKitContext';
5
- import type { ShortKitContextValue } from './ShortKitContext';
6
-
7
- interface OverlayManagerProps {
8
- overlay: OverlayConfig;
9
- }
10
-
11
- /**
12
- * Internal component that renders TWO instances of the developer's custom
13
- * overlay component — one for the current cell and one for the next cell.
14
- *
15
- * On the native side, `ShortKitFeedView` finds these views by their
16
- * `nativeID` and applies scroll-tracking transforms so each overlay moves
17
- * with its respective cell during swipe transitions — matching the native
18
- * iOS/Android SDK behavior where each cell has its own overlay instance.
19
- *
20
- * The "current" overlay uses the actual provider context.
21
- * The "next" overlay uses a context override where `currentItem = nextItem`
22
- * with initial playback state, so it renders the upcoming video's overlay
23
- * content before the user scrolls to it.
24
- */
25
- export function OverlayManager({ overlay }: OverlayManagerProps) {
26
- if (
27
- overlay === 'none' ||
28
- typeof overlay === 'string' ||
29
- overlay.type !== 'custom' ||
30
- !('component' in overlay)
31
- ) {
32
- return null;
33
- }
34
-
35
- const OverlayComponent = overlay.component;
36
-
37
- return (
38
- <>
39
- <View style={StyleSheet.absoluteFill} nativeID="overlay-current" pointerEvents="box-none">
40
- <OverlayComponent />
41
- </View>
42
- <View style={StyleSheet.absoluteFill} nativeID="overlay-next" pointerEvents="box-none">
43
- <NextOverlayProvider>
44
- <OverlayComponent />
45
- </NextOverlayProvider>
46
- </View>
47
- </>
48
- );
49
- }
50
-
51
- /**
52
- * Wraps children with a modified ShortKitContext where `currentItem` is
53
- * set to the provider's `nextItem` (the item configured for the upcoming
54
- * cell). Other state reflects an "about to play" initial state so the
55
- * overlay renders correctly before playback starts on that cell.
56
- */
57
- function NextOverlayProvider({ children }: { children: React.ReactNode }) {
58
- const context = useContext(ShortKitContext);
59
-
60
- const nextValue: ShortKitContextValue | null = useMemo(() => {
61
- if (!context) return null;
62
-
63
- return {
64
- ...context,
65
- currentItem: context.nextItem,
66
- isActive: context.nextItem != null,
67
- time: {
68
- current: 0,
69
- duration: context.nextItem?.duration ?? 0,
70
- buffered: 0,
71
- },
72
- playerState: 'idle' as PlayerState,
73
- feedScrollPhase: null,
74
- activeCue: null,
75
- lastOverlayTap: 0,
76
- lastOverlayDoubleTap: null,
77
- };
78
- }, [context]);
79
-
80
- if (!nextValue) return <>{children}</>;
81
-
82
- return (
83
- <ShortKitContext.Provider value={nextValue}>
84
- {children}
85
- </ShortKitContext.Provider>
86
- );
87
- }
@@ -1,29 +0,0 @@
1
- import { useContext } from 'react';
2
- import { ShortKitContext } from './ShortKitContext';
3
- import type { ImageCarouselItem } from './types';
4
-
5
- export interface ShortKitCarouselState {
6
- currentCarouselItem: ImageCarouselItem | null;
7
- isCarouselActive: boolean;
8
- currentCarouselPage: number;
9
- }
10
-
11
- /**
12
- * Hook to access carousel overlay state from the nearest ShortKitProvider.
13
- *
14
- * Use this inside a custom carousel overlay component to get the current
15
- * `ImageCarouselItem` data (images, title, description, etc.).
16
- *
17
- * Must be used within a `<ShortKitProvider>`.
18
- */
19
- export function useShortKitCarousel(): ShortKitCarouselState {
20
- const context = useContext(ShortKitContext);
21
- if (!context) {
22
- throw new Error('useShortKitCarousel must be used within a ShortKitProvider');
23
- }
24
- return {
25
- currentCarouselItem: context.currentCarouselItem,
26
- isCarouselActive: context.isCarouselActive,
27
- currentCarouselPage: context.currentCarouselPage,
28
- };
29
- }