@momo-kits/native-kits 0.120.0-navigation.4 → 0.120.0-navigation.6
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/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +6 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +3 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +1 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +1 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +4 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +7 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +34 -37
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +1 -6
- package/ios/Application/HeaderRight.swift +11 -3
- package/ios/Popup/PopupDisplay.swift +3 -3
- package/package.json +1 -1
|
@@ -35,6 +35,7 @@ import vn.momo.kits.const.AppTheme
|
|
|
35
35
|
data class HeaderRightData(
|
|
36
36
|
val useShortcut: Boolean = false,
|
|
37
37
|
val useMore: Boolean = false,
|
|
38
|
+
val useSystemTools: Boolean = true,
|
|
38
39
|
val tools: List<ToolGroup> = emptyList(),
|
|
39
40
|
val toolCallback: ((String) -> Unit)? = { _ -> }
|
|
40
41
|
)
|
|
@@ -43,14 +44,16 @@ data class Tool(
|
|
|
43
44
|
val key: String,
|
|
44
45
|
val icon: String,
|
|
45
46
|
val showBadge: Boolean = false,
|
|
46
|
-
val name: Map<String, String> = emptyMap()
|
|
47
|
+
val name: Map<String, String> = emptyMap(),
|
|
48
|
+
val showRightIcon: Boolean = true,
|
|
47
49
|
) {
|
|
48
50
|
fun toMap(): Map<String, Any> {
|
|
49
51
|
return mapOf(
|
|
50
52
|
"key" to key,
|
|
51
53
|
"icon" to icon,
|
|
52
54
|
"showBadge" to showBadge,
|
|
53
|
-
"name" to name
|
|
55
|
+
"name" to name,
|
|
56
|
+
"showRightIcon" to showRightIcon
|
|
54
57
|
)
|
|
55
58
|
}
|
|
56
59
|
}
|
|
@@ -160,6 +163,7 @@ private fun ToolkitHeaderRight(
|
|
|
160
163
|
api?.request(
|
|
161
164
|
"showTools",
|
|
162
165
|
mapOf(
|
|
166
|
+
"useSystemTools" to headerRight?.useSystemTools,
|
|
163
167
|
"tools" to headerRight?.tools?.map { it.toMap() },
|
|
164
168
|
"context" to mapOf(
|
|
165
169
|
"appId" to context?.appId,
|
|
@@ -202,12 +202,10 @@ fun Footer(footer: @Composable (() -> Unit)? = null, keyboardSize: Dp = 0.dp, bo
|
|
|
202
202
|
Modifier
|
|
203
203
|
.offset(y = -keyboardSize)
|
|
204
204
|
.shadow(
|
|
205
|
-
Colors.black_20.copy(alpha = 0.
|
|
206
|
-
|
|
207
|
-
10f,
|
|
205
|
+
color = Colors.black_20.copy(alpha = 0.05f),
|
|
206
|
+
blurRadius = 24f,
|
|
208
207
|
offsetX = 0.dp,
|
|
209
|
-
offsetY = (-
|
|
210
|
-
spread = 1.dp
|
|
208
|
+
offsetY = (-4).dp
|
|
211
209
|
)
|
|
212
210
|
.background(AppTheme.current.colors.background.surface)
|
|
213
211
|
) {
|
|
@@ -144,11 +144,7 @@ fun InputSearch(
|
|
|
144
144
|
if (!it.isFocused && isBlurred) inputSearchProps.onBlur()
|
|
145
145
|
if (it.isFocused && !isBlurred) isBlurred = true
|
|
146
146
|
},
|
|
147
|
-
onValueChange =
|
|
148
|
-
val data = it.replace("\n", " ")
|
|
149
|
-
inputSearchProps.text.value = data
|
|
150
|
-
inputSearchProps.onChangeText(data)
|
|
151
|
-
},
|
|
147
|
+
onValueChange = inputSearchProps.onChangeText,
|
|
152
148
|
decorationBox = { innerTextField ->
|
|
153
149
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
154
150
|
Row(
|
|
@@ -18,11 +18,8 @@ import androidx.compose.ui.Alignment
|
|
|
18
18
|
import androidx.compose.ui.Modifier
|
|
19
19
|
import androidx.compose.ui.draw.clip
|
|
20
20
|
import androidx.compose.ui.layout.ContentScale
|
|
21
|
-
import androidx.compose.ui.semantics.semantics
|
|
22
|
-
import androidx.compose.ui.semantics.testTag
|
|
23
21
|
import androidx.compose.ui.unit.dp
|
|
24
22
|
import vn.momo.kits.const.AppTheme
|
|
25
|
-
import vn.momo.kits.const.Colors
|
|
26
23
|
import vn.momo.kits.const.Radius
|
|
27
24
|
import vn.momo.kits.const.Spacing
|
|
28
25
|
import vn.momo.kits.modifier.setAutomationId
|
|
@@ -34,7 +31,7 @@ fun PopupPromotion(
|
|
|
34
31
|
onPress: (() -> Unit) = {},
|
|
35
32
|
onClose: (() -> Unit) = {},
|
|
36
33
|
) {
|
|
37
|
-
Column(Modifier.
|
|
34
|
+
Column(Modifier.padding(horizontal = Spacing.M).setAutomationId("popup_notify")) {
|
|
38
35
|
source.ifNotNull(
|
|
39
36
|
Image(
|
|
40
37
|
source = source,
|
|
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.height
|
|
|
10
10
|
import androidx.compose.foundation.layout.padding
|
|
11
11
|
import androidx.compose.foundation.layout.width
|
|
12
12
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
13
|
-
import androidx.compose.material.Text
|
|
14
13
|
import androidx.compose.runtime.Composable
|
|
15
14
|
import androidx.compose.ui.Alignment
|
|
16
15
|
import androidx.compose.ui.Modifier
|
|
@@ -22,6 +21,8 @@ import androidx.compose.ui.unit.sp
|
|
|
22
21
|
import vn.momo.kits.application.AppConfig
|
|
23
22
|
import vn.momo.kits.application.AppLanguage
|
|
24
23
|
import vn.momo.kits.modifier.noFeedbackClickable
|
|
24
|
+
import vn.momo.kits.components.Text
|
|
25
|
+
import vn.momo.kits.const.Typography
|
|
25
26
|
|
|
26
27
|
val defaultBanner = TrustBannerData(
|
|
27
28
|
content = mapOf(
|
|
@@ -120,9 +121,8 @@ fun TrustBanner(
|
|
|
120
121
|
Text(
|
|
121
122
|
text = trustBanner.content[language] ?: "",
|
|
122
123
|
modifier = Modifier.padding(bottom = 8.dp),
|
|
123
|
-
lineHeight = 16.sp,
|
|
124
|
-
fontSize = 12.sp,
|
|
125
124
|
color = Color(contentColor),
|
|
125
|
+
style = Typography.descriptionDefaultRegular,
|
|
126
126
|
maxLines = 2
|
|
127
127
|
)
|
|
128
128
|
Row(
|
|
@@ -134,9 +134,8 @@ fun TrustBanner(
|
|
|
134
134
|
) {
|
|
135
135
|
Text(
|
|
136
136
|
text = trustBanner.subContent[language] ?: "",
|
|
137
|
-
lineHeight = 16.sp,
|
|
138
|
-
fontSize = 12.sp,
|
|
139
137
|
color = Color(subContentColor),
|
|
138
|
+
style = Typography.actionXsBold,
|
|
140
139
|
maxLines = 2
|
|
141
140
|
)
|
|
142
141
|
Icon(
|
|
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.asPaddingValues
|
|
|
10
10
|
import androidx.compose.foundation.layout.systemBars
|
|
11
11
|
import androidx.compose.runtime.Composable
|
|
12
12
|
import androidx.compose.runtime.CompositionLocalProvider
|
|
13
|
+
import androidx.compose.runtime.DisposableEffect
|
|
13
14
|
import androidx.compose.runtime.remember
|
|
14
15
|
import androidx.compose.ui.unit.dp
|
|
15
16
|
import androidx.compose.ui.window.DialogProperties
|
|
@@ -122,4 +123,10 @@ fun NavigationContainer(initialScreen: @Composable () -> Unit, initialScreenTitl
|
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
}
|
|
126
|
+
|
|
127
|
+
DisposableEffect(Unit) {
|
|
128
|
+
onDispose {
|
|
129
|
+
navigator.dispose()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
125
132
|
}
|
|
@@ -3,11 +3,20 @@ package vn.momo.kits.navigation
|
|
|
3
3
|
import androidx.compose.runtime.Composable
|
|
4
4
|
import androidx.compose.runtime.staticCompositionLocalOf
|
|
5
5
|
import androidx.navigation.NavController
|
|
6
|
+
import kotlinx.coroutines.CoroutineScope
|
|
7
|
+
import kotlinx.coroutines.Dispatchers
|
|
8
|
+
import kotlinx.coroutines.IO
|
|
9
|
+
import kotlinx.coroutines.SupervisorJob
|
|
10
|
+
import kotlinx.coroutines.cancel
|
|
11
|
+
import kotlinx.coroutines.delay
|
|
12
|
+
import kotlinx.coroutines.launch
|
|
6
13
|
import kotlinx.serialization.Serializable
|
|
7
14
|
|
|
8
15
|
class Navigator(
|
|
9
16
|
private val navController: NavController
|
|
10
17
|
) {
|
|
18
|
+
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
|
19
|
+
|
|
11
20
|
fun push(content: @Composable () -> Unit, title : String? = null, options: NavigationOptions? = null) {
|
|
12
21
|
val route = DynamicScreenRegistry.register(content, title, options)
|
|
13
22
|
navController.navigate(DynamicScreenRoute(route.id, route.title))
|
|
@@ -21,8 +30,11 @@ class Navigator(
|
|
|
21
30
|
fun pop(count: Int = 1) {
|
|
22
31
|
repeat(count) {
|
|
23
32
|
if (navController.previousBackStackEntry != null){
|
|
24
|
-
DynamicScreenRegistry.unregister()
|
|
25
33
|
navController.popBackStack()
|
|
34
|
+
scope.launch {
|
|
35
|
+
delay(350)
|
|
36
|
+
DynamicScreenRegistry.unregisterLatestScreen()
|
|
37
|
+
}
|
|
26
38
|
}
|
|
27
39
|
}
|
|
28
40
|
}
|
|
@@ -50,6 +62,10 @@ class Navigator(
|
|
|
50
62
|
val route = DynamicScreenRegistry.registerModal(content, title, onDismiss)
|
|
51
63
|
navController.navigate(DynamicModalRoute(route.id, route.title, isBottomSheet = true))
|
|
52
64
|
}
|
|
65
|
+
|
|
66
|
+
fun dispose(){
|
|
67
|
+
scope.cancel()
|
|
68
|
+
}
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
val LocalNavigator = staticCompositionLocalOf<Navigator> {
|
|
@@ -74,22 +90,18 @@ data class DynamicScreen(
|
|
|
74
90
|
)
|
|
75
91
|
|
|
76
92
|
object DynamicScreenRegistry {
|
|
77
|
-
private val screens =
|
|
78
|
-
private val pendingUnregisterLists = arrayListOf<Int>()
|
|
93
|
+
private val screens = mutableMapOf<Int, DynamicScreen>()
|
|
79
94
|
private var idCounter = 1
|
|
80
95
|
|
|
81
96
|
fun register(content: @Composable () -> Unit, title: String?, options: NavigationOptions?): DynamicScreenRoute {
|
|
82
97
|
val id = idCounter++
|
|
83
98
|
val screenTitle = title ?: "StackScreen$id"
|
|
84
|
-
screens
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
options = options
|
|
90
|
-
)
|
|
99
|
+
screens[id] = DynamicScreen(
|
|
100
|
+
id = id,
|
|
101
|
+
title = screenTitle,
|
|
102
|
+
content = content,
|
|
103
|
+
options = options
|
|
91
104
|
)
|
|
92
|
-
|
|
93
105
|
return DynamicScreenRoute(id, screenTitle)
|
|
94
106
|
}
|
|
95
107
|
|
|
@@ -100,46 +112,31 @@ object DynamicScreenRegistry {
|
|
|
100
112
|
): DynamicModalRoute {
|
|
101
113
|
val id = idCounter++
|
|
102
114
|
val modalTitle = title ?: "ModalScreen$id"
|
|
103
|
-
screens
|
|
115
|
+
screens[id] = DynamicScreen(
|
|
104
116
|
id = id,
|
|
105
117
|
title = modalTitle,
|
|
106
118
|
content = content,
|
|
107
119
|
onDismiss = onDismiss
|
|
108
|
-
)
|
|
120
|
+
)
|
|
109
121
|
return DynamicModalRoute(id, modalTitle)
|
|
110
122
|
}
|
|
111
123
|
|
|
112
|
-
fun
|
|
113
|
-
val
|
|
114
|
-
|
|
115
|
-
screens.
|
|
116
|
-
if (!pendingUnregisterLists.contains(last.id)) {
|
|
117
|
-
pendingUnregisterLists.add(last.id)
|
|
118
|
-
}
|
|
124
|
+
fun unregisterLatestScreen() {
|
|
125
|
+
val lastEntry = screens.entries.lastOrNull() ?: return
|
|
126
|
+
val last = lastEntry.value
|
|
127
|
+
screens.remove(last.id)
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
fun unregisterAll(){
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
fun clearUnregisterListsIfNeeded(){
|
|
128
|
-
if(pendingUnregisterLists.isEmpty()) return
|
|
129
|
-
|
|
130
|
-
val toRemove = pendingUnregisterLists.toList()
|
|
131
|
-
for (id in toRemove) {
|
|
132
|
-
val index = screens.indexOfFirst { it.id == id }
|
|
133
|
-
if (index != -1) {
|
|
134
|
-
screens.removeAt(index)
|
|
135
|
-
pendingUnregisterLists.remove(id)
|
|
136
|
-
}
|
|
131
|
+
val ids = screens.keys.toList()
|
|
132
|
+
for (id in ids) {
|
|
133
|
+
unregisterLatestScreen()
|
|
137
134
|
}
|
|
138
135
|
}
|
|
139
136
|
|
|
140
|
-
fun getScreen(id: Int): DynamicScreen? = screens
|
|
137
|
+
fun getScreen(id: Int): DynamicScreen? = screens[id]
|
|
141
138
|
|
|
142
139
|
fun setOptions(id: Int, options: NavigationOptions){
|
|
143
|
-
screens
|
|
140
|
+
screens[id]?.options = options
|
|
144
141
|
}
|
|
145
142
|
}
|
|
@@ -88,11 +88,6 @@ fun StackScreen(
|
|
|
88
88
|
navigator.pop()
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
LaunchedEffect(Unit){
|
|
92
|
-
delay(500)
|
|
93
|
-
DynamicScreenRegistry.clearUnregisterListsIfNeeded()
|
|
94
|
-
}
|
|
95
|
-
|
|
96
91
|
CompositionLocalProvider(LocalNavigation provides navigation) {
|
|
97
92
|
Box(Modifier.fillMaxSize().background(backgroundColor)) {
|
|
98
93
|
Box(Modifier.zIndex(1f)) {
|
|
@@ -199,7 +194,7 @@ fun renderSearchAnimated(scrollState: ScrollState, inputSearchType: InputSearchT
|
|
|
199
194
|
val minTopPadding = AppStatusBar.current
|
|
200
195
|
val maxTopPadding = AppStatusBar.current + HEADER_HEIGHT.dp
|
|
201
196
|
val minStartPadding = Spacing.M
|
|
202
|
-
val maxStartPadding = 52.dp
|
|
197
|
+
val maxStartPadding = if (options.hiddenBack) Spacing.M else 52.dp
|
|
203
198
|
val minEndPadding = Spacing.M
|
|
204
199
|
val maxEndPadding = when (val headerRight = options.headerRight) {
|
|
205
200
|
is HeaderRight.Toolkit -> if (headerRight.useShortcut) 114.dp else 78.dp
|
|
@@ -6,17 +6,20 @@ public struct HeaderRightData {
|
|
|
6
6
|
var useMore: Bool
|
|
7
7
|
var tools: [ToolGroup]
|
|
8
8
|
var toolCallback: ((String) -> Void)?
|
|
9
|
+
var useSystemTools: Bool
|
|
9
10
|
|
|
10
11
|
public init(
|
|
11
12
|
useShortcut: Bool = false,
|
|
12
13
|
useMore: Bool = false,
|
|
13
14
|
tools: [ToolGroup] = [],
|
|
14
|
-
toolCallback: ((String) -> Void)? = nil
|
|
15
|
+
toolCallback: ((String) -> Void)? = nil,
|
|
16
|
+
useSystemTools: Bool = true
|
|
15
17
|
) {
|
|
16
18
|
self.useShortcut = useShortcut
|
|
17
19
|
self.useMore = useMore
|
|
18
20
|
self.tools = tools
|
|
19
21
|
self.toolCallback = toolCallback
|
|
22
|
+
self.useSystemTools = useSystemTools
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -25,17 +28,20 @@ public struct Tool {
|
|
|
25
28
|
var icon: String
|
|
26
29
|
var showBadge: Bool
|
|
27
30
|
var name: [String: String]
|
|
31
|
+
var showRightIcon: Bool
|
|
28
32
|
|
|
29
33
|
public init(
|
|
30
34
|
key: String,
|
|
31
35
|
icon: String,
|
|
32
36
|
name: [String: String] = [:],
|
|
33
|
-
showBadge: Bool = false
|
|
37
|
+
showBadge: Bool = false,
|
|
38
|
+
showRightIcon: Bool = true
|
|
34
39
|
) {
|
|
35
40
|
self.key = key
|
|
36
41
|
self.icon = icon
|
|
37
42
|
self.showBadge = showBadge
|
|
38
43
|
self.name = name
|
|
44
|
+
self.showRightIcon = showRightIcon
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
func toMap() -> [String: Any] {
|
|
@@ -43,7 +49,8 @@ public struct Tool {
|
|
|
43
49
|
"key": key,
|
|
44
50
|
"icon": icon,
|
|
45
51
|
"showBadge": showBadge,
|
|
46
|
-
"name": name
|
|
52
|
+
"name": name,
|
|
53
|
+
"showRightIcon": showRightIcon
|
|
47
54
|
]
|
|
48
55
|
}
|
|
49
56
|
}
|
|
@@ -131,6 +138,7 @@ struct ToolkitHeaderRight: View {
|
|
|
131
138
|
private func onPressMore() {
|
|
132
139
|
let context = environment.applicationContext
|
|
133
140
|
let params: [String: Any] = [
|
|
141
|
+
"useSystemTools": headerRight?.useSystemTools,
|
|
134
142
|
"tools": headerRight?.tools.map { $0.toMap() } ?? [],
|
|
135
143
|
"context": [
|
|
136
144
|
"appId": context?.appId,
|
|
@@ -72,7 +72,7 @@ public struct PopupDisplay: View {
|
|
|
72
72
|
.fill(Color.gray.opacity(0.3))
|
|
73
73
|
.frame(maxWidth: .infinity, maxHeight: 184)
|
|
74
74
|
}
|
|
75
|
-
.aspectRatio(
|
|
75
|
+
.aspectRatio(1.777, contentMode: .fit)
|
|
76
76
|
.frame(maxWidth: .infinity, alignment: .center)
|
|
77
77
|
.clipShape(RoundedCorner(radius: 15, corners: [.topLeft, .topRight]))
|
|
78
78
|
.clipped()
|
|
@@ -100,7 +100,7 @@ public struct PopupDisplay: View {
|
|
|
100
100
|
}
|
|
101
101
|
})
|
|
102
102
|
}
|
|
103
|
-
.frame(height:
|
|
103
|
+
.frame(height: 80)
|
|
104
104
|
} else {
|
|
105
105
|
Text(description)
|
|
106
106
|
.font(.paragraph)
|
|
@@ -110,7 +110,7 @@ public struct PopupDisplay: View {
|
|
|
110
110
|
Color.clear
|
|
111
111
|
.onAppear {
|
|
112
112
|
textHeight = geo.size.height
|
|
113
|
-
isScrollable = textHeight >
|
|
113
|
+
isScrollable = textHeight > 70 // adjust threshold to ~3 lines
|
|
114
114
|
}
|
|
115
115
|
})
|
|
116
116
|
}
|