@momo-kits/native-kits 0.113.1-beta.2 → 0.113.1-beta.4
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/Components.kt +120 -50
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +261 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +57 -60
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +15 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +3 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +3 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +47 -37
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +6 -1
- package/ios/Application/ApplicationEnvironment.swift +34 -0
- package/ios/Application/Components.swift +6 -7
- package/ios/Application/ComposeApi.swift +22 -0
- package/ios/Application/HeaderRight.swift +249 -0
- package/ios/Application/Screen.swift +18 -19
- package/ios/Badge/BadgeDot.swift +31 -0
- package/ios/Button/Button.swift +2 -1
- package/ios/Icon/Icon.swift +1 -3
- package/ios/Image/Image.swift +17 -1
- package/ios/Popup/PopupDisplay.swift +8 -9
- package/local.properties +2 -2
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row
|
|
|
12
12
|
import androidx.compose.foundation.layout.Spacer
|
|
13
13
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
14
14
|
import androidx.compose.foundation.layout.height
|
|
15
|
+
import androidx.compose.foundation.layout.offset
|
|
15
16
|
import androidx.compose.foundation.layout.padding
|
|
16
17
|
import androidx.compose.foundation.layout.size
|
|
17
18
|
import androidx.compose.foundation.layout.width
|
|
@@ -35,6 +36,7 @@ import androidx.compose.ui.platform.LocalDensity
|
|
|
35
36
|
import androidx.compose.ui.semantics.semantics
|
|
36
37
|
import androidx.compose.ui.semantics.testTag
|
|
37
38
|
import androidx.compose.ui.text.style.TextAlign
|
|
39
|
+
import androidx.compose.ui.unit.Dp
|
|
38
40
|
import androidx.compose.ui.unit.dp
|
|
39
41
|
import androidx.compose.ui.zIndex
|
|
40
42
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
@@ -48,9 +50,11 @@ import vn.momo.kits.components.Text
|
|
|
48
50
|
import vn.momo.kits.const.AppStatusBar
|
|
49
51
|
import vn.momo.kits.const.AppTheme
|
|
50
52
|
import vn.momo.kits.const.Colors
|
|
53
|
+
import vn.momo.kits.const.Radius
|
|
51
54
|
import vn.momo.kits.const.Spacing
|
|
52
55
|
import vn.momo.kits.const.Typography
|
|
53
56
|
import vn.momo.kits.modifier.setAutomationId
|
|
57
|
+
import vn.momo.kits.platform.getScreenDimensions
|
|
54
58
|
import vn.momo.kits.platform.getStatusBarHeight
|
|
55
59
|
import vn.momo.kits.utils.bottomBorder
|
|
56
60
|
|
|
@@ -58,6 +62,8 @@ enum class TitlePosition {
|
|
|
58
62
|
LEFT, CENTER,
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
private const val SCREEN_PADDING = 12
|
|
66
|
+
private const val BACK_WIDTH = 28
|
|
61
67
|
|
|
62
68
|
data class AnimatedHeader(
|
|
63
69
|
val type: HeaderType = HeaderType.DEFAULT,
|
|
@@ -66,20 +72,55 @@ data class AnimatedHeader(
|
|
|
66
72
|
|
|
67
73
|
@Composable
|
|
68
74
|
fun HeaderBackground(
|
|
75
|
+
isBack: Boolean = true,
|
|
69
76
|
headerType: HeaderType = HeaderType.DEFAULT,
|
|
70
|
-
scrollState: Int
|
|
77
|
+
scrollState: Int,
|
|
78
|
+
inputSearchProps: InputSearchProps? = null,
|
|
79
|
+
headerRightWidth: Dp = 0.dp,
|
|
80
|
+
useAnimationSearch: Boolean = false
|
|
71
81
|
) {
|
|
72
82
|
val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
|
|
83
|
+
val screenWidth = getScreenDimensions().width
|
|
84
|
+
val leftPosition = if (isBack) (BACK_WIDTH + 20).dp else 20.dp
|
|
85
|
+
val searchWidth = screenWidth - (SCREEN_PADDING * 2)
|
|
86
|
+
|
|
73
87
|
val opacityAni by animateFloatAsState(
|
|
74
|
-
targetValue = (1 - (scrollState /
|
|
88
|
+
targetValue = (1 - (scrollState / 52f)).coerceIn(0f, 1f),
|
|
75
89
|
)
|
|
76
90
|
|
|
77
|
-
val backgroundColor
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
val backgroundColor by animateColorAsState(
|
|
92
|
+
targetValue = animateColor(
|
|
93
|
+
Color.Transparent,
|
|
94
|
+
AppTheme.current.colors.background.surface,
|
|
95
|
+
opacityAni
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
val backgroundSearch by animateColorAsState(
|
|
100
|
+
targetValue = animateColor(
|
|
101
|
+
AppTheme.current.colors.background.default,
|
|
102
|
+
Colors.black_01,
|
|
103
|
+
opacityAni
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
val animatedTranslateX by animateFloatAsState(
|
|
108
|
+
targetValue = (scrollState / 52f).coerceIn(0f, 1f) * (leftPosition.value - 12) + 12,
|
|
109
|
+
)
|
|
82
110
|
|
|
111
|
+
val animatedWidth by animateFloatAsState(
|
|
112
|
+
targetValue = (scrollState / 52f).coerceIn(
|
|
113
|
+
0f,
|
|
114
|
+
1f
|
|
115
|
+
) * ((searchWidth - leftPosition.value - headerRightWidth.value + 12) - searchWidth) + searchWidth,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
val animatedHeight by animateFloatAsState(
|
|
119
|
+
targetValue = (1 - (scrollState / 52f).coerceIn(
|
|
120
|
+
0f,
|
|
121
|
+
1f
|
|
122
|
+
)) * (statusBarHeight.value + 94 - (statusBarHeight.value + 52)) + (statusBarHeight.value + 52),
|
|
123
|
+
)
|
|
83
124
|
|
|
84
125
|
when (headerType) {
|
|
85
126
|
HeaderType.DEFAULT -> {
|
|
@@ -119,38 +160,71 @@ fun HeaderBackground(
|
|
|
119
160
|
)
|
|
120
161
|
}
|
|
121
162
|
}
|
|
122
|
-
|
|
123
163
|
}
|
|
124
164
|
|
|
125
165
|
HeaderType.EXTENDED -> {
|
|
126
|
-
|
|
127
166
|
Box(
|
|
128
|
-
modifier = Modifier.fillMaxWidth().height(
|
|
129
|
-
.alpha(opacityAni)
|
|
130
|
-
.background(
|
|
131
|
-
Brush.linearGradient(
|
|
132
|
-
colors = listOf(
|
|
133
|
-
Color(0xFFFDCADE),
|
|
134
|
-
Color(0x00FDCADE)
|
|
135
|
-
),
|
|
136
|
-
start = Offset(0f, 0f),
|
|
137
|
-
end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
|
|
138
|
-
)
|
|
139
|
-
)
|
|
167
|
+
modifier = Modifier.fillMaxWidth().height(animatedHeight.dp)
|
|
140
168
|
) {
|
|
141
|
-
|
|
169
|
+
Box(
|
|
170
|
+
modifier = Modifier
|
|
171
|
+
.fillMaxWidth()
|
|
172
|
+
.height(154.dp)
|
|
173
|
+
.alpha(opacityAni)
|
|
174
|
+
.background(
|
|
175
|
+
Brush.linearGradient(
|
|
176
|
+
colors = listOf(
|
|
177
|
+
Color(0xFFFDCADE),
|
|
178
|
+
Color(0x00FDCADE)
|
|
179
|
+
),
|
|
180
|
+
start = Offset(0f, 0f),
|
|
181
|
+
end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
) {
|
|
185
|
+
if (AppTheme.current.assets.headerBackground != null) {
|
|
186
|
+
Box(
|
|
187
|
+
modifier = Modifier
|
|
188
|
+
.fillMaxWidth()
|
|
189
|
+
.background(backgroundColor)
|
|
190
|
+
) {
|
|
191
|
+
Image(
|
|
192
|
+
loading = false,
|
|
193
|
+
source = AppTheme.current.assets.headerBackground!!,
|
|
194
|
+
modifier = Modifier.fillMaxWidth().height(154.dp),
|
|
195
|
+
options = Options(
|
|
196
|
+
contentScale = ContentScale.FillWidth
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (inputSearchProps != null && useAnimationSearch) {
|
|
142
204
|
Box(
|
|
143
|
-
modifier = Modifier
|
|
144
|
-
.
|
|
205
|
+
modifier = Modifier
|
|
206
|
+
.height(52.dp)
|
|
207
|
+
.offset(
|
|
208
|
+
x = animatedTranslateX.dp
|
|
209
|
+
)
|
|
210
|
+
.padding(vertical = Spacing.S)
|
|
211
|
+
.align(Alignment.BottomStart)
|
|
145
212
|
) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
213
|
+
Box(
|
|
214
|
+
modifier = Modifier
|
|
215
|
+
.width(animatedWidth.dp)
|
|
216
|
+
.background(
|
|
217
|
+
color = backgroundSearch,
|
|
218
|
+
shape = RoundedCornerShape(Radius.XL)
|
|
219
|
+
)
|
|
220
|
+
) {
|
|
221
|
+
InputSearch(
|
|
222
|
+
inputSearchProps = inputSearchProps.copy(
|
|
223
|
+
showButtonText = false,
|
|
224
|
+
backgroundColor = Color.Transparent
|
|
225
|
+
)
|
|
152
226
|
)
|
|
153
|
-
|
|
227
|
+
}
|
|
154
228
|
}
|
|
155
229
|
}
|
|
156
230
|
}
|
|
@@ -160,7 +234,6 @@ fun HeaderBackground(
|
|
|
160
234
|
Box(modifier = Modifier.fillMaxWidth())
|
|
161
235
|
}
|
|
162
236
|
}
|
|
163
|
-
|
|
164
237
|
}
|
|
165
238
|
|
|
166
239
|
@Composable
|
|
@@ -173,11 +246,13 @@ fun Header(
|
|
|
173
246
|
opacity: Float? = null,
|
|
174
247
|
animatedHeader: AnimatedHeader? = null,
|
|
175
248
|
scrollState: Int = 0,
|
|
176
|
-
inputSearchProps: InputSearchProps?
|
|
249
|
+
inputSearchProps: InputSearchProps? = null,
|
|
250
|
+
useAnimationSearch: Boolean = false
|
|
177
251
|
) {
|
|
178
252
|
val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
|
|
179
253
|
val tintIconColor = AppTheme.current.colors.text.default
|
|
180
254
|
val backgroundButton = Colors.black_01.copy(alpha = 0.6f)
|
|
255
|
+
val miniAppContext = ApplicationContext.current
|
|
181
256
|
|
|
182
257
|
var colorFraction by remember { mutableStateOf(0f) }
|
|
183
258
|
|
|
@@ -188,14 +263,6 @@ fun Header(
|
|
|
188
263
|
.collect { fraction -> colorFraction = fraction }
|
|
189
264
|
}
|
|
190
265
|
|
|
191
|
-
val backgroundSearch by animateColorAsState(
|
|
192
|
-
targetValue = animateColor(
|
|
193
|
-
Colors.black_01,
|
|
194
|
-
AppTheme.current.colors.background.default,
|
|
195
|
-
colorFraction
|
|
196
|
-
)
|
|
197
|
-
)
|
|
198
|
-
|
|
199
266
|
if (headerType != HeaderType.NONE) {
|
|
200
267
|
BoxWithConstraints(
|
|
201
268
|
Modifier.height(statusBarHeight + 52.dp)
|
|
@@ -242,10 +309,12 @@ fun Header(
|
|
|
242
309
|
}
|
|
243
310
|
}
|
|
244
311
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
312
|
+
if (inputSearchProps == null && miniAppContext != null) {
|
|
313
|
+
Box(Modifier.graphicsLayer {
|
|
314
|
+
alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
|
|
315
|
+
}) {
|
|
316
|
+
headerRight?.invoke()
|
|
317
|
+
}
|
|
249
318
|
}
|
|
250
319
|
}
|
|
251
320
|
|
|
@@ -258,13 +327,14 @@ fun Header(
|
|
|
258
327
|
) {
|
|
259
328
|
if (goBack != null && titlePosition == TitlePosition.LEFT) {
|
|
260
329
|
Spacer(Modifier.width(40.dp))
|
|
261
|
-
}
|
|
262
|
-
if (inputSearchProps != null) {
|
|
263
|
-
InputSearch(inputSearchProps = inputSearchProps.copy(backgroundColor = backgroundSearch))
|
|
264
330
|
} else {
|
|
265
331
|
Box(
|
|
266
332
|
Modifier.weight(1f).graphicsLayer {
|
|
267
|
-
alpha = if (
|
|
333
|
+
alpha = if (useAnimationSearch && inputSearchProps != null) {
|
|
334
|
+
1f - (opacity ?: 1f)
|
|
335
|
+
} else {
|
|
336
|
+
if (animatedHeader !== null) opacity ?: 1f else 1f
|
|
337
|
+
}
|
|
268
338
|
},
|
|
269
339
|
contentAlignment = if (titlePosition == TitlePosition.LEFT) Alignment.TopStart else Alignment.Center
|
|
270
340
|
) {
|
|
@@ -306,4 +376,4 @@ fun animateColor(start: Color, stop: Color, fraction: Float): Color {
|
|
|
306
376
|
blue = start.blue + fraction * (stop.blue - start.blue),
|
|
307
377
|
alpha = start.alpha + fraction * (stop.alpha - start.alpha)
|
|
308
378
|
)
|
|
309
|
-
}
|
|
379
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
package vn.momo.kits.application
|
|
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.kits.components.BadgeDot
|
|
27
|
+
import vn.momo.kits.components.DotSize
|
|
28
|
+
import vn.momo.kits.components.Icon
|
|
29
|
+
import vn.momo.kits.const.Colors
|
|
30
|
+
import kotlinx.serialization.json.Json
|
|
31
|
+
import kotlinx.serialization.json.jsonObject
|
|
32
|
+
import kotlinx.serialization.json.jsonPrimitive
|
|
33
|
+
|
|
34
|
+
data class HeaderRightData(
|
|
35
|
+
val useShortcut: Boolean = false,
|
|
36
|
+
val useMore: Boolean = false,
|
|
37
|
+
val tools: List<ToolGroup> = emptyList(),
|
|
38
|
+
val toolCallback: ((String) -> Unit)? = { _ -> }
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
data class Tool(
|
|
42
|
+
val key: String,
|
|
43
|
+
val icon: String,
|
|
44
|
+
val showBadge: Boolean = false,
|
|
45
|
+
val name: Map<String, String> = emptyMap()
|
|
46
|
+
) {
|
|
47
|
+
fun toMap(): Map<String, Any> {
|
|
48
|
+
return mapOf(
|
|
49
|
+
"key" to key,
|
|
50
|
+
"icon" to icon,
|
|
51
|
+
"showBadge" to showBadge,
|
|
52
|
+
"name" to name
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
data class ToolGroup(
|
|
58
|
+
val title: Map<String, String> = emptyMap(),
|
|
59
|
+
val items: List<Tool>
|
|
60
|
+
) {
|
|
61
|
+
fun toMap(): Map<String, Any> {
|
|
62
|
+
return mapOf(
|
|
63
|
+
"title" to title,
|
|
64
|
+
"items" to items.map { it.toMap() }
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
data class NavigationButtonConfig(
|
|
70
|
+
val icon: String,
|
|
71
|
+
val onPress: () -> Unit
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
object Spacing {
|
|
75
|
+
val M = 16.dp
|
|
76
|
+
val S = 8.dp
|
|
77
|
+
val XXS = 2.dp
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Composable
|
|
81
|
+
fun HeaderRight(
|
|
82
|
+
headerRight: HeaderRightData? = null,
|
|
83
|
+
) {
|
|
84
|
+
Row(
|
|
85
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
86
|
+
) {
|
|
87
|
+
ToolkitHeaderRight(headerRight = headerRight)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Composable
|
|
92
|
+
fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
|
|
93
|
+
var isFavorite by remember { mutableStateOf(false) }
|
|
94
|
+
val isLoading by remember { mutableStateOf(false) }
|
|
95
|
+
val api = PlatformApi.current as? ComposeApi
|
|
96
|
+
val context = ApplicationContext.current
|
|
97
|
+
|
|
98
|
+
fun onPressShortcut() {
|
|
99
|
+
api?.request("onToolAction", mapOf("item" to mapOf("key" to "onFavorite"))) {
|
|
100
|
+
isFavorite = !isFavorite
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fun onPressHelpCenter() {
|
|
105
|
+
val paramMap = mapOf(
|
|
106
|
+
"appId" to context?.appId,
|
|
107
|
+
"code" to context?.appCode,
|
|
108
|
+
"name" to context?.appName,
|
|
109
|
+
"icon" to context?.appIcon,
|
|
110
|
+
"description" to context?.description
|
|
111
|
+
)
|
|
112
|
+
api?.request("showHelpCenter", paramMap) {
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fun onPressClose() {
|
|
118
|
+
api?.request("dismiss", "", {_ -> })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fun onPressMore() {
|
|
122
|
+
api?.request("showTools",
|
|
123
|
+
mapOf(
|
|
124
|
+
"tools" to headerRight?.tools?.map { it.toMap() },
|
|
125
|
+
"context" to mapOf(
|
|
126
|
+
"appId" to context?.appId,
|
|
127
|
+
"code" to context?.appCode,
|
|
128
|
+
"name" to context?.appName,
|
|
129
|
+
"icon" to context?.appIcon,
|
|
130
|
+
"description" to context?.description
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
) { response ->
|
|
134
|
+
try {
|
|
135
|
+
val json = Json.parseToJsonElement(response).jsonObject
|
|
136
|
+
val toolResponse = json["response"]?.jsonPrimitive?.content
|
|
137
|
+
if (toolResponse != null) {
|
|
138
|
+
headerRight?.toolCallback?.invoke(toolResponse)
|
|
139
|
+
}
|
|
140
|
+
} catch (e: Exception) {
|
|
141
|
+
println("Error parsing response: $e")
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
fun getNavigationButtonConfig(
|
|
147
|
+
tools: List<ToolGroup>? = emptyList(),
|
|
148
|
+
useMore: Boolean? = false,
|
|
149
|
+
isFavorite: Boolean = false,
|
|
150
|
+
onPressShortcut: () -> Unit,
|
|
151
|
+
onPressMore: () -> Unit
|
|
152
|
+
): NavigationButtonConfig {
|
|
153
|
+
val totalTools = tools?.sumOf { it.items.size } ?: 0
|
|
154
|
+
var icon = if (isFavorite) "pin_star_checked" else "pin_star"
|
|
155
|
+
var onClickHandler = onPressShortcut
|
|
156
|
+
if (totalTools > 1 || useMore == true) {
|
|
157
|
+
icon = "navigation_more_icon"
|
|
158
|
+
onClickHandler = onPressMore
|
|
159
|
+
} else if (totalTools == 1 && !tools.isNullOrEmpty()) {
|
|
160
|
+
val singleTool = tools.first().items.firstOrNull()
|
|
161
|
+
if (singleTool != null) {
|
|
162
|
+
icon = singleTool.icon
|
|
163
|
+
onClickHandler = {
|
|
164
|
+
headerRight?.toolCallback?.invoke(singleTool.key)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return NavigationButtonConfig(icon, onClickHandler)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
val navButtonConfig = getNavigationButtonConfig(
|
|
172
|
+
headerRight?.tools,
|
|
173
|
+
headerRight?.useMore,
|
|
174
|
+
isFavorite,
|
|
175
|
+
::onPressShortcut,
|
|
176
|
+
::onPressMore
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
val showBadge = headerRight?.tools?.any { group ->
|
|
180
|
+
group.items.any { it.showBadge }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Row(
|
|
184
|
+
verticalAlignment = Alignment.CenterVertically
|
|
185
|
+
) {
|
|
186
|
+
if (headerRight?.useShortcut == true) {
|
|
187
|
+
NavigationButton(
|
|
188
|
+
disabled = isLoading,
|
|
189
|
+
icon = navButtonConfig.icon,
|
|
190
|
+
showBadge = showBadge,
|
|
191
|
+
onClick = navButtonConfig.onPress
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
Row(
|
|
195
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
196
|
+
horizontalArrangement = Arrangement.Center,
|
|
197
|
+
modifier = Modifier
|
|
198
|
+
.padding(start = Spacing.S)
|
|
199
|
+
.border(0.2.dp, Color(0x33000000), shape = RoundedCornerShape(14.dp))
|
|
200
|
+
.width(65.dp)
|
|
201
|
+
.height(28.dp)
|
|
202
|
+
.clip(shape = RoundedCornerShape(14.dp))
|
|
203
|
+
.background(Color(0x99FFFFFF))
|
|
204
|
+
) {
|
|
205
|
+
Icon(source = "help_center", size = 20.dp, modifier = Modifier.padding(4.dp).clickable {
|
|
206
|
+
onPressHelpCenter()
|
|
207
|
+
})
|
|
208
|
+
Box(
|
|
209
|
+
modifier = Modifier
|
|
210
|
+
.width(0.5.dp)
|
|
211
|
+
.height(12.dp)
|
|
212
|
+
.background(Colors.black_20)
|
|
213
|
+
)
|
|
214
|
+
Icon(
|
|
215
|
+
source = "16_navigation_close_circle",
|
|
216
|
+
size = 20.dp,
|
|
217
|
+
modifier = Modifier.padding(4.dp).clickable { onPressClose() }
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@Composable
|
|
224
|
+
fun NavigationButton(
|
|
225
|
+
disabled: Boolean,
|
|
226
|
+
icon: String,
|
|
227
|
+
showBadge: Boolean? = false,
|
|
228
|
+
onClick: () -> Unit
|
|
229
|
+
) {
|
|
230
|
+
Box(
|
|
231
|
+
modifier = Modifier
|
|
232
|
+
.size(28.dp)
|
|
233
|
+
.clickable(enabled = !disabled, onClick = onClick)
|
|
234
|
+
) {
|
|
235
|
+
Box(
|
|
236
|
+
modifier = Modifier
|
|
237
|
+
.matchParentSize()
|
|
238
|
+
.clip(CircleShape)
|
|
239
|
+
.background(Color(0x99FFFFFF))
|
|
240
|
+
.border(0.2.dp, Color(0x33000000), CircleShape)
|
|
241
|
+
)
|
|
242
|
+
Box(
|
|
243
|
+
modifier = Modifier.matchParentSize(),
|
|
244
|
+
contentAlignment = Alignment.Center
|
|
245
|
+
) {
|
|
246
|
+
Icon(
|
|
247
|
+
source = icon,
|
|
248
|
+
size = 16.dp
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (showBadge == true) {
|
|
253
|
+
BadgeDot(
|
|
254
|
+
size = DotSize.Small,
|
|
255
|
+
modifier = Modifier
|
|
256
|
+
.align(Alignment.TopEnd)
|
|
257
|
+
.offset(x = -Spacing.XXS, y = -Spacing.XXS)
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -6,14 +6,10 @@ import androidx.compose.runtime.LaunchedEffect
|
|
|
6
6
|
import androidx.compose.runtime.getValue
|
|
7
7
|
import androidx.compose.runtime.mutableStateOf
|
|
8
8
|
import androidx.compose.runtime.remember
|
|
9
|
-
import androidx.compose.runtime.rememberCoroutineScope
|
|
10
9
|
import androidx.compose.runtime.setValue
|
|
11
10
|
import androidx.compose.runtime.staticCompositionLocalOf
|
|
12
11
|
import androidx.compose.ui.unit.Dp
|
|
13
|
-
import
|
|
14
|
-
import kotlinx.serialization.json.Json
|
|
15
|
-
import kotlinx.serialization.json.jsonObject
|
|
16
|
-
import kotlinx.serialization.json.jsonPrimitive
|
|
12
|
+
import vn.momo.kits.components.TrustBannerData
|
|
17
13
|
import vn.momo.kits.const.AppStatusBar
|
|
18
14
|
import vn.momo.kits.const.AppTheme
|
|
19
15
|
import vn.momo.kits.const.Theme
|
|
@@ -22,40 +18,20 @@ import vn.momo.kits.const.defaultTheme
|
|
|
22
18
|
import vn.momo.kits.platform.disableOverscroll
|
|
23
19
|
import vn.momo.kits.platform.getStatusBarHeight
|
|
24
20
|
|
|
25
|
-
val PlatformApi = staticCompositionLocalOf<Any?> { null }
|
|
26
|
-
|
|
27
21
|
interface ComposeApi {
|
|
28
|
-
fun request(funcName: String, params:
|
|
29
|
-
fun request(funcName: String, params:
|
|
30
|
-
fun requestCallback(funcName: String, params:
|
|
22
|
+
fun request(funcName: String, params: Any?): String
|
|
23
|
+
fun request(funcName: String, params: Any?, onResponse: ((String) -> Unit)?): String
|
|
24
|
+
fun requestCallback(funcName: String, params: Any?, onResponse: ((String) -> Unit)?)
|
|
31
25
|
fun removeCallback(id: String)
|
|
32
26
|
}
|
|
33
27
|
|
|
34
|
-
val AppNavigator = staticCompositionLocalOf<Navigator> {
|
|
35
|
-
error("no navigator provided!")
|
|
36
|
-
}
|
|
37
|
-
|
|
38
28
|
class Navigator {
|
|
39
|
-
fun push(content: @Composable () -> Unit) {
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
fun
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
fun reset(content: @Composable () -> Unit) {
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
fun pop() {
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
fun replace(content: @Composable () -> Unit) {
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
fun popToTop() {
|
|
57
|
-
}
|
|
58
|
-
|
|
29
|
+
fun push(content: @Composable () -> Unit) {}
|
|
30
|
+
fun present(content: @Composable () -> Unit) {}
|
|
31
|
+
fun reset(content: @Composable () -> Unit) {}
|
|
32
|
+
fun pop() {}
|
|
33
|
+
fun replace(content: @Composable () -> Unit) {}
|
|
34
|
+
fun popToTop() {}
|
|
59
35
|
fun showModal(
|
|
60
36
|
onClose: () -> Unit = {},
|
|
61
37
|
canBackgroundClose: Boolean = true,
|
|
@@ -63,52 +39,73 @@ class Navigator {
|
|
|
63
39
|
) {
|
|
64
40
|
}
|
|
65
41
|
|
|
66
|
-
fun showBottomSheet(content: @Composable () -> Unit) {
|
|
67
|
-
}
|
|
42
|
+
fun showBottomSheet(content: @Composable () -> Unit) {}
|
|
68
43
|
}
|
|
69
44
|
|
|
45
|
+
data class MiniAppContext(
|
|
46
|
+
val appIcon: String = "",
|
|
47
|
+
val appName: Any? = null,
|
|
48
|
+
val appId: String = "",
|
|
49
|
+
val appCode: String = "",
|
|
50
|
+
val description: Any? = null,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
val PlatformApi = staticCompositionLocalOf<Any?> { null }
|
|
54
|
+
|
|
55
|
+
val AppNavigator = staticCompositionLocalOf<Navigator> {
|
|
56
|
+
error("no navigator provided!")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
val ApplicationContext = staticCompositionLocalOf<MiniAppContext?> {
|
|
60
|
+
null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
val AppConfig = staticCompositionLocalOf<KitConfig?> {
|
|
64
|
+
null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
data class KitConfig(
|
|
68
|
+
val trustBanner: TrustBannerData? = null,
|
|
69
|
+
val headerBar: String? = null,
|
|
70
|
+
val headerGradient: String? = null
|
|
71
|
+
)
|
|
72
|
+
|
|
70
73
|
@Composable
|
|
71
74
|
fun ApplicationContainer(
|
|
72
75
|
theme: Theme = defaultTheme,
|
|
73
76
|
composeApi: ComposeApi? = null,
|
|
74
|
-
statusBarHeight: Dp? =
|
|
75
|
-
|
|
77
|
+
statusBarHeight: Dp? = null,
|
|
78
|
+
applicationContext: MiniAppContext? = null,
|
|
79
|
+
config: KitConfig? = null,
|
|
80
|
+
content: @Composable () -> Unit,
|
|
76
81
|
) {
|
|
77
82
|
var appTheme by remember { mutableStateOf(theme) }
|
|
78
|
-
val coroutineScope = rememberCoroutineScope()
|
|
79
83
|
|
|
80
84
|
LaunchedEffect(Unit) {
|
|
81
85
|
try {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
?.content
|
|
90
|
-
if (headerBar != null && appTheme.assets.headerBackground == null) {
|
|
91
|
-
coroutineScope.launch {
|
|
92
|
-
appTheme = appTheme.copy(
|
|
93
|
-
assets = ThemeAssets(
|
|
94
|
-
headerBackground = headerBar
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
86
|
+
val headerBar = config?.headerBar
|
|
87
|
+
if (headerBar != null && appTheme.assets.headerBackground == null) {
|
|
88
|
+
appTheme = appTheme.copy(
|
|
89
|
+
assets = ThemeAssets(
|
|
90
|
+
headerBackground = headerBar
|
|
91
|
+
)
|
|
92
|
+
)
|
|
99
93
|
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
print("@@ == NavigationContainer fetch config error $e")
|
|
94
|
+
} catch (e: Exception) {
|
|
95
|
+
print("@@ == NavigationContainer get config error $e")
|
|
103
96
|
}
|
|
104
97
|
}
|
|
105
98
|
|
|
99
|
+
val appStatusBarHeight = statusBarHeight ?: getStatusBarHeight()
|
|
100
|
+
|
|
106
101
|
disableOverscroll()
|
|
107
102
|
CompositionLocalProvider(
|
|
108
103
|
AppTheme provides appTheme,
|
|
109
104
|
PlatformApi provides composeApi,
|
|
110
105
|
AppNavigator provides Navigator(),
|
|
111
|
-
AppStatusBar provides
|
|
106
|
+
AppStatusBar provides appStatusBarHeight,
|
|
107
|
+
ApplicationContext provides applicationContext,
|
|
108
|
+
AppConfig provides config
|
|
112
109
|
) {
|
|
113
110
|
content()
|
|
114
111
|
}
|