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

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