diff --git a/CLAUDE.md b/CLAUDE.md index b811a101c2..5390fe7ada 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -628,6 +628,13 @@ Example: Text("This is some text for feature X") ``` +**Verifying whether a string key is "real" (already in Lokalise):** if you find a key in a +`strings.xml` and are unsure whether it actually exists in Lokalise or was hand-added, run +`./gradlew downloadStrings` and re-check the file. Because `downloadStrings` regenerates every +`strings.xml` from Lokalise, a key that **survives** the run exists in Lokalise; a key that +**disappears** was only added locally and would break the build once someone else syncs. Use this +before relying on (or committing code that references) a key you didn't personally add to Lokalise. + ## Debugging ### Common Issues diff --git a/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls b/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls index 9b640dcbe3..98edc25fe0 100644 --- a/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls +++ b/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls @@ -971,6 +971,11 @@ type ClaimIntent { """ displayName: String """ + Whether this draft is resumable by the member. A draft that has been deleted (see `claimIntentDeleteDraft`) + is no longer resumable. + """ + resumable: Boolean! + """ The created claim of this intent. Once this value is returned, the intent is effectively over, and no steps can be produced or interacted with. @@ -3713,6 +3718,10 @@ type Mutation { """ claimIntentRegretStep(stepId: ID!): ClaimIntentMutationOutput! """ + Delete a draft claim intent from the member's perspective (a soft delete) so it is no longer returned by `resumableClaimIntent`. + """ + claimIntentDeleteDraft(id: ID!): Boolean! + """ Submit a step containing a `ClaimIntentStepContentForm`. """ claimIntentSubmitForm(input: ClaimIntentSubmitFormInput!): ClaimIntentMutationOutput! diff --git a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml index 75a8dca92c..671a6ac4bf 100644 --- a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml @@ -503,8 +503,8 @@ Inga konversationer än. Starta en genom att skriva ett meddelande Din inkorg är tom Nytt - Anmäl något som har hänt med dig eller dina saker - Frågor om din försäkring, betalningar och annat + Anmäl något som hänt dig eller dina saker + Frågor om din försäkring, betalningar eller annat Erbjudanden Du har ingen aktiv försäkring Tillägg @@ -986,6 +986,7 @@ Välj den försäkring du vill uppdatera Visa ditt skydd Välj din skyddsnivå och självrisk + Välj din skyddsnivå Godkänn ändringar Skyddet Dokument diff --git a/app/core/core-resources/src/androidMain/res/values/strings.xml b/app/core/core-resources/src/androidMain/res/values/strings.xml index 0d194724d5..6657ce11d1 100644 --- a/app/core/core-resources/src/androidMain/res/values/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values/strings.xml @@ -986,6 +986,7 @@ Select the insurance you want to edit Show coverage Set your coverage level and deductible + Set your coverage level Confirm changes Coverage Documents diff --git a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml index 2d75e502d1..42758c2870 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -502,8 +502,8 @@ Inga konversationer än. Starta en genom att skriva ett meddelande Din inkorg är tom Nytt - Anmäl något som har hänt med dig eller dina saker - Frågor om din försäkring, betalningar och annat + Anmäl något som hänt dig eller dina saker + Frågor om din försäkring, betalningar eller annat Erbjudanden Du har ingen aktiv försäkring Tillägg @@ -985,6 +985,7 @@ Välj den försäkring du vill uppdatera Visa ditt skydd Välj din skyddsnivå och självrisk + Välj din skyddsnivå Godkänn ändringar Skyddet Dokument diff --git a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml index 0061307317..6428d4a37d 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml @@ -985,6 +985,7 @@ Select the insurance you want to edit Show coverage Set your coverage level and deductible + Set your coverage level Confirm changes Coverage Documents diff --git a/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql b/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql index 1de9f27fb4..0c21535700 100644 --- a/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql +++ b/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql @@ -19,6 +19,7 @@ $addonsFlagOn: Boolean!) { } tierLevel tierName + tierDescription } quotes { currentTotalCost { @@ -48,6 +49,7 @@ $addonsFlagOn: Boolean!) { } tierLevel tierName + tierDescription addons @include(if: $addonsFlagOn) { addonVariant { ...AddonVariantFragment diff --git a/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt b/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt index 709901bcde..91e34383cb 100644 --- a/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt +++ b/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt @@ -100,7 +100,7 @@ internal class CreateChangeTierDeductibleIntentUseCaseImpl( tier = Tier( tierName = tierName, tierLevel = tierLevel, - tierDescription = productVariant.tierDescription, + tierDescription = tierDescription ?: productVariant.tierDescription, tierDisplayName = productVariant.displayNameTier, ), displayItems = displayItems.toDisplayItems(), @@ -126,7 +126,7 @@ internal class CreateChangeTierDeductibleIntentUseCaseImpl( tier = Tier( tierName = it.tierName, tierLevel = it.tierLevel, - tierDescription = it.productVariant.tierDescription, + tierDescription = it.tierDescription ?: it.productVariant.tierDescription, tierDisplayName = it.productVariant.displayNameTier, ), addons = it.addons?.map { addon -> diff --git a/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt b/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt index b1d9e29413..2617e07546 100644 --- a/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt +++ b/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt @@ -8,6 +8,7 @@ import hedvigandroid.data_contract.generated.resources.dog import hedvigandroid.data_contract.generated.resources.home import hedvigandroid.data_contract.generated.resources.homeowner import hedvigandroid.data_contract.generated.resources.rental +import hedvigandroid.data_contract.generated.resources.safety import hedvigandroid.data_contract.generated.resources.student import hedvigandroid.data_contract.generated.resources.vacation import hedvigandroid.data_contract.generated.resources.villa @@ -25,6 +26,7 @@ enum class ContractGroup { STUDENT, COUNTRY_HOME, QASA_LANDLORD, + PAYMENT_PROTECTION, UNKNOWN, } @@ -41,6 +43,7 @@ fun ContractGroup.gradientResource(): DrawableResource = when (this) { ContractGroup.COUNTRY_HOME -> Res.drawable.vacation ContractGroup.UNKNOWN -> Res.drawable.home ContractGroup.QASA_LANDLORD -> Res.drawable.home + ContractGroup.PAYMENT_PROTECTION -> Res.drawable.safety } fun ContractGroup.pillowResource(): DrawableResource = when (this) { @@ -56,6 +59,7 @@ fun ContractGroup.pillowResource(): DrawableResource = when (this) { ContractGroup.COUNTRY_HOME -> Res.drawable.vacation ContractGroup.UNKNOWN -> Res.drawable.home ContractGroup.QASA_LANDLORD -> Res.drawable.home + ContractGroup.PAYMENT_PROTECTION -> Res.drawable.safety } fun String.toContractGroup(): ContractGroup = when (this) { @@ -132,5 +136,7 @@ fun String.toContractGroup(): ContractGroup = when (this) { "SE_QASA_LANDLORD" -> ContractGroup.QASA_LANDLORD + "SE_PAYMENT_PROTECTION" -> ContractGroup.PAYMENT_PROTECTION + else -> ContractGroup.UNKNOWN } diff --git a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt index cd86130499..0e4cad32f0 100644 --- a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt +++ b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt @@ -219,6 +219,8 @@ internal class SelectCoveragePresenter( ), quotesForChosenTier = currentPartialStateValue.map[chosenTier]!!, isTierChoiceEnabled = currentPartialStateValue.map.keys.size > 1, + isPaymentProtection = + currentPartialStateValue.contractData.contractGroup == ContractGroup.PAYMENT_PROTECTION, contractData = currentPartialStateValue.contractData, chosenInDialogQuote = chosenQuoteInDialog, chosenInDialogTier = chosenTierInDialog, @@ -326,6 +328,9 @@ internal data class SelectCoverageSuccessUiState( val chosenInDialogQuote: TierDeductibleQuote?, val isCurrentChosen: Boolean, val isTierChoiceEnabled: Boolean, + // Payment protection has no real tiers nor comparable coverage: it reuses the tier flow only to pick an + // insured amount. Used to hide the compare/show-coverage button and to swap the deductible-mentioning subtitle. + val isPaymentProtection: Boolean, // sorted list of tiers with corresponding premiums (depending on selected deductible) val tiers: List>, val quotesForChosenTier: List, diff --git a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt index b054469c73..c9d9b3d58d 100644 --- a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt +++ b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt @@ -95,7 +95,9 @@ import hedvig.resources.TIER_FLOW_SELECT_COVERAGE_SUBTITLE import hedvig.resources.TIER_FLOW_SELECT_COVERAGE_TITLE import hedvig.resources.TIER_FLOW_SELECT_DEDUCTIBLE_SUBTITLE import hedvig.resources.TIER_FLOW_SELECT_DEDUCTIBLE_TITLE +import hedvig.resources.TIER_FLOW_SHOW_COVERAGE_BUTTON import hedvig.resources.TIER_FLOW_SUBTITLE +import hedvig.resources.TIER_FLOW_SUBTITLE_WITHOUT_DEDUCTIBLE import hedvig.resources.TIER_FLOW_TITLE import hedvig.resources.TIER_FLOW_TOTAL import hedvig.resources.general_cancel_button @@ -234,7 +236,12 @@ private fun SelectTierScreen( lineBreak = LineBreak.Heading, color = HedvigTheme.colorScheme.textSecondary, ), - text = stringResource(Res.string.TIER_FLOW_SUBTITLE), + // Payment protection has no deductible, so the standard "…level and deductible" subtitle over-promises. + text = if (uiState.isPaymentProtection) { + stringResource(Res.string.TIER_FLOW_SUBTITLE_WITHOUT_DEDUCTIBLE) + } else { + stringResource(Res.string.TIER_FLOW_SUBTITLE) + }, modifier = Modifier.padding(horizontal = 16.dp), ) Spacer(Modifier.weight(1f)) @@ -270,14 +277,20 @@ private fun SelectTierScreen( .fillMaxWidth() .padding(horizontal = 16.dp), ) - if (uiState.tiers.size > 1) { + if (!uiState.isPaymentProtection) { Spacer(Modifier.height(8.dp)) HedvigTextButton( buttonSize = Large, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - text = stringResource(Res.string.TIER_FLOW_COMPARE_BUTTON), + text = stringResource( + if (uiState.tiers.size == 1) { + Res.string.TIER_FLOW_SHOW_COVERAGE_BUTTON + } else { + Res.string.TIER_FLOW_COMPARE_BUTTON + }, + ), onClick = { onCompareClick() }, @@ -327,7 +340,7 @@ private fun CustomizationCard( Spacer(Modifier.height(16.dp)) val tierSimpleItems = buildList { for (tier in tiers) { - add(SimpleDropdownItem(tier.first.tierDisplayName ?: "-")) + add(SimpleDropdownItem(tier.first.tierDisplayName ?: tier.first.tierName)) } } val hintText = stringResource(Res.string.TIER_FLOW_COVERAGE_PLACEHOLDER) @@ -543,7 +556,7 @@ private fun TierCoverageRadioGroup( options = tiers.map { pair -> RadioOption( id = RadioOptionId(pair.first.tierName), - text = pair.first.tierDisplayName ?: "-", + text = pair.first.tierDisplayName ?: pair.first.tierName, label = pair.first.tierDescription, ) }, @@ -753,6 +766,7 @@ private fun SelectTierScreenPreview() { ), quotesForChosenTier = listOf(quotesForPreview[0]), isTierChoiceEnabled = true, + isPaymentProtection = false, chosenTier = Tier( "BAS", tierLevel = 0, diff --git a/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt b/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt index 922dad5b95..682dfa6619 100644 --- a/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt +++ b/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt @@ -12,6 +12,7 @@ import assertk.assertions.isNull import assertk.assertions.prop import basTier import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.data.contract.ContractGroup import com.hedvig.android.feature.change.tier.data.CurrentContractData import com.hedvig.android.feature.change.tier.data.GetCurrentContractDataUseCase import com.hedvig.android.feature.change.tier.navigation.ComparisonKey @@ -264,8 +265,56 @@ class SelectCoveragePresenterTest { .isNull() } } + + @Test + fun `when the contract is payment protection isPaymentProtection is true`() = runTest { + val tierRepo = FakeChangeTierRepository() + val presenter = SelectCoveragePresenter( + params = params, + tierRepository = tierRepo, + getCurrentContractDataUseCase = FakeGetCurrentContractDataUseCase(), + backstack = TestBackstack(), + ) + presenter.test(SelectCoverageState.Loading) { + tierRepo.quoteListTurbine.add(listOf(paymentProtectionQuote)) + skipItems(1) + assertThat(awaitItem()) + .isInstanceOf(SelectCoverageState.Success::class) + .prop(SelectCoverageState.Success::uiState) + .isInstanceOf(SelectCoverageSuccessUiState::class.java) + .prop(SelectCoverageSuccessUiState::isPaymentProtection) + .isEqualTo(true) + } + } + + @Test + fun `when the contract is not payment protection isPaymentProtection is false`() = runTest { + val tierRepo = FakeChangeTierRepository() + val presenter = SelectCoveragePresenter( + params = params, + tierRepository = tierRepo, + getCurrentContractDataUseCase = FakeGetCurrentContractDataUseCase(), + backstack = TestBackstack(), + ) + presenter.test(SelectCoverageState.Loading) { + tierRepo.quoteListTurbine.add(listOf(testQuote, testQuote2, currentQuote)) + skipItems(1) + assertThat(awaitItem()) + .isInstanceOf(SelectCoverageState.Success::class) + .prop(SelectCoverageState.Success::uiState) + .isInstanceOf(SelectCoverageSuccessUiState::class.java) + .prop(SelectCoverageSuccessUiState::isPaymentProtection) + .isEqualTo(false) + } + } } +// A current quote whose contract group is payment protection, which reuses the tier flow only to pick an +// insured amount. Its id must match FakeChangeTierRepository.getCurrentQuoteId() so it is treated as current. +private val paymentProtectionQuote = currentQuote.copy( + productVariant = currentQuote.productVariant.copy(contractGroup = ContractGroup.PAYMENT_PROTECTION), +) + private class FakeGetCurrentContractDataUseCase() : GetCurrentContractDataUseCase { override suspend fun invoke(insuranceId: String): Either { return either { CurrentContractData("exposure name") }