-
Notifications
You must be signed in to change notification settings - Fork 868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[iOS] - Add Free Trial to Leo on iOS #26001
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,14 +32,20 @@ struct AIChatPaywallView: View { | |
private var selectedTierType: AIChatSubscriptionTier = .monthly | ||
|
||
@State | ||
private var availableTierTypes: [AIChatSubscriptionTier] = [.monthly] | ||
private var availableTierTypes: [AIChatSubscriptionTier] = [.monthly, .yearly] | ||
|
||
@ObservedObject | ||
private(set) var storeSDK = BraveStoreSDK.shared | ||
|
||
@State | ||
private var paymentStatus: AIChatPaymentStatus = .success | ||
|
||
@State | ||
private var isMonthlyIntroOfferAvailable: Bool = false | ||
|
||
@State | ||
private var isYearlyIntroOfferAvailable: Bool = false | ||
|
||
@State | ||
private var isShowingPurchaseAlert = false | ||
|
||
|
@@ -151,6 +157,9 @@ struct AIChatPaywallView: View { | |
.onDisappear { | ||
iapRestoreTimer?.cancel() | ||
} | ||
.task { | ||
await fetchIntroOfferStatus() | ||
} | ||
} | ||
|
||
private var tierSelection: some View { | ||
|
@@ -203,10 +212,19 @@ struct AIChatPaywallView: View { | |
.tint(Color.white) | ||
.padding() | ||
} else { | ||
Text(Strings.AIChat.paywallPurchaseActionTitle) | ||
.font(.body.weight(.semibold)) | ||
.foregroundColor(Color(.white)) | ||
.padding() | ||
if (selectedTierType == .monthly && isMonthlyIntroOfferAvailable) | ||
|| (selectedTierType == .yearly && isYearlyIntroOfferAvailable) | ||
{ | ||
Text(Strings.AIChat.paywallPurchaseActionIntroOfferTitle) | ||
.font(.body.weight(.semibold)) | ||
.foregroundColor(Color(.white)) | ||
.padding() | ||
} else { | ||
Text(Strings.AIChat.paywallPurchaseActionTitle) | ||
.font(.body.weight(.semibold)) | ||
.foregroundColor(Color(.white)) | ||
.padding() | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to create a conditional view here, just swap text instead: let isIntroOfferAvailable =
(selectedTierType == .monthly && isMonthlyIntroOfferAvailable) ||
(selectedTierType == .yearly && isYearlyIntroOfferAvailable)
Text(isIntroOfferAvailable ?
Strings.AIChat.paywallPurchaseActionIntroOfferTitle) :
Strings.AIChat.paywallPurchaseActionTitle)
.font(.body.weight(.semibold))
.foregroundColor(Color(.white))
.padding() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
} | ||
} | ||
.frame(maxWidth: .infinity) | ||
|
@@ -282,6 +300,20 @@ struct AIChatPaywallView: View { | |
isShowingPurchaseAlert = true | ||
} | ||
} | ||
|
||
private func fetchIntroOfferStatus() async { | ||
paymentStatus = .ongoing | ||
|
||
isMonthlyIntroOfferAvailable = await storeSDK.isIntroOfferAvailable( | ||
for: BraveStoreProduct.leoMonthly | ||
) | ||
|
||
isYearlyIntroOfferAvailable = await storeSDK.isIntroOfferAvailable( | ||
for: BraveStoreProduct.leoYearly | ||
) | ||
|
||
paymentStatus = .success | ||
} | ||
} | ||
|
||
private struct AIChatPremiumTierSelectionView: View { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -339,6 +339,22 @@ public class AppStoreSDK: ObservableObject { | |
(try? await subscription(for: product)?.subscription?.status) ?? [] | ||
} | ||
|
||
/// Retrieves a product's renewable subscription trial status | ||
/// - Parameter product: The product whose subscription trial status to retrieve | ||
/// - Returns: The renewable subscription's trial status | ||
@MainActor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are all these function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh you're right. I got used to applying |
||
public func isIntroOfferAvailable(for product: any AppStoreProduct) async -> Bool { | ||
guard let subscription = await subscription(for: product)?.subscription else { | ||
return false | ||
} | ||
|
||
if subscription.introductoryOffer == nil { | ||
return false | ||
} | ||
|
||
return await subscription.isEligibleForIntroOffer == true | ||
} | ||
|
||
/// Retrieves a product's renewable subscription renewal information | ||
/// - Parameter product: The product whose renewal to retrieve | ||
/// - Returns: The renewable subscription's renewal information | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment needs an update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.