PremiumFeaturesView.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. import SwiftUI
  2. struct PremiumFeaturesView: View {
  3. enum PlanOption: String {
  4. case perpetual
  5. case yearly
  6. }
  7. @Environment(\.dismiss) private var dismiss
  8. @State private var selectedPlan: PlanOption = .perpetual
  9. @State private var showingCheckout = false
  10. @State private var showingSuccessAlert = false
  11. private let featureRows: [(String, String)] = [
  12. ("Desktop Widgets", "Add frequently used apps as desktop widgets"),
  13. ("Custom web Apps", "Manage personalized applications"),
  14. ("Hotkey", "Quickly launch specific apps by setting shortcuts"),
  15. ("Status Bar Shortcuts", "Add app quick-access to status bar"),
  16. ("Desktop Shortcuts", "Create one-click app shortcuts on desktop"),
  17. ("Quick Search", "Search directly on Google without opening browser"),
  18. ("Ad Blocker", "Intelligently blocks ads and pop-ups for better browsing"),
  19. ("Auto Startup", "Launch automatically with system"),
  20. ("App Pin", "Keep app windows always on top"),
  21. ("Family Sharing", "One purchase for the whole family to share"),
  22. ]
  23. private let cardColors: [Color] = [
  24. .blue, .purple, .red, .green, .orange,
  25. .indigo, .pink, .cyan, .gray, .mint,
  26. ]
  27. var body: some View {
  28. ZStack {
  29. LinearGradient(
  30. colors: [Color.black.opacity(0.9), Color(red: 0.08, green: 0.1, blue: 0.14)],
  31. startPoint: .topLeading,
  32. endPoint: .bottomTrailing
  33. )
  34. .ignoresSafeArea()
  35. Rectangle()
  36. .fill(
  37. RadialGradient(
  38. colors: [Color.blue.opacity(0.25), .clear],
  39. center: .bottomTrailing,
  40. startRadius: 40,
  41. endRadius: 380
  42. )
  43. )
  44. .ignoresSafeArea()
  45. ScrollView {
  46. VStack(alignment: .leading, spacing: 14) {
  47. HStack {
  48. Button(action: { dismiss() }) {
  49. Image(systemName: "xmark")
  50. .font(.system(size: 13, weight: .semibold))
  51. .foregroundStyle(.white.opacity(0.88))
  52. }
  53. .buttonStyle(.plain)
  54. Spacer()
  55. Text("Restore")
  56. .font(.system(size: 11, weight: .semibold))
  57. .foregroundStyle(.white.opacity(0.9))
  58. .padding(.horizontal, 8)
  59. .padding(.vertical, 3)
  60. .background(
  61. RoundedRectangle(cornerRadius: 7, style: .continuous)
  62. .fill(Color.white.opacity(0.16))
  63. )
  64. }
  65. Text("Upgrade to unlock all\nPremium Features")
  66. .font(.system(size: 20, weight: .bold))
  67. .foregroundStyle(.white)
  68. .lineSpacing(1)
  69. LazyVGrid(
  70. columns: [GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10)],
  71. spacing: 10
  72. ) {
  73. ForEach(Array(featureRows.enumerated()), id: \.offset) { index, feature in
  74. featureCard(title: feature.0, subtitle: feature.1, color: cardColors[index % cardColors.count])
  75. }
  76. }
  77. priceCard(
  78. title: "Perpetual",
  79. price: "Rs 9,900.00",
  80. subtitle: "Own the software for lifetime",
  81. badgeText: "One-time payment",
  82. isSelected: selectedPlan == .perpetual
  83. )
  84. .onTapGesture {
  85. selectedPlan = .perpetual
  86. }
  87. priceCard(
  88. title: "Yearly",
  89. price: "Rs 7,900.00/year",
  90. subtitle: "Rs 658.33/mo",
  91. badgeText: nil,
  92. isSelected: selectedPlan == .yearly
  93. )
  94. .onTapGesture {
  95. selectedPlan = .yearly
  96. }
  97. Button(action: { showingCheckout = true }) {
  98. Text("Continue")
  99. .font(.system(size: 16, weight: .bold))
  100. .foregroundStyle(.white)
  101. .frame(maxWidth: .infinity)
  102. .padding(.vertical, 12)
  103. .background(
  104. RoundedRectangle(cornerRadius: 16, style: .continuous)
  105. .fill(Color.blue.opacity(0.9))
  106. )
  107. }
  108. .buttonStyle(.plain)
  109. .padding(.top, 6)
  110. HStack {
  111. Spacer()
  112. Text("Privacy Policy")
  113. .font(.system(size: 11, weight: .medium))
  114. .foregroundStyle(.white.opacity(0.7))
  115. Spacer()
  116. Text("Terms of Service")
  117. .font(.system(size: 11, weight: .medium))
  118. .foregroundStyle(.white.opacity(0.7))
  119. Spacer()
  120. }
  121. .padding(.top, 2)
  122. }
  123. .padding(16)
  124. }
  125. }
  126. .sheet(isPresented: $showingCheckout) {
  127. PaymentCheckoutView(
  128. selectedPlan: selectedPlan,
  129. onCancel: { showingCheckout = false },
  130. onConfirm: {
  131. showingCheckout = false
  132. showingSuccessAlert = true
  133. }
  134. )
  135. .frame(minWidth: 500, minHeight: 520)
  136. }
  137. .alert("Payment Successful", isPresented: $showingSuccessAlert) {
  138. Button("Done") { dismiss() }
  139. } message: {
  140. Text("Your \(selectedPlan == .perpetual ? "Perpetual" : "Yearly") plan has been activated.")
  141. }
  142. }
  143. private func featureCard(title: String, subtitle: String, color: Color) -> some View {
  144. HStack(alignment: .top, spacing: 10) {
  145. Circle()
  146. .fill(color.opacity(0.95))
  147. .frame(width: 28, height: 28)
  148. .overlay(
  149. Image(systemName: "sparkles")
  150. .font(.system(size: 11, weight: .semibold))
  151. .foregroundStyle(.white)
  152. )
  153. VStack(alignment: .leading, spacing: 3) {
  154. Text(title)
  155. .font(.system(size: 11.5, weight: .bold))
  156. .foregroundStyle(.white)
  157. .lineLimit(1)
  158. Text(subtitle)
  159. .font(.system(size: 9.5, weight: .medium))
  160. .foregroundStyle(.white.opacity(0.85))
  161. .lineLimit(2)
  162. }
  163. Spacer(minLength: 0)
  164. }
  165. .padding(10)
  166. .frame(maxWidth: .infinity, minHeight: 66, alignment: .leading)
  167. .background(
  168. RoundedRectangle(cornerRadius: 12, style: .continuous)
  169. .fill(Color.white.opacity(0.14))
  170. )
  171. }
  172. private func priceCard(title: String, price: String, subtitle: String, badgeText: String?, isSelected: Bool) -> some View {
  173. HStack(alignment: .center) {
  174. VStack(alignment: .leading, spacing: 5) {
  175. Text(title)
  176. .font(.system(size: 12, weight: .bold))
  177. .foregroundStyle(.white)
  178. Text(subtitle)
  179. .font(.system(size: 7.5, weight: .medium))
  180. .foregroundStyle(.white.opacity(0.72))
  181. }
  182. Spacer()
  183. VStack(alignment: .trailing, spacing: 7) {
  184. Text(price)
  185. .font(.system(size: 10, weight: .bold))
  186. .foregroundStyle(.white)
  187. .minimumScaleFactor(0.7)
  188. .lineLimit(1)
  189. if let badgeText {
  190. Text(badgeText)
  191. .font(.system(size: 7.5, weight: .bold))
  192. .foregroundStyle(.white)
  193. .padding(.horizontal, 8)
  194. .padding(.vertical, 4)
  195. .background(
  196. RoundedRectangle(cornerRadius: 7, style: .continuous)
  197. .fill(Color.blue.opacity(0.95))
  198. )
  199. }
  200. }
  201. }
  202. .padding(10)
  203. .background(
  204. RoundedRectangle(cornerRadius: 14, style: .continuous)
  205. .stroke(isSelected ? Color.blue.opacity(0.95) : Color.white.opacity(0.4), lineWidth: isSelected ? 2 : 1.5)
  206. .background(
  207. RoundedRectangle(cornerRadius: 14, style: .continuous)
  208. .fill(isSelected ? Color.blue.opacity(0.12) : Color.white.opacity(0.05))
  209. )
  210. )
  211. .contentShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
  212. }
  213. }
  214. private struct PaymentCheckoutView: View {
  215. let selectedPlan: PremiumFeaturesView.PlanOption
  216. let onCancel: () -> Void
  217. let onConfirm: () -> Void
  218. @State private var cardholderName = ""
  219. @State private var cardNumber = ""
  220. @State private var expiry = ""
  221. @State private var cvv = ""
  222. @State private var saveCard = true
  223. private var amountText: String {
  224. switch selectedPlan {
  225. case .perpetual:
  226. return "Rs 9,900.00"
  227. case .yearly:
  228. return "Rs 7,900.00/year"
  229. }
  230. }
  231. private var canSubmit: Bool {
  232. !cardholderName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
  233. cardNumber.filter(\.isNumber).count >= 12 &&
  234. expiry.count >= 4 &&
  235. cvv.filter(\.isNumber).count >= 3
  236. }
  237. var body: some View {
  238. ZStack {
  239. LinearGradient(
  240. colors: [Color.black.opacity(0.95), Color(red: 0.09, green: 0.11, blue: 0.16)],
  241. startPoint: .topLeading,
  242. endPoint: .bottomTrailing
  243. )
  244. .ignoresSafeArea()
  245. VStack(alignment: .leading, spacing: 16) {
  246. HStack {
  247. Text("Add Credit Card")
  248. .font(.system(size: 22, weight: .bold))
  249. .foregroundStyle(.white)
  250. Spacer()
  251. Button("Cancel", action: onCancel)
  252. .buttonStyle(.plain)
  253. .foregroundStyle(.white.opacity(0.8))
  254. }
  255. Text("Selected plan: \(selectedPlan == .perpetual ? "Perpetual" : "Yearly")")
  256. .font(.system(size: 13, weight: .medium))
  257. .foregroundStyle(.white.opacity(0.8))
  258. VStack(spacing: 12) {
  259. checkoutField(title: "Cardholder Name", text: $cardholderName, placeholder: "Name on card")
  260. checkoutField(title: "Card Number", text: $cardNumber, placeholder: "1234 5678 9012 3456")
  261. HStack(spacing: 12) {
  262. checkoutField(title: "Expiry (MM/YY)", text: $expiry, placeholder: "08/28")
  263. checkoutField(title: "CVV", text: $cvv, placeholder: "123")
  264. }
  265. }
  266. Toggle("Save card for future purchases", isOn: $saveCard)
  267. .toggleStyle(.checkbox)
  268. .foregroundStyle(.white.opacity(0.88))
  269. HStack {
  270. Text("Total")
  271. .foregroundStyle(.white.opacity(0.85))
  272. Spacer()
  273. Text(amountText)
  274. .font(.system(size: 15, weight: .bold))
  275. .foregroundStyle(.white)
  276. }
  277. .padding(12)
  278. .background(
  279. RoundedRectangle(cornerRadius: 10, style: .continuous)
  280. .fill(Color.white.opacity(0.08))
  281. )
  282. Button(action: onConfirm) {
  283. Text("Pay \(amountText)")
  284. .font(.system(size: 15, weight: .bold))
  285. .foregroundStyle(.white)
  286. .frame(maxWidth: .infinity)
  287. .padding(.vertical, 12)
  288. .background(
  289. RoundedRectangle(cornerRadius: 12, style: .continuous)
  290. .fill(canSubmit ? Color.blue.opacity(0.95) : Color.gray.opacity(0.6))
  291. )
  292. }
  293. .buttonStyle(.plain)
  294. .disabled(!canSubmit)
  295. Spacer(minLength: 0)
  296. }
  297. .padding(20)
  298. }
  299. }
  300. private func checkoutField(title: String, text: Binding<String>, placeholder: String) -> some View {
  301. VStack(alignment: .leading, spacing: 6) {
  302. Text(title)
  303. .font(.system(size: 12, weight: .semibold))
  304. .foregroundStyle(.white.opacity(0.85))
  305. TextField(placeholder, text: text)
  306. .textFieldStyle(.plain)
  307. .foregroundStyle(.white)
  308. .padding(.horizontal, 10)
  309. .padding(.vertical, 9)
  310. .background(
  311. RoundedRectangle(cornerRadius: 10, style: .continuous)
  312. .fill(Color.white.opacity(0.1))
  313. )
  314. .overlay(
  315. RoundedRectangle(cornerRadius: 10, style: .continuous)
  316. .stroke(Color.white.opacity(0.13), lineWidth: 1)
  317. )
  318. }
  319. }
  320. }
  321. #Preview {
  322. PremiumFeaturesView()
  323. .frame(width: 560, height: 690)
  324. }