|
|
@@ -14,6 +14,10 @@ struct PaywallView: View {
|
|
|
subscriptions.purchasingPlan != nil
|
|
|
}
|
|
|
|
|
|
+ private var isPremium: Bool {
|
|
|
+ subscriptions.hasPremiumAccess
|
|
|
+ }
|
|
|
+
|
|
|
var body: some View {
|
|
|
GeometryReader { proxy in
|
|
|
let horizontalInset = min(max(proxy.size.width * 0.03, 12), 34)
|
|
|
@@ -47,6 +51,10 @@ struct PaywallView: View {
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
|
|
.background(Color.white.ignoresSafeArea())
|
|
|
|
|
|
+ if isPremium {
|
|
|
+ closeButton
|
|
|
+ }
|
|
|
+
|
|
|
if subscriptions.isLoadingProducts && !subscriptions.hasAllProductsLoaded {
|
|
|
Color.white.opacity(0.65)
|
|
|
.ignoresSafeArea()
|
|
|
@@ -74,6 +82,13 @@ struct PaywallView: View {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private var closeButton: some View {
|
|
|
+ PaywallCloseButton(action: onClose)
|
|
|
+ .padding(.top, 12)
|
|
|
+ .padding(.trailing, 20)
|
|
|
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
|
|
|
+ }
|
|
|
+
|
|
|
private var titleSection: some View {
|
|
|
VStack(spacing: 4) {
|
|
|
HStack(spacing: 10) {
|
|
|
@@ -193,7 +208,10 @@ struct PaywallView: View {
|
|
|
.multilineTextAlignment(.center)
|
|
|
|
|
|
HStack(spacing: 0) {
|
|
|
- PaywallFooterLinkView(title: "Continue with free plan", action: onClose)
|
|
|
+ PaywallFooterLinkView(
|
|
|
+ title: isPremium ? "Manage Subscription" : "Continue with free plan",
|
|
|
+ action: isPremium ? subscriptions.openSubscriptionManagement : onClose
|
|
|
+ )
|
|
|
PaywallFooterLinkView(title: "Restore Purchases") {
|
|
|
Task {
|
|
|
await subscriptions.restorePurchases()
|
|
|
@@ -215,6 +233,36 @@ struct PaywallView: View {
|
|
|
|
|
|
}
|
|
|
|
|
|
+private struct PaywallCloseButton: View {
|
|
|
+ let action: () -> Void
|
|
|
+ @State private var isHovered = false
|
|
|
+
|
|
|
+ var body: some View {
|
|
|
+ Button(action: action) {
|
|
|
+ Image(systemName: "xmark")
|
|
|
+ .font(.system(size: 13, weight: .medium))
|
|
|
+ .foregroundStyle(isHovered ? AppTheme.toolbarForegroundHover : AppTheme.textSecondary)
|
|
|
+ .frame(width: 32, height: 32)
|
|
|
+ .background(isHovered ? AppTheme.toolbarBackgroundHover : Color.white)
|
|
|
+ .clipShape(RoundedRectangle(cornerRadius: 8))
|
|
|
+ .overlay(
|
|
|
+ RoundedRectangle(cornerRadius: 8)
|
|
|
+ .stroke(isHovered ? AppTheme.toolbarBorderHover : AppTheme.border, lineWidth: 1)
|
|
|
+ )
|
|
|
+ .shadow(
|
|
|
+ color: isHovered ? AppTheme.toolbarShadowHover : AppTheme.toolbarShadow,
|
|
|
+ radius: isHovered ? 4 : 2,
|
|
|
+ y: isHovered ? 2 : 1
|
|
|
+ )
|
|
|
+ .scaleEffect(isHovered ? 1.02 : 1.0)
|
|
|
+ .animation(.easeOut(duration: 0.15), value: isHovered)
|
|
|
+ }
|
|
|
+ .buttonStyle(.plain)
|
|
|
+ .onHover { isHovered = $0 }
|
|
|
+ .accessibilityLabel("Close")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
private struct PaywallFeatureBox: View {
|
|
|
let feature: PaywallFeature
|
|
|
let minHeight: CGFloat
|