@momo-kits/native-kits 0.300.4-debug → 0.300.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 (139) 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 -20
  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 -110
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +0 -62
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -1071
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -65
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -81
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -357
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -115
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -86
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -478
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -265
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -235
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -265
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -308
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -330
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -207
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -92
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -560
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -86
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -501
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -236
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -180
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -17
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -194
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -287
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -306
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -116
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -215
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -404
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -732
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -40
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -233
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -217
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +0 -97
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -377
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -455
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -103
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -106
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -413
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -39
  108. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
  109. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HelpCenterRequest.kt +0 -37
  110. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -192
  111. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -133
  112. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/ToolkitRequests.kt +0 -41
  113. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -135
  114. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
  115. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
  116. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  117. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +0 -20
  119. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
  120. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
  121. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
  122. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
  123. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/HelpCenterRequestTest.kt +0 -44
  124. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/ToolkitRequestsTest.kt +0 -62
  125. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -42
  126. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
  127. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -36
  128. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
  129. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  130. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
  131. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +0 -138
  132. package/gradle/libs.versions.toml +0 -68
  133. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  134. package/gradle/wrapper/gradle-wrapper.properties +0 -8
  135. package/gradle.properties +0 -26
  136. package/gradlew +0 -252
  137. package/gradlew.bat +0 -94
  138. package/ios-demo/README.md +0 -120
  139. 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.DesignSystemKits
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 ?: DesignSystemKits.getConfigs().designSystemConfig
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
-