@momo-kits/native-kits 0.164.1-beta.7-debug → 0.164.1-beta.7

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 (134) hide show
  1. package/package.json +1 -1
  2. package/build.gradle.kts +0 -11
  3. package/compose/build.gradle.kts +0 -190
  4. package/compose/build.gradle.kts.backup +0 -190
  5. package/compose/compose.podspec +0 -47
  6. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
  8. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -150
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -106
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -826
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -23
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -134
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -214
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -216
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -259
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -182
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -328
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -97
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -217
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -470
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -131
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -203
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -175
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -263
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -52
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -38
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -39
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -299
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -129
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -103
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -190
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -406
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -612
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -164
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -156
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -225
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -320
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -81
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -309
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -34
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -360
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -69
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -105
  121. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
  122. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -118
  123. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
  124. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  125. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -220
  126. package/gradle/libs.versions.toml +0 -69
  127. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  128. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  129. package/gradle.properties +0 -26
  130. package/gradlew +0 -252
  131. package/gradlew.bat +0 -94
  132. package/ios-demo/README.md +0 -120
  133. package/local.properties +0 -8
  134. package/settings.gradle.kts +0 -64
@@ -1,190 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.animation.core.tween
4
- import androidx.compose.animation.fadeIn
5
- import androidx.compose.animation.slideInVertically
6
- import androidx.compose.animation.slideOutVertically
7
- import androidx.compose.runtime.Composable
8
- import androidx.compose.runtime.CompositionLocalProvider
9
- import androidx.compose.runtime.DisposableEffect
10
- import androidx.compose.runtime.LaunchedEffect
11
- import androidx.compose.runtime.mutableStateOf
12
- import androidx.compose.runtime.remember
13
- import androidx.compose.runtime.saveable.rememberSaveable
14
- import androidx.compose.runtime.staticCompositionLocalOf
15
- import androidx.compose.ui.unit.Dp
16
- import androidx.navigation.compose.NavHost
17
- import androidx.navigation.compose.composable
18
- import androidx.navigation.compose.rememberNavController
19
- import androidx.navigation.toRoute
20
- import kotlinx.coroutines.delay
21
- import kotlinx.coroutines.flow.collectLatest
22
- import vn.momo.kits.application.LocalContext
23
- import vn.momo.kits.application.LocalLocalize
24
- import vn.momo.kits.application.Localize
25
- import vn.momo.kits.application.MiniAppContext
26
- import vn.momo.kits.const.AppNavigationBar
27
- import vn.momo.kits.const.AppStatusBar
28
- import vn.momo.kits.const.AppTheme
29
- import vn.momo.kits.const.Theme
30
- import vn.momo.kits.const.defaultTheme
31
- import vn.momo.kits.platform.ProvideNavigationEventDispatcherOwner
32
- import vn.momo.kits.platform.ProvideOsFontScale
33
- import vn.momo.kits.utils.getAppStatusBarHeight
34
- import vn.momo.kits.utils.getNavigationBarHeight
35
- import vn.momo.maxapi.IMaxApi
36
- import kotlin.time.Duration.Companion.milliseconds
37
-
38
- @Composable
39
- fun NavigationContainer(
40
- initialScreenName: String,
41
- initialScreen: @Composable () -> Unit,
42
- options: NavigationOptions? = null,
43
- initialTheme: Theme = defaultTheme,
44
- miniAppContext: MiniAppContext? = null,
45
- maxApi: IMaxApi? = null,
46
- setNavigator: ((Navigator) -> Unit)? = null,
47
- statusBarHeight: Dp? = null,
48
- localize: Localize? = null,
49
- ) {
50
- val navController = rememberNavController()
51
- val registry = remember { DynamicScreenRegistry() }
52
- val navigator = remember { Navigator(navController = navController, maxApi = maxApi, registry = registry) }
53
- val statusBarHeight = statusBarHeight ?: getAppStatusBarHeight()
54
- val navigationBarHeight = getNavigationBarHeight()
55
-
56
- val parentContext = LocalContext.current
57
- val mergedContext = MiniAppContext.merge(parentContext, miniAppContext)
58
-
59
- val stackTransition = remember { StackTransitionCoordinator() }
60
- val theme = remember { mutableStateOf(initialTheme) }
61
- val fallbackLocalize = remember { Localize() }
62
-
63
- val resolvedLocalize = localize ?: fallbackLocalize
64
-
65
- LaunchedEffect(maxApi, resolvedLocalize) {
66
- val api = maxApi ?: return@LaunchedEffect
67
- runCatching {
68
- api.getLanguage(callback = { data ->
69
- if (parseLanguage(data) == Localize.EN) {
70
- resolvedLocalize.changeLanguage(Localize.EN)
71
- }
72
- })
73
- }
74
- }
75
-
76
- val screenId = rememberSaveable { registry.nextId() }
77
- registry.bind(screenId, initialScreenName, initialScreen, options)
78
- val startDestination = remember(screenId) { DynamicScreenRoute(screenId) }
79
-
80
- ProvideOsFontScale {
81
- ProvideNavigationEventDispatcherOwner {
82
- CompositionLocalProvider(
83
- LocalNavigator provides navigator,
84
- LocalDynamicScreenRegistry provides registry,
85
- LocalMaxApi provides maxApi,
86
- AppTheme provides theme.value,
87
- AppStatusBar provides statusBarHeight,
88
- AppNavigationBar provides navigationBarHeight,
89
- LocalContext provides mergedContext,
90
- LocalLocalize provides resolvedLocalize,
91
- LocalStackTransition provides stackTransition,
92
- ) {
93
- LaunchedEffect(Unit) {
94
- setNavigator?.invoke(navigator)
95
- }
96
-
97
- // Gesture pops never reach Navigator.dismissScreen(), so prune the registry from
98
- // the back stack itself. collectLatest lets a rapid re-push cancel a pending prune.
99
- LaunchedEffect(navController, registry) {
100
- navController.currentBackStack.collectLatest { entries ->
101
- delay(STACK_TRANSITION_DURATION.milliseconds)
102
- val aliveIds = entries.mapNotNullTo(mutableSetOf()) { entry ->
103
- runCatching { entry.toRoute<DynamicScreenRoute>().id }.getOrNull()
104
- ?: runCatching { entry.toRoute<DynamicDialogRoute>().id }.getOrNull()
105
- }
106
- aliveIds.add(screenId)
107
- registry.retainOnly(aliveIds)
108
- }
109
- }
110
-
111
- // The transitions must live on the NavHost call site, not on the destinations:
112
- // NavHost.uikit computes isDefaultTransition from these arguments alone, and while
113
- // it is true the library paints its own hard-coded black iOS blackout that would
114
- // sit over the pink scrim.
115
- NavHost(
116
- navController,
117
- startDestination = startDestination,
118
- enterTransition = { stackEnter() },
119
- exitTransition = { stackExit() },
120
- popEnterTransition = { stackPopEnter() },
121
- popExitTransition = { stackPopExit() },
122
- ) {
123
- composable<DynamicScreenRoute> { backStackEntry ->
124
- val route = backStackEntry.toRoute<DynamicScreenRoute>()
125
- val screen = registry.getScreen(route.id)
126
-
127
- if (screen != null) {
128
- StackTransitionDecor(id = route.id, isStackRoute = true) {
129
- StackScreen(
130
- id = route.id,
131
- name = screen.name,
132
- content = screen.content,
133
- navigationOptions = screen.options
134
- )
135
- }
136
- }
137
- }
138
-
139
- composable<DynamicDialogRoute>(
140
- enterTransition = {
141
- slideInVertically(
142
- animationSpec = tween(300),
143
- initialOffsetY = { it }
144
- )
145
- },
146
- exitTransition = null,
147
- popEnterTransition = { fadeIn(animationSpec = tween(0)) },
148
- popExitTransition = {
149
- slideOutVertically(
150
- animationSpec = tween(300),
151
- targetOffsetY = { it }
152
- )
153
- }
154
- ) { backStackEntry ->
155
- val route = backStackEntry.toRoute<DynamicDialogRoute>()
156
- val screen = registry.getScreen(route.id)
157
-
158
- if (screen != null) {
159
- StackTransitionDecor(id = route.id, isStackRoute = false) {
160
- StackScreen(
161
- id = route.id,
162
- name = screen.name,
163
- content = screen.content,
164
- navigationOptions = screen.options,
165
- isDialogRoute = true
166
- )
167
- }
168
- }
169
- }
170
- }
171
- }
172
- }
173
- }
174
-
175
- DisposableEffect(Unit) {
176
- onDispose {
177
- navigator.dispose()
178
- registry.unregisterScreen(screenId)
179
- }
180
- }
181
- }
182
-
183
- val LocalMaxApi = staticCompositionLocalOf<IMaxApi?> { null }
184
-
185
- private fun parseLanguage(raw: Map<String, Any?>?): String? {
186
- val code = (raw?.get("response") as? String)?.trim()?.lowercase()
187
- if (code.isNullOrEmpty()) return null
188
- return if (code.startsWith(Localize.EN)) Localize.EN else Localize.VI
189
- }
190
-
@@ -1,406 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.foundation.layout.Box
4
- import androidx.compose.foundation.layout.fillMaxSize
5
- import androidx.compose.runtime.Composable
6
- import androidx.compose.runtime.MutableState
7
- import androidx.compose.runtime.collectAsState
8
- import androidx.compose.runtime.getValue
9
- import androidx.compose.runtime.mutableStateOf
10
- import androidx.compose.runtime.staticCompositionLocalOf
11
- import androidx.compose.ui.Alignment
12
- import androidx.compose.ui.Modifier
13
- import androidx.navigation.NavBackStackEntry
14
- import androidx.navigation.NavController
15
- import androidx.navigation.NavGraph
16
- import androidx.navigation.toRoute
17
- import kotlinx.coroutines.CoroutineScope
18
- import kotlinx.coroutines.Dispatchers
19
- import kotlinx.coroutines.SupervisorJob
20
- import kotlinx.coroutines.cancel
21
- import kotlinx.coroutines.delay
22
- import kotlinx.coroutines.launch
23
- import kotlinx.serialization.Serializable
24
- import vn.momo.kits.components.PopupAction
25
- import vn.momo.kits.components.PopupNotify
26
- import vn.momo.kits.navigation.BottomHeader.*
27
- import vn.momo.kits.navigation.component.SnackBar
28
- import vn.momo.maxapi.IMaxApi
29
- import kotlin.time.Duration.Companion.milliseconds
30
-
31
- class Navigator(
32
- private val navController: NavController,
33
- private val maxApi: IMaxApi?,
34
- private val registry: DynamicScreenRegistry
35
- ) {
36
- private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
37
-
38
- private fun currentScreen(): DynamicScreen? {
39
- val entry = navController.currentBackStackEntry ?: return null
40
- val id = runCatching { entry.toRoute<DynamicScreenRoute>().id }.getOrNull()
41
- ?: runCatching { entry.toRoute<DynamicDialogRoute>().id }.getOrNull()
42
- ?: return null
43
- return registry.getScreen(id)
44
- }
45
-
46
- fun push(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
47
- val route = registry.register(screenName, content, options)
48
- navController.navigate(DynamicScreenRoute(route.id))
49
- }
50
-
51
- fun replace(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
52
- val replacedScreen = currentScreen()
53
- replacedScreen?.let { registry.unregisterScreen(it.id) }
54
- navController.popBackStack()
55
- push(screenName, content, options)
56
- }
57
-
58
- fun onBackSafe(callBack: (() -> Unit)? = null) {
59
- val currentScreen = currentScreen()
60
- val options = currentScreen?.options
61
- if (options?.onBackHandler != null) {
62
- options.onBackHandler.invoke()
63
- return
64
- }
65
- val preventBack = options?.headerBackProps?.preventBack
66
- if (preventBack != null) {
67
- showModal(
68
- content = {
69
- PopupNotify(preventBack.copy(
70
- primary = PopupAction(
71
- title = preventBack.primary?.title ?: "",
72
- onPress = {
73
- preventBack.primary?.onPress?.invoke()
74
- pop(2, callBack) // close popup notify, then close the screen
75
- }
76
- )
77
- ))
78
- }
79
- )
80
- } else {
81
- pop(1, callBack)
82
- }
83
- }
84
-
85
- // The swipe-back opt-out predicate in StackScreen needs this: the root screen must keep custom
86
- // back handling so it still reaches maxApi.dismiss. It has to read currentBackStack rather than
87
- // previousBackStackEntry — the latter walks a plain ArrayDeque, so a composable reading it never
88
- // recomposes and the predicate would freeze at the value it had on first composition.
89
- @Composable
90
- internal fun rememberCanPop(): Boolean {
91
- val backStack by navController.currentBackStack.collectAsState()
92
- return backStack.hasPoppableEntry()
93
- }
94
-
95
- /**
96
- * Non-composable twin of [rememberCanPop] for the iOS host: its own back check is mount-based
97
- * and so answers `true` even on the mini-app root, where a swipe must dismiss rather than pop.
98
- * Reads the StateFlow value directly — correct here precisely because nothing recomposes on it.
99
- */
100
- fun canPop(): Boolean = navController.currentBackStack.value.hasPoppableEntry()
101
-
102
- fun pop(count: Int = 1, callBack: (() -> Unit)? = null) {
103
- scope.launch {
104
- repeat(count) {
105
- if (OverplayComponentRegistry.getOverplayType() == OverplayComponentType.SNACK_BAR){
106
- dismissScreen()
107
- OverplayComponentRegistry.hardClearAfterDismiss()
108
- }
109
- else if (OverplayComponentRegistry.isOverplayShowing()){
110
- dismissOverplay()
111
- } else {
112
- dismissScreen()
113
- }
114
- }
115
- callBack?.invoke()
116
- }
117
- }
118
- private suspend fun dismissOverplay(isDelay: Boolean = true) {
119
- OverplayComponentRegistry.clear()
120
- if (isDelay) delay(300L.milliseconds)
121
- OverplayComponentRegistry.hardClearAfterDismiss()
122
- }
123
-
124
- private suspend fun dismissScreen() {
125
- if (navController.previousBackStackEntry != null){
126
- val dismissing = currentScreen()
127
- navController.popBackStack()
128
- delay(STACK_TRANSITION_DURATION.milliseconds)
129
- dismissing?.let { registry.unregisterScreen(it.id) }
130
- } else {
131
- maxApi?.dismiss { }
132
- }
133
- }
134
-
135
- fun present(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
136
- val route = registry.register(screenName, content, options)
137
- navController.navigate(DynamicDialogRoute(route.id))
138
- }
139
-
140
- fun reset(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
141
- registry.unregisterAll()
142
-
143
- val route = registry.register(screenName, content, options)
144
- navController.navigate(DynamicScreenRoute(route.id)) {
145
- popUpTo(0) { inclusive = true }
146
- }
147
- }
148
-
149
- fun showModal(
150
- content: @Composable () -> Unit,
151
- barrierDismissible: Boolean = true,
152
- onDismiss: (() -> Unit)? = null
153
- ){
154
- val id = currentScreen()?.id ?: -1
155
- OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.MODAL, false, barrierDismissible, onDismiss)
156
- }
157
-
158
- fun showBottomSheet(
159
- content: @Composable () -> Unit,
160
- isSurface: Boolean = false,
161
- barrierDismissible: Boolean = true,
162
- onDismiss: (() -> Unit)? = null,
163
- bottomSheetHeader: BottomHeader? = null,
164
- draggable: Boolean = true,
165
- useBottomInset: Boolean = true,
166
- footerComponent: (@Composable () -> Unit)? = null
167
- ){
168
- val id = currentScreen()?.id ?: -1
169
- OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.BOTTOM_SHEET, isSurface, barrierDismissible, onDismiss, bottomSheetHeader, draggable, useBottomInset, footerComponent)
170
- }
171
-
172
- fun showSnackBar(snackBar: SnackBar, onDismiss: (() -> Unit)? = null) {
173
- val id = currentScreen()?.id ?: -1
174
- scope.launch {
175
- OverplayComponentRegistry.registerOverplay(
176
- id = id,
177
- content = {
178
- SnackBar(snackBar, onDismiss)
179
- },
180
- type = OverplayComponentType.SNACK_BAR,
181
- onDismiss = onDismiss
182
- )
183
- }
184
- }
185
-
186
- fun hideSnackBar() {
187
- scope.launch {
188
- OverplayComponentRegistry.clear()
189
- delay(250L.milliseconds)
190
- OverplayComponentRegistry.hardClearAfterDismiss()
191
- }
192
- }
193
-
194
- fun dispose(){
195
- scope.cancel()
196
- }
197
- }
198
-
199
- // The NavGraph itself sits in the back stack as an entry; only real destinations can be popped.
200
- private fun List<NavBackStackEntry>.hasPoppableEntry(): Boolean =
201
- count { it.destination !is NavGraph } > 1
202
-
203
- val LocalNavigator = staticCompositionLocalOf<Navigator> {
204
- error("No NavigationStack provided")
205
- }
206
-
207
- @Serializable
208
- data class DynamicScreenRoute(val id: Int)
209
-
210
- @Serializable
211
- data class DynamicDialogRoute(val id: Int)
212
-
213
- data class DynamicScreen(
214
- val id: Int,
215
- val name: String,
216
- val content: @Composable () -> Unit,
217
- var options: NavigationOptions? = null
218
- )
219
-
220
- class DynamicScreenRegistry {
221
- private val screens = mutableMapOf<Int, DynamicScreen>()
222
-
223
- fun register(screenName: String, content: @Composable () -> Unit, options: NavigationOptions?): DynamicScreenRoute {
224
- val id = nextGlobalId()
225
- screens[id] = DynamicScreen(
226
- id = id,
227
- name = screenName,
228
- content = content,
229
- options = options
230
- )
231
- return DynamicScreenRoute(id)
232
- }
233
-
234
- fun nextId(): Int = nextGlobalId()
235
-
236
- fun bind(id: Int, screenName: String, content: @Composable () -> Unit, options: NavigationOptions?) {
237
- screens[id] = DynamicScreen(
238
- id = id,
239
- name = screenName,
240
- content = content,
241
- options = options
242
- )
243
- advanceGlobalIdPast(id) // keep counter ahead after process-death restore
244
- }
245
-
246
- fun unregisterScreen(id: Int) {
247
- screens.remove(id)
248
- }
249
-
250
- // Gesture pops go through composeNavigator.popBackStack() inside NavHost and never reach
251
- // Navigator.dismissScreen(), so screens would leak without a stack-driven prune.
252
- fun retainOnly(ids: Set<Int>) {
253
- val stale = screens.keys.filter { it !in ids }
254
- for (id in stale) {
255
- screens.remove(id)
256
- }
257
- }
258
-
259
- fun unregisterAll(){
260
- val ids = screens.keys.toList()
261
- for (id in ids) {
262
- unregisterScreen(id)
263
- }
264
- }
265
-
266
- fun getScreen(id: Int): DynamicScreen? = screens[id]
267
-
268
- fun setOptions(id: Int, options: NavigationOptions){
269
- screens[id]?.options = options
270
- }
271
-
272
- companion object {
273
- private var globalIdCounter = 1
274
-
275
- private fun nextGlobalId(): Int = globalIdCounter++
276
-
277
- private fun advanceGlobalIdPast(id: Int) {
278
- if (id >= globalIdCounter) globalIdCounter = id + 1
279
- }
280
- }
281
- }
282
-
283
- val LocalDynamicScreenRegistry = staticCompositionLocalOf<DynamicScreenRegistry?> { null }
284
-
285
-
286
- sealed class OverplayComponentParams {
287
- class Modal(
288
- val onDismiss: (() -> Unit)? = null,
289
- val barrierDismissible: Boolean = true
290
- ) : OverplayComponentParams()
291
-
292
- class BottomSheet(
293
- val isSurface: Boolean = false,
294
- val onDismiss: (() -> Unit)? = null,
295
- val barrierDismissible: Boolean = true,
296
- val bottomSheetHeader: BottomHeader? = null,
297
- val draggable: Boolean = true,
298
- val useBottomInset: Boolean = true,
299
- val footerComponent: (@Composable () -> Unit)? = null,
300
- ) : OverplayComponentParams()
301
-
302
- class SnackBar : OverplayComponentParams()
303
- }
304
-
305
- data class OverplayComponent(
306
- val id: Int,
307
- val type: OverplayComponentType? = null,
308
- val content: @Composable () -> Unit,
309
- val params: OverplayComponentParams? = null
310
- )
311
-
312
- enum class OverplayComponentType {
313
- MODAL, BOTTOM_SHEET, SNACK_BAR
314
- }
315
-
316
- object OverplayComponentRegistry {
317
- private var currentOverlayComponent : MutableState<OverplayComponent?> = mutableStateOf(null)
318
- private var requestClose: (() -> Unit)? = null
319
- internal fun bindClose(handler: (() -> Unit)?) {
320
- requestClose = handler
321
- }
322
-
323
- fun registerOverplay(
324
- id: Int,
325
- content: @Composable () -> Unit,
326
- type: OverplayComponentType,
327
- isSurface: Boolean = false,
328
- barrierDismissible: Boolean = true,
329
- onDismiss: (() -> Unit)?,
330
- bottomSheetHeader: BottomHeader? = null,
331
- draggable: Boolean = true,
332
- useBottomInset: Boolean = true,
333
- footerComponent: (@Composable () -> Unit)? = null,
334
- ){
335
- val params = when(type){
336
- OverplayComponentType.MODAL -> OverplayComponentParams.Modal(onDismiss, barrierDismissible)
337
- OverplayComponentType.BOTTOM_SHEET -> OverplayComponentParams.BottomSheet(isSurface, onDismiss, barrierDismissible, bottomSheetHeader, draggable, useBottomInset, footerComponent)
338
- OverplayComponentType.SNACK_BAR -> OverplayComponentParams.SnackBar()
339
- }
340
-
341
- currentOverlayComponent.value = OverplayComponent(
342
- id = id,
343
- type = type,
344
- content = content,
345
- params = params
346
- )
347
- }
348
-
349
- fun isOverplayShowing(): Boolean = currentOverlayComponent.value != null
350
-
351
- fun getOverplayType(): OverplayComponentType? = currentOverlayComponent.value?.type
352
-
353
- fun currentRootId(): Int? = currentOverlayComponent.value?.id
354
-
355
- fun clear(){
356
- if (requestClose != null) {
357
- requestClose?.invoke()
358
- } else {
359
- currentOverlayComponent.value = null
360
- }
361
- }
362
-
363
- internal fun hardClearAfterDismiss() {
364
- currentOverlayComponent.value = null
365
- requestClose = null
366
- }
367
-
368
- @Composable
369
- internal fun OverlayComponent(){
370
- val overplay = currentOverlayComponent.value ?: return
371
-
372
- when (val params = overplay.params) {
373
- is OverplayComponentParams.BottomSheet -> {
374
- BottomSheet(
375
- content = overplay.content,
376
- header = params.bottomSheetHeader ?: Title(),
377
- isSurface = params.isSurface,
378
- barrierDismissible = params.barrierDismissible,
379
- draggable = params.draggable,
380
- useBottomInset = params.useBottomInset,
381
- footerComponent = params.footerComponent,
382
- onDismiss = params.onDismiss
383
- )
384
- }
385
-
386
- is OverplayComponentParams.Modal -> {
387
- ModalScreen(
388
- content = overplay.content,
389
- barrierDismissible = params.barrierDismissible,
390
- onDismiss = params.onDismiss
391
- )
392
- }
393
-
394
- is OverplayComponentParams.SnackBar -> {
395
- Box(
396
- modifier = Modifier.fillMaxSize(),
397
- contentAlignment = Alignment.BottomCenter
398
- ) {
399
- overplay.content()
400
- }
401
- }
402
-
403
- null -> {}
404
- }
405
- }
406
- }
@@ -1,8 +0,0 @@
1
- package vn.momo.kits.navigation
2
-
3
- import androidx.compose.runtime.Composable
4
-
5
- @Composable
6
- internal expect fun RegisterScrollToTop(callback: (() -> Unit)?)
7
-
8
-