Show Onboarding Upselling when closing Onboarding

MAILANDR-2184
This commit is contained in:
Adam Jodlowski
2024-09-24 20:13:48 +02:00
parent fc4ad00f88
commit a7d8d0b467
24 changed files with 445 additions and 70 deletions
+1
View File
@@ -71,6 +71,7 @@ scripts/uitests/AssetsFile.lock.bak
.idea/androidTestResultsUserPreferences.xml
.idea/assetWizardSettings.xml
.idea/deploymentTargetDropDown.xml
.idea/deploymentTargetSelector.xml
.idea/vcs.xml
.idea/**/shelf
.idea/runConfigurations.xml
@@ -83,6 +83,7 @@ import ch.protonmail.android.navigation.route.addMailbox
import ch.protonmail.android.navigation.route.addManageMembers
import ch.protonmail.android.navigation.route.addMessageDetail
import ch.protonmail.android.navigation.route.addNotificationsSettings
import ch.protonmail.android.navigation.route.addOnboardingUpselling
import ch.protonmail.android.navigation.route.addParentFolderList
import ch.protonmail.android.navigation.route.addPrivacySettings
import ch.protonmail.android.navigation.route.addRemoveAccountDialog
@@ -594,6 +595,7 @@ fun Home(
addThemeSettings(navController)
addNotificationsSettings(navController)
addDeepLinkHandler(navController)
addOnboardingUpselling()
}
}
}
@@ -194,6 +194,8 @@ sealed class Destination(val route: String) {
object ManageMembers : Destination("contacts/group/manageMembers")
object ContactSearch : Destination("contacts/search")
object OnboardingUpselling : Destination("upselling")
}
object Dialog {
@@ -97,7 +97,8 @@ internal fun NavGraphBuilder.addMailbox(
showNormalSnackbar = showNormalSnackbar,
showErrorSnackbar = showErrorSnackbar,
onAddLabel = { navController.navigate(Destination.Screen.CreateLabel.route) },
onAddFolder = { navController.navigate(Destination.Screen.CreateFolder.route) }
onAddFolder = { navController.navigate(Destination.Screen.CreateFolder.route) },
navigateToOnboardingUpselling = { navController.navigate(Destination.Screen.OnboardingUpselling.route) }
)
)
}
@@ -578,3 +579,8 @@ internal fun NavGraphBuilder.addContactSearch(navController: NavHostController)
}
}
internal fun NavGraphBuilder.addOnboardingUpselling() {
composable(route = Destination.Screen.OnboardingUpselling.route) {
// show Upselling Onboarding
}
}
@@ -95,7 +95,8 @@ internal class MailboxScreenTest : HiltInstrumentedTest() {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
)
val mailboxState = MailboxStateSampleData.Loading.copy(mailboxListState = mailboxListState)
val items = listOf(MailboxItemUiModelTestData.readMailboxItemUiModel)
@@ -119,7 +120,8 @@ internal class MailboxScreenTest : HiltInstrumentedTest() {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
)
val mailboxState = MailboxStateSampleData.Loading.copy(mailboxListState = mailboxListState)
val label = LabelUiModelSample.News
@@ -153,7 +155,8 @@ internal class MailboxScreenTest : HiltInstrumentedTest() {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
)
val mailboxState = MailboxStateSampleData.Loading.copy(mailboxListState = mailboxListState)
val robot = setupScreen(state = mailboxState)
@@ -173,7 +176,8 @@ internal class MailboxScreenTest : HiltInstrumentedTest() {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
)
val mailboxState = MailboxStateSampleData.Loading.copy(mailboxListState = mailboxListState)
val robot = setupScreen(state = mailboxState)
@@ -207,7 +211,8 @@ internal class MailboxScreenTest : HiltInstrumentedTest() {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
),
topAppBarState = MailboxTopAppBarState.Data.DefaultMode(
currentLabelName = MailLabel.System(systemLabel).text()
@@ -222,7 +227,8 @@ internal class MailboxScreenTest : HiltInstrumentedTest() {
storageLimitState = StorageLimitState.HasEnoughSpace,
bottomSheetState = null,
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
}
@@ -90,6 +90,7 @@ dependencies {
implementation(project(":mail-settings:domain"))
implementation(project(":mail-settings:presentation"))
implementation(project(":mail-upselling:presentation"))
implementation(project(":mail-upselling:domain"))
implementation(project(":mail-onboarding:presentation"))
implementation(project(":mail-sidebar:presentation"))
implementation(project(":uicomponents"))
@@ -176,7 +176,10 @@ fun MailboxScreen(
Timber.d("BottomState: ${mailboxState.bottomAppBarState}")
if (mailboxState.onboardingState is OnboardingState.Shown) {
OnboardingScreen(onCloseOnboarding = { viewModel.submit(MailboxViewAction.CloseOnboarding) })
OnboardingScreen(
shouldShowUpselling = mailboxState.onboardingState is OnboardingState.Shown.UpsellingOn,
onCloseOnboarding = { viewModel.submit(MailboxViewAction.CloseOnboarding) }
)
} else {
val completeActions = actions.copy(
onDisableUnreadFilter = { viewModel.submit(MailboxViewAction.DisableUnreadFilter) },
@@ -218,7 +221,8 @@ fun MailboxScreen(
onExitSearchMode = { viewModel.submit(MailboxViewAction.ExitSearchMode) },
onOpenUpsellingPage = { viewModel.submit(MailboxViewAction.RequestUpsellingBottomSheet) },
onCloseUpsellingPage = { viewModel.submit(MailboxViewAction.DismissBottomSheet) },
onShowRatingBooster = { viewModel.submit(MailboxViewAction.ShowRatingBooster(context)) }
onShowRatingBooster = { viewModel.submit(MailboxViewAction.ShowRatingBooster(context)) },
onShowOnboardingUpselling = { viewModel.submit(MailboxViewAction.ShowOnboardingUpselling) }
)
mailboxState.bottomSheetState?.let {
@@ -333,6 +337,10 @@ fun MailboxScreen(
actions.onShowRatingBooster()
}
ConsumableLaunchedEffect(mailboxState.showOnboardingUpselling) {
actions.navigateToOnboardingUpselling()
}
DeleteDialog(state = mailboxState.deleteDialogState, actions.deleteConfirmed, actions.deleteDialogDismissed)
DeleteDialog(state = mailboxState.deleteAllDialogState, actions.deleteAllConfirmed, actions.deleteAllDismissed)
@@ -1023,7 +1031,9 @@ object MailboxScreen {
val onExitSearchMode: () -> Unit,
val onOpenUpsellingPage: () -> Unit,
val onCloseUpsellingPage: () -> Unit,
val onShowRatingBooster: () -> Unit
val onShowRatingBooster: () -> Unit,
val onShowOnboardingUpselling: () -> Unit,
val navigateToOnboardingUpselling: () -> Unit
) {
companion object {
@@ -1070,7 +1080,9 @@ object MailboxScreen {
onSearchResult = {},
onOpenUpsellingPage = {},
onCloseUpsellingPage = {},
onShowRatingBooster = {}
onShowRatingBooster = {},
onShowOnboardingUpselling = {},
navigateToOnboardingUpselling = {}
)
}
}
@@ -112,6 +112,8 @@ import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.Upsellin
import ch.protonmail.android.mailonboarding.presentation.model.OnboardingState
import ch.protonmail.android.mailsettings.domain.usecase.ObserveFolderColorSettings
import ch.protonmail.android.mailsettings.domain.usecase.ObserveSwipeActionsPreference
import ch.protonmail.android.mailupselling.domain.model.UpsellingEntryPoint
import ch.protonmail.android.mailupselling.presentation.usecase.ObserveUpsellingVisibility
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
@@ -189,7 +191,8 @@ class MailboxViewModel @Inject constructor(
private val shouldUpgradeStorage: ShouldUpgradeStorage,
private val shouldShowRatingBooster: ShouldShowRatingBooster,
private val showRatingBooster: ShowRatingBooster,
private val recordRatingBoosterTriggered: RecordRatingBoosterTriggered
private val recordRatingBoosterTriggered: RecordRatingBoosterTriggered,
private val observeUpsellingVisibility: ObserveUpsellingVisibility
) : ViewModel() {
private val primaryUserId = observePrimaryUserId()
@@ -202,7 +205,8 @@ class MailboxViewModel @Inject constructor(
init {
viewModelScope.launch {
if (shouldDisplayOnboarding()) {
emitNewStateFrom(MailboxEvent.ShowOnboarding)
val shouldShowUpselling = observeUpsellingVisibility(UpsellingEntryPoint.PostOnboarding).first()
emitNewStateFrom(MailboxEvent.ShowOnboarding(shouldShowUpselling))
}
}
@@ -376,6 +380,7 @@ class MailboxViewModel @Inject constructor(
is MailboxViewAction.RequestUpsellingBottomSheet -> showUpsellingBottomSheet(viewAction)
is MailboxViewAction.NavigateToInboxLabel -> selectedMailLabelId.set(MailLabelId.System.Inbox)
is MailboxViewAction.ShowRatingBooster -> showRatingBooster(viewAction)
is MailboxViewAction.ShowOnboardingUpselling -> { emitNewStateFrom(viewAction) }
}.exhaustive
}
}
@@ -965,6 +970,9 @@ class MailboxViewModel @Inject constructor(
private suspend fun handleCloseOnboarding() {
viewModelScope.launch {
saveOnboarding(display = false)
if (state.value.onboardingState is OnboardingState.Shown.UpsellingOn) {
emitNewStateFrom(MailboxViewAction.ShowOnboardingUpselling)
}
emitNewStateFrom(MailboxViewAction.CloseOnboarding)
}
}
@@ -1303,7 +1311,8 @@ class MailboxViewModel @Inject constructor(
bottomSheetState = null,
actionResult = Effect.empty(),
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
}
}
@@ -41,12 +41,14 @@ sealed interface MailboxListState {
val swipeActions: SwipeActionsUiModel?
val clearState: ClearState
val searchState: MailboxSearchState
val showOnboardingUpselling: Effect<Unit>
data class ViewMode(
override val currentMailLabel: MailLabel,
override val swipeActions: SwipeActionsUiModel?,
override val clearState: ClearState,
override val searchState: MailboxSearchState,
override val showOnboardingUpselling: Effect<Unit>,
val openItemEffect: Effect<OpenMailboxItemRequest>,
val scrollToMailboxTop: Effect<MailLabelId>,
val offlineEffect: Effect<Unit>,
@@ -62,6 +64,7 @@ sealed interface MailboxListState {
override val swipeActions: SwipeActionsUiModel?,
override val clearState: ClearState,
override val searchState: MailboxSearchState,
override val showOnboardingUpselling: Effect<Unit>,
val selectedMailboxItems: Set<SelectedMailboxItem>
) : Data {
@@ -35,6 +35,7 @@ import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxOpera
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxOperation.AffectingStorageLimit
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxOperation.AffectingTopAppBar
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxOperation.AffectingUnreadFilter
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxOperation.AffectingOnboardingUpselling
import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.BottomSheetOperation
import me.proton.core.domain.entity.UserId
import me.proton.core.label.domain.entity.LabelId
@@ -54,6 +55,7 @@ internal sealed interface MailboxOperation {
sealed interface AffectingErrorBar
sealed interface AffectingUpgradeStorage
sealed interface AffectingRatingBooster
sealed interface AffectingOnboardingUpselling
}
internal sealed interface MailboxViewAction : MailboxOperation {
@@ -147,6 +149,7 @@ internal sealed interface MailboxViewAction : MailboxOperation {
object NavigateToInboxLabel : MailboxViewAction
object RequestUpsellingBottomSheet : MailboxViewAction, AffectingBottomSheet
data class ShowRatingBooster(val context: Context) : MailboxViewAction
object ShowOnboardingUpselling : MailboxViewAction, AffectingOnboardingUpselling
}
internal sealed interface MailboxEvent : MailboxOperation {
@@ -181,7 +184,7 @@ internal sealed interface MailboxEvent : MailboxOperation {
val swipeActionsPreference: SwipeActionsUiModel
) : MailboxEvent, AffectingMailboxList
object ShowOnboarding : MailboxEvent, MailboxOperation.AffectingOnboarding
data class ShowOnboarding(val upsellingVisible: Boolean) : MailboxEvent, MailboxOperation.AffectingOnboarding
data class Trash(val numAffectedMessages: Int) :
MailboxEvent,
@@ -243,6 +246,7 @@ internal sealed interface MailboxEvent : MailboxOperation {
object ErrorMoving : MailboxEvent, AffectingErrorBar
object ErrorRetrievingDestinationMailFolders : MailboxEvent, AffectingErrorBar, AffectingBottomSheet
data object ShowRatingBooster : MailboxEvent, AffectingRatingBooster
object ShowOnboardingUpselling : MailboxEvent, AffectingOnboardingUpselling
}
@@ -41,5 +41,6 @@ data class MailboxState(
val bottomSheetState: BottomSheetState?,
val actionResult: Effect<ActionResult>,
val error: Effect<TextUiModel>,
val showRatingBooster: Effect<Unit>
val showRatingBooster: Effect<Unit>,
val showOnboardingUpselling: Effect<Unit>
)
@@ -56,7 +56,8 @@ object MailboxStateSampleData {
bottomSheetState = null,
storageLimitState = StorageLimitState.HasEnoughSpace,
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
val Inbox = MailboxState(
@@ -72,7 +73,8 @@ object MailboxStateSampleData {
end = SwipeUiModelSampleData.Archive
),
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
),
topAppBarState = MailboxTopAppBarState.Data.DefaultMode(
currentLabelName = MailLabel.System(MailLabelId.System.Inbox).text()
@@ -92,7 +94,8 @@ object MailboxStateSampleData {
bottomSheetState = null,
storageLimitState = StorageLimitState.HasEnoughSpace,
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
val AllMail = MailboxState(
@@ -108,7 +111,8 @@ object MailboxStateSampleData {
end = SwipeUiModelSampleData.Archive
),
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
),
topAppBarState = MailboxTopAppBarState.Data.DefaultMode(
currentLabelName = MailLabel.System(MailLabelId.System.AllMail).text()
@@ -128,7 +132,8 @@ object MailboxStateSampleData {
bottomSheetState = null,
storageLimitState = StorageLimitState.HasEnoughSpace,
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
val OnboardingShown = MailboxState(
@@ -137,14 +142,15 @@ object MailboxStateSampleData {
upgradeStorageState = UpgradeStorageState(notificationDotVisible = false),
unreadFilterState = UnreadFilterState.Loading,
bottomAppBarState = BottomBarState.Loading,
onboardingState = OnboardingState.Shown,
onboardingState = OnboardingState.Shown.UpsellingOff,
actionResult = Effect.empty(),
deleteDialogState = DeleteDialogState.Hidden,
deleteAllDialogState = DeleteDialogState.Hidden,
bottomSheetState = null,
storageLimitState = StorageLimitState.HasEnoughSpace,
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
fun createSelectionMode(
@@ -164,7 +170,8 @@ object MailboxStateSampleData {
end = SwipeUiModelSampleData.Archive
),
searchState = MailboxSearchState.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
),
topAppBarState = MailboxTopAppBarState.Data.SelectionMode(
currentLabelName = MailLabel.System(MailLabelId.System.Inbox).text(),
@@ -185,6 +192,7 @@ object MailboxStateSampleData {
bottomSheetState = bottomSheetState,
storageLimitState = StorageLimitState.HasEnoughSpace,
error = error,
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
}
@@ -163,7 +163,8 @@ class MailboxListReducer @Inject constructor() {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchState.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
)
is MailboxListState.Data.SelectionMode -> currentState.copy(
@@ -193,7 +194,8 @@ class MailboxListReducer @Inject constructor() {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchState.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
)
is MailboxListState.Data.ViewMode -> currentState.copy(
@@ -317,7 +319,8 @@ class MailboxListReducer @Inject constructor() {
selectedMailboxItems = setOf(SelectedMailboxItem(item.userId, item.id, item.isRead, item.showStar)),
swipeActions = currentState.swipeActions,
clearState = currentState.clearState,
searchState = currentState.searchState
searchState = currentState.searchState,
showOnboardingUpselling = currentState.showOnboardingUpselling
)
else -> currentState
@@ -333,7 +336,8 @@ class MailboxListReducer @Inject constructor() {
refreshRequested = false,
swipeActions = currentState.swipeActions,
searchState = currentState.searchState,
clearState = currentState.clearState
clearState = currentState.clearState,
showOnboardingUpselling = currentState.showOnboardingUpselling
)
else -> currentState
@@ -19,6 +19,7 @@
package ch.protonmail.android.mailmailbox.presentation.mailbox.reducer
import ch.protonmail.android.mailcommon.presentation.Effect
import ch.protonmail.android.mailcommon.presentation.model.ActionResult
import ch.protonmail.android.mailcommon.presentation.model.BottomBarEvent
import ch.protonmail.android.mailcommon.presentation.model.BottomBarState
import ch.protonmail.android.mailcommon.presentation.model.TextUiModel
@@ -26,14 +27,12 @@ import ch.protonmail.android.mailcommon.presentation.reducer.BottomBarReducer
import ch.protonmail.android.mailcommon.presentation.ui.delete.DeleteDialogState
import ch.protonmail.android.maillabel.domain.model.SystemLabelId
import ch.protonmail.android.mailmailbox.presentation.R
import ch.protonmail.android.mailcommon.presentation.model.ActionResult
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxEvent
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxListState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxOperation
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxTopAppBarState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxViewAction
import ch.protonmail.android.mailonboarding.presentation.model.OnboardingState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.StorageLimitState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.UnreadFilterState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.UpgradeStorageState
@@ -43,6 +42,7 @@ import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.LabelAsB
import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.MoveToBottomSheetState.MoveToBottomSheetAction.MoveToDestinationSelected
import ch.protonmail.android.mailmessage.presentation.reducer.BottomSheetReducer
import ch.protonmail.android.mailonboarding.presentation.model.OnboardingOperation
import ch.protonmail.android.mailonboarding.presentation.model.OnboardingState
import ch.protonmail.android.mailonboarding.presentation.reducer.OnboardingReducer
import me.proton.core.mailsettings.domain.entity.ViewMode
import javax.inject.Inject
@@ -74,7 +74,8 @@ class MailboxReducer @Inject constructor(
bottomSheetState = currentState.toNewBottomSheetState(operation),
actionResult = currentState.toNewActionMessageStateFrom(operation),
error = currentState.toNewErrorBarState(operation),
showRatingBooster = currentState.toNewShowRatingBoosterState(operation)
showRatingBooster = currentState.toNewShowRatingBoosterState(operation),
showOnboardingUpselling = currentState.toNewShowOnboardingUpsellingState(operation)
)
private fun MailboxState.toNewMailboxListStateFrom(operation: MailboxOperation): MailboxListState {
@@ -140,7 +141,9 @@ class MailboxReducer @Inject constructor(
return if (operation is MailboxOperation.AffectingOnboarding) {
val onboardingOperation = when (operation) {
MailboxViewAction.CloseOnboarding -> OnboardingOperation.Action.CloseOnboarding
MailboxEvent.ShowOnboarding -> OnboardingOperation.Event.ShowOnboarding
is MailboxEvent.ShowOnboarding -> if (operation.upsellingVisible) {
OnboardingOperation.Event.ShowOnboarding.UpsellingOn
} else OnboardingOperation.Event.ShowOnboarding.UpsellingOff
}
onboardingReducer.newStateFrom(onboardingOperation)
} else {
@@ -250,4 +253,12 @@ class MailboxReducer @Inject constructor(
showRatingBooster
}
}
private fun MailboxState.toNewShowOnboardingUpsellingState(operation: MailboxOperation): Effect<Unit> {
return if (operation is MailboxOperation.AffectingOnboardingUpselling) {
Effect.of(Unit)
} else {
showOnboardingUpselling
}
}
}
@@ -77,9 +77,9 @@ import ch.protonmail.android.mailmailbox.domain.usecase.ObserveOnboarding
import ch.protonmail.android.mailmailbox.domain.usecase.ObservePrimaryUserAccountStorageStatus
import ch.protonmail.android.mailmailbox.domain.usecase.ObserveStorageLimitPreference
import ch.protonmail.android.mailmailbox.domain.usecase.ObserveUnreadCounters
import ch.protonmail.android.mailmailbox.domain.usecase.RecordRatingBoosterTriggered
import ch.protonmail.android.mailmailbox.domain.usecase.RelabelConversations
import ch.protonmail.android.mailmailbox.domain.usecase.RelabelMessages
import ch.protonmail.android.mailmailbox.domain.usecase.RecordRatingBoosterTriggered
import ch.protonmail.android.mailmailbox.domain.usecase.SaveOnboarding
import ch.protonmail.android.mailmailbox.domain.usecase.SaveStorageLimitPreference
import ch.protonmail.android.mailmailbox.domain.usecase.ShouldShowRatingBooster
@@ -94,7 +94,6 @@ import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxOpera
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxTopAppBarState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.MailboxViewAction
import ch.protonmail.android.mailonboarding.presentation.model.OnboardingState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.StorageLimitState
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.SwipeActionsUiModel
import ch.protonmail.android.mailmailbox.presentation.mailbox.model.UnreadFilterState
@@ -124,10 +123,12 @@ import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.LabelAsB
import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.MailboxMoreActionsBottomSheetState
import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.MoveToBottomSheetState
import ch.protonmail.android.mailmessage.presentation.model.bottomsheet.UpsellingBottomSheetState
import ch.protonmail.android.mailonboarding.presentation.model.OnboardingState
import ch.protonmail.android.mailsettings.domain.model.FolderColorSettings
import ch.protonmail.android.mailsettings.domain.model.SwipeActionsPreference
import ch.protonmail.android.mailsettings.domain.usecase.ObserveFolderColorSettings
import ch.protonmail.android.mailsettings.domain.usecase.ObserveSwipeActionsPreference
import ch.protonmail.android.mailupselling.presentation.usecase.ObserveUpsellingVisibility
import ch.protonmail.android.testdata.contact.ContactTestData
import ch.protonmail.android.testdata.label.LabelTestData
import ch.protonmail.android.testdata.mailbox.MailboxItemUiModelTestData.buildMailboxUiModelItem
@@ -290,6 +291,9 @@ class MailboxViewModelTest {
}
private val showRatingBooster = mockk<ShowRatingBooster>(relaxUnitFun = true)
private val recordRatingBoosterTriggered = mockk<RecordRatingBoosterTriggered>(relaxUnitFun = true)
private val observeUpsellingVisibility = mockk<ObserveUpsellingVisibility> {
every { this@mockk.invoke(any()) } returns flowOf(false)
}
private val mailboxViewModel by lazy {
MailboxViewModel(
@@ -337,7 +341,8 @@ class MailboxViewModelTest {
shouldUpgradeStorage = shouldUpgradeStorage,
shouldShowRatingBooster = shouldShowRatingBooster,
showRatingBooster = showRatingBooster,
recordRatingBoosterTriggered = recordRatingBoosterTriggered
recordRatingBoosterTriggered = recordRatingBoosterTriggered,
observeUpsellingVisibility = observeUpsellingVisibility
)
}
@@ -377,7 +382,8 @@ class MailboxViewModelTest {
bottomSheetState = null,
actionResult = Effect.empty(),
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
assertEquals(expected, actual)
@@ -700,6 +706,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -717,6 +724,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = expectedSwipeActions,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -730,6 +738,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = expectedSwipeActions,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Visible.Button(TextUiModel("Clear All"))
)
)
@@ -786,6 +795,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -1352,6 +1362,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -1383,6 +1394,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -1414,6 +1426,7 @@ class MailboxViewModelTest {
refreshRequested = true,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -3607,6 +3620,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
unreadFilterState = UnreadFilterState.Data(
@@ -3616,27 +3630,63 @@ class MailboxViewModelTest {
)
}
@Suppress("MaxLineLength")
@Test
fun `onboarding state should be shown when repository emits the preference with value true`() = runTest {
// When
coEvery {
observeOnboarding()
} returns flowOf(OnboardingPreference(display = true).right())
every {
mailboxReducer.newStateFrom(
any(),
any()
)
} returns MailboxStateSampleData.OnboardingShown
fun `onboarding state should be shown with upselling OFF when repository emits the preference with value true`() {
runTest {
// When
coEvery {
observeOnboarding()
} returns flowOf(OnboardingPreference(display = true).right())
every {
mailboxReducer.newStateFrom(
any(),
any()
)
} returns MailboxStateSampleData.OnboardingShown
val expectedUpsellingVisible = false
expectObserveUpsellingVisibility(expectedUpsellingVisible)
mailboxViewModel.state.test {
val currentState = awaitItem()
mailboxViewModel.state.test {
val currentState = awaitItem()
// Then
verify(exactly = 1) {
mailboxReducer.newStateFrom(any(), MailboxEvent.ShowOnboarding)
// Then
verify(exactly = 1) {
mailboxReducer.newStateFrom(any(), MailboxEvent.ShowOnboarding(expectedUpsellingVisible))
}
assertEquals(OnboardingState.Shown.UpsellingOff, currentState.onboardingState)
}
}
}
@Suppress("MaxLineLength")
@Test
fun `onboarding state should be shown with upselling ON when repository emits the preference with value true`() {
runTest {
// When
coEvery {
observeOnboarding()
} returns flowOf(OnboardingPreference(display = true).right())
every {
mailboxReducer.newStateFrom(
any(),
any()
)
} returns MailboxStateSampleData.OnboardingShown.copy(
onboardingState = OnboardingState.Shown.UpsellingOn
)
val expectedUpsellingVisible = true
expectObserveUpsellingVisibility(expectedUpsellingVisible)
mailboxViewModel.state.test {
val currentState = awaitItem()
// Then
verify(exactly = 1) {
mailboxReducer.newStateFrom(any(), MailboxEvent.ShowOnboarding(expectedUpsellingVisible))
}
assertEquals(OnboardingState.Shown.UpsellingOn, currentState.onboardingState)
}
assertEquals(OnboardingState.Shown, currentState.onboardingState)
}
}
@@ -3673,6 +3723,40 @@ class MailboxViewModelTest {
}
}
@Test
fun `should emit ShowOnboardingUpselling when closing onboarding`() = runTest {
// Given
coEvery {
saveOnboarding(display = false)
} returns Unit.right()
val initialMailboxState = createMailboxDataState().copy(
onboardingState = OnboardingState.Shown.UpsellingOn
)
val expectedState = initialMailboxState.copy(
showOnboardingUpselling = Effect.of(Unit)
)
every { mailboxReducer.newStateFrom(any(), any()) } returns initialMailboxState
every {
mailboxReducer.newStateFrom(
initialMailboxState,
MailboxViewAction.CloseOnboarding
)
} returns expectedState
// When
mailboxViewModel.submit(MailboxViewAction.CloseOnboarding)
// When
mailboxViewModel.state.test {
// Then
val actual = awaitItem()
assertEquals(Effect.of(Unit), actual.showOnboardingUpselling)
}
}
@Test
fun `when enter search mode action is submitted, search mode is updated and emitted`() = runTest {
// Given
@@ -3686,6 +3770,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NewSearch,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -3718,6 +3803,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -3753,6 +3839,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.SearchLoading,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -3785,6 +3872,7 @@ class MailboxViewModelTest {
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.SearchData,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
)
@@ -3921,6 +4009,36 @@ class MailboxViewModelTest {
verify { showRatingBooster(context) }
}
@Test
fun `should create new state for showing the Onboarding Upselling when it should be shown`() = runTest {
// Given
val initialMailboxState = createMailboxDataState()
val expectedState = initialMailboxState.copy(
showOnboardingUpselling = Effect.of(Unit)
)
every { mailboxReducer.newStateFrom(any(), any()) } returns initialMailboxState
every {
mailboxReducer.newStateFrom(
initialMailboxState,
MailboxViewAction.ShowOnboardingUpselling
)
} returns expectedState
// When
mailboxViewModel.state.test {
// Then
awaitItem()
mailboxViewModel.submit(MailboxViewAction.ShowOnboardingUpselling)
val actual = awaitItem()
assertEquals(Effect.of(Unit), actual.showOnboardingUpselling)
}
}
private fun returnExpectedStateForBottomBarEvent(
intermediateState: MailboxState? = null,
expectedState: MailboxState
@@ -4243,6 +4361,10 @@ class MailboxViewModelTest {
} returns initialState
}
private fun expectObserveUpsellingVisibility(visible: Boolean) {
every { observeUpsellingVisibility.invoke(any()) } returns flowOf(visible)
}
private fun expectUpsellingBottomSheetDataLoaded(initialState: MailboxState) {
every {
mailboxReducer.newStateFrom(
@@ -71,6 +71,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
private val listStateWithSearchModeNewSearch = listStateWithSearchModeNone.copy(
@@ -95,6 +96,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.SearchData,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
@@ -122,6 +124,7 @@ internal class MailboxListReducerTest(
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -137,6 +140,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -152,6 +156,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -167,6 +172,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -182,6 +188,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -197,6 +204,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -264,6 +272,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.SelectedLabelChanged(MailLabelTestData.customLabelTwo),
@@ -276,6 +285,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -289,6 +299,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ClearAllOperationStatus(false),
@@ -301,6 +312,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Visible.Button(
text = TextUiModel(R.string.mailbox_action_button_clear_trash)
)
@@ -316,6 +328,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ClearAllOperationStatus(true),
@@ -328,6 +341,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Visible.Banner
)
),
@@ -341,6 +355,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ClearAllOperationStatus(false),
@@ -353,6 +368,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Visible.Button(
text = TextUiModel(R.string.mailbox_action_button_clear_spam)
)
@@ -368,6 +384,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ClearAllOperationStatus(true),
@@ -380,6 +397,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Visible.Banner
)
),
@@ -393,6 +411,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.NewLabelSelected(MailLabelTestData.customLabelTwo, UNREAD_COUNT),
@@ -405,6 +424,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -418,6 +438,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ItemClicked.ItemDetailsOpenedInViewMode(
@@ -440,6 +461,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -453,6 +475,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ItemClicked.ItemDetailsOpenedInViewMode(
@@ -474,6 +497,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -487,6 +511,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ItemClicked.OpenComposer(
@@ -507,6 +532,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -520,6 +546,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ItemClicked.OpenComposer(
@@ -540,6 +567,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -553,6 +581,7 @@ internal class MailboxListReducerTest(
refreshRequested = true,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.OnOfflineWithData,
@@ -565,6 +594,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -578,6 +608,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.OnOfflineWithData,
@@ -590,6 +621,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -603,6 +635,7 @@ internal class MailboxListReducerTest(
refreshRequested = true,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.OnErrorWithData,
@@ -615,6 +648,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -628,6 +662,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.OnErrorWithData,
@@ -640,6 +675,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -653,6 +689,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.Refresh,
@@ -665,6 +702,7 @@ internal class MailboxListReducerTest(
refreshRequested = true,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -678,6 +716,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.EnterSelectionMode(MailboxItemUiModelTestData.readMailboxItemUiModel),
@@ -693,6 +732,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -709,6 +749,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.ExitSelectionMode,
@@ -721,6 +762,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -737,6 +779,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ItemClicked.ItemAddedToSelection(
@@ -760,6 +803,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -776,6 +820,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ItemClicked.ItemRemovedFromSelection(
@@ -786,6 +831,7 @@ internal class MailboxListReducerTest(
selectedMailboxItems = setOf(),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -802,6 +848,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.MarkAsRead,
@@ -817,6 +864,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -833,6 +881,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.MarkAsUnread,
@@ -848,6 +897,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -864,6 +914,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.Trash(5),
@@ -876,6 +927,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -892,6 +944,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.DeleteConfirmed(ViewMode.ConversationGrouping, 5),
@@ -904,6 +957,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -920,6 +974,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.DeleteConfirmed(ViewMode.NoConversationGrouping, 5),
@@ -932,6 +987,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -948,6 +1004,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.ItemsRemovedFromSelection(
@@ -958,6 +1015,7 @@ internal class MailboxListReducerTest(
selectedMailboxItems = emptySet(),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -974,6 +1032,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.MoveToConfirmed,
@@ -986,6 +1045,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -1002,6 +1062,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.Star,
@@ -1017,6 +1078,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -1033,6 +1095,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.Star,
@@ -1048,6 +1111,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -1064,6 +1128,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.UnStar,
@@ -1079,6 +1144,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -1095,6 +1161,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.MoveToArchive,
@@ -1107,6 +1174,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -1123,6 +1191,7 @@ internal class MailboxListReducerTest(
),
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxViewAction.MoveToSpam,
@@ -1135,6 +1204,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -1148,6 +1218,7 @@ internal class MailboxListReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
),
operation = MailboxEvent.SwipeActionsChanged(
@@ -1168,6 +1239,7 @@ internal class MailboxListReducerTest(
end = SwipeUiModelSampleData.Archive
),
searchState = MailboxSearchStateSampleData.NotSearching,
showOnboardingUpselling = Effect.empty(),
clearState = MailboxListState.Data.ClearState.Hidden
)
),
@@ -194,7 +194,8 @@ internal class MailboxReducerTest(
refreshRequested = false,
swipeActions = null,
searchState = MailboxSearchStateSampleData.NotSearching,
clearState = MailboxListState.Data.ClearState.Hidden
clearState = MailboxListState.Data.ClearState.Hidden,
showOnboardingUpselling = Effect.empty()
),
topAppBarState = MailboxTopAppBarState.Data.DefaultMode(
currentLabelName = spamLabel.text()
@@ -212,7 +213,8 @@ internal class MailboxReducerTest(
bottomSheetState = null,
storageLimitState = StorageLimitState.None,
error = Effect.empty(),
showRatingBooster = Effect.empty()
showRatingBooster = Effect.empty(),
showOnboardingUpselling = Effect.empty()
)
private val actions = listOf(
@@ -468,6 +470,18 @@ internal class MailboxReducerTest(
shouldReduceDeleteDialog = false,
shouldReduceBottomSheetState = true,
shouldReduceStorageLimitState = false
),
TestInput(
MailboxViewAction.ShowOnboardingUpselling,
shouldReduceMailboxListState = false,
shouldReduceTopAppBarState = false,
shouldReduceUnreadFilterState = false,
shouldReduceBottomAppBarState = false,
shouldReduceActionMessage = false,
shouldReduceDeleteDialog = false,
shouldReduceBottomSheetState = false,
shouldReduceStorageLimitState = false,
showOnboardingUpsellingState = Effect.of(Unit)
)
)
@@ -773,6 +787,18 @@ internal class MailboxReducerTest(
shouldReduceBottomSheetState = false,
shouldReduceStorageLimitState = false,
showRatingBoosterState = Effect.of(Unit)
),
TestInput(
MailboxEvent.ShowOnboardingUpselling,
shouldReduceMailboxListState = false,
shouldReduceTopAppBarState = false,
shouldReduceUnreadFilterState = false,
shouldReduceBottomAppBarState = false,
shouldReduceActionMessage = false,
shouldReduceDeleteDialog = false,
shouldReduceBottomSheetState = false,
shouldReduceStorageLimitState = false,
showOnboardingUpsellingState = Effect.of(Unit)
)
)
@@ -802,6 +828,7 @@ internal class MailboxReducerTest(
val shouldReduceBottomSheetState: Boolean,
val shouldReduceStorageLimitState: Boolean,
val errorBarState: Effect<TextUiModel> = Effect.empty(),
val showRatingBoosterState: Effect<Unit> = Effect.empty()
val showRatingBoosterState: Effect<Unit> = Effect.empty(),
val showOnboardingUpsellingState: Effect<Unit> = Effect.empty()
)
}
@@ -48,8 +48,13 @@ internal class OnboardingReducerTest(
private val transitionsFromHiddenState = listOf(
TestInput(
currentState = OnboardingState.Hidden,
operation = Event.ShowOnboarding,
expectedState = OnboardingState.Shown
operation = Event.ShowOnboarding.UpsellingOn,
expectedState = OnboardingState.Shown.UpsellingOn
),
TestInput(
currentState = OnboardingState.Hidden,
operation = Event.ShowOnboarding.UpsellingOff,
expectedState = OnboardingState.Shown.UpsellingOff
),
TestInput(
currentState = OnboardingState.Hidden,
@@ -60,14 +65,34 @@ internal class OnboardingReducerTest(
private val transitionsFromShownState = listOf(
TestInput(
currentState = OnboardingState.Shown,
currentState = OnboardingState.Shown.UpsellingOn,
operation = Action.CloseOnboarding,
expectedState = OnboardingState.Hidden
),
TestInput(
currentState = OnboardingState.Shown,
operation = Event.ShowOnboarding,
expectedState = OnboardingState.Shown
currentState = OnboardingState.Shown.UpsellingOff,
operation = Action.CloseOnboarding,
expectedState = OnboardingState.Hidden
),
TestInput(
currentState = OnboardingState.Shown.UpsellingOn,
operation = Event.ShowOnboarding.UpsellingOn,
expectedState = OnboardingState.Shown.UpsellingOn
),
TestInput(
currentState = OnboardingState.Shown.UpsellingOn,
operation = Event.ShowOnboarding.UpsellingOff,
expectedState = OnboardingState.Shown.UpsellingOff
),
TestInput(
currentState = OnboardingState.Shown.UpsellingOff,
operation = Event.ShowOnboarding.UpsellingOn,
expectedState = OnboardingState.Shown.UpsellingOn
),
TestInput(
currentState = OnboardingState.Shown.UpsellingOff,
operation = Event.ShowOnboarding.UpsellingOff,
expectedState = OnboardingState.Shown.UpsellingOff
)
)
@@ -44,7 +44,13 @@ import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -56,6 +62,8 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import ch.protonmail.android.mailcommon.presentation.compose.MailDimens
import ch.protonmail.android.mailonboarding.presentation.model.OnboardingUiModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.proton.core.compose.component.ProtonSolidButton
import me.proton.core.compose.theme.ProtonDimens
@@ -65,8 +73,8 @@ import me.proton.core.compose.theme.headlineNorm
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun OnboardingScreen(onCloseOnboarding: () -> Unit) {
val contentMap = listOf(
fun OnboardingScreen(shouldShowUpselling: Boolean, onCloseOnboarding: () -> Unit) {
val contentMap = listOfNotNull(
OnboardingUiModel(
illustrationId = R.drawable.illustration_onboarding_ga,
headlineId = R.string.onboarding_headline_ga,
@@ -86,11 +94,40 @@ fun OnboardingScreen(onCloseOnboarding: () -> Unit) {
illustrationId = R.drawable.illustration_neat_and_tidy,
headlineId = R.string.onboarding_neat_and_tidy_headline,
descriptionId = R.string.onboarding_neat_and_tidy_description
)
),
if (shouldShowUpselling) {
OnboardingUiModel(
illustrationId = R.drawable.empty,
headlineId = R.string.empty,
descriptionId = R.string.empty
)
} else null
)
val viewCount = contentMap.size
val pagerState = rememberPagerState(pageCount = { viewCount })
var isSwipingToUpsellingPage by remember { mutableStateOf(false) }
LaunchedEffect(pagerState) {
snapshotFlow { Pair(pagerState.currentPage, pagerState.targetPage) }
.distinctUntilChanged()
.map { (currentPage, targetPage) ->
val fromPage = currentPage + 1
val toPage = targetPage + 1
// return true if we're showing upselling and are about to swipe to last page
shouldShowUpselling && fromPage == viewCount - 1 && toPage == viewCount
}
.collect { isSwipingToUpsellingPage = it }
}
LaunchedEffect(isSwipingToUpsellingPage) {
if (isSwipingToUpsellingPage) {
onCloseOnboarding()
}
}
Column(
modifier = Modifier
.testTag(OnboardingScreenTestTags.RootItem)
@@ -244,6 +281,7 @@ fun OnboardingIndexDots(pagerState: PagerState, viewCount: Int) {
private fun OnboardingScreenPreview() {
ProtonTheme {
OnboardingScreen(
shouldShowUpselling = true,
onCloseOnboarding = {}
)
}
@@ -24,6 +24,9 @@ sealed interface OnboardingOperation {
}
sealed interface Event : OnboardingOperation {
data object ShowOnboarding : Event
sealed interface ShowOnboarding : Event {
data object UpsellingOn : ShowOnboarding
data object UpsellingOff : ShowOnboarding
}
}
}
@@ -20,7 +20,12 @@ package ch.protonmail.android.mailonboarding.presentation.model
sealed interface OnboardingState {
object Hidden : OnboardingState
data object Hidden : OnboardingState
object Shown : OnboardingState
sealed interface Shown : OnboardingState {
data object UpsellingOn : Shown
data object UpsellingOff : Shown
}
}
@@ -26,7 +26,8 @@ class OnboardingReducer @Inject constructor() {
fun newStateFrom(operation: OnboardingOperation): OnboardingState {
return when (operation) {
is OnboardingOperation.Event.ShowOnboarding -> OnboardingState.Shown
is OnboardingOperation.Event.ShowOnboarding.UpsellingOn -> OnboardingState.Shown.UpsellingOn
is OnboardingOperation.Event.ShowOnboarding.UpsellingOff -> OnboardingState.Shown.UpsellingOff
is OnboardingOperation.Action.CloseOnboarding -> OnboardingState.Hidden
}
}
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="1"
android:viewportHeight="1"
android:width="1dp"
android:height="1dp">
<path
android:fillColor="#00FFFFFF"
android:pathData=""/>
</vector>
@@ -34,4 +34,6 @@
<string name="onboarding_get_started">Get started</string>
<string name="onboarding_illustration_content_description">Onboarding illustration</string>
<string name="onboarding_close_content_description">Close</string>
<string name="empty" translatable="false"></string>
</resources>