@shortkitsdk/react-native 0.2.35 → 0.2.37
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/build.gradle.kts +8 -0
- package/android/libs/shortkit-release.aar +0 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +94 -46
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +46 -7
- package/android/src/main/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHost.kt +233 -27
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +252 -27
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +135 -6
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +15 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +21 -11
- package/android/src/test/java/com/shortkit/reactnative/ReactCarouselOverlayHostEmitTest.kt +134 -0
- package/android/src/test/java/com/shortkit/reactnative/ReactOverlayHostDragTest.kt +45 -0
- package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostDragTest.kt +69 -0
- package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostEmitTest.kt +144 -0
- package/android/src/test/java/com/shortkit/reactnative/ShortKitFeedViewActivePropTest.kt +57 -0
- package/ios/ReactOverlayHost.swift +10 -8
- package/ios/ReactVideoCarouselOverlayHost.swift +14 -11
- package/ios/ShortKitBridge.swift +18 -0
- package/ios/ShortKitModule.mm +5 -0
- package/ios/ShortKitPlayerNativeView.swift +36 -0
- package/ios/ShortKitSDK.xcframework/Info.plist +5 -5
- 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 +932 -84
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +26 -2
- 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 +26 -2
- 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 +932 -84
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +26 -2
- 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 +26 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +932 -84
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +26 -2
- 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 +26 -2
- 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/package.json +1 -1
- package/src/ShortKitCommands.ts +20 -0
- package/src/ShortKitFeed.tsx +21 -0
- package/src/specs/NativeShortKitModule.ts +10 -0
- package/src/types.ts +35 -0
package/android/build.gradle.kts
CHANGED
|
@@ -27,6 +27,10 @@ android {
|
|
|
27
27
|
java.srcDirs("src/main/java")
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
testOptions {
|
|
32
|
+
unitTests.isIncludeAndroidResources = true
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
dependencies {
|
|
@@ -47,4 +51,8 @@ dependencies {
|
|
|
47
51
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
|
48
52
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
|
|
49
53
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
|
|
54
|
+
|
|
55
|
+
testImplementation("junit:junit:4.13.2")
|
|
56
|
+
testImplementation("org.robolectric:robolectric:4.14.1")
|
|
57
|
+
testImplementation("androidx.test:core:1.6.1")
|
|
50
58
|
}
|
|
Binary file
|
|
@@ -59,6 +59,52 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
59
59
|
/** Unique identifier for this overlay instance, used for event routing. */
|
|
60
60
|
val surfaceId: String = java.util.UUID.randomUUID().toString()
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Currently configured item id — used to detect item transitions so we
|
|
64
|
+
* can emit an event instead of triggering a full Fabric remount.
|
|
65
|
+
* Marked `internal` to allow unit-test verification.
|
|
66
|
+
*/
|
|
67
|
+
internal var currentItemId: String? = null
|
|
68
|
+
private set
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Whether initial props have been pushed at least once. First configure
|
|
72
|
+
* must go through pushProps()/setProperties; subsequent same-item
|
|
73
|
+
* rebinds are no-op; subsequent different-item rebinds emit
|
|
74
|
+
* onCarouselItemChanged for a React diff (no Fabric remount).
|
|
75
|
+
*/
|
|
76
|
+
internal var hasPushedInitialProps: Boolean = false
|
|
77
|
+
private set
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Test seam for emit-event interception. Production uses the default
|
|
81
|
+
* (forward to ShortKitBridge.shared); tests can overwrite to capture.
|
|
82
|
+
* Mirrors how iOS tests wrap bridge.emit().
|
|
83
|
+
*/
|
|
84
|
+
internal var emitEvent: (String, com.facebook.react.bridge.WritableMap) -> Unit =
|
|
85
|
+
{ name, params -> ShortKitBridge.shared?.emitEvent(name, params) }
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Test seam for WritableMap creation. Defaults to Arguments.createMap()
|
|
89
|
+
* (WritableNativeMap — requires JNI). Tests override with JavaOnlyMap()
|
|
90
|
+
* to avoid the native-library initialization requirement in Robolectric.
|
|
91
|
+
*/
|
|
92
|
+
internal var createMap: () -> com.facebook.react.bridge.WritableMap =
|
|
93
|
+
{ com.facebook.react.bridge.Arguments.createMap() }
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Test seam for ReactSurface creation. Production implementation resolves
|
|
97
|
+
* the surface from the ReactHost obtained from the application context.
|
|
98
|
+
* Tests override with a stub/mock so the surface is non-null after
|
|
99
|
+
* pushProps(), allowing hasPushedInitialProps to flip as expected.
|
|
100
|
+
* Returns null when surface creation fails (e.g. reactHost unavailable).
|
|
101
|
+
*/
|
|
102
|
+
internal var createSurface: (moduleName: String, initialProps: Bundle?) -> ReactSurface? =
|
|
103
|
+
{ moduleName, initialProps ->
|
|
104
|
+
(context.applicationContext as? ReactApplication)?.reactHost
|
|
105
|
+
?.createSurface(context, moduleName, initialProps)
|
|
106
|
+
}
|
|
107
|
+
|
|
62
108
|
private var cachedItemJSON: String? = null
|
|
63
109
|
private var isActive: Boolean = false
|
|
64
110
|
private var activeImageIndex: Int = 0
|
|
@@ -133,16 +179,12 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
133
179
|
isActive = false
|
|
134
180
|
activeImageIndex = 0
|
|
135
181
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
val gen = ++configureGeneration
|
|
182
|
+
val isSameItem = item.id == currentItemId
|
|
183
|
+
currentItemId = item.id
|
|
139
184
|
|
|
140
|
-
|
|
185
|
+
val gen = ++configureGeneration
|
|
141
186
|
pendingWriteJob?.cancel()
|
|
142
187
|
|
|
143
|
-
// Fast path: check for pre-existing temp files synchronously (just
|
|
144
|
-
// file.exists(), no I/O). This avoids the flicker of remote→local
|
|
145
|
-
// URL swap for images that were cached in a previous session.
|
|
146
188
|
val lookup = cachedImage
|
|
147
189
|
val fastItem = if (lookup != null) {
|
|
148
190
|
val fastImages = item.images.map { image ->
|
|
@@ -166,9 +208,23 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
166
208
|
|
|
167
209
|
val json = Json.encodeToString(fastItem)
|
|
168
210
|
cachedItemJSON = json
|
|
169
|
-
pushProps()
|
|
170
211
|
|
|
171
|
-
|
|
212
|
+
if (!hasPushedInitialProps) {
|
|
213
|
+
pushProps()
|
|
214
|
+
// Only flip the flag if the surface was actually created. If reactHost
|
|
215
|
+
// was null (createSurfaceIfNeeded silently returned), surface remains
|
|
216
|
+
// null and pendingProps is parked — stay in "first mount" mode so the
|
|
217
|
+
// next configure() retries pushProps() with the new item. Mirrors iOS
|
|
218
|
+
// ReactCarouselOverlayHost.swift:131-141.
|
|
219
|
+
if (surface != null) {
|
|
220
|
+
hasPushedInitialProps = true
|
|
221
|
+
}
|
|
222
|
+
} else if (!isSameItem) {
|
|
223
|
+
emitItemChanged(json)
|
|
224
|
+
}
|
|
225
|
+
// else: same item rebind — no emit, no Fabric prop push.
|
|
226
|
+
|
|
227
|
+
// Pre-size the surface view (existing logic, unchanged).
|
|
172
228
|
val parentView = parent as? android.view.View
|
|
173
229
|
val w = if (width > 0) width
|
|
174
230
|
else if (parentView != null && parentView.width > 0) parentView.width
|
|
@@ -178,43 +234,19 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
178
234
|
else context.resources.displayMetrics.heightPixels
|
|
179
235
|
measureAndLayoutSurfaceView(w, h)
|
|
180
236
|
|
|
181
|
-
// Background: write any newly-cached images to temp files
|
|
182
|
-
//
|
|
237
|
+
// Background: write any newly-cached images to temp files for next-time
|
|
238
|
+
// fast-path availability. iOS-aligned: write-only, no second pushProps()
|
|
239
|
+
// (next configure() picks up the local URL via the synchronous fast-path).
|
|
183
240
|
if (lookup == null) return
|
|
184
241
|
val hasRemoteUrls = fastItem.images.any { !it.url.startsWith("file://") }
|
|
185
242
|
if (!hasRemoteUrls) return
|
|
186
243
|
|
|
187
244
|
pendingWriteJob = ioScope.launch {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
val localUrl = writeTempImage(bitmap, image.url)
|
|
193
|
-
if (localUrl != null) CarouselImage(url = localUrl, alt = image.alt)
|
|
194
|
-
else image
|
|
195
|
-
} else {
|
|
196
|
-
image
|
|
197
|
-
}
|
|
245
|
+
withContext(Dispatchers.IO) {
|
|
246
|
+
item.images.forEach { image ->
|
|
247
|
+
if (gen != configureGeneration) return@forEach
|
|
248
|
+
lookup(image.url)?.let { writeTempImage(it, image.url) }
|
|
198
249
|
}
|
|
199
|
-
ImageCarouselItem(
|
|
200
|
-
id = item.id,
|
|
201
|
-
images = localImages,
|
|
202
|
-
caption = item.caption,
|
|
203
|
-
title = item.title,
|
|
204
|
-
description = item.description,
|
|
205
|
-
author = item.author,
|
|
206
|
-
section = item.section,
|
|
207
|
-
articleUrl = item.articleUrl,
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Back on Main — only update if this configure() is still current
|
|
212
|
-
if (gen != configureGeneration) return@launch
|
|
213
|
-
val hasLocalImages = modifiedItem.images.any { it.url.startsWith("file://") }
|
|
214
|
-
if (hasLocalImages) {
|
|
215
|
-
val localJson = Json.encodeToString(modifiedItem)
|
|
216
|
-
cachedItemJSON = localJson
|
|
217
|
-
pushProps()
|
|
218
250
|
}
|
|
219
251
|
}
|
|
220
252
|
}
|
|
@@ -257,6 +289,21 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
257
289
|
applySurfaceProps(bundle)
|
|
258
290
|
}
|
|
259
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Emit onCarouselItemChanged for a React diff update on cell reuse.
|
|
294
|
+
* Replaces pushProps() on subsequent item changes to avoid Fabric remount.
|
|
295
|
+
* Payload shape mirrors iOS ReactCarouselOverlayHost.swift:151-158.
|
|
296
|
+
*/
|
|
297
|
+
private fun emitItemChanged(json: String) {
|
|
298
|
+
val params = createMap().apply {
|
|
299
|
+
putString("surfaceId", surfaceId)
|
|
300
|
+
putString("item", json)
|
|
301
|
+
putBoolean("isActive", false)
|
|
302
|
+
putInt("activeImageIndex", 0)
|
|
303
|
+
}
|
|
304
|
+
emitEvent("onCarouselItemChanged", params)
|
|
305
|
+
}
|
|
306
|
+
|
|
260
307
|
/** Apply props to the surface, handling all lifecycle states. */
|
|
261
308
|
private fun applySurfaceProps(bundle: Bundle) {
|
|
262
309
|
val s = surface
|
|
@@ -374,11 +421,6 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
374
421
|
private fun createSurfaceIfNeeded() {
|
|
375
422
|
if (surface != null) return
|
|
376
423
|
|
|
377
|
-
val reactHost = (context.applicationContext as? ReactApplication)?.reactHost
|
|
378
|
-
if (reactHost == null) {
|
|
379
|
-
android.util.Log.e(TAG, "createSurface FAILED: reactHost is null")
|
|
380
|
-
return
|
|
381
|
-
}
|
|
382
424
|
val moduleName = "ShortKitCarouselOverlay_$carouselOverlayName"
|
|
383
425
|
|
|
384
426
|
// Pass pending props as initial props so the JS component has data on first render.
|
|
@@ -387,7 +429,13 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
387
429
|
val initialProps = pendingProps
|
|
388
430
|
pendingProps = null
|
|
389
431
|
|
|
390
|
-
val newSurface =
|
|
432
|
+
val newSurface = createSurface(moduleName, initialProps)
|
|
433
|
+
if (newSurface == null) {
|
|
434
|
+
android.util.Log.e(TAG, "createSurface FAILED: reactHost is null")
|
|
435
|
+
// Restore pending props so a future createSurfaceIfNeeded() can retry.
|
|
436
|
+
if (initialProps != null) pendingProps = initialProps
|
|
437
|
+
return
|
|
438
|
+
}
|
|
391
439
|
surface = newSurface
|
|
392
440
|
|
|
393
441
|
newSurface.view?.let { surfaceView ->
|
|
@@ -99,6 +99,21 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
99
99
|
private var cachedActiveCue: JSONObject? = null
|
|
100
100
|
private var cachedFeedScrollPhase: String? = null
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Tracks active drag phase. While true, per-frame player-state emissions
|
|
104
|
+
* (playerState, isMuted, playbackRate, captionsEnabled, activeCue, time)
|
|
105
|
+
* are suppressed; emitFullState() re-syncs cached values on settle.
|
|
106
|
+
*
|
|
107
|
+
* Mirrors iOS ReactOverlayHost.swift:51 isDragging field.
|
|
108
|
+
*/
|
|
109
|
+
internal var isDragging: Boolean = false
|
|
110
|
+
private set
|
|
111
|
+
|
|
112
|
+
/** Test-only setter for [isDragging]. */
|
|
113
|
+
internal var isDraggingForTest: Boolean
|
|
114
|
+
get() = isDragging
|
|
115
|
+
set(value) { isDragging = value }
|
|
116
|
+
|
|
102
117
|
// Time coalescing (250ms)
|
|
103
118
|
private var cachedCurrentTime: Double = 0.0
|
|
104
119
|
private var cachedDuration: Double = 0.0
|
|
@@ -184,10 +199,17 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
184
199
|
cachedCaptionsEnabled = false
|
|
185
200
|
cachedActiveCue = null
|
|
186
201
|
cachedFeedScrollPhase = null
|
|
202
|
+
isDragging = false
|
|
187
203
|
|
|
188
204
|
if (surface == null) {
|
|
189
205
|
createSurfaceIfNeeded()
|
|
190
|
-
|
|
206
|
+
// Only flip the flag if surface was actually created. If reactHost was
|
|
207
|
+
// null (createSurfaceIfNeeded silently returned), surface remains null
|
|
208
|
+
// and the next configure() will retry. Mirrors iOS pattern and matches
|
|
209
|
+
// the carousel host fix from 6e5e1dce.
|
|
210
|
+
if (surface != null) {
|
|
211
|
+
surfaceHasStarted = true
|
|
212
|
+
}
|
|
191
213
|
} else if (!surfaceHasStarted) {
|
|
192
214
|
(surface as? ReactSurfaceImpl)?.updateInitProps(buildInitialPropsBundle())
|
|
193
215
|
surfaceHasStarted = true
|
|
@@ -410,7 +432,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
410
432
|
scope.launch {
|
|
411
433
|
player.playerState.collect { state ->
|
|
412
434
|
cachedPlayerState = ShortKitBridge.playerStateString(state)
|
|
413
|
-
if (isActive) {
|
|
435
|
+
if (isActive && !isDragging) {
|
|
414
436
|
val params = Arguments.createMap().apply {
|
|
415
437
|
putString("surfaceId", surfaceId)
|
|
416
438
|
putString("playerState", cachedPlayerState)
|
|
@@ -424,7 +446,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
424
446
|
scope.launch {
|
|
425
447
|
player.isMuted.collect { muted ->
|
|
426
448
|
cachedIsMuted = muted
|
|
427
|
-
if (isActive) {
|
|
449
|
+
if (isActive && !isDragging) {
|
|
428
450
|
val params = Arguments.createMap().apply {
|
|
429
451
|
putString("surfaceId", surfaceId)
|
|
430
452
|
putBoolean("isMuted", cachedIsMuted)
|
|
@@ -438,7 +460,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
438
460
|
scope.launch {
|
|
439
461
|
player.playbackRate.collect { rate ->
|
|
440
462
|
cachedPlaybackRate = rate.toDouble()
|
|
441
|
-
if (isActive) {
|
|
463
|
+
if (isActive && !isDragging) {
|
|
442
464
|
val params = Arguments.createMap().apply {
|
|
443
465
|
putString("surfaceId", surfaceId)
|
|
444
466
|
putDouble("playbackRate", cachedPlaybackRate)
|
|
@@ -452,7 +474,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
452
474
|
scope.launch {
|
|
453
475
|
player.captionsEnabled.collect { enabled ->
|
|
454
476
|
cachedCaptionsEnabled = enabled
|
|
455
|
-
if (isActive) {
|
|
477
|
+
if (isActive && !isDragging) {
|
|
456
478
|
val params = Arguments.createMap().apply {
|
|
457
479
|
putString("surfaceId", surfaceId)
|
|
458
480
|
putBoolean("captionsEnabled", cachedCaptionsEnabled)
|
|
@@ -474,7 +496,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
474
496
|
} else {
|
|
475
497
|
null
|
|
476
498
|
}
|
|
477
|
-
if (isActive) {
|
|
499
|
+
if (isActive && !isDragging) {
|
|
478
500
|
val params = Arguments.createMap().apply {
|
|
479
501
|
putString("surfaceId", surfaceId)
|
|
480
502
|
val cueJson = cachedActiveCue?.toString()
|
|
@@ -495,7 +517,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
495
517
|
cachedCurrentTime = time.currentMs / 1000.0
|
|
496
518
|
cachedDuration = time.durationMs / 1000.0
|
|
497
519
|
cachedBuffered = time.bufferedMs / 1000.0
|
|
498
|
-
if (isActive) {
|
|
520
|
+
if (isActive && !isDragging) {
|
|
499
521
|
timeDirty = true
|
|
500
522
|
}
|
|
501
523
|
}
|
|
@@ -511,6 +533,23 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
511
533
|
}.toString()
|
|
512
534
|
is FeedScrollPhase.Settled -> """{"phase":"settled"}"""
|
|
513
535
|
}
|
|
536
|
+
when (phase) {
|
|
537
|
+
is FeedScrollPhase.Dragging -> {
|
|
538
|
+
isDragging = true
|
|
539
|
+
stopTimeCoalescing()
|
|
540
|
+
}
|
|
541
|
+
is FeedScrollPhase.Settled -> {
|
|
542
|
+
val wasDragging = isDragging
|
|
543
|
+
isDragging = false
|
|
544
|
+
if (isActive) {
|
|
545
|
+
startTimeCoalescing()
|
|
546
|
+
if (wasDragging) {
|
|
547
|
+
emitFullState()
|
|
548
|
+
return@collect // emitFullState includes feedScrollPhase
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
514
553
|
if (isActive) {
|
|
515
554
|
val params = Arguments.createMap().apply {
|
|
516
555
|
putString("surfaceId", surfaceId)
|