@momo-kits/native-kits 0.164.1-beta.14-debug → 0.164.1-beta.15-debug

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.
@@ -40,7 +40,7 @@ kotlin {
40
40
  }
41
41
 
42
42
  cocoapods {
43
- version = "0.164.1-beta.14-debug"
43
+ version = "0.164.1-beta.15-debug"
44
44
  summary = "IOS Shared module"
45
45
  homepage = "https://momo.vn"
46
46
  ios.deploymentTarget = "15.0"
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |spec|
2
2
  spec.name = 'compose'
3
- spec.version = '0.164.1-beta.14-debug'
3
+ spec.version = '0.164.1-beta.15-debug'
4
4
  spec.homepage = 'https://momo.vn'
5
5
  spec.source = { :http=> ''}
6
6
  spec.authors = ''
@@ -94,7 +94,6 @@ fun NavigationContainer(
94
94
  setNavigator?.invoke(navigator)
95
95
  }
96
96
 
97
- SwipeBackHaptic(maxApi)
98
97
 
99
98
  // Gesture pops never reach Navigator.dismissScreen(), so prune the registry from
100
99
  // the back stack itself. collectLatest lets a rapid re-push cancel a pending prune.
@@ -17,7 +17,6 @@ import androidx.compose.ui.ExperimentalComposeUiApi
17
17
  import androidx.compose.ui.InternalComposeUiApi
18
18
  import androidx.compose.ui.Modifier
19
19
  import androidx.compose.ui.backhandler.LocalCompatNavigationEventDispatcherOwner
20
- import androidx.compose.ui.backhandler.BackHandler as ComposeBackHandler
21
20
  import androidx.compose.ui.graphics.Color
22
21
  import androidx.compose.ui.graphics.NativePaint
23
22
  import androidx.compose.ui.graphics.toArgb
@@ -39,7 +38,6 @@ import platform.Foundation.NSBundle
39
38
  import platform.UIKit.UIColor
40
39
  import platform.UIKit.UIDevice
41
40
  import platform.UIKit.UIScreen
42
- import vn.momo.kits.navigation.AttachHostBackGesture
43
41
 
44
42
  actual fun getPlatformName(): String = "iOS"
45
43
 
@@ -62,32 +60,21 @@ actual fun getStatusBarHeight(): Dp {
62
60
  return WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
63
61
  }
64
62
 
65
- // An enabled kit BackHandler is registered inside the NavHost destination, so it out-ranks
66
- // NavHost's own predictive-back handler and swallows the gesture — that is how a screen opts out
67
- // of swipe-back (the port of RN's `hasCustomBackHandling`).
68
- @Suppress("DEPRECATION")
69
- @OptIn(ExperimentalComposeUiApi::class)
70
63
  @Composable
71
64
  actual fun BackHandler(enabled: Boolean, onBack: () -> Unit) {
72
- ComposeBackHandler(enabled = enabled, onBack = onBack)
73
65
  }
74
66
 
75
67
  @OptIn(InternalComposeUiApi::class)
76
68
  @Composable
77
69
  actual fun ProvideNavigationEventDispatcherOwner(content: @Composable () -> Unit) {
78
- // Detached on purpose: passing the scene's owner through would arm Compose's own
79
- // UIKitBackGestureRecognizer alongside the host's, and UIKit settles that conflict by driving
80
- // one to .failed, which UIKitNavigationEventInput turns into a fabricated completed back.
81
- // Enabled on purpose: every NavigationEventDispatcher.dispatchOn* early-returns while
82
- // isEnabled is false, which would mute the host-driven seek as well.
83
70
  val navigationEventDispatcherOwner = remember {
84
71
  object : NavigationEventDispatcherOwner {
85
- override val navigationEventDispatcher = NavigationEventDispatcher()
72
+ override val navigationEventDispatcher = NavigationEventDispatcher().apply {
73
+ isEnabled = false
74
+ }
86
75
  }
87
76
  }
88
77
 
89
- AttachHostBackGesture(navigationEventDispatcherOwner.navigationEventDispatcher)
90
-
91
78
  CompositionLocalProvider(
92
79
  LocalCompatNavigationEventDispatcherOwner provides navigationEventDispatcherOwner,
93
80
  content = content
package/gradle.properties CHANGED
@@ -18,7 +18,7 @@ kotlin.apple.xcodeCompatibility.nowarn=true
18
18
  name="ComposeKits"
19
19
  group=vn.momo.kits
20
20
  artifact.id=kits
21
- version=0.164.1-beta.14
21
+ version=0.164.1-beta.15
22
22
 
23
23
  repo=GitLab
24
24
  url=https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKits'
3
- s.version = '0.164.1-beta.14'
3
+ s.version = '0.164.1-beta.15'
4
4
  s.summary = 'MoMoUIKits for iOS'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKitsDemo'
3
- s.version = '0.164.1-beta.14'
3
+ s.version = '0.164.1-beta.15'
4
4
  s.summary = 'Demo browser for MoMoUIKits SwiftUI components'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.164.1-beta.14-debug",
3
+ "version": "0.164.1-beta.15-debug",
4
4
  "private": false,
5
5
  "dependeNavigationncies": {},
6
6
  "devDependencies": {},
@@ -1,12 +0,0 @@
1
- package vn.momo.kits.platform
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.State
5
- import androidx.compose.runtime.mutableStateOf
6
- import androidx.compose.runtime.remember
7
-
8
- // No passive observer exists at androidx.activity 1.10.x — a second enabled callback would steal
9
- // the gesture from NavHost. Android's system back gesture emits its own platform haptic on commit.
10
- // Revisit if androidx.activity >= 1.12 is adopted; this then becomes a copy of the iOS actual.
11
- @Composable
12
- actual fun rememberBackGestureProgress(): State<Float?> = remember { mutableStateOf(null) }
@@ -1,39 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.LaunchedEffect
5
- import androidx.compose.runtime.snapshotFlow
6
- import vn.momo.kits.platform.getScreenDimensions
7
- import vn.momo.kits.platform.rememberBackGestureProgress
8
- import vn.momo.maxapi.IMaxApi
9
-
10
- // RN fires the tick at a flat 100 px of thumb travel; keep it unscaled so the physical distance is
11
- // the same on every device.
12
- private const val SWIPE_BACK_HAPTIC_THRESHOLD_DP = 50f
13
-
14
- /**
15
- * Fires one light tick once the back gesture has travelled ~100dp, re-arming when the finger drops
16
- * back below the threshold. iOS only — [rememberBackGestureProgress] is always `null` on Android.
17
- */
18
- @Composable
19
- internal fun SwipeBackHaptic(maxApi: IMaxApi?) {
20
- if (maxApi == null) return
21
-
22
- val progress = rememberBackGestureProgress()
23
- val screenWidth = getScreenDimensions().width.toFloat()
24
-
25
- LaunchedEffect(maxApi, screenWidth) {
26
- if (screenWidth <= 0f) return@LaunchedEffect
27
- val threshold = SWIPE_BACK_HAPTIC_THRESHOLD_DP / screenWidth
28
- var isSendHaptic = false
29
- snapshotFlow { progress.value }.collect { fraction ->
30
- val travelled = fraction ?: 0f
31
- if (travelled >= threshold && !isSendHaptic) {
32
- runCatching { maxApi.triggerEventVibration("light", callback = null) }
33
- isSendHaptic = true
34
- } else if (travelled < threshold && isSendHaptic) {
35
- isSendHaptic = false
36
- }
37
- }
38
- }
39
- }
@@ -1,15 +0,0 @@
1
- package vn.momo.kits.platform
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.State
5
-
6
- /**
7
- * Progress of an in-flight back gesture as a fraction of the container width, or `null` while no
8
- * gesture is running. Observing only — it must never consume the gesture, otherwise it would steal
9
- * it from NavHost.
10
- *
11
- * Android always returns `null`: its NavHost runs on `androidx.activity`'s OnBackPressedDispatcher,
12
- * which exposes progress only to the top-most enabled callback, and that has to stay NavHost's.
13
- */
14
- @Composable
15
- expect fun rememberBackGestureProgress(): State<Float?>
@@ -1,125 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.DisposableEffect
5
- import androidx.compose.runtime.remember
6
- import androidx.navigationevent.NavigationEvent
7
- import androidx.navigationevent.NavigationEventDispatcher
8
- import androidx.navigationevent.NavigationEventInput
9
- import kotlinx.cinterop.ExperimentalForeignApi
10
- import platform.Foundation.NSThread
11
- import platform.darwin.dispatch_async
12
- import platform.darwin.dispatch_get_main_queue
13
-
14
- /**
15
- * Kit end of the host's edge-pan recogniser.
16
- *
17
- * iOS keeps exactly one back-gesture recogniser and it belongs to the host app — the kit never
18
- * installs its own, so the `.failed -> completed` conflict between two competing recognisers is
19
- * unreachable by construction. The host replays its drag here; the events are fed into the
20
- * `NavigationEventDispatcher` that [NavigationContainer] provides, which drives NavHost's
21
- * `PredictiveBackHandler` and therefore `SeekableTransitionState.seekTo`. Parallax, scrim and edge
22
- * shadow are children of that same transition, so they follow the finger for free.
23
- *
24
- * Only primitives cross the Swift boundary: `androidx.navigationevent` is a private transitive
25
- * dependency and is absent from the generated framework header.
26
- *
27
- * Call every method from the main thread; anything else is re-posted to the main queue.
28
- */
29
- object HostBackGestureBridge {
30
- private val attachedInputs = mutableListOf<HostNavigationEventInput>()
31
- private var gestureInput: HostNavigationEventInput? = null
32
-
33
- /**
34
- * Whether the top-most mounted container has an enabled back handler, i.e. the authoritative
35
- * answer to "will Compose consume this back?". `false` means no container is mounted, or the
36
- * mounted one deliberately opts out (kill switch, overlay showing, custom back handler), and
37
- * the host should fall back to its own navigation.
38
- */
39
- val hasEnabledHandlers: Boolean
40
- get() = attachedInputs.lastOrNull()?.hasEnabledHandlers == true
41
-
42
- /** Begins a drag. Pairs with exactly one [completed] or [cancelled]. */
43
- fun started() = onMain {
44
- val input = attachedInputs.lastOrNull() ?: return@onMain
45
- gestureInput = input
46
- input.backStarted()
47
- }
48
-
49
- /** Drag progress in `0f..1f` — the fraction of the screen width travelled. */
50
- fun progressed(progress: Float) = onMain {
51
- gestureInput?.backProgressed(progress.coerceIn(0f, 1f))
52
- }
53
-
54
- /**
55
- * Released past the commit threshold: the screen pops.
56
- *
57
- * Falls back to the attached input when no drag was started, so a host that commits without
58
- * ever seeking still gets a back. Dispatching a bare completion is the documented
59
- * non-predictive path — the same shape as a back button press — and dropping it here is what
60
- * silently stranded the mini-app root, where the host never seeks.
61
- */
62
- fun completed() = onMain {
63
- (gestureInput ?: attachedInputs.lastOrNull())?.backCompleted()
64
- gestureInput = null
65
- }
66
-
67
- /** Released below the commit threshold: the screen springs back. */
68
- fun cancelled() = onMain {
69
- gestureInput?.backCancelled()
70
- gestureInput = null
71
- }
72
-
73
- internal fun attach(input: HostNavigationEventInput) {
74
- attachedInputs += input
75
- }
76
-
77
- internal fun detach(input: HostNavigationEventInput) {
78
- attachedInputs -= input
79
- // A container unmounted mid-drag must not hand its pending terminal event to whichever
80
- // container is on top now: NavigationEventInput fabricates a non-predictive start for a
81
- // completed/cancelled it never saw begin, which would pop a screen out of nowhere.
82
- if (gestureInput === input) gestureInput = null
83
- }
84
- }
85
-
86
- /** Wires one [NavigationContainer]'s dispatcher to [HostBackGestureBridge] while it is composed. */
87
- @Composable
88
- internal fun AttachHostBackGesture(dispatcher: NavigationEventDispatcher) {
89
- val input = remember(dispatcher) { HostNavigationEventInput() }
90
-
91
- DisposableEffect(dispatcher, input) {
92
- dispatcher.addInput(input)
93
- HostBackGestureBridge.attach(input)
94
- onDispose {
95
- HostBackGestureBridge.detach(input)
96
- dispatcher.removeInput(input)
97
- }
98
- }
99
- }
100
-
101
- internal class HostNavigationEventInput : NavigationEventInput() {
102
- var hasEnabledHandlers: Boolean = false
103
- private set
104
-
105
- override fun onHasEnabledHandlersChanged(hasEnabledHandlers: Boolean) {
106
- this.hasEnabledHandlers = hasEnabledHandlers
107
- }
108
-
109
- // Touch coordinates are not forwarded — the stack transition is seeked by progress alone, and
110
- // leaving them out keeps the bridge's Swift-facing signatures primitive.
111
- fun backStarted() = dispatchOnBackStarted(NavigationEvent(swipeEdge = NavigationEvent.EDGE_LEFT))
112
-
113
- fun backProgressed(progress: Float) = dispatchOnBackProgressed(
114
- NavigationEvent(swipeEdge = NavigationEvent.EDGE_LEFT, progress = progress)
115
- )
116
-
117
- fun backCancelled() = dispatchOnBackCancelled()
118
-
119
- fun backCompleted() = dispatchOnBackCompleted()
120
- }
121
-
122
- @OptIn(ExperimentalForeignApi::class)
123
- private fun onMain(block: () -> Unit) {
124
- if (NSThread.isMainThread()) block() else dispatch_async(dispatch_get_main_queue()) { block() }
125
- }
@@ -1,35 +0,0 @@
1
- package vn.momo.kits.platform
2
-
3
- import androidx.compose.runtime.Composable
4
- import androidx.compose.runtime.LaunchedEffect
5
- import androidx.compose.runtime.State
6
- import androidx.compose.runtime.mutableStateOf
7
- import androidx.compose.runtime.remember
8
- import androidx.compose.ui.InternalComposeUiApi
9
- import androidx.compose.ui.backhandler.LocalCompatNavigationEventDispatcherOwner
10
- import androidx.navigationevent.NavigationEventTransitionState
11
-
12
- // NavigationEventDispatcher.transitionState is a passive observer: UIKitNavigationEventInput
13
- // publishes every edge-pan event to it without any handler consuming the gesture. Its `progress`
14
- // is touch.x / view width, i.e. a plain fraction of the screen width.
15
- @OptIn(InternalComposeUiApi::class)
16
- @Composable
17
- actual fun rememberBackGestureProgress(): State<Float?> {
18
- val dispatcher = LocalCompatNavigationEventDispatcherOwner.current?.navigationEventDispatcher
19
- val progress = remember { mutableStateOf<Float?>(null) }
20
-
21
- LaunchedEffect(dispatcher) {
22
- if (dispatcher == null) {
23
- progress.value = null
24
- return@LaunchedEffect
25
- }
26
- dispatcher.transitionState.collect { state ->
27
- progress.value = (state as? NavigationEventTransitionState.InProgress)
28
- ?.takeIf { it.direction == NavigationEventTransitionState.TRANSITIONING_BACK }
29
- ?.latestEvent
30
- ?.progress
31
- }
32
- }
33
-
34
- return progress
35
- }