@momo-kits/native-kits 0.111.1-rc.3 → 0.112.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -242,10 +242,12 @@ fun Header(
242
242
  }
243
243
  }
244
244
 
245
- Box(Modifier.graphicsLayer {
246
- alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
247
- }) {
248
- headerRight?.invoke()
245
+ if (inputSearchProps == null) {
246
+ Box(Modifier.graphicsLayer {
247
+ alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
248
+ }) {
249
+ headerRight?.invoke()
250
+ }
249
251
  }
250
252
  }
251
253
 
@@ -40,6 +40,7 @@ import vn.momo.kits.const.Spacing
40
40
  import vn.momo.kits.modifier.conditional
41
41
  import vn.momo.kits.modifier.shadow
42
42
  import vn.momo.kits.platform.getStatusBarHeight
43
+ import vn.momo.kits.components.HeaderRight
43
44
 
44
45
  enum class HeaderType {
45
46
  DEFAULT,
@@ -62,7 +63,7 @@ fun Screen(
62
63
  onContentLayout: ((LayoutCoordinates) -> Unit)? = null,
63
64
  useAvoidKeyboard: Boolean = true,
64
65
  footer: @Composable (() -> Unit)? = null,
65
- headerRight: @Composable (() -> Unit)? = null,
66
+ headerRight: @Composable (() -> Unit)? = { HeaderRight() },
66
67
  fabProps: FabProps? = null,
67
68
  animatedHeader: AnimatedHeader? = null,
68
69
  layoutOffset: Dp = 56.dp,
@@ -17,9 +17,9 @@ enum class DotSize(val size: Int) {
17
17
  }
18
18
 
19
19
  @Composable
20
- fun BadgeDot(size: DotSize = DotSize.Large) {
20
+ fun BadgeDot(size: DotSize = DotSize.Large, modifier: Modifier = Modifier) {
21
21
  Box(
22
- modifier = Modifier
22
+ modifier = modifier
23
23
  .size(size.size.dp)
24
24
  .border(width = 1.dp, color = Colors.black_01, shape = RoundedCornerShape(Radius.S))
25
25
  .background(color = Colors.red_03, shape = RoundedCornerShape(Radius.S))
@@ -0,0 +1,225 @@
1
+ package vn.momo.kits.components
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.border
5
+ import androidx.compose.foundation.clickable
6
+ import androidx.compose.foundation.layout.Arrangement
7
+ import androidx.compose.foundation.layout.Box
8
+ import androidx.compose.foundation.layout.Row
9
+ import androidx.compose.foundation.layout.height
10
+ import androidx.compose.foundation.layout.offset
11
+ import androidx.compose.foundation.layout.padding
12
+ import androidx.compose.foundation.layout.size
13
+ import androidx.compose.foundation.layout.width
14
+ import androidx.compose.foundation.shape.CircleShape
15
+ import androidx.compose.foundation.shape.RoundedCornerShape
16
+ import androidx.compose.runtime.Composable
17
+ import androidx.compose.runtime.getValue
18
+ import androidx.compose.runtime.mutableStateOf
19
+ import androidx.compose.runtime.remember
20
+ import androidx.compose.runtime.setValue
21
+ import androidx.compose.ui.Alignment
22
+ import androidx.compose.ui.Modifier
23
+ import androidx.compose.ui.draw.clip
24
+ import androidx.compose.ui.graphics.Color
25
+ import androidx.compose.ui.unit.dp
26
+ import vn.momo.compose.utils.datamapping.ModelConvertUtils
27
+ import vn.momo.kits.application.ComposeApi
28
+ import vn.momo.kits.application.PlatformApi
29
+ import vn.momo.kits.const.Colors
30
+
31
+ data class HeaderRightData(
32
+ val useOnBoarding: Boolean = false,
33
+ val buttonOnBoarding: String? = null,
34
+ val onPress: (() -> Unit)? = null,
35
+ val tintColor: Color = Colors.black_20,
36
+ val preventClose: Any? = null,
37
+ val useShortcut: Boolean = false,
38
+ val useMore: Boolean = false,
39
+ val tools: List<ToolGroup> = emptyList(),
40
+ val appId: String = ""
41
+ )
42
+
43
+ data class Tool(
44
+ val key: String,
45
+ val icon: String,
46
+ val onPress: () -> Unit,
47
+ val showBadge: Boolean = false
48
+ )
49
+
50
+ data class ToolGroup(
51
+ val items: List<Tool>
52
+ )
53
+
54
+ data class NavigationButtonConfig(
55
+ val icon: String,
56
+ val onPress: () -> Unit
57
+ )
58
+
59
+ object Spacing {
60
+ val M = 16.dp
61
+ val S = 8.dp
62
+ val XXS = 2.dp
63
+ }
64
+
65
+ @Composable
66
+ fun HeaderRight(
67
+ headerRight: HeaderRightData? = null,
68
+ ) {
69
+ Row(
70
+ verticalAlignment = Alignment.CenterVertically,
71
+ ) {
72
+ ToolkitHeaderRight(headerRight = headerRight)
73
+ }
74
+ }
75
+
76
+ @Composable
77
+ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
78
+ var isFavorite by remember { mutableStateOf(false) }
79
+ val isLoading by remember { mutableStateOf(false) }
80
+ val api = PlatformApi.current as? ComposeApi
81
+
82
+ fun onPressShortcut() {
83
+ isFavorite = !isFavorite
84
+ }
85
+
86
+ fun onPressHelpCenter() {
87
+ val params = ModelConvertUtils.encodeToJsonFromAny(
88
+ arrayOf<Any>(
89
+ mapOf("title" to "title"),
90
+ mapOf("title" to "title")
91
+ )
92
+ ).toString()
93
+ api?.request("showHelpCenter", params, {})
94
+ }
95
+
96
+ fun onPressClose() {
97
+ api?.request("dismiss", "")
98
+ }
99
+
100
+ fun onPressMore() {
101
+ val params = ModelConvertUtils.encodeToJsonFromAny(
102
+ arrayOf(
103
+ headerRight?.tools,
104
+ emptyMap<Any, Any>()
105
+ )
106
+ ).toString()
107
+ api?.request("showTools", params, {})
108
+ }
109
+
110
+ fun getNavigationButtonConfig(
111
+ tools: List<ToolGroup>? = emptyList(),
112
+ useMore: Boolean? = false,
113
+ isFavorite: Boolean = false,
114
+ onPressShortcut: () -> Unit,
115
+ onPressMore: () -> Unit
116
+ ): NavigationButtonConfig {
117
+ val totalTools = tools?.sumOf { it.items.size } ?: 0
118
+ var icon = if (isFavorite) "pin_star_checked" else "pin_star"
119
+ var onClickHandler = onPressShortcut
120
+ if (totalTools > 1 || useMore == true) {
121
+ icon = "navigation_more_icon"
122
+ onClickHandler = onPressMore
123
+ } else if (totalTools == 1 && !tools.isNullOrEmpty()) {
124
+ val singleTool = tools.first().items.firstOrNull()
125
+ if (singleTool != null) {
126
+ icon = singleTool.icon
127
+ onClickHandler = {
128
+ singleTool.onPress()
129
+ }
130
+ }
131
+ }
132
+ return NavigationButtonConfig(icon, onClickHandler)
133
+ }
134
+
135
+ val navButtonConfig = getNavigationButtonConfig(
136
+ headerRight?.tools,
137
+ headerRight?.useMore,
138
+ isFavorite,
139
+ ::onPressShortcut,
140
+ ::onPressMore
141
+ )
142
+
143
+ val showBadge = headerRight?.tools?.any { group ->
144
+ group.items.any { it.showBadge }
145
+ }
146
+
147
+ Row(
148
+ verticalAlignment = Alignment.CenterVertically
149
+ ) {
150
+ if (headerRight?.useShortcut == true) {
151
+ NavigationButton(
152
+ disabled = isLoading,
153
+ icon = navButtonConfig.icon,
154
+ showBadge = showBadge,
155
+ onClick = navButtonConfig.onPress
156
+ )
157
+ }
158
+ Row(
159
+ verticalAlignment = Alignment.CenterVertically,
160
+ horizontalArrangement = Arrangement.Center,
161
+ modifier = Modifier
162
+ .padding(start = Spacing.S)
163
+ .border(0.2.dp, Color(0x33000000), shape = RoundedCornerShape(14.dp))
164
+ .width(65.dp)
165
+ .height(28.dp)
166
+ .clip(shape = RoundedCornerShape(14.dp))
167
+ .background(Color(0x99FFFFFF))
168
+ ) {
169
+ Icon(source = "help_center", size = 20.dp, modifier = Modifier.padding(4.dp).clickable {
170
+ onPressHelpCenter()
171
+ })
172
+ Box(
173
+ modifier = Modifier
174
+ .width(0.5.dp)
175
+ .height(12.dp)
176
+ .background(Colors.black_20)
177
+ )
178
+ Icon(
179
+ source = "16_navigation_close_circle",
180
+ size = 20.dp,
181
+ modifier = Modifier.padding(4.dp).clickable { onPressClose() }
182
+ )
183
+ }
184
+ }
185
+ }
186
+
187
+ @Composable
188
+ fun NavigationButton(
189
+ disabled: Boolean,
190
+ icon: String,
191
+ showBadge: Boolean? = false,
192
+ onClick: () -> Unit
193
+ ) {
194
+ Box(
195
+ modifier = Modifier
196
+ .size(28.dp)
197
+ .clickable(enabled = !disabled, onClick = onClick)
198
+ ) {
199
+ Box(
200
+ modifier = Modifier
201
+ .matchParentSize()
202
+ .clip(CircleShape)
203
+ .background(Color(0x99FFFFFF))
204
+ .border(0.2.dp, Color(0x33000000), CircleShape)
205
+ )
206
+ Box(
207
+ modifier = Modifier.matchParentSize(),
208
+ contentAlignment = Alignment.Center
209
+ ) {
210
+ Icon(
211
+ source = icon,
212
+ size = 16.dp
213
+ )
214
+ }
215
+
216
+ if (showBadge != true) {
217
+ BadgeDot(
218
+ size = DotSize.Small,
219
+ modifier = Modifier
220
+ .align(Alignment.TopEnd)
221
+ .offset(x = -Spacing.XXS, y = -Spacing.XXS)
222
+ )
223
+ }
224
+ }
225
+ }
@@ -22,10 +22,10 @@ fun Icon(
22
22
  color: Color? = AppTheme.current.colors.text.default,
23
23
  modifier: Modifier = Modifier,
24
24
  ) {
25
- val icon = if (source.contains("https")) {
26
- source
27
- } else {
28
- Icons[source] ?: ""
25
+ val icon = when {
26
+ source.startsWith("data:") -> source
27
+ source.contains("https") -> source
28
+ else -> Icons[source] ?: ""
29
29
  }
30
30
  Box(modifier = modifier.size(size).semantics {contentDescription = "img|$icon"}) {
31
31
  AsyncImage(
@@ -1318,5 +1318,10 @@ val Icons = mapOf(
1318
1318
  "ic_checked" to "https://img.mservice.com.vn/app/img/kits/checked_ic.png",
1319
1319
  "ic_minus" to "https://img.mservice.com.vn/app/img/kits/minus.png",
1320
1320
  "ic_navigation" to "https://static.momocdn.net/app/img/kits/ic_navigation.png",
1321
- "ic_money_bag" to "https://static.momocdn.net/app/img/kits/ic_money_bag.png"
1321
+ "ic_money_bag" to "https://static.momocdn.net/app/img/kits/ic_money_bag.png",
1322
+ "ic_support" to "https://static.momocdn.net/app/img/kits/ic_support.png",
1323
+ "navigation_more_icon" to "https://static.momocdn.net/app/img/kits/navigation_more_icon.png",
1324
+ "help_center" to "https://static.momocdn.net/app/img/kits/help-center.png",
1325
+ "pin_star" to "https://static.momocdn.net/app/img/kits/pin_star.png",
1326
+ "pin_star_checked" to "https://static.momocdn.net/app/img/kits/pin_star_checked.png"
1322
1327
  )
@@ -0,0 +1,175 @@
1
+ package vn.momo.compose.utils.datamapping
2
+
3
+ import kotlinx.serialization.json.JsonArray
4
+ import kotlinx.serialization.json.JsonElement
5
+ import kotlinx.serialization.json.JsonNull
6
+ import kotlinx.serialization.json.JsonObject
7
+ import kotlinx.serialization.json.JsonPrimitive
8
+ import kotlinx.serialization.json.booleanOrNull
9
+ import kotlinx.serialization.json.contentOrNull
10
+ import kotlinx.serialization.json.doubleOrNull
11
+ import kotlinx.serialization.json.floatOrNull
12
+ import kotlinx.serialization.json.intOrNull
13
+ import kotlinx.serialization.json.jsonArray
14
+ import kotlinx.serialization.json.jsonObject
15
+ import kotlinx.serialization.json.jsonPrimitive
16
+ import kotlinx.serialization.json.longOrNull
17
+
18
+ fun emptyJsonObject() = JsonObject(mapOf())
19
+
20
+ val JsonObject.Companion.EMPTY
21
+ get() = JsonObject(emptyMap())
22
+
23
+ val JsonArray.Companion.EMPTY
24
+ get() = JsonArray(emptyList())
25
+
26
+ fun JsonObject.tryToGetObject(key: String): JsonObject? {
27
+ return try {
28
+ this[key]?.toJsonElement()?.jsonObject
29
+ } catch (_: Exception) {
30
+ JsonObject.EMPTY
31
+ }
32
+ }
33
+
34
+ fun JsonObject.tryToGetArray(key: String): JsonArray? {
35
+ return try {
36
+ this[key]?.toJsonElement()?.jsonArray
37
+ } catch (_: Exception) {
38
+ JsonArray.EMPTY
39
+ }
40
+ }
41
+
42
+ fun JsonObject.tryToGetString(key: String): String {
43
+ return try {
44
+ this.getOrElse(key) { "" }.toJsonElement().jsonPrimitive.content
45
+ } catch (_: Exception) {
46
+ ""
47
+ }
48
+ }
49
+
50
+ fun JsonObject.tryToGetStringNullable(key: String): String? {
51
+ return try {
52
+ this.getOrElse(key) { null }.toJsonElement().jsonPrimitive.contentOrNull
53
+ } catch (_: Exception) {
54
+ null
55
+ }
56
+ }
57
+
58
+ fun JsonObject.tryToGetLong(key: String): Long? {
59
+ return try {
60
+ this.getOrElse(key) { "" }.toJsonElement().jsonPrimitive.longOrNull
61
+ } catch (_: Exception) {
62
+ null
63
+ }
64
+ }
65
+
66
+ fun JsonObject.tryToGetLong(key: String, default: Long = 0L): Long {
67
+ return try {
68
+ this.getOrElse(key) { default }.toJsonElement().jsonPrimitive.longOrNull ?: default
69
+ } catch (_: Exception) {
70
+ default
71
+ }
72
+ }
73
+
74
+ fun JsonObject.tryToGetDoubleNullable(key: String): Double? {
75
+ return try {
76
+ this.getOrElse(key) { "" }.toJsonElement().jsonPrimitive.doubleOrNull
77
+ } catch (_: Exception) {
78
+ null
79
+ }
80
+ }
81
+
82
+ fun JsonObject.tryToGetDouble(key: String, default: Double = 0.0): Double {
83
+ return try {
84
+ this.getOrElse(key) { default }.toJsonElement().jsonPrimitive.doubleOrNull ?: default
85
+ } catch (_: Exception) {
86
+ default
87
+ }
88
+ }
89
+
90
+ fun JsonObject.tryToGetBoolean(key: String): Boolean {
91
+ return try {
92
+ this[key]?.jsonPrimitive?.booleanOrNull ?: false
93
+ } catch (_: Exception) {
94
+ false
95
+ }
96
+ }
97
+
98
+ fun JsonObject.tryToGetBooleanNullable(key: String): Boolean? {
99
+ return try {
100
+ this[key]?.jsonPrimitive?.booleanOrNull
101
+ } catch (_: Exception) {
102
+ null
103
+ }
104
+ }
105
+
106
+ internal fun JsonElement.toPureMap(): Map<String, Any?> {
107
+ return (this as? JsonObject)?.toPureMap() ?: emptyMap()
108
+ }
109
+
110
+ fun JsonObject.toPureMap(): Map<String, Any?> {
111
+ val map: MutableMap<String, Any?> = mutableMapOf()
112
+ this.jsonObject.entries.forEach { (key, value) ->
113
+ when (value) {
114
+ is Map<*, *> -> map[key] = value.toPureMap()
115
+ is Collection<*> -> map[key] = value.toList().map { it.toJsonElement().toPureMap() }
116
+ is JsonPrimitive -> map[key] =
117
+ if (value.isString) value.content else value.intOrNull ?: value.doubleOrNull
118
+ ?: value.floatOrNull ?: value.booleanOrNull
119
+ is JsonNull -> map[key] = null
120
+ else -> throw IllegalStateException("Can't serialize unknown type: $value")
121
+ }
122
+ }
123
+ return map
124
+ }
125
+
126
+ fun JsonElement.tryToString(): String {
127
+ return try {
128
+ this.jsonPrimitive.content
129
+ } catch (_: Exception) {
130
+ ""
131
+ }
132
+ }
133
+
134
+ internal fun JsonElement.toCollection(): Collection<Any?> {
135
+ val list: MutableList<Any?> = mutableListOf()
136
+ (this as? JsonArray)?.forEach { value ->
137
+ when (value) {
138
+ is Map<*, *> -> list.add(value.toMap())
139
+ is Collection<*> -> list.add(value.toCollection())
140
+ is JsonPrimitive -> list.add(
141
+ if (value.isString) value.content else value.booleanOrNull ?: value.intOrNull
142
+ ?: value.longOrNull ?: value.doubleOrNull ?: value.floatOrNull
143
+ )
144
+
145
+ is JsonNull -> list.add(null)
146
+ else -> throw IllegalStateException("Can't serialize unknown type: $value")
147
+ }
148
+ }
149
+ return list
150
+ }
151
+
152
+ internal fun JsonElement.toMap(): Map<String, Any?> {
153
+ val map: MutableMap<String, Any?> = mutableMapOf()
154
+ (this as? JsonObject)?.entries?.forEach { (key, value) ->
155
+ when (value) {
156
+ is Map<*, *> -> map[key] = value.toMap()
157
+ is Collection<*> -> map[key] = value.toCollection()
158
+ is JsonPrimitive -> map[key] =
159
+ if (value.isString) value.content else value.booleanOrNull ?: value.intOrNull
160
+ ?: value.longOrNull ?: value.doubleOrNull ?: value.floatOrNull
161
+
162
+ is JsonNull -> map[key] = null
163
+ else -> throw IllegalStateException("Can't serialize unknown type: $value")
164
+ }
165
+ }
166
+ return map
167
+ }
168
+
169
+ fun JsonElement.tryForceToStringStringMap(): Map<String, String?>? = try {
170
+ this.toMap() as Map<String, String?>?
171
+ } catch (e: Exception) {
172
+ null
173
+ }
174
+
175
+
@@ -0,0 +1,82 @@
1
+ package vn.momo.compose.utils.datamapping
2
+
3
+ import kotlinx.serialization.json.Json
4
+ import kotlinx.serialization.json.JsonArray
5
+ import kotlinx.serialization.json.JsonElement
6
+ import kotlinx.serialization.json.JsonNull
7
+ import kotlinx.serialization.json.JsonObject
8
+ import kotlinx.serialization.json.JsonPrimitive
9
+
10
+
11
+ object ModelConvertUtils {
12
+ private const val discriminator = "momoClassDiscriminator"
13
+ val formatter = Json {
14
+ encodeDefaults = true
15
+ ignoreUnknownKeys = true
16
+ classDiscriminator = discriminator
17
+ }
18
+
19
+ fun encodeToJson(data: String?): JsonElement? = try {
20
+ formatter.parseToJsonElement(data ?: "")
21
+ } catch (e: Throwable) {
22
+ null
23
+ }
24
+
25
+ fun encodeToJson(data: Collection<*>): JsonElement {
26
+ val parsedData = mutableListOf<JsonElement>()
27
+ data.forEach { value ->
28
+ when (value) {
29
+ is Map<*, *> -> parsedData.add(encodeToJson(value))
30
+ is Collection<*> -> parsedData.add(encodeToJson(value))
31
+ is Number -> parsedData.add(JsonPrimitive(value))
32
+ is Boolean -> parsedData.add(JsonPrimitive(value))
33
+ is String -> parsedData.add(JsonPrimitive(value))
34
+ is Enum<*> -> encodeToJson(value.name)?.let { parsedData.add(it) }
35
+ is JsonPrimitive -> parsedData.add(value)
36
+ is JsonElement -> parsedData.add(value)
37
+ null -> parsedData.add(JsonNull)
38
+ else -> throw IllegalStateException("Can't serialize unknown collection type: $value")
39
+ }
40
+ }
41
+ return JsonArray(parsedData)
42
+ }
43
+
44
+ fun encodeToJson(data: Map<*, *>): JsonElement {
45
+ val parsedData = mutableMapOf<String, JsonElement>()
46
+ data.forEach { (key, value) ->
47
+ if (key is String && value != null) {
48
+ when (value) {
49
+ is Map<*, *> -> parsedData[key] = encodeToJson(value)
50
+ is Collection<*> -> parsedData[key] = encodeToJson(value)
51
+ is Number -> parsedData[key] = JsonPrimitive(value)
52
+ is Boolean -> parsedData[key] = JsonPrimitive(value)
53
+ is String -> parsedData[key] = JsonPrimitive(value)
54
+ is Enum<*> -> encodeToJson(value.name)?.let { parsedData[key] = it }
55
+ is Array<*> -> parsedData[key] = encodeToJsonFromArray(value)
56
+ is JsonPrimitive -> parsedData[key] = value
57
+ is JsonElement -> parsedData[key] = value
58
+ else -> throw IllegalStateException("Can't serialize unknown type: $value")
59
+ }
60
+ }
61
+ }
62
+ return JsonObject(parsedData)
63
+ }
64
+
65
+ fun encodeToJsonFromAny(data: Any): JsonElement {
66
+ return when (data) {
67
+ is Map<*, *> -> encodeToJson(data)
68
+ is Collection<*> -> encodeToJson(data)
69
+ is Number -> JsonPrimitive(data)
70
+ is Boolean -> JsonPrimitive(data)
71
+ is String -> JsonPrimitive(data)
72
+ is Enum<*> -> encodeToJson(data.name) ?: JsonNull
73
+ is Array<*> -> encodeToJsonFromArray(data)
74
+ is JsonElement -> data
75
+ else -> JsonNull
76
+ }
77
+ }
78
+
79
+ private fun encodeToJsonFromArray(data: Array<*>): JsonArray =
80
+ JsonArray(data.filterNotNull().map { encodeToJsonFromAny(it) })
81
+
82
+ }
package/local.properties CHANGED
@@ -4,5 +4,5 @@
4
4
  # Location of the SDK. This is only used by Gradle.
5
5
  # For customization when using a Version Control System, please read the
6
6
  # header note.
7
- #Wed Aug 21 14:20:12 ICT 2024
8
- sdk.dir=/Users/huynhdung/Library/Android/sdk
7
+ #Tue Apr 23 16:40:15 ICT 2024
8
+ sdk.dir=/Users/hung.dao1/Library/Android/sdk
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.111.1-rc.3",
3
+ "version": "0.112.1-beta.2",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},