Repository: Automattic/pocket-casts-android
File: modules/features/account/src/main/java/au/com/shiftyjelly/pocketcasts/account/onboarding/upgrade/OnboardingUpgradeScreen.kt (Kotlin)

Code before:
                .wrapContentHeight(),
        )
        Spacer(modifier = Modifier.height(24.dp))
        TextH10(
            text = stringResource(LR.string.onboarding_upgrade_generic_title),
            color = MaterialTheme.theme.colors.primaryText01,
        )
    }
}

sealed interface ContentPage {
    val showCta: Boolean

    data class Features(val features: List<UpgradeFeatureItem>, override val showCta: Boolean) : ContentPage
    data class TrialSchedule(val timelineItems: List<UpgradeTrialItem>, override val showCta: Boolean) : ContentPage
}

@Composable
private fun UpgradeContent(
    pages: List<ContentPage>,
    modifier: Modifier = Modifier,
) {
    Column(modifier = modifier) {
        val pagerState = rememberPagerState(initialPage = 0) { pages.size }
        val coroutineScope = rememberCoroutineScope()
        VerticalPager(state = pagerState, modifier = Modifier.fillMaxSize()) { page ->
            when (val currentPage = pages[page]) {
                is ContentPage.Features -> FeaturesContent(
                    features = currentPage,
                    onCtaClicked = { coroutineScope.launch { pagerState.animateScrollToPage(pages.size - page - 1) } },
                )

                is ContentPage.TrialSchedule -> ScheduleContent(
                    trialSchedule = currentPage,
                    onCtaClicked = { coroutineScope.launch { pagerState.animateScrollToPage(pages.size - page - 1) } },
                )
            }
        }
    }
}

@Composable
private fun FeaturesContent(
    features: ContentPage.Features,
    onCtaClicked: () -> Unit,
) {
    LazyColumn {
        items(features.features.size) {
            UpgradeFeatureItem(
                item = features.features[it],
                iconColor = MaterialTheme.theme.colors.primaryText01,

Review comment (suggestion):
The type name feels not right. `ContentPage` suggests something like a Composable UI component. I think it can also be `private`.

Code after:
                        showCta = false,
                    ),
                )
            }
        }

        Variants.VARIANT_TRIAL_TIMELINE -> {
            add(
                UpgradePagerContent.TrialSchedule(
                    timelineItems = UpgradeTrialItem.getPreviewItems(),
                    showCta = true,
                ),
            )
            add(
                UpgradePagerContent.Features(
                    features = currentPlan.featureItems,
                    showCta = false,
                ),
            )
        }
    }
}

private sealed interface UpgradePagerContent {
    val showCta: Boolean

    data class Features(val features: List<UpgradeFeatureItem>, override val showCta: Boolean) : UpgradePagerContent
    data class TrialSchedule(val timelineItems: List<UpgradeTrialItem>, override val showCta: Boolean) : UpgradePagerContent
}

@Composable
private fun UpgradeContent(
    pages: List<UpgradePagerContent>,
    modifier: Modifier = Modifier,
) {
    Column(modifier = modifier) {
        val pagerState = rememberPagerState(initialPage = 0) { pages.size }
        val coroutineScope = rememberCoroutineScope()
        VerticalPager(state = pagerState, modifier = Modifier.fillMaxSize()) { page ->
            when (val currentPage = pages[page]) {
                is UpgradePagerContent.Features -> FeaturesContent(
                    features = currentPage,
                    onCtaClick = { coroutineScope.launch { pagerState.animateScrollToPage(pages.size - page) } },
                )

                is UpgradePagerContent.TrialSchedule -> ScheduleContent(
                    trialSchedule = currentPage,
                    onCtaClick = { coroutineScope.launch { pagerState.animateScrollToPage(pages.size - page) } },
                )
            }
        }