@momo-kits/native-kits 0.113.3-beta.6 → 0.113.3-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.
@@ -0,0 +1,58 @@
1
+ package vn.momo.kits.application
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.layout.Box
5
+ import androidx.compose.foundation.layout.fillMaxWidth
6
+ import androidx.compose.foundation.layout.height
7
+ import androidx.compose.foundation.layout.offset
8
+ import androidx.compose.foundation.layout.padding
9
+ import androidx.compose.foundation.layout.width
10
+ import androidx.compose.foundation.shape.RoundedCornerShape
11
+ import androidx.compose.runtime.Composable
12
+ import androidx.compose.ui.Modifier
13
+ import androidx.compose.ui.graphics.Color
14
+ import androidx.compose.ui.unit.dp
15
+ import androidx.compose.ui.zIndex
16
+ import vn.momo.kits.components.InputSearch
17
+ import vn.momo.kits.components.InputSearchProps
18
+ import vn.momo.kits.const.Radius
19
+ import vn.momo.kits.const.Spacing
20
+
21
+ @Composable
22
+ fun AnimationSearchInput(
23
+ animations: HeaderAnimations,
24
+ inputSearchProps: InputSearchProps
25
+ ) {
26
+ Box(
27
+ modifier = Modifier
28
+ .fillMaxWidth()
29
+ .offset(
30
+ x = animations.translateX.dp,
31
+ y = animations.translateY.dp
32
+ )
33
+ ) {
34
+ Box(
35
+ modifier = Modifier
36
+ .height(HEADER_HEIGHT.dp)
37
+ .padding(vertical = Spacing.S)
38
+ .zIndex(1f)
39
+ ) {
40
+ Box(
41
+ modifier = Modifier
42
+ .width(animations.width.dp)
43
+ .background(
44
+ color = animations.backgroundSearch,
45
+ shape = RoundedCornerShape(Radius.XL)
46
+ )
47
+ ) {
48
+ InputSearch(
49
+ inputSearchProps = inputSearchProps.copy(
50
+ showButtonText = false,
51
+ backgroundColor = Color.Transparent
52
+ )
53
+ )
54
+ }
55
+ }
56
+ }
57
+
58
+ }
@@ -26,6 +26,7 @@ import androidx.compose.ui.Modifier
26
26
  import androidx.compose.ui.graphics.graphicsLayer
27
27
  import androidx.compose.ui.semantics.semantics
28
28
  import androidx.compose.ui.semantics.testTag
29
+ import androidx.compose.ui.unit.Dp
29
30
  import androidx.compose.ui.unit.dp
30
31
  import androidx.compose.ui.zIndex
31
32
  import kotlinx.coroutines.flow.distinctUntilChanged
@@ -47,10 +48,11 @@ fun Header(
47
48
  title: String,
48
49
  headerRight: @Composable (() -> Unit)? = null,
49
50
  goBack: (() -> Unit)? = null,
50
- opacity: Float? = null,
51
+ opacity: Float = 1f,
51
52
  animatedHeader: AnimatedHeader? = null,
52
53
  scrollState: Int = 0,
53
54
  inputSearchProps: InputSearchProps? = null,
55
+ headerRightWidth: Dp = 0.dp,
54
56
  useAnimationSearch: Boolean = false
55
57
  ) {
56
58
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
@@ -65,6 +67,12 @@ fun Header(
65
67
  .distinctUntilChanged()
66
68
  .collect { fraction -> colorFraction = fraction }
67
69
  }
70
+ val animations = useHeaderSearchAnimation(
71
+ opacityAni = opacity,
72
+ scrollState = scrollState,
73
+ headerRightWidth = headerRightWidth,
74
+ isBack = goBack != null
75
+ )
68
76
 
69
77
  val backgroundSearch = animateColor(
70
78
  Colors.black_01,
@@ -72,15 +80,18 @@ fun Header(
72
80
  colorFraction
73
81
  )
74
82
 
83
+ val searchAnimationEnable =
84
+ inputSearchProps != null && useAnimationSearch && headerType == HeaderType.EXTENDED
85
+
75
86
  if (headerType != HeaderType.NONE) {
76
87
  BoxWithConstraints(
77
- Modifier.height(statusBarHeight + 52.dp)
88
+ Modifier.height(statusBarHeight + HEADER_HEIGHT.dp)
78
89
  .fillMaxWidth()
79
90
  .zIndex(10f),
80
91
  contentAlignment = Alignment.BottomCenter
81
92
  ) {
82
93
  Row(
83
- modifier = Modifier.height(52.dp)
94
+ modifier = Modifier.height(HEADER_HEIGHT.dp)
84
95
  .fillMaxWidth()
85
96
  .padding(horizontal = Spacing.M),
86
97
  verticalAlignment = Alignment.CenterVertically,
@@ -120,7 +131,7 @@ fun Header(
120
131
 
121
132
  if (useAnimationSearch || inputSearchProps == null) {
122
133
  Box(Modifier.graphicsLayer {
123
- alpha = if (animatedHeader !== null) opacity ?: 1f else 1f
134
+ alpha = if (animatedHeader !== null) opacity else 1f
124
135
  }) {
125
136
  headerRight?.invoke()
126
137
  }
@@ -129,7 +140,7 @@ fun Header(
129
140
 
130
141
  // Title
131
142
  Row(
132
- modifier = Modifier.height(52.dp)
143
+ modifier = Modifier.height(HEADER_HEIGHT.dp)
133
144
  .fillMaxWidth()
134
145
  .padding(horizontal = Spacing.M),
135
146
  verticalAlignment = Alignment.CenterVertically,
@@ -143,9 +154,9 @@ fun Header(
143
154
  Box(
144
155
  Modifier.weight(1f).graphicsLayer {
145
156
  alpha = if (useAnimationSearch) {
146
- 1f - (opacity ?: 1f)
157
+ 1f - (opacity)
147
158
  } else {
148
- if (animatedHeader !== null) opacity ?: 1f else 1f
159
+ if (animatedHeader !== null) opacity else 1f
149
160
  }
150
161
  },
151
162
  contentAlignment = if (titlePosition == TitlePosition.LEFT) Alignment.TopStart else Alignment.Center
@@ -161,6 +172,13 @@ fun Header(
161
172
  }
162
173
  }
163
174
  }
175
+
176
+ if (searchAnimationEnable) {
177
+ AnimationSearchInput(
178
+ animations = animations,
179
+ inputSearchProps = inputSearchProps!!
180
+ )
181
+ }
164
182
  }
165
183
  }
166
184
  }
@@ -8,9 +8,6 @@ import androidx.compose.runtime.Composable
8
8
  import androidx.compose.runtime.getValue
9
9
  import androidx.compose.ui.Modifier
10
10
  import androidx.compose.ui.graphics.Color
11
- import androidx.compose.ui.unit.Dp
12
- import androidx.compose.ui.unit.dp
13
- import vn.momo.kits.components.InputSearchProps
14
11
  import vn.momo.kits.const.AppStatusBar
15
12
  import vn.momo.kits.const.AppTheme
16
13
  import vn.momo.kits.platform.getStatusBarHeight
@@ -35,17 +32,13 @@ fun animateColor(start: Color, stop: Color, fraction: Float): Color {
35
32
 
36
33
  @Composable
37
34
  fun HeaderBackground(
38
- isBack: Boolean = true,
39
35
  headerType: HeaderType = HeaderType.DEFAULT,
40
36
  scrollState: Int,
41
- inputSearchProps: InputSearchProps? = null,
42
- headerRightWidth: Dp = 0.dp,
43
- useAnimationSearch: Boolean = false
44
37
  ) {
45
38
  val statusBarHeight = AppStatusBar.current ?: getStatusBarHeight()
46
39
 
47
40
  val opacityAni by animateFloatAsState(
48
- targetValue = (1 - (scrollState / 52f)).coerceIn(0f, 1f),
41
+ targetValue = (1 - (scrollState / HEADER_HEIGHT*1f)).coerceIn(0f, 1f),
49
42
  )
50
43
 
51
44
  val backgroundColor by animateColorAsState(
@@ -66,12 +59,7 @@ fun HeaderBackground(
66
59
 
67
60
  HeaderType.EXTENDED -> {
68
61
  HeaderExtended(
69
- scrollState = scrollState,
70
- inputSearchProps = inputSearchProps,
71
- headerRightWidth = headerRightWidth,
72
- useAnimationSearch = useAnimationSearch,
73
62
  opacityAni = opacityAni,
74
- isBack = isBack,
75
63
  statusBarHeight = statusBarHeight,
76
64
  backgroundColor = backgroundColor,
77
65
  )
@@ -24,7 +24,7 @@ fun HeaderDefault(
24
24
  opacityAni: Float,
25
25
  statusBarHeight: Dp,
26
26
  ) {
27
- val height = statusBarHeight + 52.dp
27
+ val height = statusBarHeight + HEADER_HEIGHT.dp
28
28
  Box(
29
29
  modifier = Modifier.fillMaxWidth().height(height)
30
30
  .background(AppTheme.current.colors.background.surface)
@@ -1,18 +1,10 @@
1
1
  package vn.momo.kits.application
2
2
 
3
- import androidx.compose.animation.animateColorAsState
4
- import androidx.compose.animation.core.animateFloatAsState
5
3
  import androidx.compose.foundation.background
6
4
  import androidx.compose.foundation.layout.Box
7
5
  import androidx.compose.foundation.layout.fillMaxWidth
8
6
  import androidx.compose.foundation.layout.height
9
- import androidx.compose.foundation.layout.offset
10
- import androidx.compose.foundation.layout.padding
11
- import androidx.compose.foundation.layout.width
12
- import androidx.compose.foundation.shape.RoundedCornerShape
13
7
  import androidx.compose.runtime.Composable
14
- import androidx.compose.runtime.getValue
15
- import androidx.compose.ui.Alignment
16
8
  import androidx.compose.ui.Modifier
17
9
  import androidx.compose.ui.draw.alpha
18
10
  import androidx.compose.ui.geometry.Offset
@@ -22,115 +14,62 @@ import androidx.compose.ui.layout.ContentScale
22
14
  import androidx.compose.ui.platform.LocalDensity
23
15
  import androidx.compose.ui.unit.Dp
24
16
  import androidx.compose.ui.unit.dp
25
- import androidx.compose.ui.zIndex
26
17
  import vn.momo.kits.components.Image
27
- import vn.momo.kits.components.InputSearch
28
- import vn.momo.kits.components.InputSearchProps
29
18
  import vn.momo.kits.components.Options
30
19
  import vn.momo.kits.const.AppTheme
31
- import vn.momo.kits.const.Colors
32
- import vn.momo.kits.const.Radius
33
- import vn.momo.kits.const.Spacing
34
- import vn.momo.kits.platform.getScreenDimensions
35
20
  import vn.momo.kits.utils.bottomBorder
36
21
 
37
22
  @Composable
38
23
  fun HeaderExtended(
39
24
  opacityAni: Float,
40
- inputSearchProps: InputSearchProps?,
41
- useAnimationSearch: Boolean = true,
42
- scrollState: Int,
43
- headerRightWidth: Dp,
44
- isBack: Boolean,
45
25
  statusBarHeight: Dp,
46
26
  backgroundColor: Color
47
27
  ) {
48
- val animations = useHeaderSearchAnimation(
49
- opacityAni = opacityAni,
50
- scrollState = scrollState,
51
- headerRightWidth = headerRightWidth,
52
- statusBarHeight = statusBarHeight,
53
- isBack = isBack
54
- )
55
-
56
28
  Box(
57
- modifier = Modifier.fillMaxWidth()
58
- .height(animations.height.dp)
59
- ) {
60
- Box(
61
- modifier = Modifier
62
- .fillMaxWidth()
63
- .height(154.dp)
64
- .alpha(opacityAni)
65
- .background(
66
- Brush.linearGradient(
67
- colors = listOf(
68
- Color(0xFFFDCADE),
69
- Color(0x00FDCADE)
70
- ),
71
- start = Offset(0f, 0f),
72
- end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
73
- )
74
- )
75
- ) {
76
- if (AppTheme.current.assets.headerBackground != null) {
77
- Box(
78
- modifier = Modifier
79
- .fillMaxWidth()
80
- .background(backgroundColor)
81
- ) {
82
- Image(
83
- loading = false,
84
- source = AppTheme.current.assets.headerBackground!!,
85
- modifier = Modifier.fillMaxWidth().height(154.dp),
86
- options = Options(
87
- contentScale = ContentScale.FillWidth
88
- )
89
- )
90
- }
91
- }
92
- }
93
-
94
- Box(
95
- modifier = Modifier
96
- .fillMaxWidth()
97
- .alpha(1f - opacityAni)
98
- .height(statusBarHeight + 52.dp)
99
- .background(
100
- AppTheme.current.colors.background.surface
101
- )
102
- .bottomBorder(
103
- strokeWidth = 1.dp,
104
- color = AppTheme.current.colors.border.default
29
+ modifier = Modifier
30
+ .fillMaxWidth()
31
+ .height(154.dp)
32
+ .alpha(opacityAni)
33
+ .background(
34
+ Brush.linearGradient(
35
+ colors = listOf(
36
+ Color(0xFFFDCADE),
37
+ Color(0x00FDCADE)
38
+ ),
39
+ start = Offset(0f, 0f),
40
+ end = Offset(0f, with(LocalDensity.current) { 154.dp.toPx() })
105
41
  )
106
- )
107
- if (inputSearchProps != null && useAnimationSearch) {
42
+ )
43
+ ) {
44
+ if (AppTheme.current.assets.headerBackground != null) {
108
45
  Box(
109
46
  modifier = Modifier
110
- .height(52.dp)
111
- .offset(
112
- x = animations.translateX.dp
113
- )
114
- .padding(vertical = Spacing.S)
115
- .align(Alignment.BottomStart)
116
- .zIndex(1f)
47
+ .fillMaxWidth()
48
+ .background(backgroundColor)
117
49
  ) {
118
- Box(
119
- modifier = Modifier
120
- .width(animations.width.dp)
121
- .background(
122
- color = animations.backgroundSearch,
123
- shape = RoundedCornerShape(Radius.XL)
124
- )
125
- ) {
126
- InputSearch(
127
- inputSearchProps = inputSearchProps.copy(
128
- showButtonText = false,
129
- backgroundColor = Color.Transparent
130
- )
50
+ Image(
51
+ loading = false,
52
+ source = AppTheme.current.assets.headerBackground!!,
53
+ modifier = Modifier.fillMaxWidth().height(154.dp),
54
+ options = Options(
55
+ contentScale = ContentScale.FillWidth
131
56
  )
132
- }
57
+ )
133
58
  }
134
59
  }
135
60
  }
61
+
62
+ Box(
63
+ modifier = Modifier
64
+ .fillMaxWidth()
65
+ .alpha(1f - opacityAni)
66
+ .height(statusBarHeight + HEADER_HEIGHT.dp)
67
+ .background(
68
+ AppTheme.current.colors.background.surface
69
+ )
70
+ .bottomBorder(
71
+ strokeWidth = 1.dp,
72
+ color = AppTheme.current.colors.border.default
73
+ )
74
+ )
136
75
  }
@@ -115,11 +115,12 @@ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
115
115
  }
116
116
 
117
117
  fun onPressClose() {
118
- api?.request("dismiss", "", {_ -> })
118
+ api?.request("dismiss", "", { _ -> })
119
119
  }
120
120
 
121
121
  fun onPressMore() {
122
- api?.request("showTools",
122
+ api?.request(
123
+ "showTools",
123
124
  mapOf(
124
125
  "tools" to headerRight?.tools?.map { it.toMap() },
125
126
  "context" to mapOf(
@@ -180,10 +181,18 @@ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
180
181
  group.items.any { it.showBadge }
181
182
  }
182
183
 
184
+ val toolkitWidth = if (context !== null) 65.dp else 28.dp
185
+
186
+ var isShowShortcut = headerRight?.useShortcut == true
187
+ //backup case context null of more icon
188
+ if (headerRight?.useMore == true && context == null) {
189
+ isShowShortcut = false
190
+ }
191
+
183
192
  Row(
184
193
  verticalAlignment = Alignment.CenterVertically
185
194
  ) {
186
- if (headerRight?.useShortcut == true) {
195
+ if (isShowShortcut) {
187
196
  NavigationButton(
188
197
  disabled = isLoading,
189
198
  icon = navButtonConfig.icon,
@@ -197,20 +206,26 @@ fun ToolkitHeaderRight(headerRight: HeaderRightData? = null) {
197
206
  modifier = Modifier
198
207
  .padding(start = Spacing.S)
199
208
  .border(0.2.dp, Color(0x33000000), shape = RoundedCornerShape(14.dp))
200
- .width(65.dp)
209
+ .width(toolkitWidth)
201
210
  .height(28.dp)
202
211
  .clip(shape = RoundedCornerShape(14.dp))
203
212
  .background(Color(0x99FFFFFF))
204
213
  ) {
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
+ if (context != null) {
215
+ Icon(
216
+ source = "help_center",
217
+ size = 20.dp,
218
+ modifier = Modifier.padding(4.dp).clickable {
219
+ onPressHelpCenter()
220
+ })
221
+ Box(
222
+ modifier = Modifier
223
+ .width(0.5.dp)
224
+ .height(12.dp)
225
+ .background(Colors.black_20)
226
+ )
227
+ }
228
+
214
229
  Icon(
215
230
  source = "16_navigation_close_circle",
216
231
  size = 20.dp,
@@ -9,6 +9,8 @@ import androidx.compose.runtime.remember
9
9
  import androidx.compose.runtime.setValue
10
10
  import androidx.compose.runtime.staticCompositionLocalOf
11
11
  import androidx.compose.ui.unit.Dp
12
+ import kotlinx.serialization.Serializable
13
+ import kotlinx.serialization.json.internal.FormatLanguage
12
14
  import vn.momo.kits.components.TrustBannerData
13
15
  import vn.momo.kits.const.AppStatusBar
14
16
  import vn.momo.kits.const.AppTheme
@@ -48,6 +50,10 @@ data class MiniAppContext(
48
50
  val appId: String = "",
49
51
  val appCode: String = "",
50
52
  val description: Any? = null,
53
+ val support: Map<String, Any?> = emptyMap(),
54
+ val toolkitConfig: Map<String, Any?> = emptyMap(),
55
+ val providerId: String = "",
56
+ val permissions: List<Map<String, Any>>? = emptyList()
51
57
  )
52
58
 
53
59
  val PlatformApi = staticCompositionLocalOf<Any?> { null }
@@ -64,6 +70,10 @@ val AppConfig = staticCompositionLocalOf<KitConfig?> {
64
70
  null
65
71
  }
66
72
 
73
+ val AppLanguage = staticCompositionLocalOf<String?> {
74
+ null
75
+ }
76
+
67
77
  data class KitConfig(
68
78
  val trustBanner: TrustBannerData? = null,
69
79
  val headerBar: String? = null,
@@ -77,6 +87,7 @@ fun ApplicationContainer(
77
87
  statusBarHeight: Dp? = AppStatusBar.current,
78
88
  applicationContext: MiniAppContext? = null,
79
89
  config: KitConfig? = null,
90
+ language: String? = null,
80
91
  content: @Composable () -> Unit,
81
92
  ) {
82
93
  var appTheme by remember { mutableStateOf(theme) }
@@ -105,7 +116,8 @@ fun ApplicationContainer(
105
116
  AppNavigator provides Navigator(),
106
117
  AppStatusBar provides appStatusBarHeight,
107
118
  ApplicationContext provides applicationContext,
108
- AppConfig provides config
119
+ AppConfig provides config,
120
+ AppLanguage provides language
109
121
  ) {
110
122
  content()
111
123
  }
@@ -47,6 +47,8 @@ enum class HeaderType {
47
47
  NONE
48
48
  }
49
49
 
50
+ const val HEADER_HEIGHT = 52
51
+
50
52
  @Composable
51
53
  fun Screen(
52
54
  backgroundColor: Color? = null,
@@ -85,7 +87,7 @@ fun Screen(
85
87
  val bottomPadding = min(indicator, 21.dp)
86
88
 
87
89
  val opacity by animateFloatAsState(
88
- targetValue = ((scrollState.value / 52f)).coerceIn(0f, 1f),
90
+ targetValue = ((scrollState.value / HEADER_HEIGHT*1f)).coerceIn(0f, 1f),
89
91
  )
90
92
 
91
93
  val headerAnimated =
@@ -101,10 +103,7 @@ fun Screen(
101
103
  if (animatedHeader === null) {
102
104
  HeaderBackground(
103
105
  headerType = headerType,
104
- scrollState = scrollState.value,
105
- inputSearchProps= inputSearchProps,
106
- useAnimationSearch = useAnimationSearch,
107
- headerRightWidth = headerRightWidth
106
+ scrollState = scrollState.value
108
107
  )
109
108
  } else {
110
109
  Box(
@@ -113,21 +112,17 @@ fun Screen(
113
112
  .graphicsLayer { alpha = opacity }
114
113
  ) {
115
114
  HeaderBackground(
116
- isBack = isBack,
117
115
  headerType = headerType,
118
- scrollState = scrollState.value,
119
- inputSearchProps= inputSearchProps,
120
- useAnimationSearch = useAnimationSearch,
121
- headerRightWidth = headerRightWidth
116
+ scrollState = scrollState.value
122
117
  )
123
118
  }
124
119
  }
125
-
126
120
  Header(
127
121
  headerType = headerType,
128
122
  title = title,
129
123
  titlePosition = titlePosition,
130
124
  headerRight = headerRight,
125
+ headerRightWidth = headerRightWidth,
131
126
  goBack = goBack,
132
127
  opacity = opacity,
133
128
  animatedHeader = animatedHeader,
@@ -138,7 +133,7 @@ fun Screen(
138
133
 
139
134
  Column(
140
135
  modifier = Modifier.fillMaxSize()
141
- .padding(top = if (animatedHeader === null && headerType !== HeaderType.NONE) statusBarHeight + 52.dp else 0.dp)
136
+ .padding(top = if (animatedHeader === null && headerType !== HeaderType.NONE) statusBarHeight + HEADER_HEIGHT.dp else 0.dp)
142
137
  .pointerInput(Unit) {
143
138
  detectTapGestures(onTap = {
144
139
  keyboardController?.hide()
@@ -162,11 +157,11 @@ fun Screen(
162
157
  headerAnimated()
163
158
  Column {
164
159
  if (animatedHeader !== null) {
165
- Spacer(modifier = Modifier.padding(top = statusBarHeight + 52.dp + layoutOffset))
160
+ Spacer(modifier = Modifier.padding(top = statusBarHeight + HEADER_HEIGHT.dp + layoutOffset))
166
161
  }
167
162
 
168
163
  if (useAnimationSearch && inputSearchProps != null && headerType == HeaderType.EXTENDED) {
169
- Spacer(modifier = Modifier.padding(top = statusBarHeight))
164
+ Spacer(modifier = Modifier.padding(top = (HEADER_HEIGHT.dp + Spacing.S)))
170
165
  }
171
166
  content()
172
167
  }
@@ -16,7 +16,7 @@ data class HeaderAnimations(
16
16
  val backgroundSearch: Color,
17
17
  val translateX: Float,
18
18
  val width: Float,
19
- val height: Float
19
+ val translateY: Float
20
20
  )
21
21
 
22
22
  private const val SCREEN_PADDING = 12
@@ -27,43 +27,39 @@ fun useHeaderSearchAnimation(
27
27
  opacityAni: Float,
28
28
  scrollState: Int,
29
29
  headerRightWidth: Dp,
30
- statusBarHeight: Dp,
31
30
  isBack: Boolean
32
31
  ): HeaderAnimations {
33
32
  val screenWidth = getScreenDimensions().width
34
- val leftPosition = if (isBack) (BACK_WIDTH + 20).dp else 20.dp
33
+ val leftPosition = if (isBack) (BACK_WIDTH + 20).dp else 12.dp
35
34
  val searchWidth = screenWidth - (SCREEN_PADDING * 2)
36
35
 
37
36
  val backgroundSearch by animateColorAsState(
38
37
  targetValue = animateColor(
39
- AppTheme.current.colors.background.default,
40
38
  Colors.black_01,
39
+ AppTheme.current.colors.background.default,
41
40
  opacityAni
42
41
  ),
43
42
  )
44
43
 
45
44
  val animatedTranslateX by animateFloatAsState(
46
- targetValue = (scrollState / 52f).coerceIn(0f, 1f) * (leftPosition.value - 12) + 12,
45
+ targetValue = (scrollState / HEADER_HEIGHT*1f).coerceIn(0f, 1f) * (leftPosition.value - 12) + 12,
47
46
  )
48
47
 
49
48
  val animatedWidth by animateFloatAsState(
50
- targetValue = (scrollState / 52f).coerceIn(
49
+ targetValue = (scrollState / HEADER_HEIGHT*1f).coerceIn(
51
50
  0f,
52
51
  1f
53
52
  ) * ((searchWidth - leftPosition.value - headerRightWidth.value + 12) - searchWidth) + searchWidth,
54
53
  )
55
54
 
56
- val animatedHeight by animateFloatAsState(
57
- targetValue = (1 - (scrollState / 52f).coerceIn(
58
- 0f,
59
- 1f
60
- )) * (statusBarHeight.value + 94 - (statusBarHeight.value + 52)) + (statusBarHeight.value + 52),
55
+ val animatedTranslateY by animateFloatAsState(
56
+ targetValue = (1 - (scrollState / HEADER_HEIGHT* 1f).coerceIn(0f, 1f)) * HEADER_HEIGHT
61
57
  )
62
58
  return HeaderAnimations(
63
59
  opacity = opacityAni,
64
60
  backgroundSearch = backgroundSearch,
65
61
  translateX = animatedTranslateX,
66
62
  width = animatedWidth,
67
- height = animatedHeight
63
+ translateY = animatedTranslateY
68
64
  )
69
65
  }
@@ -20,6 +20,7 @@ import androidx.compose.ui.layout.ContentScale
20
20
  import androidx.compose.ui.unit.dp
21
21
  import androidx.compose.ui.unit.sp
22
22
  import vn.momo.kits.application.AppConfig
23
+ import vn.momo.kits.application.AppLanguage
23
24
  import vn.momo.kits.modifier.noFeedbackClickable
24
25
 
25
26
  val defaultBanner = TrustBannerData(
@@ -62,10 +63,10 @@ fun TrustBanner(
62
63
  serviceName: String = "",
63
64
  screenName: String = "",
64
65
  onPress: ((Map<String, String>) -> Unit)? = null,
65
- trackEvent: ((String, Map<String, String>) -> Unit)? = null,
66
- language: String = "vi"
66
+ trackEvent: ((String, Map<String, String>) -> Unit)? = null
67
67
  ) {
68
68
  val appConfig = AppConfig.current
69
+ val language = AppLanguage.current ?: "vi"
69
70
  val trustBanner = appConfig?.trustBanner ?: defaultBanner
70
71
  val trackParams = mapOf(
71
72
  "service_name" to serviceName,
@@ -23,12 +23,20 @@ public class MiniAppContext {
23
23
  }
24
24
  }
25
25
 
26
+ public class KitConfig {
27
+ public var trustBanner: TrustBannerData? = nil
28
+ public var headerBar: String? = nil
29
+ public var headerGradient: String? = nil
30
+ }
31
+
26
32
  public class ApplicationEnvironment: ObservableObject {
27
33
  let applicationContext: MiniAppContext?
28
- let composeApi: KitComposeApi
34
+ let composeApi: KitComposeApi?
35
+ let config: KitConfig?
29
36
 
30
- public init(applicationContext: MiniAppContext?, composeApi: KitComposeApi) {
37
+ public init(applicationContext: MiniAppContext? = nil, composeApi: KitComposeApi? = nil, config: KitConfig? = nil) {
31
38
  self.applicationContext = applicationContext
32
39
  self.composeApi = composeApi
40
+ self.config = config
33
41
  }
34
42
  }
@@ -203,9 +203,7 @@ public struct Header: View {
203
203
  Text(title)
204
204
  .font(.headline)
205
205
  .foregroundColor(tintIconColor)
206
- .frame(maxWidth: titlePosition == .center ? UIScreen.main.bounds.width - 252 : nil)
207
- .frame(maxWidth: titlePosition == .center ? .infinity : UIScreen.main.bounds.width - 172, alignment: titlePosition == .center ? .center : .leading)
208
- .lineLimit(1)
206
+ .frame(maxWidth: titlePosition == .center ? .infinity : nil, alignment: titlePosition == .center ? .center : .leading)
209
207
  }
210
208
  Spacer()
211
209
  }
@@ -95,7 +95,7 @@ struct ToolkitHeaderRight: View {
95
95
 
96
96
  // MARK: - Actions
97
97
  private func onPressShortcut() {
98
- environment.composeApi.request(
98
+ environment.composeApi?.request(
99
99
  funcName: "onToolAction",
100
100
  params: ["item": ["key": "onFavorite"]]
101
101
  ) { _ in
@@ -112,14 +112,14 @@ struct ToolkitHeaderRight: View {
112
112
  "icon": context?.appIcon,
113
113
  "description": context?.description
114
114
  ]
115
- environment.composeApi.request(
115
+ environment.composeApi?.request(
116
116
  funcName: "showHelpCenter",
117
117
  params: paramMap
118
118
  ) { _ in }
119
119
  }
120
120
 
121
121
  private func onPressClose() {
122
- environment.composeApi.request(
122
+ environment.composeApi?.request(
123
123
  funcName: "dismiss",
124
124
  params: ""
125
125
  ) { _ in }
@@ -137,7 +137,7 @@ struct ToolkitHeaderRight: View {
137
137
  "description": context?.description
138
138
  ]
139
139
  ]
140
- environment.composeApi.request(
140
+ environment.composeApi?.request(
141
141
  funcName: "showTools",
142
142
  params: params
143
143
  ) { response in
@@ -34,7 +34,7 @@ public struct Screen<Content: View>: View {
34
34
  verticalAlignment: VerticalAlignment = .top,
35
35
  horizontalAlignment: HorizontalAlignment = .leading,
36
36
  title: String = "Stack",
37
- titlePosition: TitlePosition = .left,
37
+ titlePosition: TitlePosition = .center,
38
38
  goBack: (() -> Void)? = nil,
39
39
  scrollable: Bool = true,
40
40
  onContentLayout: ((CGRect) -> Void)? = nil,
@@ -11,179 +11,110 @@ import SDWebImageSwiftUI
11
11
 
12
12
  let jsonUrl = "https://static.momocdn.net/app/json/component-kits/design_system.json"
13
13
 
14
- struct TrustBannerData: Decodable {
15
- let trustBanner: TrustBanner
16
- }
17
-
18
- struct TrustBanner: Decodable {
19
- let content: Dictionary<String, String>
14
+ public struct TrustBannerData: Decodable {
15
+ let content: [String: String]
16
+ let subContent: [String: String]
20
17
  let pciImage: String
21
18
  let sslImage: String
22
19
  let urlConfig: String
23
- let featureCode: String
24
20
  let icons: [String]
25
21
  let momoImage: String
26
-
22
+
27
23
  enum CodingKeys: String, CodingKey {
28
- case content, pciImage, sslImage, icons, momoImage
24
+ case content
25
+ case subContent
26
+ case pciImage
27
+ case sslImage
28
+ case icons
29
+ case momoImage
29
30
  case urlConfig = "url_config"
30
- case featureCode = "feature_code"
31
31
  }
32
32
  }
33
33
 
34
- let defaultBanner = TrustBanner(
35
- content: ["vi": "Bảo mật thông tin & An toàn tài sản của bạn là ưu tiên hàng đầu của MoMo.",
36
- "en": "Your data security and money safety are MoMo's top priorities."],
34
+ let defaultBanner = TrustBannerData(
35
+ content: ["vi": "An toàn tài sản & Bảo mật thông tin của bạn là ưu tiên hàng đầu của MoMo.",
36
+ "en": "Ensuring financial security and data privacy is MoMo's highest priority."],
37
+ subContent: [
38
+ "vi": "Tìm hiểu thêm",
39
+ "en": "Learn more"
40
+ ],
37
41
  pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/pci.png",
38
42
  sslImage: "https://static.momocdn.net/app/img/kits/trustBanner/ssl.png",
39
- urlConfig: "https://momo.vn/an-toan-bao-mat",
40
- featureCode: "login_and_security",
43
+ urlConfig: "login_and_security",
41
44
  icons: [
42
45
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_viettinbank.png",
43
46
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_agribank.png",
44
47
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_vietcombank.png",
45
48
  "https://static.momocdn.net/app/img/kits/trustBanner/ic_bidv.png"
46
49
  ],
47
- momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_momo_secu.png"
50
+ momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_secu.png"
48
51
  )
49
52
 
50
- let defaultData = TrustBannerData(trustBanner: defaultBanner)
51
-
52
- class TrustBannerViewModel: ObservableObject {
53
- @Published var trustBanner: TrustBannerData?
54
-
55
- init() {
56
- fetchData(from: jsonUrl)
57
- }
58
-
59
- func fetchData(from urlString: String) {
60
- guard let url = URL(string: urlString) else { return }
61
-
62
- URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
63
- guard let self = self else { return }
64
-
65
- if let error = error {
66
- print("Data fetching error: \(error)")
67
- return
68
- }
69
-
70
- guard let data = data else {
71
- print("No data received")
72
- return
73
- }
74
- do {
75
- let decodedData = try JSONDecoder().decode(TrustBannerData.self, from: data)
76
- DispatchQueue.main.async {
77
- self.trustBanner = decodedData
78
- }
79
- } catch {
80
- print("Decoding error: \(error)")
81
- }
82
- }.resume()
83
- }
84
- }
85
-
86
- public struct TrustBannerView: View {
87
- var serviceName: String? = ""
88
- var screenName: String? = ""
89
- var onPress: ((_ params: NSDictionary) -> Void)?
90
- var trackEvent: ((_ eventName: String, _ params: NSDictionary) -> Void)?
91
- var language: String?
92
-
93
- @ObservedObject private var viewModel = TrustBannerViewModel()
94
-
95
- public init(serviceName: String?,
96
- screenName: String?,
97
- onPress: ((_ params: NSDictionary) -> Void)?,
98
- trackEvent: ((_ eventName: String,_ params: NSDictionary) -> Void)?, language: String? = "vi") {
99
- if let trackEvent = trackEvent {
100
- trackEvent("service_component_displayed", ["service_name": serviceName ?? "", "screen_name": screenName ?? "", "component_name": "logo_trust"])
101
- }
53
+ public struct TrustBanner: View {
54
+ @EnvironmentObject var applicationEnvironment: ApplicationEnvironment
55
+
56
+ var onPress: ((String)->Void)?
57
+
58
+ public init(onPress: ((String)->Void)?) {
102
59
  self.onPress = onPress
103
- self.trackEvent = trackEvent
104
- self.screenName = screenName
105
- self.serviceName = serviceName
106
- self.language = language
107
60
  }
108
-
109
- public var body: some View {
110
- if let trustBanner = viewModel.trustBanner {
111
- TrustBannerContentView(trustBanner: trustBanner.trustBanner, onPress: onPress, trackEvent: trackEvent, serviceName: serviceName, screenName: screenName, language: language)
61
+
62
+ var language: String? = "vi"
63
+
64
+ func onPressSecurity() {
65
+ if (onPress != nil) {
66
+ onPress!(applicationEnvironment.config?.trustBanner?.urlConfig ?? defaultBanner.urlConfig)
112
67
  }
113
68
  }
114
- }
115
-
116
- struct TrustBannerContentView: View {
117
- var trustBanner: TrustBanner
118
- var onPress: ((_ params: NSDictionary) -> Void)?
119
- var trackEvent: ((_ eventName: String, _ params: NSDictionary) -> Void)?
120
- var serviceName: String?
121
- var screenName: String?
122
- var language: String?
123
69
 
124
- var body: some View {
125
- GeometryReader { geo in
126
- HStack(alignment: .center, spacing: 8) {
127
- WebImage(url: URL(string: trustBanner.momoImage))
128
- .resizable()
129
- .scaledToFit()
130
- .frame(width: 64, height: 64)
131
- VStack(alignment: .leading, spacing: 8){
132
- Text(trustBanner.content[language ?? "vi"] ?? "")
133
- .foregroundColor(Color(hex: 0xFF484848))
134
- .lineLimit(2)
135
- .font(.system(size: 12, weight: Font.Weight.regular))
136
- .frame(width: geo.size.width - 24 - 64 - 8, height: 40,alignment: .leading)
70
+
71
+ public var body: some View {
72
+ HStack(alignment: .center, spacing: 8) {
73
+ WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.momoImage ?? defaultBanner.momoImage))
74
+ .resizable()
75
+ .scaledToFit()
76
+ .frame(width: 64, height: 64)
77
+ VStack(alignment: .leading){
78
+ Text(applicationEnvironment.config?.trustBanner?.content[language ?? "vi"] as? String ?? defaultBanner.content[language ?? "vi"])
79
+ .foregroundColor(Color(hex: "484848"))
80
+ .lineLimit(2)
81
+ .font(.system(size: 13, weight: Font.Weight.regular))
82
+ .lineSpacing(3)
83
+ .padding(.bottom, 8)
84
+ HStack {
85
+ HStack(spacing: 0) {
86
+ Text(applicationEnvironment.config?.trustBanner?.subContent[language ?? "vi"] as? String ?? defaultBanner.subContent[language ?? "vi"])
87
+ .foregroundColor(Colors.pink03)
88
+ .font(.system(size: 13, weight: .bold))
89
+ .padding(.trailing, 4)
90
+
91
+ Icon(source: "arrow_chevron_right_small", color: Colors.pink03)
92
+ }.onTapGesture {
93
+ onPressSecurity()
94
+ }
95
+
96
+ Spacer()
137
97
  HStack{
138
- HStack{
139
- WebImage(url: URL(string: trustBanner.pciImage))
140
- .resizable()
141
- .scaledToFit()
142
- .frame(width: 52, height: 20)
143
- WebImage(url: URL(string: trustBanner.sslImage))
144
- .resizable()
145
- .scaledToFit()
146
- .frame(width: 52, height: 20)
147
- }
148
- Spacer()
149
- HStack(spacing: -6){
150
- ForEach(trustBanner.icons, id: \.self) { iconURL in
151
- WebImage(url: URL(string: iconURL))
152
- .resizable()
153
- .scaledToFit()
154
- .frame(width:24, height: 24)
155
- .background(Color.white)
156
- .cornerRadius(12)
157
- .overlay(
158
- Circle().stroke(Color(hex: 0xFFE8E8E8), lineWidth: 1)
159
- )
160
- }
161
- Text("36+")
162
- .font(.system(size: 10, weight: Font.Weight.regular))
163
- .frame(width: 24, height: 24, alignment: .center)
164
- .foregroundColor(Color(hex: 0xFFEB2F96))
165
- .background(Color(hex: 0xFFFDEAF4))
166
- .cornerRadius(12)
167
- .overlay(
168
- Circle()
169
- .stroke(Color(hex: 0xFFE8E8E8), lineWidth: 1)
170
- )
171
- }.frame(width: 96, height: 24)
98
+ WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.pciImage ?? defaultBanner.pciImage))
99
+ .resizable()
100
+ .scaledToFit()
101
+ .frame(width: 24, height: 20)
102
+ WebImage(url: URL(string: applicationEnvironment.config?.trustBanner?.sslImage ?? defaultBanner.sslImage))
103
+ .resizable()
104
+ .scaledToFit()
105
+ .frame(width: 52, height: 20)
172
106
  }
173
107
  }
108
+
174
109
  }
175
- .frame(width: geo.size.width - 24)
176
- .padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
177
- .background(Color(hex: 0xFFF2F8FF))
178
- .cornerRadius(12)
179
- .onTapGesture {
180
- if let trackEvent = trackEvent {
181
- trackEvent("service_component_clicked", ["service_name": self.serviceName ?? "", "screen_name": self.screenName ?? "", "component_name": "logo_trust", "url_config": trustBanner.urlConfig])
182
- }
183
- if let onPress = onPress {
184
- onPress(["service_name": self.serviceName ?? "", "screen_name": self.screenName ?? "", "component_name": "logo_trust", "url_config": trustBanner.urlConfig, "feature_code": trustBanner.featureCode])
185
- }
186
- }
110
+
111
+ }
112
+ .padding(.all, 12)
113
+ .background(Color(hex: "F2F8FF"))
114
+ .cornerRadius(12)
115
+ .onTapGesture {
116
+ onPressSecurity()
187
117
  }
118
+ .frame(maxWidth: UIScreen.main.bounds.size.width)
188
119
  }
189
120
  }
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
- #Thu Jan 09 10:43:10 ICT 2025
8
- sdk.dir=/Users/sophia/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.113.3-beta.6",
3
+ "version": "0.113.3-beta.7",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},