@momo-kits/native-kits 0.168.1-beta.4-debug → 0.168.1-beta.4

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