diff --git a/app/feature/feature-home/build.gradle.kts b/app/feature/feature-home/build.gradle.kts index 83bb01edf9..8798902f73 100644 --- a/app/feature/feature-home/build.gradle.kts +++ b/app/feature/feature-home/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation(projects.coreDemoMode) implementation(projects.coreMarkdown) implementation(projects.coreResources) + implementation(projects.coreUiData) implementation(projects.crossSells) implementation(projects.dataAddons) implementation(projects.dataClaimIntent) diff --git a/app/feature/feature-home/src/main/graphql/QueryHome.graphql b/app/feature/feature-home/src/main/graphql/QueryHome.graphql index 0d29fa7dd7..4b94f35902 100644 --- a/app/feature/feature-home/src/main/graphql/QueryHome.graphql +++ b/app/feature/feature-home/src/main/graphql/QueryHome.graphql @@ -80,6 +80,21 @@ query Home($claimsHistoryFlag: Boolean!, $resumeClaimEnabled: Boolean!) { } } } + ongoingShopSessions { + id + display { + title + subtitle + monthlyNet { + amount + currencyCode + } + resumeUrl + pillowImage { + src + } + } + } memberActions { firstVetAction { sections { diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt index e884924629..48351c1588 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt @@ -12,6 +12,8 @@ import com.apollographql.apollo.cache.normalized.FetchPolicy import com.apollographql.apollo.cache.normalized.fetchPolicy import com.hedvig.android.apollo.ApolloOperationError import com.hedvig.android.apollo.safeFlow +import com.hedvig.android.core.uidata.UiCurrencyCode +import com.hedvig.android.core.uidata.UiMoney import com.hedvig.android.crosssells.BundleProgress import com.hedvig.android.crosssells.CrossSellSheetData import com.hedvig.android.crosssells.RecommendedAddon @@ -67,8 +69,12 @@ internal class GetHomeDataUseCaseImpl( ) : GetHomeDataUseCase { @OptIn(ExperimentalCoroutinesApi::class) override fun invoke(forceNetworkFetch: Boolean): Flow> { - return featureManager.isFeatureEnabled(Feature.ENABLE_CLAIM_INTENT_RESUME) - .flatMapLatest { resumeClaimEnabled -> + return combine( + featureManager.isFeatureEnabled(Feature.ENABLE_CLAIM_INTENT_RESUME), + featureManager.isFeatureEnabled(Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS), + ::Pair, + ) + .flatMapLatest { (resumeClaimEnabled, disableShopSessions) -> combine( apolloClient.query(HomeQuery(true, resumeClaimEnabled)) .fetchPolicy(if (forceNetworkFetch) FetchPolicy.NetworkOnly else FetchPolicy.CacheAndNetwork) @@ -148,6 +154,22 @@ internal class GetHomeDataUseCaseImpl( val otherCrossSellsData = crossSellsData.otherCrossSells.map { it.toCrossSell() } + val ongoingShopSessions = if (disableShopSessions) { + emptyList() + } else { + homeQueryData.currentMember.ongoingShopSessions.map { session -> + OngoingShopSession( + id = session.id, + title = session.display.title, + subtitle = session.display.subtitle, + monthlyNet = session.display.monthlyNet?.let { + UiMoney(it.amount, UiCurrencyCode.fromCurrencyCode(it.currencyCode)) + }, + resumeUrl = session.display.resumeUrl, + pillowImageUrl = session.display.pillowImage?.src, + ) + } + } val recommendedAddon = crossSellsData.recommendedAddon?.let { RecommendedAddon( id = it.id, @@ -202,6 +224,7 @@ internal class GetHomeDataUseCaseImpl( showHelpCenter = true, firstVetSections = firstVetActions, crossSells = crossSells, + ongoingShopSessions = ongoingShopSessions, addonBannerInfos = travelBannerInfo.orEmpty(), showChatIcon = showChatIcon, firstName = homeQueryData.currentMember.firstName, @@ -310,6 +333,15 @@ private fun HomeQuery.Data.claimStatusCards(): HomeData.ClaimStatusCardsData? { ) } +data class OngoingShopSession( + val id: String, + val title: String, + val subtitle: String?, + val monthlyNet: UiMoney?, + val resumeUrl: String, + val pillowImageUrl: String?, +) + data class HomeData( val contractStatus: ContractStatus, val claimStatusCardsData: ClaimStatusCardsData?, @@ -321,6 +353,8 @@ data class HomeData( val firstVetSections: List, val crossSells: CrossSellSheetData, val addonBannerInfos: List, + // Defaulted only so test/demo construction sites stay terse. + val ongoingShopSessions: List = emptyList(), // Always populated from the backend; defaulted only so test/demo construction sites stay terse. val firstName: String = "", val draftClaim: DraftClaim?, diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCaseDemo.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCaseDemo.kt index c445223692..989070d798 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCaseDemo.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCaseDemo.kt @@ -3,6 +3,8 @@ package com.hedvig.android.feature.home.home.data import arrow.core.Either import arrow.core.right import com.hedvig.android.apollo.ApolloOperationError +import com.hedvig.android.core.uidata.UiCurrencyCode +import com.hedvig.android.core.uidata.UiMoney import com.hedvig.android.crosssells.CrossSellSheetData import com.hedvig.android.crosssells.RecommendedCrossSell import com.hedvig.android.data.contract.CrossSell @@ -55,6 +57,16 @@ internal class GetHomeDataUseCaseDemo : GetHomeDataUseCase { ), recommendedAddon = null, ), + ongoingShopSessions = listOf( + OngoingShopSession( + id = "demo-session-1", + title = "Home + Accident", + subtitle = "Studio apartment, Stockholm", + monthlyNet = UiMoney(199.0, UiCurrencyCode.SEK), + resumeUrl = "https://www.hedvig.com", + pillowImageUrl = null, + ), + ), addonBannerInfos = emptyList(), showChatIcon = false, firstName = "Demo", diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt index 50a27a9cf7..3d23045144 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt @@ -145,6 +145,7 @@ import com.hedvig.android.feature.home.home.data.HomeData.ClaimStatusCardsData import com.hedvig.android.feature.home.home.data.HomeData.DraftClaim import com.hedvig.android.feature.home.home.data.HomeData.VeryImportantMessage import com.hedvig.android.feature.home.home.data.HomeData.VeryImportantMessage.LinkInfo +import com.hedvig.android.feature.home.home.data.OngoingShopSession import com.hedvig.android.feature.home.home.ui.HomeText.Active import com.hedvig.android.feature.home.home.ui.HomeTopBarAction.ChatAction import com.hedvig.android.feature.home.home.ui.HomeTopBarAction.CrossSellsAction @@ -205,6 +206,7 @@ import hedvig.resources.Res import hedvig.resources.TOAST_NEW_OFFER import hedvig.resources.blur_background import hedvig.resources.general_cancel_button +import hedvig.resources.general_continue_button import hedvig.resources.home_tab_claim_button_text import hedvig.resources.home_tab_get_help import hedvig.resources.home_tab_welcome_title_without_name @@ -660,8 +662,8 @@ private fun HomeScreenSuccess( applicableReminders.homeActionRequiredReminders().isNotEmpty() } - HomeSection.Offers -> { - uiState.crossSellsPartition.offersCrossSell != null + HomeSection.Quotes -> { + uiState.ongoingShopSessions.isNotEmpty() } HomeSection.DiscoverInsurances -> { @@ -888,10 +890,10 @@ private fun HomeScreenSuccess( horizontalInsets = horizontalInsets, ) - HomeSection.Offers -> uiState.crossSellsPartition.offersCrossSell?.let { recommended -> - OffersSection( - recommendedCrossSell = recommended, - onCrossSellClick = openCrossSellUrl, + HomeSection.Quotes -> uiState.ongoingShopSessions.takeIf { it.isNotEmpty() }?.let { sessions -> + QuotesSection( + sessions = sessions, + onResumeClick = openUrl, imageLoader = imageLoader, horizontalInsets = horizontalInsets, ) @@ -971,7 +973,7 @@ private enum class HomeSection { ClaimStatusCards, VeryImportantMessages, MemberReminders, - Offers, + Quotes, DiscoverInsurances, Addons, QuickActionTiles, @@ -984,7 +986,7 @@ private val homeSectionOrder: List = listOf( HomeSection.ClaimStatusCards, HomeSection.VeryImportantMessages, HomeSection.MemberReminders, - HomeSection.Offers, + HomeSection.Quotes, HomeSection.QuickActionTiles, HomeSection.DiscoverInsurances, HomeSection.Addons, @@ -1099,13 +1101,12 @@ private fun MemberRemindersSection( } @Composable -private fun OffersSection( - recommendedCrossSell: RecommendedCrossSell, - onCrossSellClick: (String) -> Unit, +private fun QuotesSection( + sessions: List, + onResumeClick: (String) -> Unit, imageLoader: ImageLoader, horizontalInsets: PaddingValues, ) { - val crossSell = recommendedCrossSell.crossSell Column( verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier @@ -1118,52 +1119,50 @@ private fun OffersSection( style = HedvigTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() }, ) - HedvigCard( - onClick = { onCrossSellClick(crossSell.storeUrl) }, - color = HedvigTheme.colorScheme.fillNegative, - borderColor = HedvigTheme.colorScheme.borderPrimary, - modifier = Modifier - .fillMaxWidth() - .hedvigDropShadow(HedvigTheme.shapes.cornerXLarge), - ) { - Column(Modifier.padding(16.dp)) { - Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage( - model = crossSell.pillowImage.src, - contentDescription = null, - imageLoader = imageLoader, - contentScale = ContentScale.Fit, - modifier = Modifier.size(48.dp), - ) - Spacer(Modifier.width(12.dp)) - Column(Modifier.weight(1f)) { - HedvigText(text = crossSell.title, style = HedvigTheme.typography.bodySmall) - HedvigText( - text = recommendedCrossSell.bannerText, - style = HedvigTheme.typography.label, - color = HedvigTheme.colorScheme.textSecondary, - ) - } - val discountText = recommendedCrossSell.discountText - if (discountText != null) { - Spacer(Modifier.width(8.dp)) - HighlightLabel( - labelText = discountText, - size = HighlightLabelDefaults.HighLightSize.Small, - color = HighlightLabelDefaults.HighlightColor.Green(HighlightLabelDefaults.HighlightShade.LIGHT), - ) + for (session in sessions) { + HedvigCard( + onClick = { onResumeClick(session.resumeUrl) }, + color = HedvigTheme.colorScheme.fillNegative, + borderColor = HedvigTheme.colorScheme.borderPrimary, + modifier = Modifier + .fillMaxWidth() + .hedvigDropShadow(HedvigTheme.shapes.cornerXLarge), + ) { + Column(Modifier.padding(16.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { + if (session.pillowImageUrl != null) { + AsyncImage( + model = session.pillowImageUrl, + contentDescription = null, + imageLoader = imageLoader, + contentScale = ContentScale.Fit, + modifier = Modifier.size(48.dp), + ) + Spacer(Modifier.width(12.dp)) + } + Column(Modifier.weight(1f)) { + HedvigText(text = session.title, style = HedvigTheme.typography.bodySmall) + val secondary = session.subtitle ?: session.monthlyNet?.toString() + if (secondary != null) { + HedvigText( + text = secondary, + style = HedvigTheme.typography.label, + color = HedvigTheme.colorScheme.textSecondary, + ) + } + } } + Spacer(Modifier.height(12.dp)) + HedvigButton( + text = stringResource(Res.string.general_continue_button), + onClick = { onResumeClick(session.resumeUrl) }, + buttonStyle = Secondary, + buttonSize = ButtonSize.Medium, + enabled = true, + shape = HedvigTheme.shapes.cornerFull, + modifier = Modifier.fillMaxWidth(), + ) } - Spacer(Modifier.height(12.dp)) - HedvigButton( - text = recommendedCrossSell.buttonText, - onClick = { onCrossSellClick(crossSell.storeUrl) }, - buttonStyle = Secondary, - buttonSize = ButtonSize.Medium, - enabled = true, - shape = HedvigTheme.shapes.cornerFull, - modifier = Modifier.fillMaxWidth(), - ) } } } @@ -1477,23 +1476,18 @@ private fun PreviewHomeScreen( quickActions = previewQuickActions, hasUnseenChatMessages = hasUnseenChatMessages, crossSellsPartition = CrossSellsPartition( - offersCrossSell = RecommendedCrossSell( - crossSell = CrossSell( - id = "car", - title = "Car Insurance", - subtitle = "For you and your car", - storeUrl = "", - pillowImage = ImageAsset("", "", ""), - ), - bannerText = "15% bundle discount", - buttonText = "See your price", - discountText = "-15%", - buttonDescription = "", - backgroundPillowImages = "" to "", - bundleProgress = null, - ), discoverCrossSells = emptyList(), ), + ongoingShopSessions = listOf( + OngoingShopSession( + id = "preview-1", + title = "Home + Accident", + subtitle = "Studio apartment, Stockholm", + monthlyNet = null, + resumeUrl = "", + pillowImageUrl = null, + ), + ), crossSellsAction = CrossSellsAction( CrossSellSheetData( recommendedCrossSell = RecommendedCrossSell( diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenter.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenter.kt index d6c6a746e6..fae748a582 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenter.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenter.kt @@ -14,12 +14,12 @@ import arrow.core.getOrElse import com.hedvig.android.apollo.ApolloOperationError import com.hedvig.android.core.common.ApplicationScope import com.hedvig.android.crosssells.CrossSellSheetData -import com.hedvig.android.crosssells.RecommendedCrossSell import com.hedvig.android.data.addons.data.AddonBannerInfo import com.hedvig.android.data.claimintent.DeleteClaimIntentDraftUseCase import com.hedvig.android.data.contract.CrossSell import com.hedvig.android.feature.home.home.data.GetHomeDataUseCase import com.hedvig.android.feature.home.home.data.HomeData +import com.hedvig.android.feature.home.home.data.OngoingShopSession import com.hedvig.android.feature.home.home.data.SeenImportantMessagesStorage import com.hedvig.android.logger.LogPriority import com.hedvig.android.logger.logcat @@ -164,6 +164,7 @@ internal class HomePresenter( addonBannerInfos = successData.addonBannerInfos, isProduction = isProduction, crossSellsPartition = successData.crossSellsPartition, + ongoingShopSessions = successData.ongoingShopSessions, firstName = successData.firstName, draftClaim = successData.draftClaim, ) @@ -211,6 +212,7 @@ internal sealed interface HomeUiState { val quickActions: List, override val hasUnseenChatMessages: Boolean, val crossSellsPartition: CrossSellsPartition = CrossSellsPartition(), + val ongoingShopSessions: List = emptyList(), val firstName: String = "", val draftClaim: HomeData.DraftClaim?, ) : HomeUiState @@ -233,6 +235,7 @@ private data class SuccessData( val hasUnseenChatMessages: Boolean, val addonBannerInfos: List, val crossSellsPartition: CrossSellsPartition, + val ongoingShopSessions: List, val firstName: String, val draftClaim: HomeData.DraftClaim?, ) { @@ -252,6 +255,7 @@ private data class SuccessData( addonBannerInfos = lastState.addonBannerInfos, chatAction = lastState.chatAction, crossSellsPartition = lastState.crossSellsPartition, + ongoingShopSessions = lastState.ongoingShopSessions, firstName = lastState.firstName, draftClaim = lastState.draftClaim, ) @@ -305,6 +309,7 @@ private data class SuccessData( addonBannerInfos = homeData.addonBannerInfos, chatAction = if (homeData.showChatIcon) HomeTopBarAction.ChatAction else null, crossSellsPartition = partitionCrossSells(homeData.crossSells), + ongoingShopSessions = homeData.ongoingShopSessions, firstName = homeData.firstName, draftClaim = homeData.draftClaim, ) @@ -313,19 +318,15 @@ private data class SuccessData( } /** - * The home screen surfaces cross-sells in three places (Offers, the discover carousel and the - * "Discover our insurances" list). This is the single place that decides which cross-sells go where. + * The home screen surfaces cross-sells in the discover carousel and the "Discover our insurances" + * list. This is the single place that decides which cross-sells go where. */ internal data class CrossSellsPartition( - val offersCrossSell: RecommendedCrossSell? = null, val discoverCrossSells: List = emptyList(), ) -// WS0 placeholder rule until design finalizes per-section cross-sell assignment: Offers shows the -// recommended cross-sell; both the carousel and the "Discover our insurances" list show the others. internal fun partitionCrossSells(crossSells: CrossSellSheetData): CrossSellsPartition { return CrossSellsPartition( - offersCrossSell = crossSells.recommendedCrossSell, discoverCrossSells = crossSells.otherCrossSells, ) } diff --git a/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/data/GetHomeUseCaseTest.kt b/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/data/GetHomeUseCaseTest.kt index d16d1154b2..1ff82688c1 100644 --- a/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/data/GetHomeUseCaseTest.kt +++ b/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/data/GetHomeUseCaseTest.kt @@ -26,6 +26,8 @@ import com.hedvig.android.apollo.test.TestApolloClientRule import com.hedvig.android.apollo.test.TestNetworkTransportType import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.core.common.test.isRight +import com.hedvig.android.core.uidata.UiCurrencyCode +import com.hedvig.android.core.uidata.UiMoney import com.hedvig.android.crosssells.CrossSellSheetData import com.hedvig.android.crosssells.RecommendedAddon import com.hedvig.android.data.addons.data.AddonBannerInfo @@ -63,10 +65,14 @@ import octopus.builder.buildCrossSellV2 import octopus.builder.buildLinkInfo import octopus.builder.buildMember import octopus.builder.buildMemberImportantMessage +import octopus.builder.buildMoney import octopus.builder.buildPendingContract import octopus.builder.buildRecommendedAddonCrossSell +import octopus.builder.buildShopSession +import octopus.builder.buildShopSessionDisplay import octopus.builder.buildStoryblokImageAsset import octopus.type.ChatMessageSender +import octopus.type.CurrencyCode import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -460,6 +466,7 @@ internal class GetHomeUseCaseTest { // With the inbox-always-available kill switch off, the icon depends purely on existing conversations Feature.ENABLE_NEW_CONVERSATION_FROM_INBOX to false, Feature.ENABLE_CLAIM_INTENT_RESUME to false, + Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS to false, ), ) val getHomeDataUseCase = testUseCaseWithoutReminders(featureManager) @@ -532,6 +539,7 @@ internal class GetHomeUseCaseTest { mapOf( Feature.ENABLE_NEW_CONVERSATION_FROM_INBOX to inboxAlwaysAvailable, Feature.ENABLE_CLAIM_INTENT_RESUME to false, + Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS to false, ), ) val getHomeDataUseCase = testUseCaseWithoutReminders(featureManager) @@ -593,6 +601,7 @@ internal class GetHomeUseCaseTest { // Inbox-always-available off, so the icon reflects the conversation state being tested here Feature.ENABLE_NEW_CONVERSATION_FROM_INBOX to false, Feature.ENABLE_CLAIM_INTENT_RESUME to false, + Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS to false, ), ) val getHomeDataUseCase = testUseCaseWithoutReminders(featureManager) @@ -751,6 +760,7 @@ internal class GetHomeUseCaseTest { mapOf( Feature.ENABLE_NEW_CONVERSATION_FROM_INBOX to false, Feature.ENABLE_CLAIM_INTENT_RESUME to false, + Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS to false, ), ) val getHomeDataUseCase = testUseCaseWithoutReminders(featureManager) @@ -771,6 +781,105 @@ internal class GetHomeUseCaseTest { homeData.prop(HomeData::showChatIcon).isFalse() } + @Test + fun `ongoing shop sessions are mapped into HomeData when the kill switch is off`() = runTest { + val featureManager = FakeFeatureManager( + mapOf( + Feature.ENABLE_NEW_CONVERSATION_FROM_INBOX to false, + Feature.ENABLE_CLAIM_INTENT_RESUME to false, + Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS to false, + ), + ) + val getHomeDataUseCase = testUseCaseWithoutReminders(featureManager) + + apolloClient.registerTestResponse( + HomeQuery(true, false), + HomeQuery.Data(OctopusFakeResolver) { + currentMember = buildMember { + ongoingShopSessions = listOf( + buildShopSession { + id = "session-1" + display = buildShopSessionDisplay { + title = "Home + Accident" + subtitle = "Studio apartment, Stockholm" + monthlyNet = buildMoney { + amount = 199.0 + currencyCode = CurrencyCode.SEK + } + resumeUrl = "https://hedvig.com/resume/session-1" + pillowImage = null + } + }, + ) + } + }, + ) + apolloClient.registerTestResponse(UnreadMessageCountQuery(), UnreadMessageCountQuery.Data(OctopusFakeResolver)) + apolloClient.registerTestResponse( + CbmNumberOfChatMessagesQuery(), + CbmNumberOfChatMessagesQuery.Data(OctopusFakeResolver), + ) + + val result = getHomeDataUseCase.invoke(true).first() + + assertThat(result) + .isNotNull() + .isRight() + .prop(HomeData::ongoingShopSessions) + .containsExactly( + OngoingShopSession( + id = "session-1", + title = "Home + Accident", + subtitle = "Studio apartment, Stockholm", + monthlyNet = UiMoney(199.0, UiCurrencyCode.SEK), + resumeUrl = "https://hedvig.com/resume/session-1", + pillowImageUrl = null, + ), + ) + } + + @Test + fun `ongoing shop sessions are dropped when the kill switch is on`() = runTest { + val featureManager = FakeFeatureManager( + mapOf( + Feature.ENABLE_NEW_CONVERSATION_FROM_INBOX to false, + Feature.ENABLE_CLAIM_INTENT_RESUME to false, + Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS to true, + ), + ) + val getHomeDataUseCase = testUseCaseWithoutReminders(featureManager) + + apolloClient.registerTestResponse( + HomeQuery(true, false), + HomeQuery.Data(OctopusFakeResolver) { + currentMember = buildMember { + ongoingShopSessions = listOf( + buildShopSession { + id = "session-1" + display = buildShopSessionDisplay { + title = "Home + Accident" + resumeUrl = "https://hedvig.com/resume/session-1" + } + }, + ) + } + }, + ) + apolloClient.registerTestResponse(UnreadMessageCountQuery(), UnreadMessageCountQuery.Data(OctopusFakeResolver)) + apolloClient.registerTestResponse( + CbmNumberOfChatMessagesQuery(), + CbmNumberOfChatMessagesQuery.Data(OctopusFakeResolver), + ) + + val result = getHomeDataUseCase.invoke(true).first() + + assertThat(result) + .isNotNull() + .isRight() + .prop(HomeData::ongoingShopSessions) + .isEmpty() + } + // Used as a convenience to get a use case without any enqueued apollo responses, but some sane defaults for the // other dependencies private fun testUseCaseWithoutReminders( diff --git a/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenterTest.kt b/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenterTest.kt index 65b91b7e58..c489d57c10 100644 --- a/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenterTest.kt +++ b/app/feature/feature-home/src/test/kotlin/com/hedvig/android/feature/home/home/ui/HomePresenterTest.kt @@ -6,6 +6,7 @@ import arrow.core.left import arrow.core.nonEmptyListOf import arrow.core.right import assertk.assertThat +import assertk.assertions.containsExactly import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isInstanceOf @@ -16,6 +17,8 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector import com.hedvig.android.apollo.ApolloOperationError import com.hedvig.android.core.common.ApplicationScope import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.core.uidata.UiCurrencyCode +import com.hedvig.android.core.uidata.UiMoney import com.hedvig.android.crosssells.CrossSellSheetData import com.hedvig.android.crosssells.RecommendedAddon import com.hedvig.android.crosssells.RecommendedCrossSell @@ -24,6 +27,7 @@ import com.hedvig.android.data.contract.CrossSell import com.hedvig.android.data.contract.ImageAsset import com.hedvig.android.feature.home.home.data.GetHomeDataUseCase import com.hedvig.android.feature.home.home.data.HomeData +import com.hedvig.android.feature.home.home.data.OngoingShopSession import com.hedvig.android.feature.home.home.data.SeenImportantMessagesStorageImpl import com.hedvig.android.logger.TestLogcatLoggingRule import com.hedvig.android.memberquickactions.GetMemberQuickActionsUseCase @@ -217,7 +221,7 @@ internal class HomePresenterTest { hasUnseenChatMessages = false, addonBannerInfos = emptyList(), isProduction = false, - crossSellsPartition = CrossSellsPartition(offersCrossSell = testCrossSell), + crossSellsPartition = CrossSellsPartition(), draftClaim = null, ), ) @@ -558,7 +562,6 @@ internal class HomePresenterTest { addonBannerInfos = emptyList(), isProduction = false, crossSellsPartition = CrossSellsPartition( - offersCrossSell = testCrossSell, discoverCrossSells = listOf(crossSell), ), draftClaim = null, @@ -701,7 +704,6 @@ internal class HomePresenterTest { .prop(HomeUiState.Success::crossSellsPartition) .isEqualTo( CrossSellsPartition( - offersCrossSell = testCrossSell, discoverCrossSells = listOf(otherCrossSell), ), ) @@ -865,6 +867,41 @@ internal class HomePresenterTest { ), ) + @Test + fun `ongoing shop sessions propagate to the success ui state`() = runTest { + val getHomeDataUseCase = TestGetHomeDataUseCase() + val homePresenter = HomePresenter( + getHomeDataUseCase, + SeenImportantMessagesStorageImpl(), + FakeCrossSellHomeNotificationService(), + ApplicationScope(backgroundScope), + false, + TestDeleteClaimIntentDraftUseCase(), + FakeGetMemberQuickActionsUseCase(emptyList().right()), + ) + + val session = OngoingShopSession( + id = "session-1", + title = "Home + Accident", + subtitle = null, + monthlyNet = UiMoney(199.0, UiCurrencyCode.SEK), + resumeUrl = "https://hedvig.com/resume/session-1", + pillowImageUrl = null, + ) + + homePresenter.test(HomeUiState.Loading) { + assertThat(awaitItem()).isEqualTo(HomeUiState.Loading) + getHomeDataUseCase.responseTurbine.add( + someIrrelevantHomeDataInstance.copy(ongoingShopSessions = listOf(session)).right(), + ) + val success = awaitItem() + assertThat(success) + .isInstanceOf() + .prop(HomeUiState.Success::ongoingShopSessions) + .containsExactly(session) + } + } + private val someIrrelevantHomeDataInstance: HomeData = HomeData( contractStatus = HomeData.ContractStatus.Active, claimStatusCardsData = null, diff --git a/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/HedvigUnleashClient.kt b/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/HedvigUnleashClient.kt index e86fd86169..a39c1367c0 100644 --- a/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/HedvigUnleashClient.kt +++ b/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/HedvigUnleashClient.kt @@ -75,6 +75,7 @@ class HedvigUnleashClient( bootstrap = listOf( Toggle(name = Feature.DISABLE_PUPPY_GUIDE.unleashKey, enabled = true), Toggle(name = Feature.DISABLE_TERMINATION_REDIRECTION.unleashKey, enabled = true), + Toggle(name = Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS.unleashKey, enabled = true), ), ) } diff --git a/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/flags/FeatureUnleashKey.kt b/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/flags/FeatureUnleashKey.kt index 17671551c1..d0de65b5c3 100644 --- a/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/flags/FeatureUnleashKey.kt +++ b/app/featureflags/feature-flags/src/androidMain/kotlin/com/hedvig/android/featureflags/flags/FeatureUnleashKey.kt @@ -4,6 +4,7 @@ internal val Feature.unleashKey: String get() = when (this) { Feature.DISABLE_PUPPY_GUIDE -> "disable_puppy_guide" Feature.DISABLE_TERMINATION_REDIRECTION -> "disable_termination_redirection" + Feature.DISABLE_RESUMING_ONGOING_SHOP_SESSIONS -> "disable_resuming_ongoing_shop_sessions" Feature.ENABLE_CLAIM_INTENT_RESUME -> "enable_claim_intent_resume" Feature.ENABLE_NEW_CONVERSATION_FROM_INBOX -> "enable_new_conversation_from_inbox" Feature.UPDATE_NECESSARY -> "update_necessary" diff --git a/app/featureflags/feature-flags/src/commonMain/kotlin/com/hedvig/android/featureflags/flags/Feature.kt b/app/featureflags/feature-flags/src/commonMain/kotlin/com/hedvig/android/featureflags/flags/Feature.kt index eedb74609d..e3a88691ba 100644 --- a/app/featureflags/feature-flags/src/commonMain/kotlin/com/hedvig/android/featureflags/flags/Feature.kt +++ b/app/featureflags/feature-flags/src/commonMain/kotlin/com/hedvig/android/featureflags/flags/Feature.kt @@ -12,6 +12,10 @@ enum class Feature( "survey without redirections (sends redirectionEnabled = false), so the moving option keeps its plain " + "sub options instead of the redirection interstitial.", ), + DISABLE_RESUMING_ONGOING_SHOP_SESSIONS( + "Kill switch for the home screen 'Your quotes' section, which lets a member resume an ongoing " + + "shopping session they started on the web. When the toggle is on, the section is hidden.", + ), ENABLE_CLAIM_INTENT_RESUME( "Enables resuming a draft claim: the draft card on the home screen, the draft-claim dialogs, " + "and the resumable-aware leave dialog in the claim chat.",