@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.
- package/ShortKitReactNative.podspec +1 -0
- package/android/build.gradle.kts +5 -1
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +319 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactLoadingHost.kt +40 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +559 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +984 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +88 -220
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +12 -3
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +123 -741
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +2 -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 +458 -0
- package/ios/SKFabricSurfaceWrapper.h +18 -0
- package/ios/SKFabricSurfaceWrapper.mm +57 -0
- package/ios/ShortKitBridge.swift +186 -63
- package/ios/ShortKitFeedView.swift +62 -229
- package/ios/ShortKitFeedViewManager.mm +3 -2
- package/ios/ShortKitModule.mm +66 -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 +2380 -522
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +39 -12
- 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 +39 -12
- 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 +2380 -522
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +39 -12
- 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 +39 -12
- 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 -25
- package/src/ShortKitFeed.tsx +110 -41
- package/src/ShortKitLoadingSurface.tsx +24 -0
- package/src/ShortKitOverlaySurface.tsx +205 -0
- package/src/ShortKitPlayer.tsx +6 -7
- package/src/ShortKitProvider.tsx +27 -286
- package/src/index.ts +5 -3
- package/src/serialization.ts +19 -39
- package/src/specs/NativeShortKitModule.ts +58 -46
- package/src/specs/ShortKitFeedViewNativeComponent.ts +3 -2
- package/src/types.ts +78 -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
|
@@ -10,16 +10,14 @@ import android.view.ViewGroup
|
|
|
10
10
|
import android.widget.FrameLayout
|
|
11
11
|
import androidx.fragment.app.FragmentActivity
|
|
12
12
|
import androidx.viewpager2.widget.ViewPager2
|
|
13
|
-
import com.
|
|
14
|
-
import com.shortkit.ShortKitFeedFragment
|
|
13
|
+
import com.shortkit.sdk.feed.ShortKitFeedFragment
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Fabric native view that embeds [ShortKitFeedFragment] using Android
|
|
18
17
|
* Fragment transactions. Props are set by [ShortKitFeedViewManager].
|
|
19
18
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* so they move with the active cell during swipe transitions.
|
|
19
|
+
* The native SDK owns overlay lifecycle via [ReactOverlayHost] and
|
|
20
|
+
* [ReactCarouselOverlayHost] — this view is purely a fragment container.
|
|
23
21
|
*
|
|
24
22
|
* Android equivalent of iOS `ShortKitFeedView.swift`.
|
|
25
23
|
*/
|
|
@@ -30,44 +28,51 @@ class ShortKitFeedView(context: Context) : FrameLayout(context) {
|
|
|
30
28
|
// -----------------------------------------------------------------------
|
|
31
29
|
|
|
32
30
|
var config: String? = null
|
|
33
|
-
var
|
|
31
|
+
var feedId: String? = null
|
|
32
|
+
var startAtItemId: String? = null
|
|
33
|
+
var preloadId: String? = null
|
|
34
34
|
|
|
35
35
|
// -----------------------------------------------------------------------
|
|
36
36
|
// Fragment management
|
|
37
37
|
// -----------------------------------------------------------------------
|
|
38
38
|
|
|
39
39
|
private var feedFragment: ShortKitFeedFragment? = null
|
|
40
|
+
private val fragmentContainer: FrameLayout
|
|
40
41
|
private var fragmentContainerId: Int = View.generateViewId()
|
|
41
42
|
private val handler = Handler(Looper.getMainLooper())
|
|
42
43
|
|
|
43
44
|
init {
|
|
44
|
-
|
|
45
|
+
// Use a child FrameLayout as the fragment container. Fabric overrides
|
|
46
|
+
// this view's own id, so we can't use it as the fragment container.
|
|
47
|
+
fragmentContainer = FrameLayout(context).apply {
|
|
48
|
+
id = fragmentContainerId
|
|
49
|
+
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
50
|
+
}
|
|
51
|
+
addView(fragmentContainer)
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
// -----------------------------------------------------------------------
|
|
48
|
-
//
|
|
55
|
+
// Fabric layout workarounds
|
|
49
56
|
// -----------------------------------------------------------------------
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/** Carousel overlay for the currently active cell (nativeID="carousel-overlay-current"). */
|
|
61
|
-
private var currentCarouselOverlayView: View? = null
|
|
62
|
-
|
|
63
|
-
/** Carousel overlay for the upcoming cell (nativeID="carousel-overlay-next"). */
|
|
64
|
-
private var nextCarouselOverlayView: View? = null
|
|
58
|
+
/**
|
|
59
|
+
* Fabric intercepts requestLayout() and doesn't propagate measure/layout
|
|
60
|
+
* to programmatically-added children. Override to schedule a manual pass.
|
|
61
|
+
*/
|
|
62
|
+
override fun requestLayout() {
|
|
63
|
+
super.requestLayout()
|
|
64
|
+
@Suppress("UNNECESSARY_SAFE_CALL")
|
|
65
|
+
handler?.post(layoutRunnable)
|
|
66
|
+
}
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
private val layoutRunnable = Runnable {
|
|
69
|
+
measure(
|
|
70
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
71
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
72
|
+
)
|
|
73
|
+
layout(left, top, right, bottom)
|
|
74
|
+
}
|
|
68
75
|
|
|
69
|
-
/** Deferred page update to avoid flashing stale metadata. */
|
|
70
|
-
private var pageUpdateRunnable: Runnable? = null
|
|
71
76
|
|
|
72
77
|
// -----------------------------------------------------------------------
|
|
73
78
|
// Lifecycle
|
|
@@ -79,7 +84,9 @@ class ShortKitFeedView(context: Context) : FrameLayout(context) {
|
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
override fun onDetachedFromWindow() {
|
|
82
|
-
|
|
87
|
+
synchronized(ShortKitBridge.staticPendingFeedViews) {
|
|
88
|
+
ShortKitBridge.staticPendingFeedViews.remove(this)
|
|
89
|
+
}
|
|
83
90
|
removeFeedFragment()
|
|
84
91
|
super.onDetachedFromWindow()
|
|
85
92
|
}
|
|
@@ -88,36 +95,78 @@ class ShortKitFeedView(context: Context) : FrameLayout(context) {
|
|
|
88
95
|
// Fragment containment
|
|
89
96
|
// -----------------------------------------------------------------------
|
|
90
97
|
|
|
91
|
-
|
|
98
|
+
internal fun embedFeedFragmentIfNeeded() {
|
|
92
99
|
if (feedFragment != null) return
|
|
93
100
|
|
|
94
|
-
val sdk =
|
|
95
|
-
|
|
101
|
+
val sdk = ShortKitBridge.shared?.sdk ?: run {
|
|
102
|
+
synchronized(ShortKitBridge.staticPendingFeedViews) {
|
|
103
|
+
ShortKitBridge.staticPendingFeedViews.add(this)
|
|
104
|
+
}
|
|
96
105
|
return
|
|
97
106
|
}
|
|
98
107
|
|
|
99
|
-
val activity = getReactActivity() ?:
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
val activity = getReactActivity() ?: return
|
|
109
|
+
|
|
110
|
+
val feedConfig = ShortKitBridge.parseFeedConfig(config ?: "{}", context)
|
|
111
|
+
|
|
112
|
+
preloadId?.let { id ->
|
|
113
|
+
ShortKitBridge.shared?.consumePreload(id)
|
|
102
114
|
}
|
|
103
115
|
|
|
104
|
-
val fragment = ShortKitFeedFragment.newInstance(sdk)
|
|
116
|
+
val fragment = ShortKitFeedFragment.newInstance(sdk, feedConfig)
|
|
105
117
|
|
|
106
118
|
try {
|
|
107
119
|
activity.supportFragmentManager
|
|
108
120
|
.beginTransaction()
|
|
109
121
|
.replace(fragmentContainerId, fragment)
|
|
110
122
|
.commitNowAllowingStateLoss()
|
|
111
|
-
this.feedFragment = fragment
|
|
112
|
-
|
|
123
|
+
this.feedFragment = fragment
|
|
124
|
+
|
|
125
|
+
// Force an immediate layout pass on the container so the fragment's
|
|
126
|
+
// ViewPager2/RecyclerView gets measured and can create cells.
|
|
127
|
+
fragmentContainer.measure(
|
|
128
|
+
MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY),
|
|
129
|
+
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)
|
|
130
|
+
)
|
|
131
|
+
fragmentContainer.layout(0, 0, measuredWidth, measuredHeight)
|
|
132
|
+
|
|
133
|
+
// The SDK's deferCellSetup retries only run during the current message
|
|
134
|
+
// loop iteration. Since Fabric suppresses layout, ViewPager2 won't
|
|
135
|
+
// have cells until our Choreographer callback forces layout on the
|
|
136
|
+
// next frame. Schedule a delayed nudge that runs after layout settles.
|
|
137
|
+
handler.postDelayed({
|
|
138
|
+
val fragView = feedFragment?.view
|
|
139
|
+
val vp = if (fragView != null) findViewPager2(fragView) else findViewPager2(fragmentContainer)
|
|
140
|
+
if (vp != null) {
|
|
141
|
+
if (vp.width == 0 || vp.height == 0) {
|
|
142
|
+
fragView?.measure(
|
|
143
|
+
MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY),
|
|
144
|
+
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)
|
|
145
|
+
)
|
|
146
|
+
fragView?.layout(0, 0, measuredWidth, measuredHeight)
|
|
147
|
+
}
|
|
148
|
+
if (vp.adapter != null && vp.adapter!!.itemCount > 0) {
|
|
149
|
+
vp.setCurrentItem(vp.currentItem, false)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}, 200)
|
|
153
|
+
|
|
154
|
+
feedId?.let { id ->
|
|
155
|
+
ShortKitBridge.shared?.registerFeed(id)
|
|
156
|
+
ShortKitBridge.shared?.registerFeedFragment(id, fragment)
|
|
157
|
+
}
|
|
113
158
|
} catch (e: Exception) {
|
|
114
159
|
Log.e(TAG, "Failed to embed feed fragment", e)
|
|
115
160
|
}
|
|
116
161
|
}
|
|
117
162
|
|
|
118
163
|
private fun removeFeedFragment() {
|
|
164
|
+
feedId?.let { id ->
|
|
165
|
+
ShortKitBridge.shared?.unregisterFeed(id)
|
|
166
|
+
ShortKitBridge.shared?.unregisterFeedFragment(id)
|
|
167
|
+
}
|
|
119
168
|
val fragment = feedFragment ?: return
|
|
120
|
-
feedFragment = null
|
|
169
|
+
feedFragment = null
|
|
121
170
|
|
|
122
171
|
try {
|
|
123
172
|
val activity = getReactActivity() ?: return
|
|
@@ -126,192 +175,16 @@ class ShortKitFeedView(context: Context) : FrameLayout(context) {
|
|
|
126
175
|
.remove(fragment)
|
|
127
176
|
.commitNowAllowingStateLoss()
|
|
128
177
|
} catch (e: IllegalStateException) {
|
|
129
|
-
// Expected during Activity teardown
|
|
178
|
+
// Expected during Activity teardown
|
|
130
179
|
} catch (e: Exception) {
|
|
131
180
|
Log.e(TAG, "Unexpected error removing feed fragment", e)
|
|
132
181
|
}
|
|
133
182
|
}
|
|
134
183
|
|
|
135
184
|
// -----------------------------------------------------------------------
|
|
136
|
-
//
|
|
185
|
+
// Helpers
|
|
137
186
|
// -----------------------------------------------------------------------
|
|
138
187
|
|
|
139
|
-
private fun setupScrollTracking(fragment: ShortKitFeedFragment) {
|
|
140
|
-
val pager = findViewPager2(fragment.view ?: return) ?: run {
|
|
141
|
-
Log.w(TAG, "Could not find ViewPager2 in feed fragment hierarchy.")
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
viewPager = pager
|
|
145
|
-
|
|
146
|
-
val callback = object : ViewPager2.OnPageChangeCallback() {
|
|
147
|
-
override fun onPageScrolled(
|
|
148
|
-
position: Int,
|
|
149
|
-
positionOffset: Float,
|
|
150
|
-
positionOffsetPixels: Int
|
|
151
|
-
) {
|
|
152
|
-
handleScrollOffset(position, positionOffset, positionOffsetPixels)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
pageChangeCallback = callback
|
|
156
|
-
pager.registerOnPageChangeCallback(callback)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private fun teardownScrollTracking() {
|
|
160
|
-
pageUpdateRunnable?.let { handler.removeCallbacks(it) }
|
|
161
|
-
pageUpdateRunnable = null
|
|
162
|
-
pageChangeCallback?.let { viewPager?.unregisterOnPageChangeCallback(it) }
|
|
163
|
-
pageChangeCallback = null
|
|
164
|
-
viewPager = null
|
|
165
|
-
currentOverlayView?.translationY = 0f
|
|
166
|
-
nextOverlayView?.translationY = 0f
|
|
167
|
-
currentCarouselOverlayView?.translationY = 0f
|
|
168
|
-
nextCarouselOverlayView?.translationY = 0f
|
|
169
|
-
currentOverlayView = null
|
|
170
|
-
nextOverlayView = null
|
|
171
|
-
currentCarouselOverlayView = null
|
|
172
|
-
nextCarouselOverlayView = null
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
private fun handleScrollOffset(
|
|
176
|
-
position: Int,
|
|
177
|
-
positionOffset: Float,
|
|
178
|
-
positionOffsetPixels: Int
|
|
179
|
-
) {
|
|
180
|
-
val cellHeight = height.toFloat()
|
|
181
|
-
if (cellHeight <= 0) return
|
|
182
|
-
|
|
183
|
-
// Detect page change, but DEFER updating currentPage.
|
|
184
|
-
//
|
|
185
|
-
// Why: when the scroll settles on a new page, overlay-current still
|
|
186
|
-
// shows the OLD page's metadata (React hasn't processed OVERLAY_ACTIVATE
|
|
187
|
-
// yet). If we update currentPage immediately, delta snaps to 0 and
|
|
188
|
-
// overlay-current becomes visible with stale data.
|
|
189
|
-
//
|
|
190
|
-
// By deferring ~80ms, overlay-next (which already shows the correct
|
|
191
|
-
// data via NextOverlayProvider) stays visible at y=0 while React
|
|
192
|
-
// processes the state update.
|
|
193
|
-
if (positionOffset == 0f) {
|
|
194
|
-
if (position != currentPage && pageUpdateRunnable == null) {
|
|
195
|
-
val targetPage = position
|
|
196
|
-
val runnable = Runnable {
|
|
197
|
-
currentPage = targetPage
|
|
198
|
-
pageUpdateRunnable = null
|
|
199
|
-
// Reapply overlay transforms now that currentPage is updated.
|
|
200
|
-
// Without this, overlay-next (static NextOverlayProvider state)
|
|
201
|
-
// stays visible at y=0 while overlay-current (live state) stays
|
|
202
|
-
// hidden — no scroll event fires to trigger handleScrollOffset.
|
|
203
|
-
val h = height.toFloat()
|
|
204
|
-
currentOverlayView?.translationY = 0f
|
|
205
|
-
nextOverlayView?.translationY = h
|
|
206
|
-
currentCarouselOverlayView?.translationY = 0f
|
|
207
|
-
nextCarouselOverlayView?.translationY = h
|
|
208
|
-
}
|
|
209
|
-
pageUpdateRunnable = runnable
|
|
210
|
-
handler.postDelayed(runnable, 80)
|
|
211
|
-
}
|
|
212
|
-
} else if (pageUpdateRunnable != null) {
|
|
213
|
-
// User is scrolling again — apply pending update immediately
|
|
214
|
-
// so transforms stay aligned for the new gesture.
|
|
215
|
-
handler.removeCallbacks(pageUpdateRunnable!!)
|
|
216
|
-
pageUpdateRunnable = null
|
|
217
|
-
currentPage = position
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// positionOffset is 0.0 when settled, approaches 1.0 during forward scroll.
|
|
221
|
-
// position is the index of the page currently filling most of the screen.
|
|
222
|
-
val delta: Float = if (position >= currentPage) {
|
|
223
|
-
// Forward scroll (or idle)
|
|
224
|
-
positionOffsetPixels.toFloat()
|
|
225
|
-
} else {
|
|
226
|
-
// Backward scroll: position dropped below currentPage
|
|
227
|
-
-(cellHeight - positionOffsetPixels.toFloat())
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Find the overlay views if not cached
|
|
231
|
-
if (currentOverlayView == null || nextOverlayView == null) {
|
|
232
|
-
findOverlayViews()
|
|
233
|
-
}
|
|
234
|
-
if (currentCarouselOverlayView == null || nextCarouselOverlayView == null) {
|
|
235
|
-
findCarouselOverlayViews()
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Current overlays follow the active cell
|
|
239
|
-
currentOverlayView?.translationY = -delta
|
|
240
|
-
currentCarouselOverlayView?.translationY = -delta
|
|
241
|
-
|
|
242
|
-
// Next overlays: positioned one page ahead in the scroll direction
|
|
243
|
-
if (delta >= 0) {
|
|
244
|
-
// Forward scroll (or idle): next cell is below
|
|
245
|
-
val nextY = cellHeight - delta
|
|
246
|
-
nextOverlayView?.translationY = nextY
|
|
247
|
-
nextCarouselOverlayView?.translationY = nextY
|
|
248
|
-
} else {
|
|
249
|
-
// Backward scroll: next cell is above
|
|
250
|
-
val nextY = -cellHeight - delta
|
|
251
|
-
nextOverlayView?.translationY = nextY
|
|
252
|
-
nextCarouselOverlayView?.translationY = nextY
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Find the sibling RN overlay views by nativeID.
|
|
258
|
-
*
|
|
259
|
-
* In Fabric interop, this view may be wrapped in an intermediate
|
|
260
|
-
* ViewGroup, so `getParent()` may not be the React `<View>` container
|
|
261
|
-
* from ShortKitFeed.tsx. We walk up the ancestor chain until we find
|
|
262
|
-
* the overlays.
|
|
263
|
-
*/
|
|
264
|
-
private fun findOverlayViews() {
|
|
265
|
-
var ancestor = parent as? ViewGroup ?: return
|
|
266
|
-
while (true) {
|
|
267
|
-
for (i in 0 until ancestor.childCount) {
|
|
268
|
-
val child = ancestor.getChildAt(i)
|
|
269
|
-
if (child === this || isOwnAncestor(child)) continue
|
|
270
|
-
|
|
271
|
-
if (currentOverlayView == null) {
|
|
272
|
-
currentOverlayView = ReactFindViewUtil.findView(child, "overlay-current")
|
|
273
|
-
}
|
|
274
|
-
if (nextOverlayView == null) {
|
|
275
|
-
nextOverlayView = ReactFindViewUtil.findView(child, "overlay-next")
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (currentOverlayView != null && nextOverlayView != null) return
|
|
279
|
-
ancestor = ancestor.parent as? ViewGroup ?: return
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Find the sibling RN carousel overlay views by nativeID.
|
|
285
|
-
*/
|
|
286
|
-
private fun findCarouselOverlayViews() {
|
|
287
|
-
var ancestor = parent as? ViewGroup ?: return
|
|
288
|
-
while (true) {
|
|
289
|
-
for (i in 0 until ancestor.childCount) {
|
|
290
|
-
val child = ancestor.getChildAt(i)
|
|
291
|
-
if (child === this || isOwnAncestor(child)) continue
|
|
292
|
-
|
|
293
|
-
if (currentCarouselOverlayView == null) {
|
|
294
|
-
currentCarouselOverlayView = ReactFindViewUtil.findView(child, "carousel-overlay-current")
|
|
295
|
-
}
|
|
296
|
-
if (nextCarouselOverlayView == null) {
|
|
297
|
-
nextCarouselOverlayView = ReactFindViewUtil.findView(child, "carousel-overlay-next")
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (currentCarouselOverlayView != null && nextCarouselOverlayView != null) return
|
|
301
|
-
ancestor = ancestor.parent as? ViewGroup ?: return
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/** Check if the given view is an ancestor of this view. */
|
|
306
|
-
private fun isOwnAncestor(view: View): Boolean {
|
|
307
|
-
var current: ViewParent? = parent
|
|
308
|
-
while (current != null) {
|
|
309
|
-
if (current === view) return true
|
|
310
|
-
current = current.parent
|
|
311
|
-
}
|
|
312
|
-
return false
|
|
313
|
-
}
|
|
314
|
-
|
|
315
188
|
/** Recursively find the first [ViewPager2] in a view hierarchy. */
|
|
316
189
|
private fun findViewPager2(view: View): ViewPager2? {
|
|
317
190
|
if (view is ViewPager2) return view
|
|
@@ -323,13 +196,8 @@ class ShortKitFeedView(context: Context) : FrameLayout(context) {
|
|
|
323
196
|
return null
|
|
324
197
|
}
|
|
325
198
|
|
|
326
|
-
// -----------------------------------------------------------------------
|
|
327
|
-
// Helpers
|
|
328
|
-
// -----------------------------------------------------------------------
|
|
329
|
-
|
|
330
199
|
/**
|
|
331
200
|
* Walk the context wrapper chain to find the hosting [FragmentActivity].
|
|
332
|
-
* React Native's [ThemedReactContext] wraps the Activity, so we unwrap it.
|
|
333
201
|
*/
|
|
334
202
|
private fun getReactActivity(): FragmentActivity? {
|
|
335
203
|
var ctx = context
|
|
@@ -15,16 +15,25 @@ class ShortKitFeedViewManager : SimpleViewManager<ShortKitFeedView>() {
|
|
|
15
15
|
return ShortKitFeedView(context)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
@ReactProp(name = "feedId")
|
|
19
|
+
fun setFeedId(view: ShortKitFeedView, feedId: String?) {
|
|
20
|
+
view.feedId = feedId
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
@ReactProp(name = "config")
|
|
19
24
|
fun setConfig(view: ShortKitFeedView, config: String?) {
|
|
20
25
|
view.config = config
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
@ReactProp(name = "
|
|
24
|
-
fun
|
|
25
|
-
view.
|
|
28
|
+
@ReactProp(name = "startAtItemId")
|
|
29
|
+
fun setStartAtItemId(view: ShortKitFeedView, startAtItemId: String?) {
|
|
30
|
+
view.startAtItemId = startAtItemId
|
|
26
31
|
}
|
|
27
32
|
|
|
33
|
+
@ReactProp(name = "preloadId")
|
|
34
|
+
fun setPreloadId(view: ShortKitFeedView, preloadId: String?) {
|
|
35
|
+
view.preloadId = preloadId
|
|
36
|
+
}
|
|
28
37
|
|
|
29
38
|
companion object {
|
|
30
39
|
const val REACT_CLASS = "ShortKitFeedView"
|