修改IAP及隐私条款相关。
@ -723,7 +723,7 @@
|
|||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar;
|
INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "用户拍照获取文本";
|
INFOPLIST_KEY_NSCameraUsageDescription = "Camera access is required to capture text for grammar and spelling correction.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
@ -759,7 +759,7 @@
|
|||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar;
|
INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "用户拍照获取文本";
|
INFOPLIST_KEY_NSCameraUsageDescription = "Camera access is required to capture text for grammar and spelling correction.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
@ -16,9 +16,9 @@ struct VIPPaymentView: View {
|
|||||||
let freeValues = ["2 Times / Day", "2 Times / Day", "2 Times / Day"]
|
let freeValues = ["2 Times / Day", "2 Times / Day", "2 Times / Day"]
|
||||||
let premiumValues = ["Unlimited", "Unlimited", "Unlimited"]
|
let premiumValues = ["Unlimited", "Unlimited", "Unlimited"]
|
||||||
let products = [
|
let products = [
|
||||||
("Premium Weekly", "$4.99", "Billed Weekly", "weekly", IAPProduct.premiumFeature1),
|
("Weekly", "$4.99 / week", "Billed as one payment of $4.99", "weekly", IAPProduct.premiumFeature1, "Billed Weekly"),
|
||||||
("Premium Monthly", "$9.99 / month", "Billed Monthly", "monthly", IAPProduct.premiumFeature2),
|
("Monthly", "$9.99 / month", "Billed as one payment of $9.99", "monthly", IAPProduct.premiumFeature2, "Billed Monthly"),
|
||||||
("Premium Yearly", "$49.99 / year", "Billed Yearly", "yearly", IAPProduct.premiumFeature3)
|
("Annual", "$4.16 / month", "Billed as one payment of $49.99", "yearly", IAPProduct.premiumFeature3, "Billed Yearly")
|
||||||
]
|
]
|
||||||
|
|
||||||
@State private var selectedProductIndex: Int = 1 // 默认选择第二个商品
|
@State private var selectedProductIndex: Int = 1 // 默认选择第二个商品
|
||||||
@ -74,44 +74,46 @@ struct VIPPaymentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach(products.indices, id: \.self) { index in
|
VStack(alignment: .leading) { // 使用 VStack 包装整个 ForEach 循环,并指定左对齐
|
||||||
Button(action: {
|
ForEach(products.indices, id: \.self) { index in
|
||||||
// do someting
|
Button(action: {
|
||||||
}) {
|
// do someting
|
||||||
HStack {
|
}) {
|
||||||
Image(systemName: selectedProductIndex == index ? "checkmark.circle.fill" : "circle")
|
HStack() {
|
||||||
.foregroundColor(selectedProductIndex == index ? .green : .secondary)
|
Image(systemName: selectedProductIndex == index ? "checkmark.circle.fill" : "circle")
|
||||||
.padding(.horizontal, 4)
|
.foregroundColor(selectedProductIndex == index ? .green : .secondary)
|
||||||
|
.padding(.horizontal, 4)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
if products[index].0 == "Premium Yearly" {
|
if products[index].0 == "Annual" {
|
||||||
// 特别为 "Premium Yearly" 添加图标和折扣信息
|
// 特别为 "Premium Yearly" 添加图标和折扣信息
|
||||||
Text(products[index].0) + Text(" ⚡️") + Text(" 58% off")
|
Text(products[index].0) + Text(" ⚡️") + Text(" 58% off")
|
||||||
.bold()
|
.bold()
|
||||||
.foregroundColor(.red) // 折扣信息使用红色
|
.foregroundColor(.red) // 折扣信息使用红色
|
||||||
|
.font(.subheadline)
|
||||||
|
} else {
|
||||||
|
Text(products[index].0)
|
||||||
|
}
|
||||||
|
Text(products[index].1)
|
||||||
|
.font(.caption)
|
||||||
|
Text(products[index].2)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
} else {
|
|
||||||
Text(products[index].0)
|
|
||||||
}
|
}
|
||||||
Text(products[index].1)
|
.frame(maxWidth: .infinity, alignment: .leading) // 使每个 HStack 占据整个宽度,并左对齐
|
||||||
.font(.subheadline)
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(self.selectedProductIndex == index ? Color.yellow : Color.clear)
|
||||||
|
.cornerRadius(5)
|
||||||
|
.padding(.horizontal, 20) // 增加水平缩进
|
||||||
|
.onTapGesture {
|
||||||
|
self.selectedProductIndex = index
|
||||||
|
self.selectedPlanText = products[index].5
|
||||||
}
|
}
|
||||||
Spacer()
|
|
||||||
Text(products[index].2)
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(self.selectedProductIndex == index ? Color.yellow : Color.clear)
|
|
||||||
.cornerRadius(5)
|
|
||||||
.padding(.horizontal, 20) // 增加水平缩进
|
|
||||||
.onTapGesture {
|
|
||||||
self.selectedProductIndex = index
|
|
||||||
self.selectedPlanText = products[index].2
|
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading) // 使每个 HStack 占据整个宽度,并左对齐
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer()
|
.padding(.horizontal, 5) // 增加水平缩进
|
||||||
|
|
||||||
Button("Purchase") {
|
Button("Purchase") {
|
||||||
// Handle purchase logic here
|
// Handle purchase logic here
|
||||||
@ -125,7 +127,7 @@ struct VIPPaymentView: View {
|
|||||||
.frame(minWidth: 0, maxWidth: .infinity)
|
.frame(minWidth: 0, maxWidth: .infinity)
|
||||||
.shadow(radius: 2) // 添加阴影效果
|
.shadow(radius: 2) // 添加阴影效果
|
||||||
.font(.headline) // 调整字体大小为标题大小
|
.font(.headline) // 调整字体大小为标题大小
|
||||||
.padding(.top, 50)
|
.padding(.top, 20)
|
||||||
|
|
||||||
Text((selectedPlanText ) + ", Cancel Anytime")
|
Text((selectedPlanText ) + ", Cancel Anytime")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
@ -135,7 +137,7 @@ struct VIPPaymentView: View {
|
|||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.background(Color(.systemBackground))
|
.background(Color(.systemBackground))
|
||||||
.toast(isPresented: $showingToast, dismissAfter: 5.0, onDismiss: {
|
.toast(isPresented: $showingToast, dismissAfter: globalEnvironment.toastPresentMsNormal, onDismiss: {
|
||||||
// Toast 消失后执行的动作
|
// Toast 消失后执行的动作
|
||||||
presentationMode.wrappedValue.dismiss() // 关闭当前视图
|
presentationMode.wrappedValue.dismiss() // 关闭当前视图
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@ -53,6 +53,7 @@ struct SettingsView: View {
|
|||||||
@EnvironmentObject var iapManager: IAPManager // 确保在上级视图中已提供 IAPManager
|
@EnvironmentObject var iapManager: IAPManager // 确保在上级视图中已提供 IAPManager
|
||||||
|
|
||||||
@State private var showingFullSafari = false
|
@State private var showingFullSafari = false
|
||||||
|
@State private var currentSafariURL = globalEnvironment.userTermsURL
|
||||||
|
|
||||||
|
|
||||||
@State private var showingAdvancedSettings = false // 控制高级设置显示的状态
|
@State private var showingAdvancedSettings = false // 控制高级设置显示的状态
|
||||||
@ -78,10 +79,19 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// About
|
// About
|
||||||
settingItem(icon: "info.circle", text: "About")
|
settingItem(icon: "info.circle", text: "Terms of Use")
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
// Code to show About View
|
// Code to show About View
|
||||||
self.showingFullSafari = true
|
self.showingFullSafari = true
|
||||||
|
currentSafariURL = globalEnvironment.userTermsURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// About
|
||||||
|
settingItem(icon: "info.circle", text: "Privacy Policy")
|
||||||
|
.onTapGesture {
|
||||||
|
// Code to show About View
|
||||||
|
self.showingFullSafari = true
|
||||||
|
currentSafariURL = globalEnvironment.userPrivacyURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore Purchases
|
// Restore Purchases
|
||||||
@ -92,7 +102,7 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
}
|
}
|
||||||
.fullScreenCover(isPresented: $showingFullSafari) {
|
.fullScreenCover(isPresented: $showingFullSafari) {
|
||||||
FullScreenSafariView(url: URL(string: globalEnvironment.userTermsURL)!, onDismiss: {
|
FullScreenSafariView(url: URL(string: currentSafariURL)!, onDismiss: {
|
||||||
self.showingFullSafari = false
|
self.showingFullSafari = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,7 +54,8 @@ class GlobalEnvironment: ObservableObject {
|
|||||||
|
|
||||||
var jwtSecret: String = "mCTf-JhNRnhaaGJy_x"
|
var jwtSecret: String = "mCTf-JhNRnhaaGJy_x"
|
||||||
|
|
||||||
var userTermsURL: String = "https://grammar.easyprompt8.com/about/"
|
var userTermsURL: String = "https://grammar.easyprompt8.com/about/terms.html"
|
||||||
|
var userPrivacyURL: String = "https://grammar.easyprompt8.com/about/privacy.html"
|
||||||
|
|
||||||
// 请求地址,区分环境。
|
// 请求地址,区分环境。
|
||||||
// var baseHost: String = "http://192.168.2.2:1080"
|
// var baseHost: String = "http://192.168.2.2:1080"
|
||||||
|
|||||||
@ -111,8 +111,8 @@ class IAPManager: ObservableObject {
|
|||||||
logger.error("User cancelled the purchase.")
|
logger.error("User cancelled the purchase.")
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// 用户取消购买
|
// 用户取消购买,暂时不要做什么动作。
|
||||||
completion(.failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Purchase Cancelled"])))
|
// completion(.failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Purchase Cancelled"])))
|
||||||
}
|
}
|
||||||
case .pending:
|
case .pending:
|
||||||
// 需要上报上去
|
// 需要上报上去
|
||||||
|
|||||||