chore: 修改文案
This commit is contained in:
parent
bae3923475
commit
28a9db04ab
Binary file not shown.
81
wake.xcodeproj/xcshareddata/xcschemes/wake.xcscheme
Normal file
81
wake.xcodeproj/xcshareddata/xcschemes/wake.xcscheme
Normal file
@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ABB4E2072E4B75D900660198"
|
||||
BuildableName = "wake.app"
|
||||
BlueprintName = "wake"
|
||||
ReferencedContainer = "container:wake.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ABB4E2072E4B75D900660198"
|
||||
BuildableName = "wake.app"
|
||||
BlueprintName = "wake"
|
||||
ReferencedContainer = "container:wake.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<StoreKitConfigurationFileReference
|
||||
identifier = "../../wake/MemoWake.storekit">
|
||||
</StoreKitConfigurationFileReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ABB4E2072E4B75D900660198"
|
||||
BuildableName = "wake.app"
|
||||
BlueprintName = "wake"
|
||||
ReferencedContainer = "container:wake.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -3,22 +3,4 @@
|
||||
uuid = "55F37A93-4556-4005-B9BD-8F1A1D6A8474"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "F1BBF7E2-4D6E-4646-83BC-F57E600056E4"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "wake/View/Subscribe/SubscribeView.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "65"
|
||||
endingLineNumber = "65"
|
||||
landmarkName = "body"
|
||||
landmarkType = "24">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
|
||||
@ -10,5 +10,13 @@
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>ABB4E2072E4B75D900660198</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -79,6 +79,17 @@ struct ContentView: View {
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
|
||||
// 订阅测试按钮
|
||||
NavigationLink(destination: SubscribeView()) {
|
||||
Text("Subscribe")
|
||||
.font(.subheadline)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(Color.orange)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
.padding(.trailing)
|
||||
}
|
||||
|
||||
|
||||
215
wake/MemoWake.storekit
Normal file
215
wake/MemoWake.storekit
Normal file
@ -0,0 +1,215 @@
|
||||
{
|
||||
"appPolicies" : {
|
||||
"eula" : "",
|
||||
"policies" : [
|
||||
{
|
||||
"locale" : "en_US",
|
||||
"policyText" : "",
|
||||
"policyURL" : ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"identifier" : "C75471B9",
|
||||
"nonRenewingSubscriptions" : [
|
||||
|
||||
],
|
||||
"products" : [
|
||||
|
||||
],
|
||||
"settings" : {
|
||||
"_applicationInternalID" : "6748205761",
|
||||
"_developerTeamID" : "392N3QB7XR",
|
||||
"_failTransactionsEnabled" : false,
|
||||
"_lastSynchronizedDate" : 777364219.49411595,
|
||||
"_locale" : "en_US",
|
||||
"_storefront" : "USA",
|
||||
"_storeKitErrors" : [
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Load Products"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Purchase"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Verification"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "App Store Sync"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Subscription Status"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "App Transaction"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Manage Subscriptions Sheet"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Refund Request Sheet"
|
||||
},
|
||||
{
|
||||
"current" : null,
|
||||
"enabled" : false,
|
||||
"name" : "Offer Code Redeem Sheet"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subscriptionGroups" : [
|
||||
{
|
||||
"id" : "21759571",
|
||||
"localizations" : [
|
||||
|
||||
],
|
||||
"name" : "Membership",
|
||||
"subscriptions" : [
|
||||
{
|
||||
"adHocOffers" : [
|
||||
|
||||
],
|
||||
"codeOffers" : [
|
||||
|
||||
],
|
||||
"displayPrice" : "0.99",
|
||||
"familyShareable" : false,
|
||||
"groupNumber" : 1,
|
||||
"internalID" : "6751260055",
|
||||
"introductoryOffer" : null,
|
||||
"localizations" : [
|
||||
{
|
||||
"description" : "The Pioneer Plan unlocks many restrictions.",
|
||||
"displayName" : "Pioneer Plan",
|
||||
"locale" : "en_US"
|
||||
},
|
||||
{
|
||||
"description" : "先锋计划用户,不限制盲盒购买数量,不限制回忆上传数量,每天免费获取500积分",
|
||||
"displayName" : "先锋计划",
|
||||
"locale" : "zh_Hans"
|
||||
}
|
||||
],
|
||||
"productID" : "MEMBERSHIP_PIONEER_MONTHLY",
|
||||
"recurringSubscriptionPeriod" : "P1M",
|
||||
"referenceName" : "Pioneer计划",
|
||||
"subscriptionGroupID" : "21759571",
|
||||
"type" : "RecurringSubscription",
|
||||
"winbackOffers" : [
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "21740727",
|
||||
"localizations" : [
|
||||
|
||||
],
|
||||
"name" : "Pro会员",
|
||||
"subscriptions" : [
|
||||
{
|
||||
"adHocOffers" : [
|
||||
|
||||
],
|
||||
"codeOffers" : [
|
||||
|
||||
],
|
||||
"displayPrice" : "12.99",
|
||||
"familyShareable" : false,
|
||||
"groupNumber" : 1,
|
||||
"internalID" : "6749133482",
|
||||
"introductoryOffer" : null,
|
||||
"localizations" : [
|
||||
{
|
||||
"description" : "Pro会员每月有更高的存储空间与积分数量",
|
||||
"displayName" : "季度Pro会员",
|
||||
"locale" : "zh_Hans"
|
||||
}
|
||||
],
|
||||
"productID" : "MEMBERSHIP_PRO_QUARTERLY",
|
||||
"recurringSubscriptionPeriod" : "P3M",
|
||||
"referenceName" : "季度Pro会员",
|
||||
"subscriptionGroupID" : "21740727",
|
||||
"type" : "RecurringSubscription",
|
||||
"winbackOffers" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"adHocOffers" : [
|
||||
|
||||
],
|
||||
"codeOffers" : [
|
||||
|
||||
],
|
||||
"displayPrice" : "59.99",
|
||||
"familyShareable" : false,
|
||||
"groupNumber" : 2,
|
||||
"internalID" : "6749229999",
|
||||
"introductoryOffer" : null,
|
||||
"localizations" : [
|
||||
{
|
||||
"description" : "Pro会员每月有更高的存储空间与积分数量",
|
||||
"displayName" : "年度Pro会员",
|
||||
"locale" : "zh_Hans"
|
||||
}
|
||||
],
|
||||
"productID" : "MEMBERSHIP_PRO_YEARLY",
|
||||
"recurringSubscriptionPeriod" : "P1Y",
|
||||
"referenceName" : "年度Pro会员",
|
||||
"subscriptionGroupID" : "21740727",
|
||||
"type" : "RecurringSubscription",
|
||||
"winbackOffers" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"adHocOffers" : [
|
||||
|
||||
],
|
||||
"codeOffers" : [
|
||||
|
||||
],
|
||||
"displayPrice" : "3.99",
|
||||
"familyShareable" : false,
|
||||
"groupNumber" : 3,
|
||||
"internalID" : "6749230171",
|
||||
"introductoryOffer" : null,
|
||||
"localizations" : [
|
||||
{
|
||||
"description" : "Pro会员每月有更高的存储空间与积分数量",
|
||||
"displayName" : "月度Pro会员",
|
||||
"locale" : "zh_Hans"
|
||||
}
|
||||
],
|
||||
"productID" : "MEMBERSHIP_PRO_MONTH",
|
||||
"recurringSubscriptionPeriod" : "P1M",
|
||||
"referenceName" : "月度Pro会员",
|
||||
"subscriptionGroupID" : "21740727",
|
||||
"type" : "RecurringSubscription",
|
||||
"winbackOffers" : [
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"version" : {
|
||||
"major" : 4,
|
||||
"minor" : 0
|
||||
}
|
||||
}
|
||||
125
wake/Utils/IAPManager.swift
Normal file
125
wake/Utils/IAPManager.swift
Normal file
@ -0,0 +1,125 @@
|
||||
import Foundation
|
||||
import StoreKit
|
||||
|
||||
@MainActor
|
||||
final class IAPManager: ObservableObject {
|
||||
@Published var isPurchasing: Bool = false
|
||||
@Published var pioneerProduct: Product?
|
||||
@Published var errorMessage: String?
|
||||
@Published var isSubscribed: Bool = false
|
||||
@Published var subscriptionExpiry: Date? = nil
|
||||
|
||||
// Product IDs from App Store Connect
|
||||
private let productIDs: [String] = [
|
||||
"MEMBERSHIP_PIONEER_MONTHLY"
|
||||
]
|
||||
|
||||
init() {
|
||||
Task { await observeTransactions() }
|
||||
}
|
||||
|
||||
// Load products defined in App Store Connect
|
||||
func loadProducts() async {
|
||||
do {
|
||||
let products = try await Product.products(for: productIDs)
|
||||
// You can refine selection logic if you have multiple tiers
|
||||
self.pioneerProduct = products.first
|
||||
if products.isEmpty {
|
||||
// No products found is a common setup issue (App Store Connect, StoreKit config, or bundle ID)
|
||||
self.errorMessage = "No subscription products found. Please try again later."
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "Failed to load products: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger App Store purchase sheet
|
||||
func purchasePioneer() async {
|
||||
guard !isPurchasing else { return }
|
||||
guard let product = pioneerProduct else {
|
||||
// Surface an actionable error so the UI can inform the user
|
||||
self.errorMessage = "Subscription product unavailable. Please try again later."
|
||||
return
|
||||
}
|
||||
isPurchasing = true
|
||||
defer { isPurchasing = false }
|
||||
|
||||
do {
|
||||
let result = try await product.purchase()
|
||||
switch result {
|
||||
case .success(let verification):
|
||||
switch verification {
|
||||
case .unverified(_, let error):
|
||||
self.errorMessage = "Purchase unverified: \(error.localizedDescription)"
|
||||
case .verified(let transaction):
|
||||
// Update entitlement for the purchased product
|
||||
updateEntitlement(from: transaction)
|
||||
await transaction.finish()
|
||||
}
|
||||
case .userCancelled:
|
||||
break
|
||||
case .pending:
|
||||
break
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "Purchase failed: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
|
||||
// Restore purchases (sync entitlements)
|
||||
func restorePurchases() async {
|
||||
do {
|
||||
try await AppStore.sync()
|
||||
} catch {
|
||||
self.errorMessage = "Restore failed: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
|
||||
// Observe transaction updates for entitlement changes
|
||||
private func observeTransactions() async {
|
||||
for await result in Transaction.updates {
|
||||
do {
|
||||
let transaction: Transaction = try checkVerified(result)
|
||||
// Update entitlement state for transaction.productID
|
||||
updateEntitlement(from: transaction)
|
||||
await transaction.finish()
|
||||
} catch {
|
||||
// Ignore unverified transactions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check current entitlements (useful on launch)
|
||||
func refreshEntitlements() async {
|
||||
for await result in Transaction.currentEntitlements {
|
||||
if case .verified(let transaction) = result,
|
||||
productIDs.contains(transaction.productID) {
|
||||
updateEntitlement(from: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: verify
|
||||
private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
|
||||
switch result {
|
||||
case .unverified(_, let error):
|
||||
throw error
|
||||
case .verified(let safe):
|
||||
return safe
|
||||
}
|
||||
}
|
||||
|
||||
private func updateEntitlement(from transaction: Transaction) {
|
||||
guard productIDs.contains(transaction.productID) else { return }
|
||||
// For auto-renewable subs, use expirationDate and revocationDate
|
||||
if transaction.revocationDate == nil {
|
||||
self.isSubscribed = true
|
||||
self.subscriptionExpiry = transaction.expirationDate
|
||||
} else {
|
||||
self.isSubscribed = false
|
||||
self.subscriptionExpiry = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,35 +23,12 @@ struct SettingsView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
// 自定义导航栏
|
||||
HStack {
|
||||
// 返回按钮
|
||||
Button(action: {
|
||||
// 简洁导航头
|
||||
SimpleNaviHeader(title: "Setting") {
|
||||
withAnimation(animation) {
|
||||
isPresented = false
|
||||
}
|
||||
}) {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "chevron.left")
|
||||
.font(.system(size: 16, weight: .medium))
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
// 标题
|
||||
Text("Setting")
|
||||
.font(.headline)
|
||||
|
||||
Spacer()
|
||||
|
||||
// 用于平衡布局的透明视图
|
||||
Color.clear
|
||||
.frame(width: 44, height: 44)
|
||||
}
|
||||
.padding(.horizontal,16)
|
||||
.padding(.vertical, 8)
|
||||
|
||||
// 设置项列表
|
||||
List {
|
||||
|
||||
@ -25,7 +25,7 @@ struct PlanCompare: View {
|
||||
title: "Mystery Box Purchase:",
|
||||
subtitle: nil,
|
||||
freeValue: "3 /week",
|
||||
pioneerValue: "Free",
|
||||
pioneerValue: "Unlimited",
|
||||
icon: nil
|
||||
),
|
||||
PlanFeature(
|
||||
|
||||
@ -5,15 +5,18 @@ struct SubscribeButton: View {
|
||||
let title: String
|
||||
let isLoading: Bool
|
||||
let action: () -> Void
|
||||
let subscribed: Bool
|
||||
|
||||
init(
|
||||
title: String = "Subscribe",
|
||||
isLoading: Bool,
|
||||
action: @escaping () -> Void
|
||||
subscribed: Bool,
|
||||
action: @escaping () -> Void,
|
||||
) {
|
||||
self.title = title
|
||||
self.isLoading = isLoading
|
||||
self.action = action
|
||||
self.subscribed = subscribed
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@ -29,6 +32,13 @@ struct SubscribeButton: View {
|
||||
}
|
||||
|
||||
VStack {
|
||||
if subscribed {
|
||||
Spacer()
|
||||
Text("Subscribed")
|
||||
.font(Typography.font(for: .body, family: .quicksandBold))
|
||||
Spacer()
|
||||
}
|
||||
else {
|
||||
Spacer()
|
||||
Spacer()
|
||||
Text(title)
|
||||
@ -42,6 +52,7 @@ struct SubscribeButton: View {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 56)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Theme.Colors.primary) // primary color background
|
||||
@ -54,15 +65,16 @@ struct SubscribeButton: View {
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(isLoading)
|
||||
.disabled(isLoading || subscribed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("SubscribeButton") {
|
||||
VStack(spacing: Theme.Spacing.xl) {
|
||||
SubscribeButton(isLoading: false) {}
|
||||
SubscribeButton(isLoading: true) {}
|
||||
SubscribeButton(isLoading: false, subscribed: false) {}
|
||||
SubscribeButton(isLoading: true, subscribed: false) {}
|
||||
SubscribeButton(isLoading: false, subscribed: true) {}
|
||||
}
|
||||
.padding()
|
||||
.background(Theme.Colors.background)
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import StoreKit
|
||||
|
||||
// MARK: - 订阅计划枚举
|
||||
enum SubscriptionPlan: String, CaseIterable {
|
||||
@ -38,9 +39,13 @@ struct SubscriptionFeature {
|
||||
}
|
||||
|
||||
struct SubscribeView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@StateObject private var store = IAPManager()
|
||||
@State private var selectedPlan: SubscriptionPlan? = .pioneer
|
||||
@State private var isLoading = false
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@State private var showErrorAlert = false
|
||||
@State private var errorText = ""
|
||||
|
||||
|
||||
// 功能对比数据
|
||||
private let features = [
|
||||
@ -50,12 +55,14 @@ struct SubscribeView: View {
|
||||
]
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
VStack(spacing: 0) {
|
||||
// 自定义简洁导航头,统一风格
|
||||
SimpleNaviHeader(title: "Subscription") {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
VStack(spacing: 0) {
|
||||
// 导航栏
|
||||
navigationHeader
|
||||
|
||||
// 当前订阅状态卡片
|
||||
currentSubscriptionCard
|
||||
|
||||
@ -88,21 +95,42 @@ struct SubscribeView: View {
|
||||
}
|
||||
}
|
||||
.background(Theme.Colors.background)
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.task {
|
||||
// Load products and refresh current entitlements on appear
|
||||
await store.loadProducts()
|
||||
await store.refreshEntitlements()
|
||||
}
|
||||
.onChange(of: store.isPurchasing) { newValue in
|
||||
// Bind purchasing state to button loading
|
||||
isLoading = newValue
|
||||
}
|
||||
.onChange(of: store.errorMessage) { newValue in
|
||||
if let message = newValue, !message.isEmpty {
|
||||
errorText = message
|
||||
showErrorAlert = true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 导航栏
|
||||
private var navigationHeader: some View {
|
||||
NaviHeader(title: "Subscription") {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
.alert("Purchase Error", isPresented: $showErrorAlert) {
|
||||
Button("OK", role: .cancel) { store.errorMessage = nil }
|
||||
} message: {
|
||||
Text(errorText)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 当前订阅状态卡片
|
||||
private var currentSubscriptionCard: some View {
|
||||
SubscriptionStatusBar(
|
||||
status: .pioneer(expiryDate: Date()) ,
|
||||
let status: SubscriptionStatus = {
|
||||
if store.isSubscribed {
|
||||
return .pioneer(expiryDate: store.subscriptionExpiry ?? Date())
|
||||
} else {
|
||||
return .free
|
||||
}
|
||||
}()
|
||||
|
||||
return SubscriptionStatusBar(
|
||||
status: status,
|
||||
onSubscribeTap: {
|
||||
// 订阅操作
|
||||
handleSubscribe()
|
||||
@ -182,6 +210,7 @@ struct SubscribeView: View {
|
||||
SubscribeButton(
|
||||
title: "Subscribe",
|
||||
isLoading: isLoading,
|
||||
subscribed: store.isSubscribed,
|
||||
action: handleSubscribe
|
||||
)
|
||||
}
|
||||
@ -213,7 +242,7 @@ struct SubscribeView: View {
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Button(action: {
|
||||
// 恢复购买
|
||||
Task { await store.restorePurchases() }
|
||||
}) {
|
||||
Text("Restore Purchase")
|
||||
.underline()
|
||||
@ -226,13 +255,7 @@ struct SubscribeView: View {
|
||||
|
||||
// MARK: - 订阅处理
|
||||
private func handleSubscribe() {
|
||||
isLoading = true
|
||||
|
||||
// 模拟订阅处理
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||
isLoading = false
|
||||
// 处理订阅逻辑
|
||||
}
|
||||
Task { await store.purchasePioneer() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user