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