Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Gymly/Settings/FloatingClouds.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,35 @@ struct CloudsTheme {
bottomTrailing: Color(red: 0.3, green: 0.7, blue: 1.0, opacity: 0.75)
)
}

static func premium(_ scheme: ColorScheme) -> CloudsTheme {
CloudsTheme(
// Deep indigo/purple-black background for sophisticated luxury
background: scheme == .dark
? Color(red: 0.05, green: 0.03, blue: 0.10) // Deep indigo black
: Color(red: 0.08, green: 0.06, blue: 0.12), // Dark purple charcoal

// Royal purple shimmer - top left
topLeading: scheme == .dark
? Color(red: 0.45, green: 0.20, blue: 0.75, opacity: 0.80) // Royal purple
: Color(red: 0.50, green: 0.25, blue: 0.70, opacity: 0.70),

// Deep violet glow - top right
topTrailing: scheme == .dark
? Color(red: 0.60, green: 0.30, blue: 0.85, opacity: 0.70) // Deep violet
: Color(red: 0.55, green: 0.30, blue: 0.75, opacity: 0.60),

// Rich plum warmth - bottom left
bottomLeading: scheme == .dark
? Color(red: 0.35, green: 0.15, blue: 0.55, opacity: 0.65) // Rich plum
: Color(red: 0.40, green: 0.20, blue: 0.55, opacity: 0.55),

// Lavender/light purple - bottom right
bottomTrailing: scheme == .dark
? Color(red: 0.65, green: 0.45, blue: 0.90, opacity: 0.75) // Lavender purple
: Color(red: 0.60, green: 0.40, blue: 0.80, opacity: 0.65)
)
}
}

class CloudProvider: ObservableObject {
Expand Down
7 changes: 1 addition & 6 deletions Gymly/Settings/NewSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ struct NewSettingsView: View {

// Premium Section
Section("Premium") {
Button(action: {
// TODO: Show premium upgrade flow
}) {
NavigationLink(destination: PremiumSubscriptionView()) {
HStack {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
Expand All @@ -93,9 +91,6 @@ struct NewSettingsView: View {
.font(.caption)
}
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.secondary)
.font(.caption)
}
}
.frame(width: 300)
Expand Down
297 changes: 297 additions & 0 deletions Gymly/Settings/PremiumSubscriptionView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
//
// PremiumSubscriptionView.swift
// Gymly
//
// Created by Sebastián Kučera on 20.10.2025.
//

import SwiftUI

struct PremiumSubscriptionView: View {
@Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) private var scheme
@State private var selectedPlan: SubscriptionPlan = .monthly

enum SubscriptionPlan {
case monthly
case yearly

var price: String {
switch self {
case .monthly: return "2.99€"
case .yearly: return "29.99€"
}
}

var period: String {
switch self {
case .monthly: return "month"
case .yearly: return "year"
}
}

var savings: String? {
switch self {
case .monthly: return nil
case .yearly: return "Save 17%"
}
}
}

var body: some View {
NavigationView {
ZStack {
FloatingClouds(theme: CloudsTheme.premium(scheme))
.ignoresSafeArea()

ScrollView {
VStack(spacing: 24) {
// Header
VStack(spacing: 12) {
Image(systemName: "star.fill")
.font(.system(size: 60))
.foregroundColor(.yellow)

Text("Gymly Pro")
.font(.largeTitle)
.fontWeight(.bold)

Text("Unlock your full potential")
.font(.subheadline)
.foregroundStyle(.secondary)
}
.padding(.top, 40)
.padding(.bottom, 20)

// Features List
VStack(spacing: 16) {
FeatureRow(
icon: "brain.head.profile",
title: "Smart Progression Coach",
description: "AI suggests optimal weight increases"
)

FeatureRow(
icon: "trophy.fill",
title: "Automatic PR Tracking",
description: "Never miss a personal record"
)

FeatureRow(
icon: "camera.fill",
title: "Progress Photo Timeline",
description: "Track your visual transformation"
)

FeatureRow(
icon: "chart.line.uptrend.xyaxis",
title: "Extended Analytics",
description: "Week, month, and all-time graphs"
)

FeatureRow(
icon: "apple.intelligence",
title: "AI Workout Summary",
description: "Weekly insights & recommendations"
)

FeatureRow(
icon: "book.fill",
title: "Workout Templates",
description: "Pre-built programs for your goals"
)

FeatureRow(
icon: "flame.fill",
title: "Advanced Streak Analytics",
description: "Motivation & predictions"
)

FeatureRow(
icon: "square.and.arrow.up",
title: "Export Your Data",
description: "CSV/PDF reports anytime"
)

FeatureRow(
icon: "calendar",
title: "Unlimited History",
description: "Lifetime workout access"
)

FeatureRow(
icon: "paintbrush.fill",
title: "Custom App Appearance",
description: "Choose your theme & colors"
)
}
.padding(.horizontal, 24)

// Pricing Plans
VStack(spacing: 12) {
Text("Choose Your Plan")
.font(.headline)
.padding(.top, 20)

// Monthly Plan
PlanCard(
plan: .monthly,
isSelected: selectedPlan == .monthly
) {
selectedPlan = .monthly
}

// Yearly Plan
PlanCard(
plan: .yearly,
isSelected: selectedPlan == .yearly
) {
selectedPlan = .yearly
}
}
.padding(.horizontal, 24)

// CTA Button
Button(action: {
// TODO: Implement subscription purchase
print("Starting subscription: \(selectedPlan)")
}) {
VStack(spacing: 8) {
Text("Start 7-Day Free Trial")
.font(.headline)
.foregroundColor(.white)

Text("Then \(selectedPlan.price)/\(selectedPlan.period)")
.font(.caption)
.foregroundColor(.white.opacity(0.8))
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.red)
.cornerRadius(12)
}
.padding(.horizontal, 24)
.padding(.top, 8)

// Fine Print
VStack(spacing: 8) {
Text("Cancel anytime. No commitment.")
.font(.caption)
.foregroundStyle(.secondary)

HStack(spacing: 16) {
Button("Terms of Service") {
// TODO: Open terms
}
.font(.caption2)
.foregroundStyle(.secondary)

Button("Privacy Policy") {
// TODO: Open privacy
}
.font(.caption2)
.foregroundStyle(.secondary)

Button("Restore Purchase") {
// TODO: Restore purchase
}
.font(.caption2)
.foregroundStyle(.secondary)
}
}
.padding(.bottom, 40)
}
}
}
.navigationBarTitleDisplayMode(.inline)
}
}
}

// MARK: - Feature Row Component
struct FeatureRow: View {
let icon: String
let title: String
let description: String

var body: some View {
HStack(spacing: 16) {
Image(systemName: icon)
.font(.system(size: 28))
.foregroundColor(.red)
.frame(width: 40)

VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.headline)
.foregroundColor(.primary)

Text(description)
.font(.caption)
.foregroundStyle(.secondary)
}

Spacer()
}
.padding(.vertical, 8)
}
}

// MARK: - Plan Card Component
struct PlanCard: View {
let plan: PremiumSubscriptionView.SubscriptionPlan
let isSelected: Bool
let action: () -> Void

var body: some View {
Button(action: action) {
HStack {
VStack(alignment: .leading, spacing: 4) {
HStack {
Text(plan.period.capitalized)
.font(.headline)
.foregroundColor(.primary)

if let savings = plan.savings {
Text(savings)
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.green)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.green.opacity(0.2))
.cornerRadius(6)
}
}

Text("\(plan.price)/\(plan.period)")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.primary)
}

Spacer()

if isSelected {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 24))
.foregroundColor(.red)
}
}
.padding()
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color.black.opacity(0.3))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(isSelected ? Color.red : Color.clear, lineWidth: 2)
)
)
}
.buttonStyle(.plain)
}
}

#Preview {
PremiumSubscriptionView()
}