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: Update paywall tester to be able to display paywall footer #1315

Merged
merged 1 commit into from
Oct 6, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.revenuecat.paywallstester.ui.screens.AppScreen
import com.revenuecat.paywallstester.ui.screens.main.MainScreen
import com.revenuecat.paywallstester.ui.screens.paywall.PaywallScreen
import com.revenuecat.paywallstester.ui.screens.paywall.PaywallScreenViewModel
import com.revenuecat.paywallstester.ui.screens.paywallfooter.PaywallFooterScreen

@Composable
fun PaywallTesterApp(
Expand All @@ -31,15 +32,26 @@ private fun AppNavHost(
modifier = modifier,
) {
composable(AppScreen.Main.route) {
MainScreen(navigateToPaywallScreen = { offering ->
navController.navigate(AppScreen.Paywall.route.plus("/${offering?.identifier}"))
})
MainScreen(
navigateToPaywallScreen = { offering ->
navController.navigate(AppScreen.Paywall.route.plus("/${offering?.identifier}"))
},
navigateToPaywallFooterScreen = { offering ->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that I've made showing the paywall as a footer a top level destination in the navhost, same as the normal navigation one. We might want to think about adding another composable to display the PaywallFooter as a dialog, but that can come later.

navController.navigate(AppScreen.PaywallFooter.route.plus("/${offering?.identifier}"))
},
)
}
composable(
route = AppScreen.Paywall.route.plus("/{${PaywallScreenViewModel.OFFERING_ID_KEY}}"),
arguments = listOf(navArgument(PaywallScreenViewModel.OFFERING_ID_KEY) { type = NavType.StringType }),
) {
PaywallScreen()
}
composable(
route = AppScreen.PaywallFooter.route.plus("/{${PaywallScreenViewModel.OFFERING_ID_KEY}}"),
arguments = listOf(navArgument(PaywallScreenViewModel.OFFERING_ID_KEY) { type = NavType.StringType }),
) {
PaywallFooterScreen()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package com.revenuecat.paywallstester.ui.screens
sealed class AppScreen(val route: String) {
object Main : AppScreen("main")
object Paywall : AppScreen("paywall")
object PaywallFooter : AppScreen("paywall_footer")
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ import com.revenuecat.purchases.Offering
@Composable
fun MainScreen(
navigateToPaywallScreen: (Offering?) -> Unit,
navigateToPaywallFooterScreen: (Offering?) -> Unit,
navController: NavHostController = rememberNavController(),
) {
Scaffold(
bottomBar = { BottomBarNavigation(navController) },
) {
MainNavHost(navController, navigateToPaywallScreen, Modifier.padding(it))
MainNavHost(navController, navigateToPaywallScreen, navigateToPaywallFooterScreen, Modifier.padding(it))
}
}

@Preview
@Composable
fun MainScreenPreview() {
MainScreen(navigateToPaywallScreen = {})
MainScreen(navigateToPaywallScreen = {}, navigateToPaywallFooterScreen = {})
}

private val bottomNavigationItems = listOf(
Expand All @@ -50,6 +51,7 @@ private val bottomNavigationItems = listOf(
private fun MainNavHost(
navController: NavHostController,
navigateToPaywallScreen: (Offering?) -> Unit,
navigateToPaywallFooterScreen: (Offering?) -> Unit,
modifier: Modifier = Modifier,
) {
NavHost(
Expand All @@ -64,7 +66,10 @@ private fun MainNavHost(
PaywallsScreen()
}
composable(Tab.Offerings.route) {
OfferingsScreen(tappedOnOffering = { offering -> navigateToPaywallScreen(offering) })
OfferingsScreen(
tappedOnOffering = { offering -> navigateToPaywallScreen(offering) },
tappedOnOfferingFooter = { offering -> navigateToPaywallFooterScreen(offering) },
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ import kotlinx.coroutines.flow.asStateFlow
@Composable
fun OfferingsScreen(
tappedOnOffering: (Offering) -> Unit,
tappedOnOfferingFooter: (Offering) -> Unit,
viewModel: OfferingsViewModel = viewModel<OfferingsViewModelImpl>(),
) {
when (val state = viewModel.offeringsState.collectAsState().value) {
is OfferingsState.Error -> ErrorOfferingsScreen(errorState = state)
is OfferingsState.Loaded -> OfferingsListScreen(
offeringsState = state,
tappedOnNavigateToOffering = tappedOnOffering,
tappedOnNavigateToOfferingFooter = tappedOnOfferingFooter,
)
OfferingsState.Loading -> LoadingOfferingsScreen()
}
Expand Down Expand Up @@ -74,6 +76,7 @@ private fun LoadingOfferingsScreen() {
private fun OfferingsListScreen(
offeringsState: OfferingsState.Loaded,
tappedOnNavigateToOffering: (Offering) -> Unit,
tappedOnNavigateToOfferingFooter: (Offering) -> Unit,
) {
var dropdownExpandedOffering by remember { mutableStateOf<Offering?>(null) }
var displayPaywallDialogOffering by remember { mutableStateOf<Offering?>(null) }
Expand All @@ -86,6 +89,7 @@ private fun OfferingsListScreen(
offering = offering,
tappedOnNavigateToOffering = tappedOnNavigateToOffering,
tappedOnDisplayOfferingAsDialog = { displayPaywallDialogOffering = it },
tappedOnDisplayOfferingAsFooter = tappedOnNavigateToOfferingFooter,
dismissed = { dropdownExpandedOffering = null },
)
}
Expand Down Expand Up @@ -127,6 +131,7 @@ private fun DisplayOfferingMenu(
offering: Offering,
tappedOnNavigateToOffering: (Offering) -> Unit,
tappedOnDisplayOfferingAsDialog: (Offering) -> Unit,
tappedOnDisplayOfferingAsFooter: (Offering) -> Unit,
dismissed: () -> Unit,
) {
DropdownMenu(expanded = true, onDismissRequest = { dismissed() }) {
Expand All @@ -138,6 +143,10 @@ private fun DisplayOfferingMenu(
text = { Text(text = "Display paywall as dialog") },
onClick = { tappedOnDisplayOfferingAsDialog(offering) },
)
DropdownMenuItem(
text = { Text(text = "Display paywall as footer") },
onClick = { tappedOnDisplayOfferingAsFooter(offering) },
)
}
}

Expand All @@ -146,6 +155,7 @@ private fun DisplayOfferingMenu(
fun OfferingsScreenPreview() {
OfferingsScreen(
tappedOnOffering = {},
tappedOnOfferingFooter = {},
viewModel = object : OfferingsViewModel() {
private val _offeringsState = MutableStateFlow<OfferingsState>(
OfferingsState.Loaded(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.revenuecat.paywallstester.ui.screens.paywallfooter

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import com.revenuecat.paywallstester.ui.screens.paywall.PaywallScreenState
import com.revenuecat.paywallstester.ui.screens.paywall.PaywallScreenViewModel
import com.revenuecat.paywallstester.ui.screens.paywall.PaywallScreenViewModelImpl
import com.revenuecat.purchases.ui.revenuecatui.PaywallFooter
import com.revenuecat.purchases.ui.revenuecatui.PaywallViewOptions

@Composable
fun PaywallFooterScreen(
viewModel: PaywallScreenViewModel = viewModel<PaywallScreenViewModelImpl>(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm reusing the same view model as the full screen one. No need to create a separate view model.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup 👍🏻

) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
when (val state = viewModel.state.collectAsState().value) {
is PaywallScreenState.Loading -> {
Text(text = "Loading...")
}
is PaywallScreenState.Error -> {
Text(text = "Error: ${state.errorMessage}")
}
is PaywallScreenState.Loaded -> {
PaywallFooter(
options = PaywallViewOptions.Builder()
.setOffering(state.offering)
Copy link
Contributor

Choose a reason for hiding this comment

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

Wait hmmm does this mean that in order to use PaywallFooter, users need to load the offering themselves?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, this setter is optional. If you don't set it, we will load the current offering by default

.setListener(viewModel)
.build(),
) {
SamplePaywall(paddingValues = it)
}
state.dialogText?.let {
PurchaseAlertDialog(viewModel, it)
}
}
}
}
}

@Suppress("MagicNumber")
@Composable
private fun SamplePaywall(paddingValues: PaddingValues) {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(paddingValues),
) {
// TODO-Paywalls: Implement an actual sample paywall
for (i in 1..50) {
Text(text = "Main content $i")
}
}
}

@Composable
private fun PurchaseAlertDialog(
viewModel: PaywallScreenViewModel,
text: String,
) {
AlertDialog(
onDismissRequest = {
viewModel.onDialogDismissed()
},
buttons = {
TextButton(
onClick = {
viewModel.onDialogDismissed()
},
) {
Text("Ok")
}
},
text = {
Text(text)
},
)
}