diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj index 08153ac3e1..10bef04a4f 100644 --- a/RevenueCat.xcodeproj/project.pbxproj +++ b/RevenueCat.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 1E99F81F2AC5917F0023E26E /* StoreMessagesHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E99F81D2AC5917F0023E26E /* StoreMessagesHelperTests.swift */; }; 1ED4CA9F2CC25A5F0021AB8F /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED4CA9E2CC25A5F0021AB8F /* SafariView.swift */; }; 2C08B2E92CD40DBF0024857B /* ButtonComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C08B2E82CD40DBF0024857B /* ButtonComponentTests.swift */; }; + 2C08B3072CD55D660024857B /* FlexHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C08B3062CD55D590024857B /* FlexHStack.swift */; }; 2C0B98CD2797070B00C5874F /* PromotionalOffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0B98CC2797070B00C5874F /* PromotionalOffer.swift */; }; 2C2AEB0F2CA64E0E00A50F38 /* Template1Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AEB0E2CA64E0E00A50F38 /* Template1Preview.swift */; }; 2C2AEB3B2CA7209F00A50F38 /* PaywallPackageComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AEB3A2CA7209F00A50F38 /* PaywallPackageComponent.swift */; }; @@ -1178,6 +1179,7 @@ 1E99F81D2AC5917F0023E26E /* StoreMessagesHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreMessagesHelperTests.swift; sourceTree = ""; }; 1ED4CA9E2CC25A5F0021AB8F /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; }; 2C08B2E82CD40DBF0024857B /* ButtonComponentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonComponentTests.swift; sourceTree = ""; }; + 2C08B3062CD55D590024857B /* FlexHStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlexHStack.swift; sourceTree = ""; }; 2C0B98CC2797070B00C5874F /* PromotionalOffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromotionalOffer.swift; sourceTree = ""; }; 2C2AEB0E2CA64E0E00A50F38 /* Template1Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Template1Preview.swift; sourceTree = ""; }; 2C2AEB3A2CA7209F00A50F38 /* PaywallPackageComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallPackageComponent.swift; sourceTree = ""; }; @@ -4495,6 +4497,7 @@ 88B1BAEA2C813A3C001B7EE5 /* Stack */ = { isa = PBXGroup; children = ( + 2C08B3062CD55D590024857B /* FlexHStack.swift */, 88B1BAE82C813A3C001B7EE5 /* StackComponentView.swift */, 88B1BAE92C813A3C001B7EE5 /* StackComponentViewModel.swift */, ); @@ -6213,6 +6216,7 @@ 887A60C12C1D037000E1A461 /* DebugErrorView.swift in Sources */, 887A607C2C1D037000E1A461 /* ColorInformation+MultiScheme.swift in Sources */, 88B1BAFE2C813A3C001B7EE5 /* ImageComponentViewModel.swift in Sources */, + 2C08B3072CD55D660024857B /* FlexHStack.swift in Sources */, 35C200B12C39254100B9778B /* FeedbackSurveyView.swift in Sources */, 35F249CC2C493DCC0058993A /* CustomerCenterPurchasesType.swift in Sources */, 887A60672C1D037000E1A461 /* PaywallError.swift in Sources */, diff --git a/RevenueCatUI/Templates/Components/PaywallComponentTypeTransformers.swift b/RevenueCatUI/Templates/Components/PaywallComponentTypeTransformers.swift index 8521c5e363..cb7ed8e7e4 100644 --- a/RevenueCatUI/Templates/Components/PaywallComponentTypeTransformers.swift +++ b/RevenueCatUI/Templates/Components/PaywallComponentTypeTransformers.swift @@ -60,7 +60,7 @@ extension PaywallComponent.FontWeight { var fontWeight: Font.Weight { switch self { - case .ultraLight: + case .extraLight: return .ultraLight case .thin: return .thin @@ -74,7 +74,7 @@ extension PaywallComponent.FontWeight { return .semibold case .bold: return .bold - case .heavy: + case .extraBold: return .heavy case .black: return .black @@ -127,6 +127,17 @@ extension PaywallComponent.TwoDimensionAlignment { extension PaywallComponent.HorizontalAlignment { + var stackAlignment: SwiftUI.HorizontalAlignment { + switch self { + case .leading: + return .leading + case .center: + return .center + case .trailing: + return .trailing + } + } + var textAlignment: TextAlignment { switch self { case .leading: @@ -138,7 +149,7 @@ extension PaywallComponent.HorizontalAlignment { } } - var stackAlignment: SwiftUI.Alignment { + var frameAlignment: SwiftUI.Alignment { switch self { case .leading: return .leading diff --git a/RevenueCatUI/Templates/Components/Stack/FlexHStack.swift b/RevenueCatUI/Templates/Components/Stack/FlexHStack.swift new file mode 100644 index 0000000000..354467be3b --- /dev/null +++ b/RevenueCatUI/Templates/Components/Stack/FlexHStack.swift @@ -0,0 +1,96 @@ +// +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// FlexHStack.swift +// +// Created by Josh Holtz on 11/1/24. + +import SwiftUI + +#if PAYWALL_COMPONENTS + +enum JustifyContent { + case start, center, end, spaceBetween, spaceAround, spaceEvenly +} + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +struct FlexHStack: View { + let alignment: VerticalAlignment + let justifyContent: JustifyContent + let spacing: CGFloat? + let componentViewModels: [PaywallComponentViewModel] + + let onDismiss: () -> Void + + init( + alignment: VerticalAlignment, + spacing: CGFloat?, + justifyContent: JustifyContent, + componentViewModels: [PaywallComponentViewModel], + onDismiss: @escaping () -> Void + ) { + self.alignment = alignment + self.spacing = spacing + self.justifyContent = justifyContent + self.componentViewModels = componentViewModels + self.onDismiss = onDismiss + } + + var body: some View { + HStack(alignment: self.alignment, spacing: self.spacing) { + switch justifyContent { + case .start: + ForEach(0.. Dimension { - return .horizontal(.center) + return .horizontal(.center, .start) } public static func vertical() -> Dimension { @@ -70,6 +72,7 @@ public extension PaywallComponent { case type case alignment + case distribution } diff --git a/Sources/Paywalls/Components/Common/PaywallComponentBase.swift b/Sources/Paywalls/Components/Common/PaywallComponentBase.swift index fa6f3eb752..d3161c2d3d 100644 --- a/Sources/Paywalls/Components/Common/PaywallComponentBase.swift +++ b/Sources/Paywalls/Components/Common/PaywallComponentBase.swift @@ -32,8 +32,8 @@ public enum PaywallComponent: PaywallComponentBase { case linkButton = "link_button" case button case package - case purchaseButton - case stickyFooter + case purchaseButton = "purchase_button" + case stickyFooter = "sticky_footer" } diff --git a/Sources/Paywalls/Components/Common/PaywallComponentPropertyTypes.swift b/Sources/Paywalls/Components/Common/PaywallComponentPropertyTypes.swift index 3d0364172d..57f04f7d21 100644 --- a/Sources/Paywalls/Components/Common/PaywallComponentPropertyTypes.swift +++ b/Sources/Paywalls/Components/Common/PaywallComponentPropertyTypes.swift @@ -125,6 +125,17 @@ public extension PaywallComponent { } + enum FlexDistribution: String, Codable, Sendable, Hashable, Equatable { + + case start + case center + case end + case spaceBetween = "space_between" + case spaceAround = "space_around" + case spaceEvenly = "space_evenly" + + } + enum HorizontalAlignment: String, Codable, Sendable, Hashable, Equatable { case leading @@ -148,23 +159,23 @@ public extension PaywallComponent { case trailing case top case bottom - case topLeading - case topTrailing - case bottomLeading - case bottomTrailing + case topLeading = "top_leading" + case topTrailing = "top_trailing" + case bottomLeading = "bottom_leading" + case bottomTrailing = "bottom_trailing" } enum FontWeight: String, Codable, Sendable, Hashable, Equatable { - case ultraLight + case extraLight = "extra_light" case thin case light case regular case medium case semibold case bold - case heavy + case extraBold = "extra_bold" case black } diff --git a/Sources/Paywalls/Components/PaywallPackageComponent.swift b/Sources/Paywalls/Components/PaywallPackageComponent.swift index 821b8595e2..ed7c52eb60 100644 --- a/Sources/Paywalls/Components/PaywallPackageComponent.swift +++ b/Sources/Paywalls/Components/PaywallPackageComponent.swift @@ -35,6 +35,18 @@ public extension PaywallComponent { self.isSelectedByDefault = isSelectedByDefault self.stack = stack } + + } + +} + +extension PaywallComponent.PackageComponent { + + enum CodingKeys: String, CodingKey { + case type + case packageID = "packageId" + case isSelectedByDefault + case stack } } diff --git a/Sources/Paywalls/Components/PaywallTextComponent.swift b/Sources/Paywalls/Components/PaywallTextComponent.swift index 1c6cfad283..d3f2e1f787 100644 --- a/Sources/Paywalls/Components/PaywallTextComponent.swift +++ b/Sources/Paywalls/Components/PaywallTextComponent.swift @@ -97,7 +97,7 @@ extension PaywallComponent.TextComponent { enum CodingKeys: String, CodingKey { case type - case text = "text_lid" + case text = "textLid" case fontFamily case fontWeight case color @@ -116,7 +116,7 @@ extension PaywallComponent.PartialTextComponent { enum CodingKeys: String, CodingKey { case visible - case text = "text_lid" + case text = "textLid" case fontFamily case fontWeight case color diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Config/SamplePaywalls.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Config/SamplePaywalls.swift index daf920511e..eb79b900b6 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Config/SamplePaywalls.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Config/SamplePaywalls.swift @@ -650,7 +650,7 @@ private extension SamplePaywallLoader { ) -> PaywallComponentsData { PaywallComponentsData(templateName: "Component Sample", assetBaseURL: URL(string:"https://assets.pawwalls.com/")!, - componentsConfigs: .init( + componentsConfig: .init( base: .init( stack: .init(components: components), stickyFooter: stickyFooter @@ -928,7 +928,7 @@ private extension SamplePaywallLoader { static func simpleFeatureStack(text: PaywallComponent) -> PaywallComponent { .stack(.init(components: [checkmarkImage, text], - dimension: .horizontal(.center), + dimension: .horizontal(.center, .start), spacing: nil, backgroundColor: nil, padding: PaywallComponent.Padding(top: 0, bottom: 0, leading: 40, trailing: 40))) @@ -1109,7 +1109,7 @@ private extension SamplePaywallLoader { static var featureImageStack1: PaywallComponent = { .stack(.init(components: [treadmillText, spacer, treadmill, spacer], - dimension: .horizontal(.center), + dimension: .horizontal(.center, .start), spacing: nil, backgroundColor: nil, padding: PaywallComponent.Padding(top: 0, bottom: 0, leading: 40, trailing: 40))) @@ -1117,7 +1117,7 @@ private extension SamplePaywallLoader { static var featureImageStack2: PaywallComponent = { .stack(.init(components: [spacer, cycle, spacer, cycleText], - dimension: .horizontal(.center), + dimension: .horizontal(.center, .start), spacing: nil, backgroundColor: nil, padding: PaywallComponent.Padding(top: 0, bottom: 0, leading: 40, trailing: 40))) @@ -1125,7 +1125,7 @@ private extension SamplePaywallLoader { static var featureImageStack3: PaywallComponent = { .stack(.init(components: [weightsText, spacer, weights, spacer], - dimension: .horizontal(.center), + dimension: .horizontal(.center, .start), spacing: nil, backgroundColor: nil, padding: PaywallComponent.Padding(top: 0, bottom: 0, leading: 40, trailing: 40))) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/OfferingList/APIKeyDashboardList.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/OfferingList/APIKeyDashboardList.swift index cdc39ea97c..f0ba534a45 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/OfferingList/APIKeyDashboardList.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/OfferingList/APIKeyDashboardList.swift @@ -140,26 +140,10 @@ struct APIKeyDashboardList: View { } } .sheet(item: self.$presentedPaywall) { paywall in - #if PAYWALL_COMPONENTS - if let componentData = paywall.offering.paywallComponentsData { - // TODO: Get locale - TemplateComponentsView( - paywallComponentsData: componentData, - offering: paywall.offering, - onDismiss: { self.presentedPaywall = nil } - ) - } else { - PaywallPresenter(offering: paywall.offering, mode: paywall.mode, introEligility: .eligible) - .onRestoreCompleted { _ in - self.presentedPaywall = nil - } - } - #else PaywallPresenter(offering: paywall.offering, mode: paywall.mode, introEligility: .eligible) .onRestoreCompleted { _ in self.presentedPaywall = nil } - #endif } } @@ -208,7 +192,9 @@ extension APIKeyDashboardList.Template: CustomStringConvertible { var description: String { if let name = self.name { #if DEBUG - if let template = PaywallTemplate(rawValue: name) { + if name == "components" { + return "V2" + } else if let template = PaywallTemplate(rawValue: name) { return template.name } else { return "Unrecognized template" diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/SamplePaywallsList.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/SamplePaywallsList.swift index 2df080faa4..9d8a549a5d 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/SamplePaywallsList.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/SamplePaywallsList.swift @@ -109,7 +109,7 @@ struct SamplePaywallsList: View { Section("Components") { Button { let data = SamplePaywallLoader.template1Components - data.componentsConfigs.base.stack.components.printAsJSON() + data.componentsConfig.base.stack.components.printAsJSON() data.componentsLocalizations.printAsJSON() self.display = .componentPaywall(data) } label: { @@ -117,7 +117,7 @@ struct SamplePaywallsList: View { } Button { let data = SamplePaywallLoader.fitnessComponents - data.componentsConfigs.base.stack.components.printAsJSON() + data.componentsConfig.base.stack.components.printAsJSON() data.componentsLocalizations.printAsJSON() self.display = .componentPaywall(data) } label: { @@ -125,7 +125,7 @@ struct SamplePaywallsList: View { } Button { let data = SamplePaywallLoader.simpleSampleComponents - data.componentsConfigs.base.stack.components.printAsJSON() + data.componentsConfig.base.stack.components.printAsJSON() data.componentsLocalizations.printAsJSON() self.display = .componentPaywall(data) } label: { @@ -133,7 +133,7 @@ struct SamplePaywallsList: View { } Button { let data = SamplePaywallLoader.longWithStickyFooter - data.componentsConfigs.base.stack.components.printAsJSON() + data.componentsConfig.base.stack.components.printAsJSON() data.componentsLocalizations.printAsJSON() self.display = .componentPaywall(data) } label: {