-
Notifications
You must be signed in to change notification settings - Fork 0
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
Fixes for paywall tester #1
base: main
Are you sure you want to change the base?
Changes from all 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 |
---|---|---|
@@ -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..<componentViewModels.count, id: \.self) { index in | ||
ComponentsView(componentViewModels: [self.componentViewModels[index]], onDismiss: self.onDismiss) | ||
} | ||
Spacer() | ||
|
||
case .center: | ||
Spacer() | ||
ForEach(0..<componentViewModels.count, id: \.self) { index in | ||
ComponentsView(componentViewModels: [self.componentViewModels[index]], onDismiss: self.onDismiss) | ||
} | ||
Spacer() | ||
|
||
case .end: | ||
Spacer() | ||
ForEach(0..<componentViewModels.count, id: \.self) { index in | ||
ComponentsView(componentViewModels: [self.componentViewModels[index]], onDismiss: self.onDismiss) | ||
} | ||
|
||
case .spaceBetween: | ||
ForEach(0..<componentViewModels.count, id: \.self) { index in | ||
ComponentsView(componentViewModels: [self.componentViewModels[index]], onDismiss: self.onDismiss) | ||
if index < self.componentViewModels.count - 1 { | ||
Spacer() | ||
} | ||
} | ||
|
||
case .spaceAround: | ||
Spacer() | ||
ForEach(0..<componentViewModels.count, id: \.self) { index in | ||
ComponentsView(componentViewModels: [self.componentViewModels[index]], onDismiss: self.onDismiss) | ||
if index < self.componentViewModels.count - 1 { | ||
Spacer() | ||
} | ||
} | ||
Spacer() | ||
|
||
case .spaceEvenly: | ||
ForEach(0..<componentViewModels.count, id: \.self) { index in | ||
Spacer() | ||
ComponentsView(componentViewModels: [self.componentViewModels[index]], onDismiss: self.onDismiss) | ||
} | ||
Spacer() | ||
Comment on lines
+86
to
+90
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. logic: spaceEvenly implementation places Spacer before first item, which differs from CSS flex justify-content: space-evenly. Should have equal space between all items |
||
} | ||
} | ||
} | ||
} | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,14 +35,53 @@ struct StackComponentView: View { | |
Group { | ||
switch viewModel.dimension { | ||
case .vertical(let horizontalAlignment): | ||
// LazyVStack needed for performance when loading | ||
LazyVStack(spacing: viewModel.spacing) { | ||
ComponentsView(componentViewModels: self.viewModel.viewModels, onDismiss: self.onDismiss) | ||
.frame(maxWidth: .infinity, alignment: horizontalAlignment.stackAlignment) | ||
Group { | ||
// This is NOT a final implementation of this | ||
// There are some horizontal sizing issues with using LazyVStack | ||
// There are so performance issues with VStack with lots of children | ||
Comment on lines
+40
to
+41
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. syntax: typo in comment: 'There are so performance issues' should be 'There are some performance issues' |
||
if viewModel.shouldUseVStack { | ||
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. logic: using a hardcoded threshold of 3 items (from ViewModel) to switch between VStack and LazyVStack could cause performance issues with complex child views even with few items |
||
// VStack when not many things | ||
VStack( | ||
alignment: horizontalAlignment.stackAlignment, | ||
spacing: viewModel.spacing | ||
) { | ||
ComponentsView( | ||
componentViewModels: self.viewModel.viewModels, | ||
onDismiss: self.onDismiss | ||
) | ||
} | ||
} else { | ||
// LazyVStack needed for performance when loading | ||
LazyVStack( | ||
alignment: horizontalAlignment.stackAlignment, | ||
spacing: viewModel.spacing | ||
) { | ||
ComponentsView( | ||
componentViewModels: self.viewModel.viewModels, | ||
onDismiss: self.onDismiss | ||
) | ||
} | ||
} | ||
} | ||
case .horizontal(let verticalAlignment): | ||
HStack(alignment: verticalAlignment.stackAlignment, spacing: viewModel.spacing) { | ||
ComponentsView(componentViewModels: self.viewModel.viewModels, onDismiss: self.onDismiss) | ||
.applyIf(viewModel.shouldUseFlex) { | ||
$0.frame( | ||
maxWidth: .infinity, | ||
alignment: horizontalAlignment.frameAlignment | ||
) | ||
} | ||
case .horizontal(let verticalAlignment, let distribution): | ||
if viewModel.shouldUseFlex { | ||
FlexHStack( | ||
alignment: verticalAlignment.stackAlignment, | ||
spacing: viewModel.spacing, | ||
justifyContent: distribution.justifyContent, | ||
componentViewModels: self.viewModel.viewModels, | ||
onDismiss: self.onDismiss | ||
) | ||
} else { | ||
HStack(alignment: verticalAlignment.stackAlignment, spacing: viewModel.spacing) { | ||
ComponentsView(componentViewModels: self.viewModel.viewModels, onDismiss: self.onDismiss) | ||
} | ||
} | ||
case .zlayer(let alignment): | ||
ZStack(alignment: alignment.stackAlignment) { | ||
|
@@ -65,6 +104,27 @@ struct StackComponentView: View { | |
|
||
} | ||
|
||
extension PaywallComponent.FlexDistribution { | ||
|
||
var justifyContent: JustifyContent { | ||
switch self { | ||
case .start: | ||
return .start | ||
case .center: | ||
return .center | ||
case .end: | ||
return .end | ||
case .spaceBetween: | ||
return .spaceBetween | ||
case .spaceAround: | ||
return .spaceAround | ||
case .spaceEvenly: | ||
return .spaceEvenly | ||
} | ||
} | ||
|
||
} | ||
|
||
struct WidthModifier: ViewModifier { | ||
var width: PaywallComponent.WidthSize? | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,33 @@ class StackComponentViewModel { | |
self.viewModels = viewModels | ||
} | ||
|
||
var shouldUseVStack: Bool { | ||
switch self.dimension { | ||
case .vertical: | ||
if viewModels.count < 3 { | ||
return true | ||
} | ||
return false | ||
case .horizontal, .zlayer: | ||
Comment on lines
+53
to
+57
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. style: Simplify to |
||
return false | ||
} | ||
} | ||
|
||
var shouldUseFlex: Bool { | ||
guard let widthType = self.component.width?.type else { | ||
return false | ||
} | ||
|
||
switch widthType { | ||
case .fit: | ||
return false | ||
case .fill: | ||
return true | ||
case .fixed: | ||
return true | ||
} | ||
Comment on lines
+67
to
+74
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. style: Consider combining .fill and .fixed cases since they have the same behavior |
||
} | ||
|
||
var dimension: PaywallComponent.Dimension { | ||
component.dimension | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,7 +83,7 @@ struct TemplateComponentsView: View { | |
self.onDismiss = onDismiss | ||
|
||
// Step 0: Decide which ComponentsConfig to use (base is default) | ||
let componentsConfig = paywallComponentsData.componentsConfigs.base | ||
let componentsConfig = paywallComponentsData.componentsConfig.base | ||
|
||
// Step 1: Get localization | ||
let localization = Self.chooseLocalization(for: paywallComponentsData) | ||
|
@@ -113,10 +113,11 @@ struct TemplateComponentsView: View { | |
) | ||
) | ||
|
||
guard packageValidator.isValid else { | ||
Logger.error(Strings.paywall_could_not_find_any_packages) | ||
throw PackageGroupValidationError.noAvailablePackages("No available packages found") | ||
} | ||
// WIP: Maybe re-enable this later or add some warnings | ||
// guard packageValidator.isValid else { | ||
// Logger.error(Strings.paywall_could_not_find_any_packages) | ||
// throw PackageGroupValidationError.noAvailablePackages("No available packages found") | ||
// } | ||
Comment on lines
+116
to
+120
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. logic: Commenting out package validation could lead to runtime issues if no valid packages exist. Consider adding a warning or fallback behavior instead of completely removing the validation. |
||
|
||
self.componentViewModel = componentViewModel | ||
self._paywallState = .init(wrappedValue: PaywallState( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,7 @@ private enum Template5Preview { | |
static let title = PaywallComponent.TextComponent( | ||
text: "title", | ||
fontFamily: nil, | ||
fontWeight: .heavy, | ||
fontWeight: .black, | ||
color: .init(light: "#000000"), | ||
backgroundColor: nil, | ||
padding: .zero, | ||
|
@@ -171,7 +171,7 @@ private enum Template5Preview { | |
components: [ | ||
.purchaseButton(purchaseButton) | ||
], | ||
dimension: .horizontal(.center), | ||
dimension: .horizontal(.center, .start), | ||
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. style: horizontal dimension now includes .start parameter which may affect button alignment - verify this change is intentional and doesn't break existing layouts |
||
width: .init(type: .fill, value: nil), | ||
spacing: 0, | ||
backgroundColor: nil | ||
|
@@ -207,7 +207,7 @@ private enum Template5Preview { | |
static let data: PaywallComponentsData = .init( | ||
templateName: "components", | ||
assetBaseURL: URL(string: "https://assets.pawwalls.com")!, | ||
componentsConfigs: .init( | ||
componentsConfig: .init( | ||
base: .init( | ||
stack: .init( | ||
components: [ | ||
|
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.
logic: Using array index directly as id could cause issues if items change. Consider using a more stable identifier from PaywallComponentViewModel