@momo-kits/native-kits 0.163.1-beta.14-debug → 0.163.1-beta.15-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/settings.local.json +84 -0
- package/compose/build.gradle.kts +1 -1
- package/compose/compose.podspec +1 -1
- package/gradle.properties +1 -1
- package/ios/Application/Components.swift +14 -14
- package/ios/Application/HeaderRight.swift +45 -79
- package/ios/Application/Localize.swift +32 -0
- package/ios/Application/Navigation/NavigationContainer.swift +3 -0
- package/ios/Application/Navigation/component/Header.swift +1 -1
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +11 -0
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +196 -0
- package/ios/Popup/PopupDisplay.swift +1 -1
- package/ios/Popup/PopupNotify.swift +1 -1
- package/ios/Popup/PopupPromotion.swift +5 -11
- package/ios/Template/TrustBanner/TrustBanner.swift +2 -2
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +2 -2
- package/local.properties +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(curl -s -o /dev/null -w \"%{http_code}\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
|
|
5
|
+
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/maven-metadata.xml\")",
|
|
6
|
+
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/maven-metadata.xml\")",
|
|
7
|
+
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/0.144.6/skiko-0.144.6.module\")",
|
|
8
|
+
"Bash(curl -s -o /dev/null -w '%{http_code}' https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.__TRACKED_VAR__)",
|
|
9
|
+
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/\")",
|
|
10
|
+
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/\")",
|
|
11
|
+
"Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.148.1/skiko-android-0.148.1.pom\")",
|
|
12
|
+
"Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.5/skiko-android-0.144.5.pom\")",
|
|
13
|
+
"Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://dl.google.com/dl/android/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
|
|
14
|
+
"Bash(curl -s \"https://search.maven.org/solrsearch/select?q=a:skiko-android&rows=5&wt=json\")",
|
|
15
|
+
"Bash(curl -sL -o /dev/null -w \"%{http_code} %{url_effective}\\\\n\" \"https://repo1.maven.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
|
|
16
|
+
"Bash(curl *)",
|
|
17
|
+
"Bash(./gradlew :sample:androidApp:dependencies --configuration debugRuntimeClasspath)",
|
|
18
|
+
"Bash(./gradlew :sample:shared:compileDebugKotlinAndroid)",
|
|
19
|
+
"Bash(./gradlew :sample:androidApp:compileDebugKotlin)",
|
|
20
|
+
"Bash(ls -d *.xcodeproj)",
|
|
21
|
+
"Bash(grep -c ChipDemo.swift __TRACKED_VAR__/project.pbxproj)",
|
|
22
|
+
"Bash(grep -c PBXFileSystemSynchronizedRootGroup __TRACKED_VAR__/project.pbxproj)",
|
|
23
|
+
"Bash(grep -c InformationDemo.swift __TRACKED_VAR__/project.pbxproj)",
|
|
24
|
+
"Bash(plutil -lint project.pbxproj)",
|
|
25
|
+
"Bash(xcode-select -p)",
|
|
26
|
+
"Bash(/usr/bin/xcrun xcodebuild *)",
|
|
27
|
+
"Bash(echo \"exit: $?\")",
|
|
28
|
+
"Bash(/Applications/Xcode26.app/Contents/Developer/usr/bin/xcodebuild -version)",
|
|
29
|
+
"Bash(./gradlew :compose:tasks --all)",
|
|
30
|
+
"Bash(grep -n \"vanniktech\\\\|mavenPublishing\\\\|^publishing\\\\|version = gitlabVersion\\\\|namespace =\\\\|compileSdk =\\\\|minSdk =\\\\|GitLabPackages\\\\|name = \\\\\"\\\\${gitlabRepo}Packages\\\\\"\")",
|
|
31
|
+
"Bash(git checkout *)",
|
|
32
|
+
"Bash(./gradlew :compose:publishAllPublicationsToGitLabPackagesRepository --dry-run)",
|
|
33
|
+
"Bash(git fetch *)",
|
|
34
|
+
"Bash(git pull *)",
|
|
35
|
+
"Bash(git push *)",
|
|
36
|
+
"Bash(git branch *)",
|
|
37
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -list)",
|
|
38
|
+
"Bash(xcrun simctl *)",
|
|
39
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
40
|
+
"Bash(pod install *)",
|
|
41
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'id=1E6F9A79-5E64-4590-BEA2-828D75D99393' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
42
|
+
"Bash(xcrun swiftc *)",
|
|
43
|
+
"Bash(echo \"=== exit: $? \\(1=none found\\) ===\")",
|
|
44
|
+
"Bash(cat Radius.swift)",
|
|
45
|
+
"Bash(cat Spacing.swift)",
|
|
46
|
+
"Bash(echo \"exit=$?\")",
|
|
47
|
+
"Bash(ruby *)",
|
|
48
|
+
"Bash(xcodebuild -list -workspace iosApp.xcworkspace)",
|
|
49
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' build)",
|
|
50
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
|
|
51
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
|
|
52
|
+
"Bash(xcodebuild build *)",
|
|
53
|
+
"Bash(git stash *)",
|
|
54
|
+
"Bash(plutil -lint sample/iosApp/iosApp.xcodeproj/project.pbxproj)",
|
|
55
|
+
"Bash(git add *)",
|
|
56
|
+
"Bash(git commit -m 'feat: DS-646 [ci build] *)",
|
|
57
|
+
"Bash(git commit -q -m 'fix: DS-656 [ci build] *)",
|
|
58
|
+
"Bash(xargs git log --oneline -1)",
|
|
59
|
+
"Bash(git rev-list *)",
|
|
60
|
+
"Bash(git status *)",
|
|
61
|
+
"Bash(git rebase *)",
|
|
62
|
+
"Bash(git merge *)",
|
|
63
|
+
"Bash(git reset *)",
|
|
64
|
+
"Bash(awk '/^ *)",
|
|
65
|
+
"Bash(git ls-tree *)",
|
|
66
|
+
"Bash(awk *)",
|
|
67
|
+
"Read(//Users/sophia/Workspace/momo/momo-app/**)",
|
|
68
|
+
"Bash(git log *)",
|
|
69
|
+
"Bash(xcodebuild -version)",
|
|
70
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
71
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
72
|
+
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug clean build CODE_SIGNING_ALLOWED=NO)",
|
|
73
|
+
"Bash(git commit *)",
|
|
74
|
+
"Bash(npm view *)",
|
|
75
|
+
"Bash(echo \"EXIT=$? -> not resolvable\")",
|
|
76
|
+
"Bash(yarn install *)",
|
|
77
|
+
"Bash(RCT_NEW_ARCH_ENABLED=1 pod install)",
|
|
78
|
+
"Bash(xcodebuild -workspace MoMoPlatform.xcworkspace -list)",
|
|
79
|
+
"Bash(git --no-pager diff --stat ios/Application/Localize.swift ios/Application/Navigation/NavigationContainer.swift ios/Popup/PopupDisplay.swift ios/Popup/PopupNotify.swift ios/Template/TrustBanner/TrustBanner.swift)",
|
|
80
|
+
"Bash(git rm *)",
|
|
81
|
+
"Bash(echo \"EXIT=$?\")"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
package/compose/build.gradle.kts
CHANGED
package/compose/compose.podspec
CHANGED
package/gradle.properties
CHANGED
|
@@ -100,6 +100,20 @@ public struct HeaderBackground: View {
|
|
|
100
100
|
.frame(height: height)
|
|
101
101
|
.shadow(color: Colors.black20.opacity(0.2), radius: 10, x: 0, y: -2)
|
|
102
102
|
.background(backgroundColor)
|
|
103
|
+
.overlay(
|
|
104
|
+
headerTransparent ? AnyView(EmptyView()) :
|
|
105
|
+
AnyView(
|
|
106
|
+
Rectangle()
|
|
107
|
+
.fill(LinearGradient(
|
|
108
|
+
gradient: Gradient(colors: [
|
|
109
|
+
Color(red: 1, green: 0.8, blue: 0.87),
|
|
110
|
+
Color(red: 1, green: 0.8, blue: 0.87).opacity(0.5)
|
|
111
|
+
]),
|
|
112
|
+
startPoint: .top,
|
|
113
|
+
endPoint: .bottom))
|
|
114
|
+
.opacity(opacity)
|
|
115
|
+
.frame(height: height))
|
|
116
|
+
)
|
|
103
117
|
if !headerTransparent {
|
|
104
118
|
Rectangle()
|
|
105
119
|
.fill(Color.black.opacity(0.1))
|
|
@@ -132,17 +146,6 @@ public struct HeaderBackground: View {
|
|
|
132
146
|
}
|
|
133
147
|
}
|
|
134
148
|
|
|
135
|
-
// MARK: - Header right width preference
|
|
136
|
-
|
|
137
|
-
/// Reports the measured width of the header-right area so the animated search
|
|
138
|
-
/// header can compute its trailing inset (mirrors Compose's headerRightWidthPx).
|
|
139
|
-
public struct HeaderRightWidthKey: PreferenceKey {
|
|
140
|
-
public static var defaultValue: CGFloat = 0
|
|
141
|
-
public static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
142
|
-
value = max(value, nextValue())
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
149
|
// MARK: - Bottom-tab root preference
|
|
147
150
|
|
|
148
151
|
/// Set by `BottomTab` on appear so its enclosing `StackScreen` can detect that
|
|
@@ -232,9 +235,6 @@ public struct Header: View {
|
|
|
232
235
|
|
|
233
236
|
if let headerRight = headerRight {
|
|
234
237
|
AnyView(headerRight())
|
|
235
|
-
.background(GeometryReader { geo in
|
|
236
|
-
Color.clear.preference(key: HeaderRightWidthKey.self, value: geo.size.width)
|
|
237
|
-
})
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
.padding(.horizontal, 12)
|
|
@@ -101,98 +101,69 @@ struct ToolkitHeaderRight: View {
|
|
|
101
101
|
var tintColor: Color?
|
|
102
102
|
@State private var isFavorite: Bool = false
|
|
103
103
|
@State private var isLoading: Bool = false
|
|
104
|
-
@
|
|
105
|
-
|
|
106
|
-
private func apiValue(_ value: Any?) -> Any {
|
|
107
|
-
value ?? NSNull()
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
private var contextMap: [String: Any] {
|
|
111
|
-
let context = environment.applicationContext
|
|
112
|
-
return [
|
|
113
|
-
"appId": apiValue(context?.appId),
|
|
114
|
-
"code": apiValue(context?.appCode),
|
|
115
|
-
"name": apiValue(context?.appName),
|
|
116
|
-
"icon": apiValue(context?.appIcon),
|
|
117
|
-
"description": apiValue(context?.description),
|
|
118
|
-
"support": apiValue(context?.support),
|
|
119
|
-
"toolkitConfig": apiValue(context?.toolkitConfig),
|
|
120
|
-
"providerId": apiValue(context?.providerId),
|
|
121
|
-
"permissions": apiValue(context?.permissions)
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private var shouldShowShortcut: Bool {
|
|
126
|
-
headerRight?.useShortcut == true && !(headerRight?.useMore == true && environment.applicationContext == nil)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private func parseResponse(_ response: String) -> Any? {
|
|
130
|
-
guard let jsonData = response.data(using: .utf8),
|
|
131
|
-
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
|
132
|
-
return nil
|
|
133
|
-
}
|
|
134
|
-
return json["response"]
|
|
135
|
-
}
|
|
104
|
+
@EnvironmentObject private var environment: ApplicationEnvironment
|
|
136
105
|
|
|
137
106
|
// MARK: - Actions
|
|
138
|
-
private func loadFavoriteState() {
|
|
139
|
-
environment.composeApi?.request(
|
|
140
|
-
funcName: "isFavoriteApp",
|
|
141
|
-
params: ["code": apiValue(environment.applicationContext?.appCode)]
|
|
142
|
-
) { response in
|
|
143
|
-
if let isFavorite = parseResponse(response) as? Bool {
|
|
144
|
-
self.isFavorite = isFavorite
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
107
|
private func onPressShortcut() {
|
|
150
108
|
environment.composeApi?.request(
|
|
151
109
|
funcName: "onToolAction",
|
|
152
|
-
params: [
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
]
|
|
156
|
-
) { response in
|
|
157
|
-
if let result = parseResponse(response) as? [String: Any],
|
|
158
|
-
result["success"] as? Bool == true {
|
|
159
|
-
isFavorite.toggle()
|
|
160
|
-
}
|
|
110
|
+
params: ["item": ["key": "onFavorite"]]
|
|
111
|
+
) { _ in
|
|
112
|
+
isFavorite.toggle()
|
|
161
113
|
}
|
|
162
114
|
}
|
|
163
115
|
|
|
164
116
|
private func onPressHelpCenter() {
|
|
117
|
+
let context = environment.applicationContext
|
|
118
|
+
let paramMap: [String: Any] = [
|
|
119
|
+
"appId": context?.appId,
|
|
120
|
+
"code": context?.appCode,
|
|
121
|
+
"name": context?.appName,
|
|
122
|
+
"icon": context?.appIcon,
|
|
123
|
+
"description": context?.description
|
|
124
|
+
]
|
|
165
125
|
environment.composeApi?.request(
|
|
166
126
|
funcName: "showHelpCenter",
|
|
167
|
-
params:
|
|
168
|
-
"appId": apiValue(environment.applicationContext?.appId),
|
|
169
|
-
"code": apiValue(environment.applicationContext?.appCode),
|
|
170
|
-
"name": apiValue(environment.applicationContext?.appName),
|
|
171
|
-
"icon": apiValue(environment.applicationContext?.appIcon),
|
|
172
|
-
"description": apiValue(environment.applicationContext?.description)
|
|
173
|
-
]
|
|
127
|
+
params: paramMap
|
|
174
128
|
) { _ in }
|
|
175
129
|
}
|
|
176
130
|
|
|
177
131
|
private func onPressClose() {
|
|
178
132
|
environment.composeApi?.request(
|
|
179
133
|
funcName: "dismissAll",
|
|
180
|
-
params:
|
|
134
|
+
params: ""
|
|
181
135
|
) { _ in }
|
|
182
136
|
}
|
|
183
137
|
|
|
184
138
|
private func onPressMore() {
|
|
139
|
+
let context = environment.applicationContext
|
|
185
140
|
let params: [String: Any] = [
|
|
186
|
-
"useSystemTools": headerRight?.useSystemTools
|
|
141
|
+
"useSystemTools": headerRight?.useSystemTools,
|
|
187
142
|
"tools": headerRight?.tools.map { $0.toMap() } ?? [],
|
|
188
|
-
"context":
|
|
143
|
+
"context": [
|
|
144
|
+
"appId": context?.appId,
|
|
145
|
+
"code": context?.appCode,
|
|
146
|
+
"name": context?.appName,
|
|
147
|
+
"icon": context?.appIcon,
|
|
148
|
+
"description": context?.description,
|
|
149
|
+
"support": context?.support,
|
|
150
|
+
"toolkitConfig": context?.toolkitConfig,
|
|
151
|
+
"providerId": context?.providerId,
|
|
152
|
+
"permissions": context?.permissions
|
|
153
|
+
]
|
|
189
154
|
]
|
|
190
155
|
environment.composeApi?.request(
|
|
191
156
|
funcName: "showTools",
|
|
192
157
|
params: params
|
|
193
158
|
) { response in
|
|
194
|
-
|
|
195
|
-
|
|
159
|
+
do {
|
|
160
|
+
if let jsonData = response.data(using: .utf8),
|
|
161
|
+
let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
|
|
162
|
+
let toolResponse = json["response"] as? String {
|
|
163
|
+
headerRight?.toolCallback?(toolResponse)
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
print("Error parsing response:", error)
|
|
196
167
|
}
|
|
197
168
|
}
|
|
198
169
|
}
|
|
@@ -224,7 +195,7 @@ struct ToolkitHeaderRight: View {
|
|
|
224
195
|
} ?? false
|
|
225
196
|
|
|
226
197
|
HStack(alignment: .center, spacing: 0) {
|
|
227
|
-
if
|
|
198
|
+
if headerRight?.useShortcut == true {
|
|
228
199
|
NavigationButton(
|
|
229
200
|
disabled: isLoading,
|
|
230
201
|
icon: navButtonConfig.icon,
|
|
@@ -235,17 +206,15 @@ struct ToolkitHeaderRight: View {
|
|
|
235
206
|
}
|
|
236
207
|
|
|
237
208
|
HStack(alignment: .center, spacing: 0) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
.frame(width: 0.5, height: 12)
|
|
248
|
-
}
|
|
209
|
+
Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
|
|
210
|
+
.padding(4)
|
|
211
|
+
.onTapGesture {
|
|
212
|
+
onPressHelpCenter()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Rectangle()
|
|
216
|
+
.fill(tintColor ?? Colors.black20)
|
|
217
|
+
.frame(width: 0.5, height: 12)
|
|
249
218
|
|
|
250
219
|
Icon(source: "16_basic_home", size: 20, color: tintColor ?? Colors.black17)
|
|
251
220
|
.padding(4)
|
|
@@ -262,9 +231,6 @@ struct ToolkitHeaderRight: View {
|
|
|
262
231
|
)
|
|
263
232
|
.padding(.leading, Spacing.S)
|
|
264
233
|
}
|
|
265
|
-
.onAppear {
|
|
266
|
-
loadFavoriteState()
|
|
267
|
-
}
|
|
268
234
|
}
|
|
269
235
|
}
|
|
270
236
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
//
|
|
12
12
|
|
|
13
13
|
import Foundation
|
|
14
|
+
import SwiftUI
|
|
14
15
|
|
|
15
16
|
/// Translation dictionary keyed by language; both `vi` and `en` are required.
|
|
16
17
|
public struct LocalizationObject {
|
|
@@ -275,3 +276,34 @@ public final class Localize: ObservableObject {
|
|
|
275
276
|
]
|
|
276
277
|
)
|
|
277
278
|
}
|
|
279
|
+
|
|
280
|
+
/// Snapshots `currentLanguage` alongside the instance so SwiftUI sees an environment
|
|
281
|
+
/// *change* on `changeLanguage`: the `Localize` reference alone never changes, and
|
|
282
|
+
/// `@Environment` does not subscribe to `@Published` the way `@EnvironmentObject` does.
|
|
283
|
+
private struct LocalizeBox: Equatable {
|
|
284
|
+
let localize: Localize
|
|
285
|
+
let language: String
|
|
286
|
+
|
|
287
|
+
init(_ localize: Localize) {
|
|
288
|
+
self.localize = localize
|
|
289
|
+
self.language = localize.currentLanguage
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
static func == (lhs: LocalizeBox, rhs: LocalizeBox) -> Bool {
|
|
293
|
+
lhs.localize === rhs.localize && lhs.language == rhs.language
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private struct LocalizeKey: EnvironmentKey {
|
|
298
|
+
/// A default-constructed `Localize` already carries the full kit dictionary, so a
|
|
299
|
+
/// consumer that never injects one renders untranslated-but-correct text instead of
|
|
300
|
+
/// trapping the way `@EnvironmentObject` does.
|
|
301
|
+
static let defaultValue = LocalizeBox(Localize())
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
public extension EnvironmentValues {
|
|
305
|
+
var localize: Localize {
|
|
306
|
+
get { self[LocalizeKey.self].localize }
|
|
307
|
+
set { self[LocalizeKey.self] = LocalizeBox(newValue) }
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -63,12 +63,14 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
63
63
|
.environmentObject(applicationEnvironment)
|
|
64
64
|
.environment(\.applicationEnvironment, applicationEnvironment)
|
|
65
65
|
.environmentObject(localize)
|
|
66
|
+
.environment(\.localize, localize)
|
|
66
67
|
.fullScreenCover(item: $navigator.presented) { route in
|
|
67
68
|
screenView(forDialog: route)
|
|
68
69
|
.environmentObject(navigator)
|
|
69
70
|
.environmentObject(applicationEnvironment)
|
|
70
71
|
.environment(\.applicationEnvironment, applicationEnvironment)
|
|
71
72
|
.environmentObject(localize)
|
|
73
|
+
.environment(\.localize, localize)
|
|
72
74
|
}
|
|
73
75
|
.overlay {
|
|
74
76
|
if let item = navigator.overplay {
|
|
@@ -77,6 +79,7 @@ public struct NavigationContainer<Initial: View>: View {
|
|
|
77
79
|
.environmentObject(applicationEnvironment)
|
|
78
80
|
.environment(\.applicationEnvironment, applicationEnvironment)
|
|
79
81
|
.environmentObject(localize)
|
|
82
|
+
.environment(\.localize, localize)
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
.onAppear {
|
|
@@ -22,7 +22,7 @@ public struct NavigationHeader: View {
|
|
|
22
22
|
/// Optional rich title (mirrors Compose's `HeaderTitle`); `.user` renders an
|
|
23
23
|
/// avatar group, `.default`/nil renders plain `title`.
|
|
24
24
|
var headerTitle: HeaderTitle?
|
|
25
|
-
var headerRight: (() -> any View)? = {
|
|
25
|
+
var headerRight: (() -> any View)? = { NavigationHeaderRight() }
|
|
26
26
|
var goBack: (() -> Void)?
|
|
27
27
|
var opacity: CGFloat = 1
|
|
28
28
|
var animatedHeader: AnimatedHeader?
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/// Reports the measured width of the header-right area so the animated search
|
|
4
|
+
/// header can compute its trailing inset (mirrors Compose's headerRightWidthPx).
|
|
5
|
+
/// Written by the header views (`NavigationHeader`) and read by `StackScreen`.
|
|
6
|
+
public struct HeaderRightWidthKey: PreferenceKey {
|
|
7
|
+
public static var defaultValue: CGFloat = 0
|
|
8
|
+
public static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
9
|
+
value = max(value, nextValue())
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/// Keyed (`@Environment(\.applicationEnvironment)`) variant of `HeaderRight`, for hosts
|
|
5
|
+
/// wired through `NavigationContainer`/`KitContainer` (the injection style introduced by
|
|
6
|
+
/// MR 227 / DS-637). Reuses the shared data types (`HeaderRightData`, `Tool`, `ToolGroup`)
|
|
7
|
+
/// and `NavigationButton` from `HeaderRight.swift`. The legacy `HeaderRight` there stays on
|
|
8
|
+
/// `@EnvironmentObject` for hosts that render `Screen` directly with
|
|
9
|
+
/// `.environmentObject(ApplicationEnvironment(...))`. Opt in per screen with
|
|
10
|
+
/// `headerRight: { NavigationHeaderRight() }`.
|
|
11
|
+
public struct NavigationHeaderRight: View {
|
|
12
|
+
public init(headerRight: HeaderRightData? = nil, tintColor: Color? = nil) {
|
|
13
|
+
self.headerRight = headerRight
|
|
14
|
+
self.tintColor = tintColor
|
|
15
|
+
}
|
|
16
|
+
var headerRight: HeaderRightData?
|
|
17
|
+
var tintColor: Color? = nil
|
|
18
|
+
|
|
19
|
+
public var body: some View {
|
|
20
|
+
HStack(alignment: .center) {
|
|
21
|
+
ToolkitNavigationHeaderRight(headerRight: headerRight, tintColor: tintColor)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
struct ToolkitNavigationHeaderRight: View {
|
|
27
|
+
var headerRight: HeaderRightData?
|
|
28
|
+
var tintColor: Color?
|
|
29
|
+
@State private var isFavorite: Bool = false
|
|
30
|
+
@State private var isLoading: Bool = false
|
|
31
|
+
@Environment(\.applicationEnvironment) private var environment
|
|
32
|
+
|
|
33
|
+
private func apiValue(_ value: Any?) -> Any {
|
|
34
|
+
value ?? NSNull()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private var contextMap: [String: Any] {
|
|
38
|
+
let context = environment.applicationContext
|
|
39
|
+
return [
|
|
40
|
+
"appId": apiValue(context?.appId),
|
|
41
|
+
"code": apiValue(context?.appCode),
|
|
42
|
+
"name": apiValue(context?.appName),
|
|
43
|
+
"icon": apiValue(context?.appIcon),
|
|
44
|
+
"description": apiValue(context?.description),
|
|
45
|
+
"support": apiValue(context?.support),
|
|
46
|
+
"toolkitConfig": apiValue(context?.toolkitConfig),
|
|
47
|
+
"providerId": apiValue(context?.providerId),
|
|
48
|
+
"permissions": apiValue(context?.permissions)
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private var shouldShowShortcut: Bool {
|
|
53
|
+
headerRight?.useShortcut == true && !(headerRight?.useMore == true && environment.applicationContext == nil)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private func parseResponse(_ response: String) -> Any? {
|
|
57
|
+
guard let jsonData = response.data(using: .utf8),
|
|
58
|
+
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
|
59
|
+
return nil
|
|
60
|
+
}
|
|
61
|
+
return json["response"]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// MARK: - Actions
|
|
65
|
+
private func loadFavoriteState() {
|
|
66
|
+
environment.composeApi?.request(
|
|
67
|
+
funcName: "isFavoriteApp",
|
|
68
|
+
params: ["code": apiValue(environment.applicationContext?.appCode)]
|
|
69
|
+
) { response in
|
|
70
|
+
if let isFavorite = parseResponse(response) as? Bool {
|
|
71
|
+
self.isFavorite = isFavorite
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private func onPressShortcut() {
|
|
77
|
+
environment.composeApi?.request(
|
|
78
|
+
funcName: "onToolAction",
|
|
79
|
+
params: [
|
|
80
|
+
"item": ["key": "onFavorite"],
|
|
81
|
+
"context": contextMap
|
|
82
|
+
]
|
|
83
|
+
) { response in
|
|
84
|
+
if let result = parseResponse(response) as? [String: Any],
|
|
85
|
+
result["success"] as? Bool == true {
|
|
86
|
+
isFavorite.toggle()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private func onPressHelpCenter() {
|
|
92
|
+
environment.composeApi?.request(
|
|
93
|
+
funcName: "showHelpCenter",
|
|
94
|
+
params: [
|
|
95
|
+
"appId": apiValue(environment.applicationContext?.appId),
|
|
96
|
+
"code": apiValue(environment.applicationContext?.appCode),
|
|
97
|
+
"name": apiValue(environment.applicationContext?.appName),
|
|
98
|
+
"icon": apiValue(environment.applicationContext?.appIcon),
|
|
99
|
+
"description": apiValue(environment.applicationContext?.description)
|
|
100
|
+
]
|
|
101
|
+
) { _ in }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private func onPressClose() {
|
|
105
|
+
environment.composeApi?.request(
|
|
106
|
+
funcName: "dismissAll",
|
|
107
|
+
params: nil
|
|
108
|
+
) { _ in }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private func onPressMore() {
|
|
112
|
+
let params: [String: Any] = [
|
|
113
|
+
"useSystemTools": headerRight?.useSystemTools ?? true,
|
|
114
|
+
"tools": headerRight?.tools.map { $0.toMap() } ?? [],
|
|
115
|
+
"context": contextMap
|
|
116
|
+
]
|
|
117
|
+
environment.composeApi?.request(
|
|
118
|
+
funcName: "showTools",
|
|
119
|
+
params: params
|
|
120
|
+
) { response in
|
|
121
|
+
if let toolResponse = parseResponse(response) as? String {
|
|
122
|
+
headerRight?.toolCallback?(toolResponse)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private func getNavigationButtonConfig() -> NavigationButtonConfig {
|
|
128
|
+
let totalTools = headerRight?.tools.reduce(0) { $0 + $1.items.count } ?? 0
|
|
129
|
+
var icon = isFavorite ? "pin_star_checked" : "pin_star"
|
|
130
|
+
var onClickHandler: () -> Void = onPressShortcut
|
|
131
|
+
|
|
132
|
+
if totalTools > 1 || headerRight?.useMore == true {
|
|
133
|
+
icon = "navigation_more_icon"
|
|
134
|
+
onClickHandler = onPressMore
|
|
135
|
+
} else if totalTools == 1, let singleTool = headerRight?.tools.first?.items.first {
|
|
136
|
+
icon = singleTool.icon
|
|
137
|
+
onClickHandler = {
|
|
138
|
+
headerRight?.toolCallback?(singleTool.key)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return NavigationButtonConfig(icon: icon, onPress: onClickHandler)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
var body: some View {
|
|
146
|
+
let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
|
|
147
|
+
let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
|
|
148
|
+
let navButtonConfig = getNavigationButtonConfig()
|
|
149
|
+
let showBadge = headerRight?.tools.contains { group in
|
|
150
|
+
group.items.contains { $0.showBadge }
|
|
151
|
+
} ?? false
|
|
152
|
+
|
|
153
|
+
HStack(alignment: .center, spacing: 0) {
|
|
154
|
+
if shouldShowShortcut {
|
|
155
|
+
NavigationButton(
|
|
156
|
+
disabled: isLoading,
|
|
157
|
+
icon: navButtonConfig.icon,
|
|
158
|
+
showBadge: showBadge,
|
|
159
|
+
onClick: navButtonConfig.onPress,
|
|
160
|
+
tintColor: tintColor
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
HStack(alignment: .center, spacing: 0) {
|
|
165
|
+
if environment.applicationContext != nil {
|
|
166
|
+
Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
|
|
167
|
+
.padding(4)
|
|
168
|
+
.onTapGesture {
|
|
169
|
+
onPressHelpCenter()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
Rectangle()
|
|
173
|
+
.fill(tintColor ?? Colors.black20)
|
|
174
|
+
.frame(width: 0.5, height: 12)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
Icon(source: "16_basic_home", size: 20, color: tintColor ?? Colors.black17)
|
|
178
|
+
.padding(4)
|
|
179
|
+
.onTapGesture {
|
|
180
|
+
onPressClose()
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
.frame(width: 65, height: 28)
|
|
184
|
+
.background(backgroundButtonColor)
|
|
185
|
+
.cornerRadius(14)
|
|
186
|
+
.overlay(
|
|
187
|
+
RoundedRectangle(cornerRadius: 14)
|
|
188
|
+
.stroke(borderColor, lineWidth: 0.2)
|
|
189
|
+
)
|
|
190
|
+
.padding(.leading, Spacing.S)
|
|
191
|
+
}
|
|
192
|
+
.onAppear {
|
|
193
|
+
loadFavoriteState()
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -20,26 +20,21 @@ public struct PopupPromotion: View {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
public var body: some View {
|
|
23
|
-
// Mirrors Kotlin's `Modifier.fillMaxWidth().aspectRatio(0.72f)` box with
|
|
24
|
-
// `ContentScale.FillBounds` inside it: a fixed-ratio frame the image stretches
|
|
25
|
-
// to exactly fill (no `.aspectRatio` modifier on the image itself, unlike the
|
|
26
|
-
// previous `.fit`, which letterboxed instead of stretching).
|
|
27
|
-
let boxWidth = UIScreen.main.bounds.width - Spacing.XL * 2
|
|
28
|
-
let boxHeight = boxWidth / 0.72
|
|
29
|
-
|
|
30
23
|
VStack(spacing: 8) {
|
|
31
24
|
if(imageUrl.isEmpty) {
|
|
32
|
-
Icon(source: "media_fail", size:
|
|
25
|
+
Icon(source: "media_fail", size: UIScreen.main.bounds.width - Spacing.XL * 2)
|
|
33
26
|
}
|
|
34
27
|
else {
|
|
35
28
|
WebImage(url: URL(string: imageUrl)!)
|
|
36
29
|
.resizable()
|
|
37
30
|
.placeholder {
|
|
38
31
|
Color.gray
|
|
39
|
-
.
|
|
32
|
+
.aspectRatio(0.72, contentMode: .fit)
|
|
33
|
+
.frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2)
|
|
40
34
|
.clipped()
|
|
41
35
|
}
|
|
42
|
-
.
|
|
36
|
+
.aspectRatio(0.72, contentMode: .fit)
|
|
37
|
+
.frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2, alignment: .center)
|
|
43
38
|
.clipped()
|
|
44
39
|
.onTapGesture {
|
|
45
40
|
onAction?()
|
|
@@ -70,7 +65,6 @@ public struct PopupPromotion: View {
|
|
|
70
65
|
.padding(8)
|
|
71
66
|
.zIndex(1)
|
|
72
67
|
}
|
|
73
|
-
.buttonStyle(.plain)
|
|
74
68
|
}
|
|
75
69
|
}
|
|
76
70
|
|
|
@@ -51,8 +51,8 @@ let defaultBanner = TrustBannerData(
|
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
public struct TrustBanner: View {
|
|
54
|
-
@
|
|
55
|
-
@
|
|
54
|
+
@Environment(\.applicationEnvironment) var applicationEnvironment
|
|
55
|
+
@Environment(\.localize) private var localize
|
|
56
56
|
|
|
57
57
|
var onPress: ((String)->Void)?
|
|
58
58
|
|
package/ios/native-kits.podspec
CHANGED
|
@@ -29,7 +29,7 @@ struct PopupPromotionDemo: View {
|
|
|
29
29
|
private struct PopupPromotionDemoContent: View {
|
|
30
30
|
@EnvironmentObject private var navigator: Navigator
|
|
31
31
|
|
|
32
|
-
@State private var imageUrl = "https://static.momocdn.net/app/
|
|
32
|
+
@State private var imageUrl = "https://static.momocdn.net/app/app/appx/ABC.png?size=M"
|
|
33
33
|
|
|
34
34
|
var body: some View {
|
|
35
35
|
ScrollView {
|
|
@@ -48,7 +48,7 @@ private struct PopupPromotionDemoContent: View {
|
|
|
48
48
|
)
|
|
49
49
|
.padding(.top, 8)
|
|
50
50
|
}
|
|
51
|
-
.padding(
|
|
51
|
+
.padding(Spacing.M * 2)
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
package/local.properties
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## This file must *NOT* be checked into Version Control Systems,
|
|
2
|
+
# as it contains information specific to your local configuration.
|
|
3
|
+
#
|
|
4
|
+
# Location of the SDK. This is only used by Gradle.
|
|
5
|
+
# For customization when using a Version Control System, please read the
|
|
6
|
+
# header note.
|
|
7
|
+
#Tue Jun 02 17:05:32 ICT 2026
|
|
8
|
+
sdk.dir=/Users/sophia/Library/Android/sdk
|