@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.
@@ -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.2f),
206
- borderRadius = 0.dp,
207
- 10f,
205
+ color = Colors.black_20.copy(alpha = 0.05f),
206
+ blurRadius = 24f,
208
207
  offsetX = 0.dp,
209
- offsetY = (-2).dp,
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.semantics { testTag = "popup_notify" }) {
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 = arrayListOf<DynamicScreen>()
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.add(
85
- DynamicScreen(
86
- id = id,
87
- title = screenTitle,
88
- content = content,
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.add(DynamicScreen(
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 unregister() {
113
- val last = screens.lastOrNull() ?: return
114
- screens.removeLast()
115
- screens.add(0, last)
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
- repeat(screens.size) {
123
- unregister()
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.find { it.id == id }
137
+ fun getScreen(id: Int): DynamicScreen? = screens[id]
141
138
 
142
139
  fun setOptions(id: Int, options: NavigationOptions){
143
- screens.find { it.id == id }?.options = options
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(2, contentMode: .fit)
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: 60)
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 > 60 // adjust threshold to ~3 lines
113
+ isScrollable = textHeight > 70 // adjust threshold to ~3 lines
114
114
  }
115
115
  })
116
116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.120.0-navigation.4",
3
+ "version": "0.120.0-navigation.6",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},