// // SettingsTabView.swift // Reflect (iOS) // // Created by Trey Tartt on 12/13/25. // import SwiftUI import StoreKit enum SettingsTab: String, CaseIterable { case customize = "Customize" case settings = "Settings" } struct SettingsTabView: View { @State private var selectedTab: SettingsTab = .customize @State private var showWhyUpgrade = false @State private var showSubscriptionStore = false @EnvironmentObject var authManager: BiometricAuthManager @EnvironmentObject var iapManager: IAPManager @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system private var textColor: Color { theme.currentTheme.labelColor } var body: some View { VStack(spacing: 0) { // Header Text("Settings") .font(.title.weight(.bold)) .foregroundColor(textColor) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 16) .padding(.top, 8) .accessibilityIdentifier(AccessibilityID.Settings.header) // Upgrade Banner (only show if not subscribed) if !iapManager.isSubscribed && !iapManager.bypassSubscription { UpgradeBannerView( showWhyUpgrade: $showWhyUpgrade, showSubscriptionStore: $showSubscriptionStore, daysRemaining: iapManager.daysLeftInTrial ) .padding(.horizontal, 16) .padding(.top, 12) } // Segmented control Picker("", selection: $selectedTab) { ForEach(SettingsTab.allCases, id: \.self) { tab in Text(tab.rawValue) .accessibilityIdentifier(tab == .customize ? AccessibilityID.Settings.customizeTab : AccessibilityID.Settings.settingsTab) .tag(tab) } } .pickerStyle(.segmented) .accessibilityIdentifier(AccessibilityID.Settings.segmentedPicker) .padding(.horizontal, 16) .padding(.top, 12) .padding(.bottom, 16) // Content based on selected tab if selectedTab == .customize { CustomizeContentView() } else { SettingsContentView() .environmentObject(authManager) } } .background( theme.currentTheme.bg .edgesIgnoringSafeArea(.all) ) .sheet(isPresented: $showWhyUpgrade) { WhyUpgradeView() } .sheet(isPresented: $showSubscriptionStore) { ReflectSubscriptionStoreView(source: "settings_tab") .environmentObject(iapManager) } } } // MARK: - Upgrade Banner View struct UpgradeBannerView: View { @Binding var showWhyUpgrade: Bool @Binding var showSubscriptionStore: Bool let daysRemaining: Int @Environment(\.colorScheme) private var colorScheme @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system private var textColor: Color { theme.currentTheme.labelColor } var body: some View { VStack(spacing: 12) { // Countdown timer HStack(spacing: 6) { Image(systemName: "clock") .font(.subheadline.weight(.medium)) .foregroundColor(.orange) if daysRemaining > 0 { Text("\(Text("Trial expires in ").font(.subheadline.weight(.medium)).foregroundColor(textColor.opacity(0.8)))\(Text("\(daysRemaining) days").font(.subheadline.weight(.bold)).foregroundColor(.orange))") } else { Text("Trial expired") .font(.subheadline.weight(.medium)) .foregroundColor(.orange) } } // Buttons in HStack HStack(spacing: 12) { // Why Upgrade button Button { showWhyUpgrade = true } label: { Text("Why Upgrade?") .font(.subheadline.weight(.semibold)) .foregroundColor(.accentColor) .frame(maxWidth: .infinity) .padding(.vertical, 10) .background( RoundedRectangle(cornerRadius: 10) .stroke(Color.accentColor, lineWidth: 1.5) ) } .accessibilityIdentifier(AccessibilityID.Settings.whyUpgradeButton) // Subscribe button Button { showSubscriptionStore = true } label: { Text("Subscribe") .font(.subheadline.weight(.semibold)) .foregroundColor(.white) .frame(maxWidth: .infinity) .padding(.vertical, 10) .background( RoundedRectangle(cornerRadius: 10) .fill(Color.pink) ) } .accessibilityIdentifier(AccessibilityID.Settings.subscribeButton) } } .padding(14) .background( RoundedRectangle(cornerRadius: 14) .fill(colorScheme == .dark ? Color(.systemGray6) : Color(.systemGray6).opacity(0.5)) ) .accessibilityElement(children: .contain) .accessibilityIdentifier(AccessibilityID.Settings.upgradeBanner) } } // MARK: - Why Upgrade View struct WhyUpgradeView: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme var body: some View { NavigationView { ScrollView { VStack(spacing: 24) { // Header VStack(spacing: 12) { Image(systemName: "star.fill") .font(.largeTitle) .foregroundStyle( LinearGradient( colors: [.orange, .pink], startPoint: .topLeading, endPoint: .bottomTrailing ) ) Text("Unlock Premium") .font(.title.weight(.bold)) Text("Get the most out of your mood tracking journey") .font(.body) .foregroundColor(.secondary) .multilineTextAlignment(.center) } .padding(.top, 20) // Benefits list VStack(spacing: 16) { PremiumBenefitRow( icon: "calendar", iconColor: .blue, title: "Month View", description: "See your mood patterns across entire months at a glance" ) PremiumBenefitRow( icon: "chart.bar.fill", iconColor: .green, title: "Year View", description: "Track long-term trends and see how your mood evolves over time" ) PremiumBenefitRow( icon: "lightbulb.fill", iconColor: .yellow, title: "AI Insights", description: "Get personalized insights and patterns discovered by AI" ) PremiumBenefitRow( icon: "note.text", iconColor: .purple, title: "Journal Notes", description: "Add notes and context to your mood entries" ) PremiumBenefitRow( icon: "photo.fill", iconColor: .pink, title: "Photo Attachments", description: "Capture moments with photos attached to entries" ) PremiumBenefitRow( icon: "heart.fill", iconColor: .red, title: "Health Integration", description: "Correlate mood with steps, sleep, and exercise data" ) PremiumBenefitRow( icon: "square.and.arrow.up", iconColor: .orange, title: "Export Data", description: "Export your data as CSV or beautiful PDF reports" ) PremiumBenefitRow( icon: "faceid", iconColor: .gray, title: "Privacy Lock", description: "Protect your data with Face ID or Touch ID" ) } .padding(.horizontal, 20) } .padding(.bottom, 40) } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Done") { dismiss() } } } } } } // MARK: - Premium Benefit Row struct PremiumBenefitRow: View { let icon: String let iconColor: Color let title: String let description: String @Environment(\.colorScheme) private var colorScheme var body: some View { HStack(alignment: .top, spacing: 14) { Image(systemName: icon) .font(.title3) .foregroundColor(iconColor) .frame(width: 44, height: 44) .background( RoundedRectangle(cornerRadius: 10) .fill(iconColor.opacity(0.15)) ) VStack(alignment: .leading, spacing: 4) { Text(title) .font(.body.weight(.semibold)) Text(description) .font(.subheadline) .foregroundColor(.secondary) } Spacer() } .padding(14) .background( RoundedRectangle(cornerRadius: 12) .fill(colorScheme == .dark ? Color(.systemGray6) : .white) ) } } struct SettingsTabView_Previews: PreviewProvider { static var previews: some View { SettingsTabView() .environmentObject(BiometricAuthManager()) .environmentObject(IAPManager()) } }