@shortkitsdk/react-native 0.2.6 → 0.2.11

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 +5 -1
  3. package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +319 -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 +559 -0
  6. package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +984 -0
  7. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +88 -220
  8. package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +12 -3
  9. package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +123 -741
  10. package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +2 -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 +458 -0
  15. package/ios/SKFabricSurfaceWrapper.h +18 -0
  16. package/ios/SKFabricSurfaceWrapper.mm +57 -0
  17. package/ios/ShortKitBridge.swift +186 -63
  18. package/ios/ShortKitFeedView.swift +62 -229
  19. package/ios/ShortKitFeedViewManager.mm +3 -2
  20. package/ios/ShortKitModule.mm +66 -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 +2380 -522
  25. package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +39 -12
  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 +39 -12
  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 +2380 -522
  31. package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +39 -12
  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 +39 -12
  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 -25
  57. package/src/ShortKitFeed.tsx +110 -41
  58. package/src/ShortKitLoadingSurface.tsx +24 -0
  59. package/src/ShortKitOverlaySurface.tsx +205 -0
  60. package/src/ShortKitPlayer.tsx +6 -7
  61. package/src/ShortKitProvider.tsx +27 -286
  62. package/src/index.ts +5 -3
  63. package/src/serialization.ts +19 -39
  64. package/src/specs/NativeShortKitModule.ts +58 -46
  65. package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
  66. package/src/types.ts +78 -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
@@ -55,7 +55,7 @@ class ShortKitPlayerNativeView(context: Context) : FrameLayout(context) {
55
55
  private fun rebuildIfNeeded() {
56
56
  if (playerView != null) return
57
57
 
58
- val sdk = ShortKitModule.shared?.sdk ?: return
58
+ val sdk = ShortKitBridge.shared?.sdk ?: return
59
59
  val playerConfig = parsePlayerConfig(configJson)
60
60
 
61
61
  val view = ShortKitPlayerView(context).apply {
@@ -107,7 +107,7 @@ class ShortKitPlayerNativeView(context: Context) : FrameLayout(context) {
107
107
  val overlay = obj.opt("overlay") ?: return VideoOverlayMode.None
108
108
  if (overlay is JSONObject && overlay.optString("type") == "custom") {
109
109
  return VideoOverlayMode.Custom {
110
- ShortKitOverlayBridge(context)
110
+ ReactOverlayHost(context)
111
111
  }
112
112
  }
113
113
  return VideoOverlayMode.None
@@ -48,7 +48,7 @@ class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
48
48
  private fun rebuildIfNeeded() {
49
49
  if (widgetView != null) return
50
50
 
51
- val sdk = ShortKitModule.shared?.sdk ?: return
51
+ val sdk = ShortKitBridge.shared?.sdk ?: return
52
52
  val widgetConfig = parseWidgetConfig(configJson)
53
53
 
54
54
  val view = ShortKitWidgetView(context).apply {
@@ -97,7 +97,7 @@ class ShortKitWidgetNativeView(context: Context) : FrameLayout(context) {
97
97
  val overlay = obj.opt("overlay") ?: return VideoOverlayMode.None
98
98
  if (overlay is JSONObject && overlay.optString("type") == "custom") {
99
99
  return VideoOverlayMode.Custom {
100
- ShortKitOverlayBridge(context)
100
+ ReactOverlayHost(context)
101
101
  }
102
102
  }
103
103
  return VideoOverlayMode.None
@@ -0,0 +1,177 @@
1
+ import UIKit
2
+ import ShortKitSDK
3
+
4
+ /// A UIView that conforms to `CarouselOverlay` and hosts an `RCTFabricSurface`
5
+ /// for rendering the developer's React carousel overlay component inside a feed cell.
6
+ ///
7
+ /// Unlike ReactOverlayHost, this does not subscribe to player state (carousels
8
+ /// are image-based). It emits image URLs to JS for Image.prefetch() and pushes
9
+ /// ImageCarouselItem data as appProperties.
10
+ @objc public class ReactCarouselOverlayHost: UIView, @unchecked Sendable, CarouselOverlay {
11
+
12
+ // MARK: - Configuration
13
+
14
+ var surfacePresenter: AnyObject?
15
+ weak var bridge: ShortKitBridge?
16
+
17
+ /// Module name for the RCTFabricSurface. Set by the overlay factory
18
+ /// based on the feed config's carousel overlay name.
19
+ var carouselOverlayModuleName: String = "ShortKitCarouselOverlay"
20
+
21
+ // MARK: - Eager Surface Creation
22
+
23
+ /// Eagerly create the RN surface so it's ready before the first configure().
24
+ /// Called by the overlay factory right after setting surfacePresenter and moduleName.
25
+ /// Mirrors ReactOverlayHost.attach(player:) which does the same for video overlays.
26
+ func prepareSurface() {
27
+ createSurfaceIfNeeded()
28
+ }
29
+
30
+ // MARK: - CarouselOverlay
31
+
32
+ public var cachedImage: ((String) -> UIImage?)?
33
+ public var wantsNativeImagePrefetch: Bool { true }
34
+
35
+ // MARK: - State
36
+
37
+ private var surface: SKFabricSurfaceWrapper?
38
+ private var surfaceView: UIView?
39
+ private var pendingProps: [String: Any]?
40
+
41
+ // MARK: - Init
42
+
43
+ override init(frame: CGRect) {
44
+ super.init(frame: frame)
45
+ backgroundColor = .clear
46
+ isUserInteractionEnabled = true
47
+ }
48
+
49
+ required init?(coder: NSCoder) {
50
+ fatalError("init(coder:) is not supported")
51
+ }
52
+
53
+ deinit {
54
+ surface?.stop()
55
+ }
56
+
57
+ // MARK: - CarouselOverlay
58
+
59
+ public func configure(with item: ImageCarouselItem) {
60
+ createSurfaceIfNeeded()
61
+
62
+ // Replace remote URLs with local file URLs for any natively-cached images.
63
+ // The SDK's PrefetchManager downloads images into NSCache<NSString, UIImage>
64
+ // which RN can't access. Writing to temp files lets RN load instantly from disk.
65
+ var modifiedItem = item
66
+ if let cachedImage {
67
+ var localImages: [CarouselImage] = []
68
+ for image in item.images {
69
+ if let uiImage = cachedImage(image.url),
70
+ let localURL = writeTempImage(uiImage, for: image.url) {
71
+ localImages.append(CarouselImage(url: localURL, alt: image.alt))
72
+ } else {
73
+ localImages.append(image)
74
+ }
75
+ }
76
+ modifiedItem = ImageCarouselItem(
77
+ id: item.id,
78
+ images: localImages,
79
+ caption: item.caption,
80
+ title: item.title,
81
+ description: item.description,
82
+ author: item.author,
83
+ section: item.section,
84
+ articleUrl: item.articleUrl
85
+ )
86
+ }
87
+
88
+ // Build props — push now if surface exists, otherwise store for later
89
+ if let data = try? JSONEncoder().encode(modifiedItem),
90
+ let json = String(data: data, encoding: .utf8) {
91
+ let props: [String: Any] = ["item": json]
92
+ if let surface {
93
+ surface.setProperties(props)
94
+ } else {
95
+ pendingProps = props
96
+ }
97
+ }
98
+ }
99
+
100
+ /// Write a UIImage to the temp directory, returning a file:// URL string.
101
+ /// Uses a hash of the remote URL as the filename to avoid duplicates.
102
+ private func writeTempImage(_ image: UIImage, for remoteURL: String) -> String? {
103
+ let hash = remoteURL.hash
104
+ let fileName = "sk-carousel-\(abs(hash)).jpg"
105
+ let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
106
+
107
+ // Skip if already written
108
+ if FileManager.default.fileExists(atPath: fileURL.path) {
109
+ return fileURL.absoluteString
110
+ }
111
+
112
+ guard let data = image.jpegData(compressionQuality: 0.9) else { return nil }
113
+ do {
114
+ try data.write(to: fileURL, options: .atomic)
115
+ return fileURL.absoluteString
116
+ } catch {
117
+ return nil
118
+ }
119
+ }
120
+
121
+ public func resetState() {
122
+ // Don't clear surface props — matches video overlay (ReactOverlayHost) pattern.
123
+ // configure() will overwrite with new props directly, letting React do an
124
+ // in-place re-render (old→new) instead of an unmount+remount (old→empty→new).
125
+ // The overlay's synchronous reset pattern handles state transitions.
126
+ }
127
+
128
+ // MARK: - Surface Creation
129
+
130
+ private func createSurfaceIfNeeded() {
131
+ guard surface == nil, let presenter = surfacePresenter else { return }
132
+
133
+ // Must always dispatch to main queue — see ReactOverlayHost for details.
134
+ DispatchQueue.main.async { [weak self] in
135
+ guard let self, self.surface == nil else { return }
136
+ self.installSurface(presenter: presenter)
137
+ }
138
+ }
139
+
140
+ private func installSurface(presenter: AnyObject) {
141
+ guard let surf = SKFabricSurfaceWrapper(
142
+ presenter: presenter,
143
+ moduleName: carouselOverlayModuleName,
144
+ initialProperties: [:]
145
+ ) else { return }
146
+ surf.start()
147
+
148
+ let view = surf.view
149
+ view.translatesAutoresizingMaskIntoConstraints = false
150
+ addSubview(view)
151
+ NSLayoutConstraint.activate([
152
+ view.topAnchor.constraint(equalTo: topAnchor),
153
+ view.leadingAnchor.constraint(equalTo: leadingAnchor),
154
+ view.trailingAnchor.constraint(equalTo: trailingAnchor),
155
+ view.bottomAnchor.constraint(equalTo: bottomAnchor),
156
+ ])
157
+ surfaceView = view
158
+ surface = surf
159
+
160
+ // Flush any props that arrived before surface was ready
161
+ if let pending = pendingProps {
162
+ surf.setProperties(pending)
163
+ pendingProps = nil
164
+ }
165
+ }
166
+
167
+ // MARK: - Layout
168
+
169
+ public override func layoutSubviews() {
170
+ super.layoutSubviews()
171
+ guard let surface else { return }
172
+ let size = bounds.size
173
+ guard size.width > 0, size.height > 0 else { return }
174
+ surface.setMinimumSize(size)
175
+ surface.setMaximumSize(size)
176
+ }
177
+ }
@@ -0,0 +1,38 @@
1
+ import UIKit
2
+
3
+ /// UIView that hosts a React Fabric surface for the custom loading component.
4
+ /// Used as the `loadingViewProvider` for the ShortKit SDK.
5
+ @objc public class ReactLoadingHost: UIView {
6
+
7
+ public var surfacePresenter: AnyObject?
8
+
9
+ private var surface: SKFabricSurfaceWrapper?
10
+
11
+ public override func didMoveToWindow() {
12
+ super.didMoveToWindow()
13
+ if window != nil && surface == nil {
14
+ guard let presenter = surfacePresenter ?? ShortKitBridge.shared?.surfacePresenter else { return }
15
+ let wrapper = SKFabricSurfaceWrapper(presenter: presenter, moduleName: "ShortKitLoading", initialProperties: [:])
16
+ wrapper?.start()
17
+ if let surfaceView = wrapper?.view {
18
+ surfaceView.frame = bounds
19
+ surfaceView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
20
+ addSubview(surfaceView)
21
+ }
22
+ surface = wrapper
23
+ }
24
+ }
25
+
26
+ public override func willMove(toWindow newWindow: UIWindow?) {
27
+ super.willMove(toWindow: newWindow)
28
+ if newWindow == nil {
29
+ surface?.stop()
30
+ surface = nil
31
+ }
32
+ }
33
+
34
+ public override func layoutSubviews() {
35
+ super.layoutSubviews()
36
+ surface?.view.frame = bounds
37
+ }
38
+ }