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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

joshdholtz
Copy link
Member

@joshdholtz joshdholtz commented Jan 11, 2025

Motivation

Allow a multi-tier paywall with tabs and toggle

Description

  • 4 new components
    • tabs
    • tabs_control
    • tabs_control_button
    • tabs_control_toggle
  • ViewModelFactory
    • Creates a new PackageCollector when digging the tabs stack
    • Collects all the packages and then passes them into the TabViewModel
    • Merges the found packages back into the rest of the factory so the paywall knows about all the packages

TabsComponent

{
    "type": "tabs",
    "control": {
        "type": "buttons", // or "toggle"
        "stack": {
            "components": [
                {
                    "type": "tab_control_button" ,
                    "stack": { /* standard stack */ }
                },
                {
                    "type": "tab_control_button" ,
                    "stack": { /* standard stack */ }
                }
            ]
        },
    },
    "tabs": [
        {
            "type": "tab",
            "stack": {
                "type": "stack",
                "components": [
                    // This is all that is needed for `tab_control`
                    // The `control` property from above gets injected here in the view
                    { "type": "tab_control" },

                    { "type": "text", /* other props */ },
                    { "type": "text", /* other props */ },
                    { "type": "package" , /* other props */},
                    { "type": "package", /* other props */ }
                ]
            }
        },
        { /* another tab */ }
    ]
}

TabControlComponent

{
    "type": "tab_control"
}

TabControlButtonComponent

{
    "type": "tab_control_button" ,
    "stack": { /* standard stack */ }
}

TabControlToggleComponent

{
    "type": "tab_control_toggle" ,
    "default_value": false,
    "thumb_color_on": { /* color */ },
    "thumb_color_off": { /* color */ },
    "track_color_on": { /* color */ },
    "track_color_off": { /* color */ }
}

@joshdholtz joshdholtz changed the base branch from main to paywalls-v2/fix-stack-infinity January 11, 2025 16:49
Copy link

emerge-tools bot commented Jan 11, 2025

1 build increased size

Name Version Download Change Install Change Approval
⚠️ Paywalls
com.revenuecat.PaywallsTester
1.0 (1) 11.3 MB ⬆️ 342.1 kB (3.11%) 42.0 MB ⬆️ 1.1 MB (2.76%) N/A

Paywalls 1.0 (1)
com.revenuecat.PaywallsTester

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 1.1 MB (2.76%)
Total download size change: ⬆️ 342.1 kB (3.11%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 146.1 kB
📝 RevenueCatUI.TabsComponentViewModel.TabsComponentViewModel ⬆️ 33.5 kB
Code Signature ⬆️ 28.1 kB
📝 RevenueCatUI.TabViewModel.TabViewModel ⬆️ 24.8 kB
RevenueCatUI.StickyFooterComponentViewModel.StickyFooterComponent... ⬇️ -15.4 kB
View Treemap

Image of diff


🛸 Powered by Emerge Tools

Comment trigger: Size diff threshold of 100.00kB exceeded

Comment on lines +53 to +57
LoadedTabsComponentView(
viewModel: self.viewModel,
parentPackageContext: self.packageContext,
onDismiss: self.onDismiss
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment variables cannot be referenced in initializers so... I created LoadedTabsComponentView so that it would be passed self.packageContext since we need a the list of packages for creating a context for each tab/tier

Comment on lines +117 to +122
onChange: { context in
self.packageContext.update(
package: context.package,
variableContext: context.variableContext
)
},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each tab will call back to the entire tabs when one of its package contexts is updated (so that the whole paywall knows which new package was selected)

Comment on lines +82 to +83
@State
private var tierPackageContexts: [PackageContext]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeps a separate PackageContext stored for each tab

Comment on lines +79 to +80
@StateObject
private var tabControlContext: TabControlContext
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stores the shared state of the tab control (buttons of toggle)

Each tab will use this when it comes across the TabControlComponent in its stack so it knows what to render and what the active tab state is

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this happen recursively? What if the TabControlComponent is not a direct child of TabComponent?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question! The TabControlComponent could be in stack of stack of stack of stack and it will still find it since .environmentObjects get passed to the whole stack. So it it just needs to be within the tab somewhere

But the other way around... we will need to validate (on the backend) that TabControlComponent is always within a TabComponent somewhere (can't be outside) 😅

@joshdholtz joshdholtz requested review from a team January 13, 2025 11:17
@joshdholtz joshdholtz marked this pull request as ready for review January 13, 2025 11:18
Base automatically changed from paywalls-v2/fix-stack-infinity to main January 13, 2025 11:33
@joshdholtz joshdholtz force-pushed the paywalls-v2/multi-tier branch 2 times, most recently from ec9c328 to d5bb741 Compare January 13, 2025 12:33
Copy link
Member

@JayShortway JayShortway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very awesome! Just some questions basically.

Comment on lines +79 to +80
@StateObject
private var tabControlContext: TabControlContext
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this happen recursively? What if the TabControlComponent is not a direct child of TabComponent?

@joshdholtz joshdholtz force-pushed the paywalls-v2/multi-tier branch from c0225db to 2a109b7 Compare January 13, 2025 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants