@momo-kits/native-kits 0.162.2-test.2-debug → 0.162.2-test.21-debug

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/.claude/settings.local.json +14 -0
  2. package/CLAUDE.md +78 -0
  3. package/compose/build.gradle.kts +12 -8
  4. package/compose/build.gradle.kts.backup +15 -11
  5. package/compose/compose.podspec +1 -1
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +2 -7
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +6 -10
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +6 -24
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +4 -19
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +1 -10
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +2 -2
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +1 -1
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +1 -1
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +6 -17
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +1 -1
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +6 -101
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +5 -19
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +8 -33
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +1 -1
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +22 -25
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +27 -29
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +3 -26
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +30 -31
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +3 -13
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -5
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +3 -11
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +3 -18
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +11 -11
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +3 -11
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +69 -26
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -5
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +84 -59
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +13 -71
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +10 -51
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +6 -1
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +4 -21
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +1 -1
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +16 -75
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +5 -26
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +1 -1
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +3 -11
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +16 -40
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +3 -9
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -28
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +2 -13
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +17 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +4 -10
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +3 -28
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +8 -47
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +4 -25
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +2 -11
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +1 -17
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +1 -6
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +50 -111
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +1 -5
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +1 -5
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +1 -12
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +4 -96
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +5 -7
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +2 -12
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +9 -21
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +2 -2
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +9 -14
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +8 -11
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +1 -1
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +7 -27
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +1 -1
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -5
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -2
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +94 -4
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -2
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +2 -2
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +3 -7
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +12 -16
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +33 -27
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +48 -33
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +91 -63
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +9 -14
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +13 -31
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +1 -1
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +6 -24
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +11 -6
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +2 -1
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +9 -6
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +1 -1
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +3 -13
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +3 -2
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +1 -40
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -2
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -8
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +2 -17
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +1 -1
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +3 -3
  95. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +2 -3
  96. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -2
  97. package/gradle/libs.versions.toml +2 -0
  98. package/gradle.properties +1 -1
  99. package/ios/Application/Components.swift +1 -0
  100. package/ios/Application/FloatingButton.swift +1 -0
  101. package/ios/Badge/Badge.swift +1 -0
  102. package/ios/Badge/BadgeRibbon.swift +2 -0
  103. package/ios/Button/Button.swift +1 -1
  104. package/ios/Checkbox/Checkbox.swift +1 -0
  105. package/ios/Information/Information.swift +151 -0
  106. package/ios/Input/Input.swift +1 -0
  107. package/ios/Input/InputPhoneNumber.swift +1 -0
  108. package/ios/Input/InputSearch.swift +3 -0
  109. package/ios/Input/InputTextArea.swift +1 -0
  110. package/ios/OTPKeyboard/KeyboardButton.swift +1 -1
  111. package/ios/Popup/PopupDisplay.swift +6 -0
  112. package/ios/Popup/PopupInput.swift +2 -0
  113. package/ios/Template/TrustBanner/TrustBanner.swift +2 -0
  114. package/ios/Typography/Text.swift +1 -0
  115. package/ios/Typography/Typography.swift +14 -5
  116. package/local.properties +2 -2
  117. package/package.json +1 -1
  118. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FontScaleStore.kt +0 -77
  119. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +0 -32
  120. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  121. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  122. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +0 -16
  123. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  124. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  125. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  126. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  127. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  128. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  129. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  130. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +0 -58
  131. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +0 -58
  132. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -60
@@ -10,11 +10,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
10
10
  import androidx.compose.foundation.layout.Box
11
11
  import androidx.compose.foundation.layout.fillMaxSize
12
12
  import androidx.compose.foundation.layout.wrapContentSize
13
- import androidx.compose.runtime.Composable
14
- import androidx.compose.runtime.DisposableEffect
15
- import androidx.compose.runtime.LaunchedEffect
16
- import androidx.compose.runtime.remember
17
- import androidx.compose.runtime.rememberCoroutineScope
13
+ import androidx.compose.runtime.*
18
14
  import androidx.compose.ui.Alignment
19
15
  import androidx.compose.ui.Modifier
20
16
  import androidx.compose.ui.draw.alpha
@@ -22,7 +18,7 @@ import androidx.compose.ui.draw.scale
22
18
  import androidx.compose.ui.graphics.Color
23
19
  import androidx.compose.ui.unit.dp
24
20
  import kotlinx.coroutines.launch
25
- import vn.momo.kits.application.ApplicationContext
21
+ import vn.momo.kits.application.LocalContext
26
22
  import vn.momo.kits.application.IsShowBaseLineDebug
27
23
  import vn.momo.kits.const.Colors
28
24
  import vn.momo.kits.modifier.conditional
@@ -44,7 +40,7 @@ internal fun ModalScreen(
44
40
  val coroutineScope = rememberCoroutineScope()
45
41
 
46
42
  val maxApi = LocalMaxApi.current
47
- val context = ApplicationContext.current
43
+ val context = LocalContext.current
48
44
 
49
45
  DisposableEffect(Unit) {
50
46
  ScreenTracker.trackPopupDisplayed(
@@ -1,29 +1,25 @@
1
1
  package vn.momo.kits.navigation
2
2
 
3
3
  import androidx.compose.foundation.gestures.ScrollableState
4
- import androidx.compose.runtime.Composable
5
- import androidx.compose.runtime.Stable
6
- import androidx.compose.runtime.State
7
- import androidx.compose.runtime.mutableStateOf
8
- import androidx.compose.runtime.staticCompositionLocalOf
4
+ import androidx.compose.runtime.*
9
5
  import androidx.compose.ui.graphics.Color
10
6
  import vn.momo.kits.navigation.bottomtab.setBottomTabOption
11
- import vn.momo.kits.navigation.component.FloatingButtonProps
12
- import vn.momo.kits.navigation.component.HeaderBackProps
13
- import vn.momo.kits.navigation.component.HeaderRight
14
- import vn.momo.kits.navigation.component.HeaderTitle
15
- import vn.momo.kits.navigation.component.HeaderType
16
- import vn.momo.kits.navigation.component.TitlePosition
7
+ import vn.momo.kits.navigation.component.*
17
8
 
18
9
  class Navigation(
19
10
  val id: Int = -1,
20
11
  val bottomTabIndex: Int = -1,
21
- val initOptions: NavigationOptions? = null
12
+ val initOptions: NavigationOptions? = null,
13
+ private val registry: DynamicScreenRegistry? = null
22
14
  ) {
23
15
  private val _options = mutableStateOf(initOptions ?: NavigationOptions())
24
16
  val currentOptions: State<NavigationOptions> get() = _options
25
17
  val options: NavigationOptions get() = _options.value
26
18
 
19
+ private val _isBottomTabRoot = mutableStateOf(false)
20
+ internal val isBottomTabRoot: Boolean get() = _isBottomTabRoot.value
21
+ internal fun markAsBottomTabRoot() { _isBottomTabRoot.value = true }
22
+
27
23
  fun setOptions(
28
24
  onBackHandler: (() -> Unit)? = null,
29
25
  hiddenBack: Boolean? = null,
@@ -65,7 +61,7 @@ class Navigation(
65
61
  private fun updateOptions(updated: NavigationOptions) {
66
62
  _options.value = updated
67
63
  if (bottomTabIndex != -1) setBottomTabOption(bottomTabIndex, updated)
68
- if (id != -1) DynamicScreenRegistry.setOptions(id, updated)
64
+ if (id != -1) registry?.setOptions(id, updated)
69
65
  }
70
66
  }
71
67
 
@@ -89,14 +85,14 @@ data class NavigationOptions(
89
85
  val footerComponent: @Composable (() -> Unit)? = null,
90
86
  val floatingButtonProps: FloatingButtonProps? = null,
91
87
  val keyboardOptions: KeyboardOptions = KeyboardOptions()
92
- )
88
+ ) {
89
+ internal fun hasFooter() : Boolean = footerComponent != null
90
+ }
93
91
 
94
92
  data class KeyboardOptions(
95
93
  val keyboardShouldPersistTaps: Boolean = false,
96
94
  val useAvoidKeyboard: Boolean = true
97
95
  )
98
-
99
-
100
96
  data class ScrollData(
101
97
  val scrollable: Boolean = true,
102
98
  val scrollState: ScrollableState? = null,
@@ -22,53 +22,52 @@ fun NavigationContainer(
22
22
  initialScreen: @Composable () -> Unit,
23
23
  options: NavigationOptions? = null,
24
24
  initialTheme: Theme = defaultTheme,
25
- applicationContext: MiniAppContext? = null,
25
+ miniAppContext: MiniAppContext? = null,
26
26
  maxApi: IMaxApi? = null,
27
27
  setNavigator: ((Navigator) -> Unit)? = null,
28
28
  statusBarHeight: Dp? = null,
29
- config: KitConfig? = null,
30
- language: String? = null
29
+ localize: Localize? = null,
31
30
  ){
32
31
  val navController = rememberNavController()
33
- val navigator = remember { Navigator(navController = navController, maxApi = maxApi) }
32
+ val registry = remember { DynamicScreenRegistry() }
33
+ val navigator = remember { Navigator(navController = navController, maxApi = maxApi, registry = registry) }
34
34
  val statusBarHeight = statusBarHeight ?: getAppStatusBarHeight()
35
35
  val navigationBarHeight = getNavigationBarHeight()
36
36
 
37
- val parentContext = ApplicationContext.current
38
- val mergedContext = MiniAppContext.merge(parentContext, applicationContext)
39
-
40
- LaunchedEffect(maxApi) {
41
- maxApi?.let { FontScaleStore.start(it) }
42
- }
37
+ val parentContext = LocalContext.current
38
+ val mergedContext = MiniAppContext.merge(parentContext, miniAppContext)
43
39
 
44
40
  val theme = remember { mutableStateOf(initialTheme) }
41
+ val fallbackLocalize = remember { Localize() }
45
42
 
46
- LaunchedEffect(Unit) {
47
- val headerBar = config?.headerBar
48
- if (headerBar != null && theme.value.assets.headerBackground == null) {
49
- theme.value = theme.value.copy(
50
- assets = ThemeAssets(
51
- headerBackground = headerBar
52
- )
53
- )
43
+ val resolvedLocalize = localize ?: fallbackLocalize
44
+
45
+ LaunchedEffect(maxApi, resolvedLocalize) {
46
+ val api = maxApi ?: return@LaunchedEffect
47
+ runCatching {
48
+ api.getLanguage(callback = { data ->
49
+ if (parseLanguage(data) == Localize.EN) {
50
+ resolvedLocalize.changeLanguage(Localize.EN)
51
+ }
52
+ })
54
53
  }
55
54
  }
56
55
 
57
- val screenId = rememberSaveable { DynamicScreenRegistry.nextId() }
58
- DynamicScreenRegistry.bind(screenId, initialScreenName, initialScreen, options)
56
+ val screenId = rememberSaveable { registry.nextId() }
57
+ registry.bind(screenId, initialScreenName, initialScreen, options)
59
58
  val startDestination = remember(screenId) { DynamicScreenRoute(screenId) }
60
59
 
61
60
  ProvideNavigationEventDispatcherOwner {
62
61
  CompositionLocalProvider(
63
62
  LocalNavigator provides navigator,
63
+ LocalDynamicScreenRegistry provides registry,
64
64
  LocalMaxApi provides maxApi,
65
65
  AppTheme provides theme.value,
66
66
  AppStatusBar provides statusBarHeight,
67
67
  AppNavigationBar provides navigationBarHeight,
68
- ApplicationContext provides mergedContext,
69
- AppConfig provides config,
70
- AppLanguage provides language,
71
- ) {
68
+ LocalContext provides mergedContext,
69
+ LocalLocalize provides resolvedLocalize,
70
+ ) {
72
71
  LaunchedEffect(Unit) {
73
72
  setNavigator?.invoke(navigator)
74
73
  }
@@ -91,7 +90,7 @@ fun NavigationContainer(
91
90
  }
92
91
  ) { backStackEntry ->
93
92
  val route = backStackEntry.toRoute<DynamicScreenRoute>()
94
- val screen = DynamicScreenRegistry.getScreen(route.id)
93
+ val screen = registry.getScreen(route.id)
95
94
 
96
95
  if (screen != null) {
97
96
  StackScreen(
@@ -120,7 +119,7 @@ fun NavigationContainer(
120
119
  }
121
120
  ) { backStackEntry ->
122
121
  val route = backStackEntry.toRoute<DynamicDialogRoute>()
123
- val screen = DynamicScreenRegistry.getScreen(route.id)
122
+ val screen = registry.getScreen(route.id)
124
123
 
125
124
  if (screen != null) {
126
125
  StackScreen(
@@ -138,7 +137,7 @@ fun NavigationContainer(
138
137
  DisposableEffect(Unit) {
139
138
  onDispose {
140
139
  navigator.dispose()
141
- DynamicScreenRegistry.unregisterScreen(screenId)
140
+ registry.unregisterScreen(screenId)
142
141
  }
143
142
  }
144
143
  }
@@ -146,3 +145,10 @@ fun NavigationContainer(
146
145
  val LocalMaxApi = staticCompositionLocalOf<IMaxApi?> {
147
146
  error("No MaxApi provided")
148
147
  }
148
+
149
+ private fun parseLanguage(raw: Map<String, Any?>?): String? {
150
+ val code = (raw?.get("response") as? String)?.trim()?.lowercase()
151
+ if (code.isNullOrEmpty()) return null
152
+ return if (code.startsWith(Localize.EN)) Localize.EN else Localize.VI
153
+ }
154
+
@@ -9,6 +9,7 @@ import androidx.compose.runtime.staticCompositionLocalOf
9
9
  import androidx.compose.ui.Alignment
10
10
  import androidx.compose.ui.Modifier
11
11
  import androidx.navigation.NavController
12
+ import androidx.navigation.toRoute
12
13
  import kotlinx.coroutines.CoroutineScope
13
14
  import kotlinx.coroutines.Dispatchers
14
15
  import kotlinx.coroutines.SupervisorJob
@@ -21,30 +22,38 @@ import vn.momo.kits.components.PopupNotify
21
22
  import vn.momo.kits.navigation.BottomHeader.*
22
23
  import vn.momo.kits.navigation.component.SnackBar
23
24
  import vn.momo.maxapi.IMaxApi
25
+ import kotlin.time.Duration.Companion.milliseconds
24
26
 
25
27
  class Navigator(
26
28
  private val navController: NavController,
27
- private val maxApi: IMaxApi?
29
+ private val maxApi: IMaxApi?,
30
+ private val registry: DynamicScreenRegistry
28
31
  ) {
29
32
  private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
30
33
 
34
+ private fun currentScreen(): DynamicScreen? {
35
+ val entry = navController.currentBackStackEntry ?: return null
36
+ val id = runCatching { entry.toRoute<DynamicScreenRoute>().id }.getOrNull()
37
+ ?: runCatching { entry.toRoute<DynamicDialogRoute>().id }.getOrNull()
38
+ ?: return null
39
+ return registry.getScreen(id)
40
+ }
41
+
31
42
  fun push(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
32
- val route = DynamicScreenRegistry.register(screenName, content, options)
43
+ val route = registry.register(screenName, content, options)
33
44
  navController.navigate(DynamicScreenRoute(route.id))
34
45
  }
35
46
 
36
47
  fun replace(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
37
- if (navController.previousBackStackEntry != null){
38
- val latestScreen = DynamicScreenRegistry.getLatestScreen()
39
- latestScreen?.let { DynamicScreenRegistry.unregisterScreen(it.id) }
40
- navController.popBackStack()
41
- push(screenName, content, options)
42
- }
48
+ val replacedScreen = currentScreen()
49
+ replacedScreen?.let { registry.unregisterScreen(it.id) }
50
+ navController.popBackStack()
51
+ push(screenName, content, options)
43
52
  }
44
53
 
45
54
  fun onBackSafe(callBack: (() -> Unit)? = null) {
46
- val latestScreen = DynamicScreenRegistry.getLatestScreen()
47
- val options = latestScreen?.options
55
+ val currentScreen = currentScreen()
56
+ val options = currentScreen?.options
48
57
  if (options?.onBackHandler != null) {
49
58
  options.onBackHandler.invoke()
50
59
  return
@@ -87,31 +96,30 @@ class Navigator(
87
96
  }
88
97
  private suspend fun dismissOverplay(isDelay: Boolean = true) {
89
98
  OverplayComponentRegistry.clear()
90
- if (isDelay) delay(300L)
99
+ if (isDelay) delay(300L.milliseconds)
91
100
  OverplayComponentRegistry.hardClearAfterDismiss()
92
101
  }
93
102
 
94
103
  private suspend fun dismissScreen() {
95
104
  if (navController.previousBackStackEntry != null){
105
+ val dismissing = currentScreen()
96
106
  navController.popBackStack()
97
- delay(300L)
98
- DynamicScreenRegistry.getLatestScreen()?.let { it1 ->
99
- DynamicScreenRegistry.unregisterScreen(it1.id)
100
- }
107
+ delay(300L.milliseconds)
108
+ dismissing?.let { registry.unregisterScreen(it.id) }
101
109
  } else {
102
110
  maxApi?.dismiss { }
103
111
  }
104
112
  }
105
113
 
106
114
  fun present(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
107
- val route = DynamicScreenRegistry.register(screenName, content, options)
115
+ val route = registry.register(screenName, content, options)
108
116
  navController.navigate(DynamicDialogRoute(route.id))
109
117
  }
110
118
 
111
119
  fun reset(screenName: String, content: @Composable () -> Unit, options: NavigationOptions? = null) {
112
- DynamicScreenRegistry.unregisterAll()
120
+ registry.unregisterAll()
113
121
 
114
- val route = DynamicScreenRegistry.register(screenName, content, options)
122
+ val route = registry.register(screenName, content, options)
115
123
  navController.navigate(DynamicScreenRoute(route.id)) {
116
124
  popUpTo(0) { inclusive = true }
117
125
  }
@@ -122,7 +130,7 @@ class Navigator(
122
130
  barrierDismissible: Boolean = true,
123
131
  onDismiss: (() -> Unit)? = null
124
132
  ){
125
- val id = DynamicScreenRegistry.getLatestScreen()?.id ?: -1
133
+ val id = currentScreen()?.id ?: -1
126
134
  OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.MODAL, false, barrierDismissible, onDismiss)
127
135
  }
128
136
 
@@ -133,12 +141,12 @@ class Navigator(
133
141
  onDismiss: (() -> Unit)? = null,
134
142
  bottomSheetHeader: BottomHeader? = null
135
143
  ){
136
- val id = DynamicScreenRegistry.getLatestScreen()?.id ?: -1
144
+ val id = currentScreen()?.id ?: -1
137
145
  OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.BOTTOM_SHEET, isSurface, barrierDismissible, onDismiss, bottomSheetHeader)
138
146
  }
139
147
 
140
148
  fun showSnackBar(snackBar: SnackBar, onDismiss: (() -> Unit)? = null) {
141
- val id = DynamicScreenRegistry.getLatestScreen()?.id ?: -1
149
+ val id = currentScreen()?.id ?: -1
142
150
  scope.launch {
143
151
  OverplayComponentRegistry.registerOverplay(
144
152
  id = id,
@@ -154,7 +162,7 @@ class Navigator(
154
162
  fun hideSnackBar() {
155
163
  scope.launch {
156
164
  OverplayComponentRegistry.clear()
157
- delay(250L)
165
+ delay(250L.milliseconds)
158
166
  OverplayComponentRegistry.hardClearAfterDismiss()
159
167
  }
160
168
  }
@@ -181,12 +189,11 @@ data class DynamicScreen(
181
189
  var options: NavigationOptions? = null
182
190
  )
183
191
 
184
- object DynamicScreenRegistry {
192
+ class DynamicScreenRegistry {
185
193
  private val screens = mutableMapOf<Int, DynamicScreen>()
186
- private var idCounter = 1
187
194
 
188
195
  fun register(screenName: String, content: @Composable () -> Unit, options: NavigationOptions?): DynamicScreenRoute {
189
- val id = idCounter++
196
+ val id = nextGlobalId()
190
197
  screens[id] = DynamicScreen(
191
198
  id = id,
192
199
  name = screenName,
@@ -196,7 +203,7 @@ object DynamicScreenRegistry {
196
203
  return DynamicScreenRoute(id)
197
204
  }
198
205
 
199
- fun nextId(): Int = idCounter++
206
+ fun nextId(): Int = nextGlobalId()
200
207
 
201
208
  fun bind(id: Int, screenName: String, content: @Composable () -> Unit, options: NavigationOptions?) {
202
209
  screens[id] = DynamicScreen(
@@ -205,17 +212,13 @@ object DynamicScreenRegistry {
205
212
  content = content,
206
213
  options = options
207
214
  )
208
- if (id >= idCounter) idCounter = id + 1 // keep counter ahead after process-death restore
215
+ advanceGlobalIdPast(id) // keep counter ahead after process-death restore
209
216
  }
210
217
 
211
218
  fun unregisterScreen(id: Int) {
212
219
  screens.remove(id)
213
220
  }
214
221
 
215
- fun getLatestScreen(): DynamicScreen?{
216
- return screens.entries.lastOrNull()?.value
217
- }
218
-
219
222
  fun unregisterAll(){
220
223
  val ids = screens.keys.toList()
221
224
  for (id in ids) {
@@ -228,8 +231,20 @@ object DynamicScreenRegistry {
228
231
  fun setOptions(id: Int, options: NavigationOptions){
229
232
  screens[id]?.options = options
230
233
  }
234
+
235
+ companion object {
236
+ private var globalIdCounter = 1
237
+
238
+ private fun nextGlobalId(): Int = globalIdCounter++
239
+
240
+ private fun advanceGlobalIdPast(id: Int) {
241
+ if (id >= globalIdCounter) globalIdCounter = id + 1
242
+ }
243
+ }
231
244
  }
232
245
 
246
+ val LocalDynamicScreenRegistry = staticCompositionLocalOf<DynamicScreenRegistry?> { null }
247
+
233
248
 
234
249
  sealed class OverplayComponentParams {
235
250
  class Modal(
@@ -244,7 +259,7 @@ sealed class OverplayComponentParams {
244
259
  val bottomSheetHeader: BottomHeader? = null,
245
260
  ) : OverplayComponentParams()
246
261
 
247
- class SnackBar() : OverplayComponentParams()
262
+ class SnackBar : OverplayComponentParams()
248
263
  }
249
264
 
250
265
  data class OverplayComponent(
@@ -308,7 +323,7 @@ object OverplayComponentRegistry {
308
323
  }
309
324
 
310
325
  @Composable
311
- fun OverlayComponent(){
326
+ internal fun OverlayComponent(){
312
327
  val overplay = currentOverlayComponent.value ?: return
313
328
 
314
329
  when (val params = overplay.params) {