@momo-kits/native-kits 0.151.2-test.2 → 0.151.2-test.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.
@@ -47,6 +47,8 @@ kotlin {
47
47
  implementation(libs.coil.multiplatform.network.ktor)
48
48
  implementation(libs.jetbrains.serialization.json)
49
49
  implementation(libs.kotlinx.datetime)
50
+ implementation(libs.compottie)
51
+ implementation(libs.compottie.res)
50
52
  api(project(":NativeMaxApi"))
51
53
  }
52
54
  }
@@ -0,0 +1 @@
1
+ {"v":"5.10.2","fr":30,"ip":0,"op":50,"w":100,"h":100,"nm":"Spinner3_white","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"loading","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[0],"e":[200]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[200],"e":[360]},{"t":35}],"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[44.124,44.124,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[90,90],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.996078431373,0.956862745098,0.980392156863,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[44.124,44.124],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.392],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":18,"s":[0],"e":[100]},{"t":50}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[100]},{"t":28}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"loading 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0],"e":[200]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":27,"s":[200],"e":[360]},{"t":37}],"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[44.124,44.124,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[90,90],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.98431372549,0.835294117647,0.917647058824,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[44.124,44.124],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.392],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0],"e":[100]},{"t":49}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[0],"e":[100]},{"t":30}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":30,"op":50,"st":2,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"base","sr":1,"ks":{"o":{"a":0,"k":40,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[44.124,44.124,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[90,90],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.996078431373,0.956862745098,0.980392156863,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[44.124,44.124],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0}],"markers":[]}
@@ -2,6 +2,7 @@ package vn.momo.kits.application
2
2
 
3
3
  import androidx.compose.runtime.Composable
4
4
  import androidx.compose.runtime.CompositionLocalProvider
5
+ import androidx.compose.runtime.Immutable
5
6
  import androidx.compose.runtime.LaunchedEffect
6
7
  import androidx.compose.runtime.getValue
7
8
  import androidx.compose.runtime.mutableStateOf
@@ -63,6 +64,7 @@ class Navigator {
63
64
  }
64
65
  }
65
66
 
67
+ @Immutable
66
68
  data class MiniAppContext(
67
69
  val appIcon: String = "",
68
70
  val appName: Any? = null,
@@ -156,6 +158,7 @@ val AppLanguage = staticCompositionLocalOf<String?> {
156
158
  null
157
159
  }
158
160
 
161
+ @Immutable
159
162
  data class KitConfig(
160
163
  val trustBanner: TrustBannerData? = null,
161
164
  val headerBar: String? = null,
@@ -11,23 +11,30 @@ import androidx.compose.foundation.layout.height
11
11
  import androidx.compose.foundation.layout.padding
12
12
  import androidx.compose.foundation.layout.size
13
13
  import androidx.compose.foundation.shape.RoundedCornerShape
14
- import androidx.compose.material3.CircularProgressIndicator
15
14
  import androidx.compose.runtime.Composable
15
+ import androidx.compose.runtime.getValue
16
16
  import androidx.compose.runtime.remember
17
17
  import androidx.compose.ui.Alignment
18
18
  import androidx.compose.ui.Modifier
19
+ import androidx.compose.ui.draw.alpha
19
20
  import androidx.compose.ui.draw.clip
20
21
  import androidx.compose.ui.graphics.Color
21
22
  import androidx.compose.ui.text.TextStyle
22
23
  import androidx.compose.ui.text.style.TextOverflow
23
24
  import androidx.compose.ui.unit.Dp
24
25
  import androidx.compose.ui.unit.dp
26
+ import io.github.alexzhirkevich.compottie.Compottie
27
+ import io.github.alexzhirkevich.compottie.LottieCompositionSpec
28
+ import io.github.alexzhirkevich.compottie.animateLottieCompositionAsState
29
+ import io.github.alexzhirkevich.compottie.rememberLottieComposition
30
+ import io.github.alexzhirkevich.compottie.rememberLottiePainter
25
31
  import vn.momo.kits.const.AppTheme
26
32
  import vn.momo.kits.const.Colors
27
33
  import vn.momo.kits.const.Radius
28
34
  import vn.momo.kits.const.Spacing
29
35
  import vn.momo.kits.const.Typography
30
36
  import vn.momo.kits.modifier.activeOpacityClickable
37
+ import vn.momo.uikits.resources.Res
31
38
 
32
39
  enum class ButtonType {
33
40
  PRIMARY,
@@ -145,22 +152,35 @@ fun RenderLeading(
145
152
  ) {
146
153
  if (loading) {
147
154
  Box(Modifier.padding(end = marginRight)) {
148
- CircularProgressIndicator(
155
+ val composition by rememberLottieComposition {
156
+ LottieCompositionSpec.JsonString(
157
+ Res.readBytes("files/spinner_white.json").decodeToString()
158
+ )
159
+ }
160
+
161
+ val progress by animateLottieCompositionAsState(
162
+ composition = composition,
163
+ iterations = Compottie.IterateForever
164
+ )
165
+
166
+ Image(
167
+ source = rememberLottiePainter(
168
+ composition = composition,
169
+ progress = { progress }
170
+ ),
149
171
  modifier = Modifier.size(iconSize - 4.dp),
172
+ )
173
+ }
174
+ } else {
175
+ if (iconLeft.isNotEmpty()) {
176
+ Icon(
177
+ source = iconLeft,
150
178
  color = color,
151
- trackColor = Color.Transparent,
152
- strokeWidth = 2.dp
179
+ size = iconSize,
180
+ modifier = Modifier.padding(end = marginRight)
153
181
  )
154
182
  }
155
183
  }
156
- if (iconLeft.isNotEmpty()) {
157
- Icon(
158
- source = iconLeft,
159
- color = color,
160
- size = iconSize,
161
- modifier = Modifier.padding(end = marginRight)
162
- )
163
- }
164
184
  }
165
185
  }
166
186
 
@@ -261,7 +281,7 @@ fun Button(
261
281
  }
262
282
  }.activeOpacityClickable(
263
283
  activeOpacity = 0.5f,
264
- enabled = isEnabled,
284
+ enabled = isEnabled && !loading,
265
285
  onClick = onClick
266
286
  )
267
287
 
@@ -272,7 +292,8 @@ fun Button(
272
292
  .then(getTypeStyle(type, size = size))
273
293
  .padding(horizontal = sizeSpecs.padding)
274
294
  .defaultMinSize(minWidth = sizeSpecs.width)
275
- .height(sizeSpecs.height),
295
+ .height(sizeSpecs.height)
296
+ .alpha(if(loading) 0.75f else 1f),
276
297
  horizontalArrangement = Arrangement.Center,
277
298
  verticalAlignment = Alignment.CenterVertically,
278
299
  ) {
@@ -2,7 +2,6 @@ package vn.momo.kits.components.datetimepicker
2
2
 
3
3
  import androidx.compose.runtime.Composable
4
4
  import androidx.compose.runtime.remember
5
- import kotlinx.datetime.Clock
6
5
  import kotlinx.datetime.DatePeriod
7
6
  import kotlinx.datetime.LocalDateTime
8
7
  import kotlinx.datetime.TimeZone
@@ -10,6 +9,8 @@ import kotlinx.datetime.minus
10
9
  import kotlinx.datetime.number
11
10
  import kotlinx.datetime.plus
12
11
  import kotlinx.datetime.toLocalDateTime
12
+ import kotlin.time.Clock.System.now
13
+ import kotlin.time.ExperimentalTime
13
14
 
14
15
  /**
15
16
  * Format a LocalDateTime object into a string
@@ -149,8 +150,9 @@ val timeMode = listOf("AM", "PM")
149
150
  /**
150
151
  * Get today's date
151
152
  */
153
+ @OptIn(ExperimentalTime::class)
152
154
  fun getCurrentDateTime(): LocalDateTime {
153
- return Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
155
+ return now().toLocalDateTime(TimeZone.currentSystemDefault())
154
156
  }
155
157
 
156
158
  /**
@@ -2,6 +2,7 @@ package vn.momo.kits.navigation
2
2
 
3
3
  import androidx.compose.foundation.gestures.ScrollableState
4
4
  import androidx.compose.runtime.Composable
5
+ import androidx.compose.runtime.Stable
5
6
  import androidx.compose.runtime.State
6
7
  import androidx.compose.runtime.mutableStateOf
7
8
  import androidx.compose.runtime.staticCompositionLocalOf
@@ -66,6 +67,7 @@ val LocalNavigation = staticCompositionLocalOf<Navigation> {
66
67
  error("No NavigationStack provided")
67
68
  }
68
69
 
70
+ @Stable
69
71
  data class NavigationOptions(
70
72
  val onBackHandler: (() -> Unit)? = null,
71
73
  val hiddenBack: Boolean = false,
@@ -87,6 +89,6 @@ data class KeyboardOptions(
87
89
 
88
90
 
89
91
  data class ScrollData(
90
- val scrollable: Boolean = false,
92
+ val scrollable: Boolean = true,
91
93
  val scrollState: ScrollableState? = null,
92
94
  )
@@ -1,6 +1,7 @@
1
1
 
2
2
  import Foundation
3
3
  import SwiftUI
4
+ import Lottie
4
5
 
5
6
  // MARK: - ButtonType
6
7
 
@@ -27,13 +28,14 @@ public enum ButtonSize {
27
28
  public struct Button: View {
28
29
  // MARK: Lifecycle
29
30
 
30
- public init(title: String = "", action: @escaping () -> Void, type: ButtonType = .primary, size: ButtonSize = .large, iconLeft: AnyView? = nil, iconRight: AnyView? = nil) {
31
+ public init(title: String = "", action: @escaping () -> Void, type: ButtonType = .primary, size: ButtonSize = .large, iconLeft: AnyView? = nil, iconRight: AnyView? = nil, loading: Bool = false) {
31
32
  self.title = title
32
33
  self.action = action
33
34
  self.type = type
34
35
  self.size = size
35
36
  self.iconLeft = iconLeft
36
37
  self.iconRight = iconRight
38
+ self.loading = loading
37
39
  }
38
40
 
39
41
  // MARK: Public
@@ -41,13 +43,26 @@ public struct Button: View {
41
43
  public var body: some View {
42
44
  SwiftUI.Button(action: action) {
43
45
  HStack {
44
- iconLeft
46
+ if (loading) {
47
+ LottieView(
48
+ name: "lottie_circle_loader",
49
+ loopMode: .loop,
50
+ contentMode: .center
51
+ )
52
+ .frame(width: 32, height: 32)
53
+ .foregroundColor(.white)
54
+ .colorMultiply(.white)
55
+ } else {
56
+ iconLeft
57
+ }
45
58
  renderTitle(title: title, type: type, size: size).lineLimit(1).truncationMode(.tail)
46
59
  iconRight
47
60
  }
48
61
  .buttonSize(size)
49
62
  .buttonType(type)
50
- }.disabled(type == .disabled)
63
+ .opacity(loading ? 0.75 : 1.0)
64
+ }
65
+ .disabled(type == .disabled && !loading)
51
66
  .clipShape(RoundedRectangle(cornerRadius: Radius.S))
52
67
  }
53
68
 
@@ -59,6 +74,7 @@ public struct Button: View {
59
74
  var size: ButtonSize = .large
60
75
  var iconLeft: AnyView?
61
76
  var iconRight: AnyView?
77
+ var loading: Bool
62
78
 
63
79
  func renderTitle(title: String, type: ButtonType, size: ButtonSize) -> Text {
64
80
  switch size {
@@ -0,0 +1,86 @@
1
+ //
2
+ // LottieView.swift
3
+ // MoMoPlatform
4
+ //
5
+ // Created by thanhdat on 16/01/2023.
6
+ // Copyright © 2023 Facebook. All rights reserved.
7
+ //
8
+
9
+ import SwiftUI
10
+ import Lottie
11
+
12
+ struct LottieView: UIViewRepresentable {
13
+ var name: String = ""
14
+ var url: String?
15
+ var loopMode: LottieLoopMode = .playOnce
16
+ var contentMode : UIView.ContentMode = .scaleAspectFit
17
+ var onDone: (() -> Void)?
18
+
19
+ // Add static cache for animations
20
+ private static var animationCache = NSCache<NSString, LottieAnimation>()
21
+
22
+ func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
23
+ let view = UIView(frame: .zero)
24
+
25
+ // Configure Lottie with optimized settings
26
+ let configuration = LottieConfiguration(
27
+ renderingEngine: .coreAnimation,
28
+ decodingStrategy: .dictionaryBased
29
+ )
30
+
31
+ let animationView = LottieAnimationView(configuration: configuration)
32
+
33
+ if let url = url {
34
+ // Use cached animation if available
35
+ if let cachedAnimation = Self.animationCache.object(forKey: url as NSString) {
36
+ animationView.animation = cachedAnimation
37
+ setupAnimationView(animationView, view)
38
+ } else {
39
+ LottieAnimation.loadedFrom(url: URL(string: url)!, closure: { animation in
40
+ if let animation = animation {
41
+ Self.animationCache.setObject(animation, forKey: url as NSString)
42
+ animationView.animation = animation
43
+ setupAnimationView(animationView, view)
44
+ }
45
+ }, animationCache: nil)
46
+ }
47
+ } else {
48
+ // Use cached animation if available
49
+ if let cachedAnimation = Self.animationCache.object(forKey: name as NSString) {
50
+ animationView.animation = cachedAnimation
51
+ setupAnimationView(animationView, view)
52
+ } else if let animation = LottieAnimation.named(name) {
53
+ Self.animationCache.setObject(animation, forKey: name as NSString)
54
+ animationView.animation = animation
55
+ setupAnimationView(animationView, view)
56
+ }
57
+ }
58
+
59
+ return view
60
+ }
61
+
62
+ private func setupAnimationView(_ animationView: LottieAnimationView, _ containerView: UIView) {
63
+ animationView.contentMode = contentMode
64
+ animationView.loopMode = loopMode
65
+ animationView.backgroundBehavior = loopMode == .loop ? .pauseAndRestore : .pause
66
+
67
+ // Optimize rendering
68
+ animationView.shouldRasterizeWhenIdle = true
69
+
70
+ animationView.translatesAutoresizingMaskIntoConstraints = false
71
+ containerView.addSubview(animationView)
72
+
73
+ NSLayoutConstraint.activate([
74
+ animationView.widthAnchor.constraint(equalTo: containerView.widthAnchor),
75
+ animationView.heightAnchor.constraint(equalTo: containerView.heightAnchor)
76
+ ])
77
+
78
+ animationView.play { finished in
79
+ if finished {
80
+ onDone?()
81
+ }
82
+ }
83
+ }
84
+
85
+ func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {}
86
+ }
@@ -0,0 +1,55 @@
1
+ //
2
+ // LottieViewController.swift
3
+ // MoMoPlatform
4
+ //
5
+ // Created by Đại Trịnh on 26/09/2023.
6
+ // Copyright © 2023 Facebook. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+ import Lottie
11
+ import SwiftUI
12
+
13
+ public class LottieViewController: UIViewController {
14
+ var name: String = ""
15
+ var url: String?
16
+ var loopMode: LottieLoopMode = .playOnce
17
+ var frame: CGRect
18
+ var id: String?
19
+
20
+ init(name: String, url: String? = nil, loopMode: LottieLoopMode, frame: CGRect, id: String? = nil) {
21
+ self.name = name
22
+ self.url = url
23
+ self.loopMode = loopMode
24
+ self.frame = frame
25
+ self.id = id
26
+
27
+ super.init(nibName: nil, bundle: nil)
28
+ }
29
+
30
+ required init?(coder: NSCoder) {
31
+ fatalError("init(coder:) has not been implemented")
32
+ }
33
+
34
+ public override func viewDidLoad() {
35
+ super.viewDidLoad()
36
+ setupCustomView()
37
+ }
38
+
39
+ private func setupCustomView() {
40
+ let basicView = UIView()
41
+ basicView.frame = frame
42
+
43
+ let customLottieView = LottieView(name: name, url: url, loopMode: loopMode)
44
+ let hostViewController = UIHostingController(rootView: customLottieView)
45
+ hostViewController.view.frame = basicView.bounds;
46
+ hostViewController.view.backgroundColor = .clear
47
+
48
+ // set accessibility label
49
+ basicView.accessibilityLabel = id ?? "" // Set accessibility label directly to basicView
50
+ basicView.isAccessibilityElement = true // Ensure basicView is treated as an accessible element
51
+
52
+ basicView.addSubview(hostViewController.view)
53
+ view.addSubview(basicView)
54
+ }
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.151.2-test.2",
3
+ "version": "0.151.2-test.4",
4
4
  "private": false,
5
5
  "dependencies": {
6
6
  "@momo-platform/native-max-api": "1.0.18"