@momo-kits/native-kits 0.168.1-beta.10-debug → 0.168.1-beta.11-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.
- package/CLAUDE.md +1 -0
- package/compose/build.gradle.kts +1 -1
- package/compose/compose.podspec +1 -1
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +9 -8
- package/gradle.properties +1 -1
- package/ios/Application/DesignSystemKits.swift +64 -1
- package/ios/Typography/FontScale.swift +15 -4
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -82,6 +82,7 @@ Publishing is automated (`publish.sh` + GitLab CI on `[ci build]` / `main`). Do
|
|
|
82
82
|
- Quick return header: glass headers and Animated headers (with a `ScrollState`) on the Duo side bar layout slide out with the content and back in on a reverse scroll; a half-shown header settles in or out once the scroll stops. The side bar is drawn apart from the header, so it stays put.
|
|
83
83
|
- Overlays on Duo: popups cap at 440pt wide and the room between `duoTopInset` and the bottom inset (body scrolls, actions stay); modal centers inside that room; bottom sheet uses the window height, centers at width ≥ 600 and pads by side insets in landscape.
|
|
84
84
|
- Font scale deliberately uses the short side (`min(w, h)`), unlike RN which reads the window width — landscape must not inflate every font.
|
|
85
|
+
- Font scale measures the **window**, never `UIScreen.main`: on iPhone Duo the main screen stays on one display while the window follows the one in use. Compose iOS `getScreenDimensions()` reads `LocalWindowInfo.containerSize` (recomposes on fold/unfold); SwiftUI `DesignSystemKits` KVO-observes each `UIWindowScene.effectiveGeometry` and republishes `windowShortSide`.
|
|
85
86
|
- `StackScreen` pads content/footer/search/FAB by the side insets and provides an inset-consumed layout to its content; a bottom-tab root stays full width (tab bar + each tab's StackScreen handle insets). Grid columns use `contentWidth`.
|
|
86
87
|
|
|
87
88
|
## i18n / Localize (port of RN `Localize`)
|
package/compose/build.gradle.kts
CHANGED
package/compose/compose.podspec
CHANGED
|
@@ -21,6 +21,8 @@ import androidx.compose.ui.backhandler.BackHandler as ComposeBackHandler
|
|
|
21
21
|
import androidx.compose.ui.graphics.Color
|
|
22
22
|
import androidx.compose.ui.graphics.NativePaint
|
|
23
23
|
import androidx.compose.ui.graphics.toArgb
|
|
24
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
25
|
+
import androidx.compose.ui.platform.LocalWindowInfo
|
|
24
26
|
import androidx.compose.ui.unit.Dp
|
|
25
27
|
import androidx.compose.ui.unit.dp
|
|
26
28
|
import androidx.compose.ui.viewinterop.UIKitInteropProperties
|
|
@@ -29,8 +31,6 @@ import cocoapods.lottie_ios.CompatibleAnimation
|
|
|
29
31
|
import cocoapods.lottie_ios.CompatibleAnimationKeypath
|
|
30
32
|
import cocoapods.lottie_ios.CompatibleAnimationView
|
|
31
33
|
import kotlinx.cinterop.ExperimentalForeignApi
|
|
32
|
-
import kotlinx.cinterop.get
|
|
33
|
-
import kotlinx.cinterop.memScoped
|
|
34
34
|
import androidx.navigationevent.NavigationEventDispatcher
|
|
35
35
|
import androidx.navigationevent.NavigationEventDispatcherOwner
|
|
36
36
|
import org.jetbrains.skia.FilterBlurMode
|
|
@@ -38,7 +38,6 @@ import org.jetbrains.skia.MaskFilter
|
|
|
38
38
|
import platform.Foundation.NSBundle
|
|
39
39
|
import platform.UIKit.UIColor
|
|
40
40
|
import platform.UIKit.UIDevice
|
|
41
|
-
import platform.UIKit.UIScreen
|
|
42
41
|
import platform.UIKit.UIUserInterfaceIdiomPhone
|
|
43
42
|
import vn.momo.kits.navigation.AttachHostBackGesture
|
|
44
43
|
import vn.momo.kits.navigation.Navigator
|
|
@@ -48,15 +47,17 @@ actual fun getPlatformName(): String = "iOS"
|
|
|
48
47
|
actual fun isPhoneIdiom(): Boolean =
|
|
49
48
|
UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
// AI-GENERATED START: window size, not UIScreen.main (stays on one display on iPhone Duo); recomposes on fold/unfold
|
|
52
51
|
@Composable
|
|
53
|
-
actual fun getScreenDimensions(): ScreenDimension
|
|
54
|
-
val
|
|
52
|
+
actual fun getScreenDimensions(): ScreenDimension {
|
|
53
|
+
val density = LocalDensity.current
|
|
54
|
+
val size = LocalWindowInfo.current.containerSize
|
|
55
55
|
return ScreenDimension(
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
(size.width / density.density).toInt(),
|
|
57
|
+
(size.height / density.density).toInt()
|
|
58
58
|
)
|
|
59
59
|
}
|
|
60
|
+
// AI-GENERATED END: window size, not UIScreen.main (stays on one display on iPhone Duo); recomposes on fold/unfold
|
|
60
61
|
|
|
61
62
|
actual fun NativePaint.setMaskFilter(blurRadius: Float) {
|
|
62
63
|
this.maskFilter = MaskFilter.makeBlur(FilterBlurMode.NORMAL, blurRadius / 2, true)
|
package/gradle.properties
CHANGED
|
@@ -36,6 +36,10 @@ public final class DesignSystemKits: ObservableObject {
|
|
|
36
36
|
|
|
37
37
|
@Published private var configs = DesignSystemKitsConfigs()
|
|
38
38
|
@Published public private(set) var osFontScale: CGFloat = DesignSystemKits.currentOsFontScale()
|
|
39
|
+
// AI-GENERATED START: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
40
|
+
@Published public private(set) var windowShortSide: CGFloat = DesignSystemKits.currentWindowShortSide()
|
|
41
|
+
private var geometryObservations: [ObjectIdentifier: NSKeyValueObservation] = [:]
|
|
42
|
+
// AI-GENERATED END: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
39
43
|
|
|
40
44
|
private init() {
|
|
41
45
|
NotificationCenter.default.addObserver(
|
|
@@ -44,6 +48,19 @@ public final class DesignSystemKits: ObservableObject {
|
|
|
44
48
|
name: UIContentSizeCategory.didChangeNotification,
|
|
45
49
|
object: nil
|
|
46
50
|
)
|
|
51
|
+
// AI-GENERATED START: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
52
|
+
NotificationCenter.default.addObserver(
|
|
53
|
+
self,
|
|
54
|
+
selector: #selector(sceneDidActivate(_:)),
|
|
55
|
+
name: UIScene.didActivateNotification,
|
|
56
|
+
object: nil
|
|
57
|
+
)
|
|
58
|
+
onMain {
|
|
59
|
+
UIApplication.shared.connectedScenes
|
|
60
|
+
.compactMap { $0 as? UIWindowScene }
|
|
61
|
+
.forEach(self.observeGeometry(of:))
|
|
62
|
+
}
|
|
63
|
+
// AI-GENERATED END: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
47
64
|
}
|
|
48
65
|
|
|
49
66
|
public func setConfigs(_ configs: DesignSystemKitsConfigs) {
|
|
@@ -62,7 +79,10 @@ public final class DesignSystemKits: ObservableObject {
|
|
|
62
79
|
FontScale(
|
|
63
80
|
userScaleRate: configs.userScaleRate,
|
|
64
81
|
useOSScaleRate: configs.useOSScaleRate,
|
|
65
|
-
osFontScale: osFontScale
|
|
82
|
+
osFontScale: osFontScale,
|
|
83
|
+
// AI-GENERATED START: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
84
|
+
windowShortSide: windowShortSide
|
|
85
|
+
// AI-GENERATED END: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
66
86
|
)
|
|
67
87
|
}
|
|
68
88
|
|
|
@@ -72,6 +92,49 @@ public final class DesignSystemKits: ObservableObject {
|
|
|
72
92
|
osFontScale = next
|
|
73
93
|
}
|
|
74
94
|
|
|
95
|
+
// AI-GENERATED START: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
96
|
+
@objc private func sceneDidActivate(_ notification: Notification) {
|
|
97
|
+
guard let scene = notification.object as? UIWindowScene else { return }
|
|
98
|
+
observeGeometry(of: scene)
|
|
99
|
+
refreshWindowShortSide()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private func observeGeometry(of scene: UIWindowScene) {
|
|
103
|
+
guard #available(iOS 16.0, *) else { return }
|
|
104
|
+
let key = ObjectIdentifier(scene)
|
|
105
|
+
guard geometryObservations[key] == nil else { return }
|
|
106
|
+
geometryObservations[key] = scene.observe(\.effectiveGeometry, options: [.new]) { [weak self] _, _ in
|
|
107
|
+
// The window is resized after the scene geometry, so read it on the next turn.
|
|
108
|
+
DispatchQueue.main.async { self?.refreshWindowShortSide() }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private func refreshWindowShortSide() {
|
|
113
|
+
let next = Self.currentWindowShortSide()
|
|
114
|
+
guard next > 0, next != windowShortSide else { return }
|
|
115
|
+
windowShortSide = next
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// UIScreen.main stays on one display on iPhone Duo, so measure the foreground window instead.
|
|
119
|
+
static func currentWindowShortSide() -> CGFloat {
|
|
120
|
+
guard Thread.isMainThread else {
|
|
121
|
+
return min(UIScreen.main.bounds.width, UIScreen.main.bounds.height)
|
|
122
|
+
}
|
|
123
|
+
let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
|
|
124
|
+
let scene = scenes.first { $0.activationState == .foregroundActive } ?? scenes.first
|
|
125
|
+
let window = scene?.windows.first { $0.isKeyWindow } ?? scene?.windows.first
|
|
126
|
+
let size: CGSize
|
|
127
|
+
if let window, window.bounds.width > 0 {
|
|
128
|
+
size = window.bounds.size
|
|
129
|
+
} else if let scene {
|
|
130
|
+
size = scene.coordinateSpace.bounds.size
|
|
131
|
+
} else {
|
|
132
|
+
size = UIScreen.main.bounds.size
|
|
133
|
+
}
|
|
134
|
+
return min(size.width, size.height)
|
|
135
|
+
}
|
|
136
|
+
// AI-GENERATED END: republish the font scale when the window scene is resized (iPhone Duo fold/unfold)
|
|
137
|
+
|
|
75
138
|
/// Same factor Compose's iOS `ProvideOsFontScale` derives from
|
|
76
139
|
/// `UIFont.preferredFont(forTextStyle: .body).pointSize / 17`, so both iOS surfaces scale alike.
|
|
77
140
|
static func currentOsFontScale() -> CGFloat {
|
|
@@ -19,11 +19,19 @@ public struct FontScale: Equatable {
|
|
|
19
19
|
public let useOSScaleRate: Bool
|
|
20
20
|
/// OS Dynamic Type factor at the time the snapshot was taken.
|
|
21
21
|
public let osFontScale: CGFloat
|
|
22
|
+
// AI-GENERATED START: device scale from the app window, not UIScreen.main (fixed to one display on iPhone Duo)
|
|
23
|
+
public let windowShortSide: CGFloat
|
|
22
24
|
|
|
23
|
-
public init(
|
|
25
|
+
public init(
|
|
26
|
+
userScaleRate: CGFloat = 1,
|
|
27
|
+
useOSScaleRate: Bool = true,
|
|
28
|
+
osFontScale: CGFloat = 1,
|
|
29
|
+
windowShortSide: CGFloat? = nil
|
|
30
|
+
) {
|
|
24
31
|
self.userScaleRate = userScaleRate
|
|
25
32
|
self.useOSScaleRate = useOSScaleRate
|
|
26
33
|
self.osFontScale = osFontScale
|
|
34
|
+
self.windowShortSide = windowShortSide ?? DesignSystemKits.currentWindowShortSide()
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
/// `callAsFunction` so call sites keep reading like Compose's `scaleSize(16)`.
|
|
@@ -31,8 +39,8 @@ public struct FontScale: Equatable {
|
|
|
31
39
|
guard useOSScaleRate else { return size * userScaleRate }
|
|
32
40
|
|
|
33
41
|
// Short side, not width: landscape must not inflate every font (and so every popup's height).
|
|
34
|
-
let
|
|
35
|
-
|
|
42
|
+
let deviceScale = windowShortSide / Self.defaultScreenSize
|
|
43
|
+
// AI-GENERATED END: device scale from the app window, not UIScreen.main (fixed to one display on iPhone Duo)
|
|
36
44
|
|
|
37
45
|
var deviceScaled = size
|
|
38
46
|
var osScaled = size
|
|
@@ -58,7 +66,10 @@ public struct FontScale: Equatable {
|
|
|
58
66
|
return FontScale(
|
|
59
67
|
userScaleRate: userScaleRate ?? self.userScaleRate,
|
|
60
68
|
useOSScaleRate: useOSScaleRate ?? self.useOSScaleRate,
|
|
61
|
-
osFontScale: osFontScale
|
|
69
|
+
osFontScale: osFontScale,
|
|
70
|
+
// AI-GENERATED START: keep the window size through context overrides
|
|
71
|
+
windowShortSide: windowShortSide
|
|
72
|
+
// AI-GENERATED END: keep the window size through context overrides
|
|
62
73
|
)
|
|
63
74
|
}
|
|
64
75
|
}
|
package/ios/native-kits.podspec
CHANGED