This module helps coordinate upsell flows on different pages across the app
To upsell a feature on a certain page, one has to be aware of all the other features that are also being tried to be "upsold" on that same page and their timing and implementation logic behind it. In order to reduce this complexity and to prevent the user from being in states where multiple features are being shown to the users at the same time, this module was created
The upseller module encapsulates the timing and implementation logic into UpSell
s. Each UpSell
contains the following properties and functions:
id
- A unique identifier for each UpSell
active
- The current internal state of the UpSell
. It could be true
or false
.
becomesInActiveOnceShown
- This flag is used for "weak" UpSells that only need to be shown once and set to inactive immediately after.
source
- This suspend
function defines the external condition which must be fulfilled for an UpSell to be eligible for running. For example, a Biometrics UpSell would have a source = { isFingerprintOnboardingRequired() }
fun shouldRun
- defines if an UpSell
should be run (based on timing strategy)
fun preCheckUpdateState
- before a shouldRun
check is run, this function is called to update the state of the UpSell
, if necessary. For example, in a LaunchCountUpSell
, you want to increment the current launch counter before you do a shouldRun
check.
fun postCheckUpdateState
- after a shouldRun
check is run, this function can be used to update the state of the UpSell
, if necessary. For example, in a PeriodicUntilSoldUpSell
, you want to update the lastChecked
property.
fun setActive(active: Boolean)
- used to activate/deactivate an UpSell
There are currently 4 types of UpSell
s based on timing strategy and implementation logic
LaunchCountUpSell
- used when you want to upsell a feature after a fixed number of screen launchElapsedTimeUpSell
- used when you want to upsell a feature a defined number of milliseconds (timeAfterRegistration
) from a statedtimeOfRegistration
also in millisecondsFixedDateUpSell
- used to upsell a feature on a definedexecutionDate
PeriodicUntilSoldUpSell
- used to upsell a feature periodically afterperiod
milliseconds
- Create a sealed class that defines the possible up-sells there are on a screen. See here for an full example
sealed class LoggedInScreenUpSell{
object BiometricsUpSell : LoggedInScreenUpSell()
class SpoUpSell(val socialProvider : String) : LoggedInScreenUpSell()
object OneTouchUpSell : LoggedInScreenUpSell()
}
-
Create a class e.g GetLoggedInMainActivityUpSells which gets an aggregated list of up-sells to be run on the
LoggedInMainActivity
screen. -
In the
ViewModel
of your screen, make a call to the aforementioned classGetLoggedInMainActivityUpSells
to get the List. Pass this into thegetNextUpSellInteractor(List<UpSell>)
to fetch the nextUpSell sealed class
(defined in step 1) for the up-sell that's ready to be run. See here for a full example
viewModelScope.launch(coroutineContextProvider.main) {
val upSellToRun = getLoggedInMainActivityUpSells(profilesInteractor)
checkUpSells(upSellToRun)
}
private suspend fun checkUpSells(upSells: List<UpSell<out LoggedInScreenUpSell>>) {
val upSellToRun = upNextUpSellInteractor(upSells)
upSellToRun?.let {
when (it) {
is LoggedInScreenUpSell.BiometricsUpSell -> mutableActionState.value = ActionState.ShowBiometricsEnrol
is LoggedInScreenUpSell.SpoUpSell -> mutableActionState.value = ActionState.ShowSocialPasswordOnboarding(it.socialProvider)
is LoggedInScreenUpSell.OneTouchUpSell -> mutableActionState.value = ActionState.ShowOneTouchOnBoarding
}
}
}