@@ -4,6 +4,9 @@ import Dependencies
44struct PurchaseSheet : View {
55 @Dependency ( \. purchaseManager) var purchaseManager
66 @Environment ( \. dismiss) private var dismiss
7+ @State private var isPurchasing = false
8+ @State private var showSuccess = false
9+ @State private var showSuccessModal = false
710
811 var body : some View {
912 ZStack ( alignment: . topLeading) {
@@ -49,7 +52,7 @@ struct PurchaseSheet: View {
4952 . multilineTextAlignment ( . center)
5053 . padding ( . horizontal)
5154 . padding ( . bottom, 12 )
52- Text ( " Unlock the full power of Longevity Master with these exclusive benefits : " )
55+ Text ( " Unlock the full power of Longevity Master with exclusive benefit : " )
5356 . font ( . body)
5457 . multilineTextAlignment ( . center)
5558 . foregroundColor ( . secondary)
@@ -61,36 +64,54 @@ struct PurchaseSheet: View {
6164 Text ( " No Ads: " )
6265 . fontWeight ( . semibold) + Text( " Enjoy a clean, ad-free habit tracking experience. " )
6366 }
64- HStack ( alignment: . top) {
65- Text ( " • " ) . font ( . title3) . fontWeight ( . bold)
66- Text ( " Unlimited Habits: " )
67- . fontWeight ( . semibold) + Text( " Create and track as many healthy habits as you want—no limits. " )
68- }
67+ // HStack(alignment: .top) {
68+ // Text("• ").font(.title3).fontWeight(.bold)
69+ // Text("Unlimited Habits: ")
70+ // .fontWeight(.semibold) + Text("Create and track as many healthy habits as you want—no limits.")
71+ // }
6972 }
7073 . font ( . body)
7174 . padding ( . horizontal)
7275 . padding ( . bottom, 24 )
7376 // Purchase button
74- if let product = purchaseManager. removeAdsProduct {
75- Button ( action: {
76- Task {
77- await purchaseManager. purchaseRemoveAds ( )
78- if purchaseManager. isRemoveAdsPurchased {
79- dismiss ( )
77+ if let product = purchaseManager. premiumUserProduct {
78+ if purchaseManager. isPremiumUserPurchased {
79+ Text ( " You are now Premium user! " )
80+ . font ( . title2)
81+ . fontWeight ( . bold)
82+ . multilineTextAlignment ( . center)
83+ . padding ( . horizontal)
84+ . padding ( . bottom, 12 )
85+ } else {
86+ Button ( action: {
87+ Task {
88+ isPurchasing = true
89+ if await purchaseManager. purchasePremiumUser ( ) {
90+ showSuccess = true
91+ showSuccessModal = true
92+ }
93+ isPurchasing = false
8094 }
81- }
82- } ) {
83- Text ( " \( product. displayPrice) - Upgrade to Premium " )
84- . font ( . headline)
85- . frame ( maxWidth: . infinity)
86- . padding ( )
87- . background ( Color . blue)
95+ } ) {
96+ HStack {
97+ if isPurchasing {
98+ ProgressView ( )
99+ . progressViewStyle ( CircularProgressViewStyle ( tint: . white) )
100+ . scaleEffect ( 0.8 )
101+ }
102+ Text ( showSuccess ? " Purchase Successful! " : " \( product. displayPrice) - Upgrade to Premium " )
103+ . font ( . subheadline)
104+ }
105+ . padding ( . vertical, 12 )
106+ . padding ( . horizontal, 20 )
107+ . background ( showSuccess ? Color . green : Color . blue)
88108 . foregroundColor ( . white)
89- . cornerRadius ( 22 )
109+ . cornerRadius ( 16 )
110+ }
111+ . padding ( . horizontal)
112+ . padding ( . bottom, 18 )
113+ . disabled ( isPurchasing)
90114 }
91- . padding ( . horizontal)
92- . padding ( . bottom, 18 )
93- . disabled ( purchaseManager. isRemoveAdsPurchased)
94115 } else {
95116 ProgressView ( )
96117 }
@@ -118,12 +139,110 @@ struct PurchaseSheet: View {
118139 Spacer ( )
119140 }
120141 }
142+ . sheet ( isPresented: $showSuccessModal, onDismiss: { showSuccess = false } ) {
143+ PremiumSuccessView ( onContinue: {
144+ showSuccessModal = false
145+ showSuccess = false
146+ dismiss ( )
147+ } )
148+ }
121149 . task {
122150 await purchaseManager. loadProducts ( )
123151 }
124152 }
125153}
126154
155+ struct ConfettiDot : Identifiable {
156+ let id = UUID ( )
157+ let x : CGFloat
158+ let y : CGFloat
159+ let color : Color
160+ let size : CGFloat
161+ }
162+
163+ struct PremiumSuccessView : View {
164+ var onContinue : ( ) -> Void
165+ @State private var animate = false
166+ private let confetti : [ ConfettiDot ] = ( 0 ..< 20 ) . map { _ in
167+ ConfettiDot (
168+ x: CGFloat . random ( in: 40 ... 340 ) ,
169+ y: CGFloat . random ( in: 40 ... 600 ) ,
170+ color: [ Color . yellow, Color . orange, Color . green, Color . blue, Color . pink, Color . purple] . randomElement ( ) !,
171+ size: CGFloat . random ( in: 8 ... 16 )
172+ )
173+ }
174+
175+ var body : some View {
176+ ZStack {
177+ Color . white. opacity ( 0.4 ) . ignoresSafeArea ( )
178+
179+ // Confetti
180+ ForEach ( confetti) { dot in
181+ Circle ( )
182+ . fill ( dot. color)
183+ . frame ( width: dot. size, height: dot. size)
184+ . position ( x: dot. x, y: animate ? dot. y : dot. y - 80 )
185+ . opacity ( 0.7 )
186+ . animation ( . easeOut( duration: 1.2 ) , value: animate)
187+ }
188+
189+ VStack ( spacing: 28 ) {
190+ ZStack {
191+ Circle ( )
192+ . fill ( LinearGradient ( gradient: Gradient ( colors: [ Color . yellow. opacity ( 0.5 ) , Color . orange. opacity ( 0.2 ) ] ) , startPoint: . top, endPoint: . bottom) )
193+ . frame ( width: 120 , height: 120 )
194+ . blur ( radius: 8 )
195+ Image ( systemName: " crown.fill " )
196+ . resizable ( )
197+ . scaledToFit ( )
198+ . frame ( width: 80 , height: 80 )
199+ . foregroundColor ( . yellow)
200+ . shadow ( color: . yellow, radius: 12 )
201+ }
202+ Text ( " Congratulations! " )
203+ . font ( . system( size: 28 , weight: . bold) )
204+ . foregroundColor ( . primary)
205+ Text ( " 💎 Thanks for being a Premium member! " )
206+ . font ( . title3)
207+ . foregroundColor ( . secondary)
208+ Divider ( )
209+ VStack ( alignment: . leading, spacing: 10 ) {
210+ HStack {
211+ Image ( systemName: " checkmark.seal.fill " ) . foregroundColor ( . green)
212+ Text ( " Ad-free experience " )
213+ }
214+ HStack {
215+ Image ( systemName: " star.fill " ) . foregroundColor ( . yellow)
216+ Text ( " Thank you for supporting Longevity Master! " )
217+ }
218+ }
219+ . font ( . body)
220+ . frame ( maxWidth: . infinity, alignment: . leading)
221+ Button ( action: onContinue) {
222+ Text ( " Continue " )
223+ . font ( . headline)
224+ . padding ( . vertical, 12 )
225+ . padding ( . horizontal, 40 )
226+ . background (
227+ LinearGradient ( gradient: Gradient ( colors: [ Color . blue, Color . purple] ) , startPoint: . leading, endPoint: . trailing)
228+ )
229+ . foregroundColor ( . white)
230+ . cornerRadius ( 16 )
231+ . shadow ( radius: 4 )
232+ }
233+ }
234+ . padding ( 36 )
235+ . background (
236+ RoundedRectangle ( cornerRadius: 28 )
237+ . fill ( LinearGradient ( gradient: Gradient ( colors: [ Color . white, Color . yellow. opacity ( 0.1 ) ] ) , startPoint: . top, endPoint: . bottom) )
238+ )
239+ . shadow ( radius: 24 )
240+ . padding ( . horizontal, 24 )
241+ . onAppear { animate = true }
242+ }
243+ }
244+ }
245+
127246#Preview {
128247 PurchaseSheet ( )
129248}
0 commit comments