- Update NeonMoodTint to use synthwave colors matching Neon voting style (cyan, lime, yellow, orange, magenta) - Replace text label with 5 color circles in theme preview Colors row - Remove unused textColor customization code and picker views - Add .id(moodTint) to Month/Year views for color refresh - Clean up various unused color-related code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
199 lines
6.3 KiB
Swift
199 lines
6.3 KiB
Swift
//
|
|
// PurchaseButtonView.swift
|
|
// Feels
|
|
//
|
|
// Subscription status and purchase view for settings.
|
|
//
|
|
|
|
import SwiftUI
|
|
import StoreKit
|
|
|
|
struct PurchaseButtonView: View {
|
|
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
|
|
|
|
private var textColor: Color { theme.currentTheme.labelColor }
|
|
|
|
@ObservedObject var iapManager: IAPManager
|
|
|
|
@State private var showSubscriptionStore = false
|
|
@State private var showManageSubscriptions = false
|
|
|
|
var body: some View {
|
|
VStack(spacing: 16) {
|
|
if iapManager.isLoading {
|
|
loadingView
|
|
} else if iapManager.isSubscribed {
|
|
subscribedView
|
|
} else {
|
|
notSubscribedView
|
|
}
|
|
}
|
|
.padding()
|
|
.background(theme.currentTheme.secondaryBGColor)
|
|
.cornerRadius(10)
|
|
.sheet(isPresented: $showSubscriptionStore) {
|
|
FeelsSubscriptionStoreView()
|
|
}
|
|
.manageSubscriptionsSheet(isPresented: $showManageSubscriptions)
|
|
}
|
|
|
|
// MARK: - Loading View
|
|
|
|
private var loadingView: some View {
|
|
VStack(spacing: 12) {
|
|
ProgressView()
|
|
Text(String(localized: "purchase_view_loading"))
|
|
.font(.body)
|
|
.foregroundColor(textColor)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
}
|
|
|
|
// MARK: - Subscribed View
|
|
|
|
private var subscribedView: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text(String(localized: "purchase_view_current_subscription"))
|
|
.font(.title3)
|
|
.bold()
|
|
.foregroundColor(textColor)
|
|
|
|
if let product = iapManager.currentProduct {
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(product.displayName)
|
|
.font(.headline)
|
|
.foregroundColor(textColor)
|
|
Text(product.displayPrice)
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
subscriptionStatusBadge
|
|
}
|
|
}
|
|
|
|
Divider()
|
|
|
|
// Manage subscription button
|
|
Button {
|
|
showManageSubscriptions = true
|
|
} label: {
|
|
Text(String(localized: "purchase_view_manage_subscription"))
|
|
.font(.body)
|
|
.foregroundColor(.blue)
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
|
|
// Show other subscription options
|
|
if iapManager.sortedProducts.count > 1 {
|
|
Button {
|
|
showSubscriptionStore = true
|
|
} label: {
|
|
Text(String(localized: "purchase_view_change_plan"))
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var subscriptionStatusBadge: some View {
|
|
Group {
|
|
if case .subscribed(_, let willAutoRenew) = iapManager.state {
|
|
if willAutoRenew {
|
|
Text(String(localized: "subscription_status_active"))
|
|
.font(.caption)
|
|
.foregroundColor(.white)
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(Color.green)
|
|
.cornerRadius(4)
|
|
} else {
|
|
Text(String(localized: "subscription_status_expires"))
|
|
.font(.caption)
|
|
.foregroundColor(.white)
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(Color.orange)
|
|
.cornerRadius(4)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Not Subscribed View
|
|
|
|
private var notSubscribedView: some View {
|
|
VStack(spacing: 16) {
|
|
Text(String(localized: "purchase_view_title"))
|
|
.font(.title3)
|
|
.bold()
|
|
.foregroundColor(textColor)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
// Trial status
|
|
if iapManager.shouldShowTrialWarning {
|
|
trialStatusView
|
|
} else if iapManager.shouldShowPaywall {
|
|
Text(String(localized: "purchase_view_trial_expired"))
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Text(String(localized: "purchase_view_current_why_subscribe"))
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
|
|
// Subscribe button
|
|
Button {
|
|
showSubscriptionStore = true
|
|
} label: {
|
|
Text(String(localized: "purchase_view_subscribe_button"))
|
|
.font(.headline)
|
|
.foregroundColor(.white)
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
.background(Color.pink)
|
|
.cornerRadius(10)
|
|
}
|
|
|
|
// Restore purchases
|
|
Button {
|
|
Task {
|
|
await iapManager.restore()
|
|
}
|
|
} label: {
|
|
Text(String(localized: "purchase_view_restore"))
|
|
.font(.body)
|
|
.foregroundColor(.blue)
|
|
}
|
|
}
|
|
}
|
|
|
|
private var trialStatusView: some View {
|
|
HStack {
|
|
Image(systemName: "clock")
|
|
.foregroundColor(.orange)
|
|
|
|
if let expirationDate = iapManager.trialExpirationDate, expirationDate > Date() {
|
|
Text("\(Text(String(localized: "purchase_view_trial_expires_in")).foregroundColor(textColor)) \(Text(expirationDate, style: .relative).foregroundColor(.orange).bold())")
|
|
} else {
|
|
Text(String(localized: "purchase_view_trial_expired"))
|
|
.foregroundColor(.orange)
|
|
.bold()
|
|
}
|
|
}
|
|
.font(.body)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
PurchaseButtonView(iapManager: IAPManager())
|
|
}
|