wake-ios/wake/View/Subscribe/SubscribeView.swift
2025-08-20 13:16:17 +08:00

242 lines
6.9 KiB
Swift

//
// SubscribeView.swift
// wake
//
// Created by fairclip on 2025/8/19.
//
import SwiftUI
// MARK: -
enum SubscriptionPlan: String, CaseIterable {
case free = "Free"
case pioneer = "Pioneer"
var displayName: String {
return self.rawValue
}
var price: String {
switch self {
case .free:
return "Free"
case .pioneer:
return "1$/Mon"
}
}
var isPopular: Bool {
return self == .pioneer
}
}
// MARK: -
struct SubscriptionFeature {
let name: String
let freeValue: String
let proValue: String
}
struct SubscribeView: View {
@State private var selectedPlan: SubscriptionPlan? = .pioneer
@State private var isLoading = false
@Environment(\.presentationMode) var presentationMode
//
private let features = [
SubscriptionFeature(name: "Mystery Box Purchase:", freeValue: "3 /week", proValue: "Free"),
SubscriptionFeature(name: "Material Upload:", freeValue: "50 images and\n5 videos/day", proValue: "Unlimited"),
SubscriptionFeature(name: "Free Credits:", freeValue: "200 /day", proValue: "500 /day")
]
var body: some View {
NavigationView {
ScrollView {
VStack(spacing: 0) {
//
navigationHeader
//
currentSubscriptionCard
//
creditsSection
VStack {
//
subscriptionPlansSection
//
specialOfferBanner
}
.background(Theme.Colors.cardBackground)
.cornerRadius(Theme.CornerRadius.medium)
.padding(.horizontal, Theme.Spacing.lg)
.padding(.vertical, Theme.Spacing.lg)
//
featureComparisonTable
//
subscribeButton
//
legalLinks
Spacer(minLength: 100)
}
}
.background(Theme.Colors.background)
.navigationBarHidden(true)
}
}
// MARK: -
private var navigationHeader: some View {
NaviHeader(title: "Subscription") {
presentationMode.wrappedValue.dismiss()
}
}
// MARK: -
private var currentSubscriptionCard: some View {
SubscriptionStatusBar(
status: .pioneer(expiryDate: Date()) ,
onSubscribeTap: {
//
handleSubscribe()
}
)
.padding(.horizontal, Theme.Spacing.xl)
}
// MARK: -
private var creditsSection: some View {
VStack(spacing: 16) {
CreditsInfoCard(
totalCredits: 3290,
onInfoTap: {
//
},
onDetailTap: {
//
}
)
}
.padding(.horizontal, Theme.Spacing.xl)
.padding(.top, Theme.Spacing.xl)
}
// MARK: -
private var subscriptionPlansSection: some View {
PlanSelector(
selectedPlan: $selectedPlan,
onPlanSelected: { plan in
print("Selected plan: \(plan.displayName)")
}
)
.padding(.horizontal, Theme.Spacing.xl)
.padding(.top, Theme.Spacing.xl)
}
// MARK: -
private var specialOfferBanner: some View {
HStack(spacing: 0) {
Text("First")
.font(Typography.font(for: .footnote, family: .quicksandRegular))
.foregroundColor(Theme.Colors.textPrimary)
Text(" 100")
.font(Typography.font(for: .footnote, family: .quicksandBold))
.foregroundColor(Theme.Colors.textPrimary)
Text(" users get a special deal: justs")
.font(Typography.font(for: .footnote, family: .quicksandRegular))
.foregroundColor(Theme.Colors.textPrimary)
Text(" $1")
.font(Typography.font(for: .footnote, family: .quicksandBold))
.foregroundColor(Theme.Colors.textPrimary)
Text(" for your first month!")
.font(Typography.font(for: .footnote, family: .quicksandRegular))
.foregroundColor(Theme.Colors.textPrimary)
}
.multilineTextAlignment(.center)
.padding(.horizontal, Theme.Spacing.lg)
.padding(.top, Theme.Spacing.sm)
.padding(.bottom, Theme.Spacing.lg)
}
// MARK: -
private var featureComparisonTable: some View {
PlanCompare()
.padding(.horizontal, Theme.Spacing.lg)
}
// MARK: -
private var subscribeButton: some View {
VStack(spacing: 12) {
SubscribeButton(
title: "Subscribe",
isLoading: isLoading,
action: handleSubscribe
)
}
.padding(.horizontal, Theme.Spacing.xl)
.padding(.top, Theme.Spacing.lg)
}
// MARK: -
private var legalLinks: some View {
HStack(spacing: 8) {
Button(action: {
//
}) {
Text("Terms of Service")
.underline()
}
Text("|")
.foregroundColor(.secondary)
Button(action: {
//
}) {
Text("Privacy Policy")
.underline()
}
Text("|")
.foregroundColor(.secondary)
Button(action: {
//
}) {
Text("Restore Purchase")
.underline()
}
}
.font(Typography.font(for: .caption, family: .quicksandRegular))
.foregroundColor(.secondary)
.padding(.top, Theme.Spacing.sm)
}
// MARK: -
private func handleSubscribe() {
isLoading = true
//
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
isLoading = false
//
}
}
}
#Preview {
SubscribeView()
}