diff --git a/AIGrammar.xcodeproj/project.pbxproj b/AIGrammar.xcodeproj/project.pbxproj index 5e166d1..4c94a75 100644 --- a/AIGrammar.xcodeproj/project.pbxproj +++ b/AIGrammar.xcodeproj/project.pbxproj @@ -723,7 +723,7 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar; 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_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -759,7 +759,7 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar; 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_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-1024.png index 697f327..e6ccef8 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-1024.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png index 279a758..00de7b0 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png index 86d6205..889ab10 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png index e1c3f93..fb6b3f7 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png index 5a32cb2..c21394d 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png index 9f192ea..8b408d9 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png index 195391f..a7296f6 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png index 3dc20a6..bd533e0 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png index 763a246..8ddca72 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png index 763a246..8ddca72 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png index edfd7ab..676ebf8 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png index 03c97ba..dd91db9 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png index 738f755..2badb1a 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png index c6f1990..749818a 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png index e5dd69b..17fea01 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ diff --git a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png index 8560367..16c7334 100644 Binary files a/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png and b/AIGrammar/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ diff --git a/AIGrammar/View/IAPView.swift b/AIGrammar/View/IAPView.swift index ea7af6b..3235fcb 100644 --- a/AIGrammar/View/IAPView.swift +++ b/AIGrammar/View/IAPView.swift @@ -16,9 +16,9 @@ struct VIPPaymentView: View { let freeValues = ["2 Times / Day", "2 Times / Day", "2 Times / Day"] let premiumValues = ["Unlimited", "Unlimited", "Unlimited"] let products = [ - ("Premium Weekly", "$4.99", "Billed Weekly", "weekly", IAPProduct.premiumFeature1), - ("Premium Monthly", "$9.99 / month", "Billed Monthly", "monthly", IAPProduct.premiumFeature2), - ("Premium Yearly", "$49.99 / year", "Billed Yearly", "yearly", IAPProduct.premiumFeature3) + ("Weekly", "$4.99 / week", "Billed as one payment of $4.99", "weekly", IAPProduct.premiumFeature1, "Billed Weekly"), + ("Monthly", "$9.99 / month", "Billed as one payment of $9.99", "monthly", IAPProduct.premiumFeature2, "Billed Monthly"), + ("Annual", "$4.16 / month", "Billed as one payment of $49.99", "yearly", IAPProduct.premiumFeature3, "Billed Yearly") ] @State private var selectedProductIndex: Int = 1 // 默认选择第二个商品 @@ -74,44 +74,46 @@ struct VIPPaymentView: View { } } - ForEach(products.indices, id: \.self) { index in - Button(action: { - // do someting - }) { - HStack { - Image(systemName: selectedProductIndex == index ? "checkmark.circle.fill" : "circle") - .foregroundColor(selectedProductIndex == index ? .green : .secondary) - .padding(.horizontal, 4) - - VStack(alignment: .leading) { - if products[index].0 == "Premium Yearly" { - // 特别为 "Premium Yearly" 添加图标和折扣信息 - Text(products[index].0) + Text(" ⚡️") + Text(" 58% off") - .bold() - .foregroundColor(.red) // 折扣信息使用红色 + VStack(alignment: .leading) { // 使用 VStack 包装整个 ForEach 循环,并指定左对齐 + ForEach(products.indices, id: \.self) { index in + Button(action: { + // do someting + }) { + HStack() { + Image(systemName: selectedProductIndex == index ? "checkmark.circle.fill" : "circle") + .foregroundColor(selectedProductIndex == index ? .green : .secondary) + .padding(.horizontal, 4) + + VStack(alignment: .leading) { + if products[index].0 == "Annual" { + // 特别为 "Premium Yearly" 添加图标和折扣信息 + Text(products[index].0) + Text(" ⚡️") + Text(" 58% off") + .bold() + .foregroundColor(.red) // 折扣信息使用红色 + .font(.subheadline) + } else { + Text(products[index].0) + } + Text(products[index].1) + .font(.caption) + Text(products[index].2) .font(.subheadline) - } else { - Text(products[index].0) } - Text(products[index].1) - .font(.subheadline) + .frame(maxWidth: .infinity, alignment: .leading) // 使每个 HStack 占据整个宽度,并左对齐 + } + .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") { // Handle purchase logic here @@ -125,7 +127,7 @@ struct VIPPaymentView: View { .frame(minWidth: 0, maxWidth: .infinity) .shadow(radius: 2) // 添加阴影效果 .font(.headline) // 调整字体大小为标题大小 - .padding(.top, 50) + .padding(.top, 20) Text((selectedPlanText ) + ", Cancel Anytime") .font(.footnote) @@ -135,7 +137,7 @@ struct VIPPaymentView: View { } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(.systemBackground)) - .toast(isPresented: $showingToast, dismissAfter: 5.0, onDismiss: { + .toast(isPresented: $showingToast, dismissAfter: globalEnvironment.toastPresentMsNormal, onDismiss: { // Toast 消失后执行的动作 presentationMode.wrappedValue.dismiss() // 关闭当前视图 }) { diff --git a/AIGrammar/View/SettingsView.swift b/AIGrammar/View/SettingsView.swift index 4322c5f..b6785de 100644 --- a/AIGrammar/View/SettingsView.swift +++ b/AIGrammar/View/SettingsView.swift @@ -53,6 +53,7 @@ struct SettingsView: View { @EnvironmentObject var iapManager: IAPManager // 确保在上级视图中已提供 IAPManager @State private var showingFullSafari = false + @State private var currentSafariURL = globalEnvironment.userTermsURL @State private var showingAdvancedSettings = false // 控制高级设置显示的状态 @@ -78,10 +79,19 @@ struct SettingsView: View { } // About - settingItem(icon: "info.circle", text: "About") + settingItem(icon: "info.circle", text: "Terms of Use") .onTapGesture { // Code to show About View 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 @@ -92,7 +102,7 @@ struct SettingsView: View { } .fullScreenCover(isPresented: $showingFullSafari) { - FullScreenSafariView(url: URL(string: globalEnvironment.userTermsURL)!, onDismiss: { + FullScreenSafariView(url: URL(string: currentSafariURL)!, onDismiss: { self.showingFullSafari = false }) } diff --git a/AIGrammar/ViewModel/Config.swift b/AIGrammar/ViewModel/Config.swift index ff9c35d..e05dcea 100644 --- a/AIGrammar/ViewModel/Config.swift +++ b/AIGrammar/ViewModel/Config.swift @@ -54,7 +54,8 @@ class GlobalEnvironment: ObservableObject { 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" diff --git a/AIGrammar/lib/IapManager.swift b/AIGrammar/lib/IapManager.swift index feeca42..683fcea 100644 --- a/AIGrammar/lib/IapManager.swift +++ b/AIGrammar/lib/IapManager.swift @@ -111,8 +111,8 @@ class IAPManager: ObservableObject { logger.error("User cancelled the purchase.") 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: // 需要上报上去