@momo-kits/native-kits 0.167.1-sizescale.2-debug → 0.300.1-debug

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 (56) hide show
  1. package/CLAUDE.md +4 -4
  2. package/README.md +10 -2
  3. package/build.gradle.kts +3 -1
  4. package/compose/build.gradle.kts +19 -20
  5. package/compose/build.gradle.kts.backup +18 -19
  6. package/compose/compose.podspec +1 -1
  7. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +1 -4
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +2 -2
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +3 -3
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +25 -16
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +23 -13
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +1 -1
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +3 -3
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +1 -1
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +11 -15
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +10 -4
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +6 -23
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +22 -9
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +28 -21
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +6 -1
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +6 -1
  24. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +26 -17
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +4 -5
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +47 -30
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +4 -2
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -1
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/ToolkitRequests.kt +33 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +40 -32
  31. package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/ToolkitRequestsTest.kt +47 -0
  32. package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +6 -2
  33. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +1 -3
  34. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +6 -11
  35. package/gradle/libs.versions.toml +9 -3
  36. package/gradle/wrapper/gradle-wrapper.properties +3 -1
  37. package/gradle.properties +1 -1
  38. package/ios/native-kits.podspec +1 -1
  39. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  40. package/package.json +1 -1
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitSizeScale.kt +0 -56
  42. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Example.xcscheme +0 -32
  43. package/example/ios/Example.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  44. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  45. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/WorkspaceSettings.xcsettings +0 -16
  46. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  47. package/example/ios/Example.xcworkspace/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  48. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  49. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  50. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  51. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  52. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  53. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios-LottiePrivacyInfo.xcscheme +0 -58
  54. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/lottie-ios.xcscheme +0 -58
  55. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/sonnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +0 -60
  56. package/local.properties +0 -8
package/CLAUDE.md CHANGED
@@ -7,7 +7,7 @@ Guidance for working in this repo. Keep it accurate — update it when structure
7
7
  `momo-native-kits` — MoMo's design-system / UI-kit as a **Kotlin Multiplatform + Compose Multiplatform** library, published for Android (AAR/Maven) and iOS (static framework + CocoaPods). It is the Compose port of the React Native `momo-kits`. Consumed by mini-apps embedded in the host MoMo app.
8
8
 
9
9
  - Artifact: `vn.momo.kits:kits` (version in `gradle.properties`). NPM mirror: `@momo-kits/native-kits`.
10
- - Targets: `androidTarget` (JVM 17, compile/min/target SDK 36/24/36), `iosArm64`, `iosSimulatorArm64` (framework `baseName = "kits"`, `isStatic = true`).
10
+ - Targets: AGP KMP `android` target (JVM 17, compile/min SDK 37/24; AGP 9.2.1, Gradle 9.5.0), `iosArm64`, `iosSimulatorArm64` (framework `baseName = "kits"`, `isStatic = true`).
11
11
  - Kotlin 2.3.10, Compose MP 1.10.3.
12
12
 
13
13
  ## Modules
@@ -26,11 +26,11 @@ Guidance for working in this repo. Keep it accurate — update it when structure
26
26
 
27
27
  ## Build & verify
28
28
 
29
- No unit-test suite is configured. Validate changes by compiling the relevant target(s); use the sample app for visual checks.
29
+ Run `./gradlew :compose:testAndroidHostTest` for the request ownership tests in `commonTest`. Compile the relevant Android/iOS targets and use the sample app for visual checks.
30
30
 
31
31
  ```bash
32
32
  ./gradlew :compose:compileCommonMainKotlinMetadata # validate commonMain (fast, do this first)
33
- ./gradlew :compose:compileDebugKotlinAndroid # Android target
33
+ ./gradlew :compose:compileAndroidMain # Android target
34
34
  ./gradlew :compose:linkDebugFrameworkIosSimulatorArm64 # iOS framework (slow)
35
35
  ./gradlew :sample:androidApp:installDebug # run demo on device/emulator
36
36
  ```
@@ -43,7 +43,7 @@ Publishing is automated (`publish.sh` + GitLab CI on `[ci build]` / `main`). Do
43
43
  - **Configuration flows via CompositionLocals**, not params. Public ones: `AppTheme`, `AppStatusBar`, `AppNavigationBar` (`const/Theme.kt`); `ApplicationContext`, `AppConfig`, `AppLanguage`, `LocalLocalize` (`application/`); `LocalNavigator`, `LocalMaxApi` (`navigation/`). Read with `X.current`.
44
44
  - **Design-system config** (`trustBanner`, `headerBar`, `headerGradient`): the host seeds `KitDesignSystemConfig.update(...)` once (same pattern as `KitFontScale`); `NavigationContainer` provides it as `LocalDesignSystemConfig` (param `designSystemConfig` → parent → global) and applies `headerBar` as the header image when the theme has none.
45
45
  - **Platform code** goes through `expect`/`actual` in `platform/` — never reference Android/iOS APIs from `commonMain` directly.
46
- - **Native bridge:** `IMaxApi` (`vn.momo.maxapi`, exposed as `LocalMaxApi`) is the host-app bridge (dismiss, tracking, device info, language).
46
+ - **Native bridge:** typed `IMaxApi` from NativeMaxApi `0.3.1` (`vn.momo.maxapi`, exposed as `LocalMaxApi`) is the host-app bridge. Toolkit uses `ToolkitOptions` to preserve custom groups and system-tool policy; pending callbacks are cancelled when their composition is disposed. Containers borrow the host client and must not close it.
47
47
  - Navigation uses a runtime `DynamicScreenRegistry`; overlays (modal/bottom-sheet/snackbar) go through `Navigator` + `OverplayComponentRegistry`.
48
48
  - **iOS swipe-back:** the host owns the only edge-pan recogniser and replays it through `HostBackGestureBridge` (`navigation/HostBackGestureBridge.kt`; the kit framework is never exported, so the host reaches it through its own Koin-provided facade): `checkCanPop` / `canPopInner` / `pop` route the back into the kit stack, `started` / `progressed` / `completed` / `cancelled` seek the NavHost transition, `shouldUseFloatingArrowBack` is set by bottom tabs. `NavigationContainer` attaches itself automatically (`AttachHostBackGesture`, iOS only, detached by identity on dispose) — hosts need no wrapper and no `setNavigator`. `maxApi` is a required parameter of `NavigationContainer` — every container, nested or root, is handed its host bridge explicitly.
49
49
 
package/README.md CHANGED
@@ -53,7 +53,7 @@ ComposeKit là bộ UI Kit đa nền tảng phục vụ Mini App/SDK của MoMo.
53
53
  ---
54
54
 
55
55
  ### Yêu cầu môi trường
56
- - JDK 17+, Gradle 8, Android Studio Hedgehog+ với Android SDK (compileSdk xem `libs.versions.toml`).
56
+ - JDK 17+ và Android SDK (compileSdk xem `libs.versions.toml`); wrapper Gradle `9.5.0`, AGP `9.2.1`.
57
57
  - Kotlin Multiplatform plugin + Compose Multiplatform (JetBrains 1.6.1+).
58
58
  - Xcode 15, CocoaPods 1.12+ để build `iosApp` hoặc publish Pod.
59
59
  - Truy cập mạng nội bộ GitLab Packages nếu cần publish/consume Maven repo `https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven`.
@@ -174,4 +174,12 @@ fun SuccessState() {
174
174
  ```
175
175
 
176
176
  - `shared/build.gradle.kts` đã bật `coreLibraryDesugaringEnabled` để tương thích `compottie-dot`/`compottie-network` với `minSdk = 24`.
177
- - Nếu animation dùng asset/font nội bộ, kết hợp `compottie-resources` (`rememberResourcesAssetsManager`, `rememberResourcesFontManager`) để trỏ về `composeResources`.
177
+ - Nếu animation dùng asset/font nội bộ, kết hợp `compottie-resources` (`rememberResourcesAssetsManager`, `rememberResourcesFontManager`) để trỏ về `composeResources`.
178
+ ### NativeMaxApi 0.3.1
179
+
180
+ Compose dùng typed `vn.momo.maxapi.IMaxApi` do host truyền vào `NavigationContainer`.
181
+ Toolkit dùng overload `showToolkit(ToolkitOptions, callback)` để giữ nhóm tool tùy chỉnh,
182
+ nhãn đa ngôn ngữ, icon, badge và `useSystemTools`. Callback trả `actionKey`; request đang chờ
183
+ được hủy khi composition bị gỡ. Kits không đóng client do host sở hữu.
184
+
185
+ Chạy `./gradlew :compose:testAndroidHostTest` để kiểm tra hủy request và callback đến muộn.
package/build.gradle.kts CHANGED
@@ -2,7 +2,9 @@ plugins {
2
2
  // this is necessary to avoid the plugins to be loaded multiple times
3
3
  // in each subproject's classloader
4
4
  alias(libs.plugins.android.application) apply false
5
- alias(libs.plugins.android.library) apply false
5
+ // AI-GENERATED START: use the AGP 9 Kotlin Multiplatform Android library target
6
+ alias(libs.plugins.android.kotlin.multiplatform.library) apply false
7
+ // AI-GENERATED END: use the AGP 9 Kotlin Multiplatform Android library target
6
8
  alias(libs.plugins.compose) apply false
7
9
  alias(libs.plugins.kotlin.compose.compiler) apply false
8
10
  alias(libs.plugins.jetbrains.kotlin.multiplatform) apply false
@@ -4,7 +4,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
4
4
 
5
5
  plugins {
6
6
  alias(libs.plugins.jetbrains.kotlin.multiplatform)
7
- alias(libs.plugins.android.library)
7
+ // AI-GENERATED START: use the AGP 9 Kotlin Multiplatform Android library target
8
+ alias(libs.plugins.android.kotlin.multiplatform.library)
9
+ // AI-GENERATED END: use the AGP 9 Kotlin Multiplatform Android library target
8
10
  alias(libs.plugins.compose)
9
11
  alias(libs.plugins.kotlin.compose.compiler)
10
12
  // alias(libs.plugins.vanniktech.mavenPublish)
@@ -22,12 +24,18 @@ val gitlabUser: String = findProperty("gitlab.user") as String? ?: ""
22
24
  val gitlabKey: String = findProperty("gitlab.password") as String? ?: ""
23
25
 
24
26
  kotlin {
25
- androidTarget {
27
+ // AI-GENERATED START: configure the AGP 9 Android target and host tests
28
+ android {
29
+ namespace = "vn.momo.kits.kits"
30
+ compileSdk = 35
31
+ minSdk = 24
32
+ androidResources.enable = true
33
+ withHostTest {}
26
34
  compilerOptions {
27
35
  jvmTarget.set(JvmTarget.JVM_17)
28
36
  }
29
- publishLibraryVariants("release")
30
37
  }
38
+ // AI-GENERATED END: configure the AGP 9 Android target and host tests
31
39
 
32
40
  val fwName = gitlabArtifactId
33
41
  val iosTargets = listOf(iosArm64(), iosSimulatorArm64())
@@ -40,7 +48,7 @@ kotlin {
40
48
  }
41
49
 
42
50
  cocoapods {
43
- version = "0.167.1-sizescale.2-debug"
51
+ version = "0.300.1-debug"
44
52
  summary = "IOS Shared module"
45
53
  homepage = "https://momo.vn"
46
54
  ios.deploymentTarget = "15.0"
@@ -86,24 +94,14 @@ kotlin {
86
94
  iosMain.dependencies {
87
95
  implementation(libs.ktor.client.darwin)
88
96
  }
97
+ // AI-GENERATED START: verify toolkit request ownership
98
+ commonTest.dependencies {
99
+ implementation(kotlin("test"))
100
+ }
101
+ // AI-GENERATED END: verify toolkit request ownership
89
102
  }
90
103
  }
91
104
 
92
- android {
93
- namespace = "vn.momo.kits.kits"
94
- compileSdk = 35
95
- compileOptions {
96
- sourceCompatibility = JavaVersion.VERSION_17
97
- targetCompatibility = JavaVersion.VERSION_17
98
- }
99
- defaultConfig {
100
- minSdk = 24
101
- }
102
- }
103
-
104
- dependencies {
105
- }
106
-
107
105
  repositories {
108
106
  google {
109
107
  mavenContent {
@@ -153,8 +151,9 @@ compose {
153
151
  // configure(
154
152
  // KotlinMultiplatform(
155
153
  // javadocJar = JavadocJar.Empty(),
154
+ // // AI-GENERATED START: the AGP KMP target publishes a single Android variant
156
155
  // sourcesJar = true,
157
- // androidVariantsToPublish = listOf("release")
156
+ // // AI-GENERATED END: the AGP KMP target publishes a single Android variant
158
157
  // )
159
158
  // )
160
159
  //
@@ -4,7 +4,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
4
4
 
5
5
  plugins {
6
6
  alias(libs.plugins.jetbrains.kotlin.multiplatform)
7
- alias(libs.plugins.android.library)
7
+ // AI-GENERATED START: use the AGP 9 Kotlin Multiplatform Android library target
8
+ alias(libs.plugins.android.kotlin.multiplatform.library)
9
+ // AI-GENERATED END: use the AGP 9 Kotlin Multiplatform Android library target
8
10
  alias(libs.plugins.compose)
9
11
  alias(libs.plugins.kotlin.compose.compiler)
10
12
  alias(libs.plugins.vanniktech.mavenPublish)
@@ -22,12 +24,18 @@ val gitlabUser: String = findProperty("gitlab.user") as String? ?: ""
22
24
  val gitlabKey: String = findProperty("gitlab.password") as String? ?: ""
23
25
 
24
26
  kotlin {
25
- androidTarget {
27
+ // AI-GENERATED START: configure the AGP 9 Android target and host tests
28
+ android {
29
+ namespace = "$gitlabGroup.$gitlabArtifactId"
30
+ compileSdk = libs.versions.android.compileSdk.get().toInt()
31
+ minSdk = libs.versions.android.minSdk.get().toInt()
32
+ androidResources.enable = true
33
+ withHostTest {}
26
34
  compilerOptions {
27
35
  jvmTarget.set(JvmTarget.JVM_17)
28
36
  }
29
- publishLibraryVariants("release")
30
37
  }
38
+ // AI-GENERATED END: configure the AGP 9 Android target and host tests
31
39
 
32
40
  val fwName = gitlabArtifactId
33
41
  val iosTargets = listOf(iosArm64(), iosSimulatorArm64())
@@ -86,24 +94,14 @@ kotlin {
86
94
  iosMain.dependencies {
87
95
  implementation(libs.ktor.client.darwin)
88
96
  }
97
+ // AI-GENERATED START: verify toolkit request ownership
98
+ commonTest.dependencies {
99
+ implementation(kotlin("test"))
100
+ }
101
+ // AI-GENERATED END: verify toolkit request ownership
89
102
  }
90
103
  }
91
104
 
92
- android {
93
- namespace = "$gitlabGroup.$gitlabArtifactId"
94
- compileSdk = libs.versions.android.compileSdk.get().toInt()
95
- compileOptions {
96
- sourceCompatibility = JavaVersion.VERSION_17
97
- targetCompatibility = JavaVersion.VERSION_17
98
- }
99
- defaultConfig {
100
- minSdk = libs.versions.android.minSdk.get().toInt()
101
- }
102
- }
103
-
104
- dependencies {
105
- }
106
-
107
105
  repositories {
108
106
  google {
109
107
  mavenContent {
@@ -153,8 +151,9 @@ mavenPublishing {
153
151
  configure(
154
152
  KotlinMultiplatform(
155
153
  javadocJar = JavadocJar.Empty(),
154
+ // AI-GENERATED START: the AGP KMP target publishes a single Android variant
156
155
  sourcesJar = true,
157
- androidVariantsToPublish = listOf("release")
156
+ // AI-GENERATED END: the AGP KMP target publishes a single Android variant
158
157
  )
159
158
  )
160
159
 
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |spec|
2
2
  spec.name = 'compose'
3
- spec.version = '0.167.1-sizescale.2-debug'
3
+ spec.version = '0.167.1-beta.4-debug'
4
4
  spec.homepage = 'https://momo.vn'
5
5
  spec.source = { :http=> ''}
6
6
  spec.authors = ''
@@ -30,7 +30,6 @@ import com.airbnb.lottie.compose.rememberLottieDynamicProperties
30
30
  import com.airbnb.lottie.compose.rememberLottieDynamicProperty
31
31
  import vn.momo.kits.const.AppNavigationBar
32
32
  import vn.momo.kits.const.AppStatusBar
33
- import vn.momo.kits.const.LocalSizeScale
34
33
  import vn.momo.kits.navigation.Navigator
35
34
  import vn.momo.kits.utils.readJson
36
35
  import androidx.activity.compose.BackHandler as AndroidBackHandler
@@ -40,10 +39,8 @@ actual fun getPlatformName(): String = "Android"
40
39
  @Composable
41
40
  actual fun getScreenDimensions(): ScreenDimension {
42
41
  val config = LocalConfiguration.current
43
- // Logical dp: the container size scale enlarges every dp, so the screen spans fewer of them.
44
- val sizeScale = LocalSizeScale.current
45
42
  return ScreenDimension(
46
- (config.screenWidthDp / sizeScale).toInt(), (config.screenHeightDp / sizeScale).toInt()
43
+ config.screenWidthDp, config.screenHeightDp
47
44
  )
48
45
  }
49
46
 
@@ -44,7 +44,7 @@ import vn.momo.kits.const.getFontFamily
44
44
  import vn.momo.kits.modifier.kitsAutomationId
45
45
  import vn.momo.kits.modifier.noFeedbackClickable
46
46
  import vn.momo.kits.modifier.setAutomationId
47
- import vn.momo.kits.navigation.component.HEADER_FONT_FAMILY
47
+ import vn.momo.kits.navigation.component.headerFontFamily
48
48
  import vn.momo.kits.navigation.component.HEADER_HEIGHT
49
49
  import vn.momo.kits.utils.getAppStatusBarHeight
50
50
  import kotlin.math.max
@@ -362,7 +362,7 @@ private fun LiteScreenLayout(
362
362
  text = title,
363
363
  color = headerColor.tintIconColor,
364
364
  style = titleStyle,
365
- fontFamily = HEADER_FONT_FAMILY,
365
+ fontFamily = headerFontFamily,
366
366
  modifier = titleModifier,
367
367
  maxLines = 1,
368
368
  overflow = TextOverflow.Ellipsis
@@ -7,6 +7,7 @@ import androidx.compose.ui.Modifier
7
7
  import androidx.compose.ui.unit.Dp
8
8
  import vn.momo.kits.const.*
9
9
  import vn.momo.kits.modifier.DeprecatedModifier
10
+ import vn.momo.kits.platform.ProvideOsFontScale
10
11
  import vn.momo.kits.platform.getStatusBarHeight
11
12
 
12
13
  @Deprecated("Use IMaxApi instead", ReplaceWith("IMaxApi"))
@@ -91,10 +92,9 @@ fun ApplicationContainer(
91
92
  }
92
93
  }
93
94
 
94
- // Insets are measured in real units; convert to the dp of the size-scaled container.
95
- val appStatusBarHeight = (statusBarHeight ?: getStatusBarHeight()) / pendingSizeScale()
95
+ val appStatusBarHeight = statusBarHeight ?: getStatusBarHeight()
96
96
 
97
- ProvideKitScale {
97
+ ProvideOsFontScale {
98
98
  CompositionLocalProvider(
99
99
  AppTheme provides appTheme,
100
100
  PlatformApi provides composeApi,
@@ -2,13 +2,18 @@ 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.LaunchedEffect
5
+ // AI-GENERATED START: own the typed language request
6
+ import androidx.compose.runtime.DisposableEffect
7
+ // AI-GENERATED END: own the typed language request
6
8
  import androidx.compose.runtime.remember
7
9
  import vn.momo.kits.const.AppTheme
8
10
  import vn.momo.kits.const.Theme
9
11
  import vn.momo.kits.navigation.LocalMaxApi
10
- import vn.momo.kits.const.ProvideKitScale
12
+ import vn.momo.kits.platform.ProvideOsFontScale
13
+ // AI-GENERATED START: consume typed NativeMaxApi results
11
14
  import vn.momo.maxapi.IMaxApi
15
+ import vn.momo.maxapi.runtime.ApiResult
16
+ // AI-GENERATED END: consume typed NativeMaxApi results
12
17
 
13
18
 
14
19
  @Composable
@@ -27,17 +32,27 @@ fun WidgetContainer(
27
32
  val resolvedLocalize = localize ?: LocalLocalize.current
28
33
  val resolvedMaxApi = maxApi ?: LocalMaxApi.current
29
34
 
30
- LaunchedEffect(resolvedMaxApi, localize) {
31
- val api = resolvedMaxApi ?: return@LaunchedEffect
32
- val target = localize ?: return@LaunchedEffect
33
- runCatching {
34
- api.getLanguage(callback = { data ->
35
- if (parseLanguage(data) == Localize.EN) target.changeLanguage(Localize.EN)
36
- })
35
+ // AI-GENERATED START: cancel language delivery when its composition owner changes
36
+ DisposableEffect(resolvedMaxApi, localize) {
37
+ val api = resolvedMaxApi
38
+ val target = localize
39
+ var active = true
40
+ val handle = runCatching {
41
+ api?.getLanguage { result ->
42
+ if (active && target != null && result is ApiResult.Success &&
43
+ result.value.trim().lowercase().startsWith(Localize.EN)) {
44
+ target.changeLanguage(Localize.EN)
45
+ }
46
+ }
47
+ }.getOrNull()
48
+ onDispose {
49
+ active = false
50
+ handle?.cancel()
37
51
  }
38
52
  }
53
+ // AI-GENERATED END: cancel language delivery when its composition owner changes
39
54
 
40
- ProvideKitScale {
55
+ ProvideOsFontScale {
41
56
  CompositionLocalProvider(
42
57
  AppTheme provides resolvedTheme,
43
58
  LocalContext provides mergedContext,
@@ -48,9 +63,3 @@ fun WidgetContainer(
48
63
  }
49
64
  }
50
65
  }
51
-
52
- private fun parseLanguage(raw: Map<String, Any?>?): String? {
53
- val code = (raw?.get("response") as? String)?.trim()?.lowercase()
54
- if (code.isNullOrEmpty()) return null
55
- return if (code.startsWith(Localize.EN)) Localize.EN else Localize.VI
56
- }
@@ -19,6 +19,7 @@ import coil3.request.ImageRequest
19
19
  import vn.momo.kits.application.IsShowBaseLineDebug
20
20
  import vn.momo.kits.const.AppTheme
21
21
  import vn.momo.kits.const.Colors
22
+ import vn.momo.kits.gifimage.GifImage
22
23
  import vn.momo.kits.modifier.conditional
23
24
  import vn.momo.kits.utils.Icons
24
25
  import vn.momo.kits.utils.noThemeIcons
@@ -59,18 +60,27 @@ fun Icon(
59
60
  }
60
61
  .semantics { contentDescription = contentDesc }
61
62
  ) {
62
- AsyncImage(
63
- modifier = Modifier.matchParentSize(),
64
- model = ImageRequest.Builder(context)
65
- .data(iconUrl)
66
- .size(
67
- coil3.size.Dimension.Undefined,
68
- coil3.size.Dimension.Undefined
69
- )
70
- .build(),
71
- contentDescription = null,
72
- contentScale = ContentScale.Fit,
73
- colorFilter = colorFilter,
74
- )
63
+ if (isGifUrl(iconUrl)) {
64
+ GifImage(
65
+ modifier = Modifier.matchParentSize(),
66
+ url = iconUrl,
67
+ contentDescription = null,
68
+ contentScale = ContentScale.Fit,
69
+ )
70
+ } else {
71
+ AsyncImage(
72
+ modifier = Modifier.matchParentSize(),
73
+ model = ImageRequest.Builder(context)
74
+ .data(iconUrl)
75
+ .size(
76
+ coil3.size.Dimension.Undefined,
77
+ coil3.size.Dimension.Undefined
78
+ )
79
+ .build(),
80
+ contentDescription = null,
81
+ contentScale = ContentScale.Fit,
82
+ colorFilter = colorFilter,
83
+ )
84
+ }
75
85
  }
76
86
  }
@@ -77,7 +77,7 @@ private fun getHostFromUrl(url: String): String? {
77
77
  }
78
78
  }
79
79
 
80
- private fun isGifUrl(url: String): Boolean {
80
+ internal fun isGifUrl(url: String): Boolean {
81
81
  val path = url.substringBefore('#').substringBefore('?')
82
82
  return path.endsWith(".gif", ignoreCase = true)
83
83
  }
@@ -26,9 +26,9 @@ import kotlin.math.roundToInt
26
26
  private val THUMB_SIZE = 20.dp
27
27
  private val THUMB_BORDER = 3.dp
28
28
  private val TRACK_HEIGHT = 4.dp
29
- private val LABEL_PAD_H get() = Spacing.XS
30
- private val LABEL_PAD_V get() = Spacing.XXS
31
- private val LABEL_GAP get() = Spacing.XS // space between label and thumb top
29
+ private val LABEL_PAD_H = Spacing.XS
30
+ private val LABEL_PAD_V = Spacing.XXS
31
+ private val LABEL_GAP = Spacing.XS // space between label and thumb top
32
32
 
33
33
 
34
34
  private fun clamp(value: Float, min: Float, max: Float): Float =
@@ -67,7 +67,7 @@ class TooltipState {
67
67
  fun rememberTooltipState(): TooltipState {
68
68
  return remember { TooltipState() }
69
69
  }
70
- private val TOOLTIP_OFFSET get() = Spacing.S
70
+ private val TOOLTIP_OFFSET = Spacing.S
71
71
  private val ARROW_SIZE = 6.dp
72
72
  private class TooltipPositionProvider(
73
73
  private val placement: TooltipPlacement,
@@ -1,20 +1,16 @@
1
1
  package vn.momo.kits.const
2
2
 
3
- import androidx.compose.ui.unit.Dp
4
3
  import androidx.compose.ui.unit.dp
5
4
 
6
- // Divided by KitSizeScale.rate: containers scale LocalDensity for sizes, spacing must stay unscaled.
7
- private fun spacing(value: Int): Dp = (value / KitSizeScale.rate).dp
8
-
9
5
  object Spacing {
10
- val XXS: Dp get() = spacing(2)
11
- val XS: Dp get() = spacing(4)
12
- val S: Dp get() = spacing(8)
13
- val M: Dp get() = spacing(12)
14
- val L: Dp get() = spacing(16)
15
- val XL: Dp get() = spacing(24)
16
- val XXL: Dp get() = spacing(32)
17
- val Size3XL: Dp get() = spacing(48)
18
- val Size4XL: Dp get() = spacing(56)
19
- val Size5XL: Dp get() = spacing(64)
20
- }
6
+ val XXS = 2.dp
7
+ val XS = 4.dp
8
+ val S = 8.dp
9
+ val M = 12.dp
10
+ val L = 16.dp
11
+ val XL = 24.dp
12
+ val XXL = 32.dp
13
+ val Size3XL = 48.dp
14
+ val Size4XL = 56.dp
15
+ val Size5XL = 64.dp
16
+ }
@@ -67,7 +67,7 @@ data class Theme(
67
67
  return !assets.headerBackground.isNullOrEmpty()
68
68
  }
69
69
  }
70
- val defaultTheme = Theme(
70
+ private val lightBaseTheme = Theme(
71
71
  dark = false,
72
72
  colors = ColorScheme(
73
73
  primary = Colors.pink_03,
@@ -117,14 +117,14 @@ val defaultTheme = Theme(
117
117
  container = Colors.blue_08
118
118
  )
119
119
  ),
120
- font = "MomoTrustSans",
120
+ font = "SFProText",
121
121
  assets = ThemeAssets(
122
122
  headerBackground = null
123
123
  )
124
124
  )
125
125
 
126
126
 
127
- val darkTheme = Theme(
127
+ private val darkBaseTheme = Theme(
128
128
  dark = true,
129
129
  colors = ColorScheme(
130
130
  primary = Colors.pink_04,
@@ -174,13 +174,19 @@ val darkTheme = Theme(
174
174
  container = Colors.blue_08
175
175
  )
176
176
  ),
177
- font = "MomoTrustSans",
177
+ font = "SFProText",
178
178
  assets = ThemeAssets(
179
179
  headerBackground = null
180
180
  )
181
181
  )
182
182
 
183
183
 
184
+ val defaultTheme: Theme
185
+ get() = lightBaseTheme.copy(font = defaultFontFamily)
186
+
187
+ val darkTheme: Theme
188
+ get() = darkBaseTheme.copy(font = defaultFontFamily)
189
+
184
190
  val AppTheme = staticCompositionLocalOf { defaultTheme }
185
191
 
186
192
  val AppStatusBar = staticCompositionLocalOf { 0.dp }
@@ -26,13 +26,11 @@ fun scaleSize(size: Float): Float {
26
26
  return size * (context?.userScaleRate ?: KitFontScale.userScaleRate)
27
27
  }
28
28
  val scaleSizeMaxRate: Float = MAX_FONT_SCALE
29
- // Real screen width / OS font scale: undo the container size scale.
30
- val sizeScale = LocalSizeScale.current
31
- val deviceWidth = getScreenDimensions().width * sizeScale
29
+ val deviceWidth = getScreenDimensions().width
32
30
  val deviceScale = deviceWidth / DEFAULT_SCREEN_SIZE
33
31
 
34
32
  val density = LocalDensity.current
35
- val fontScale = density.fontScale * sizeScale
33
+ val fontScale = density.fontScale
36
34
 
37
35
  var fontSizeScaleDevice = size
38
36
  var fontSizeScaleOS = size
@@ -59,8 +57,7 @@ fun scaleSize(size: TextUnit): TextUnit {
59
57
  val density = LocalDensity.current
60
58
 
61
59
  val scaled = scaleSize(size.value)
62
- // Real OS font scale (density.fontScale is divided by the size scale inside containers).
63
- val spValue = scaled / (density.fontScale * LocalSizeScale.current)
60
+ val spValue = scaled / density.fontScale
64
61
 
65
62
  return TextUnit(value = spValue, type = TextUnitType.Sp)
66
63
  }
@@ -123,31 +120,17 @@ private val hostFontFiles: Map<String, String> = mapOf(
123
120
  )
124
121
  }
125
122
 
126
- /**
127
- * Font mặc định mới, chỉ áp cho user nằm trong whitelist design system ([KitDesignSystem]).
128
- * Ngoài whitelist thì quay về [LEGACY_DEFAULT_FONT_FAMILY] như trước.
129
- */
130
123
  private const val NEW_DEFAULT_FONT_FAMILY = "MomoTrustSans"
131
124
  private const val LEGACY_DEFAULT_FONT_FAMILY = "SFProText"
132
125
 
133
- /**
134
- * Font thuoc bo design system moi. MoMoTrustDisplay chi duoc header dung tu DS-791
135
- * (commit header title 5bfde6251); truoc do header lay font theo theme, nen ngoai
136
- * whitelist ca hai deu ha ve [LEGACY_DEFAULT_FONT_FAMILY].
137
- */
138
- private val newFontFamilies = setOf(NEW_DEFAULT_FONT_FAMILY, "MoMoTrustDisplay")
126
+ internal val defaultFontFamily: String
127
+ get() = if (KitDesignSystem.isWhiteList) NEW_DEFAULT_FONT_FAMILY else LEGACY_DEFAULT_FONT_FAMILY
139
128
 
140
129
  @Composable
141
130
  fun getFontFamily(fontFamily: String, fontWeight: FontWeight? = FontWeight.Normal): FontFamily {
142
131
  val isWhiteList = KitDesignSystem.isWhiteList
143
- val resolvedFamily =
144
- if (fontFamily in newFontFamilies && !isWhiteList) {
145
- LEGACY_DEFAULT_FONT_FAMILY
146
- } else {
147
- fontFamily
148
- }
149
132
  val defaultFile = if (isWhiteList) NEW_DEFAULT_FONT_FILE else LEGACY_DEFAULT_FONT_FILE
150
- val key = "$resolvedFamily-${fontWeight?.weight}"
133
+ val key = "$fontFamily-${fontWeight?.weight}"
151
134
  val fileName = hostFontFiles[key] ?: defaultFile
152
135
  return hostFontFamily(fileName)
153
136
  ?: hostFontFamily(defaultFile)
@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
4
4
  import androidx.compose.foundation.layout.Arrangement
5
5
  import androidx.compose.foundation.layout.Box
6
6
  import androidx.compose.foundation.layout.BoxWithConstraints
7
+ import androidx.compose.foundation.layout.ExperimentalLayoutApi
7
8
  import androidx.compose.foundation.layout.FlowRow
8
9
  import androidx.compose.foundation.layout.PaddingValues
9
10
  import androidx.compose.foundation.layout.fillMaxWidth
@@ -14,6 +15,7 @@ import androidx.compose.runtime.CompositionLocalProvider
14
15
  import androidx.compose.ui.Modifier
15
16
  import androidx.compose.ui.draw.clip
16
17
  import androidx.compose.ui.draw.shadow
18
+ import androidx.compose.ui.graphics.Color
17
19
  import androidx.compose.ui.unit.dp
18
20
  import vn.momo.kits.components.Image
19
21
  import vn.momo.kits.const.AppTheme
@@ -21,30 +23,30 @@ import vn.momo.kits.const.Radius
21
23
  import vn.momo.kits.const.Spacing
22
24
 
23
25
  @Composable
24
- internal fun Card(
26
+ @OptIn(ExperimentalLayoutApi::class)
27
+ fun Card(
25
28
  useShadow: Boolean = false,
26
29
  backgroundImage: String? = null,
30
+ backgroundColor: Color? = null,
27
31
  contentPadding: PaddingValues = PaddingValues(Spacing.M),
28
32
  modifier: Modifier = Modifier,
29
33
  content: @Composable () -> Unit,
30
34
  ) {
31
- val columns = 12
32
- val gutter = 8.dp
35
+ val columns = gridColumns()
36
+ val gap = Spacing.S
33
37
  val shape = RoundedCornerShape(Radius.M)
34
- var surface = modifier.fillMaxWidth().padding(horizontal = 12.dp)
35
- if (useShadow) surface = surface.shadow(4.dp, shape)
36
- surface = surface.clip(shape)
37
- if (backgroundImage == null) surface = surface.background(AppTheme.current.colors.background.surface)
38
+ var surface = modifier.fillMaxWidth().padding(horizontal = Spacing.M)
39
+ if (useShadow) surface = surface.shadow(6.dp, shape)
40
+ surface = surface.clip(shape).background(backgroundColor ?: AppTheme.current.colors.background.surface)
38
41
  Box(modifier = surface) {
39
42
  if (backgroundImage != null) {
40
43
  Image(source = backgroundImage, modifier = Modifier.matchParentSize())
41
44
  }
42
45
  BoxWithConstraints(modifier = Modifier.fillMaxWidth().padding(contentPadding)) {
43
- val sizePerSpan = (maxWidth - gutter * (columns - 1)) / columns - 1.dp / columns
44
- CompositionLocalProvider(LocalGridContext provides GridContext(columns, gutter, sizePerSpan)) {
46
+ CompositionLocalProvider(LocalGridContext provides gridContext(constraints.maxWidth, columns, gap)) {
45
47
  FlowRow(
46
- horizontalArrangement = Arrangement.spacedBy(gutter),
47
- verticalArrangement = Arrangement.spacedBy(gutter),
48
+ horizontalArrangement = Arrangement.spacedBy(gap),
49
+ verticalArrangement = Arrangement.spacedBy(gap),
48
50
  ) { content() }
49
51
  }
50
52
  }