Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions app/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,15 @@ dependencies {
implementation(projects.coreBuildConstants)
implementation(projects.coreCommonPublic)
implementation(projects.coreDatastorePublic)
implementation(projects.coreRive)
implementation(projects.coreDemoMode)
implementation(projects.coreFileUpload)
implementation(projects.coreIcons)
implementation(projects.coreResources)
implementation(projects.coreRive)
implementation(projects.crossSells)
implementation(projects.dataAddons)
implementation(projects.dataChangetier)
implementation(projects.dataChat)

implementation(projects.dataContract)
implementation(projects.dataConversations)
implementation(projects.dataCrossSellAfterClaimClosed)
Expand All @@ -185,14 +184,14 @@ dependencies {
implementation(projects.featureAddonPurchase)
implementation(projects.featurePurchaseApartment)
implementation(projects.featurePurchaseCar)
implementation(projects.featurePurchaseHouse)
implementation(projects.featurePurchasePet)
implementation(projects.purchaseCommon)
implementation(projects.featureChat)
implementation(projects.featureChooseTier)
implementation(projects.featureClaimChat)
implementation(projects.featureClaimDetails)
implementation(projects.featureClaimHistory)

implementation(projects.featureConnectPaymentTrustly)
implementation(projects.featureCrossSellSheet)
implementation(projects.featureDeleteAccount)
Expand All @@ -207,10 +206,12 @@ dependencies {
implementation(projects.featureInsurances)
implementation(projects.featureLogin)
implementation(projects.featureMovingflow)

implementation(projects.featureRemoveAddons)
implementation(projects.featurePayments)
implementation(projects.featureProfile)
implementation(projects.featurePurchaseApartment)
implementation(projects.featurePurchaseCar)
implementation(projects.featurePurchaseHouse)
implementation(projects.featureRemoveAddons)
implementation(projects.featureTerminateInsurance)
implementation(projects.featureTravelCertificate)
implementation(projects.foreverUi)
Expand All @@ -220,7 +221,6 @@ dependencies {
implementation(projects.languageMigration)
implementation(projects.loggingDeviceModel)
implementation(projects.loggingPublic)
implementation(projects.permissionCore)
implementation(projects.memberRemindersPublic)
implementation(projects.navigationActivity)
implementation(projects.navigationCommon)
Expand All @@ -230,20 +230,22 @@ dependencies {
implementation(projects.notificationBadgeDataPublic)
implementation(projects.notificationCore)
implementation(projects.notificationFirebase)
implementation(projects.permissionCore)
implementation(projects.purchaseCommon)
implementation(projects.shareddi)
implementation(projects.theme)
implementation(projects.tierComparison)
implementation(projects.trackingCore)
implementation(projects.trackingDatadog)
implementation(projects.uiForceUpgrade)

debugImplementation(libs.androidx.compose.uiTooling)
debugImplementation(projects.featureImpersonation)

// OkHttp for ProGuard rules only - not available at compile time
runtimeOnly(platform(libs.okhttp.bom))
runtimeOnly(libs.okhttp.core)

debugImplementation(libs.androidx.compose.uiTooling)
debugImplementation(projects.featureImpersonation)

debugRuntimeOnly(libs.androidx.compose.uiTestManifest)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import com.hedvig.android.feature.profile.di.profileModule
import com.hedvig.android.feature.purchase.apartment.di.apartmentPurchaseModule
import com.hedvig.android.feature.purchase.car.di.carPurchaseModule
import com.hedvig.android.feature.purchase.common.di.purchaseCommonModule
import com.hedvig.android.feature.purchase.house.di.housePurchaseModule
import com.hedvig.android.feature.purchase.pet.di.petPurchaseModule
import com.hedvig.android.feature.terminateinsurance.di.terminateInsuranceModule
import com.hedvig.android.feature.travelcertificate.di.travelCertificateModule
Expand Down Expand Up @@ -296,6 +297,7 @@ val applicationModule = module {
addonRemovalModule,
apartmentPurchaseModule,
carPurchaseModule,
housePurchaseModule,
petPurchaseModule,
androidPermissionModule,
apolloAuthListenersModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ import com.hedvig.android.feature.purchase.car.navigation.CarPurchaseGraphDestin
import com.hedvig.android.feature.purchase.car.navigation.carPurchaseNavGraph
import com.hedvig.android.feature.purchase.common.navigation.PurchaseCommonDestination
import com.hedvig.android.feature.purchase.common.ui.success.PurchaseSuccessDestination
import com.hedvig.android.feature.purchase.house.navigation.HousePurchaseGraphDestination
import com.hedvig.android.feature.purchase.house.navigation.housePurchaseNavGraph
import com.hedvig.android.feature.purchase.pet.navigation.PetPurchaseGraphDestination
import com.hedvig.android.feature.purchase.pet.navigation.petPurchaseNavGraph
import com.hedvig.android.feature.terminateinsurance.navigation.TerminateInsuranceGraphDestination
Expand Down Expand Up @@ -342,6 +344,9 @@ internal fun HedvigNavHost(
onNavigateToPetPurchase = {
navController.navigate(PetPurchaseGraphDestination)
},
onNavigateToHousePurchase = { productName ->
navController.navigate(HousePurchaseGraphDestination(productName))
},
)
foreverGraph(
hedvigDeepLinkContainer = hedvigDeepLinkContainer,
Expand Down Expand Up @@ -515,6 +520,12 @@ internal fun HedvigNavHost(
finishApp = finishApp,
crossSellAfterFlowRepository = crossSellAfterFlowRepository,
)
housePurchaseNavGraph(
navController = navController,
popBackStack = popBackStackOrFinish,
finishApp = finishApp,
crossSellAfterFlowRepository = crossSellAfterFlowRepository,
)
navdestination<PurchaseCommonDestination.Success> { backStackEntry ->
val route = backStackEntry.toRoute<PurchaseCommonDestination.Success>()
PurchaseSuccessDestination(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fun NavGraphBuilder.insuranceGraph(
onNavigateToApartmentPurchase: (productName: String) -> Unit,
onNavigateToCarPurchase: (productName: String) -> Unit,
onNavigateToPetPurchase: () -> Unit,
onNavigateToHousePurchase: (productName: String) -> Unit,
) {
navgraph<InsurancesDestination.Graph>(
startDestination = InsurancesDestination.Insurances::class,
Expand Down Expand Up @@ -71,6 +72,10 @@ fun NavGraphBuilder.insuranceGraph(
}
val lower = decoded.lowercase()
when {
"fritidshusforsakring" in lower || "vacation-home" in lower -> {
onNavigateToHousePurchase("SE_VACATION_HOME")
}

"car-insurance" in lower || "bilforsakring" in lower -> {
onNavigateToCarPurchase("SE_CAR")
}
Expand Down
40 changes: 40 additions & 0 deletions app/feature/feature-purchase-house/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
plugins {
id("hedvig.android.library")
id("hedvig.gradle.plugin")
}

hedvig {
apollo("octopus")
serialization()
compose()
}

android {
testOptions.unitTests.isReturnDefaultValues = true
}

dependencies {
api(libs.androidx.navigation.common)

implementation(libs.androidx.navigation.compose)
implementation(libs.arrow.core)
implementation(libs.arrow.fx)
implementation(libs.jetbrains.lifecycle.runtime.compose)
implementation(libs.koin.composeViewModel)
implementation(libs.koin.core)
implementation(libs.kotlinx.serialization.core)
implementation(projects.apolloCore)
implementation(projects.apolloOctopusPublic)
implementation(projects.composeUi)
implementation(projects.coreCommonPublic)
implementation(projects.coreResources)
implementation(projects.coreUiData)
implementation(projects.dataCrossSellAfterFlow)
implementation(projects.designSystemHedvig)
implementation(projects.purchaseCommon)
implementation(projects.moleculePublic)
implementation(projects.navigationCommon)
implementation(projects.navigationCompose)
implementation(projects.navigationComposeTyped)
implementation(projects.navigationCore)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
query HouseMemberContactInfo {
currentMember {
id
ssn
email
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
mutation HousePriceIntentConfirm($priceIntentId: UUID!) {
priceIntentConfirm(priceIntentId: $priceIntentId) {
priceIntent {
id
offers {
...HouseProductOfferFragment
}
}
userError {
message
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation HousePriceIntentCreate($shopSessionId: UUID!, $productName: String!) {
priceIntentCreate(input: { shopSessionId: $shopSessionId, productName: $productName }) {
id
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mutation HousePriceIntentDataUpdate($priceIntentId: UUID!, $data: PricingFormData!) {
priceIntentDataUpdate(priceIntentId: $priceIntentId, data: $data) {
priceIntent {
id
}
userError {
message
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
fragment HouseProductOfferFragment on ProductOffer {
id
variant {
displayName
displayNameSubtype
displayNameTier
tierDescription
typeOfContract
perils {
title
description
colorCode
covered
info
}
documents {
type
displayName
url
}
}
cost {
gross {
...MoneyFragment
}
net {
...MoneyFragment
}
discountsV2 {
amount {
...MoneyFragment
}
}
}
startDate
deductible {
displayName
amount
}
usps
exposure {
displayNameShort
}
bundleDiscount {
isEligible
potentialYearlySavings {
...MoneyFragment
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation HouseShopSessionCreate($countryCode: CountryCode!) {
shopSessionCreate(input: { countryCode: $countryCode }) {
id
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.hedvig.android.feature.purchase.house.data

import arrow.core.Either
import arrow.core.raise.either
import com.apollographql.apollo.ApolloClient
import com.hedvig.android.apollo.safeExecute
import com.hedvig.android.core.common.ErrorMessage
import com.hedvig.android.logger.LogPriority
import com.hedvig.android.logger.logcat
import octopus.HouseMemberContactInfoQuery
import octopus.HousePriceIntentCreateMutation
import octopus.HouseShopSessionCreateMutation
import octopus.type.CountryCode

internal interface CreateHouseSessionAndPriceIntentUseCase {
suspend fun invoke(productName: String): Either<ErrorMessage, SessionAndIntent>
}

internal class CreateHouseSessionAndPriceIntentUseCaseImpl(
private val apolloClient: ApolloClient,
) : CreateHouseSessionAndPriceIntentUseCase {
override suspend fun invoke(productName: String): Either<ErrorMessage, SessionAndIntent> {
return either {
val shopSessionId = apolloClient
.mutation(HouseShopSessionCreateMutation(CountryCode.SE))
.safeExecute()
.fold(
ifLeft = {
logcat(LogPriority.ERROR) { "Failed to create shop session: $it" }
raise(ErrorMessage())
},
ifRight = { it.shopSessionCreate.id },
)

val priceIntentId = apolloClient
.mutation(HousePriceIntentCreateMutation(shopSessionId = shopSessionId, productName = productName))
.safeExecute()
.fold(
ifLeft = {
logcat(LogPriority.ERROR) { "Failed to create price intent: $it" }
raise(ErrorMessage())
},
ifRight = { it.priceIntentCreate.id },
)

val member = apolloClient
.query(HouseMemberContactInfoQuery())
.safeExecute()
.fold(
ifLeft = {
logcat(LogPriority.ERROR) { "Failed to fetch member contact info: $it" }
raise(ErrorMessage())
},
ifRight = { it.currentMember },
)
val ssn = member.ssn
if (ssn == null) {
logcat(LogPriority.ERROR) { "Member is missing SSN — cannot continue house purchase" }
raise(ErrorMessage())
}

SessionAndIntent(
shopSessionId = shopSessionId,
priceIntentId = priceIntentId,
ssn = ssn,
email = member.email,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.hedvig.android.feature.purchase.house.data

import com.hedvig.android.core.uidata.UiMoney

internal data class SessionAndIntent(
val shopSessionId: String,
val priceIntentId: String,
val ssn: String,
val email: String,
)

internal data class HouseOffers(
val productDisplayName: String,
val offers: List<HouseTierOffer>,
)

internal data class HouseTierOffer(
val offerId: String,
val tierDisplayName: String,
val tierDescription: String,
val grossPrice: UiMoney,
val netPrice: UiMoney,
val usps: List<String>,
val exposureDisplayName: String,
val deductibleDisplayName: String?,
val hasDiscount: Boolean,
)
Loading
Loading