Skip to content

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

Merged
joshdholtz merged 11 commits into
mainfrom
paywalls-v2/multi-tier
Jan 14, 2025
Merged

[Paywalls] Tabs (multi-tier / toggle) component#4648
joshdholtz merged 11 commits into
mainfrom
paywalls-v2/multi-tier

Conversation

@joshdholtz

@joshdholtz joshdholtz commented Jan 11, 2025

Copy link
Copy Markdown
Member

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
@emerge-tools

emerge-tools Bot commented Jan 11, 2025

Copy link
Copy Markdown

1 build increased size

Name Version Download Change Install Change Approval
⚠️ Paywalls
com.revenuecat.PaywallsTester
1.0 (1) 11.5 MB ⬆️ 352.7 kB (3.18%) 42.5 MB ⬆️ 1.2 MB (2.85%) N/A

Paywalls 1.0 (1)
com.revenuecat.PaywallsTester

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

Total install size change: ⬆️ 1.2 MB (2.85%)
Total download size change: ⬆️ 352.7 kB (3.18%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 176.1 kB
📝 RevenueCatUI.TabsComponentViewModel.TabsComponentViewModel ⬆️ 33.8 kB
Code Signature ⬆️ 29.3 kB
📝 RevenueCatUI.TabViewModel.TabViewModel ⬆️ 25.7 kB
RevenueCatUI.StickyFooterComponentViewModel.StickyFooterComponent... ⬇️ -15.9 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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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) 😅

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! Happy that that's supported! 😄

@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

@JayShortway JayShortway left a comment

Copy link
Copy Markdown
Member

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
Copy Markdown
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?

Comment thread Sources/Paywalls/Components/PaywallTabsComponent.swift
@joshdholtz joshdholtz force-pushed the paywalls-v2/multi-tier branch 3 times, most recently from 9dd6452 to f889fc8 Compare January 13, 2025 22:27
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