From e012acc28174d8d34fc2cbe8e9e64dcceaf4dc2e Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Fri, 21 Jul 2023 06:46:12 +0200 Subject: [PATCH] `Paywalls`: extracted `IntroEligibilityStateView` (#2837) Small refactor, starting to take views out of `Example1Template` so they're usable by other templates. This also removes some of the duplicate code. --- RevenueCatUI/Templates/Example1Template.swift | 75 ++++-------------- .../Views/IntroEligibilityStateView.swift | 76 +++++++++++++++++++ 2 files changed, 90 insertions(+), 61 deletions(-) create mode 100644 RevenueCatUI/Views/IntroEligibilityStateView.swift diff --git a/RevenueCatUI/Templates/Example1Template.swift b/RevenueCatUI/Templates/Example1Template.swift index 68378228b7..14bd6d38dd 100644 --- a/RevenueCatUI/Templates/Example1Template.swift +++ b/RevenueCatUI/Templates/Example1Template.swift @@ -82,7 +82,14 @@ private struct Example1TemplateContent: View { Spacer() } - self.offerDetails + IntroEligibilityStateView( + textWithNoIntroOffer: self.localization.offerDetails, + textWithIntroOffer: self.localization.offerDetailsWithIntroOffer, + introEligibility: self.introEligibility + ) + .font(self.configuration.mode.offerDetailsFont) + .foregroundColor(self.configuration.colors.text1Color) + self.button .padding(.horizontal) } @@ -138,49 +145,6 @@ private struct Example1TemplateContent: View { } } - private var offerDetails: some View { - let detailsWithIntroOffer = self.localization.offerDetailsWithIntroOffer - - func text() -> String { - if let detailsWithIntroOffer = detailsWithIntroOffer, self.isEligibleForIntro { - return detailsWithIntroOffer - } else { - return self.localization.offerDetails - } - } - - return Text(verbatim: text()) - // Hide until we've determined intro eligibility - // only if there is a custom intro offer string. - .withPendingData(self.needsToWaitForIntroEligibility(detailsWithIntroOffer != nil)) - .font(self.configuration.mode.offerDetailsFont) - } - - private var ctaText: some View { - let ctaWithIntroOffer = self.localization.callToActionWithIntroOffer - - func text() -> String { - if let ctaWithIntroOffer = ctaWithIntroOffer, self.isEligibleForIntro { - return ctaWithIntroOffer - } else { - return self.localization.callToAction - } - } - - return Text(verbatim: text()) - // Hide until we've determined intro eligibility - // only if there is a custom intro offer string. - .withPendingData(self.needsToWaitForIntroEligibility(ctaWithIntroOffer != nil)) - } - - private var isEligibleForIntro: Bool { - return self.introEligibility?.isEligible != false - } - - private func needsToWaitForIntroEligibility(_ hasCustomString: Bool) -> Bool { - return self.introEligibility == nil && hasCustomString && self.isEligibleForIntro - } - @ViewBuilder private var button: some View { let package = self.configuration.packages.single.content @@ -192,8 +156,13 @@ private struct Example1TemplateContent: View { self.dismiss() } } label: { - self.ctaText + IntroEligibilityStateView( + textWithNoIntroOffer: self.localization.callToAction, + textWithIntroOffer: self.localization.callToActionWithIntroOffer, + introEligibility: self.introEligibility + ) .foregroundColor(self.configuration.colors.callToActionForegroundColor) + .tint(self.configuration.colors.callToActionForegroundColor) .frame( maxWidth: self.configuration.mode.fullWidthButton ? .infinity @@ -289,19 +258,3 @@ private extension PaywallViewMode { } } - -@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) -private extension View { - - func withPendingData(_ pending: Bool) -> some View { - self - .hidden(if: pending) - .overlay { - if pending { - ProgressView() - } - } - .transition(.opacity.animation(Constants.defaultAnimation)) - } - -} diff --git a/RevenueCatUI/Views/IntroEligibilityStateView.swift b/RevenueCatUI/Views/IntroEligibilityStateView.swift new file mode 100644 index 0000000000..8c9b9d89ee --- /dev/null +++ b/RevenueCatUI/Views/IntroEligibilityStateView.swift @@ -0,0 +1,76 @@ +// +// IntroEligibilityStateView.swift +// +// +// Created by Nacho Soto on 7/18/23. +// + +import RevenueCat +import SwiftUI + +/// A view that can process intro eligibility and display different data based on the result. +@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) +struct IntroEligibilityStateView: View { + + var textWithNoIntroOffer: String + var textWithIntroOffer: String? + var introEligibility: IntroEligibilityStatus? + + init( + textWithNoIntroOffer: String, + textWithIntroOffer: String?, + introEligibility: IntroEligibilityStatus? + ) { + self.textWithNoIntroOffer = textWithNoIntroOffer + self.textWithIntroOffer = textWithIntroOffer + self.introEligibility = introEligibility + } + + var body: some View { + Text(self.text) + // Hide until we've determined intro eligibility + // only if there is a custom intro text. + .withPendingData(self.needsToWaitForIntroEligibility) + } + + private var text: String { + if let textWithIntroOffer = self.textWithIntroOffer, self.isEligibleForIntro { + return textWithIntroOffer + } else { + return self.textWithNoIntroOffer + } + } + +} + +// MARK: - Extensions + +@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) +private extension IntroEligibilityStateView { + + var isEligibleForIntro: Bool { + return self.introEligibility?.isEligible != false + } + + var needsToWaitForIntroEligibility: Bool { + return self.introEligibility == nil && self.textWithIntroOffer != nil + } + +} + +@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) +private extension View { + + func withPendingData(_ pending: Bool) -> some View { + self + .hidden(if: pending) + .overlay { + if pending { + ProgressView() + .progressViewStyle(.circular) + } + } + .transition(.opacity.animation(Constants.defaultAnimation)) + } + +}