Skip to content
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

[Paywalls] Tabs (multi-tier / toggle) component #4648

Merged
merged 11 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 0 additions & 77 deletions Package.resolved

This file was deleted.

56 changes: 52 additions & 4 deletions RevenueCat.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ fileprivate extension ButtonComponentViewModel {
let factory = ViewModelFactory()
let stackViewModel = try factory.toStackViewModel(
component: component.stack,
packageValidator: factory.packageValidator,
localizationProvider: localizationProvider,
uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()),
offering: offering
Expand Down
10 changes: 10 additions & 0 deletions RevenueCatUI/Templates/V2/Components/ComponentsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct ComponentsView: View {
}

@ViewBuilder
// swiftlint:disable:next cyclomatic_complexity
func layoutComponents(_ componentViewModels: [PaywallComponentViewModel]) -> some View {
ForEach(Array(componentViewModels.enumerated()), id: \.offset) { _, item in
switch item {
Expand All @@ -51,6 +52,15 @@ struct ComponentsView: View {
PurchaseButtonComponentView(viewModel: viewModel)
case .stickyFooter(let viewModel):
StickyFooterComponentView(viewModel: viewModel)

case .tabs(let viewModel):
TabsComponentView(viewModel: viewModel, onDismiss: onDismiss)
case .tabControl(let viewModel):
TabControlComponentView(viewModel: viewModel, onDismiss: onDismiss)
case .tabControlButton(let viewModel):
TabControlButtonComponentView(viewModel: viewModel, onDismiss: onDismiss)
case .tabControlToggle(let viewModel):
TabControlToggleComponentView(viewModel: viewModel, onDismiss: onDismiss)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ fileprivate extension PackageComponentViewModel {
let factory = ViewModelFactory()
let stackViewModel = try factory.toStackViewModel(
component: component.stack,
packageValidator: factory.packageValidator,
localizationProvider: localizationProvider,
uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()),
offering: offering
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ fileprivate extension PurchaseButtonComponentViewModel {
let factory = ViewModelFactory()
let stackViewModel = try factory.toStackViewModel(
component: component.stack,
packageValidator: factory.packageValidator,
localizationProvider: localizationProvider,
uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()),
offering: offering
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ struct StackComponentView: View {
/// area when displayed as a sticky footer.
private let additionalPadding: EdgeInsets

init(viewModel: StackComponentViewModel, onDismiss: @escaping () -> Void, additionalPadding: EdgeInsets? = nil) {
init(
viewModel: StackComponentViewModel,
onDismiss: @escaping () -> Void,
additionalPadding: EdgeInsets? = nil
) {
self.viewModel = viewModel
self.onDismiss = onDismiss
self.additionalPadding = additionalPadding ?? EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
Expand Down Expand Up @@ -506,7 +510,7 @@ struct StackComponentView_Previews: PreviewProvider {
}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
fileprivate extension StackComponentViewModel {
extension StackComponentViewModel {

convenience init(
component: PaywallComponent.StackComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ private typealias PresentedStackPartial = PaywallComponent.PartialStackComponent
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
class StackComponentViewModel {

private let component: PaywallComponent.StackComponent
let component: PaywallComponent.StackComponent
let uiConfigProvider: UIConfigProvider
private let presentedOverrides: PresentedOverrides<PresentedStackPartial>?

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// 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
//
// TabsComponentView.swift
//
// Created by Josh Holtz on 1/9/25.

import Foundation
import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct TabControlButtonComponentView: View {

@EnvironmentObject
private var introOfferEligibilityContext: IntroOfferEligibilityContext

@EnvironmentObject
private var packageContext: PackageContext

@Environment(\.componentViewState)
private var componentViewState

@Environment(\.screenCondition)
private var screenCondition

@EnvironmentObject
private var tabControlContext: TabControlContext

private let viewModel: TabControlButtonComponentViewModel
private let onDismiss: () -> Void

private var selectedState: ComponentViewState {
return self.tabControlContext.selectedIndex == self.viewModel.component.tabIndex ? .selected : .default
}

init(viewModel: TabControlButtonComponentViewModel, onDismiss: @escaping () -> Void) {
self.viewModel = viewModel
self.onDismiss = onDismiss
}

var body: some View {
Button {
self.tabControlContext.selectedIndex = self.viewModel.component.tabIndex
} label: {
StackComponentView(
viewModel: self.viewModel.stackViewModel,
onDismiss: self.onDismiss
)
.environment(\.componentViewState, self.selectedState)
}

}

}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// 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
//
// TabsComponentViewModel.swift
//
// Created by Josh Holtz on 1/9/25.

import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
class TabControlButtonComponentViewModel {

let component: PaywallComponent.TabControlButtonComponent
let stackViewModel: StackComponentViewModel
let uiConfigProvider: UIConfigProvider

init(
component: PaywallComponent.TabControlButtonComponent,
stackViewModel: StackComponentViewModel,
uiConfigProvider: UIConfigProvider
) throws {
self.component = component
self.stackViewModel = stackViewModel
self.uiConfigProvider = uiConfigProvider
}

}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// 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
//
// TabsComponentView.swift
//
// Created by Josh Holtz on 1/9/25.

import Foundation
import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct TabControlComponentView: View {

@EnvironmentObject
private var introOfferEligibilityContext: IntroOfferEligibilityContext

@EnvironmentObject
private var packageContext: PackageContext

@Environment(\.componentViewState)
private var componentViewState

@Environment(\.screenCondition)
private var screenCondition

@EnvironmentObject
private var tabControlContext: TabControlContext

private let viewModel: TabControlComponentViewModel
private let onDismiss: () -> Void

init(viewModel: TabControlComponentViewModel, onDismiss: @escaping () -> Void) {
self.viewModel = viewModel
self.onDismiss = onDismiss
}

var body: some View {
StackComponentView(
viewModel: self.tabControlContext.controlStackViewModel,
onDismiss: self.onDismiss
)
}

}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// 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
//
// TabsComponentViewModel.swift
//
// Created by Josh Holtz on 1/9/25.

import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
class TabControlComponentViewModel {

let component: PaywallComponent.TabControlComponent
let uiConfigProvider: UIConfigProvider

init(
component: PaywallComponent.TabControlComponent,
uiConfigProvider: UIConfigProvider
) throws {
self.component = component
self.uiConfigProvider = uiConfigProvider
}

}

#endif
Loading