// // FeelsSubscriptionStoreView.swift // Feels // // Premium subscription experience with multiple theme options. // import SwiftUI import StoreKit struct FeelsSubscriptionStoreView: View { @Environment(\.dismiss) private var dismiss @EnvironmentObject var iapManager: IAPManager var style: PaywallStyle = .celestial var body: some View { SubscriptionStoreView(groupID: IAPManager.subscriptionGroupID) { marketingContent } .subscriptionStoreControlStyle(.prominentPicker) .storeButton(.visible, for: .restorePurchases) .subscriptionStoreButtonLabel(.multiline) .tint(tintColor) .onInAppPurchaseCompletion { _, result in if case .success(.success(_)) = result { dismiss() } } } @ViewBuilder private var marketingContent: some View { switch style { case .celestial: CelestialMarketingContent() case .garden: GardenMarketingContent() case .neon: NeonMarketingContent() case .minimal: MinimalMarketingContent() } } private var tintColor: Color { switch style { case .celestial: return Color(red: 1.0, green: 0.4, blue: 0.5) case .garden: return Color(red: 0.4, green: 0.75, blue: 0.45) case .neon: return Color(red: 0.0, green: 1.0, blue: 0.8) case .minimal: return Color(red: 0.95, green: 0.6, blue: 0.5) } } } // MARK: - 1. Celestial Theme (Aurora & Floating Orbs) struct CelestialMarketingContent: View { @State private var animateGradient = false @State private var animateOrbs = false @State private var showContent = false var body: some View { ZStack { CelestialBackground(animate: $animateGradient) VStack(spacing: 0) { EmotionOrbsView(animate: $animateOrbs) .frame(height: 140) .padding(.top, 20) VStack(spacing: 16) { Text("Understand\nYourself Deeper") .font(.system(size: 34, weight: .bold, design: .serif)) .multilineTextAlignment(.center) .foregroundStyle( LinearGradient( colors: [.white, .white.opacity(0.85)], startPoint: .top, endPoint: .bottom ) ) .shadow(color: .black.opacity(0.3), radius: 10, x: 0, y: 5) Text("Your emotions tell a story.\nPremium helps you read it.") .font(.system(size: 16, weight: .medium, design: .rounded)) .multilineTextAlignment(.center) .foregroundColor(.white.opacity(0.7)) .lineSpacing(4) } .padding(.horizontal, 24) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 20) FeatureCardsGrid(style: .celestial) .padding(.top, 32) .padding(.horizontal, 28) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 30) SocialProofBadge(style: .celestial) .padding(.top, 24) .opacity(showContent ? 1 : 0) Spacer().frame(height: 20) } } .onAppear { withAnimation(.easeInOut(duration: 3).repeatForever(autoreverses: true)) { animateGradient = true } withAnimation(.easeInOut(duration: 4).repeatForever(autoreverses: true)) { animateOrbs = true } withAnimation(.easeOut(duration: 0.8).delay(0.2)) { showContent = true } } } } // MARK: - 2. Garden Theme (Organic Growth & Blooming) struct GardenMarketingContent: View { @State private var bloomPhase = false @State private var showContent = false @State private var swayPhase = false var body: some View { ZStack { GardenBackground(bloom: $bloomPhase, sway: $swayPhase) VStack(spacing: 0) { // Blooming flower illustration BloomingFlowerView(bloom: $bloomPhase) .frame(height: 160) .padding(.top, 10) VStack(spacing: 16) { Text("Watch Yourself\nBloom") .font(.system(size: 34, weight: .bold, design: .serif)) .multilineTextAlignment(.center) .foregroundStyle( LinearGradient( colors: [ Color(red: 0.95, green: 0.95, blue: 0.9), Color(red: 0.85, green: 0.9, blue: 0.8) ], startPoint: .top, endPoint: .bottom ) ) .shadow(color: .black.opacity(0.2), radius: 8, x: 0, y: 4) Text("Every feeling is a seed.\nPremium helps you grow.") .font(.system(size: 16, weight: .medium, design: .rounded)) .multilineTextAlignment(.center) .foregroundColor(.white.opacity(0.75)) .lineSpacing(4) } .padding(.horizontal, 24) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 20) FeatureCardsGrid(style: .garden) .padding(.top, 28) .padding(.horizontal, 28) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 30) SocialProofBadge(style: .garden) .padding(.top, 24) .opacity(showContent ? 1 : 0) Spacer().frame(height: 20) } } .onAppear { withAnimation(.easeInOut(duration: 4).repeatForever(autoreverses: true)) { bloomPhase = true } withAnimation(.easeInOut(duration: 3).repeatForever(autoreverses: true)) { swayPhase = true } withAnimation(.easeOut(duration: 0.8).delay(0.3)) { showContent = true } } } } // MARK: - 3. Neon Theme (Synthwave & Energy) struct NeonMarketingContent: View { @State private var pulsePhase = false @State private var glowPhase = false @State private var showContent = false @State private var scanlineOffset: CGFloat = 0 var body: some View { ZStack { NeonBackground(pulse: $pulsePhase, glow: $glowPhase) // Scanlines overlay NeonScanlines(offset: $scanlineOffset) .opacity(0.03) VStack(spacing: 0) { // Glowing mood meter NeonMoodMeter(pulse: $pulsePhase) .frame(height: 140) .padding(.top, 20) VStack(spacing: 16) { Text("UNLOCK YOUR\nFULL SIGNAL") .font(.system(size: 32, weight: .black, design: .monospaced)) .multilineTextAlignment(.center) .foregroundStyle( LinearGradient( colors: [ Color(red: 0.0, green: 1.0, blue: 0.8), Color(red: 1.0, green: 0.0, blue: 0.8) ], startPoint: .leading, endPoint: .trailing ) ) .shadow(color: Color(red: 0.0, green: 1.0, blue: 0.8).opacity(0.5), radius: 20, x: 0, y: 0) Text("Amplify your emotional intelligence.\nGo premium. Go limitless.") .font(.system(size: 15, weight: .medium, design: .monospaced)) .multilineTextAlignment(.center) .foregroundColor(.white.opacity(0.7)) .lineSpacing(4) } .padding(.horizontal, 24) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 20) FeatureCardsGrid(style: .neon) .padding(.top, 28) .padding(.horizontal, 28) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 30) SocialProofBadge(style: .neon) .padding(.top, 24) .opacity(showContent ? 1 : 0) Spacer().frame(height: 20) } } .onAppear { withAnimation(.easeInOut(duration: 1.5).repeatForever(autoreverses: true)) { pulsePhase = true } withAnimation(.easeInOut(duration: 2).repeatForever(autoreverses: true)) { glowPhase = true } withAnimation(.linear(duration: 8).repeatForever(autoreverses: false)) { scanlineOffset = 400 } withAnimation(.easeOut(duration: 0.6).delay(0.2)) { showContent = true } } } } // MARK: - 4. Minimal Theme (Clean & Sophisticated) struct MinimalMarketingContent: View { @State private var showContent = false @State private var breathe = false var body: some View { ZStack { MinimalBackground() VStack(spacing: 0) { // Elegant breathing circle MinimalBreathingCircle(breathe: $breathe) .frame(height: 160) .padding(.top, 10) VStack(spacing: 20) { Text("Simply\nKnow Yourself") .font(.system(size: 36, weight: .light, design: .serif)) .italic() .multilineTextAlignment(.center) .foregroundColor(Color(red: 0.2, green: 0.15, blue: 0.1)) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 15) Text("Clarity through simplicity.\nPremium unlocks understanding.") .font(.system(size: 15, weight: .regular, design: .serif)) .multilineTextAlignment(.center) .foregroundColor(Color(red: 0.4, green: 0.35, blue: 0.3)) .lineSpacing(6) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 15) } .padding(.horizontal, 32) FeatureCardsGrid(style: .minimal) .padding(.top, 32) .padding(.horizontal, 32) .opacity(showContent ? 1 : 0) .offset(y: showContent ? 0 : 20) SocialProofBadge(style: .minimal) .padding(.top, 28) .opacity(showContent ? 1 : 0) Spacer().frame(height: 20) } } .onAppear { withAnimation(.easeInOut(duration: 4).repeatForever(autoreverses: true)) { breathe = true } withAnimation(.easeOut(duration: 1.0).delay(0.2)) { showContent = true } } } } // MARK: - Background Views struct CelestialBackground: View { @Binding var animate: Bool var body: some View { ZStack { LinearGradient( colors: [ Color(red: 0.05, green: 0.05, blue: 0.12), Color(red: 0.08, green: 0.06, blue: 0.15), Color(red: 0.04, green: 0.04, blue: 0.1) ], startPoint: .top, endPoint: .bottom ) EllipticalGradient( colors: [ Color(red: 1.0, green: 0.4, blue: 0.3).opacity(0.4), Color(red: 1.0, green: 0.6, blue: 0.4).opacity(0.2), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.8 ) .frame(width: 400, height: 300) .offset(x: animate ? 30 : -30, y: animate ? -50 : -80) .blur(radius: 60) EllipticalGradient( colors: [ Color(red: 0.4, green: 0.3, blue: 0.9).opacity(0.3), Color(red: 0.3, green: 0.5, blue: 0.8).opacity(0.15), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.7 ) .frame(width: 350, height: 250) .offset(x: animate ? -40 : 20, y: animate ? 100 : 60) .blur(radius: 50) EllipticalGradient( colors: [ Color(red: 0.9, green: 0.3, blue: 0.5).opacity(0.25), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.6 ) .frame(width: 300, height: 200) .offset(x: animate ? 60 : -20, y: animate ? -20 : 40) .blur(radius: 40) } .ignoresSafeArea() } } struct GardenBackground: View { @Binding var bloom: Bool @Binding var sway: Bool var body: some View { ZStack { // Deep forest gradient LinearGradient( colors: [ Color(red: 0.05, green: 0.12, blue: 0.08), Color(red: 0.08, green: 0.18, blue: 0.1), Color(red: 0.04, green: 0.1, blue: 0.06) ], startPoint: .top, endPoint: .bottom ) // Soft green glow EllipticalGradient( colors: [ Color(red: 0.3, green: 0.7, blue: 0.4).opacity(0.25), Color(red: 0.2, green: 0.5, blue: 0.3).opacity(0.1), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.8 ) .frame(width: 400, height: 400) .offset(y: bloom ? -20 : 20) .blur(radius: 80) // Warm accent EllipticalGradient( colors: [ Color(red: 1.0, green: 0.8, blue: 0.5).opacity(0.15), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.5 ) .frame(width: 300, height: 200) .offset(x: sway ? 40 : -40, y: -100) .blur(radius: 60) // Floating leaves particles ForEach(0..<8, id: \.self) { i in LeafParticle(index: i, sway: sway) } } .ignoresSafeArea() } } struct LeafParticle: View { let index: Int let sway: Bool var body: some View { Circle() .fill(Color(red: 0.4, green: 0.7, blue: 0.4).opacity(0.15)) .frame(width: CGFloat.random(in: 4...12), height: CGFloat.random(in: 4...12)) .offset( x: CGFloat(index * 40 - 140) + (sway ? 10 : -10), y: CGFloat(index * 30 - 100) ) .blur(radius: 2) } } struct NeonBackground: View { @Binding var pulse: Bool @Binding var glow: Bool var body: some View { ZStack { // Deep dark base Color(red: 0.02, green: 0.02, blue: 0.05) // Grid lines NeonGrid() .opacity(0.3) // Cyan glow EllipticalGradient( colors: [ Color(red: 0.0, green: 1.0, blue: 0.8).opacity(pulse ? 0.3 : 0.15), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.6 ) .frame(width: 400, height: 300) .offset(y: -80) .blur(radius: 60) // Magenta glow EllipticalGradient( colors: [ Color(red: 1.0, green: 0.0, blue: 0.8).opacity(glow ? 0.25 : 0.1), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.5 ) .frame(width: 350, height: 250) .offset(x: 50, y: 100) .blur(radius: 50) } .ignoresSafeArea() } } struct NeonGrid: View { var body: some View { Canvas { context, size in let gridSpacing: CGFloat = 30 let lineColor = Color(red: 0.0, green: 0.8, blue: 0.8).opacity(0.15) // Horizontal lines for y in stride(from: 0, to: size.height, by: gridSpacing) { var path = Path() path.move(to: CGPoint(x: 0, y: y)) path.addLine(to: CGPoint(x: size.width, y: y)) context.stroke(path, with: .color(lineColor), lineWidth: 0.5) } // Vertical lines for x in stride(from: 0, to: size.width, by: gridSpacing) { var path = Path() path.move(to: CGPoint(x: x, y: 0)) path.addLine(to: CGPoint(x: x, y: size.height)) context.stroke(path, with: .color(lineColor), lineWidth: 0.5) } } } } struct NeonScanlines: View { @Binding var offset: CGFloat var body: some View { GeometryReader { geo in ForEach(0..<20, id: \.self) { i in Rectangle() .fill(Color.white) .frame(height: 1) .offset(y: CGFloat(i * 20) + offset.truncatingRemainder(dividingBy: 400)) } } } } struct MinimalBackground: View { var body: some View { ZStack { // Warm cream gradient LinearGradient( colors: [ Color(red: 0.98, green: 0.96, blue: 0.92), Color(red: 0.95, green: 0.93, blue: 0.88), Color(red: 0.92, green: 0.90, blue: 0.85) ], startPoint: .top, endPoint: .bottom ) // Subtle warm accent EllipticalGradient( colors: [ Color(red: 0.95, green: 0.85, blue: 0.75).opacity(0.4), Color.clear ], center: .center, startRadiusFraction: 0, endRadiusFraction: 0.6 ) .frame(width: 500, height: 400) .offset(y: -50) .blur(radius: 100) } .ignoresSafeArea() } } // MARK: - Decorative Elements struct EmotionOrbsView: View { @Binding var animate: Bool private let emotions: [(color: Color, size: CGFloat, xOffset: CGFloat, yOffset: CGFloat)] = [ (Color(red: 1.0, green: 0.8, blue: 0.3), 56, -90, 20), (Color(red: 0.4, green: 0.8, blue: 0.6), 44, -30, -30), (Color(red: 1.0, green: 0.5, blue: 0.5), 52, 40, 10), (Color(red: 0.6, green: 0.5, blue: 0.9), 40, 95, -20), (Color(red: 0.3, green: 0.7, blue: 1.0), 36, 60, 50), ] var body: some View { ZStack { ForEach(0.. [Color] { let colorSets: [[[Color]]] = [ // Celestial [ [Color(red: 1.0, green: 0.6, blue: 0.4), Color(red: 1.0, green: 0.4, blue: 0.3)], [Color(red: 0.4, green: 0.7, blue: 0.9), Color(red: 0.3, green: 0.5, blue: 0.8)], [Color(red: 0.6, green: 0.8, blue: 0.5), Color(red: 0.4, green: 0.7, blue: 0.4)], [Color(red: 0.8, green: 0.5, blue: 0.9), Color(red: 0.6, green: 0.3, blue: 0.8)] ], // Garden [ [Color(red: 0.6, green: 0.8, blue: 0.5), Color(red: 0.4, green: 0.7, blue: 0.4)], [Color(red: 1.0, green: 0.7, blue: 0.7), Color(red: 0.9, green: 0.5, blue: 0.5)], [Color(red: 0.7, green: 0.6, blue: 0.9), Color(red: 0.5, green: 0.4, blue: 0.7)], [Color(red: 1.0, green: 0.85, blue: 0.5), Color(red: 0.9, green: 0.7, blue: 0.3)] ], // Neon [ [Color(red: 0.0, green: 1.0, blue: 0.8), Color(red: 0.0, green: 0.7, blue: 0.6)], [Color(red: 1.0, green: 0.0, blue: 0.8), Color(red: 0.7, green: 0.0, blue: 0.6)], [Color(red: 1.0, green: 1.0, blue: 0.0), Color(red: 0.8, green: 0.8, blue: 0.0)], [Color(red: 0.5, green: 0.0, blue: 1.0), Color(red: 0.3, green: 0.0, blue: 0.7)] ], // Minimal [ [Color(red: 0.85, green: 0.7, blue: 0.65), Color(red: 0.75, green: 0.6, blue: 0.55)], [Color(red: 0.7, green: 0.65, blue: 0.6), Color(red: 0.6, green: 0.55, blue: 0.5)], [Color(red: 0.8, green: 0.65, blue: 0.6), Color(red: 0.7, green: 0.55, blue: 0.5)], [Color(red: 0.75, green: 0.7, blue: 0.65), Color(red: 0.65, green: 0.6, blue: 0.55)] ] ] return colorSets[style.rawValue][index % 4] } } // MARK: - Preview #Preview("Celestial") { FeelsSubscriptionStoreView(style: .celestial) .environmentObject(IAPManager()) .preferredColorScheme(.dark) } #Preview("Garden") { FeelsSubscriptionStoreView(style: .garden) .environmentObject(IAPManager()) .preferredColorScheme(.dark) } #Preview("Neon") { FeelsSubscriptionStoreView(style: .neon) .environmentObject(IAPManager()) .preferredColorScheme(.dark) } #Preview("Minimal") { FeelsSubscriptionStoreView(style: .minimal) .environmentObject(IAPManager()) .preferredColorScheme(.light) }