@momo-kits/native-kits 0.89.18 → 0.89.20

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.
Files changed (41) hide show
  1. package/compose/build.gradle.kts +18 -14
  2. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +22 -0
  3. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Components.kt +79 -26
  4. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +200 -0
  5. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +47 -0
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +7 -139
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +89 -22
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +17 -1
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +6 -4
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +6 -8
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +40 -17
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +54 -21
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +155 -0
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +2 -20
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +185 -0
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -19
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +2 -18
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +58 -0
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +37 -0
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +65 -0
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +43 -0
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +23 -6
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PromotionPopup.kt +8 -2
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +14 -1
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +2 -2
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +28 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +1 -2
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +18 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -23
  30. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +33 -0
  31. package/package.json +1 -1
  32. package/settings.gradle.kts +1 -0
  33. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Platform.kt +0 -3
  34. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +0 -8
  35. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Utils.android.kt +0 -14
  36. package/compose/src/commonMain/composeResources/drawable/test.jpg +0 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Platform.kt +0 -3
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +0 -5
  39. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Platform.kt +0 -3
  40. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +0 -9
  41. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Utils.ios.kt +0 -18
@@ -1,48 +1,23 @@
1
1
  package vn.momo.kits.application
2
2
 
3
- import androidx.compose.foundation.background
4
- import androidx.compose.foundation.clickable
5
- import androidx.compose.foundation.interaction.MutableInteractionSource
6
- import androidx.compose.foundation.layout.Box
7
3
  import androidx.compose.foundation.layout.WindowInsets
8
4
  import androidx.compose.foundation.layout.asPaddingValues
9
- import androidx.compose.foundation.layout.fillMaxSize
10
- import androidx.compose.foundation.layout.padding
11
5
  import androidx.compose.foundation.layout.systemBars
12
- import androidx.compose.material.ExperimentalMaterialApi
13
6
  import androidx.compose.runtime.Composable
14
7
  import androidx.compose.runtime.CompositionLocalProvider
15
- import androidx.compose.runtime.remember
16
8
  import androidx.compose.runtime.staticCompositionLocalOf
17
- import androidx.compose.ui.Alignment
18
- import androidx.compose.ui.Modifier
19
- import androidx.compose.ui.graphics.Color
20
- import androidx.compose.ui.unit.Dp
21
- import androidx.compose.ui.unit.dp
22
- import cafe.adriel.voyager.core.screen.Screen
23
- import cafe.adriel.voyager.navigator.LocalNavigator
24
- import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
25
- import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
26
- import cafe.adriel.voyager.navigator.currentOrThrow
27
- import cafe.adriel.voyager.transitions.SlideTransition
28
9
  import vn.momo.kits.const.AppStatusBar
29
10
  import vn.momo.kits.const.AppTheme
30
- import vn.momo.kits.const.Spacing
31
11
  import vn.momo.kits.const.Theme
32
12
  import vn.momo.kits.const.defaultTheme
13
+ import vn.momo.kits.platform.disableOverscroll
33
14
 
34
15
  val AppNavigator = staticCompositionLocalOf<Navigator> {
35
16
  error("no navigator provided!")
36
17
  }
37
18
 
38
- class Navigator(
39
- private val stack: cafe.adriel.voyager.navigator.Navigator,
40
- private val sheet: BottomSheetNavigator,
41
- ) {
19
+ class Navigator {
42
20
  fun push(content: @Composable () -> Unit) {
43
- stack.push(StackScreen {
44
- content()
45
- })
46
21
  }
47
22
 
48
23
  fun present(content: @Composable () -> Unit) {
@@ -54,21 +29,12 @@ class Navigator(
54
29
  }
55
30
 
56
31
  fun pop() {
57
- if (sheet.isVisible) {
58
- sheet.hide()
59
- } else {
60
- stack.pop()
61
- }
62
32
  }
63
33
 
64
34
  fun replace(content: @Composable () -> Unit) {
65
- stack.replace(StackScreen {
66
- content()
67
- })
68
35
  }
69
36
 
70
37
  fun popToTop() {
71
- stack.popUntilRoot()
72
38
  }
73
39
 
74
40
  fun showModal(
@@ -76,122 +42,24 @@ class Navigator(
76
42
  canBackgroundClose: Boolean = true,
77
43
  content: @Composable () -> Unit,
78
44
  ) {
79
- sheet.show(ModalScreen(onClose = onClose, barrierDismissible = canBackgroundClose) {
80
- content()
81
- })
82
45
  }
83
46
 
84
47
  fun showBottomSheet(content: @Composable () -> Unit) {
85
- sheet.show(SheetScreen {
86
- content()
87
- })
88
48
  }
89
49
  }
90
50
 
91
- @OptIn(ExperimentalMaterialApi::class)
92
51
  @Composable
93
52
  fun ApplicationContainer(
94
53
  theme: Theme = defaultTheme,
95
- statusBarHeight: Dp = WindowInsets.systemBars.asPaddingValues().calculateTopPadding(),
96
54
  content: @Composable () -> Unit,
97
55
  ) {
56
+ disableOverscroll()
98
57
  CompositionLocalProvider(
99
58
  AppTheme provides theme,
100
- AppStatusBar provides statusBarHeight,
59
+ AppNavigator provides Navigator(),
60
+ AppStatusBar provides WindowInsets.systemBars.asPaddingValues().calculateTopPadding(),
101
61
  ) {
102
- BottomSheetNavigator(sheetBackgroundColor = Color.Transparent, sheetElevation = 0.dp) {
103
- cafe.adriel.voyager.navigator.Navigator(MainScreen {
104
- content()
105
- }) {
106
- SlideTransition(it)
107
- }
108
- }
109
- }
110
-
111
- }
112
-
113
- class MainScreen(private val content: @Composable () -> Unit) : Screen {
114
- @Composable
115
- override fun Content() {
116
- CompositionLocalProvider(
117
- AppNavigator provides Navigator(
118
- LocalNavigator.currentOrThrow,
119
- LocalBottomSheetNavigator.current
120
- ),
121
- ) {
122
- content()
123
- }
124
- }
125
- }
126
-
127
- class StackScreen(private val content: @Composable () -> Unit) : Screen {
128
- @Composable
129
- override fun Content() {
130
- CompositionLocalProvider(
131
- AppNavigator provides Navigator(
132
- LocalNavigator.currentOrThrow,
133
- LocalBottomSheetNavigator.current
134
- ),
135
- ) {
136
- content()
137
- }
62
+ content()
138
63
  }
139
- }
140
64
 
141
- class SheetScreen(private val content: @Composable () -> Unit) : Screen {
142
- @Composable
143
- override fun Content() {
144
- CompositionLocalProvider(
145
- AppNavigator provides Navigator(
146
- LocalNavigator.currentOrThrow,
147
- LocalBottomSheetNavigator.current
148
- ),
149
- ) {
150
- content()
151
- }
152
- }
153
- }
154
-
155
- class ModalScreen(
156
- private val onClose: () -> Unit,
157
- private val barrierDismissible: Boolean = true,
158
- private val content: @Composable () -> Unit,
159
- ) :
160
- Screen {
161
- @Composable
162
- override fun Content() {
163
- val navigator = Navigator(
164
- LocalNavigator.currentOrThrow,
165
- LocalBottomSheetNavigator.current
166
- )
167
- CompositionLocalProvider(
168
- AppNavigator provides navigator,
169
- ) {
170
- Box(
171
- modifier = Modifier.fillMaxSize().padding(horizontal = Spacing.XL)
172
- .background(Color.Transparent)
173
- .clickable(
174
- interactionSource = remember { MutableInteractionSource() },
175
- indication = null,
176
- onClick = {
177
- if (barrierDismissible) {
178
- navigator.pop()
179
- onClose()
180
- }
181
- }
182
- ),
183
- contentAlignment = Alignment.Center
184
- ) {
185
- Box(
186
- modifier = Modifier.clickable(
187
- interactionSource = remember { MutableInteractionSource() },
188
- indication = null
189
- ) {},
190
- contentAlignment = Alignment.Center
191
- ) {
192
- content()
193
- }
194
- }
195
- }
196
- }
197
- }
65
+ }
@@ -1,10 +1,12 @@
1
1
  package vn.momo.kits.application
2
2
 
3
+ import androidx.compose.animation.core.animateFloatAsState
3
4
  import androidx.compose.foundation.background
4
5
  import androidx.compose.foundation.gestures.detectTapGestures
5
6
  import androidx.compose.foundation.layout.Arrangement
6
7
  import androidx.compose.foundation.layout.Box
7
8
  import androidx.compose.foundation.layout.Column
9
+ import androidx.compose.foundation.layout.Spacer
8
10
  import androidx.compose.foundation.layout.WindowInsets
9
11
  import androidx.compose.foundation.layout.asPaddingValues
10
12
  import androidx.compose.foundation.layout.fillMaxSize
@@ -16,14 +18,18 @@ import androidx.compose.foundation.layout.systemBars
16
18
  import androidx.compose.foundation.rememberScrollState
17
19
  import androidx.compose.foundation.verticalScroll
18
20
  import androidx.compose.runtime.Composable
21
+ import androidx.compose.runtime.getValue
19
22
  import androidx.compose.ui.Alignment
20
23
  import androidx.compose.ui.Modifier
21
24
  import androidx.compose.ui.graphics.Color
25
+ import androidx.compose.ui.graphics.graphicsLayer
22
26
  import androidx.compose.ui.input.pointer.pointerInput
23
27
  import androidx.compose.ui.platform.LocalSoftwareKeyboardController
24
28
  import androidx.compose.ui.unit.Dp
25
29
  import androidx.compose.ui.unit.dp
26
30
  import androidx.compose.ui.unit.min
31
+ import androidx.compose.ui.zIndex
32
+ import vn.momo.kits.const.AppStatusBar
27
33
  import vn.momo.kits.const.AppTheme
28
34
  import vn.momo.kits.const.Colors
29
35
  import vn.momo.kits.const.Spacing
@@ -51,6 +57,9 @@ fun Screen(
51
57
  useAvoidKeyboard: Boolean = true,
52
58
  footer: @Composable (() -> Unit)? = null,
53
59
  headerRight: @Composable (() -> Unit)? = null,
60
+ fabProps: FabProps? = null,
61
+ animatedHeader: AnimatedHeader? = null,
62
+ layoutOffset: Dp = 56.dp,
54
63
  content: @Composable () -> Unit,
55
64
  ) {
56
65
  val keyboardController = LocalSoftwareKeyboardController.current
@@ -62,50 +71,108 @@ fun Screen(
62
71
  else -> 0.dp
63
72
  }
64
73
 
74
+ val indicator = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
75
+ val bottomPadding = min(indicator, 21.dp)
76
+ val scrollState = rememberScrollState()
77
+
78
+ val opacity by animateFloatAsState(
79
+ targetValue = ((scrollState.value / 114f)).coerceIn(0f, 1f)
80
+ )
81
+
82
+ val headerAnimated =
83
+ @Composable { animatedHeader?.composable?.invoke(scrollState.value) }
84
+
65
85
  Box(
66
- Modifier.fillMaxSize().background(backgroundColor ?: AppTheme.current.colors.background.default)
86
+ Modifier.fillMaxSize()
87
+ .background(backgroundColor ?: AppTheme.current.colors.background.default)
67
88
  ) {
68
- HeaderImage(headerType, headerBackground ?: AppTheme.current.assets.headerBackground)
89
+
90
+ if (animatedHeader === null) {
91
+ HeaderImage(
92
+ headerType,
93
+ headerBackground ?: AppTheme.current.assets.headerBackground
94
+ )
95
+ } else {
96
+ Box(
97
+ Modifier.zIndex(5f).background(Color.White.copy(alpha = opacity))
98
+ .graphicsLayer { alpha = opacity }) {
99
+ HeaderImage(
100
+ headerType,
101
+ headerBackground ?: AppTheme.current.assets.headerBackground
102
+ )
103
+ }
104
+ }
105
+
106
+ Header(
107
+ headerType = headerType,
108
+ title = title,
109
+ titlePosition = titlePosition,
110
+ headerRight = headerRight,
111
+ isBack = isBack,
112
+ goBack = goBack,
113
+ opacity = opacity,
114
+ animatedHeader = animatedHeader,
115
+ scrollState = scrollState.value
116
+ )
117
+
69
118
  Column(
70
- modifier = Modifier.fillMaxSize().pointerInput(Unit) {
71
- detectTapGestures(onTap = {
72
- keyboardController?.hide()
73
- })
74
- },
119
+ modifier = Modifier.fillMaxSize()
120
+ .padding(top = if (animatedHeader === null && headerType !== HeaderType.NONE) AppStatusBar.current + 52.dp else 0.dp)
121
+ .pointerInput(Unit) {
122
+ detectTapGestures(onTap = {
123
+ keyboardController?.hide()
124
+ })
125
+ }
126
+ .zIndex(1f),
75
127
  ) {
76
- Header(
77
- headerType = headerType,
78
- title = title,
79
- titlePosition = titlePosition,
80
- headerRight = headerRight,
81
- isBack = isBack,
82
- goBack = goBack,
83
- )
84
128
 
85
129
  Column(
86
130
  modifier = if (scrollable) Modifier.weight(1f).padding(bottom = keyboardSize)
87
- .verticalScroll(rememberScrollState()) else
131
+ .verticalScroll(scrollState) else
88
132
  Modifier.padding(bottom = keyboardSize),
89
133
  verticalArrangement = verticalArrangement,
90
134
  horizontalAlignment = horizontalAlignment
91
135
  ) {
92
- content()
136
+ Box {
137
+ headerAnimated()
138
+ Column {
139
+ if (animatedHeader !== null) {
140
+ Spacer(modifier = Modifier.padding(top = AppStatusBar.current + 52.dp + layoutOffset))
141
+ }
142
+ content()
143
+ }
144
+ }
93
145
  }
94
146
 
95
147
  footer?.let {
96
- Footer(footer, keyboardSize)
148
+ Footer(footer, keyboardSize, bottomPadding)
97
149
  }
98
150
  }
151
+
152
+ if (fabProps != null) {
153
+ FloatingButton(
154
+ scrollPosition = fabProps.scrollState?.value ?: scrollState.value,
155
+ onClick = fabProps.onClick,
156
+ containerColor = AppTheme.current.colors.primary,
157
+ bottom = fabProps.bottom
158
+ ?: if (footer != null) bottomPadding + 76.dp else bottomPadding + 12.dp,
159
+ icon = fabProps.icon,
160
+ iconColor = fabProps.iconColor,
161
+ text = fabProps.label,
162
+ size = fabProps.size,
163
+ position = fabProps.position ?: FABPosition.END,
164
+ keyboardSize = keyboardSize
165
+ )
166
+ }
99
167
  }
100
168
  }
101
169
 
102
170
  @Composable
103
- fun Footer(footer: @Composable (() -> Unit)? = null, keyboardSize: Dp = 0.dp) {
104
- val indicator = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
171
+ fun Footer(footer: @Composable (() -> Unit)? = null, keyboardSize: Dp = 0.dp, bottom: Dp = 0.dp) {
105
172
  Box(
106
173
  Modifier
107
- .offset(y = - keyboardSize)
108
- .padding(bottom = min(indicator, 21.dp))
174
+ .offset(y = -keyboardSize)
175
+ .padding(bottom = bottom)
109
176
  .shadow(
110
177
  Colors.black_20.copy(alpha = 0.1f),
111
178
  borderRadius = 0.dp,
@@ -18,10 +18,26 @@ import vn.momo.kits.const.Radius
18
18
  import vn.momo.kits.const.Spacing
19
19
  import vn.momo.kits.const.Typography
20
20
  import vn.momo.kits.const.scaleSize
21
- import vn.momo.kits.utils.primaryColors
21
+
22
+
22
23
 
23
24
  @Composable
24
25
  fun Badge(label: String = "Label", backgroundColor: Color? = null) {
26
+ val primaryColors = listOf(
27
+ Color(0xFFF0F0F0),
28
+ Color(0xFFEB2F96),
29
+ Color(0xFF962AF0),
30
+ Color(0xFF4E4BFF),
31
+ Color(0xFF007AFF),
32
+ Color(0xFF13C2C2),
33
+ Color(0xFF34C759),
34
+ Color(0xFFA0D911),
35
+ Color(0xFFFFCC00),
36
+ Color(0xFFFA8C16),
37
+ Color(0xFFFA541C),
38
+ Color(0xFFF5222D)
39
+ )
40
+
25
41
  fun isNumber(label: String): Boolean {
26
42
  val numberRegex = "^\\d+$".toRegex()
27
43
  return numberRegex.matches(label)
@@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier
22
22
  import androidx.compose.ui.draw.clip
23
23
  import androidx.compose.ui.graphics.Color
24
24
  import androidx.compose.ui.text.TextStyle
25
+ import androidx.compose.ui.text.style.TextOverflow
25
26
  import androidx.compose.ui.unit.Dp
26
27
  import androidx.compose.ui.unit.dp
27
28
  import vn.momo.kits.const.AppTheme
@@ -116,7 +117,7 @@ fun getTextColor(type: ButtonType): Color {
116
117
  fun RenderTitle(size: Size, title: String = "", type: ButtonType) {
117
118
  val style = getStyle(size)
118
119
  val color = getTextColor(type)
119
- Text(style = style, text = title, color = color)
120
+ Text(style = style, text = title, color = color, overflow = TextOverflow.Ellipsis, maxLines = 1)
120
121
  }
121
122
 
122
123
  @Composable
@@ -240,9 +241,10 @@ fun Button(
240
241
  isRipple: Boolean = true,
241
242
  useTintColor: Boolean = true,
242
243
  isFull: Boolean = true,
244
+ modifier: Modifier = Modifier,
243
245
  ) {
244
246
  val customModifier = if (isFull) {
245
- Modifier
247
+ modifier
246
248
  .fillMaxWidth()
247
249
  .clip(RoundedCornerShape(size.value.radius)).clickable(
248
250
  interactionSource = remember { MutableInteractionSource() },
@@ -252,7 +254,7 @@ fun Button(
252
254
  )
253
255
 
254
256
  } else {
255
- Modifier
257
+ modifier
256
258
  .clip(RoundedCornerShape(size.value.radius)).clickable(
257
259
  interactionSource = remember { MutableInteractionSource() },
258
260
  indication = if (isRipple) rememberRipple() else null,
@@ -285,4 +287,4 @@ fun Button(
285
287
  iconRight = iconRight
286
288
  )
287
289
  }
288
- }
290
+ }
@@ -11,8 +11,7 @@ import androidx.compose.ui.graphics.ColorFilter
11
11
  import androidx.compose.ui.layout.ContentScale
12
12
  import androidx.compose.ui.unit.Dp
13
13
  import androidx.compose.ui.unit.dp
14
- import com.skydoves.landscapist.ImageOptions
15
- import com.skydoves.landscapist.coil3.CoilImage
14
+ import coil3.compose.AsyncImage
16
15
  import vn.momo.kits.const.AppTheme
17
16
  import vn.momo.kits.utils.Icons
18
17
 
@@ -29,13 +28,12 @@ fun Icon(
29
28
  Icons[source]
30
29
  }
31
30
  Box(modifier.size(size), contentAlignment = Alignment.Center) {
32
- CoilImage(
31
+ AsyncImage(
33
32
  modifier = Modifier.fillMaxSize(),
34
- imageOptions = ImageOptions(
35
- contentScale = ContentScale.Fit,
36
- colorFilter = color?.let { ColorFilter.tint(it) }
37
- ),
38
- imageModel = { icon },
33
+ model = icon,
34
+ contentDescription = null,
35
+ contentScale = ContentScale.Fit,
36
+ colorFilter = color?.let { ColorFilter.tint(it) }
39
37
  )
40
38
  }
41
39
  }
@@ -1,15 +1,21 @@
1
1
  package vn.momo.kits.components
2
2
 
3
+ import androidx.compose.foundation.layout.Box
3
4
  import androidx.compose.runtime.Composable
5
+ import androidx.compose.runtime.getValue
6
+ import androidx.compose.runtime.mutableStateOf
7
+ import androidx.compose.runtime.remember
8
+ import androidx.compose.runtime.setValue
4
9
  import androidx.compose.ui.Alignment
5
10
  import androidx.compose.ui.Modifier
6
11
  import androidx.compose.ui.graphics.ColorFilter
7
12
  import androidx.compose.ui.graphics.DefaultAlpha
8
13
  import androidx.compose.ui.graphics.painter.Painter
9
14
  import androidx.compose.ui.layout.ContentScale
10
- import com.skydoves.landscapist.ImageOptions
11
- import com.skydoves.landscapist.coil3.CoilImage
12
- import org.jetbrains.compose.resources.ExperimentalResourceApi
15
+ import androidx.compose.ui.semantics.contentDescription
16
+ import androidx.compose.ui.semantics.semantics
17
+ import androidx.compose.ui.semantics.testTag
18
+ import coil3.compose.AsyncImage
13
19
  import vn.momo.kits.const.AppTheme
14
20
 
15
21
  data class Options(
@@ -19,37 +25,54 @@ data class Options(
19
25
  val alpha: Float = DefaultAlpha,
20
26
  )
21
27
 
22
- @OptIn(ExperimentalResourceApi::class)
28
+ enum class ImageState {
29
+ Loading, Success, Error
30
+ }
31
+
23
32
  @Composable
24
33
  fun Image(
25
- source: String,
34
+ source: Any,
26
35
  modifier: Modifier = Modifier,
27
36
  options: Options? = null,
28
37
  loading: Boolean = true,
29
38
  ) {
30
- var imageOptions = ImageOptions()
39
+ modifier.semantics {contentDescription = "img|$source"; testTag = "img|$source"}
40
+ var imageOptions = Options()
41
+ var imageState by remember { mutableStateOf(ImageState.Loading) }
42
+
31
43
  options?.let {
32
- imageOptions = ImageOptions(
44
+ imageOptions = Options(
33
45
  alignment = options.alignment,
34
46
  contentScale = options.contentScale,
35
47
  colorFilter = options.colorFilter,
36
48
  alpha = DefaultAlpha,
37
49
  )
38
50
  }
39
- CoilImage(
40
- modifier = modifier,
41
- imageOptions = imageOptions,
42
- imageModel = { source },
43
- loading = {
44
- if (loading) Skeleton()
45
- },
46
- failure = {
47
- Icon(
51
+
52
+ Box(modifier = modifier) {
53
+ AsyncImage(
54
+ modifier = modifier,
55
+ model = source,
56
+ contentDescription = null,
57
+ contentScale = imageOptions.contentScale,
58
+ alignment = imageOptions.alignment,
59
+ colorFilter = imageOptions.colorFilter,
60
+ alpha = imageOptions.alpha,
61
+ onLoading = { imageState = ImageState.Loading },
62
+ onSuccess = { imageState = ImageState.Success },
63
+ onError = { imageState = ImageState.Error },
64
+ )
65
+ when (imageState) {
66
+ ImageState.Loading -> Skeleton()
67
+ ImageState.Error -> Icon(
48
68
  source = "media_fail",
49
69
  color = AppTheme.current.colors.text.disable,
70
+ modifier = modifier
50
71
  )
72
+ ImageState.Success -> {}
51
73
  }
52
- )
74
+ }
75
+
53
76
  }
54
77
 
55
78
  @Composable