From ee424a551dbc85d3f29d3ff8085965867a49dcc9 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 10 Apr 2026 18:01:40 +0200 Subject: [PATCH 01/50] Add draft code for payout account logic in payments tab --- app/app/build.gradle.kts | 1 + .../android/app/di/ApplicationModule.kt | 2 + .../android/app/navigation/HedvigNavHost.kt | 8 ++ .../payments/navigation/PaymentsGraph.kt | 2 + .../ui/payments/PaymentsDestination.kt | 25 ++++ .../feature-payout-account/build.gradle.kts | 26 ++++ .../payoutaccount/data/BankNameLookup.kt | 78 +++++++++++ .../data/GetPayoutAccountUseCase.kt | 25 ++++ .../payoutaccount/data/PayoutAccount.kt | 11 ++ .../data/UpdateBankAccountUseCase.kt | 25 ++++ .../payoutaccount/di/PayoutAccountModule.kt | 18 +++ .../navigation/PayoutAccountDestination.kt | 17 +++ .../navigation/PayoutAccountGraph.kt | 44 ++++++ .../EditBankAccountDestination.kt | 119 ++++++++++++++++ .../EditBankAccountViewModel.kt | 125 +++++++++++++++++ .../PayoutAccountOverviewDestination.kt | 129 ++++++++++++++++++ .../PayoutAccountOverviewViewModel.kt | 60 ++++++++ 17 files changed, 715 insertions(+) create mode 100644 app/feature/feature-payout-account/build.gradle.kts create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts index f5f26cf90a..6f11b9e455 100644 --- a/app/app/build.gradle.kts +++ b/app/app/build.gradle.kts @@ -207,6 +207,7 @@ dependencies { implementation(projects.featureMovingflow) implementation(projects.featureRemoveAddons) + implementation(projects.featurePayoutAccount) implementation(projects.featurePayments) implementation(projects.featureProfile) implementation(projects.featureTerminateInsurance) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/di/ApplicationModule.kt b/app/app/src/main/kotlin/com/hedvig/android/app/di/ApplicationModule.kt index b4d78717c5..ff9b0703c5 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/di/ApplicationModule.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/di/ApplicationModule.kt @@ -79,6 +79,7 @@ import com.hedvig.android.feature.insurances.di.insurancesModule import com.hedvig.android.feature.login.di.loginModule import com.hedvig.android.feature.movingflow.di.movingFlowModule import com.hedvig.android.feature.payments.di.paymentsModule +import com.hedvig.android.feature.payoutaccount.di.payoutAccountModule import com.hedvig.android.feature.profile.di.profileModule import com.hedvig.android.feature.terminateinsurance.di.terminateInsuranceModule import com.hedvig.android.feature.travelcertificate.di.travelCertificateModule @@ -344,6 +345,7 @@ val applicationModule = module { networkModule, notificationBadgeModule, notificationModule, + payoutAccountModule, paymentsModule, profileModule, settingsDatastoreModule, diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 43fdab75f4..73f92eb3ac 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -68,6 +68,8 @@ import com.hedvig.android.feature.login.navigation.loginGraph import com.hedvig.android.feature.movingflow.SelectContractForMoving import com.hedvig.android.feature.movingflow.movingFlowGraph import com.hedvig.android.feature.payments.navigation.paymentsGraph +import com.hedvig.android.feature.payoutaccount.navigation.PayoutAccountDestination +import com.hedvig.android.feature.payoutaccount.navigation.payoutAccountGraph import com.hedvig.android.feature.profile.navigation.ProfileDestination import com.hedvig.android.feature.profile.tab.profileGraph import com.hedvig.android.feature.terminateinsurance.navigation.TerminateInsuranceGraphDestination @@ -339,10 +341,16 @@ internal fun HedvigNavHost( navController = navController, hedvigDeepLinkContainer = hedvigDeepLinkContainer, navigateToConnectPayment = navigateToConnectPayment, + navigateToPayoutAccount = { navController.navigate(PayoutAccountDestination.Graph) }, languageService = languageService, hedvigBuildConstants = hedvigBuildConstants, onOpenChat = ::navigateToNewConversation, ) + payoutAccountGraph( + navController = navController, + globalSnackBarState = globalSnackBarState, + navigateUp = navController::navigateUp, + ) profileGraph( settingsDestinationNestedGraphs = { deleteAccountGraph(hedvigDeepLinkContainer, navController) diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt index 20fa3dd7a4..d0f5b8698c 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt @@ -32,6 +32,7 @@ fun NavGraphBuilder.paymentsGraph( languageService: LanguageService, hedvigBuildConstants: HedvigBuildConstants, navigateToConnectPayment: () -> Unit, + navigateToPayoutAccount: () -> Unit, onOpenChat: () -> Unit, ) { navgraph( @@ -48,6 +49,7 @@ fun NavGraphBuilder.paymentsGraph( onPaymentHistoryClicked = dropUnlessResumed { navController.navigate(PaymentsDestinations.History) }, + onPayoutAccountClicked = dropUnlessResumed { navigateToPayoutAccount() }, onChangeBankAccount = dropUnlessResumed { navigateToConnectPayment() }, onDiscountClicked = dropUnlessResumed { navController.navigate(PaymentsDestinations.Discounts) diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt index f630e86423..6896d0a528 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt @@ -62,6 +62,7 @@ import com.hedvig.android.design.system.hedvig.icon.Card import com.hedvig.android.design.system.hedvig.icon.ChevronRight import com.hedvig.android.design.system.hedvig.icon.Clock import com.hedvig.android.design.system.hedvig.icon.HedvigIcons +import com.hedvig.android.design.system.hedvig.icon.PaymentOutline import com.hedvig.android.design.system.hedvig.placeholder.hedvigPlaceholder import com.hedvig.android.design.system.hedvig.placeholder.shimmer import com.hedvig.android.design.system.hedvig.rememberHedvigDateTimeFormatter @@ -111,6 +112,7 @@ internal fun PaymentsDestination( onPaymentClicked: (id: String?) -> Unit, onDiscountClicked: () -> Unit, onPaymentHistoryClicked: () -> Unit, + onPayoutAccountClicked: () -> Unit, onMemberPaymentDetailsClicked: () -> Unit, onChangeBankAccount: () -> Unit, ) { @@ -121,6 +123,7 @@ internal fun PaymentsDestination( onChangeBankAccount = onChangeBankAccount, onDiscountClicked = onDiscountClicked, onPaymentHistoryClicked = onPaymentHistoryClicked, + onPayoutAccountClicked = onPayoutAccountClicked, onRetry = { viewModel.emit(Retry) }, onPaymentDetailsClicked = onMemberPaymentDetailsClicked, ) @@ -133,6 +136,7 @@ private fun PaymentsScreen( onChangeBankAccount: () -> Unit, onDiscountClicked: () -> Unit, onPaymentHistoryClicked: () -> Unit, + onPayoutAccountClicked: () -> Unit, onPaymentDetailsClicked: () -> Unit, onRetry: () -> Unit, ) { @@ -194,6 +198,7 @@ private fun PaymentsScreen( onChangeBankAccount = onChangeBankAccount, onDiscountClicked = onDiscountClicked, onPaymentHistoryClicked = onPaymentHistoryClicked, + onPayoutAccountClicked = onPayoutAccountClicked, onPaymentDetailsClicked = onPaymentDetailsClicked, ) Spacer(Modifier.height(16.dp)) @@ -218,6 +223,7 @@ private fun PaymentsContent( onChangeBankAccount: () -> Unit, onDiscountClicked: () -> Unit, onPaymentHistoryClicked: () -> Unit, + onPayoutAccountClicked: () -> Unit, onPaymentDetailsClicked: () -> Unit, modifier: Modifier = Modifier, ) { @@ -279,6 +285,7 @@ private fun PaymentsContent( uiState, onDiscountClicked = onDiscountClicked, onPaymentHistoryClicked = onPaymentHistoryClicked, + onPayoutAccountClicked = onPayoutAccountClicked, onPaymentDetailsClicked = onPaymentDetailsClicked, ) if (uiState is Content) { @@ -375,6 +382,7 @@ private fun PaymentsListItems( uiState: PaymentsUiState, onDiscountClicked: () -> Unit, onPaymentHistoryClicked: () -> Unit, + onPayoutAccountClicked: () -> Unit, onPaymentDetailsClicked: () -> Unit, ) { val listItemsSideSpacingModifier = Modifier @@ -398,6 +406,22 @@ private fun PaymentsListItems( .fillMaxWidth(), ) HorizontalDivider(modifier = listItemsSideSpacingModifier) + PaymentsListItem( + text = "Payout", + icon = { + Icon( + imageVector = HedvigIcons.PaymentOutline, + contentDescription = null, + modifier = Modifier.size(24.dp), + ) + }, + modifier = Modifier + .clickable(onClick = onPayoutAccountClicked) + .then(listItemsSideSpacingModifier) + .padding(vertical = 16.dp) + .fillMaxWidth(), + ) + HorizontalDivider(modifier = listItemsSideSpacingModifier) PaymentsListItem( text = stringResource(Res.string.PAYMENTS_PAYMENT_HISTORY_BUTTON_LABEL), icon = { @@ -592,6 +616,7 @@ private fun PreviewPaymentScreen( {}, {}, {}, + {}, ) } } diff --git a/app/feature/feature-payout-account/build.gradle.kts b/app/feature/feature-payout-account/build.gradle.kts new file mode 100644 index 0000000000..de9f2f598b --- /dev/null +++ b/app/feature/feature-payout-account/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("hedvig.android.library") + id("hedvig.gradle.plugin") +} + +hedvig { + serialization() + compose() +} + +dependencies { + implementation(libs.arrow.core) + implementation(libs.jetbrains.compose.runtime) + implementation(libs.jetbrains.lifecycle.runtime.compose) + implementation(libs.jetbrains.navigation.compose) + implementation(libs.koin.composeViewModel) + implementation(libs.koin.core) + implementation(projects.composeUi) + implementation(projects.coreCommonPublic) + implementation(projects.coreResources) + implementation(projects.designSystemHedvig) + implementation(projects.moleculePublic) + implementation(projects.navigationCommon) + implementation(projects.navigationCompose) + implementation(projects.navigationComposeTyped) +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt new file mode 100644 index 0000000000..2117996ec2 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt @@ -0,0 +1,78 @@ +package com.hedvig.android.feature.payoutaccount.data + +/** + * Todo bring this information from backend possibly + * https://www.bankinfrastruktur.se/media/kelmctkm/1906_clearingnummer-institut-221212_-nummerordning.pdf + */ +internal fun bankNameForClearingNumber(clearingNumber: String): String? { + val number = clearingNumber.toIntOrNull() ?: return null + return when (number) { + in 1000..1099 -> "Sveriges Riksbank" + in 1100..1199 -> "Nordea" + in 1200..1399 -> "Danske Bank" + in 1400..2099 -> "Nordea" + in 2300..2399 -> "Ålandsbanken" + in 2400..2499 -> "Danske Bank" + in 3000..3399 -> "Nordea" + in 3400..3409 -> "Länsförsäkringar Bank" + in 3410..4999 -> "Nordea" + in 5000..5999 -> "SEB" + in 6000..6999 -> "Handelsbanken" + in 7000..8999 -> "Swedbank" + in 9020..9029 -> "Länsförsäkringar Bank" + in 9040..9049 -> "Citibank" + in 9060..9069 -> "Länsförsäkringar Bank" + in 9070..9079 -> "Multitude Bank" + in 9080..9089 -> "Crédit Agricole Corporate" + in 9100..9109 -> "Nordnet Bank" + in 9120..9124 -> "SEB" + in 9130..9149 -> "SEB" + in 9150..9169 -> "Skandiabanken" + in 9170..9179 -> "IKANO Banken" + in 9180..9189 -> "Danske Bank" + in 9190..9199 -> "DNB Bank" + in 9230..9239 -> "Marginalen Bank" + in 9250..9259 -> "SBAB Bank" + in 9260..9269 -> "DNB Bank" + in 9270..9279 -> "ICA Banken" + in 9280..9289 -> "Resurs Bank" + in 9300..9349 -> "Swedbank" + in 9380..9389 -> "Pareto Securities" + in 9390..9399 -> "Landshypotek" + in 9400..9449 -> "Forex Bank" + in 9460..9469 -> "Santander Consumer Bank" + in 9470..9479 -> "BNP Paribas" + in 9490..9499 -> "Brite" + in 9500..9549 -> "Nordea" + in 9550..9569 -> "Avanza Bank" + in 9570..9579 -> "Sparbanken Syd" + in 9580..9589 -> "AION Bank" + in 9590..9599 -> "Erik Penser Bank" + in 9600..9609 -> "Banking Circle" + in 9610..9619 -> "Volvofinans Bank" + in 9620..9629 -> "Bank of China" + in 9630..9639 -> "Lån & Spar Bank" + in 9640..9649 -> "Nordax Bank" + in 9650..9659 -> "MedMera Bank" + in 9660..9669 -> "Svea Bank" + in 9670..9679 -> "JAK Medlemsbank" + in 9680..9689 -> "Bluestep Finans" + in 9690..9699 -> "Folkia" + in 9700..9709 -> "Ekobanken" + in 9710..9719 -> "Lunar Bank" + in 9750..9759 -> "Northmill Bank" + in 9770..9779 -> "Intergiro" + in 9780..9789 -> "Klarna Bank" + in 9860..9869 -> "Privatgirot" + in 9870..9879 -> "Nasdaq OMX" + in 9880..9899 -> "Riksgälden" + 9951 -> "Teller Branch Norway" + 9952 -> "Bankernas Automatbolag" + 9953 -> "Teller Branch Sweden" + 9954 -> "Kortaccept Nordic" + 9955 -> "Kommuninvest" + 9956 -> "VP Securities" + in 9960..9969 -> "Nordea" + else -> null + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt new file mode 100644 index 0000000000..fd24318165 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -0,0 +1,25 @@ +package com.hedvig.android.feature.payoutaccount.data + +import arrow.core.Either +import arrow.core.right +import com.hedvig.android.core.common.ErrorMessage + +internal interface GetPayoutAccountUseCase { + suspend fun invoke(): Either +} + +internal class GetPayoutAccountUseCaseImpl : GetPayoutAccountUseCase { + private var currentAccount: PayoutAccount = PayoutAccount.BankAccount( + clearingNumber = "3300", + accountNumber = "9202195211", + bankName = "Nordea", + ) + + fun update(payoutAccount: PayoutAccount) { + currentAccount = payoutAccount + } + + override suspend fun invoke(): Either { + return currentAccount.right() + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt new file mode 100644 index 0000000000..4d98ef58d2 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt @@ -0,0 +1,11 @@ +package com.hedvig.android.feature.payoutaccount.data + +internal sealed interface PayoutAccount { + data object Trustly : PayoutAccount + + data class BankAccount( + val clearingNumber: String, + val accountNumber: String, + val bankName: String?, + ) : PayoutAccount +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt new file mode 100644 index 0000000000..cfdbaa776a --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt @@ -0,0 +1,25 @@ +package com.hedvig.android.feature.payoutaccount.data + +import arrow.core.Either +import arrow.core.right +import com.hedvig.android.core.common.ErrorMessage + +internal interface UpdateBankAccountUseCase { + suspend fun invoke(clearingNumber: String, accountNumber: String): Either +} + +internal class UpdateBankAccountUseCaseImpl( + private val getPayoutAccountUseCase: GetPayoutAccountUseCaseImpl, +) : UpdateBankAccountUseCase { + override suspend fun invoke(clearingNumber: String, accountNumber: String): Either { + val bankName = bankNameForClearingNumber(clearingNumber) + getPayoutAccountUseCase.update( + PayoutAccount.BankAccount( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankName, + ), + ) + return Unit.right() + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt new file mode 100644 index 0000000000..22efc93d2a --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt @@ -0,0 +1,18 @@ +package com.hedvig.android.feature.payoutaccount.di + +import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCase +import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCaseImpl +import com.hedvig.android.feature.payoutaccount.data.UpdateBankAccountUseCase +import com.hedvig.android.feature.payoutaccount.data.UpdateBankAccountUseCaseImpl +import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel +import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel +import org.koin.core.module.dsl.viewModel +import org.koin.dsl.module + +val payoutAccountModule = module { + single { GetPayoutAccountUseCaseImpl() } + single { get() } + single { UpdateBankAccountUseCaseImpl(get()) } + viewModel { PayoutAccountOverviewViewModel(get()) } + viewModel { EditBankAccountViewModel(get()) } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt new file mode 100644 index 0000000000..2631514199 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt @@ -0,0 +1,17 @@ +package com.hedvig.android.feature.payoutaccount.navigation + +import com.hedvig.android.navigation.common.Destination +import kotlinx.serialization.Serializable + +sealed interface PayoutAccountDestination { + @Serializable + data object Graph : PayoutAccountDestination, Destination +} + +internal sealed interface PayoutAccountDestinations { + @Serializable + data object Overview : PayoutAccountDestinations, Destination + + @Serializable + data object EditBankAccount : PayoutAccountDestinations, Destination +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt new file mode 100644 index 0000000000..c673e3d143 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -0,0 +1,44 @@ +package com.hedvig.android.feature.payoutaccount.navigation + +import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import com.hedvig.android.compose.ui.dropUnlessResumed +import com.hedvig.android.design.system.hedvig.GlobalSnackBarState +import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountDestination +import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel +import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewDestination +import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel +import com.hedvig.android.navigation.compose.navdestination +import com.hedvig.android.navigation.compose.navgraph +import org.koin.compose.viewmodel.koinViewModel + +fun NavGraphBuilder.payoutAccountGraph( + navController: NavController, + globalSnackBarState: GlobalSnackBarState, + navigateUp: () -> Unit, +) { + navgraph( + startDestination = PayoutAccountDestinations.Overview::class, + ) { + navdestination { + val viewModel: PayoutAccountOverviewViewModel = koinViewModel() + PayoutAccountOverviewDestination( + viewModel = viewModel, + onEditBankAccountClicked = dropUnlessResumed { + navController.navigate(PayoutAccountDestinations.EditBankAccount) + }, + navigateUp = navigateUp, + ) + } + + navdestination { + val viewModel: EditBankAccountViewModel = koinViewModel() + EditBankAccountDestination( + viewModel = viewModel, + globalSnackBarState = globalSnackBarState, + navigateUp = navController::navigateUp, + ) + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt new file mode 100644 index 0000000000..9c09b66bae --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -0,0 +1,119 @@ +package com.hedvig.android.feature.payoutaccount.ui.editbankaccount + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.GlobalSnackBarState +import com.hedvig.android.design.system.hedvig.HedvigButton +import com.hedvig.android.design.system.hedvig.HedvigNotificationCard +import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigText +import com.hedvig.android.design.system.hedvig.HedvigTextField +import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults +import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority + +@Composable +internal fun EditBankAccountDestination( + viewModel: EditBankAccountViewModel, + globalSnackBarState: GlobalSnackBarState, + navigateUp: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + EditBankAccountScreen( + uiState = uiState, + globalSnackBarState = globalSnackBarState, + onSave = { viewModel.emit(EditBankAccountEvent.Save) }, + onSnackBarShown = { viewModel.emit(EditBankAccountEvent.SnackBarShown) }, + navigateUp = navigateUp, + ) +} + +@Composable +private fun EditBankAccountScreen( + uiState: EditBankAccountUiState, + globalSnackBarState: GlobalSnackBarState, + onSave: () -> Unit, + onSnackBarShown: () -> Unit, + navigateUp: () -> Unit, +) { + LaunchedEffect(uiState.showSuccessSnackBar) { + if (!uiState.showSuccessSnackBar) return@LaunchedEffect + globalSnackBarState.show("Changes saved", NotificationPriority.Campaign) + onSnackBarShown() + } + + HedvigScaffold( + topAppBarText = "Bank account", + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + Spacer(Modifier.weight(1f)) + Column(Modifier.padding(horizontal = 16.dp)) { + HedvigTextField( + state = uiState.clearingNumberState, + labelText = "Clearing", + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + inputTransformation = uiState.clearingInputTransformation, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + trailingContent = uiState.bankName?.let { + { HedvigText(text = it) } + }, + modifier = Modifier.fillMaxWidth(), + ) + Spacer(Modifier.height(4.dp)) + HedvigTextField( + state = uiState.accountNumberState, + labelText = "Account number", + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + inputTransformation = uiState.accountNumberInputTransformation, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + modifier = Modifier.fillMaxWidth(), + ) + } + AnimatedVisibility( + visible = uiState.errorMessage != null, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + HedvigNotificationCard( + message = uiState.errorMessage ?: "", + priority = NotificationPriority.Attention, + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = 4.dp) + .fillMaxWidth(), + ) + } + Spacer(Modifier.height(16.dp)) + HedvigButton( + text = "Save", + onClick = onSave, + enabled = !uiState.isLoading && + uiState.clearingNumberState.text.isNotBlank() && + uiState.accountNumberState.text.isNotBlank(), + isLoading = uiState.isLoading, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + Spacer(Modifier.height(16.dp)) + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt new file mode 100644 index 0000000000..fa4dc02f5e --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -0,0 +1,125 @@ +package com.hedvig.android.feature.payoutaccount.ui.editbankaccount + +import androidx.compose.foundation.text.input.InputTransformation +import androidx.compose.foundation.text.input.TextFieldBuffer +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.maxLength +import androidx.compose.foundation.text.input.placeCursorAtEnd +import androidx.compose.foundation.text.input.then +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import com.hedvig.android.feature.payoutaccount.data.UpdateBankAccountUseCase +import com.hedvig.android.feature.payoutaccount.data.bankNameForClearingNumber +import com.hedvig.android.molecule.public.MoleculePresenter +import com.hedvig.android.molecule.public.MoleculePresenterScope +import com.hedvig.android.molecule.public.MoleculeViewModel +import okio.`-DeprecatedOkio`.buffer + +internal class EditBankAccountViewModel( + updateBankAccountUseCase: UpdateBankAccountUseCase, +) : MoleculeViewModel( + EditBankAccountUiState(TextFieldState(), TextFieldState(), null, false, null, false), + EditBankAccountPresenter(updateBankAccountUseCase), +) + +internal sealed interface EditBankAccountEvent { + data object Save : EditBankAccountEvent + + data object SnackBarShown : EditBankAccountEvent +} + +internal data class EditBankAccountUiState( + val clearingNumberState: TextFieldState, + val accountNumberState: TextFieldState, + val bankName: String?, + val isLoading: Boolean, + val errorMessage: String?, + val showSuccessSnackBar: Boolean, +) { + // Swedish clearing numbers are 4 digits for most banks, 5 for Swedbank's 8-series + val clearingInputTransformation: InputTransformation = InputTransformation.maxLength(5).digitsOnly() + + // Swedish account numbers are up to 10 digits + val accountNumberInputTransformation: InputTransformation = InputTransformation.maxLength(10).digitsOnly() +} + +internal class EditBankAccountPresenter( + private val updateBankAccountUseCase: UpdateBankAccountUseCase, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present( + lastState: EditBankAccountUiState, + ): EditBankAccountUiState { + val clearingNumberState = remember { lastState.clearingNumberState } + val accountNumberState = remember { lastState.accountNumberState } + val bankName = bankNameForClearingNumber(clearingNumberState.text.toString()) + var isLoading by remember { mutableStateOf(false) } + var errorMessage by remember { mutableStateOf(null) } + var showSuccessSnackBar by remember { mutableStateOf(false) } + var saveIteration by remember { mutableStateOf?>(null) } + + val currentSave = saveIteration + if (currentSave != null) { + LaunchedEffect(currentSave) { + isLoading = true + errorMessage = null + updateBankAccountUseCase.invoke(currentSave.first, currentSave.second).fold( + ifLeft = { + isLoading = false + errorMessage = "Something went wrong, please try again" + saveIteration = null + }, + ifRight = { + isLoading = false + showSuccessSnackBar = true + saveIteration = null + }, + ) + } + } + + CollectEvents { event -> + when (event) { + EditBankAccountEvent.Save -> { + if (!isLoading) { + saveIteration = clearingNumberState.text.toString() to accountNumberState.text.toString() + } + } + + EditBankAccountEvent.SnackBarShown -> { + showSuccessSnackBar = false + } + } + } + + return EditBankAccountUiState( + clearingNumberState = clearingNumberState, + accountNumberState = accountNumberState, + bankName = bankName, + isLoading = isLoading, + errorMessage = errorMessage, + showSuccessSnackBar = showSuccessSnackBar, + ) + } +} + +@Stable +fun InputTransformation.digitsOnly(): InputTransformation = this.then(DigitsOnlyTransformation) + +private data object DigitsOnlyTransformation : InputTransformation { + override fun TextFieldBuffer.transformInput() { + val current = toString() + val filtered = current.filter { it.isDigit() } + if (filtered.length != current.length) { + replace(0, current.length, filtered) + placeCursorAtEnd() + } + } + + override fun toString(): String = "InputTransformation.DigitsOnly" +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt new file mode 100644 index 0000000000..464cf13133 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -0,0 +1,129 @@ +package com.hedvig.android.feature.payoutaccount.ui.overview + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.HedvigButton +import com.hedvig.android.design.system.hedvig.HedvigErrorSection +import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProgressDebounced +import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigTextField +import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults +import com.hedvig.android.feature.payoutaccount.data.PayoutAccount + +@Composable +internal fun PayoutAccountOverviewDestination( + viewModel: PayoutAccountOverviewViewModel, + onEditBankAccountClicked: () -> Unit, + navigateUp: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + PayoutAccountOverviewScreen( + uiState = uiState, + onEditBankAccountClicked = onEditBankAccountClicked, + onRetry = { viewModel.emit(PayoutAccountOverviewEvent.Retry) }, + navigateUp = navigateUp, + ) +} + +@Composable +private fun PayoutAccountOverviewScreen( + uiState: PayoutAccountOverviewUiState, + onEditBankAccountClicked: () -> Unit, + onRetry: () -> Unit, + navigateUp: () -> Unit, +) { + HedvigScaffold( + topAppBarText = "Payout account", + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + when (uiState) { + PayoutAccountOverviewUiState.Loading -> { + HedvigFullScreenCenterAlignedProgressDebounced( + Modifier + .weight(1f) + .wrapContentHeight(), + ) + } + + PayoutAccountOverviewUiState.Error -> { + HedvigErrorSection( + onButtonClick = onRetry, + modifier = Modifier + .weight(1f) + .wrapContentHeight(), + ) + } + + is PayoutAccountOverviewUiState.Content -> { + PayoutAccountContent( + payoutAccount = uiState.payoutAccount, + onEditBankAccountClicked = onEditBankAccountClicked, + ) + } + } + } +} + +@Composable +private fun PayoutAccountContent(payoutAccount: PayoutAccount, onEditBankAccountClicked: () -> Unit) { + Column { + Spacer(Modifier.height(8.dp)) + when (payoutAccount) { + PayoutAccount.Trustly -> { + HedvigTextField( + text = "Trustly", + onValueChange = {}, + labelText = "Account", + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + readOnly = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + + is PayoutAccount.BankAccount -> { + val displayText = buildString { + if (payoutAccount.bankName != null) { + append(payoutAccount.bankName) + append(" ") + } + append(payoutAccount.clearingNumber) + append("-") + append(payoutAccount.accountNumber) + } + HedvigTextField( + text = displayText, + onValueChange = {}, + labelText = "Account", + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + readOnly = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + Spacer(Modifier.height(8.dp)) + HedvigButton( + text = "Edit account", + onClick = onEditBankAccountClicked, + enabled = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + } + Spacer(Modifier.height(16.dp)) + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt new file mode 100644 index 0000000000..17f2ecbac9 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt @@ -0,0 +1,60 @@ +package com.hedvig.android.feature.payoutaccount.ui.overview + +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.setValue +import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCase +import com.hedvig.android.feature.payoutaccount.data.PayoutAccount +import com.hedvig.android.molecule.public.MoleculePresenter +import com.hedvig.android.molecule.public.MoleculePresenterScope +import com.hedvig.android.molecule.public.MoleculeViewModel + +internal class PayoutAccountOverviewViewModel( + getPayoutAccountUseCase: GetPayoutAccountUseCase, +) : MoleculeViewModel( + PayoutAccountOverviewUiState.Loading, + PayoutAccountOverviewPresenter(getPayoutAccountUseCase), + ) + +internal sealed interface PayoutAccountOverviewEvent { + data object Retry : PayoutAccountOverviewEvent +} + +internal sealed interface PayoutAccountOverviewUiState { + data object Loading : PayoutAccountOverviewUiState + + data object Error : PayoutAccountOverviewUiState + + data class Content(val payoutAccount: PayoutAccount) : PayoutAccountOverviewUiState +} + +internal class PayoutAccountOverviewPresenter( + private val getPayoutAccountUseCase: GetPayoutAccountUseCase, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present( + lastState: PayoutAccountOverviewUiState, + ): PayoutAccountOverviewUiState { + var loadIteration by remember { mutableStateOf(0) } + var uiState by remember { mutableStateOf(lastState) } + + LaunchedEffect(loadIteration) { + uiState = PayoutAccountOverviewUiState.Loading + getPayoutAccountUseCase.invoke().fold( + ifLeft = { uiState = PayoutAccountOverviewUiState.Error }, + ifRight = { account -> uiState = PayoutAccountOverviewUiState.Content(account) }, + ) + } + + CollectEvents { event -> + when (event) { + PayoutAccountOverviewEvent.Retry -> loadIteration++ + } + } + + return uiState + } +} From 858660f43f4e83204a602d291c641f282b671e51 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 15 Apr 2026 11:33:03 +0200 Subject: [PATCH 02/50] Add logic for new payout connection possibility through the app --- CLAUDE.md | 1 + .../android/apollo/octopus/schema.graphqls | 12 ++- .../android/app/navigation/HedvigNavHost.kt | 3 +- .../main/graphql/SetupTrustlyPayout.graphql | 16 ++++ .../StartTrustlyPayoutSessionUseCase.kt | 36 +++++++ .../payment/trustly/TrustlyPayoutPresenter.kt | 88 ++++++++++++++++++ .../payment/trustly/TrustlyPayoutViewModel.kt | 18 ++++ .../trustly/di/ConnectPaymentTrustlyModule.kt | 12 +++ .../navigation/ConnectTrustlyPaymentGraph.kt | 11 +++ .../payment/trustly/ui/TrustlyDestination.kt | 10 +- .../feature-payout-account/build.gradle.kts | 7 ++ .../src/main/graphql/GetPayoutMethods.graphql | 23 +++++ .../main/graphql/SetupNordeaPayout.graphql | 10 ++ .../src/main/graphql/SetupSwishPayout.graphql | 8 ++ .../data/GetPayoutAccountUseCase.kt | 77 ++++++++++++--- .../payoutaccount/data/PayoutAccount.kt | 2 + .../data/SetupNordeaPayoutUseCase.kt | 39 ++++++++ .../data/SetupSwishPayoutUseCase.kt | 33 +++++++ .../data/UpdateBankAccountUseCase.kt | 25 ----- .../payoutaccount/di/PayoutAccountModule.kt | 21 +++-- .../navigation/PayoutAccountDestination.kt | 8 ++ .../navigation/PayoutAccountGraph.kt | 36 ++++++- .../EditBankAccountDestination.kt | 12 +-- .../EditBankAccountViewModel.kt | 29 +++--- .../PayoutAccountOverviewDestination.kt | 77 +++++++++++++-- .../PayoutAccountOverviewViewModel.kt | 13 ++- .../SelectPayoutMethodDestination.kt | 86 +++++++++++++++++ .../setupswish/SetupSwishPayoutDestination.kt | 93 +++++++++++++++++++ .../setupswish/SetupSwishPayoutViewModel.kt | 83 +++++++++++++++++ 29 files changed, 800 insertions(+), 89 deletions(-) create mode 100644 app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql create mode 100644 app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt create mode 100644 app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt create mode 100644 app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt create mode 100644 app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql create mode 100644 app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql create mode 100644 app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt delete mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt diff --git a/CLAUDE.md b/CLAUDE.md index 71031c0c27..daedd47cb0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -194,6 +194,7 @@ val applicationModule = module { - Use `Provider` when we need a different implementation for the demo mode of the App, which we very rarely do. We always do that using `ProdOrDemoProvider` - Each feature/data module has its own DI module - Common dependencies (logging, tracking) auto-injected by build plugin +- When a Presenter or ViewModel needs to call a use case, always inject the use case directly as a typed dependency — never abstract it into an anonymous `suspend () -> T` lambda. If two separate operations are needed (e.g. payin vs payout setup), create two separate, dedicated use case classes and two separate presenters. Do not create a shared interface just to enable reuse through a single presenter. ### Data Layer 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 288983627b..df38b7dc8f 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 @@ -1728,6 +1728,11 @@ type ExternalInsurer { displayName: String! insurelyId: String } +type FetchedExternalInsurance { + displayName: String! + subtitle: String + insurer: ExternalInsurer! +} type FirstVetAction { sections: [FirstVetSection!]! } @@ -3711,7 +3716,7 @@ input PaymentMethodSetupInvoicePayinInput { """ Set up invoice payment method as default. """ - setAsDefaultPayout: Boolean! + setAsDefaultPayin: Boolean! } input PaymentMethodSetupNordeaPayoutInput { """ @@ -3905,6 +3910,11 @@ type PriceIntent { When 'true' it means user has gone trough Insurely flow with that price intent """ hasCollectedInsurelyData: Boolean! + """ + List of external insurances fetched via Insurely that correspond to products Hedvig offers. + Null when no Insurely data collection has been associated with this price intent. + """ + fetchedExternalInsurances: [FetchedExternalInsurance!] } enum PriceIntentAnimal { CAT diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 73f92eb3ac..04505af23c 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -36,6 +36,7 @@ import com.hedvig.android.feature.claimhistory.nav.ClaimHistoryDestination import com.hedvig.android.feature.claimhistory.nav.claimHistoryGraph import com.hedvig.android.feature.connect.payment.connectPaymentGraph import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyDestination +import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyPayoutDestination import com.hedvig.android.feature.deleteaccount.navigation.DeleteAccountDestination import com.hedvig.android.feature.deleteaccount.navigation.deleteAccountGraph import com.hedvig.android.feature.editcoinsured.navigation.EditCoInsuredDestination.CoInsuredAddInfo @@ -348,7 +349,7 @@ internal fun HedvigNavHost( ) payoutAccountGraph( navController = navController, - globalSnackBarState = globalSnackBarState, + navigateToTrustlyPayout = { navController.navigate(TrustlyPayoutDestination) }, navigateUp = navController::navigateUp, ) profileGraph( diff --git a/app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql b/app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql new file mode 100644 index 0000000000..2b1fe46243 --- /dev/null +++ b/app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql @@ -0,0 +1,16 @@ +mutation SetupTrustlyPayout($successUrl: String!, $failureUrl: String!) { + paymentMethodSetupTrustly( + input: { + setAsDefaultPayin: false, + setAsDefaultPayout: true, + successUrl: $successUrl, + failureUrl: $failureUrl + } + ) { + status + url + error { + message + } + } +} diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt new file mode 100644 index 0000000000..4752f491ad --- /dev/null +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt @@ -0,0 +1,36 @@ +package com.hedvig.android.feature.connect.payment.trustly + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensureNotNull +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.ErrorMessage +import com.hedvig.android.apollo.safeExecute +import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback +import com.hedvig.android.logger.logcat +import octopus.SetupTrustlyPayoutMutation + +internal class StartTrustlyPayoutSessionUseCase( + private val apolloClient: ApolloClient, + private val trustlyCallback: TrustlyCallback, +) { + suspend fun invoke(): Either { + return either { + val data = apolloClient + .mutation( + SetupTrustlyPayoutMutation( + successUrl = trustlyCallback.successUrl, + failureUrl = trustlyCallback.failureUrl, + ), + ) + .safeExecute(::ErrorMessage) + .bind() + logcat { "StartTrustlyPayoutSessionUseCase received: ${data.paymentMethodSetupTrustly}" } + val url = ensureNotNull(data.paymentMethodSetupTrustly.url) { + ErrorMessage("Trustly payout setup returned no URL") + } + TrustlyInitiateProcessUrl(url) + } + } +} diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt new file mode 100644 index 0000000000..54da291b63 --- /dev/null +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt @@ -0,0 +1,88 @@ +package com.hedvig.android.feature.connect.payment.trustly + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.core.common.safeCast +import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback +import com.hedvig.android.molecule.public.MoleculePresenter +import com.hedvig.android.molecule.public.MoleculePresenterScope + +internal class TrustlyPayoutPresenter( + private val trustlyCallback: TrustlyCallback, + private val startTrustlyPayoutSessionUseCase: StartTrustlyPayoutSessionUseCase, + private val cacheManager: NetworkCacheManager, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present(lastState: TrustlyUiState): TrustlyUiState { + var browsing: TrustlyUiState.Browsing? by remember { + mutableStateOf(lastState.safeCast()) + } + var startSessionError: ErrorMessage? by remember { mutableStateOf(null) } + var connectingCardFailed by remember { mutableStateOf(lastState is TrustlyUiState.FailedToConnectCard) } + var succeededInConnectingCard by remember { mutableStateOf(lastState is TrustlyUiState.SucceededInConnectingCard) } + + var loadIteration by remember { mutableIntStateOf(0) } + + LaunchedEffect(loadIteration) { + if (browsing != null) return@LaunchedEffect + if (startSessionError != null) return@LaunchedEffect + if (connectingCardFailed) return@LaunchedEffect + if (succeededInConnectingCard) return@LaunchedEffect + startTrustlyPayoutSessionUseCase.invoke().fold( + ifLeft = { + startSessionError = it + browsing = null + }, + ifRight = { + startSessionError = null + browsing = TrustlyUiState.Browsing(it.url, trustlyCallback) + }, + ) + } + + CollectEvents { event -> + when (event) { + TrustlyEvent.ConnectingCardFailed -> { + connectingCardFailed = true + } + + TrustlyEvent.ConnectingCardSucceeded -> { + succeededInConnectingCard = true + } + + TrustlyEvent.RetryConnectingCard -> { + browsing = null + startSessionError = null + connectingCardFailed = false + succeededInConnectingCard = false + loadIteration++ + } + } + } + + if (succeededInConnectingCard) { + LaunchedEffect(Unit) { + cacheManager.clearCache() + } + return TrustlyUiState.SucceededInConnectingCard + } + if (connectingCardFailed) { + return TrustlyUiState.FailedToConnectCard + } + if (startSessionError != null) { + return TrustlyUiState.FailedToStartSession + } + val browsingValue = browsing + if (browsingValue != null) { + return browsingValue + } + return TrustlyUiState.Loading + } +} diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt new file mode 100644 index 0000000000..36ed291cba --- /dev/null +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt @@ -0,0 +1,18 @@ +package com.hedvig.android.feature.connect.payment.trustly + +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback +import com.hedvig.android.molecule.public.MoleculeViewModel + +internal class TrustlyPayoutViewModel( + trustlyCallback: TrustlyCallback, + startTrustlyPayoutSessionUseCase: StartTrustlyPayoutSessionUseCase, + networkCacheManager: NetworkCacheManager, +) : MoleculeViewModel( + TrustlyUiState.Loading, + TrustlyPayoutPresenter( + trustlyCallback, + startTrustlyPayoutSessionUseCase, + networkCacheManager, + ), + ) diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt index 9b4f4182f8..a00ecf9cb2 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt @@ -3,7 +3,9 @@ package com.hedvig.android.feature.connect.payment.trustly.di import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.core.buildconstants.HedvigBuildConstants +import com.hedvig.android.feature.connect.payment.trustly.StartTrustlyPayoutSessionUseCase import com.hedvig.android.feature.connect.payment.trustly.StartTrustlySessionUseCase +import com.hedvig.android.feature.connect.payment.trustly.TrustlyPayoutViewModel import com.hedvig.android.feature.connect.payment.trustly.TrustlyViewModel import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallbackImpl @@ -15,6 +17,9 @@ val connectPaymentTrustlyModule = module { single { StartTrustlySessionUseCase(get(), get()) } + single { + StartTrustlyPayoutSessionUseCase(get(), get()) + } viewModel { TrustlyViewModel( trustlyCallback = get(), @@ -22,4 +27,11 @@ val connectPaymentTrustlyModule = module { networkCacheManager = get(), ) } + viewModel { + TrustlyPayoutViewModel( + trustlyCallback = get(), + startTrustlyPayoutSessionUseCase = get(), + networkCacheManager = get(), + ) + } } diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt index ae0d4d83e0..a63c8eaff6 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt @@ -2,8 +2,10 @@ package com.hedvig.android.feature.connect.payment import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import com.hedvig.android.feature.connect.payment.trustly.TrustlyPayoutViewModel import com.hedvig.android.feature.connect.payment.trustly.TrustlyViewModel import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyDestination +import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyPayoutDestination import com.hedvig.android.navigation.compose.navDeepLinks import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.core.HedvigDeepLinkContainer @@ -26,4 +28,13 @@ fun NavGraphBuilder.connectPaymentGraph( finishTrustlyFlow = navController::popBackStack, ) } + + navdestination { + val viewModel: TrustlyPayoutViewModel = koinViewModel() + TrustlyDestination( + viewModel = viewModel, + navigateUp = navController::navigateUp, + finishTrustlyFlow = navController::popBackStack, + ) + } } diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt index f0acc11a1b..3c1c3b90f6 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt @@ -41,6 +41,7 @@ import com.hedvig.android.feature.connect.payment.trustly.TrustlyEvent import com.hedvig.android.feature.connect.payment.trustly.TrustlyUiState import com.hedvig.android.feature.connect.payment.trustly.TrustlyViewModel import com.hedvig.android.feature.connect.payment.trustly.data.PreviewTrustlyCallback +import com.hedvig.android.molecule.public.MoleculeViewModel import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebChromeClient import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebView import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebViewClient @@ -58,8 +59,15 @@ import org.jetbrains.compose.resources.stringResource @Serializable data object TrustlyDestination : Destination +@Serializable +data object TrustlyPayoutDestination : Destination + @Composable -internal fun TrustlyDestination(viewModel: TrustlyViewModel, navigateUp: () -> Unit, finishTrustlyFlow: () -> Unit) { +internal fun TrustlyDestination( + viewModel: MoleculeViewModel, + navigateUp: () -> Unit, + finishTrustlyFlow: () -> Unit, +) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() TrustlyScreen( uiState = uiState, diff --git a/app/feature/feature-payout-account/build.gradle.kts b/app/feature/feature-payout-account/build.gradle.kts index de9f2f598b..73a55cf270 100644 --- a/app/feature/feature-payout-account/build.gradle.kts +++ b/app/feature/feature-payout-account/build.gradle.kts @@ -4,12 +4,19 @@ plugins { } hedvig { + apollo("octopus") serialization() compose() } dependencies { + implementation(libs.apollo.normalizedCache) + implementation(libs.apollo.runtime) implementation(libs.arrow.core) + implementation(projects.apolloCore) + implementation(projects.apolloNetworkCacheManager) + implementation(projects.apolloOctopusPublic) + implementation(projects.coreBuildConstants) implementation(libs.jetbrains.compose.runtime) implementation(libs.jetbrains.lifecycle.runtime.compose) implementation(libs.jetbrains.navigation.compose) diff --git a/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql new file mode 100644 index 0000000000..6c74da40e7 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql @@ -0,0 +1,23 @@ +query GetPayoutMethods { + currentMember { + paymentMethods { + defaultPayoutMethod { + id + provider + details { + ... on PaymentMethodBankAccountDetails { + account + bank + } + ... on PaymentMethodSwishDetails { + phoneNumber + } + } + } + availableMethods { + provider + supportsPayout + } + } + } +} diff --git a/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql b/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql new file mode 100644 index 0000000000..12b22d1dcc --- /dev/null +++ b/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql @@ -0,0 +1,10 @@ +mutation SetupNordeaPayout($clearingNumber: String!, $accountNumber: String!) { + paymentMethodSetupNordeaPayout( + input: { setAsDefault: true, clearingNumber: $clearingNumber, accountNumber: $accountNumber } + ) { + status + error { + message + } + } +} diff --git a/app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql b/app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql new file mode 100644 index 0000000000..e8d76c73f0 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql @@ -0,0 +1,8 @@ +mutation SetupSwishPayout($phoneNumber: String!) { + paymentMethodSetupSwishPayout( + input: { setAsDefault: true, phoneNumber: $phoneNumber } + ) { + status + error { message } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index fd24318165..5e79eb0cbd 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -1,25 +1,76 @@ package com.hedvig.android.feature.payoutaccount.data import arrow.core.Either -import arrow.core.right +import arrow.core.raise.either +import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.cache.normalized.FetchPolicy +import com.apollographql.apollo.cache.normalized.fetchPolicy +import com.hedvig.android.apollo.ErrorMessage +import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage +import octopus.GetPayoutMethodsQuery +import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodBankAccountDetails +import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodSwishDetails +import octopus.type.MemberPaymentProvider + +internal data class PayoutAccountData( + val currentMethod: PayoutAccount?, + val availablePayoutMethods: List, +) internal interface GetPayoutAccountUseCase { - suspend fun invoke(): Either + suspend fun invoke(): Either } -internal class GetPayoutAccountUseCaseImpl : GetPayoutAccountUseCase { - private var currentAccount: PayoutAccount = PayoutAccount.BankAccount( - clearingNumber = "3300", - accountNumber = "9202195211", - bankName = "Nordea", - ) +internal class GetPayoutAccountUseCaseImpl( + private val apolloClient: ApolloClient, +) : GetPayoutAccountUseCase { + override suspend fun invoke(): Either = either { + val result = apolloClient + .query(GetPayoutMethodsQuery()) + .fetchPolicy(FetchPolicy.NetworkOnly) + .safeExecute(::ErrorMessage) + .bind() - fun update(payoutAccount: PayoutAccount) { - currentAccount = payoutAccount - } + val paymentMethods = result.currentMember.paymentMethods + val currentMethod = paymentMethods.defaultPayoutMethod?.let { method -> + when (method.provider) { + MemberPaymentProvider.TRUSTLY -> PayoutAccount.Trustly + MemberPaymentProvider.SWISH -> { + val swishDetails = method.details.asPaymentMethodSwishDetails() + if (swishDetails != null) { + PayoutAccount.SwishPayout(phoneNumber = swishDetails.phoneNumber) + } else { + null + } + } + MemberPaymentProvider.NORDEA -> { + val bankAccountDetails = method.details.asPaymentMethodBankAccountDetails() + if (bankAccountDetails != null) { + val account = bankAccountDetails.account + val dashIndex = account.indexOf('-') + val clearingNumber = if (dashIndex >= 0) account.substring(0, dashIndex) else account + val accountNumber = if (dashIndex >= 0) account.substring(dashIndex + 1) else "" + PayoutAccount.BankAccount( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankAccountDetails.bank, + ) + } else { + null + } + } + else -> null + } + } + + val availablePayoutMethods = paymentMethods.availableMethods + .filter { it.supportsPayout } + .map { it.provider } - override suspend fun invoke(): Either { - return currentAccount.right() + PayoutAccountData( + currentMethod = currentMethod, + availablePayoutMethods = availablePayoutMethods, + ) } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt index 4d98ef58d2..228fb4e454 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt @@ -3,6 +3,8 @@ package com.hedvig.android.feature.payoutaccount.data internal sealed interface PayoutAccount { data object Trustly : PayoutAccount + data class SwishPayout(val phoneNumber: String) : PayoutAccount + data class BankAccount( val clearingNumber: String, val accountNumber: String, diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt new file mode 100644 index 0000000000..8369ed530f --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt @@ -0,0 +1,39 @@ +package com.hedvig.android.feature.payoutaccount.data + +import arrow.core.Either +import arrow.core.left +import arrow.core.raise.either +import arrow.core.right +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.ErrorMessage +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.apollo.safeExecute +import com.hedvig.android.core.common.ErrorMessage +import octopus.SetupNordeaPayoutMutation +import octopus.type.PaymentMethodSetupStatus + +internal interface SetupNordeaPayoutUseCase { + suspend fun invoke(clearingNumber: String, accountNumber: String): Either +} + +internal class SetupNordeaPayoutUseCaseImpl( + private val apolloClient: ApolloClient, + private val networkCacheManager: NetworkCacheManager, +) : SetupNordeaPayoutUseCase { + override suspend fun invoke(clearingNumber: String, accountNumber: String): Either = either { + val result = apolloClient + .mutation(SetupNordeaPayoutMutation(clearingNumber = clearingNumber, accountNumber = accountNumber)) + .safeExecute(::ErrorMessage) + .bind() + + val output = result.paymentMethodSetupNordeaPayout + when (output.status) { + PaymentMethodSetupStatus.FAILED -> { + raise(ErrorMessage(output.error?.message ?: "Failed to set up payout method")) + } + else -> { + networkCacheManager.clearCache() + } + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt new file mode 100644 index 0000000000..84b11df5e1 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt @@ -0,0 +1,33 @@ +package com.hedvig.android.feature.payoutaccount.data + +import arrow.core.Either +import arrow.core.raise.either +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.ErrorMessage +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.apollo.safeExecute +import com.hedvig.android.core.common.ErrorMessage +import octopus.SetupSwishPayoutMutation +import octopus.type.PaymentMethodSetupStatus + +internal class SetupSwishPayoutUseCase( + private val apolloClient: ApolloClient, + private val networkCacheManager: NetworkCacheManager, +) { + suspend fun invoke(phoneNumber: String): Either = either { + val result = apolloClient + .mutation(SetupSwishPayoutMutation(phoneNumber = phoneNumber)) + .safeExecute(::ErrorMessage) + .bind() + + val output = result.paymentMethodSetupSwishPayout + when (output.status) { + PaymentMethodSetupStatus.FAILED -> { + raise(ErrorMessage(output.error?.message ?: "Failed to set up Swish payout")) + } + else -> { + networkCacheManager.clearCache() + } + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt deleted file mode 100644 index cfdbaa776a..0000000000 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/UpdateBankAccountUseCase.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.hedvig.android.feature.payoutaccount.data - -import arrow.core.Either -import arrow.core.right -import com.hedvig.android.core.common.ErrorMessage - -internal interface UpdateBankAccountUseCase { - suspend fun invoke(clearingNumber: String, accountNumber: String): Either -} - -internal class UpdateBankAccountUseCaseImpl( - private val getPayoutAccountUseCase: GetPayoutAccountUseCaseImpl, -) : UpdateBankAccountUseCase { - override suspend fun invoke(clearingNumber: String, accountNumber: String): Either { - val bankName = bankNameForClearingNumber(clearingNumber) - getPayoutAccountUseCase.update( - PayoutAccount.BankAccount( - clearingNumber = clearingNumber, - accountNumber = accountNumber, - bankName = bankName, - ), - ) - return Unit.right() - } -} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt index 22efc93d2a..3d21498662 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt @@ -1,18 +1,27 @@ package com.hedvig.android.feature.payoutaccount.di +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCase import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCaseImpl -import com.hedvig.android.feature.payoutaccount.data.UpdateBankAccountUseCase -import com.hedvig.android.feature.payoutaccount.data.UpdateBankAccountUseCaseImpl +import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCase +import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCaseImpl +import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel +import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import org.koin.core.module.dsl.viewModel import org.koin.dsl.module val payoutAccountModule = module { - single { GetPayoutAccountUseCaseImpl() } - single { get() } - single { UpdateBankAccountUseCaseImpl(get()) } + single { GetPayoutAccountUseCaseImpl(get()) } + single { + SetupNordeaPayoutUseCaseImpl(get(), get()) + } + single { + SetupSwishPayoutUseCase(get(), get()) + } viewModel { PayoutAccountOverviewViewModel(get()) } - viewModel { EditBankAccountViewModel(get()) } + viewModel { EditBankAccountViewModel(get()) } + viewModel { SetupSwishPayoutViewModel(get()) } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt index 2631514199..aabd195ec5 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt @@ -12,6 +12,14 @@ internal sealed interface PayoutAccountDestinations { @Serializable data object Overview : PayoutAccountDestinations, Destination + @Serializable + data class SelectPayoutMethod( + val availableProviders: List, + ) : PayoutAccountDestinations, Destination + @Serializable data object EditBankAccount : PayoutAccountDestinations, Destination + + @Serializable + data object SetupSwishPayout : PayoutAccountDestinations, Destination } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index c673e3d143..4a330fd1ad 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -3,19 +3,22 @@ package com.hedvig.android.feature.payoutaccount.navigation import androidx.lifecycle.compose.dropUnlessResumed import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder -import com.hedvig.android.compose.ui.dropUnlessResumed -import com.hedvig.android.design.system.hedvig.GlobalSnackBarState import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountDestination import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewDestination +import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel +import com.hedvig.android.feature.payoutaccount.ui.selectmethod.SelectPayoutMethodDestination +import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutDestination +import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.compose.navgraph +import octopus.type.MemberPaymentProvider import org.koin.compose.viewmodel.koinViewModel fun NavGraphBuilder.payoutAccountGraph( navController: NavController, - globalSnackBarState: GlobalSnackBarState, + navigateToTrustlyPayout: () -> Unit, navigateUp: () -> Unit, ) { navgraph( @@ -25,6 +28,14 @@ fun NavGraphBuilder.payoutAccountGraph( val viewModel: PayoutAccountOverviewViewModel = koinViewModel() PayoutAccountOverviewDestination( viewModel = viewModel, + onConnectPayoutMethodClicked = dropUnlessResumed { + val content = viewModel.uiState.value as? PayoutAccountOverviewUiState.Content + navController.navigate( + PayoutAccountDestinations.SelectPayoutMethod( + availableProviders = content?.availablePayoutMethods?.map { it.rawValue } ?: emptyList(), + ), + ) + }, onEditBankAccountClicked = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.EditBankAccount) }, @@ -32,11 +43,28 @@ fun NavGraphBuilder.payoutAccountGraph( ) } + navdestination { + SelectPayoutMethodDestination( + availableProviders = this.availableProviders.map { MemberPaymentProvider.safeValueOf(it) }, + onTrustlySelected = dropUnlessResumed { navigateToTrustlyPayout() }, + onNordeaSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.EditBankAccount) }, + onSwishSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.SetupSwishPayout) }, + navigateUp = navController::navigateUp, + ) + } + navdestination { val viewModel: EditBankAccountViewModel = koinViewModel() EditBankAccountDestination( viewModel = viewModel, - globalSnackBarState = globalSnackBarState, + navigateUp = navController::navigateUp, + ) + } + + navdestination { + val viewModel: SetupSwishPayoutViewModel = koinViewModel() + SetupSwishPayoutDestination( + viewModel = viewModel, navigateUp = navController::navigateUp, ) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index 9c09b66bae..012575d59f 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.hedvig.android.design.system.hedvig.GlobalSnackBarState import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigScaffold @@ -29,15 +28,12 @@ import com.hedvig.android.design.system.hedvig.NotificationDefaults.Notification @Composable internal fun EditBankAccountDestination( viewModel: EditBankAccountViewModel, - globalSnackBarState: GlobalSnackBarState, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() EditBankAccountScreen( uiState = uiState, - globalSnackBarState = globalSnackBarState, onSave = { viewModel.emit(EditBankAccountEvent.Save) }, - onSnackBarShown = { viewModel.emit(EditBankAccountEvent.SnackBarShown) }, navigateUp = navigateUp, ) } @@ -45,15 +41,11 @@ internal fun EditBankAccountDestination( @Composable private fun EditBankAccountScreen( uiState: EditBankAccountUiState, - globalSnackBarState: GlobalSnackBarState, onSave: () -> Unit, - onSnackBarShown: () -> Unit, navigateUp: () -> Unit, ) { - LaunchedEffect(uiState.showSuccessSnackBar) { - if (!uiState.showSuccessSnackBar) return@LaunchedEffect - globalSnackBarState.show("Changes saved", NotificationPriority.Campaign) - onSnackBarShown() + LaunchedEffect(uiState.navigateBack) { + if (uiState.navigateBack) navigateUp() } HedvigScaffold( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index fa4dc02f5e..832af1b486 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -13,24 +13,21 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import com.hedvig.android.feature.payoutaccount.data.UpdateBankAccountUseCase +import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCase import com.hedvig.android.feature.payoutaccount.data.bankNameForClearingNumber import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope import com.hedvig.android.molecule.public.MoleculeViewModel -import okio.`-DeprecatedOkio`.buffer internal class EditBankAccountViewModel( - updateBankAccountUseCase: UpdateBankAccountUseCase, + setupNordeaPayoutUseCase: SetupNordeaPayoutUseCase, ) : MoleculeViewModel( EditBankAccountUiState(TextFieldState(), TextFieldState(), null, false, null, false), - EditBankAccountPresenter(updateBankAccountUseCase), + EditBankAccountPresenter(setupNordeaPayoutUseCase), ) internal sealed interface EditBankAccountEvent { data object Save : EditBankAccountEvent - - data object SnackBarShown : EditBankAccountEvent } internal data class EditBankAccountUiState( @@ -39,7 +36,7 @@ internal data class EditBankAccountUiState( val bankName: String?, val isLoading: Boolean, val errorMessage: String?, - val showSuccessSnackBar: Boolean, + val navigateBack: Boolean, ) { // Swedish clearing numbers are 4 digits for most banks, 5 for Swedbank's 8-series val clearingInputTransformation: InputTransformation = InputTransformation.maxLength(5).digitsOnly() @@ -49,7 +46,7 @@ internal data class EditBankAccountUiState( } internal class EditBankAccountPresenter( - private val updateBankAccountUseCase: UpdateBankAccountUseCase, + private val setupNordeaPayoutUseCase: SetupNordeaPayoutUseCase, ) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present( @@ -60,7 +57,7 @@ internal class EditBankAccountPresenter( val bankName = bankNameForClearingNumber(clearingNumberState.text.toString()) var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } - var showSuccessSnackBar by remember { mutableStateOf(false) } + var navigateBack by remember { mutableStateOf(false) } var saveIteration by remember { mutableStateOf?>(null) } val currentSave = saveIteration @@ -68,15 +65,15 @@ internal class EditBankAccountPresenter( LaunchedEffect(currentSave) { isLoading = true errorMessage = null - updateBankAccountUseCase.invoke(currentSave.first, currentSave.second).fold( + setupNordeaPayoutUseCase.invoke(currentSave.first, currentSave.second).fold( ifLeft = { isLoading = false - errorMessage = "Something went wrong, please try again" + errorMessage = it.message ?: "Something went wrong, please try again" saveIteration = null }, ifRight = { isLoading = false - showSuccessSnackBar = true + navigateBack = true saveIteration = null }, ) @@ -90,10 +87,6 @@ internal class EditBankAccountPresenter( saveIteration = clearingNumberState.text.toString() to accountNumberState.text.toString() } } - - EditBankAccountEvent.SnackBarShown -> { - showSuccessSnackBar = false - } } } @@ -103,13 +96,13 @@ internal class EditBankAccountPresenter( bankName = bankName, isLoading = isLoading, errorMessage = errorMessage, - showSuccessSnackBar = showSuccessSnackBar, + navigateBack = navigateBack, ) } } @Stable -fun InputTransformation.digitsOnly(): InputTransformation = this.then(DigitsOnlyTransformation) +private fun InputTransformation.digitsOnly(): InputTransformation = this.then(DigitsOnlyTransformation) private data object DigitsOnlyTransformation : InputTransformation { override fun TextFieldBuffer.transformInput() { diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 464cf13133..7d21a804e9 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.ButtonDefaults import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigErrorSection import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProgressDebounced @@ -19,16 +20,20 @@ import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.feature.payoutaccount.data.PayoutAccount +import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState.Content +import octopus.type.MemberPaymentProvider @Composable internal fun PayoutAccountOverviewDestination( viewModel: PayoutAccountOverviewViewModel, + onConnectPayoutMethodClicked: () -> Unit, onEditBankAccountClicked: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() PayoutAccountOverviewScreen( uiState = uiState, + onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, onEditBankAccountClicked = onEditBankAccountClicked, onRetry = { viewModel.emit(PayoutAccountOverviewEvent.Retry) }, navigateUp = navigateUp, @@ -38,6 +43,7 @@ internal fun PayoutAccountOverviewDestination( @Composable private fun PayoutAccountOverviewScreen( uiState: PayoutAccountOverviewUiState, + onConnectPayoutMethodClicked: () -> Unit, onEditBankAccountClicked: () -> Unit, onRetry: () -> Unit, navigateUp: () -> Unit, @@ -65,9 +71,11 @@ private fun PayoutAccountOverviewScreen( ) } - is PayoutAccountOverviewUiState.Content -> { + is Content -> { PayoutAccountContent( - payoutAccount = uiState.payoutAccount, + currentMethod = uiState.currentMethod, + availablePayoutMethods = uiState.availablePayoutMethods, + onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, onEditBankAccountClicked = onEditBankAccountClicked, ) } @@ -76,10 +84,51 @@ private fun PayoutAccountOverviewScreen( } @Composable -private fun PayoutAccountContent(payoutAccount: PayoutAccount, onEditBankAccountClicked: () -> Unit) { +private fun PayoutAccountContent( + currentMethod: PayoutAccount?, + availablePayoutMethods: List, + onConnectPayoutMethodClicked: () -> Unit, + onEditBankAccountClicked: () -> Unit, +) { Column { Spacer(Modifier.height(8.dp)) - when (payoutAccount) { + when (currentMethod) { + null -> { + HedvigButton( + text = "Connect payout account", + onClick = onConnectPayoutMethodClicked, + enabled = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + + is PayoutAccount.SwishPayout -> { + HedvigTextField( + text = currentMethod.phoneNumber, + onValueChange = {}, + labelText = "Swish", + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + readOnly = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + if (availablePayoutMethods.size > 1) { + Spacer(Modifier.height(8.dp)) + HedvigButton( + text = "Change account", + onClick = onConnectPayoutMethodClicked, + enabled = true, + buttonStyle = ButtonDefaults.ButtonStyle.Secondary, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + } + PayoutAccount.Trustly -> { HedvigTextField( text = "Trustly", @@ -91,17 +140,29 @@ private fun PayoutAccountContent(payoutAccount: PayoutAccount, onEditBankAccount .fillMaxWidth() .padding(horizontal = 16.dp), ) + if (availablePayoutMethods.size > 1) { + Spacer(Modifier.height(8.dp)) + HedvigButton( + text = "Change account", + onClick = onConnectPayoutMethodClicked, + enabled = true, + buttonStyle = ButtonDefaults.ButtonStyle.Secondary, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } } is PayoutAccount.BankAccount -> { val displayText = buildString { - if (payoutAccount.bankName != null) { - append(payoutAccount.bankName) + if (currentMethod.bankName != null) { + append(currentMethod.bankName) append(" ") } - append(payoutAccount.clearingNumber) + append(currentMethod.clearingNumber) append("-") - append(payoutAccount.accountNumber) + append(currentMethod.accountNumber) } HedvigTextField( text = displayText, diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt index 17f2ecbac9..61bfcb5b63 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt @@ -11,6 +11,7 @@ import com.hedvig.android.feature.payoutaccount.data.PayoutAccount import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope import com.hedvig.android.molecule.public.MoleculeViewModel +import octopus.type.MemberPaymentProvider internal class PayoutAccountOverviewViewModel( getPayoutAccountUseCase: GetPayoutAccountUseCase, @@ -28,7 +29,10 @@ internal sealed interface PayoutAccountOverviewUiState { data object Error : PayoutAccountOverviewUiState - data class Content(val payoutAccount: PayoutAccount) : PayoutAccountOverviewUiState + data class Content( + val currentMethod: PayoutAccount?, + val availablePayoutMethods: List, + ) : PayoutAccountOverviewUiState } internal class PayoutAccountOverviewPresenter( @@ -45,7 +49,12 @@ internal class PayoutAccountOverviewPresenter( uiState = PayoutAccountOverviewUiState.Loading getPayoutAccountUseCase.invoke().fold( ifLeft = { uiState = PayoutAccountOverviewUiState.Error }, - ifRight = { account -> uiState = PayoutAccountOverviewUiState.Content(account) }, + ifRight = { data -> + uiState = PayoutAccountOverviewUiState.Content( + currentMethod = data.currentMethod, + availablePayoutMethods = data.availablePayoutMethods, + ) + }, ) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt new file mode 100644 index 0000000000..ee3ffb5d66 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -0,0 +1,86 @@ +package com.hedvig.android.feature.payoutaccount.ui.selectmethod + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.hedvig.android.design.system.hedvig.HedvigCard +import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigText +import octopus.type.MemberPaymentProvider + +@Composable +internal fun SelectPayoutMethodDestination( + availableProviders: List, + onTrustlySelected: () -> Unit, + onNordeaSelected: () -> Unit, + onSwishSelected: () -> Unit, + navigateUp: () -> Unit, +) { + HedvigScaffold( + topAppBarText = "Connect payout account", + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + Spacer(Modifier.height(8.dp)) + Column(Modifier.padding(horizontal = 16.dp)) { + for (provider in availableProviders) { + when (provider) { + MemberPaymentProvider.TRUSTLY -> { + PayoutMethodRow( + title = "Trustly", + subtitle = "Connect via Trustly", + onClick = onTrustlySelected, + ) + Spacer(Modifier.height(8.dp)) + } + MemberPaymentProvider.NORDEA -> { + PayoutMethodRow( + title = "Bank account", + subtitle = "Enter clearing and account number", + onClick = onNordeaSelected, + ) + Spacer(Modifier.height(8.dp)) + } + MemberPaymentProvider.SWISH -> { + PayoutMethodRow( + title = "Swish", + subtitle = "Connect via Swish", + onClick = onSwishSelected, + ) + Spacer(Modifier.height(8.dp)) + } + else -> {} + } + } + } + Spacer(Modifier.height(16.dp)) + } +} + +@Composable +private fun PayoutMethodRow( + title: String, + subtitle: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + HedvigCard( + onClick = onClick, + modifier = modifier.fillMaxWidth(), + ) { + Column(Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) { + HedvigText(text = title) + HedvigText( + text = subtitle, + color = com.hedvig.android.design.system.hedvig.HedvigTheme.colorScheme.textSecondary, + ) + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt new file mode 100644 index 0000000000..793a12fdfd --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt @@ -0,0 +1,93 @@ +package com.hedvig.android.feature.payoutaccount.ui.setupswish + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.HedvigButton +import com.hedvig.android.design.system.hedvig.HedvigNotificationCard +import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigTextField +import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults +import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority + +@Composable +internal fun SetupSwishPayoutDestination( + viewModel: SetupSwishPayoutViewModel, + navigateUp: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + SetupSwishPayoutScreen( + uiState = uiState, + onSave = { viewModel.emit(SetupSwishPayoutEvent.Save) }, + navigateUp = navigateUp, + ) +} + +@Composable +private fun SetupSwishPayoutScreen( + uiState: SetupSwishPayoutUiState, + onSave: () -> Unit, + navigateUp: () -> Unit, +) { + LaunchedEffect(uiState.navigateBack) { + if (uiState.navigateBack) navigateUp() + } + + HedvigScaffold( + topAppBarText = "Swish", + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + Spacer(Modifier.weight(1f)) + Column(Modifier.padding(horizontal = 16.dp)) { + HedvigTextField( + state = uiState.phoneNumberState, + labelText = "Phone number", + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Phone, + ), + modifier = Modifier.fillMaxWidth(), + ) + } + AnimatedVisibility( + visible = uiState.errorMessage != null, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + HedvigNotificationCard( + message = uiState.errorMessage ?: "", + priority = NotificationPriority.Attention, + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = 4.dp) + .fillMaxWidth(), + ) + } + Spacer(Modifier.height(16.dp)) + HedvigButton( + text = "Save", + onClick = onSave, + enabled = !uiState.isLoading && uiState.phoneNumberState.text.isNotBlank(), + isLoading = uiState.isLoading, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + Spacer(Modifier.height(16.dp)) + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt new file mode 100644 index 0000000000..14b7419498 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt @@ -0,0 +1,83 @@ +package com.hedvig.android.feature.payoutaccount.ui.setupswish + +import androidx.compose.foundation.text.input.TextFieldState +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.setValue +import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase +import com.hedvig.android.molecule.public.MoleculePresenter +import com.hedvig.android.molecule.public.MoleculePresenterScope +import com.hedvig.android.molecule.public.MoleculeViewModel + +internal class SetupSwishPayoutViewModel( + setupSwishPayoutUseCase: SetupSwishPayoutUseCase, +) : MoleculeViewModel( + SetupSwishPayoutUiState(TextFieldState(), false, null, false), + SetupSwishPayoutPresenter(setupSwishPayoutUseCase), + ) + +internal sealed interface SetupSwishPayoutEvent { + data object Save : SetupSwishPayoutEvent +} + +internal data class SetupSwishPayoutUiState( + val phoneNumberState: TextFieldState, + val isLoading: Boolean, + val errorMessage: String?, + val navigateBack: Boolean, +) + +internal class SetupSwishPayoutPresenter( + private val setupSwishPayoutUseCase: SetupSwishPayoutUseCase, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present( + lastState: SetupSwishPayoutUiState, + ): SetupSwishPayoutUiState { + val phoneNumberState = remember { lastState.phoneNumberState } + var isLoading by remember { mutableStateOf(false) } + var errorMessage by remember { mutableStateOf(null) } + var navigateBack by remember { mutableStateOf(false) } + var saveIteration by remember { mutableStateOf(null) } + + val currentSave = saveIteration + if (currentSave != null) { + LaunchedEffect(currentSave) { + isLoading = true + errorMessage = null + setupSwishPayoutUseCase.invoke(currentSave).fold( + ifLeft = { + isLoading = false + errorMessage = it.message ?: "Something went wrong, please try again" + saveIteration = null + }, + ifRight = { + isLoading = false + navigateBack = true + saveIteration = null + }, + ) + } + } + + CollectEvents { event -> + when (event) { + SetupSwishPayoutEvent.Save -> { + if (!isLoading) { + saveIteration = phoneNumberState.text.toString() + } + } + } + } + + return SetupSwishPayoutUiState( + phoneNumberState = phoneNumberState, + isLoading = isLoading, + errorMessage = errorMessage, + navigateBack = navigateBack, + ) + } +} From af163ae05d04c9c1856c223dcba1c8e5d14dd5e3 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 15 Apr 2026 15:59:50 +0200 Subject: [PATCH 03/50] Add logic to hide the "payout" payments button when it's not applicable --- .../QueryShouldShowPayoutButton.graphql | 15 +++ .../feature/payments/di/PaymentsModule.kt | 20 +++ .../data/GetShouldShowPayoutUseCase.kt | 41 ++++++ .../GetShouldShowPayoutUseCaseProvider.kt | 10 ++ .../ui/payments/PaymentsDestination.kt | 45 ++++--- .../payments/ui/payments/PaymentsPresenter.kt | 117 ++++++++++-------- .../payments/ui/payments/PaymentsViewModel.kt | 3 + 7 files changed, 183 insertions(+), 68 deletions(-) create mode 100644 app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql create mode 100644 app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt create mode 100644 app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt diff --git a/app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql b/app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql new file mode 100644 index 0000000000..9c9ceab92c --- /dev/null +++ b/app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql @@ -0,0 +1,15 @@ +query ShouldShowPayoutButton { + currentMember { + paymentMethods { + defaultPayoutMethod { + id + } + payoutMethods { + id + } + availableMethods { + supportsPayout + } + } + } +} diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt index 7272dbcefd..4471be10e5 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt @@ -15,6 +15,10 @@ import com.hedvig.android.feature.payments.data.GetPaymentsHistoryUseCase import com.hedvig.android.feature.payments.data.GetPaymentsHistoryUseCaseImpl import com.hedvig.android.feature.payments.overview.data.GetForeverInformationUseCase import com.hedvig.android.feature.payments.overview.data.GetForeverInformationUseCaseImpl +import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCase +import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCaseDemo +import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCaseImpl +import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCaseProvider import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCase import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCaseDemo import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCaseImpl @@ -67,6 +71,7 @@ val paymentsModule = module { viewModel { PaymentsViewModel( get(), + get(), ) } @@ -116,4 +121,19 @@ val paymentsModule = module { clock = get(), ) } + single { + GetShouldShowPayoutUseCaseProvider( + demoManager = get(), + demoImpl = get(), + prodImpl = get(), + ) + } + single { + GetShouldShowPayoutUseCaseImpl( + get(), + ) + } + single { + GetShouldShowPayoutUseCaseDemo() + } } diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt new file mode 100644 index 0000000000..111a706c50 --- /dev/null +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt @@ -0,0 +1,41 @@ +package com.hedvig.android.feature.payments.overview.data + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.right +import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.cache.normalized.FetchPolicy +import com.apollographql.apollo.cache.normalized.fetchPolicy +import com.hedvig.android.apollo.ErrorMessage +import com.hedvig.android.apollo.safeExecute +import com.hedvig.android.core.common.ErrorMessage +import octopus.ShouldShowPayoutButtonQuery + +internal interface GetShouldShowPayoutUseCase { + suspend fun invoke(): Either +} + +/** + * We do not want to show the payout button at all when there is no payout method connected nor is there a possibility + * to add one in the member's current state + */ +internal class GetShouldShowPayoutUseCaseImpl( + private val apolloClient: ApolloClient, +) : GetShouldShowPayoutUseCase { + override suspend fun invoke(): Either = either { + val result = apolloClient + .query(ShouldShowPayoutButtonQuery()) + .fetchPolicy(FetchPolicy.NetworkFirst) + .safeExecute(::ErrorMessage) + .bind() + + val paymentMethods = result.currentMember.paymentMethods + paymentMethods.availableMethods.any { it.supportsPayout } || + paymentMethods.defaultPayoutMethod != null || + paymentMethods.payoutMethods.isNotEmpty() + } +} + +internal class GetShouldShowPayoutUseCaseDemo : GetShouldShowPayoutUseCase { + override suspend fun invoke(): Either = false.right() +} diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt new file mode 100644 index 0000000000..1577822747 --- /dev/null +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt @@ -0,0 +1,10 @@ +package com.hedvig.android.feature.payments.overview.data + +import com.hedvig.android.core.demomode.DemoManager +import com.hedvig.android.core.demomode.ProdOrDemoProvider + +internal class GetShouldShowPayoutUseCaseProvider( + override val demoManager: DemoManager, + override val demoImpl: GetShouldShowPayoutUseCase, + override val prodImpl: GetShouldShowPayoutUseCase, +) : ProdOrDemoProvider diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt index 6896d0a528..63ad6b0704 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt @@ -287,6 +287,7 @@ private fun PaymentsContent( onPaymentHistoryClicked = onPaymentHistoryClicked, onPayoutAccountClicked = onPayoutAccountClicked, onPaymentDetailsClicked = onPaymentDetailsClicked, + showPayoutButton = (uiState as? Content)?.showPayoutButton == true, ) if (uiState is Content) { when (uiState.connectedPaymentInfo) { @@ -384,6 +385,7 @@ private fun PaymentsListItems( onPaymentHistoryClicked: () -> Unit, onPayoutAccountClicked: () -> Unit, onPaymentDetailsClicked: () -> Unit, + showPayoutButton: Boolean, ) { val listItemsSideSpacingModifier = Modifier .padding(horizontal = 16.dp) @@ -405,22 +407,24 @@ private fun PaymentsListItems( .padding(vertical = 16.dp) .fillMaxWidth(), ) - HorizontalDivider(modifier = listItemsSideSpacingModifier) - PaymentsListItem( - text = "Payout", - icon = { - Icon( - imageVector = HedvigIcons.PaymentOutline, - contentDescription = null, - modifier = Modifier.size(24.dp), - ) - }, - modifier = Modifier - .clickable(onClick = onPayoutAccountClicked) - .then(listItemsSideSpacingModifier) - .padding(vertical = 16.dp) - .fillMaxWidth(), - ) + if (showPayoutButton) { + HorizontalDivider(modifier = listItemsSideSpacingModifier) + PaymentsListItem( + text = "Payout", + icon = { + Icon( + imageVector = HedvigIcons.PaymentOutline, + contentDescription = null, + modifier = Modifier.size(24.dp), + ) + }, + modifier = Modifier + .clickable(onClick = onPayoutAccountClicked) + .then(listItemsSideSpacingModifier) + .padding(vertical = 16.dp) + .fillMaxWidth(), + ) + } HorizontalDivider(modifier = listItemsSideSpacingModifier) PaymentsListItem( text = stringResource(Res.string.PAYMENTS_PAYMENT_HISTORY_BUTTON_LABEL), @@ -636,6 +640,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< "Card", "****1234", ), + showPayoutButton = false, ), ) add( @@ -652,6 +657,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< "Card", "****1234", ), + showPayoutButton = false, ), ) add( @@ -668,6 +674,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< "Card", "****1234", ), + showPayoutButton = false, ), ) add( @@ -687,6 +694,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< "Card", "****1234", ), + showPayoutButton = false, ), ) add( @@ -700,6 +708,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< upcomingPaymentInfo = NoInfo, ongoingCharges = emptyList(), connectedPaymentInfo = ConnectedPaymentInfo.Pending, + showPayoutButton = false, ), ) add( @@ -715,6 +724,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< connectedPaymentInfo = ConnectedPaymentInfo.NeedsSetup( null, ), + showPayoutButton = false, ), ) add( @@ -730,6 +740,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< connectedPaymentInfo = ConnectedPaymentInfo.NeedsSetup( null, ), + showPayoutButton = false, ), ) add( @@ -748,6 +759,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< connectedPaymentInfo = ConnectedPaymentInfo.NeedsSetup( dueDateToConnect = System.now().plus(30.days).toLocalDateTime(TimeZone.UTC).date, ), + showPayoutButton = false, ), ) add( @@ -766,6 +778,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< connectedPaymentInfo = ConnectedPaymentInfo.NeedsSetup( System.now().plus(30.days).toLocalDateTime(TimeZone.UTC).date, ), + showPayoutButton = false, ), ) }, diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt index 18c59e81aa..03335a4a2c 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import arrow.core.getOrElse import com.hedvig.android.core.demomode.Provider import com.hedvig.android.core.uidata.UiMoney import com.hedvig.android.feature.payments.data.MemberCharge @@ -15,13 +16,17 @@ import com.hedvig.android.feature.payments.data.PaymentConnection.NeedsSetup import com.hedvig.android.feature.payments.data.PaymentConnection.Pending import com.hedvig.android.feature.payments.data.PaymentConnection.Unknown import com.hedvig.android.feature.payments.data.PaymentOverview.OngoingCharge +import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCase import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCase import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.datetime.LocalDate internal class PaymentsPresenter( private val getUpcomingPaymentUseCase: Provider, + private val getShouldShowPayoutUseCase: Provider, ) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present(lastState: PaymentsUiState): PaymentsUiState { @@ -45,59 +50,66 @@ internal class PaymentsPresenter( PaymentsUiState.Loading } } - getUpcomingPaymentUseCase.provide().invoke().fold( - ifLeft = { - paymentsUiState = PaymentsUiState.Error - }, - ifRight = { paymentOverview -> - paymentsUiState = PaymentsUiState.Content( - isRetrying = false, - upcomingPayment = paymentOverview.memberChargeShortInfo?.let { memberCharge -> - PaymentsUiState.Content.UpcomingPayment.Content( - netAmount = memberCharge.netAmount, - dueDate = memberCharge.dueDate, - id = memberCharge.id, - ) - } ?: PaymentsUiState.Content.UpcomingPayment.NoUpcomingPayment, - upcomingPaymentInfo = run { - val memberCharge = paymentOverview.memberChargeShortInfo - if (memberCharge?.status == MemberCharge.MemberChargeStatus.PENDING) { - return@run PaymentsUiState.Content.UpcomingPaymentInfo.InProgress - } - memberCharge?.failedCharge?.let { failedCharge -> - return@run PaymentsUiState.Content.UpcomingPaymentInfo.PaymentFailed( - failedPaymentStartDate = failedCharge.fromDate, - failedPaymentEndDate = failedCharge.toDate, + coroutineScope { + val upcomingPaymentDeferred = async { getUpcomingPaymentUseCase.provide().invoke() } + val shouldShowPayoutDeferred = async { getShouldShowPayoutUseCase.provide().invoke() } + val upcomingPaymentResult = upcomingPaymentDeferred.await() + val shouldShowPayoutResult = shouldShowPayoutDeferred.await() + upcomingPaymentResult.fold( + ifLeft = { + paymentsUiState = PaymentsUiState.Error + }, + ifRight = { paymentOverview -> + paymentsUiState = PaymentsUiState.Content( + isRetrying = false, + upcomingPayment = paymentOverview.memberChargeShortInfo?.let { memberCharge -> + PaymentsUiState.Content.UpcomingPayment.Content( + netAmount = memberCharge.netAmount, + dueDate = memberCharge.dueDate, + id = memberCharge.id, ) - } - PaymentsUiState.Content.UpcomingPaymentInfo.NoInfo - }, - ongoingCharges = paymentOverview.ongoingCharges, - connectedPaymentInfo = when (val paymentConnection = paymentOverview.paymentConnection) { - is Active -> { - PaymentsUiState.Content.ConnectedPaymentInfo.Active( - displayName = paymentConnection.displayName, - maskedAccountNumber = paymentConnection.displayValue, - ) - } - - Pending -> { - PaymentsUiState.Content.ConnectedPaymentInfo.Pending - } - - is NeedsSetup -> { - PaymentsUiState.Content.ConnectedPaymentInfo.NeedsSetup( - dueDateToConnect = paymentConnection.terminationDateIfNotConnected, - ) - } - - Unknown -> { - PaymentsUiState.Content.ConnectedPaymentInfo.Unknown - } - }, - ) - }, - ) + } ?: PaymentsUiState.Content.UpcomingPayment.NoUpcomingPayment, + upcomingPaymentInfo = run { + val memberCharge = paymentOverview.memberChargeShortInfo + if (memberCharge?.status == MemberCharge.MemberChargeStatus.PENDING) { + return@run PaymentsUiState.Content.UpcomingPaymentInfo.InProgress + } + memberCharge?.failedCharge?.let { failedCharge -> + return@run PaymentsUiState.Content.UpcomingPaymentInfo.PaymentFailed( + failedPaymentStartDate = failedCharge.fromDate, + failedPaymentEndDate = failedCharge.toDate, + ) + } + PaymentsUiState.Content.UpcomingPaymentInfo.NoInfo + }, + ongoingCharges = paymentOverview.ongoingCharges, + connectedPaymentInfo = when (val paymentConnection = paymentOverview.paymentConnection) { + is Active -> { + PaymentsUiState.Content.ConnectedPaymentInfo.Active( + displayName = paymentConnection.displayName, + maskedAccountNumber = paymentConnection.displayValue, + ) + } + + Pending -> { + PaymentsUiState.Content.ConnectedPaymentInfo.Pending + } + + is NeedsSetup -> { + PaymentsUiState.Content.ConnectedPaymentInfo.NeedsSetup( + dueDateToConnect = paymentConnection.terminationDateIfNotConnected, + ) + } + + Unknown -> { + PaymentsUiState.Content.ConnectedPaymentInfo.Unknown + } + }, + showPayoutButton = shouldShowPayoutResult.getOrElse { false }, + ) + }, + ) + } } return paymentsUiState } @@ -118,6 +130,7 @@ internal sealed interface PaymentsUiState { val upcomingPaymentInfo: UpcomingPaymentInfo, val ongoingCharges: List, val connectedPaymentInfo: ConnectedPaymentInfo, + val showPayoutButton: Boolean, ) : PaymentsUiState { sealed interface UpcomingPayment { data object NoUpcomingPayment : UpcomingPayment diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt index 616b9004ca..3486475610 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt @@ -1,14 +1,17 @@ package com.hedvig.android.feature.payments.ui.payments import com.hedvig.android.core.demomode.Provider +import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCase import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCase import com.hedvig.android.molecule.public.MoleculeViewModel internal class PaymentsViewModel( getUpcomingPaymentUseCase: Provider, + getShouldShowPayoutUseCase: Provider, ) : MoleculeViewModel( PaymentsUiState.Loading, PaymentsPresenter( getUpcomingPaymentUseCase = getUpcomingPaymentUseCase, + getShouldShowPayoutUseCase = getShouldShowPayoutUseCase, ), ) From 0faf36f920ca2a70c516de88bbe5b9ff26a2432a Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 15 Apr 2026 16:52:09 +0200 Subject: [PATCH 04/50] Extract presenter for the payout button Contains some more involved logic around retrying when there is an error --- .../feature/payments/di/PaymentsModule.kt | 9 +- .../payments/ui/payments/PaymentsPresenter.kt | 155 ++++++++++-------- .../payments/ui/payments/PaymentsViewModel.kt | 5 +- 3 files changed, 93 insertions(+), 76 deletions(-) diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt index 4471be10e5..1099d8d811 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt @@ -28,6 +28,7 @@ import com.hedvig.android.feature.payments.ui.discounts.DiscountsViewModel import com.hedvig.android.feature.payments.ui.history.PaymentHistoryViewModel import com.hedvig.android.feature.payments.ui.memberpaymentdetails.MemberPaymentDetailsViewModel import com.hedvig.android.feature.payments.ui.payments.PaymentsViewModel +import com.hedvig.android.feature.payments.ui.payments.ShouldShowPayoutPresenter import kotlin.time.Clock import org.koin.core.module.dsl.viewModel import org.koin.dsl.module @@ -68,10 +69,16 @@ val paymentsModule = module { ) } + single { + ShouldShowPayoutPresenter( + get(), + ) + } + viewModel { PaymentsViewModel( get(), - get(), + get(), ) } diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt index 03335a4a2c..5ca57975a3 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt @@ -7,31 +7,35 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import arrow.core.getOrElse +import arrow.core.Either +import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.core.demomode.Provider import com.hedvig.android.core.uidata.UiMoney import com.hedvig.android.feature.payments.data.MemberCharge +import com.hedvig.android.feature.payments.data.PaymentConnection import com.hedvig.android.feature.payments.data.PaymentConnection.Active import com.hedvig.android.feature.payments.data.PaymentConnection.NeedsSetup import com.hedvig.android.feature.payments.data.PaymentConnection.Pending import com.hedvig.android.feature.payments.data.PaymentConnection.Unknown +import com.hedvig.android.feature.payments.data.PaymentOverview import com.hedvig.android.feature.payments.data.PaymentOverview.OngoingCharge import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCase import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCase +import com.hedvig.android.feature.payments.ui.payments.PaymentsUiState.Content.ConnectedPaymentInfo import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.delay import kotlinx.datetime.LocalDate internal class PaymentsPresenter( private val getUpcomingPaymentUseCase: Provider, - private val getShouldShowPayoutUseCase: Provider, + private val shouldShowPayoutPresenter: ShouldShowPayoutPresenter, ) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present(lastState: PaymentsUiState): PaymentsUiState { - var paymentsUiState: PaymentsUiState by remember { mutableStateOf(lastState) } var loadIteration by remember { mutableIntStateOf(0) } + var paymentOverviewResult: Either? by remember { mutableStateOf(null) } CollectEvents { event -> when (event) { @@ -40,78 +44,85 @@ internal class PaymentsPresenter( } LaunchedEffect(loadIteration) { - val currentPaymentUiState = paymentsUiState - paymentsUiState = when (currentPaymentUiState) { - is PaymentsUiState.Content -> { - currentPaymentUiState.copy(isRetrying = true) - } - - else -> { - PaymentsUiState.Loading - } - } - coroutineScope { - val upcomingPaymentDeferred = async { getUpcomingPaymentUseCase.provide().invoke() } - val shouldShowPayoutDeferred = async { getShouldShowPayoutUseCase.provide().invoke() } - val upcomingPaymentResult = upcomingPaymentDeferred.await() - val shouldShowPayoutResult = shouldShowPayoutDeferred.await() - upcomingPaymentResult.fold( - ifLeft = { - paymentsUiState = PaymentsUiState.Error - }, - ifRight = { paymentOverview -> - paymentsUiState = PaymentsUiState.Content( - isRetrying = false, - upcomingPayment = paymentOverview.memberChargeShortInfo?.let { memberCharge -> - PaymentsUiState.Content.UpcomingPayment.Content( - netAmount = memberCharge.netAmount, - dueDate = memberCharge.dueDate, - id = memberCharge.id, - ) - } ?: PaymentsUiState.Content.UpcomingPayment.NoUpcomingPayment, - upcomingPaymentInfo = run { - val memberCharge = paymentOverview.memberChargeShortInfo - if (memberCharge?.status == MemberCharge.MemberChargeStatus.PENDING) { - return@run PaymentsUiState.Content.UpcomingPaymentInfo.InProgress - } - memberCharge?.failedCharge?.let { failedCharge -> - return@run PaymentsUiState.Content.UpcomingPaymentInfo.PaymentFailed( - failedPaymentStartDate = failedCharge.fromDate, - failedPaymentEndDate = failedCharge.toDate, - ) - } - PaymentsUiState.Content.UpcomingPaymentInfo.NoInfo - }, - ongoingCharges = paymentOverview.ongoingCharges, - connectedPaymentInfo = when (val paymentConnection = paymentOverview.paymentConnection) { - is Active -> { - PaymentsUiState.Content.ConnectedPaymentInfo.Active( - displayName = paymentConnection.displayName, - maskedAccountNumber = paymentConnection.displayValue, - ) - } - - Pending -> { - PaymentsUiState.Content.ConnectedPaymentInfo.Pending - } - - is NeedsSetup -> { - PaymentsUiState.Content.ConnectedPaymentInfo.NeedsSetup( - dueDateToConnect = paymentConnection.terminationDateIfNotConnected, - ) - } - - Unknown -> { - PaymentsUiState.Content.ConnectedPaymentInfo.Unknown - } - }, - showPayoutButton = shouldShowPayoutResult.getOrElse { false }, + paymentOverviewResult = null + paymentOverviewResult = getUpcomingPaymentUseCase.provide().invoke() + } + + val shouldShowPayout = shouldShowPayoutPresenter.present(loadIteration) + + val currentPaymentResult = paymentOverviewResult ?: return PaymentsUiState.Loading + + return currentPaymentResult.fold( + ifLeft = { PaymentsUiState.Error }, + ifRight = { paymentOverview -> + PaymentsUiState.Content( + isRetrying = false, + upcomingPayment = paymentOverview.memberChargeShortInfo?.let { memberCharge -> + PaymentsUiState.Content.UpcomingPayment.Content( + netAmount = memberCharge.netAmount, + dueDate = memberCharge.dueDate, + id = memberCharge.id, ) + } ?: PaymentsUiState.Content.UpcomingPayment.NoUpcomingPayment, + upcomingPaymentInfo = run { + val memberCharge = paymentOverview.memberChargeShortInfo + if (memberCharge?.status == MemberCharge.MemberChargeStatus.PENDING) { + return@run PaymentsUiState.Content.UpcomingPaymentInfo.InProgress + } + memberCharge?.failedCharge?.let { failedCharge -> + return@run PaymentsUiState.Content.UpcomingPaymentInfo.PaymentFailed( + failedPaymentStartDate = failedCharge.fromDate, + failedPaymentEndDate = failedCharge.toDate, + ) + } + PaymentsUiState.Content.UpcomingPaymentInfo.NoInfo + }, + ongoingCharges = paymentOverview.ongoingCharges, + connectedPaymentInfo = paymentOverview.paymentConnection.toConnectedPaymentInfo(), + showPayoutButton = shouldShowPayout, + ) + }, + ) + } +} + +internal class ShouldShowPayoutPresenter( + private val getShouldShowPayoutUseCase: Provider, +) { + @Composable + fun present(loadIteration: Int): Boolean { + var shouldShowPayout by remember { mutableStateOf(false) } + LaunchedEffect(loadIteration) { + shouldShowPayout = false + for (attempt in 0..2) { + delay(attempt.seconds) + getShouldShowPayoutUseCase.provide().invoke().fold( + ifLeft = {}, + ifRight = { result -> + shouldShowPayout = result + return@LaunchedEffect }, ) } } - return paymentsUiState + return shouldShowPayout + } +} + +private fun PaymentConnection.toConnectedPaymentInfo(): ConnectedPaymentInfo { + return when (this) { + is Active -> ConnectedPaymentInfo.Active( + displayName = displayName, + maskedAccountNumber = displayValue, + ) + + Pending -> ConnectedPaymentInfo.Pending + + is NeedsSetup -> ConnectedPaymentInfo.NeedsSetup( + dueDateToConnect = terminationDateIfNotConnected, + ) + + Unknown -> ConnectedPaymentInfo.Unknown } } diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt index 3486475610..ecb7fcc20b 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt @@ -1,17 +1,16 @@ package com.hedvig.android.feature.payments.ui.payments import com.hedvig.android.core.demomode.Provider -import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCase import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCase import com.hedvig.android.molecule.public.MoleculeViewModel internal class PaymentsViewModel( getUpcomingPaymentUseCase: Provider, - getShouldShowPayoutUseCase: Provider, + shouldShowPayoutPresenter: ShouldShowPayoutPresenter, ) : MoleculeViewModel( PaymentsUiState.Loading, PaymentsPresenter( getUpcomingPaymentUseCase = getUpcomingPaymentUseCase, - getShouldShowPayoutUseCase = getShouldShowPayoutUseCase, + shouldShowPayoutPresenter = shouldShowPayoutPresenter, ), ) From 81d25c0d3ab6cbbcd35febe054c2c40b371de450 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Thu, 16 Apr 2026 12:20:16 +0200 Subject: [PATCH 05/50] Make ShouldShowPayoutPresenter private in class Should serve as a way to delegate some of the work without the ceremony with CI etc --- .../hedvig/android/feature/payments/di/PaymentsModule.kt | 9 +-------- .../feature/payments/ui/payments/PaymentsPresenter.kt | 5 +++-- .../feature/payments/ui/payments/PaymentsViewModel.kt | 5 +++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt index 1099d8d811..4471be10e5 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt @@ -28,7 +28,6 @@ import com.hedvig.android.feature.payments.ui.discounts.DiscountsViewModel import com.hedvig.android.feature.payments.ui.history.PaymentHistoryViewModel import com.hedvig.android.feature.payments.ui.memberpaymentdetails.MemberPaymentDetailsViewModel import com.hedvig.android.feature.payments.ui.payments.PaymentsViewModel -import com.hedvig.android.feature.payments.ui.payments.ShouldShowPayoutPresenter import kotlin.time.Clock import org.koin.core.module.dsl.viewModel import org.koin.dsl.module @@ -69,16 +68,10 @@ val paymentsModule = module { ) } - single { - ShouldShowPayoutPresenter( - get(), - ) - } - viewModel { PaymentsViewModel( get(), - get(), + get(), ) } diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt index 5ca57975a3..21423a16dc 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt @@ -30,8 +30,9 @@ import kotlinx.datetime.LocalDate internal class PaymentsPresenter( private val getUpcomingPaymentUseCase: Provider, - private val shouldShowPayoutPresenter: ShouldShowPayoutPresenter, + getShouldShowPayoutUseCase: Provider, ) : MoleculePresenter { + private val shouldShowPayoutPresenter = ShouldShowPayoutPresenter(getShouldShowPayoutUseCase) @Composable override fun MoleculePresenterScope.present(lastState: PaymentsUiState): PaymentsUiState { var loadIteration by remember { mutableIntStateOf(0) } @@ -86,7 +87,7 @@ internal class PaymentsPresenter( } } -internal class ShouldShowPayoutPresenter( +private class ShouldShowPayoutPresenter( private val getShouldShowPayoutUseCase: Provider, ) { @Composable diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt index ecb7fcc20b..3486475610 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt @@ -1,16 +1,17 @@ package com.hedvig.android.feature.payments.ui.payments import com.hedvig.android.core.demomode.Provider +import com.hedvig.android.feature.payments.overview.data.GetShouldShowPayoutUseCase import com.hedvig.android.feature.payments.overview.data.GetUpcomingPaymentUseCase import com.hedvig.android.molecule.public.MoleculeViewModel internal class PaymentsViewModel( getUpcomingPaymentUseCase: Provider, - shouldShowPayoutPresenter: ShouldShowPayoutPresenter, + getShouldShowPayoutUseCase: Provider, ) : MoleculeViewModel( PaymentsUiState.Loading, PaymentsPresenter( getUpcomingPaymentUseCase = getUpcomingPaymentUseCase, - shouldShowPayoutPresenter = shouldShowPayoutPresenter, + getShouldShowPayoutUseCase = getShouldShowPayoutUseCase, ), ) From c84fe7673f2b917f265d36db1f9dc939217674d0 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Thu, 16 Apr 2026 15:45:23 +0200 Subject: [PATCH 06/50] Move payments feature to commonMain Remove unused dependencies which were blocking this move Turn apollo-network-cache-manager also KMP as it was ready already --- .../build.gradle.kts | 14 ++- .../android/apollo/NetworkCacheManager.kt | 0 .../apollo/di/NetworkCacheManagerModule.kt | 0 app/feature/feature-payments/build.gradle.kts | 94 ++++++++----------- .../graphql/QueryDiscounts.graphql | 0 .../graphql/QueryMemberPaymentDetails.graphql | 0 .../graphql/QueryPaymentsHistory.graphql | 0 .../graphql/QueryReferrals.graphql | 0 .../graphql/QueryShortPaymentsHistory.graphql | 0 .../QueryShouldShowPayoutButton.graphql | 0 .../graphql/QueryUpcomingPayment.graphql | 0 .../android/feature/payments/PreviewData.kt | 0 .../android/feature/payments/data/Discount.kt | 0 .../payments/data/GetChargeDetailsUseCase.kt | 0 .../data/GetDiscountsOverviewUseCase.kt | 0 .../payments/data/GetDiscountsUseCase.kt | 0 .../data/GetMemberPaymentsDetailsUseCase.kt | 0 .../data/GetPaymentsHistoryUseCase.kt | 0 .../feature/payments/data/MemberCharge.kt | 0 .../payments/data/PaymentConnection.kt | 0 .../feature/payments/data/PaymentOverview.kt | 0 .../feature/payments/di/PaymentsModule.kt | 0 .../navigation/PaymentsDestination.kt | 0 .../payments/navigation/PaymentsGraph.kt | 0 .../data/GetForeverInformationUseCase.kt | 0 .../data/GetShouldShowPayoutUseCase.kt | 0 .../GetShouldShowPayoutUseCaseProvider.kt | 0 .../data/GetUpcomingPaymentUseCase.kt | 0 .../data/GetUpcomingPaymentUseCaseProvider.kt | 0 .../ui/details/PaymentDetailExpandableCard.kt | 0 .../ui/details/PaymentDetailsDestination.kt | 0 .../ui/details/PaymentDetailsViewModel.kt | 0 .../AddDiscountBottomSheetContent.kt | 0 .../payments/ui/discounts/DiscountRow.kt | 0 .../ui/discounts/DiscountsDestination.kt | 0 .../ui/discounts/DiscountsPresenter.kt | 0 .../ui/discounts/DiscountsViewModel.kt | 0 .../ui/history/PaymentHistoryDestination.kt | 0 .../ui/history/PaymentHistoryViewModel.kt | 0 .../MemberPaymentDetailsDestination.kt | 0 .../MemberPaymentDetailsViewModel.kt | 0 .../ui/payments/PaymentsDestination.kt | 0 .../payments/ui/payments/PaymentsPresenter.kt | 0 .../payments/ui/payments/PaymentsViewModel.kt | 0 .../src/main/AndroidManifest.xml | 2 - 45 files changed, 48 insertions(+), 62 deletions(-) rename app/apollo/apollo-network-cache-manager/src/{main => commonMain}/kotlin/com/hedvig/android/apollo/NetworkCacheManager.kt (100%) rename app/apollo/apollo-network-cache-manager/src/{main => commonMain}/kotlin/com/hedvig/android/apollo/di/NetworkCacheManagerModule.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/graphql/QueryDiscounts.graphql (100%) rename app/feature/feature-payments/src/{main => commonMain}/graphql/QueryMemberPaymentDetails.graphql (100%) rename app/feature/feature-payments/src/{main => commonMain}/graphql/QueryPaymentsHistory.graphql (100%) rename app/feature/feature-payments/src/{main => commonMain}/graphql/QueryReferrals.graphql (100%) rename app/feature/feature-payments/src/{main => commonMain}/graphql/QueryShortPaymentsHistory.graphql (100%) rename app/feature/feature-payments/src/{main => commonMain}/graphql/QueryShouldShowPayoutButton.graphql (100%) rename app/feature/feature-payments/src/{main => commonMain}/graphql/QueryUpcomingPayment.graphql (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/PreviewData.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/Discount.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt (100%) rename app/feature/feature-payments/src/{main => commonMain}/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt (100%) delete mode 100644 app/feature/feature-payments/src/main/AndroidManifest.xml diff --git a/app/apollo/apollo-network-cache-manager/build.gradle.kts b/app/apollo/apollo-network-cache-manager/build.gradle.kts index ab91ad2ff0..dceb1aaaae 100644 --- a/app/apollo/apollo-network-cache-manager/build.gradle.kts +++ b/app/apollo/apollo-network-cache-manager/build.gradle.kts @@ -1,10 +1,14 @@ plugins { - id("hedvig.jvm.library") + id("hedvig.multiplatform.library") id("hedvig.gradle.plugin") } -dependencies { - implementation(libs.apollo.normalizedCache) - implementation(libs.apollo.runtime) - implementation(libs.koin.core) +kotlin { + sourceSets { + commonMain.dependencies { + implementation(libs.apollo.normalizedCache) + implementation(libs.apollo.runtime) + implementation(libs.koin.core) + } + } } diff --git a/app/apollo/apollo-network-cache-manager/src/main/kotlin/com/hedvig/android/apollo/NetworkCacheManager.kt b/app/apollo/apollo-network-cache-manager/src/commonMain/kotlin/com/hedvig/android/apollo/NetworkCacheManager.kt similarity index 100% rename from app/apollo/apollo-network-cache-manager/src/main/kotlin/com/hedvig/android/apollo/NetworkCacheManager.kt rename to app/apollo/apollo-network-cache-manager/src/commonMain/kotlin/com/hedvig/android/apollo/NetworkCacheManager.kt diff --git a/app/apollo/apollo-network-cache-manager/src/main/kotlin/com/hedvig/android/apollo/di/NetworkCacheManagerModule.kt b/app/apollo/apollo-network-cache-manager/src/commonMain/kotlin/com/hedvig/android/apollo/di/NetworkCacheManagerModule.kt similarity index 100% rename from app/apollo/apollo-network-cache-manager/src/main/kotlin/com/hedvig/android/apollo/di/NetworkCacheManagerModule.kt rename to app/apollo/apollo-network-cache-manager/src/commonMain/kotlin/com/hedvig/android/apollo/di/NetworkCacheManagerModule.kt diff --git a/app/feature/feature-payments/build.gradle.kts b/app/feature/feature-payments/build.gradle.kts index 763237417d..cd1416f07a 100644 --- a/app/feature/feature-payments/build.gradle.kts +++ b/app/feature/feature-payments/build.gradle.kts @@ -1,5 +1,6 @@ plugins { - id("hedvig.android.library") + id("hedvig.multiplatform.library") + id("hedvig.multiplatform.library.android") id("hedvig.gradle.plugin") } @@ -9,58 +10,41 @@ hedvig { compose() } -android { - testOptions.unitTests.isReturnDefaultValues = true -} - -dependencies { - implementation(libs.androidx.compose.foundation) - implementation(libs.apollo.normalizedCache) - implementation(libs.apollo.runtime) - implementation(libs.arrow.core) - implementation(libs.arrow.fx) - implementation(libs.jetbrains.compose.runtime) - implementation(libs.jetbrains.lifecycle.runtime.compose) - implementation(libs.jetbrains.navigation.compose) - implementation(libs.koin.composeViewModel) - implementation(libs.koin.core) - implementation(libs.kotlinx.serialization.core) - implementation(projects.apolloCore) - implementation(projects.apolloNetworkCacheManager) - implementation(projects.apolloOctopusPublic) - implementation(projects.authCorePublic) - implementation(projects.composeUi) - implementation(projects.coreBuildConstants) - implementation(projects.coreCommonPublic) - implementation(projects.coreDatastorePublic) - implementation(projects.coreDemoMode) - implementation(projects.coreResources) - implementation(projects.coreUiData) - implementation(projects.dataContract) - implementation(projects.dataPayingMember) - implementation(projects.dataSettingsDatastorePublic) - implementation(projects.designSystemHedvig) - implementation(projects.featureFlagsPublic) - implementation(projects.foreverUi) - implementation(projects.languageCore) - implementation(projects.languageData) - implementation(projects.memberRemindersPublic) - implementation(projects.memberRemindersUi) - implementation(projects.moleculePublic) - implementation(projects.navigationCommon) - implementation(projects.navigationCompose) - implementation(projects.navigationComposeTyped) - implementation(projects.navigationCore) - implementation(projects.notificationPermission) - implementation(projects.pullrefresh) - implementation(projects.theme) - - testImplementation(libs.coroutines.test) - testImplementation(projects.coreCommonTest) - testImplementation(projects.coreDatastoreTest) - testImplementation(projects.dataSettingsDatastoreTest) - testImplementation(projects.featureFlagsTest) - testImplementation(projects.languageTest) - testImplementation(projects.memberRemindersTest) - testImplementation(projects.moleculeTest) +kotlin { + sourceSets { + commonMain.dependencies { + implementation(libs.apollo.normalizedCache) + implementation(libs.apollo.runtime) + implementation(libs.arrow.core) + implementation(libs.arrow.fx) + implementation(libs.jetbrains.compose.foundation) + implementation(libs.jetbrains.compose.runtime) + implementation(libs.jetbrains.lifecycle.runtime.compose) + implementation(libs.jetbrains.navigation.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.coreBuildConstants) + implementation(projects.coreCommonPublic) + implementation(projects.coreDemoMode) + implementation(projects.coreResources) + implementation(projects.coreUiData) + implementation(projects.dataPayingMember) + implementation(projects.designSystemHedvig) + implementation(projects.foreverUi) + implementation(projects.languageCore) + implementation(projects.moleculePublic) + implementation(projects.navigationCommon) + implementation(projects.navigationCompose) + implementation(projects.navigationCore) + implementation(projects.pullrefresh) + implementation(projects.theme) + } + androidMain.dependencies { + implementation(libs.bundles.kmpPreviewBugWorkaround) + } + } } diff --git a/app/feature/feature-payments/src/main/graphql/QueryDiscounts.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryDiscounts.graphql similarity index 100% rename from app/feature/feature-payments/src/main/graphql/QueryDiscounts.graphql rename to app/feature/feature-payments/src/commonMain/graphql/QueryDiscounts.graphql diff --git a/app/feature/feature-payments/src/main/graphql/QueryMemberPaymentDetails.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryMemberPaymentDetails.graphql similarity index 100% rename from app/feature/feature-payments/src/main/graphql/QueryMemberPaymentDetails.graphql rename to app/feature/feature-payments/src/commonMain/graphql/QueryMemberPaymentDetails.graphql diff --git a/app/feature/feature-payments/src/main/graphql/QueryPaymentsHistory.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryPaymentsHistory.graphql similarity index 100% rename from app/feature/feature-payments/src/main/graphql/QueryPaymentsHistory.graphql rename to app/feature/feature-payments/src/commonMain/graphql/QueryPaymentsHistory.graphql diff --git a/app/feature/feature-payments/src/main/graphql/QueryReferrals.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryReferrals.graphql similarity index 100% rename from app/feature/feature-payments/src/main/graphql/QueryReferrals.graphql rename to app/feature/feature-payments/src/commonMain/graphql/QueryReferrals.graphql diff --git a/app/feature/feature-payments/src/main/graphql/QueryShortPaymentsHistory.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryShortPaymentsHistory.graphql similarity index 100% rename from app/feature/feature-payments/src/main/graphql/QueryShortPaymentsHistory.graphql rename to app/feature/feature-payments/src/commonMain/graphql/QueryShortPaymentsHistory.graphql diff --git a/app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql similarity index 100% rename from app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql rename to app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql diff --git a/app/feature/feature-payments/src/main/graphql/QueryUpcomingPayment.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryUpcomingPayment.graphql similarity index 100% rename from app/feature/feature-payments/src/main/graphql/QueryUpcomingPayment.graphql rename to app/feature/feature-payments/src/commonMain/graphql/QueryUpcomingPayment.graphql diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/PreviewData.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/PreviewData.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/PreviewData.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/PreviewData.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/Discount.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/Discount.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/Discount.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/Discount.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt rename to app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt diff --git a/app/feature/feature-payments/src/main/AndroidManifest.xml b/app/feature/feature-payments/src/main/AndroidManifest.xml deleted file mode 100644 index 568741e54f..0000000000 --- a/app/feature/feature-payments/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file From e585caa394ba1ce35067776adeaf2af02219704e Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 17 Apr 2026 10:55:55 +0200 Subject: [PATCH 07/50] Fix using java local date in CMP code --- .../android/feature/payments/data/MemberCharge.kt | 11 ++++------- .../ui/details/PaymentDetailExpandableCard.kt | 1 - .../payments/ui/history/PaymentHistoryDestination.kt | 1 - 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt index 99fada4411..9f98e86fc5 100644 --- a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt +++ b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt @@ -3,13 +3,10 @@ package com.hedvig.android.feature.payments.data import com.hedvig.android.core.uidata.UiCurrencyCode import com.hedvig.android.core.uidata.UiMoney import com.hedvig.android.feature.payments.data.Discount.DiscountStatus -import kotlin.String -import kotlin.time.Clock +import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.LocalDate -import kotlinx.datetime.TimeZone import kotlinx.datetime.daysUntil -import kotlinx.datetime.toJavaLocalDate -import kotlinx.datetime.todayIn +import kotlinx.datetime.plus import kotlinx.serialization.Serializable import octopus.PaymentHistoryWithDetailsQuery import octopus.ShortPaymentHistoryQuery @@ -74,7 +71,7 @@ internal data class MemberCharge( val isPreviouslyFailedCharge: Boolean, ) { val description: Description? = when { - fromDate.dayOfMonth == 1 && toDate.isLastDayOfMonth() -> { + fromDate.day == 1 && toDate.isLastDayOfMonth() -> { Description.FullPeriod } @@ -195,5 +192,5 @@ internal fun MemberChargeFragment.toFailedCharge(): MemberCharge.FailedCharge? { } fun LocalDate.isLastDayOfMonth(): Boolean { - return toJavaLocalDate().lengthOfMonth() == dayOfMonth + return plus(1, DateTimeUnit.DAY).day == 1 } diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt index 08b7e32e55..6cea80d2cc 100644 --- a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt +++ b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt @@ -66,7 +66,6 @@ import hedvig.resources.TALKBACK_EXPANDABLE_CLICK_LABEL_EXPAND import hedvig.resources.TALKBACK_EXPANDABLE_STATE_COLLAPSED import hedvig.resources.TALKBACK_EXPANDABLE_STATE_EXPANDED import kotlinx.datetime.LocalDate -import kotlinx.datetime.toJavaLocalDate import org.jetbrains.compose.resources.stringResource @Composable diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt index 5858b5acd7..5747eb73c7 100644 --- a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt +++ b/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt @@ -40,7 +40,6 @@ import hedvig.resources.PAYMENTS_NO_HISTORY_DATA import hedvig.resources.PAYMENT_HISTORY_TITLE import hedvig.resources.Res import kotlinx.datetime.LocalDate -import kotlinx.datetime.toJavaLocalDate import org.jetbrains.compose.resources.stringResource @Composable From b46e36aad79eb89d65c32e507691b06a192e089d Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 17 Apr 2026 11:51:20 +0200 Subject: [PATCH 08/50] Update schema --- .../android/apollo/octopus/schema.graphqls | 142 ++++++++++++------ .../main/graphql/SetupTrustlyPayout.graphql | 2 - .../QueryShouldShowPayoutButton.graphql | 4 +- .../src/main/graphql/GetPayoutMethods.graphql | 1 - .../main/graphql/SetupNordeaPayout.graphql | 2 +- .../src/main/graphql/SetupSwishPayout.graphql | 2 +- 6 files changed, 102 insertions(+), 51 deletions(-) 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 df38b7dc8f..c22e8b1420 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 @@ -2734,6 +2734,22 @@ type MemberPaymentAvailablePaymentMethod { True if the member can set up this payment method for payout. """ supportsPayout: Boolean! + """ + True if this method is already ACTIVE for member and can be chosen as default directly without setup, false if + this is a new payment method that the member has not yet set up. + If this is true, then the `details` field will be populated with the payment method details. If this is false, then + the `details` field will be null since the member has not yet set up this payment method. + If true then this method can be set up as default directly by calling `paymentMethodSetDefaultPayin` or + `paymentMethodSetDefaultPayout` mutation depending on if it's a payin or payout method. If false, then the + corresponding mutation for setting up this payment method should be called, eg. `paymentMethodSetupTrustly`, + `paymentMethodSetupSwishPayin` etc. + """ + isActive: Boolean! + """ + For already connected and ACTIVE methods, ie isActive=true, specific details of the actual connection - e.g. a bank + account reference, phone number for swish, or email/kivra for invoice. + """ + details: PaymentMethodDetails } type MemberPaymentChargeMethodInfo { """ @@ -2806,48 +2822,75 @@ type MemberPaymentInformation { } type MemberPaymentMethod { """ - The unique id of the payment method. This id is used for switching default and revoking payment methods. - """ - id: ID! - """ - Payment provider, eg Trustly, Swish, Nordea, Kivra etc. + Payment provider, eg Trustly, Swish, Nordea, Kivra etc. + This is used as the "identifier" of the payment method since there can only be one ACTIVE or PENDING payment method + per provider. """ provider: MemberPaymentProvider! """ - The payment method status - ACTIVE, PENDING, or PENDING_DEFAULT. - PENDING_DEFAULT means the payment method is awaiting activation and will become default once activated. + The payment method status - ACTIVE, PENDING. + If ACTIVE, the payment method is ready to use for payins or payouts depending on if it's a payin or payout method. + If PENDING, the payment method has been set up but is still awaiting activation and cannot be used for payins or + payouts until then. Once activated, the status will change to ACTIVE. """ status: MemberPaymentMethodStatus! """ - True if this is the default payment method. Only one ACTIVE payment method can be default at a time. - If status is PENDING then payment method will become the default once activated. + This is 'true' for only one of the members ACTIVE methods which is the default payment method that will be used for + charging or payout the member. For PENDING methods, this can also be 'true' if the member has chosen to set up this + payment method as default during the setup process. """ isDefault: Boolean! """ - Specific details of the actual connection - e.g. a bank account reference, phone number for swish, + Specific details of the actual connection if method is ACTIVE - e.g. a bank account reference, phone number for swish, or email/kivra for invoice. """ details: PaymentMethodDetails! } type MemberPaymentMethods { """ - List of active and pending payment payin methods for this member. + List of all member's ACTIVE and PENDING payment payin methods. + A member can have multiple ACTIVE payment methods with these constraints: + - Only one ACTIVE payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + - Only one ACTIVE payment method can be default at a time. + A member can have multiple PENDING payment methods with these constraints: + - Only one PENDING payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + So there can exist max two payment methods per provider, one ACTIVE and one PENDING. + If a PENDING payment method has isDefault=true, then it will become the default ACTIVE payment method once activated. """ payinMethods: [MemberPaymentMethod!]! """ - List of active and pending payment payout methods for this member. + List of all member's ACTIVE and PENDING payment payout methods. + A member can have multiple ACTIVE payment methods with these constraints: + - Only one ACTIVE payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + - Only one ACTIVE payment method can be default at a time. + A member can have multiple PENDING payment methods with these constraints: + - Only one PENDING payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + So there can exist max two payment methods per provider, one ACTIVE and one PENDING. + If a PENDING payment method has isDefault=true, then it will become the default ACTIVE payment method once activated. """ payoutMethods: [MemberPaymentMethod!]! """ - The default payment method for payin if any. + The default payment method to use for payins if any. + Note that there can exist a PENDING payment method in `payinMethods` list with `isDefault`=true, in that case this default + payment method will be replaced by it once the pending method is activated. """ defaultPayinMethod: MemberPaymentMethod """ - The default payment method for payout if any. + The default payment method to use for payouts if any. + Note that there can exist a PENDING payment method in `payoutMethods` list with `isDefault`=true, in that case this default + payment method will be replaced by it once the pending method is activated. """ defaultPayoutMethod: MemberPaymentMethod """ - The available payment methods that the member can choose from when setting up a new payment method. + The available payment methods that the member can choose from when setting up a new payment method. + This list can include both payment methods that the member has already set up and new payment methods that the + member has not yet set up but are available to them. For already set up payment methods, the `isActive` field will + be true and the `details` field will be populated with the payment method details. For new payment methods that the + member has not yet set up, the `isActive` field will be false and the `details` field will be null. + If member picks a new payment method to set up, the corresponding mutation for setting up that payment method should + be called, eg. `paymentMethodSetupTrustly`, `paymentMethodSetupSwishPayin` etc. + If member picks an already set up payment method to set up as default, then `paymentMethodSetDefaultPayin` or + `paymentMethodSetDefaultPayout` mutation should be called depending on if it's a payin or payout method. """ availableMethods: [MemberPaymentAvailablePaymentMethod!]! """ @@ -3397,9 +3440,9 @@ type Mutation { """ Setup invoice payment method for the member. Kivra will be used as the provider if supported, else mail. """ - paymentMethodSetupInvoicePayin(input: PaymentMethodSetupInvoicePayinInput!): PaymentMethodSetupOutput! + paymentMethodSetupInvoicePayin: PaymentMethodSetupOutput! """ - Setup Trustly payment payin and payout method for the member. + Setup Trustly payment payin and payout method for the member. Requires member consent via redirect to Trustly URL in response. """ paymentMethodSetupTrustly(input: PaymentMethodSetupTrustlyInput!): PaymentMethodSetupOutput! """ @@ -3411,17 +3454,19 @@ type Mutation { """ paymentMethodSetupSwishPayout(input: PaymentMethodSetupSwishInput!): PaymentMethodSetupOutput! """ - Setup Swish payin method for the member. + Setup Swish payin method for the member. Requires member consent in Swish app. """ paymentMethodSetupSwishPayin(input: PaymentMethodSetupSwishInput!): PaymentMethodSetupOutput! """ - Revoke an active payment method. The member will be required to set up a new payment method if they revoke their default one. + A member can have multiple ACTIVE payment methods where one of those is default. This mutation changes the + members default payment method for charging to any of his/hers other active payment methods. """ - paymentMethodRevoke(id: ID!): UserError + paymentMethodSetDefaultPayin(provider: MemberPaymentProvider!): UserError """ - Set an active payment method as default. + A member can have multiple ACTIVE payment methods where one of those is default. This mutation changes the + members default payment method for payouts to any of his/hers other active payment methods. """ - paymentMethodSetDefault(id: ID!): UserError + paymentMethodSetDefaultPayout(provider: MemberPaymentProvider!): UserError """ Start a conversation. This is effectively creating one, but with two slight differences from a regular "create something"-mutation: @@ -3712,17 +3757,7 @@ type PaymentMethodInvoiceDetails { """ email: String } -input PaymentMethodSetupInvoicePayinInput { - """ - Set up invoice payment method as default. - """ - setAsDefaultPayin: Boolean! -} input PaymentMethodSetupNordeaPayoutInput { - """ - Set up Nordea payout method as default. - """ - setAsDefault: Boolean! """ The clearing number for member's bank account. """ @@ -3761,24 +3796,12 @@ enum PaymentMethodSetupStatus { FAILED } input PaymentMethodSetupSwishInput { - """ - Set up Swish payment method as default. - """ - setAsDefault: Boolean! """ The Swish mobile number to use for payout or payin. """ phoneNumber: String! } input PaymentMethodSetupTrustlyInput { - """ - Set up Trustly payment method as default for payin. - """ - setAsDefaultPayin: Boolean! - """ - Set up Trustly payment method as default for payout. - """ - setAsDefaultPayout: Boolean! """ The URL to redirect the member back to after a successful setup after Trustly onboarding. """ @@ -4244,6 +4267,11 @@ if the user has input enough information to generate it. type ProductRecommendation { product: Product! offer: ProductOffer + """ + External insurance data from Insurely, available even when no offer could be generated. + Null when no Insurely data collection is associated with the session. + """ + externalInsurance: RecommendationExternalInsurance } type ProductVariant { """ @@ -4376,6 +4404,32 @@ type Query { """ addonOfferCost(quoteId: ID!, selectedAddonIds: [ID!]!): ItemCost! } +type RecommendationExternalInsurance { + """ + Display name of the external insurance product + """ + displayName: String! + """ + The external insurer + """ + insurer: ExternalInsurer! + """ + Monthly price of the external insurance. Null if not available. + """ + price: Money + """ + Contextual subtitle (e.g. address, registration number, pet name) + """ + subtitle: String + """ + Renewal date of the external policy. Null when not provided. + """ + renewalDate: Date + """ + Insurely data collection ID + """ + dataCollectionId: String! +} type RecommendedCrossSell { crossSell: CrossSell! bannerText: String! diff --git a/app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql b/app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql index 2b1fe46243..5ecb8535cc 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql +++ b/app/feature/feature-connect-payment-trustly/src/main/graphql/SetupTrustlyPayout.graphql @@ -1,8 +1,6 @@ mutation SetupTrustlyPayout($successUrl: String!, $failureUrl: String!) { paymentMethodSetupTrustly( input: { - setAsDefaultPayin: false, - setAsDefaultPayout: true, successUrl: $successUrl, failureUrl: $failureUrl } diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql b/app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql index 9c9ceab92c..0de72fbf20 100644 --- a/app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql +++ b/app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql @@ -2,10 +2,10 @@ query ShouldShowPayoutButton { currentMember { paymentMethods { defaultPayoutMethod { - id + provider } payoutMethods { - id + provider } availableMethods { supportsPayout diff --git a/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql index 6c74da40e7..98c876de37 100644 --- a/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql +++ b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql @@ -2,7 +2,6 @@ query GetPayoutMethods { currentMember { paymentMethods { defaultPayoutMethod { - id provider details { ... on PaymentMethodBankAccountDetails { diff --git a/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql b/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql index 12b22d1dcc..4de67f6f87 100644 --- a/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql +++ b/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql @@ -1,6 +1,6 @@ mutation SetupNordeaPayout($clearingNumber: String!, $accountNumber: String!) { paymentMethodSetupNordeaPayout( - input: { setAsDefault: true, clearingNumber: $clearingNumber, accountNumber: $accountNumber } + input: { clearingNumber: $clearingNumber, accountNumber: $accountNumber } ) { status error { diff --git a/app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql b/app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql index e8d76c73f0..347b1a44b9 100644 --- a/app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql +++ b/app/feature/feature-payout-account/src/main/graphql/SetupSwishPayout.graphql @@ -1,6 +1,6 @@ mutation SetupSwishPayout($phoneNumber: String!) { paymentMethodSetupSwishPayout( - input: { setAsDefault: true, phoneNumber: $phoneNumber } + input: { phoneNumber: $phoneNumber } ) { status error { message } From 8f3fed7d2b8dace6ce8772f918b0328abb26ac35 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Mon, 20 Apr 2026 18:46:18 +0200 Subject: [PATCH 09/50] More payout changes Add support for showing invoice option as the default payout option Add back snackbar success texts Fix swish basic numer validation --- .../android/app/navigation/HedvigNavHost.kt | 9 ++++--- .../src/main/graphql/GetPayoutMethods.graphql | 4 +++ .../data/GetPayoutAccountUseCase.kt | 24 +++++++++++++++-- .../payoutaccount/data/PayoutAccount.kt | 7 +++++ .../data/SetupNordeaPayoutUseCase.kt | 1 + .../data/SetupSwishPayoutUseCase.kt | 1 + .../navigation/PayoutAccountGraph.kt | 4 +++ .../EditBankAccountDestination.kt | 15 +++++++++-- .../EditBankAccountViewModel.kt | 27 ++++++++++++++----- .../PayoutAccountOverviewDestination.kt | 25 +++++++++++++++++ .../SelectPayoutMethodDestination.kt | 10 +++---- .../setupswish/SetupSwishPayoutDestination.kt | 17 +++++++++--- .../setupswish/SetupSwishPayoutViewModel.kt | 14 +++++++--- 13 files changed, 130 insertions(+), 28 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 04505af23c..757ffe25fc 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -349,6 +349,7 @@ internal fun HedvigNavHost( ) payoutAccountGraph( navController = navController, + globalSnackBarState = globalSnackBarState, navigateToTrustlyPayout = { navController.navigate(TrustlyPayoutDestination) }, navigateUp = navController::navigateUp, ) @@ -431,10 +432,10 @@ internal fun HedvigNavHost( hedvigDeepLinkContainer = hedvigDeepLinkContainer, popBackStackOrFinish = popBackStackOrFinish, goHome = { - navController.navigate(HomeDestination.Graph) { - popUpTo(ChipIdGraphDestination::class) { inclusive = true } - } - } + navController.navigate(HomeDestination.Graph) { + popUpTo(ChipIdGraphDestination::class) { inclusive = true } + } + }, ) movingFlowGraph( navController = navController, diff --git a/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql index 98c876de37..aa8f8d53b0 100644 --- a/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql +++ b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql @@ -11,6 +11,10 @@ query GetPayoutMethods { ... on PaymentMethodSwishDetails { phoneNumber } + ... on PaymentMethodInvoiceDetails { + delivery + email + } } } availableMethods { diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index 5e79eb0cbd..3ada7073b1 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -10,6 +10,7 @@ import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage import octopus.GetPayoutMethodsQuery import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodBankAccountDetails +import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodInvoiceDetails import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodSwishDetails import octopus.type.MemberPaymentProvider @@ -35,7 +36,10 @@ internal class GetPayoutAccountUseCaseImpl( val paymentMethods = result.currentMember.paymentMethods val currentMethod = paymentMethods.defaultPayoutMethod?.let { method -> when (method.provider) { - MemberPaymentProvider.TRUSTLY -> PayoutAccount.Trustly + MemberPaymentProvider.TRUSTLY -> { + PayoutAccount.Trustly + } + MemberPaymentProvider.SWISH -> { val swishDetails = method.details.asPaymentMethodSwishDetails() if (swishDetails != null) { @@ -44,6 +48,7 @@ internal class GetPayoutAccountUseCaseImpl( null } } + MemberPaymentProvider.NORDEA -> { val bankAccountDetails = method.details.asPaymentMethodBankAccountDetails() if (bankAccountDetails != null) { @@ -60,7 +65,22 @@ internal class GetPayoutAccountUseCaseImpl( null } } - else -> null + + MemberPaymentProvider.INVOICE -> { + val invoiceDetails = method.details.asPaymentMethodInvoiceDetails() + if (invoiceDetails != null) { + PayoutAccount.Invoice( + delivery = invoiceDetails.delivery, + email = invoiceDetails.email, + ) + } else { + null + } + } + + else -> { + null + } } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt index 228fb4e454..e510741c9b 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt @@ -1,5 +1,7 @@ package com.hedvig.android.feature.payoutaccount.data +import octopus.type.PaymentMethodInvoiceDelivery + internal sealed interface PayoutAccount { data object Trustly : PayoutAccount @@ -10,4 +12,9 @@ internal sealed interface PayoutAccount { val accountNumber: String, val bankName: String?, ) : PayoutAccount + + data class Invoice( + val delivery: PaymentMethodInvoiceDelivery, + val email: String?, + ) : PayoutAccount } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt index 8369ed530f..d8c25f6d9c 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt @@ -31,6 +31,7 @@ internal class SetupNordeaPayoutUseCaseImpl( PaymentMethodSetupStatus.FAILED -> { raise(ErrorMessage(output.error?.message ?: "Failed to set up payout method")) } + else -> { networkCacheManager.clearCache() } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt index 84b11df5e1..f78b2240ae 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt @@ -25,6 +25,7 @@ internal class SetupSwishPayoutUseCase( PaymentMethodSetupStatus.FAILED -> { raise(ErrorMessage(output.error?.message ?: "Failed to set up Swish payout")) } + else -> { networkCacheManager.clearCache() } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index 4a330fd1ad..92a3f4a133 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -3,6 +3,7 @@ package com.hedvig.android.feature.payoutaccount.navigation import androidx.lifecycle.compose.dropUnlessResumed import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import com.hedvig.android.design.system.hedvig.GlobalSnackBarState import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountDestination import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewDestination @@ -18,6 +19,7 @@ import org.koin.compose.viewmodel.koinViewModel fun NavGraphBuilder.payoutAccountGraph( navController: NavController, + globalSnackBarState: GlobalSnackBarState, navigateToTrustlyPayout: () -> Unit, navigateUp: () -> Unit, ) { @@ -57,6 +59,7 @@ fun NavGraphBuilder.payoutAccountGraph( val viewModel: EditBankAccountViewModel = koinViewModel() EditBankAccountDestination( viewModel = viewModel, + globalSnackBarState = globalSnackBarState, navigateUp = navController::navigateUp, ) } @@ -65,6 +68,7 @@ fun NavGraphBuilder.payoutAccountGraph( val viewModel: SetupSwishPayoutViewModel = koinViewModel() SetupSwishPayoutDestination( viewModel = viewModel, + globalSnackBarState = globalSnackBarState, navigateUp = navController::navigateUp, ) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index 012575d59f..0814528d43 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.GlobalSnackBarState import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigScaffold @@ -28,12 +29,18 @@ import com.hedvig.android.design.system.hedvig.NotificationDefaults.Notification @Composable internal fun EditBankAccountDestination( viewModel: EditBankAccountViewModel, + globalSnackBarState: GlobalSnackBarState, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() EditBankAccountScreen( uiState = uiState, + globalSnackBarState = globalSnackBarState, onSave = { viewModel.emit(EditBankAccountEvent.Save) }, + showedSnackBar = { + viewModel.emit(EditBankAccountEvent.ShowedSnackBar) + navigateUp() + }, navigateUp = navigateUp, ) } @@ -41,11 +48,15 @@ internal fun EditBankAccountDestination( @Composable private fun EditBankAccountScreen( uiState: EditBankAccountUiState, + globalSnackBarState: GlobalSnackBarState, onSave: () -> Unit, + showedSnackBar: () -> Unit, navigateUp: () -> Unit, ) { - LaunchedEffect(uiState.navigateBack) { - if (uiState.navigateBack) navigateUp() + LaunchedEffect(uiState.showSuccessSnackBar) { + if (!uiState.showSuccessSnackBar) return@LaunchedEffect + globalSnackBarState.show("Changes saved", NotificationPriority.Campaign) + showedSnackBar() } HedvigScaffold( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index 832af1b486..2c1a5af266 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -22,12 +22,21 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class EditBankAccountViewModel( setupNordeaPayoutUseCase: SetupNordeaPayoutUseCase, ) : MoleculeViewModel( - EditBankAccountUiState(TextFieldState(), TextFieldState(), null, false, null, false), - EditBankAccountPresenter(setupNordeaPayoutUseCase), -) + EditBankAccountUiState( + clearingNumberState = TextFieldState(), + accountNumberState = TextFieldState(), + bankName = null, + isLoading = false, + errorMessage = null, + showSuccessSnackBar = false, + ), + EditBankAccountPresenter(setupNordeaPayoutUseCase), + ) internal sealed interface EditBankAccountEvent { data object Save : EditBankAccountEvent + + data object ShowedSnackBar : EditBankAccountEvent } internal data class EditBankAccountUiState( @@ -36,7 +45,7 @@ internal data class EditBankAccountUiState( val bankName: String?, val isLoading: Boolean, val errorMessage: String?, - val navigateBack: Boolean, + val showSuccessSnackBar: Boolean, ) { // Swedish clearing numbers are 4 digits for most banks, 5 for Swedbank's 8-series val clearingInputTransformation: InputTransformation = InputTransformation.maxLength(5).digitsOnly() @@ -57,7 +66,7 @@ internal class EditBankAccountPresenter( val bankName = bankNameForClearingNumber(clearingNumberState.text.toString()) var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } - var navigateBack by remember { mutableStateOf(false) } + var showSuccessSnackBar by remember { mutableStateOf(false) } var saveIteration by remember { mutableStateOf?>(null) } val currentSave = saveIteration @@ -73,7 +82,7 @@ internal class EditBankAccountPresenter( }, ifRight = { isLoading = false - navigateBack = true + showSuccessSnackBar = true saveIteration = null }, ) @@ -87,6 +96,10 @@ internal class EditBankAccountPresenter( saveIteration = clearingNumberState.text.toString() to accountNumberState.text.toString() } } + + EditBankAccountEvent.ShowedSnackBar -> { + showSuccessSnackBar = false + } } } @@ -96,7 +109,7 @@ internal class EditBankAccountPresenter( bankName = bankName, isLoading = isLoading, errorMessage = errorMessage, - navigateBack = navigateBack, + showSuccessSnackBar = showSuccessSnackBar, ) } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 7d21a804e9..0e447fa510 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -154,6 +154,31 @@ private fun PayoutAccountContent( } } + is PayoutAccount.Invoice -> { + HedvigTextField( + text = "Invoice", + onValueChange = {}, + labelText = "Account", + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + readOnly = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + if (availablePayoutMethods.size > 1) { + Spacer(Modifier.height(8.dp)) + HedvigButton( + text = "Change account", + onClick = onConnectPayoutMethodClicked, + enabled = true, + buttonStyle = ButtonDefaults.ButtonStyle.Secondary, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + } + is PayoutAccount.BankAccount -> { val displayText = buildString { if (currentMethod.bankName != null) { diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt index ee3ffb5d66..94be09ad09 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -40,6 +40,7 @@ internal fun SelectPayoutMethodDestination( ) Spacer(Modifier.height(8.dp)) } + MemberPaymentProvider.NORDEA -> { PayoutMethodRow( title = "Bank account", @@ -48,6 +49,7 @@ internal fun SelectPayoutMethodDestination( ) Spacer(Modifier.height(8.dp)) } + MemberPaymentProvider.SWISH -> { PayoutMethodRow( title = "Swish", @@ -56,6 +58,7 @@ internal fun SelectPayoutMethodDestination( ) Spacer(Modifier.height(8.dp)) } + else -> {} } } @@ -65,12 +68,7 @@ internal fun SelectPayoutMethodDestination( } @Composable -private fun PayoutMethodRow( - title: String, - subtitle: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { +private fun PayoutMethodRow(title: String, subtitle: String, onClick: () -> Unit, modifier: Modifier = Modifier) { HedvigCard( onClick = onClick, modifier = modifier.fillMaxWidth(), diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt index 793a12fdfd..c968e6db90 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.GlobalSnackBarState import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigScaffold @@ -27,12 +28,18 @@ import com.hedvig.android.design.system.hedvig.NotificationDefaults.Notification @Composable internal fun SetupSwishPayoutDestination( viewModel: SetupSwishPayoutViewModel, + globalSnackBarState: GlobalSnackBarState, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() SetupSwishPayoutScreen( uiState = uiState, + globalSnackBarState = globalSnackBarState, onSave = { viewModel.emit(SetupSwishPayoutEvent.Save) }, + showedSnackBar = { + viewModel.emit(SetupSwishPayoutEvent.ShowedSnackBar) + navigateUp() + }, navigateUp = navigateUp, ) } @@ -40,11 +47,15 @@ internal fun SetupSwishPayoutDestination( @Composable private fun SetupSwishPayoutScreen( uiState: SetupSwishPayoutUiState, + globalSnackBarState: GlobalSnackBarState, onSave: () -> Unit, + showedSnackBar: () -> Unit, navigateUp: () -> Unit, ) { - LaunchedEffect(uiState.navigateBack) { - if (uiState.navigateBack) navigateUp() + LaunchedEffect(uiState.showSuccessSnackBar) { + if (!uiState.showSuccessSnackBar) return@LaunchedEffect + globalSnackBarState.show("Changes saved", NotificationPriority.Campaign) + showedSnackBar() } HedvigScaffold( @@ -82,7 +93,7 @@ private fun SetupSwishPayoutScreen( HedvigButton( text = "Save", onClick = onSave, - enabled = !uiState.isLoading && uiState.phoneNumberState.text.isNotBlank(), + enabled = !uiState.isLoading && uiState.phoneNumberState.text.length >= 10, isLoading = uiState.isLoading, modifier = Modifier .fillMaxWidth() diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt index 14b7419498..c024917d88 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt @@ -21,13 +21,15 @@ internal class SetupSwishPayoutViewModel( internal sealed interface SetupSwishPayoutEvent { data object Save : SetupSwishPayoutEvent + + data object ShowedSnackBar : SetupSwishPayoutEvent } internal data class SetupSwishPayoutUiState( val phoneNumberState: TextFieldState, val isLoading: Boolean, val errorMessage: String?, - val navigateBack: Boolean, + val showSuccessSnackBar: Boolean, ) internal class SetupSwishPayoutPresenter( @@ -40,7 +42,7 @@ internal class SetupSwishPayoutPresenter( val phoneNumberState = remember { lastState.phoneNumberState } var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } - var navigateBack by remember { mutableStateOf(false) } + var showSuccessSnackBar by remember { mutableStateOf(false) } var saveIteration by remember { mutableStateOf(null) } val currentSave = saveIteration @@ -56,7 +58,7 @@ internal class SetupSwishPayoutPresenter( }, ifRight = { isLoading = false - navigateBack = true + showSuccessSnackBar = true saveIteration = null }, ) @@ -70,6 +72,10 @@ internal class SetupSwishPayoutPresenter( saveIteration = phoneNumberState.text.toString() } } + + SetupSwishPayoutEvent.ShowedSnackBar -> { + showSuccessSnackBar = false + } } } @@ -77,7 +83,7 @@ internal class SetupSwishPayoutPresenter( phoneNumberState = phoneNumberState, isLoading = isLoading, errorMessage = errorMessage, - navigateBack = navigateBack, + showSuccessSnackBar = showSuccessSnackBar, ) } } From b27a94210c2610c1d6205fbdc6564d6cf75b2d53 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Mon, 20 Apr 2026 19:15:52 +0200 Subject: [PATCH 10/50] Test locally --- .../payoutaccount/data/FakePayoutAccountStorage.kt | 5 +++++ .../payoutaccount/data/GetPayoutAccountUseCase.kt | 9 +++++++++ .../payoutaccount/data/SetupNordeaPayoutUseCase.kt | 6 ++++++ .../payoutaccount/data/SetupSwishPayoutUseCase.kt | 2 ++ .../feature/payoutaccount/di/PayoutAccountModule.kt | 4 +++- 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt new file mode 100644 index 0000000000..6f4b267739 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt @@ -0,0 +1,5 @@ +package com.hedvig.android.feature.payoutaccount.data + +internal object FakePayoutAccountStorage { + var currentMethod: PayoutAccount? = null +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index 3ada7073b1..e693c851f8 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -27,6 +27,15 @@ internal class GetPayoutAccountUseCaseImpl( private val apolloClient: ApolloClient, ) : GetPayoutAccountUseCase { override suspend fun invoke(): Either = either { + return@either PayoutAccountData( + currentMethod = FakePayoutAccountStorage.currentMethod, + availablePayoutMethods = listOf( + MemberPaymentProvider.TRUSTLY, + MemberPaymentProvider.NORDEA, + MemberPaymentProvider.SWISH, + MemberPaymentProvider.INVOICE, + ), + ) val result = apolloClient .query(GetPayoutMethodsQuery()) .fetchPolicy(FetchPolicy.NetworkOnly) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt index d8c25f6d9c..fb9709fab6 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt @@ -21,6 +21,12 @@ internal class SetupNordeaPayoutUseCaseImpl( private val networkCacheManager: NetworkCacheManager, ) : SetupNordeaPayoutUseCase { override suspend fun invoke(clearingNumber: String, accountNumber: String): Either = either { + FakePayoutAccountStorage.currentMethod = PayoutAccount.BankAccount( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankNameForClearingNumber(clearingNumber), + ) + return@either val result = apolloClient .mutation(SetupNordeaPayoutMutation(clearingNumber = clearingNumber, accountNumber = accountNumber)) .safeExecute(::ErrorMessage) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt index f78b2240ae..872b464bab 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt @@ -15,6 +15,8 @@ internal class SetupSwishPayoutUseCase( private val networkCacheManager: NetworkCacheManager, ) { suspend fun invoke(phoneNumber: String): Either = either { + FakePayoutAccountStorage.currentMethod = PayoutAccount.SwishPayout(phoneNumber = phoneNumber) + return@either val result = apolloClient .mutation(SetupSwishPayoutMutation(phoneNumber = phoneNumber)) .safeExecute(::ErrorMessage) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt index 3d21498662..68f579dbc8 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt @@ -14,7 +14,9 @@ import org.koin.core.module.dsl.viewModel import org.koin.dsl.module val payoutAccountModule = module { - single { GetPayoutAccountUseCaseImpl(get()) } + single { + GetPayoutAccountUseCaseImpl(get()) + } single { SetupNordeaPayoutUseCaseImpl(get(), get()) } From 8bb25b3f247f571ac81f164c7ade39179a90596c Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 10:56:43 +0200 Subject: [PATCH 11/50] Make pullrefresh KMP --- app/ui/pullrefresh/build.gradle.kts | 15 ++++++++++----- .../com/hedvig/android/pullrefresh/PullRefresh.kt | 0 .../android/pullrefresh/PullRefreshIndicator.kt | 0 .../pullrefresh/PullRefreshIndicatorTransform.kt | 0 .../android/pullrefresh/PullRefreshState.kt | 0 app/ui/pullrefresh/src/main/AndroidManifest.xml | 2 -- 6 files changed, 10 insertions(+), 7 deletions(-) rename app/ui/pullrefresh/src/{main => commonMain}/kotlin/com/hedvig/android/pullrefresh/PullRefresh.kt (100%) rename app/ui/pullrefresh/src/{main => commonMain}/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicator.kt (100%) rename app/ui/pullrefresh/src/{main => commonMain}/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicatorTransform.kt (100%) rename app/ui/pullrefresh/src/{main => commonMain}/kotlin/com/hedvig/android/pullrefresh/PullRefreshState.kt (100%) delete mode 100644 app/ui/pullrefresh/src/main/AndroidManifest.xml diff --git a/app/ui/pullrefresh/build.gradle.kts b/app/ui/pullrefresh/build.gradle.kts index affe7b8740..7bec49f8ed 100644 --- a/app/ui/pullrefresh/build.gradle.kts +++ b/app/ui/pullrefresh/build.gradle.kts @@ -1,5 +1,6 @@ plugins { - id("hedvig.android.library") + id("hedvig.multiplatform.library") + id("hedvig.multiplatform.library.android") id("hedvig.gradle.plugin") } @@ -7,8 +8,12 @@ hedvig { compose() } -dependencies { - implementation(libs.androidx.compose.animationCore) - implementation(libs.jetbrains.compose.runtime) - implementation(projects.designSystemHedvig) +kotlin { + sourceSets { + commonMain.dependencies { + implementation(libs.jetbrains.compose.animation.core) + implementation(libs.jetbrains.compose.runtime) + implementation(projects.designSystemHedvig) + } + } } diff --git a/app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefresh.kt b/app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefresh.kt similarity index 100% rename from app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefresh.kt rename to app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefresh.kt diff --git a/app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicator.kt b/app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicator.kt similarity index 100% rename from app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicator.kt rename to app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicator.kt diff --git a/app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicatorTransform.kt b/app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicatorTransform.kt similarity index 100% rename from app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicatorTransform.kt rename to app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefreshIndicatorTransform.kt diff --git a/app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefreshState.kt b/app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefreshState.kt similarity index 100% rename from app/ui/pullrefresh/src/main/kotlin/com/hedvig/android/pullrefresh/PullRefreshState.kt rename to app/ui/pullrefresh/src/commonMain/kotlin/com/hedvig/android/pullrefresh/PullRefreshState.kt diff --git a/app/ui/pullrefresh/src/main/AndroidManifest.xml b/app/ui/pullrefresh/src/main/AndroidManifest.xml deleted file mode 100644 index 568741e54f..0000000000 --- a/app/ui/pullrefresh/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file From be16d44c0deb282a5a30350d2e19d0d4b4bdc394 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 10:57:37 +0200 Subject: [PATCH 12/50] Test code --- .../payoutaccount/data/FakePayoutAccountStorage.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt index 6f4b267739..2dac3d2999 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt @@ -1,5 +1,13 @@ package com.hedvig.android.feature.payoutaccount.data +import octopus.type.PaymentMethodInvoiceDelivery + +// Swap the initial value to test each display scenario in the Overview: +// null → "Connect payout account" button +// PayoutAccount.Trustly → Trustly display +// PayoutAccount.SwishPayout("0701234567") → Swish display +// PayoutAccount.BankAccount("8327", "12345678", "Swedbank") → Bank account display +// PayoutAccount.Invoice(PaymentMethodInvoiceDelivery.KIVRA, null) → Invoice display internal object FakePayoutAccountStorage { var currentMethod: PayoutAccount? = null } From 53398184f0550fb016171b4ed0cd57bb8c3dfcf1 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 11:35:27 +0200 Subject: [PATCH 13/50] Add setup invoice step --- .../StartTrustlyPayoutSessionUseCase.kt | 1 + .../payment/trustly/TrustlyPayoutPresenter.kt | 9 +- .../payment/trustly/ui/TrustlyDestination.kt | 21 +++-- .../main/graphql/SetupInvoicePayout.graphql | 8 ++ .../data/SetupInvoicePayoutUseCase.kt | 40 +++++++++ .../payoutaccount/di/PayoutAccountModule.kt | 16 ++-- .../navigation/PayoutAccountDestination.kt | 3 + .../navigation/PayoutAccountGraph.kt | 12 +++ .../SelectPayoutMethodDestination.kt | 10 +++ .../SetupInvoicePayoutDestination.kt | 88 +++++++++++++++++++ .../SetupInvoicePayoutViewModel.kt | 86 ++++++++++++++++++ 11 files changed, 272 insertions(+), 22 deletions(-) create mode 100644 app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt create mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutViewModel.kt diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt index 4752f491ad..b43ca2fc01 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt @@ -17,6 +17,7 @@ internal class StartTrustlyPayoutSessionUseCase( ) { suspend fun invoke(): Either { return either { + return@either TrustlyInitiateProcessUrl("fake://trustly-payout-success") val data = apolloClient .mutation( SetupTrustlyPayoutMutation( diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt index 54da291b63..2d8621a4ac 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt @@ -41,8 +41,13 @@ internal class TrustlyPayoutPresenter( browsing = null }, ifRight = { - startSessionError = null - browsing = TrustlyUiState.Browsing(it.url, trustlyCallback) + // todo testing remove hardcoded success + if (it.url.startsWith("fake://")) { + succeededInConnectingCard = true + } else { + startSessionError = null + browsing = TrustlyUiState.Browsing(it.url, trustlyCallback) + } }, ) } diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt index 3c1c3b90f6..6098e1effb 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt @@ -41,11 +41,11 @@ import com.hedvig.android.feature.connect.payment.trustly.TrustlyEvent import com.hedvig.android.feature.connect.payment.trustly.TrustlyUiState import com.hedvig.android.feature.connect.payment.trustly.TrustlyViewModel import com.hedvig.android.feature.connect.payment.trustly.data.PreviewTrustlyCallback -import com.hedvig.android.molecule.public.MoleculeViewModel import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebChromeClient import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebView import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebViewClient import com.hedvig.android.logger.logcat +import com.hedvig.android.molecule.public.MoleculeViewModel import com.hedvig.android.navigation.common.Destination import hedvig.resources.Res import hedvig.resources.general_done_button @@ -222,13 +222,12 @@ private fun TrustlyPreview( } } -private class TrustlyUiStateProvider : - CollectionPreviewParameterProvider( - listOf( - TrustlyUiState.Browsing("", PreviewTrustlyCallback("", "")), - TrustlyUiState.Loading, - TrustlyUiState.FailedToConnectCard, - TrustlyUiState.FailedToStartSession, - TrustlyUiState.SucceededInConnectingCard, - ), - ) +private class TrustlyUiStateProvider : CollectionPreviewParameterProvider( + listOf( + TrustlyUiState.Browsing("", PreviewTrustlyCallback("", "")), + TrustlyUiState.Loading, + TrustlyUiState.FailedToConnectCard, + TrustlyUiState.FailedToStartSession, + TrustlyUiState.SucceededInConnectingCard, + ), +) diff --git a/app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql b/app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql new file mode 100644 index 0000000000..dd61ac8dcf --- /dev/null +++ b/app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql @@ -0,0 +1,8 @@ +mutation SetupInvoicePayout { + paymentMethodSetupInvoicePayin { + status + error { + message + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt new file mode 100644 index 0000000000..9078358f80 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt @@ -0,0 +1,40 @@ +package com.hedvig.android.feature.payoutaccount.data + +import arrow.core.Either +import arrow.core.raise.either +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.ErrorMessage +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.apollo.safeExecute +import com.hedvig.android.core.common.ErrorMessage +import octopus.SetupInvoicePayoutMutation +import octopus.type.PaymentMethodInvoiceDelivery +import octopus.type.PaymentMethodSetupStatus + +internal class SetupInvoicePayoutUseCase( + private val apolloClient: ApolloClient, + private val networkCacheManager: NetworkCacheManager, +) { + suspend fun invoke(): Either = either { + FakePayoutAccountStorage.currentMethod = PayoutAccount.Invoice( + delivery = PaymentMethodInvoiceDelivery.KIVRA, + email = null, + ) + return@either + val result = apolloClient + .mutation(SetupInvoicePayoutMutation()) + .safeExecute(::ErrorMessage) + .bind() + + val output = result.paymentMethodSetupInvoicePayin + when (output.status) { + PaymentMethodSetupStatus.FAILED -> { + raise(ErrorMessage(output.error?.message ?: "Failed to set up invoice payout")) + } + + else -> { + networkCacheManager.clearCache() + } + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt index 68f579dbc8..7d722de6d6 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt @@ -4,26 +4,24 @@ import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCase import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCaseImpl +import com.hedvig.android.feature.payoutaccount.data.SetupInvoicePayoutUseCase import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCase import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCaseImpl import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel +import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayoutViewModel import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import org.koin.core.module.dsl.viewModel import org.koin.dsl.module val payoutAccountModule = module { - single { - GetPayoutAccountUseCaseImpl(get()) - } - single { - SetupNordeaPayoutUseCaseImpl(get(), get()) - } - single { - SetupSwishPayoutUseCase(get(), get()) - } + single { GetPayoutAccountUseCaseImpl(get()) } + single { SetupNordeaPayoutUseCaseImpl(get(), get()) } + single { SetupSwishPayoutUseCase(get(), get()) } + single { SetupInvoicePayoutUseCase(get(), get()) } viewModel { PayoutAccountOverviewViewModel(get()) } viewModel { EditBankAccountViewModel(get()) } viewModel { SetupSwishPayoutViewModel(get()) } + viewModel { SetupInvoicePayoutViewModel(get()) } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt index aabd195ec5..6ea6e48a3a 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountDestination.kt @@ -22,4 +22,7 @@ internal sealed interface PayoutAccountDestinations { @Serializable data object SetupSwishPayout : PayoutAccountDestinations, Destination + + @Serializable + data object SetupInvoicePayout : PayoutAccountDestinations, Destination } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index 92a3f4a133..9c8546d4e7 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -10,6 +10,8 @@ import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOvervie import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel import com.hedvig.android.feature.payoutaccount.ui.selectmethod.SelectPayoutMethodDestination +import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayoutDestination +import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayoutViewModel import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutDestination import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import com.hedvig.android.navigation.compose.navdestination @@ -51,6 +53,7 @@ fun NavGraphBuilder.payoutAccountGraph( onTrustlySelected = dropUnlessResumed { navigateToTrustlyPayout() }, onNordeaSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.EditBankAccount) }, onSwishSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.SetupSwishPayout) }, + onInvoiceSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.SetupInvoicePayout) }, navigateUp = navController::navigateUp, ) } @@ -72,5 +75,14 @@ fun NavGraphBuilder.payoutAccountGraph( navigateUp = navController::navigateUp, ) } + + navdestination { + val viewModel: SetupInvoicePayoutViewModel = koinViewModel() + SetupInvoicePayoutDestination( + viewModel = viewModel, + globalSnackBarState = globalSnackBarState, + navigateUp = navController::navigateUp, + ) + } } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt index 94be09ad09..36344178bf 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -21,6 +21,7 @@ internal fun SelectPayoutMethodDestination( onTrustlySelected: () -> Unit, onNordeaSelected: () -> Unit, onSwishSelected: () -> Unit, + onInvoiceSelected: () -> Unit, navigateUp: () -> Unit, ) { HedvigScaffold( @@ -59,6 +60,15 @@ internal fun SelectPayoutMethodDestination( Spacer(Modifier.height(8.dp)) } + MemberPaymentProvider.INVOICE -> { + PayoutMethodRow( + title = "Invoice", + subtitle = "Connect via Kivra", + onClick = onInvoiceSelected, + ) + Spacer(Modifier.height(8.dp)) + } + else -> {} } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt new file mode 100644 index 0000000000..cfcf698639 --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt @@ -0,0 +1,88 @@ +package com.hedvig.android.feature.payoutaccount.ui.setupinvoice + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.GlobalSnackBarState +import com.hedvig.android.design.system.hedvig.HedvigButton +import com.hedvig.android.design.system.hedvig.HedvigNotificationCard +import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority + +@Composable +internal fun SetupInvoicePayoutDestination( + viewModel: SetupInvoicePayoutViewModel, + globalSnackBarState: GlobalSnackBarState, + navigateUp: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + SetupInvoicePayoutScreen( + uiState = uiState, + globalSnackBarState = globalSnackBarState, + onConnect = { viewModel.emit(SetupInvoicePayoutEvent.Connect) }, + showedSnackBar = { + viewModel.emit(SetupInvoicePayoutEvent.ShowedSnackBar) + navigateUp() + }, + navigateUp = navigateUp, + ) +} + +@Composable +private fun SetupInvoicePayoutScreen( + uiState: SetupInvoicePayoutUiState, + globalSnackBarState: GlobalSnackBarState, + onConnect: () -> Unit, + showedSnackBar: () -> Unit, + navigateUp: () -> Unit, +) { + LaunchedEffect(uiState.showSuccessSnackBar) { + if (!uiState.showSuccessSnackBar) return@LaunchedEffect + globalSnackBarState.show("Changes saved", NotificationPriority.Campaign) + showedSnackBar() + } + + HedvigScaffold( + topAppBarText = "Invoice", + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + Spacer(Modifier.weight(1f)) + AnimatedVisibility( + visible = uiState.errorMessage != null, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + HedvigNotificationCard( + message = uiState.errorMessage ?: "", + priority = NotificationPriority.Attention, + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = 4.dp) + .fillMaxWidth(), + ) + } + Spacer(Modifier.height(16.dp)) + HedvigButton( + text = "Connect", + onClick = onConnect, + enabled = !uiState.isLoading, + isLoading = uiState.isLoading, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + Spacer(Modifier.height(16.dp)) + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutViewModel.kt new file mode 100644 index 0000000000..478e28d66e --- /dev/null +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutViewModel.kt @@ -0,0 +1,86 @@ +package com.hedvig.android.feature.payoutaccount.ui.setupinvoice + +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.setValue +import com.hedvig.android.feature.payoutaccount.data.SetupInvoicePayoutUseCase +import com.hedvig.android.molecule.public.MoleculePresenter +import com.hedvig.android.molecule.public.MoleculePresenterScope +import com.hedvig.android.molecule.public.MoleculeViewModel + +internal class SetupInvoicePayoutViewModel( + setupInvoicePayoutUseCase: SetupInvoicePayoutUseCase, +) : MoleculeViewModel( + SetupInvoicePayoutUiState(false, null, false), + SetupInvoicePayoutPresenter(setupInvoicePayoutUseCase), + ) + +internal sealed interface SetupInvoicePayoutEvent { + data object Connect : SetupInvoicePayoutEvent + + data object ShowedSnackBar : SetupInvoicePayoutEvent +} + +internal data class SetupInvoicePayoutUiState( + val isLoading: Boolean, + val errorMessage: String?, + val showSuccessSnackBar: Boolean, +) + +internal class SetupInvoicePayoutPresenter( + private val setupInvoicePayoutUseCase: SetupInvoicePayoutUseCase, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present( + lastState: SetupInvoicePayoutUiState, + ): SetupInvoicePayoutUiState { + var isLoading by remember { mutableStateOf(false) } + var errorMessage by remember { mutableStateOf(null) } + var showSuccessSnackBar by remember { mutableStateOf(false) } + var connectIteration by remember { mutableStateOf(0) } + var shouldConnect by remember { mutableStateOf(false) } + + if (shouldConnect) { + LaunchedEffect(connectIteration) { + isLoading = true + errorMessage = null + setupInvoicePayoutUseCase.invoke().fold( + ifLeft = { + isLoading = false + errorMessage = it.message ?: "Something went wrong, please try again" + shouldConnect = false + }, + ifRight = { + isLoading = false + showSuccessSnackBar = true + shouldConnect = false + }, + ) + } + } + + CollectEvents { event -> + when (event) { + SetupInvoicePayoutEvent.Connect -> { + if (!isLoading) { + shouldConnect = true + connectIteration++ + } + } + + SetupInvoicePayoutEvent.ShowedSnackBar -> { + showSuccessSnackBar = false + } + } + } + + return SetupInvoicePayoutUiState( + isLoading = isLoading, + errorMessage = errorMessage, + showSuccessSnackBar = showSuccessSnackBar, + ) + } +} From 10f132a93a0e5cc02ba65e8d26df53375e72d88e Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 12:35:48 +0200 Subject: [PATCH 14/50] Make feature-payments an android module again This one does not at the moment need to be MP --- app/feature/feature-payments/build.gradle.kts | 70 ++++++++----------- .../src/main/AndroidManifest.xml | 2 + .../graphql/QueryDiscounts.graphql | 0 .../graphql/QueryMemberPaymentDetails.graphql | 0 .../graphql/QueryPaymentsHistory.graphql | 0 .../graphql/QueryReferrals.graphql | 0 .../graphql/QueryShortPaymentsHistory.graphql | 0 .../QueryShouldShowPayoutButton.graphql | 0 .../graphql/QueryUpcomingPayment.graphql | 0 .../android/feature/payments/PreviewData.kt | 0 .../android/feature/payments/data/Discount.kt | 0 .../payments/data/GetChargeDetailsUseCase.kt | 0 .../data/GetDiscountsOverviewUseCase.kt | 0 .../payments/data/GetDiscountsUseCase.kt | 0 .../data/GetMemberPaymentsDetailsUseCase.kt | 0 .../data/GetPaymentsHistoryUseCase.kt | 0 .../feature/payments/data/MemberCharge.kt | 0 .../payments/data/PaymentConnection.kt | 0 .../feature/payments/data/PaymentOverview.kt | 0 .../feature/payments/di/PaymentsModule.kt | 0 .../navigation/PaymentsDestination.kt | 0 .../payments/navigation/PaymentsGraph.kt | 0 .../data/GetForeverInformationUseCase.kt | 0 .../data/GetShouldShowPayoutUseCase.kt | 1 + .../GetShouldShowPayoutUseCaseProvider.kt | 0 .../data/GetUpcomingPaymentUseCase.kt | 0 .../data/GetUpcomingPaymentUseCaseProvider.kt | 0 .../ui/details/PaymentDetailExpandableCard.kt | 0 .../ui/details/PaymentDetailsDestination.kt | 0 .../ui/details/PaymentDetailsViewModel.kt | 0 .../AddDiscountBottomSheetContent.kt | 0 .../payments/ui/discounts/DiscountRow.kt | 0 .../ui/discounts/DiscountsDestination.kt | 0 .../ui/discounts/DiscountsPresenter.kt | 0 .../ui/discounts/DiscountsViewModel.kt | 0 .../ui/history/PaymentHistoryDestination.kt | 0 .../ui/history/PaymentHistoryViewModel.kt | 0 .../MemberPaymentDetailsDestination.kt | 0 .../MemberPaymentDetailsViewModel.kt | 0 .../ui/payments/PaymentsDestination.kt | 2 +- .../payments/ui/payments/PaymentsPresenter.kt | 0 .../payments/ui/payments/PaymentsViewModel.kt | 0 .../data/FakePayoutAccountStorage.kt | 2 +- 43 files changed, 36 insertions(+), 41 deletions(-) create mode 100644 app/feature/feature-payments/src/main/AndroidManifest.xml rename app/feature/feature-payments/src/{commonMain => main}/graphql/QueryDiscounts.graphql (100%) rename app/feature/feature-payments/src/{commonMain => main}/graphql/QueryMemberPaymentDetails.graphql (100%) rename app/feature/feature-payments/src/{commonMain => main}/graphql/QueryPaymentsHistory.graphql (100%) rename app/feature/feature-payments/src/{commonMain => main}/graphql/QueryReferrals.graphql (100%) rename app/feature/feature-payments/src/{commonMain => main}/graphql/QueryShortPaymentsHistory.graphql (100%) rename app/feature/feature-payments/src/{commonMain => main}/graphql/QueryShouldShowPayoutButton.graphql (100%) rename app/feature/feature-payments/src/{commonMain => main}/graphql/QueryUpcomingPayment.graphql (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/PreviewData.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/Discount.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt (97%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt (99%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt (100%) rename app/feature/feature-payments/src/{commonMain => main}/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt (100%) diff --git a/app/feature/feature-payments/build.gradle.kts b/app/feature/feature-payments/build.gradle.kts index cd1416f07a..0a249e7b59 100644 --- a/app/feature/feature-payments/build.gradle.kts +++ b/app/feature/feature-payments/build.gradle.kts @@ -1,6 +1,5 @@ plugins { - id("hedvig.multiplatform.library") - id("hedvig.multiplatform.library.android") + id("hedvig.android.library") id("hedvig.gradle.plugin") } @@ -10,41 +9,34 @@ hedvig { compose() } -kotlin { - sourceSets { - commonMain.dependencies { - implementation(libs.apollo.normalizedCache) - implementation(libs.apollo.runtime) - implementation(libs.arrow.core) - implementation(libs.arrow.fx) - implementation(libs.jetbrains.compose.foundation) - implementation(libs.jetbrains.compose.runtime) - implementation(libs.jetbrains.lifecycle.runtime.compose) - implementation(libs.jetbrains.navigation.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.coreBuildConstants) - implementation(projects.coreCommonPublic) - implementation(projects.coreDemoMode) - implementation(projects.coreResources) - implementation(projects.coreUiData) - implementation(projects.dataPayingMember) - implementation(projects.designSystemHedvig) - implementation(projects.foreverUi) - implementation(projects.languageCore) - implementation(projects.moleculePublic) - implementation(projects.navigationCommon) - implementation(projects.navigationCompose) - implementation(projects.navigationCore) - implementation(projects.pullrefresh) - implementation(projects.theme) - } - androidMain.dependencies { - implementation(libs.bundles.kmpPreviewBugWorkaround) - } - } +dependencies { + implementation(libs.apollo.normalizedCache) + implementation(libs.apollo.runtime) + implementation(libs.arrow.core) + implementation(libs.arrow.fx) + implementation(libs.jetbrains.compose.foundation) + implementation(libs.jetbrains.compose.runtime) + implementation(libs.jetbrains.lifecycle.runtime.compose) + implementation(libs.jetbrains.navigation.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.coreBuildConstants) + implementation(projects.coreCommonPublic) + implementation(projects.coreDemoMode) + implementation(projects.coreResources) + implementation(projects.coreUiData) + implementation(projects.dataPayingMember) + implementation(projects.designSystemHedvig) + implementation(projects.foreverUi) + implementation(projects.languageCore) + implementation(projects.moleculePublic) + implementation(projects.navigationCommon) + implementation(projects.navigationCompose) + implementation(projects.navigationCore) + implementation(projects.pullrefresh) + implementation(projects.theme) } diff --git a/app/feature/feature-payments/src/main/AndroidManifest.xml b/app/feature/feature-payments/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..568741e54f --- /dev/null +++ b/app/feature/feature-payments/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryDiscounts.graphql b/app/feature/feature-payments/src/main/graphql/QueryDiscounts.graphql similarity index 100% rename from app/feature/feature-payments/src/commonMain/graphql/QueryDiscounts.graphql rename to app/feature/feature-payments/src/main/graphql/QueryDiscounts.graphql diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryMemberPaymentDetails.graphql b/app/feature/feature-payments/src/main/graphql/QueryMemberPaymentDetails.graphql similarity index 100% rename from app/feature/feature-payments/src/commonMain/graphql/QueryMemberPaymentDetails.graphql rename to app/feature/feature-payments/src/main/graphql/QueryMemberPaymentDetails.graphql diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryPaymentsHistory.graphql b/app/feature/feature-payments/src/main/graphql/QueryPaymentsHistory.graphql similarity index 100% rename from app/feature/feature-payments/src/commonMain/graphql/QueryPaymentsHistory.graphql rename to app/feature/feature-payments/src/main/graphql/QueryPaymentsHistory.graphql diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryReferrals.graphql b/app/feature/feature-payments/src/main/graphql/QueryReferrals.graphql similarity index 100% rename from app/feature/feature-payments/src/commonMain/graphql/QueryReferrals.graphql rename to app/feature/feature-payments/src/main/graphql/QueryReferrals.graphql diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryShortPaymentsHistory.graphql b/app/feature/feature-payments/src/main/graphql/QueryShortPaymentsHistory.graphql similarity index 100% rename from app/feature/feature-payments/src/commonMain/graphql/QueryShortPaymentsHistory.graphql rename to app/feature/feature-payments/src/main/graphql/QueryShortPaymentsHistory.graphql diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql b/app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql similarity index 100% rename from app/feature/feature-payments/src/commonMain/graphql/QueryShouldShowPayoutButton.graphql rename to app/feature/feature-payments/src/main/graphql/QueryShouldShowPayoutButton.graphql diff --git a/app/feature/feature-payments/src/commonMain/graphql/QueryUpcomingPayment.graphql b/app/feature/feature-payments/src/main/graphql/QueryUpcomingPayment.graphql similarity index 100% rename from app/feature/feature-payments/src/commonMain/graphql/QueryUpcomingPayment.graphql rename to app/feature/feature-payments/src/main/graphql/QueryUpcomingPayment.graphql diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/PreviewData.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/PreviewData.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/PreviewData.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/PreviewData.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/Discount.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/Discount.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/Discount.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/Discount.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetChargeDetailsUseCase.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsOverviewUseCase.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetDiscountsUseCase.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetMemberPaymentsDetailsUseCase.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/GetPaymentsHistoryUseCase.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/MemberCharge.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentConnection.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/data/PaymentOverview.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/di/PaymentsModule.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsDestination.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/navigation/PaymentsGraph.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetForeverInformationUseCase.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt similarity index 97% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt index 111a706c50..481bf68f30 100644 --- a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCase.kt @@ -23,6 +23,7 @@ internal class GetShouldShowPayoutUseCaseImpl( private val apolloClient: ApolloClient, ) : GetShouldShowPayoutUseCase { override suspend fun invoke(): Either = either { + return@either true // todo testing delete val result = apolloClient .query(ShouldShowPayoutButtonQuery()) .fetchPolicy(FetchPolicy.NetworkFirst) diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetShouldShowPayoutUseCaseProvider.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCase.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/overview/data/GetUpcomingPaymentUseCaseProvider.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailExpandableCard.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsDestination.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/details/PaymentDetailsViewModel.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/AddDiscountBottomSheetContent.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountRow.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsDestination.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsPresenter.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/discounts/DiscountsViewModel.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryDestination.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/history/PaymentHistoryViewModel.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt similarity index 99% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt index 63ad6b0704..99994730ce 100644 --- a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt @@ -640,7 +640,7 @@ private class PaymentsStatePreviewProvider : CollectionPreviewParameterProvider< "Card", "****1234", ), - showPayoutButton = false, + showPayoutButton = true, ), ) add( diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsPresenter.kt diff --git a/app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt similarity index 100% rename from app/feature/feature-payments/src/commonMain/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt rename to app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsViewModel.kt diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt index 2dac3d2999..00efb60308 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt @@ -9,5 +9,5 @@ import octopus.type.PaymentMethodInvoiceDelivery // PayoutAccount.BankAccount("8327", "12345678", "Swedbank") → Bank account display // PayoutAccount.Invoice(PaymentMethodInvoiceDelivery.KIVRA, null) → Invoice display internal object FakePayoutAccountStorage { - var currentMethod: PayoutAccount? = null + var currentMethod: PayoutAccount? = PayoutAccount.Trustly } From 633d811090a2fca1b4ec17cc1d91b62d017a5502 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 17:18:06 +0200 Subject: [PATCH 15/50] Download strings --- .../androidMain/res/values-sv-rSE/strings.xml | 18 +++++++++++++++++- .../src/androidMain/res/values/strings.xml | 18 +++++++++++++++++- .../composeResources/values-sv-rSE/strings.xml | 18 +++++++++++++++++- .../composeResources/values/strings.xml | 18 +++++++++++++++++- 4 files changed, 68 insertions(+), 4 deletions(-) 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 f58b1218a2..673f80885c 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 @@ -78,6 +78,9 @@ Vi använder AI för att du ska få så snabb hjälp som möjligt. Svaren genereras automatiskt och kan ibland innehålla felaktigheter.\n\nVid behov kopplas du till vårt serviceteam som hjälper dig.\n\nVad som gäller för din försäkring anges i ditt försäkringsbrev och i dina villkor. AI och automatiska meddelanden Skanna QR-koden med BankID-appen på din telefon, eller logga in med din e-postadress nedan. + Utbetalning till ett svenskt bankkonto + Bankkonto + Clearing Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Tack för ditt meddelande. Vi svarar så snart som möjligt. Just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -143,6 +146,7 @@ Ange byggår Byggår Du + %1$d + Ändra konto Ändra skyddsnivå Chatta med en specialist Om det inte fungerar eller om du har några andra frågor, vänligen meddela oss så hjälper en av våra specialister till inom kort! @@ -508,8 +512,12 @@ Invalid National Identity Number Vi försöker reparera i första hand, men om din %1$s skulle behöva ersättas helt (ex. om den blivit stulen) ersätts du med **%2$d\u0025** av inköpspriset **%3$d kr**, alltså **%4$d kr**. Värdering - Du kan inte ändra betalmetod just nu. Kontakta oss för hjälp. + Vill du betala din försäkring via autogiro och möjliggöra snabba utbetalningar vid skador? Fakturan skickas till din Kivra-inkorg 14 dagar före förfallodatumet varje månad. + Tillgänglighet + Juridisk information + Legal information + Personuppgifter Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut @@ -613,6 +621,13 @@ Kommande betalning Giltig till %1$s Betalningshistorik + Lägg till utbetalningsmetod + Direktutbetalning via Faktura + Direktutbetalning via Swish + Direktutbetalning via Trustly + Det verkar som att du saknar utbetalningsmetod. Lägg till den så att vi kan betala ut till dig. + Utbetalningskonto + Välj utbetalningsmetod Vi behöver tillåtelse att spela in ljud Behöver tillåtelse Telefonnummer @@ -630,6 +645,7 @@ Vi kan inte radera ditt konto just nu För att vi ska kunna radera ditt konto måste alla öppna skadeärenden vara stängda och inte finnas några aktiva försäkringar. Var vänlig skriv till oss i appen. Hedvig Forever + Information Är du säker på att du vill logga ut? Epost Inkorrekt epostadress 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 f2db8a113b..9d06df2d5a 100644 --- a/app/core/core-resources/src/androidMain/res/values/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values/strings.xml @@ -78,6 +78,9 @@ We use AI to help you as quickly as possible. The answers are generated automatically and may not always be correct.\n\nIf necessary, you’ll be connected to our service team to help you further.\n\nThe details of your insurance are outlined in your insurance letter and terms and conditions. AI and automated messages Scan the QR-code with the BankID app on the phone where it’s installed or log in with email using the button below. + Payout via a Swedish bank account + Bank account + Clearing Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Thank you for reaching out to us. We will answer as soon as possible. Hej - just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -143,6 +146,7 @@ Please enter year of construction Year of construction You + %1$d + Change account Change coverage level Talk to a human If it’s not working or if you have any other questions, please let us know and one of our specialists will help out shortly! @@ -508,8 +512,12 @@ Invalid National Identity Number We first try to repair your %1$s, but if it needs to be replaced (e.g. if it was stolen) you will be compensated **%2$d\u0025** of the purchase price **%3$d SEK**, i.e **%4$d SEK**. Valuation - You can’t change the payment method right now. Contact us for help. + Do you want to pay your insurance via direct debit and enable quick payouts in the event of claims? The invoice is sent to your Kivra inbox 14 days before the due date each month. + Accessibility + Legal + Legal information + Privacy policy Question regarding claim, Vehicle reg. %1$s Preferences Logout @@ -613,6 +621,13 @@ Upcoming payment Valid until %1$s Payment history + Add payout method + Direct payment via Invoice + Direct payment via Swish + Direct payment via Trustly + It looks like you are missing payout method. Add it so we can payout to you + Payout account + Choose payout method We need permission to record audio Needs permission Phone @@ -630,6 +645,7 @@ We can\'t delete your account right now In order to delete your account you need to have your open claims settled and not have any active insurances. Please reach out to our service team via message in the app. Hedvig Forever + Information Are you sure you want to log out? Email Invalid email 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 35d531ddea..1bb3ed2131 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 @@ -78,6 +78,9 @@ Vi använder AI för att du ska få så snabb hjälp som möjligt. Svaren genereras automatiskt och kan ibland innehålla felaktigheter.\n\nVid behov kopplas du till vårt serviceteam som hjälper dig.\n\nVad som gäller för din försäkring anges i ditt försäkringsbrev och i dina villkor. AI och automatiska meddelanden Skanna QR-koden med BankID-appen på din telefon, eller logga in med din e-postadress nedan. + Utbetalning till ett svenskt bankkonto + Bankkonto + Clearing Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Tack för ditt meddelande. Vi svarar så snart som möjligt. Just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -143,6 +146,7 @@ Ange byggår Byggår Du + %1$d + Ändra konto Ändra skyddsnivå Chatta med en specialist Om det inte fungerar eller om du har några andra frågor, vänligen meddela oss så hjälper en av våra specialister till inom kort! @@ -508,8 +512,12 @@ Invalid National Identity Number Vi försöker reparera i första hand, men om din %1$s skulle behöva ersättas helt (ex. om den blivit stulen) ersätts du med **%2$d\u0025** av inköpspriset **%3$d kr**, alltså **%4$d kr**. Värdering - Du kan inte ändra betalmetod just nu. Kontakta oss för hjälp. + Vill du betala din försäkring via autogiro och möjliggöra snabba utbetalningar vid skador? Fakturan skickas till din Kivra-inkorg 14 dagar före förfallodatumet varje månad. + Tillgänglighet + Juridisk information + Legal information + Personuppgifter Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut @@ -613,6 +621,13 @@ Kommande betalning Giltig till %1$s Betalningshistorik + Lägg till utbetalningsmetod + Direktutbetalning via Faktura + Direktutbetalning via Swish + Direktutbetalning via Trustly + Det verkar som att du saknar utbetalningsmetod. Lägg till den så att vi kan betala ut till dig. + Utbetalningskonto + Välj utbetalningsmetod Vi behöver tillåtelse att spela in ljud Behöver tillåtelse Telefonnummer @@ -630,6 +645,7 @@ Vi kan inte radera ditt konto just nu För att vi ska kunna radera ditt konto måste alla öppna skadeärenden vara stängda och inte finnas några aktiva försäkringar. Var vänlig skriv till oss i appen. Hedvig Forever + Information Är du säker på att du vill logga ut? Epost Inkorrekt epostadress 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 7edb7fd368..496464cdb6 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml @@ -78,6 +78,9 @@ We use AI to help you as quickly as possible. The answers are generated automatically and may not always be correct.\n\nIf necessary, you’ll be connected to our service team to help you further.\n\nThe details of your insurance are outlined in your insurance letter and terms and conditions. AI and automated messages Scan the QR-code with the BankID app on the phone where it’s installed or log in with email using the button below. + Payout via a Swedish bank account + Bank account + Clearing Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Thank you for reaching out to us. We will answer as soon as possible. Hej - just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -143,6 +146,7 @@ Please enter year of construction Year of construction You + %1$d + Change account Change coverage level Talk to a human If it’s not working or if you have any other questions, please let us know and one of our specialists will help out shortly! @@ -508,8 +512,12 @@ Invalid National Identity Number We first try to repair your %1$s, but if it needs to be replaced (e.g. if it was stolen) you will be compensated **%2$d\u0025** of the purchase price **%3$d SEK**, i.e **%4$d SEK**. Valuation - You can’t change the payment method right now. Contact us for help. + Do you want to pay your insurance via direct debit and enable quick payouts in the event of claims? The invoice is sent to your Kivra inbox 14 days before the due date each month. + Accessibility + Legal + Legal information + Privacy policy Question regarding claim, Vehicle reg. %1$s Preferences Logout @@ -613,6 +621,13 @@ Upcoming payment Valid until %1$s Payment history + Add payout method + Direct payment via Invoice + Direct payment via Swish + Direct payment via Trustly + It looks like you are missing payout method. Add it so we can payout to you + Payout account + Choose payout method We need permission to record audio Needs permission Phone @@ -630,6 +645,7 @@ We can't delete your account right now In order to delete your account you need to have your open claims settled and not have any active insurances. Please reach out to our service team via message in the app. Hedvig Forever + Information Are you sure you want to log out? Email Invalid email From 751a168e73b893493fd7156fa9f292073d9b6c59 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 17:18:32 +0200 Subject: [PATCH 16/50] Use some new strings --- .../EditBankAccountDestination.kt | 8 +++-- .../SelectPayoutMethodDestination.kt | 33 +++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index 0814528d43..d1bb1357f4 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -18,6 +18,10 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.design.system.hedvig.GlobalSnackBarState +import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE +import hedvig.resources.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL +import hedvig.resources.Res +import org.jetbrains.compose.resources.stringResource import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigScaffold @@ -60,7 +64,7 @@ private fun EditBankAccountScreen( } HedvigScaffold( - topAppBarText = "Bank account", + topAppBarText = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_TITLE), navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { @@ -68,7 +72,7 @@ private fun EditBankAccountScreen( Column(Modifier.padding(horizontal = 16.dp)) { HedvigTextField( state = uiState.clearingNumberState, - labelText = "Clearing", + labelText = stringResource(Res.string.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL), textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, inputTransformation = uiState.clearingInputTransformation, keyboardOptions = KeyboardOptions( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt index 36344178bf..6a2d45a71f 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -1,6 +1,7 @@ package com.hedvig.android.feature.payoutaccount.ui.selectmethod import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -13,7 +14,17 @@ import androidx.compose.ui.unit.dp import com.hedvig.android.design.system.hedvig.HedvigCard import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigText +import com.hedvig.android.design.system.hedvig.HedvigTheme +import hedvig.resources.BANK_PAYOUT_METHOD_CARD_DESCRIPTION +import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE +import hedvig.resources.PAYMENTS_INVOICE +import hedvig.resources.PAYOUT_METHOD_INVOICE_DESCRIPTION +import hedvig.resources.PAYOUT_METHOD_SWISH_DESCRIPTION +import hedvig.resources.PAYOUT_METHOD_TRUSTLY_DESCRIPTION +import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD +import hedvig.resources.Res import octopus.type.MemberPaymentProvider +import org.jetbrains.compose.resources.stringResource @Composable internal fun SelectPayoutMethodDestination( @@ -25,48 +36,44 @@ internal fun SelectPayoutMethodDestination( navigateUp: () -> Unit, ) { HedvigScaffold( - topAppBarText = "Connect payout account", + topAppBarText = stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD), navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { Spacer(Modifier.height(8.dp)) - Column(Modifier.padding(horizontal = 16.dp)) { + Column(Modifier.padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { for (provider in availableProviders) { when (provider) { MemberPaymentProvider.TRUSTLY -> { PayoutMethodRow( title = "Trustly", - subtitle = "Connect via Trustly", + subtitle = stringResource(Res.string.PAYOUT_METHOD_TRUSTLY_DESCRIPTION), onClick = onTrustlySelected, ) - Spacer(Modifier.height(8.dp)) } MemberPaymentProvider.NORDEA -> { PayoutMethodRow( - title = "Bank account", - subtitle = "Enter clearing and account number", + title = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_TITLE), + subtitle = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_DESCRIPTION), onClick = onNordeaSelected, ) - Spacer(Modifier.height(8.dp)) } MemberPaymentProvider.SWISH -> { PayoutMethodRow( title = "Swish", - subtitle = "Connect via Swish", + subtitle = stringResource(Res.string.PAYOUT_METHOD_SWISH_DESCRIPTION), onClick = onSwishSelected, ) - Spacer(Modifier.height(8.dp)) } MemberPaymentProvider.INVOICE -> { PayoutMethodRow( - title = "Invoice", - subtitle = "Connect via Kivra", + title = stringResource(Res.string.PAYMENTS_INVOICE), + subtitle = stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION), onClick = onInvoiceSelected, ) - Spacer(Modifier.height(8.dp)) } else -> {} @@ -87,7 +94,7 @@ private fun PayoutMethodRow(title: String, subtitle: String, onClick: () -> Unit HedvigText(text = title) HedvigText( text = subtitle, - color = com.hedvig.android.design.system.hedvig.HedvigTheme.colorScheme.textSecondary, + color = HedvigTheme.colorScheme.textSecondary, ) } } From a8c7c314db737f0f709653ee1d05bc9bab4f353d Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 17:18:44 +0200 Subject: [PATCH 17/50] Use some new strings and small UI fixes --- .../PayoutAccountOverviewDestination.kt | 102 ++++++++++++++++-- 1 file changed, 91 insertions(+), 11 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 0e447fa510..51d2445eb6 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -2,6 +2,7 @@ package com.hedvig.android.feature.payoutaccount.ui.overview import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -9,19 +10,32 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.design.system.hedvig.ButtonDefaults import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigErrorSection import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProgressDebounced +import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults +import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.Surface import com.hedvig.android.feature.payoutaccount.data.PayoutAccount import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState.Content +import hedvig.resources.CHANGE_PAYOUT_METHOD_BUTTON_LABEL +import hedvig.resources.PAYMENTS_ACCOUNT +import hedvig.resources.PAYOUT_PAGE_HEADING +import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD +import hedvig.resources.Res import octopus.type.MemberPaymentProvider +import octopus.type.PaymentMethodInvoiceDelivery +import org.jetbrains.compose.resources.stringResource @Composable internal fun PayoutAccountOverviewDestination( @@ -49,7 +63,7 @@ private fun PayoutAccountOverviewScreen( navigateUp: () -> Unit, ) { HedvigScaffold( - topAppBarText = "Payout account", + topAppBarText = stringResource(Res.string.PAYOUT_PAGE_HEADING), navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { @@ -77,6 +91,7 @@ private fun PayoutAccountOverviewScreen( availablePayoutMethods = uiState.availablePayoutMethods, onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, onEditBankAccountClicked = onEditBankAccountClicked, + modifier = Modifier.weight(1f), ) } } @@ -89,13 +104,15 @@ private fun PayoutAccountContent( availablePayoutMethods: List, onConnectPayoutMethodClicked: () -> Unit, onEditBankAccountClicked: () -> Unit, + modifier: Modifier = Modifier, ) { - Column { + Column(modifier) { Spacer(Modifier.height(8.dp)) when (currentMethod) { null -> { + Spacer(Modifier.weight(1f)) HedvigButton( - text = "Connect payout account", + text = stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD), onClick = onConnectPayoutMethodClicked, enabled = true, modifier = Modifier @@ -109,7 +126,7 @@ private fun PayoutAccountContent( text = currentMethod.phoneNumber, onValueChange = {}, labelText = "Swish", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, readOnly = true, modifier = Modifier .fillMaxWidth() @@ -118,7 +135,7 @@ private fun PayoutAccountContent( if (availablePayoutMethods.size > 1) { Spacer(Modifier.height(8.dp)) HedvigButton( - text = "Change account", + text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), onClick = onConnectPayoutMethodClicked, enabled = true, buttonStyle = ButtonDefaults.ButtonStyle.Secondary, @@ -134,7 +151,7 @@ private fun PayoutAccountContent( text = "Trustly", onValueChange = {}, labelText = "Account", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, readOnly = true, modifier = Modifier .fillMaxWidth() @@ -143,7 +160,7 @@ private fun PayoutAccountContent( if (availablePayoutMethods.size > 1) { Spacer(Modifier.height(8.dp)) HedvigButton( - text = "Change account", + text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), onClick = onConnectPayoutMethodClicked, enabled = true, buttonStyle = ButtonDefaults.ButtonStyle.Secondary, @@ -159,7 +176,7 @@ private fun PayoutAccountContent( text = "Invoice", onValueChange = {}, labelText = "Account", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, readOnly = true, modifier = Modifier .fillMaxWidth() @@ -168,7 +185,7 @@ private fun PayoutAccountContent( if (availablePayoutMethods.size > 1) { Spacer(Modifier.height(8.dp)) HedvigButton( - text = "Change account", + text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), onClick = onConnectPayoutMethodClicked, enabled = true, buttonStyle = ButtonDefaults.ButtonStyle.Secondary, @@ -192,8 +209,8 @@ private fun PayoutAccountContent( HedvigTextField( text = displayText, onValueChange = {}, - labelText = "Account", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + labelText = stringResource(Res.string.PAYMENTS_ACCOUNT), + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, readOnly = true, modifier = Modifier .fillMaxWidth() @@ -213,3 +230,66 @@ private fun PayoutAccountContent( Spacer(Modifier.height(16.dp)) } } + +@Composable +@HedvigPreview +private fun PreviewPayoutAccountOverviewScreen( + @PreviewParameter(PayoutAccountOverviewUiStateProvider::class) uiState: PayoutAccountOverviewUiState, +) { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + PayoutAccountOverviewScreen( + uiState = uiState, + onConnectPayoutMethodClicked = {}, + onEditBankAccountClicked = {}, + onRetry = {}, + navigateUp = {}, + ) + } + } +} + +private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterProvider( + listOf( + PayoutAccountOverviewUiState.Loading, + PayoutAccountOverviewUiState.Error, + Content( + currentMethod = null, + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67"), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67"), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + ), + Content( + currentMethod = PayoutAccount.Trustly, + availablePayoutMethods = listOf(MemberPaymentProvider.TRUSTLY), + ), + Content( + currentMethod = PayoutAccount.BankAccount( + clearingNumber = "3300", + accountNumber = "1234567", + bankName = "Nordea", + ), + availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), + ), + Content( + currentMethod = PayoutAccount.Invoice( + delivery = PaymentMethodInvoiceDelivery.KIVRA, + email = null, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE), + ), + Content( + currentMethod = PayoutAccount.Invoice( + delivery = PaymentMethodInvoiceDelivery.MAIL, + email = "user@example.com", + ), + availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE, MemberPaymentProvider.TRUSTLY), + ), + ), +) From 637028c9833c366c6e5e2f71f64f26cfeb7c7a12 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 17:19:03 +0200 Subject: [PATCH 18/50] Pop payout choice screen before going to connect trustly --- .../hedvig/android/app/navigation/HedvigNavHost.kt | 4 +++- .../payoutaccount/navigation/PayoutAccountGraph.kt | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 757ffe25fc..0a4957b13c 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -350,7 +350,9 @@ internal fun HedvigNavHost( payoutAccountGraph( navController = navController, globalSnackBarState = globalSnackBarState, - navigateToTrustlyPayout = { navController.navigate(TrustlyPayoutDestination) }, + navigateToTrustlyPayout = { builder -> + navController.navigate(TrustlyPayoutDestination, builder) + }, navigateUp = navController::navigateUp, ) profileGraph( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index 9c8546d4e7..bb7af440a1 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -3,6 +3,7 @@ package com.hedvig.android.feature.payoutaccount.navigation import androidx.lifecycle.compose.dropUnlessResumed import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptionsBuilder import com.hedvig.android.design.system.hedvig.GlobalSnackBarState import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountDestination import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel @@ -16,13 +17,14 @@ import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutDe import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.compose.navgraph +import com.hedvig.android.navigation.compose.typedPopUpTo import octopus.type.MemberPaymentProvider import org.koin.compose.viewmodel.koinViewModel fun NavGraphBuilder.payoutAccountGraph( navController: NavController, globalSnackBarState: GlobalSnackBarState, - navigateToTrustlyPayout: () -> Unit, + navigateToTrustlyPayout: (builder: NavOptionsBuilder.() -> Unit) -> Unit, navigateUp: () -> Unit, ) { navgraph( @@ -50,7 +52,13 @@ fun NavGraphBuilder.payoutAccountGraph( navdestination { SelectPayoutMethodDestination( availableProviders = this.availableProviders.map { MemberPaymentProvider.safeValueOf(it) }, - onTrustlySelected = dropUnlessResumed { navigateToTrustlyPayout() }, + onTrustlySelected = dropUnlessResumed { + navigateToTrustlyPayout { + typedPopUpTo { + inclusive = true + } + } + }, onNordeaSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.EditBankAccount) }, onSwishSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.SetupSwishPayout) }, onInvoiceSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.SetupInvoicePayout) }, From 35695209c3e70fe30a6ae6daff3992cedeb0c28b Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 21 Apr 2026 17:50:01 +0200 Subject: [PATCH 19/50] Pop payout choice screen before finishing a successful connection --- .../payoutaccount/navigation/PayoutAccountGraph.kt | 13 ++++++++++--- .../editbankaccount/EditBankAccountDestination.kt | 10 ++++++---- .../ui/overview/PayoutAccountOverviewDestination.kt | 11 +++-------- .../setupinvoice/SetupInvoicePayoutDestination.kt | 12 +++++++++--- .../ui/setupswish/SetupSwishPayoutDestination.kt | 5 +++-- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index bb7af440a1..331f95c077 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -17,6 +17,7 @@ import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutDe import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.compose.navgraph +import com.hedvig.android.navigation.compose.typedPopBackStack import com.hedvig.android.navigation.compose.typedPopUpTo import octopus.type.MemberPaymentProvider import org.koin.compose.viewmodel.koinViewModel @@ -42,9 +43,6 @@ fun NavGraphBuilder.payoutAccountGraph( ), ) }, - onEditBankAccountClicked = dropUnlessResumed { - navController.navigate(PayoutAccountDestinations.EditBankAccount) - }, navigateUp = navigateUp, ) } @@ -71,6 +69,9 @@ fun NavGraphBuilder.payoutAccountGraph( EditBankAccountDestination( viewModel = viewModel, globalSnackBarState = globalSnackBarState, + onSuccessfullyConnected = { + navController.typedPopBackStack(inclusive = true) + }, navigateUp = navController::navigateUp, ) } @@ -80,6 +81,9 @@ fun NavGraphBuilder.payoutAccountGraph( SetupSwishPayoutDestination( viewModel = viewModel, globalSnackBarState = globalSnackBarState, + onSuccessfullyConnected = { + navController.typedPopBackStack(inclusive = true) + }, navigateUp = navController::navigateUp, ) } @@ -89,6 +93,9 @@ fun NavGraphBuilder.payoutAccountGraph( SetupInvoicePayoutDestination( viewModel = viewModel, globalSnackBarState = globalSnackBarState, + onSuccessfullyConnected = { + navController.typedPopBackStack(inclusive = true) + }, navigateUp = navController::navigateUp, ) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index d1bb1357f4..db950d5327 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -29,11 +29,13 @@ import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import hedvig.resources.PAYMENTS_ACCOUNT @Composable internal fun EditBankAccountDestination( viewModel: EditBankAccountViewModel, globalSnackBarState: GlobalSnackBarState, + onSuccessfullyConnected: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -43,7 +45,7 @@ internal fun EditBankAccountDestination( onSave = { viewModel.emit(EditBankAccountEvent.Save) }, showedSnackBar = { viewModel.emit(EditBankAccountEvent.ShowedSnackBar) - navigateUp() + onSuccessfullyConnected() }, navigateUp = navigateUp, ) @@ -73,7 +75,7 @@ private fun EditBankAccountScreen( HedvigTextField( state = uiState.clearingNumberState, labelText = stringResource(Res.string.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL), - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, inputTransformation = uiState.clearingInputTransformation, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -86,8 +88,8 @@ private fun EditBankAccountScreen( Spacer(Modifier.height(4.dp)) HedvigTextField( state = uiState.accountNumberState, - labelText = "Account number", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + labelText = stringResource(Res.string.PAYMENTS_ACCOUNT), + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, inputTransformation = uiState.accountNumberInputTransformation, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 51d2445eb6..1ef6001aa8 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -41,14 +41,12 @@ import org.jetbrains.compose.resources.stringResource internal fun PayoutAccountOverviewDestination( viewModel: PayoutAccountOverviewViewModel, onConnectPayoutMethodClicked: () -> Unit, - onEditBankAccountClicked: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() PayoutAccountOverviewScreen( uiState = uiState, onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, - onEditBankAccountClicked = onEditBankAccountClicked, onRetry = { viewModel.emit(PayoutAccountOverviewEvent.Retry) }, navigateUp = navigateUp, ) @@ -58,7 +56,6 @@ internal fun PayoutAccountOverviewDestination( private fun PayoutAccountOverviewScreen( uiState: PayoutAccountOverviewUiState, onConnectPayoutMethodClicked: () -> Unit, - onEditBankAccountClicked: () -> Unit, onRetry: () -> Unit, navigateUp: () -> Unit, ) { @@ -90,7 +87,6 @@ private fun PayoutAccountOverviewScreen( currentMethod = uiState.currentMethod, availablePayoutMethods = uiState.availablePayoutMethods, onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, - onEditBankAccountClicked = onEditBankAccountClicked, modifier = Modifier.weight(1f), ) } @@ -103,7 +99,6 @@ private fun PayoutAccountContent( currentMethod: PayoutAccount?, availablePayoutMethods: List, onConnectPayoutMethodClicked: () -> Unit, - onEditBankAccountClicked: () -> Unit, modifier: Modifier = Modifier, ) { Column(modifier) { @@ -216,10 +211,11 @@ private fun PayoutAccountContent( .fillMaxWidth() .padding(horizontal = 16.dp), ) + Spacer(Modifier.weight(1f)) Spacer(Modifier.height(8.dp)) HedvigButton( - text = "Edit account", - onClick = onEditBankAccountClicked, + text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), + onClick = onConnectPayoutMethodClicked, enabled = true, modifier = Modifier .fillMaxWidth() @@ -241,7 +237,6 @@ private fun PreviewPayoutAccountOverviewScreen( PayoutAccountOverviewScreen( uiState = uiState, onConnectPayoutMethodClicked = {}, - onEditBankAccountClicked = {}, onRetry = {}, navigateUp = {}, ) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt index cfcf698639..b73f09e543 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt @@ -19,11 +19,16 @@ import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import hedvig.resources.CONTACT_INFO_CHANGES_SAVED +import hedvig.resources.PAYMENTS_INVOICE +import hedvig.resources.Res +import org.jetbrains.compose.resources.stringResource @Composable internal fun SetupInvoicePayoutDestination( viewModel: SetupInvoicePayoutViewModel, globalSnackBarState: GlobalSnackBarState, + onSuccessfullyConnected: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -33,7 +38,7 @@ internal fun SetupInvoicePayoutDestination( onConnect = { viewModel.emit(SetupInvoicePayoutEvent.Connect) }, showedSnackBar = { viewModel.emit(SetupInvoicePayoutEvent.ShowedSnackBar) - navigateUp() + onSuccessfullyConnected() }, navigateUp = navigateUp, ) @@ -47,14 +52,15 @@ private fun SetupInvoicePayoutScreen( showedSnackBar: () -> Unit, navigateUp: () -> Unit, ) { + val changesSaved = stringResource(Res.string.CONTACT_INFO_CHANGES_SAVED) LaunchedEffect(uiState.showSuccessSnackBar) { if (!uiState.showSuccessSnackBar) return@LaunchedEffect - globalSnackBarState.show("Changes saved", NotificationPriority.Campaign) + globalSnackBarState.show(changesSaved, NotificationPriority.Campaign) showedSnackBar() } HedvigScaffold( - topAppBarText = "Invoice", + topAppBarText = stringResource(Res.string.PAYMENTS_INVOICE), navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt index c968e6db90..61f196b450 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt @@ -29,6 +29,7 @@ import com.hedvig.android.design.system.hedvig.NotificationDefaults.Notification internal fun SetupSwishPayoutDestination( viewModel: SetupSwishPayoutViewModel, globalSnackBarState: GlobalSnackBarState, + onSuccessfullyConnected: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -38,7 +39,7 @@ internal fun SetupSwishPayoutDestination( onSave = { viewModel.emit(SetupSwishPayoutEvent.Save) }, showedSnackBar = { viewModel.emit(SetupSwishPayoutEvent.ShowedSnackBar) - navigateUp() + onSuccessfullyConnected() }, navigateUp = navigateUp, ) @@ -68,7 +69,7 @@ private fun SetupSwishPayoutScreen( HedvigTextField( state = uiState.phoneNumberState, labelText = "Phone number", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Large, + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Phone, ), From c114d2fb1c6f35efc004c916c5d4b034d11505b9 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 22 Apr 2026 14:14:35 +0200 Subject: [PATCH 20/50] Extract common code in Payout overview screen --- .../PayoutAccountOverviewDestination.kt | 135 +++++++----------- 1 file changed, 50 insertions(+), 85 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 1ef6001aa8..3e7662c1d5 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -2,7 +2,6 @@ package com.hedvig.android.feature.payoutaccount.ui.overview import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -10,7 +9,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider @@ -104,91 +102,18 @@ private fun PayoutAccountContent( Column(modifier) { Spacer(Modifier.height(8.dp)) when (currentMethod) { - null -> { - Spacer(Modifier.weight(1f)) - HedvigButton( - text = stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD), - onClick = onConnectPayoutMethodClicked, - enabled = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - } + null -> {} is PayoutAccount.SwishPayout -> { - HedvigTextField( - text = currentMethod.phoneNumber, - onValueChange = {}, - labelText = "Swish", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, - readOnly = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - if (availablePayoutMethods.size > 1) { - Spacer(Modifier.height(8.dp)) - HedvigButton( - text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), - onClick = onConnectPayoutMethodClicked, - enabled = true, - buttonStyle = ButtonDefaults.ButtonStyle.Secondary, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - } + PayoutAccountReadOnlyTextField(label = "Swish", text = currentMethod.phoneNumber) } PayoutAccount.Trustly -> { - HedvigTextField( - text = "Trustly", - onValueChange = {}, - labelText = "Account", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, - readOnly = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - if (availablePayoutMethods.size > 1) { - Spacer(Modifier.height(8.dp)) - HedvigButton( - text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), - onClick = onConnectPayoutMethodClicked, - enabled = true, - buttonStyle = ButtonDefaults.ButtonStyle.Secondary, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - } + PayoutAccountReadOnlyTextField(label = "Account", text = "Trustly") } is PayoutAccount.Invoice -> { - HedvigTextField( - text = "Invoice", - onValueChange = {}, - labelText = "Account", - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, - readOnly = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - if (availablePayoutMethods.size > 1) { - Spacer(Modifier.height(8.dp)) - HedvigButton( - text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), - onClick = onConnectPayoutMethodClicked, - enabled = true, - buttonStyle = ButtonDefaults.ButtonStyle.Secondary, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - } + PayoutAccountReadOnlyTextField(label = "Account", text = "Invoice") } is PayoutAccount.BankAccount -> { @@ -201,17 +126,26 @@ private fun PayoutAccountContent( append("-") append(currentMethod.accountNumber) } - HedvigTextField( + PayoutAccountReadOnlyTextField( + label = stringResource(Res.string.PAYMENTS_ACCOUNT), text = displayText, - onValueChange = {}, - labelText = stringResource(Res.string.PAYMENTS_ACCOUNT), - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, - readOnly = true, + ) + } + } + Spacer(Modifier.weight(1f)) + when { + currentMethod == null -> { + HedvigButton( + text = stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD), + onClick = onConnectPayoutMethodClicked, + enabled = true, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), ) - Spacer(Modifier.weight(1f)) + } + + currentMethod is PayoutAccount.BankAccount -> { Spacer(Modifier.height(8.dp)) HedvigButton( text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), @@ -222,11 +156,42 @@ private fun PayoutAccountContent( .padding(horizontal = 16.dp), ) } + + availablePayoutMethods.size > 1 -> { + Spacer(Modifier.height(8.dp)) + HedvigButton( + text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), + onClick = onConnectPayoutMethodClicked, + enabled = true, + buttonStyle = ButtonDefaults.ButtonStyle.Secondary, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } } Spacer(Modifier.height(16.dp)) } } +@Composable +private fun PayoutAccountReadOnlyTextField( + label: String, + text: String, + modifier: Modifier = Modifier, +) { + HedvigTextField( + text = text, + onValueChange = {}, + labelText = label, + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, + readOnly = true, + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) +} + @Composable @HedvigPreview private fun PreviewPayoutAccountOverviewScreen( From 562db1496ec3a688b957b9037cd1c991da88c784 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 22 Apr 2026 14:36:47 +0200 Subject: [PATCH 21/50] Update payout to the new schema --- .../hedvig/android/apollo/octopus/schema.graphqls | 15 ++++++++++----- .../payoutaccount/data/GetPayoutAccountUseCase.kt | 6 +++--- 2 files changed, 13 insertions(+), 8 deletions(-) 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 c22e8b1420..bd2f3f8067 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 @@ -2842,9 +2842,10 @@ type MemberPaymentMethod { isDefault: Boolean! """ Specific details of the actual connection if method is ACTIVE - e.g. a bank account reference, phone number for swish, - or email/kivra for invoice. + or email/kivra for invoice. If method is PENDING, then this field will be null since the member has not yet set up + this payment method. """ - details: PaymentMethodDetails! + details: PaymentMethodDetails } type MemberPaymentMethods { """ @@ -3769,15 +3770,19 @@ input PaymentMethodSetupNordeaPayoutInput { } type PaymentMethodSetupOutput { """ - The status of the setup process. If FAILED the reason for failure can be found in the `error` field. + The status of the setup process. If FAILED the reason for failure can be found in the `error` field. """ status: PaymentMethodSetupStatus! """ - Url to redirect the member to if any. + The order id for the payment method setup order if SUCCESSFUL. + """ + orderId: ID + """ + Url to redirect the member to if any. """ url: String """ - An error message. Only sat if the setup process failed. + An error message. Only sat if the setup process failed. """ error: UserError } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index e693c851f8..1f9ff7e099 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -50,7 +50,7 @@ internal class GetPayoutAccountUseCaseImpl( } MemberPaymentProvider.SWISH -> { - val swishDetails = method.details.asPaymentMethodSwishDetails() + val swishDetails = method.details?.asPaymentMethodSwishDetails() if (swishDetails != null) { PayoutAccount.SwishPayout(phoneNumber = swishDetails.phoneNumber) } else { @@ -59,7 +59,7 @@ internal class GetPayoutAccountUseCaseImpl( } MemberPaymentProvider.NORDEA -> { - val bankAccountDetails = method.details.asPaymentMethodBankAccountDetails() + val bankAccountDetails = method.details?.asPaymentMethodBankAccountDetails() if (bankAccountDetails != null) { val account = bankAccountDetails.account val dashIndex = account.indexOf('-') @@ -76,7 +76,7 @@ internal class GetPayoutAccountUseCaseImpl( } MemberPaymentProvider.INVOICE -> { - val invoiceDetails = method.details.asPaymentMethodInvoiceDetails() + val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() if (invoiceDetails != null) { PayoutAccount.Invoice( delivery = invoiceDetails.delivery, From 76746ed0229bf68d516344a96af429511bf8bc93 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 22 Apr 2026 14:39:33 +0200 Subject: [PATCH 22/50] Remove redundant interfaces --- .../payoutaccount/data/GetPayoutAccountUseCase.kt | 10 +++------- .../payoutaccount/data/SetupNordeaPayoutUseCase.kt | 10 +++------- .../feature/payoutaccount/di/PayoutAccountModule.kt | 6 ++---- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index 1f9ff7e099..4f11264b3c 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -19,14 +19,10 @@ internal data class PayoutAccountData( val availablePayoutMethods: List, ) -internal interface GetPayoutAccountUseCase { - suspend fun invoke(): Either -} - -internal class GetPayoutAccountUseCaseImpl( +internal class GetPayoutAccountUseCase( private val apolloClient: ApolloClient, -) : GetPayoutAccountUseCase { - override suspend fun invoke(): Either = either { +) { + suspend fun invoke(): Either = either { return@either PayoutAccountData( currentMethod = FakePayoutAccountStorage.currentMethod, availablePayoutMethods = listOf( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt index fb9709fab6..9ba096bf39 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt @@ -12,15 +12,11 @@ import com.hedvig.android.core.common.ErrorMessage import octopus.SetupNordeaPayoutMutation import octopus.type.PaymentMethodSetupStatus -internal interface SetupNordeaPayoutUseCase { - suspend fun invoke(clearingNumber: String, accountNumber: String): Either -} - -internal class SetupNordeaPayoutUseCaseImpl( +internal class SetupNordeaPayoutUseCase( private val apolloClient: ApolloClient, private val networkCacheManager: NetworkCacheManager, -) : SetupNordeaPayoutUseCase { - override suspend fun invoke(clearingNumber: String, accountNumber: String): Either = either { +) { + suspend fun invoke(clearingNumber: String, accountNumber: String): Either = either { FakePayoutAccountStorage.currentMethod = PayoutAccount.BankAccount( clearingNumber = clearingNumber, accountNumber = accountNumber, diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt index 7d722de6d6..b7d1f52094 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt @@ -3,10 +3,8 @@ package com.hedvig.android.feature.payoutaccount.di import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCase -import com.hedvig.android.feature.payoutaccount.data.GetPayoutAccountUseCaseImpl import com.hedvig.android.feature.payoutaccount.data.SetupInvoicePayoutUseCase import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCase -import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCaseImpl import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel @@ -16,8 +14,8 @@ import org.koin.core.module.dsl.viewModel import org.koin.dsl.module val payoutAccountModule = module { - single { GetPayoutAccountUseCaseImpl(get()) } - single { SetupNordeaPayoutUseCaseImpl(get(), get()) } + single { GetPayoutAccountUseCase(get()) } + single { SetupNordeaPayoutUseCase(get(), get()) } single { SetupSwishPayoutUseCase(get(), get()) } single { SetupInvoicePayoutUseCase(get(), get()) } viewModel { PayoutAccountOverviewViewModel(get()) } From d6711facaa05f9c28ce672d5336223ab1b8adb47 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 22 Apr 2026 15:09:23 +0200 Subject: [PATCH 23/50] Read the pending status of payout options --- .../src/main/graphql/GetPayoutMethods.graphql | 4 +- .../data/FakePayoutAccountStorage.kt | 2 +- .../data/GetPayoutAccountUseCase.kt | 62 ++++++++----------- .../payoutaccount/data/PayoutAccount.kt | 17 +++-- .../data/SetupInvoicePayoutUseCase.kt | 1 + .../data/SetupNordeaPayoutUseCase.kt | 1 + .../data/SetupSwishPayoutUseCase.kt | 2 +- .../PayoutAccountOverviewDestination.kt | 27 ++++---- 8 files changed, 60 insertions(+), 56 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql index aa8f8d53b0..944ae5d726 100644 --- a/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql +++ b/app/feature/feature-payout-account/src/main/graphql/GetPayoutMethods.graphql @@ -1,8 +1,10 @@ query GetPayoutMethods { currentMember { paymentMethods { - defaultPayoutMethod { + payoutMethods { provider + status + isDefault details { ... on PaymentMethodBankAccountDetails { account diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt index 00efb60308..7419d85d5e 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt @@ -9,5 +9,5 @@ import octopus.type.PaymentMethodInvoiceDelivery // PayoutAccount.BankAccount("8327", "12345678", "Swedbank") → Bank account display // PayoutAccount.Invoice(PaymentMethodInvoiceDelivery.KIVRA, null) → Invoice display internal object FakePayoutAccountStorage { - var currentMethod: PayoutAccount? = PayoutAccount.Trustly + var currentMethod: PayoutAccount? = PayoutAccount.Trustly(isPending = false) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index 4f11264b3c..03f611580c 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -9,9 +9,10 @@ import com.hedvig.android.apollo.ErrorMessage import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage import octopus.GetPayoutMethodsQuery -import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodBankAccountDetails -import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodInvoiceDetails -import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.DefaultPayoutMethod.Details.Companion.asPaymentMethodSwishDetails +import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.PayoutMethod.Details.Companion.asPaymentMethodBankAccountDetails +import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.PayoutMethod.Details.Companion.asPaymentMethodInvoiceDetails +import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.PayoutMethod.Details.Companion.asPaymentMethodSwishDetails +import octopus.type.MemberPaymentMethodStatus import octopus.type.MemberPaymentProvider internal data class PayoutAccountData( @@ -39,53 +40,44 @@ internal class GetPayoutAccountUseCase( .bind() val paymentMethods = result.currentMember.paymentMethods - val currentMethod = paymentMethods.defaultPayoutMethod?.let { method -> + val defaultPayoutMethod = paymentMethods.payoutMethods.firstOrNull { it.isDefault } + // todo payout feature. Return the other, non-default payout methods when we can switch to them + val currentMethod = defaultPayoutMethod?.let { method -> + val isPending = method.status == MemberPaymentMethodStatus.PENDING when (method.provider) { MemberPaymentProvider.TRUSTLY -> { - PayoutAccount.Trustly + PayoutAccount.Trustly(isPending = isPending) } MemberPaymentProvider.SWISH -> { - val swishDetails = method.details?.asPaymentMethodSwishDetails() - if (swishDetails != null) { - PayoutAccount.SwishPayout(phoneNumber = swishDetails.phoneNumber) - } else { - null - } + val phoneNumber = method.details?.asPaymentMethodSwishDetails()?.phoneNumber + PayoutAccount.SwishPayout(phoneNumber = phoneNumber, isPending = isPending) } MemberPaymentProvider.NORDEA -> { val bankAccountDetails = method.details?.asPaymentMethodBankAccountDetails() - if (bankAccountDetails != null) { - val account = bankAccountDetails.account - val dashIndex = account.indexOf('-') - val clearingNumber = if (dashIndex >= 0) account.substring(0, dashIndex) else account - val accountNumber = if (dashIndex >= 0) account.substring(dashIndex + 1) else "" - PayoutAccount.BankAccount( - clearingNumber = clearingNumber, - accountNumber = accountNumber, - bankName = bankAccountDetails.bank, - ) - } else { - null - } + val account = bankAccountDetails?.account + val dashIndex = account?.indexOf('-') ?: -1 + val clearingNumber = if (dashIndex >= 0) account?.substring(0, dashIndex) else account + val accountNumber = if (dashIndex >= 0) account?.substring(dashIndex + 1) else null + PayoutAccount.BankAccount( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankAccountDetails?.bank, + isPending = isPending, + ) } MemberPaymentProvider.INVOICE -> { val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() - if (invoiceDetails != null) { - PayoutAccount.Invoice( - delivery = invoiceDetails.delivery, - email = invoiceDetails.email, - ) - } else { - null - } + PayoutAccount.Invoice( + delivery = invoiceDetails?.delivery, + email = invoiceDetails?.email, + isPending = isPending, + ) } - else -> { - null - } + else -> null } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt index e510741c9b..28a52dcd8f 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt @@ -3,18 +3,25 @@ package com.hedvig.android.feature.payoutaccount.data import octopus.type.PaymentMethodInvoiceDelivery internal sealed interface PayoutAccount { - data object Trustly : PayoutAccount + val isPending: Boolean - data class SwishPayout(val phoneNumber: String) : PayoutAccount + data class Trustly(override val isPending: Boolean) : PayoutAccount + + data class SwishPayout( + val phoneNumber: String?, + override val isPending: Boolean, + ) : PayoutAccount data class BankAccount( - val clearingNumber: String, - val accountNumber: String, + val clearingNumber: String?, + val accountNumber: String?, val bankName: String?, + override val isPending: Boolean, ) : PayoutAccount data class Invoice( - val delivery: PaymentMethodInvoiceDelivery, + val delivery: PaymentMethodInvoiceDelivery?, val email: String?, + override val isPending: Boolean, ) : PayoutAccount } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt index 9078358f80..e42742a87b 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt @@ -19,6 +19,7 @@ internal class SetupInvoicePayoutUseCase( FakePayoutAccountStorage.currentMethod = PayoutAccount.Invoice( delivery = PaymentMethodInvoiceDelivery.KIVRA, email = null, + isPending = false, ) return@either val result = apolloClient diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt index 9ba096bf39..a1478f6683 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt @@ -21,6 +21,7 @@ internal class SetupNordeaPayoutUseCase( clearingNumber = clearingNumber, accountNumber = accountNumber, bankName = bankNameForClearingNumber(clearingNumber), + isPending = false, ) return@either val result = apolloClient diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt index 872b464bab..28fadfac5d 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt @@ -15,7 +15,7 @@ internal class SetupSwishPayoutUseCase( private val networkCacheManager: NetworkCacheManager, ) { suspend fun invoke(phoneNumber: String): Either = either { - FakePayoutAccountStorage.currentMethod = PayoutAccount.SwishPayout(phoneNumber = phoneNumber) + FakePayoutAccountStorage.currentMethod = PayoutAccount.SwishPayout(phoneNumber = phoneNumber, isPending = false) return@either val result = apolloClient .mutation(SetupSwishPayoutMutation(phoneNumber = phoneNumber)) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 3e7662c1d5..b5f150ada7 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -105,10 +105,10 @@ private fun PayoutAccountContent( null -> {} is PayoutAccount.SwishPayout -> { - PayoutAccountReadOnlyTextField(label = "Swish", text = currentMethod.phoneNumber) + PayoutAccountReadOnlyTextField(label = "Swish", text = currentMethod.phoneNumber.orEmpty()) } - PayoutAccount.Trustly -> { + is PayoutAccount.Trustly -> { PayoutAccountReadOnlyTextField(label = "Account", text = "Trustly") } @@ -122,9 +122,11 @@ private fun PayoutAccountContent( append(currentMethod.bankName) append(" ") } - append(currentMethod.clearingNumber) - append("-") - append(currentMethod.accountNumber) + if (currentMethod.clearingNumber != null && currentMethod.accountNumber != null) { + append(currentMethod.clearingNumber) + append("-") + append(currentMethod.accountNumber) + } } PayoutAccountReadOnlyTextField( label = stringResource(Res.string.PAYMENTS_ACCOUNT), @@ -175,11 +177,7 @@ private fun PayoutAccountContent( } @Composable -private fun PayoutAccountReadOnlyTextField( - label: String, - text: String, - modifier: Modifier = Modifier, -) { +private fun PayoutAccountReadOnlyTextField(label: String, text: String, modifier: Modifier = Modifier) { HedvigTextField( text = text, onValueChange = {}, @@ -218,15 +216,15 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), Content( - currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67"), + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = false), availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), ), Content( - currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67"), + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = false), availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), Content( - currentMethod = PayoutAccount.Trustly, + currentMethod = PayoutAccount.Trustly(isPending = false), availablePayoutMethods = listOf(MemberPaymentProvider.TRUSTLY), ), Content( @@ -234,6 +232,7 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP clearingNumber = "3300", accountNumber = "1234567", bankName = "Nordea", + isPending = false, ), availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), ), @@ -241,6 +240,7 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP currentMethod = PayoutAccount.Invoice( delivery = PaymentMethodInvoiceDelivery.KIVRA, email = null, + isPending = false, ), availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE), ), @@ -248,6 +248,7 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP currentMethod = PayoutAccount.Invoice( delivery = PaymentMethodInvoiceDelivery.MAIL, email = "user@example.com", + isPending = false, ), availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE, MemberPaymentProvider.TRUSTLY), ), From 4a7ef9d36fb47b8c5feba62c34da1c77562948e5 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 22 Apr 2026 15:51:22 +0200 Subject: [PATCH 24/50] Fix connect payout button in overview screen --- .../PayoutAccountOverviewDestination.kt | 49 +++++-------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index b5f150ada7..fff8d44c75 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -135,43 +135,18 @@ private fun PayoutAccountContent( } } Spacer(Modifier.weight(1f)) - when { - currentMethod == null -> { - HedvigButton( - text = stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD), - onClick = onConnectPayoutMethodClicked, - enabled = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - } - - currentMethod is PayoutAccount.BankAccount -> { - Spacer(Modifier.height(8.dp)) - HedvigButton( - text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), - onClick = onConnectPayoutMethodClicked, - enabled = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - } - - availablePayoutMethods.size > 1 -> { - Spacer(Modifier.height(8.dp)) - HedvigButton( - text = stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL), - onClick = onConnectPayoutMethodClicked, - enabled = true, - buttonStyle = ButtonDefaults.ButtonStyle.Secondary, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - } - } + HedvigButton( + text = if (currentMethod == null) { + stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD) + } else { + stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL) + }, + onClick = onConnectPayoutMethodClicked, + enabled = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) Spacer(Modifier.height(16.dp)) } } From 84bd96ecca3ae9798f6ad4376ded5ae26588acaf Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 22 Apr 2026 16:16:12 +0200 Subject: [PATCH 25/50] Remove extra space when not needed --- .../PayoutAccountOverviewDestination.kt | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index fff8d44c75..9a2a878def 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.hedvig.android.design.system.hedvig.ButtonDefaults import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigErrorSection import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProgressDebounced @@ -117,20 +116,22 @@ private fun PayoutAccountContent( } is PayoutAccount.BankAccount -> { - val displayText = buildString { - if (currentMethod.bankName != null) { - append(currentMethod.bankName) - append(" ") - } - if (currentMethod.clearingNumber != null && currentMethod.accountNumber != null) { - append(currentMethod.clearingNumber) - append("-") - append(currentMethod.accountNumber) - } - } PayoutAccountReadOnlyTextField( label = stringResource(Res.string.PAYMENTS_ACCOUNT), - text = displayText, + text = buildString { + val hasBankNumber = currentMethod.clearingNumber != null && currentMethod.accountNumber != null + if (currentMethod.bankName != null) { + this.append(currentMethod.bankName) + if (hasBankNumber) { + this.append(" ") + } + } + if (hasBankNumber) { + this.append(currentMethod.clearingNumber) + this.append("-") + this.append(currentMethod.accountNumber) + } + }, ) } } From 5fe89d6f816e09550cb1b2dc429dc3b0e0582817 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Wed, 22 Apr 2026 16:30:35 +0200 Subject: [PATCH 26/50] Update schema --- .../android/apollo/octopus/schema.graphqls | 154 +++++++++++++----- 1 file changed, 112 insertions(+), 42 deletions(-) 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 bd2f3f8067..8542f80023 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 @@ -468,6 +468,41 @@ type BundleYearlySavings { """ bundleDiscountCoversFullPeriod: Boolean! } +""" +SHA-256-hashed user data for Facebook Conversions API (CAPI). +All non-null field values are hex-encoded SHA-256 hashes of the normalized plaintext. +Normalization follows https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters +""" +type CapiUserData { + """ + Hashed email (em) + """ + em: String! + """ + Hashed Gmail-normalized email, only present for Gmail addresses (gem) + """ + gem: String + """ + Hashed first name (fn) + """ + fn: String! + """ + Hashed last name (ln) + """ + ln: String! + """ + Hashed postal/zip code, null if unavailable (zp) + """ + zp: String + """ + Hashed city, null if unavailable (ct) + """ + ct: String + """ + Hashed country code, e.g. 'se' (co) + """ + co: String! +} type CarItemNotification { message: String! } @@ -1687,15 +1722,15 @@ returned as ExtendedItemDiscount """ type ExtendedItemDiscount { """ - General discount information + General discount information """ itemDiscount: ItemDiscount! """ - Monthly reduction applied by the discount. It's a negative number + Monthly reduction applied by the discount. It's a negative number """ amount: Money! """ - Whether discount is on a pending state or not + Whether discount is on a pending state or not """ isPending: Boolean! } @@ -2482,6 +2517,11 @@ type Member { """ crossSellV2(input: CrossSellInput!): CrossSellV2! """ + Young Pet Guide stories for the member. + Returns a list of educational content stories for young pet owners. + """ + puppyGuideStories: [PuppyGuideStory!]! + """ Fetch all the active contracts for this member. Active contracts include all insurances that are either active today, or to-be-active in the future. """ @@ -3596,7 +3636,7 @@ type Mutation { Update the raw insurance-related data for this `PriceIntent`. This data is mostly related to the insured object itself, and not the "holder" of the insurance. """ - priceIntentDataUpdate(priceIntentId: UUID!, data: PricingFormData!): PriceIntentMutationOutput! + priceIntentDataUpdate(priceIntentId: UUID!, data: PricingFormData!, applySuggestedData: Boolean): PriceIntentMutationOutput! """ Associate a specific Insurely `dataCollectionId` from lookup-service with this PriceIntent. """ @@ -3627,6 +3667,10 @@ type Mutation { """ productOfferReprice(offerId: UUID!, data: PricingFormData!): ProductOffersMutationOutput! """ + Mark a young pet guide story as read for a specific member. + """ + puppyGuideEngagement(engagement: PuppyEngagementInput!): PuppyGuideStoryMutationOutput! + """ Update the customer of the shop session. Only non-null fields will be changed. Can trigger automatic lookup of other information. The session can be placed in a "point of no return" state where it is no longer legal to update the customer, @@ -3770,19 +3814,19 @@ input PaymentMethodSetupNordeaPayoutInput { } type PaymentMethodSetupOutput { """ - The status of the setup process. If FAILED the reason for failure can be found in the `error` field. + The status of the setup process. If FAILED the reason for failure can be found in the `error` field. """ status: PaymentMethodSetupStatus! """ - The order id for the payment method setup order if SUCCESSFUL. + The order id for the payment method setup order if SUCCESSFUL. """ orderId: ID """ - Url to redirect the member to if any. + Url to redirect the member to if any. """ url: String """ - An error message. Only sat if the setup process failed. + An error message. Only sat if the setup process failed. """ error: UserError } @@ -3906,11 +3950,11 @@ type PriceIntent { """ product: Product! """ - Submitted user form data. + UI-safe masked form data. PII fields (street, zipCode, city) are always masked. """ data: PricingFormData! """ - Data submitted in other places or inferred from other data points + Masked, uncommitted form data from automatic lookup (e.g. SPAR address, trial data). PII fields are masked the same way as 'data'. """ suggestedData: PricingFormData! """ @@ -3943,6 +3987,10 @@ type PriceIntent { Null when no Insurely data collection has been associated with this price intent. """ fetchedExternalInsurances: [FetchedExternalInsurance!] + """ + When 'true' all required form data has been provided and the price intent can be confirmed. + """ + isReadyToConfirm: Boolean! } enum PriceIntentAnimal { CAT @@ -4103,7 +4151,7 @@ type ProductOffer { """ priceIntentId: UUID """ - The form data used to generate the offer + UI-safe masked form data used to generate the offer. PII fields (street, zipCode, city) are masked when address came from registration address lookup. """ priceIntentData: PricingFormData! """ @@ -4272,11 +4320,6 @@ if the user has input enough information to generate it. type ProductRecommendation { product: Product! offer: ProductOffer - """ - External insurance data from Insurely, available even when no offer could be generated. - Null when no Insurely data collection is associated with the session. - """ - externalInsurance: RecommendationExternalInsurance } type ProductVariant { """ @@ -4342,6 +4385,53 @@ type ProductVariantComparisonRow { """ covered: [String!]! } +input PuppyEngagementInput { + name: String! + rating: Int + opened: Boolean + read: Boolean + closed: Boolean +} +type PuppyGuideStory { + """ + The unique name/identifier of the story. + """ + name: String! + """ + The display title of the story. + """ + title: String! + """ + The subtitle or description of the story. + """ + subtitle: String! + """ + The main content of the story. + """ + content: String! + """ + The image associated with this story. + """ + image: String! + """ + Categories this story belongs to. + """ + categories: [String!]! + """ + The date when the story was marked as read by the user. + """ + read: Boolean! + """ + The user's rating of the story. + """ + rating: Int +} +type PuppyGuideStoryMutationOutput { + """ + Indicates whether the mutation was successful. + """ + success: Boolean! +} type Query { """ Return a conversation for a given ID. @@ -4409,32 +4499,6 @@ type Query { """ addonOfferCost(quoteId: ID!, selectedAddonIds: [ID!]!): ItemCost! } -type RecommendationExternalInsurance { - """ - Display name of the external insurance product - """ - displayName: String! - """ - The external insurer - """ - insurer: ExternalInsurer! - """ - Monthly price of the external insurance. Null if not available. - """ - price: Money - """ - Contextual subtitle (e.g. address, registration number, pet name) - """ - subtitle: String - """ - Renewal date of the external policy. Null when not provided. - """ - renewalDate: Date - """ - Insurely data collection ID - """ - dataCollectionId: String! -} type RecommendedCrossSell { crossSell: CrossSell! bannerText: String! @@ -4683,6 +4747,12 @@ type ShopSessionOutcome { Note that this will not contain and `PendingContract`s. """ createdContracts: [Contract!]! + """ + Pre-hashed Facebook CAPI user data (SHA-256 hex, lowercase) for this signed session. + Fields follow Meta's Conversions API customer information parameter spec. + Requires authentication (inherits MemberAccessible from the outcome). + """ + capiUserData: CapiUserData! } type ShopSessionSigning { id: UUID! From 1e0dd858dab8f6fc894e6625864cce6168fe2233 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 24 Apr 2026 12:14:49 +0200 Subject: [PATCH 27/50] Use new `paymentMethodSetupTrustly` over `registerDirectDebit2` --- .../android/app/navigation/HedvigNavHost.kt | 3 +- ...itiateTrustlyConnectPaymentSession.graphql | 6 -- .../StartTrustlyPayoutSessionUseCase.kt | 37 -------- .../trustly/StartTrustlySessionUseCase.kt | 13 ++- .../payment/trustly/TrustlyPayoutPresenter.kt | 93 ------------------- .../payment/trustly/TrustlyPayoutViewModel.kt | 18 ---- .../payment/trustly/TrustlyPresenter.kt | 7 +- .../trustly/di/ConnectPaymentTrustlyModule.kt | 12 --- .../navigation/ConnectTrustlyPaymentGraph.kt | 11 --- .../payment/trustly/ui/TrustlyDestination.kt | 11 +-- 10 files changed, 18 insertions(+), 193 deletions(-) delete mode 100644 app/feature/feature-connect-payment-trustly/src/main/graphql/InitiateTrustlyConnectPaymentSession.graphql delete mode 100644 app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt delete mode 100644 app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt delete mode 100644 app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 0a4957b13c..824092fa7b 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -36,7 +36,6 @@ import com.hedvig.android.feature.claimhistory.nav.ClaimHistoryDestination import com.hedvig.android.feature.claimhistory.nav.claimHistoryGraph import com.hedvig.android.feature.connect.payment.connectPaymentGraph import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyDestination -import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyPayoutDestination import com.hedvig.android.feature.deleteaccount.navigation.DeleteAccountDestination import com.hedvig.android.feature.deleteaccount.navigation.deleteAccountGraph import com.hedvig.android.feature.editcoinsured.navigation.EditCoInsuredDestination.CoInsuredAddInfo @@ -351,7 +350,7 @@ internal fun HedvigNavHost( navController = navController, globalSnackBarState = globalSnackBarState, navigateToTrustlyPayout = { builder -> - navController.navigate(TrustlyPayoutDestination, builder) + navController.navigate(TrustlyDestination, builder) }, navigateUp = navController::navigateUp, ) diff --git a/app/feature/feature-connect-payment-trustly/src/main/graphql/InitiateTrustlyConnectPaymentSession.graphql b/app/feature/feature-connect-payment-trustly/src/main/graphql/InitiateTrustlyConnectPaymentSession.graphql deleted file mode 100644 index 95ada42b81..0000000000 --- a/app/feature/feature-connect-payment-trustly/src/main/graphql/InitiateTrustlyConnectPaymentSession.graphql +++ /dev/null @@ -1,6 +0,0 @@ -mutation InitiateTrustlyConnectPaymentSession($successUrl: String!, $failureUrl: String!) { - registerDirectDebit2(clientContext: { successUrl: $successUrl, failureUrl: $failureUrl }) { - url - orderId - } -} diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt deleted file mode 100644 index b43ca2fc01..0000000000 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlyPayoutSessionUseCase.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.hedvig.android.feature.connect.payment.trustly - -import arrow.core.Either -import arrow.core.raise.either -import arrow.core.raise.ensureNotNull -import com.apollographql.apollo.ApolloClient -import com.hedvig.android.apollo.ErrorMessage -import com.hedvig.android.apollo.safeExecute -import com.hedvig.android.core.common.ErrorMessage -import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback -import com.hedvig.android.logger.logcat -import octopus.SetupTrustlyPayoutMutation - -internal class StartTrustlyPayoutSessionUseCase( - private val apolloClient: ApolloClient, - private val trustlyCallback: TrustlyCallback, -) { - suspend fun invoke(): Either { - return either { - return@either TrustlyInitiateProcessUrl("fake://trustly-payout-success") - val data = apolloClient - .mutation( - SetupTrustlyPayoutMutation( - successUrl = trustlyCallback.successUrl, - failureUrl = trustlyCallback.failureUrl, - ), - ) - .safeExecute(::ErrorMessage) - .bind() - logcat { "StartTrustlyPayoutSessionUseCase received: ${data.paymentMethodSetupTrustly}" } - val url = ensureNotNull(data.paymentMethodSetupTrustly.url) { - ErrorMessage("Trustly payout setup returned no URL") - } - TrustlyInitiateProcessUrl(url) - } - } -} diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlySessionUseCase.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlySessionUseCase.kt index 78c6c229b5..397ee1168d 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlySessionUseCase.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/StartTrustlySessionUseCase.kt @@ -2,13 +2,14 @@ package com.hedvig.android.feature.connect.payment.trustly import arrow.core.Either import arrow.core.raise.either +import arrow.core.raise.ensureNotNull import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.ErrorMessage import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback import com.hedvig.android.logger.logcat -import octopus.InitiateTrustlyConnectPaymentSessionMutation +import octopus.SetupTrustlyPayoutMutation internal class StartTrustlySessionUseCase( private val apolloClient: ApolloClient, @@ -18,15 +19,19 @@ internal class StartTrustlySessionUseCase( return either { val data = apolloClient .mutation( - InitiateTrustlyConnectPaymentSessionMutation( + SetupTrustlyPayoutMutation( successUrl = trustlyCallback.successUrl, failureUrl = trustlyCallback.failureUrl, ), ) .safeExecute(::ErrorMessage) .bind() - logcat { "StartTrustlySessionUseCase received: ${data.registerDirectDebit2}" } - TrustlyInitiateProcessUrl(data.registerDirectDebit2.url) + logcat { "StartTrustlySessionUseCase received: ${data.paymentMethodSetupTrustly}" } + val url = ensureNotNull(data.paymentMethodSetupTrustly.url) { + logcat { "StartTrustlySessionUseCase received: ${data.paymentMethodSetupTrustly}" } + ErrorMessage(data.paymentMethodSetupTrustly.error?.message) + } + TrustlyInitiateProcessUrl(url) } } } diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt deleted file mode 100644 index 2d8621a4ac..0000000000 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutPresenter.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.hedvig.android.feature.connect.payment.trustly - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import com.hedvig.android.apollo.NetworkCacheManager -import com.hedvig.android.core.common.ErrorMessage -import com.hedvig.android.core.common.safeCast -import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback -import com.hedvig.android.molecule.public.MoleculePresenter -import com.hedvig.android.molecule.public.MoleculePresenterScope - -internal class TrustlyPayoutPresenter( - private val trustlyCallback: TrustlyCallback, - private val startTrustlyPayoutSessionUseCase: StartTrustlyPayoutSessionUseCase, - private val cacheManager: NetworkCacheManager, -) : MoleculePresenter { - @Composable - override fun MoleculePresenterScope.present(lastState: TrustlyUiState): TrustlyUiState { - var browsing: TrustlyUiState.Browsing? by remember { - mutableStateOf(lastState.safeCast()) - } - var startSessionError: ErrorMessage? by remember { mutableStateOf(null) } - var connectingCardFailed by remember { mutableStateOf(lastState is TrustlyUiState.FailedToConnectCard) } - var succeededInConnectingCard by remember { mutableStateOf(lastState is TrustlyUiState.SucceededInConnectingCard) } - - var loadIteration by remember { mutableIntStateOf(0) } - - LaunchedEffect(loadIteration) { - if (browsing != null) return@LaunchedEffect - if (startSessionError != null) return@LaunchedEffect - if (connectingCardFailed) return@LaunchedEffect - if (succeededInConnectingCard) return@LaunchedEffect - startTrustlyPayoutSessionUseCase.invoke().fold( - ifLeft = { - startSessionError = it - browsing = null - }, - ifRight = { - // todo testing remove hardcoded success - if (it.url.startsWith("fake://")) { - succeededInConnectingCard = true - } else { - startSessionError = null - browsing = TrustlyUiState.Browsing(it.url, trustlyCallback) - } - }, - ) - } - - CollectEvents { event -> - when (event) { - TrustlyEvent.ConnectingCardFailed -> { - connectingCardFailed = true - } - - TrustlyEvent.ConnectingCardSucceeded -> { - succeededInConnectingCard = true - } - - TrustlyEvent.RetryConnectingCard -> { - browsing = null - startSessionError = null - connectingCardFailed = false - succeededInConnectingCard = false - loadIteration++ - } - } - } - - if (succeededInConnectingCard) { - LaunchedEffect(Unit) { - cacheManager.clearCache() - } - return TrustlyUiState.SucceededInConnectingCard - } - if (connectingCardFailed) { - return TrustlyUiState.FailedToConnectCard - } - if (startSessionError != null) { - return TrustlyUiState.FailedToStartSession - } - val browsingValue = browsing - if (browsingValue != null) { - return browsingValue - } - return TrustlyUiState.Loading - } -} diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt deleted file mode 100644 index 36ed291cba..0000000000 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPayoutViewModel.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.hedvig.android.feature.connect.payment.trustly - -import com.hedvig.android.apollo.NetworkCacheManager -import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback -import com.hedvig.android.molecule.public.MoleculeViewModel - -internal class TrustlyPayoutViewModel( - trustlyCallback: TrustlyCallback, - startTrustlyPayoutSessionUseCase: StartTrustlyPayoutSessionUseCase, - networkCacheManager: NetworkCacheManager, -) : MoleculeViewModel( - TrustlyUiState.Loading, - TrustlyPayoutPresenter( - trustlyCallback, - startTrustlyPayoutSessionUseCase, - networkCacheManager, - ), - ) diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPresenter.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPresenter.kt index 4927100483..da3269781f 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPresenter.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/TrustlyPresenter.kt @@ -76,8 +76,9 @@ internal class TrustlyPresenter( if (connectingCardFailed) { return TrustlyUiState.FailedToConnectCard } - if (startSessionError != null) { - return TrustlyUiState.FailedToStartSession + val startSessionErrorValue = startSessionError + if (startSessionErrorValue != null) { + return TrustlyUiState.FailedToStartSession(startSessionErrorValue) } val browsingValue = browsing if (browsingValue != null) { @@ -105,7 +106,7 @@ internal sealed interface TrustlyUiState { data object FailedToConnectCard : TrustlyUiState - data object FailedToStartSession : TrustlyUiState + data class FailedToStartSession(val errorMessage: ErrorMessage) : TrustlyUiState data object SucceededInConnectingCard : TrustlyUiState } diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt index a00ecf9cb2..9b4f4182f8 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/di/ConnectPaymentTrustlyModule.kt @@ -3,9 +3,7 @@ package com.hedvig.android.feature.connect.payment.trustly.di import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.core.buildconstants.HedvigBuildConstants -import com.hedvig.android.feature.connect.payment.trustly.StartTrustlyPayoutSessionUseCase import com.hedvig.android.feature.connect.payment.trustly.StartTrustlySessionUseCase -import com.hedvig.android.feature.connect.payment.trustly.TrustlyPayoutViewModel import com.hedvig.android.feature.connect.payment.trustly.TrustlyViewModel import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallback import com.hedvig.android.feature.connect.payment.trustly.data.TrustlyCallbackImpl @@ -17,9 +15,6 @@ val connectPaymentTrustlyModule = module { single { StartTrustlySessionUseCase(get(), get()) } - single { - StartTrustlyPayoutSessionUseCase(get(), get()) - } viewModel { TrustlyViewModel( trustlyCallback = get(), @@ -27,11 +22,4 @@ val connectPaymentTrustlyModule = module { networkCacheManager = get(), ) } - viewModel { - TrustlyPayoutViewModel( - trustlyCallback = get(), - startTrustlyPayoutSessionUseCase = get(), - networkCacheManager = get(), - ) - } } diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt index a63c8eaff6..ae0d4d83e0 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt @@ -2,10 +2,8 @@ package com.hedvig.android.feature.connect.payment import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder -import com.hedvig.android.feature.connect.payment.trustly.TrustlyPayoutViewModel import com.hedvig.android.feature.connect.payment.trustly.TrustlyViewModel import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyDestination -import com.hedvig.android.feature.connect.payment.trustly.ui.TrustlyPayoutDestination import com.hedvig.android.navigation.compose.navDeepLinks import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.core.HedvigDeepLinkContainer @@ -28,13 +26,4 @@ fun NavGraphBuilder.connectPaymentGraph( finishTrustlyFlow = navController::popBackStack, ) } - - navdestination { - val viewModel: TrustlyPayoutViewModel = koinViewModel() - TrustlyDestination( - viewModel = viewModel, - navigateUp = navController::navigateUp, - finishTrustlyFlow = navController::popBackStack, - ) - } } diff --git a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt index 6098e1effb..860938bfad 100644 --- a/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt +++ b/app/feature/feature-connect-payment-trustly/src/main/kotlin/com/hedvig/android/feature/connect/payment/trustly/ui/TrustlyDestination.kt @@ -28,6 +28,7 @@ import com.hedvig.android.composewebview.LoadingState import com.hedvig.android.composewebview.WebView import com.hedvig.android.composewebview.rememberSaveableWebViewState import com.hedvig.android.composewebview.rememberWebViewNavigator +import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.design.system.hedvig.EmptyState import com.hedvig.android.design.system.hedvig.EmptyStateDefaults.EmptyStateButtonStyle.Button import com.hedvig.android.design.system.hedvig.EmptyStateDefaults.EmptyStateIconStyle.SUCCESS @@ -39,7 +40,6 @@ import com.hedvig.android.design.system.hedvig.Surface import com.hedvig.android.design.system.hedvig.TopAppBarWithBack import com.hedvig.android.feature.connect.payment.trustly.TrustlyEvent import com.hedvig.android.feature.connect.payment.trustly.TrustlyUiState -import com.hedvig.android.feature.connect.payment.trustly.TrustlyViewModel import com.hedvig.android.feature.connect.payment.trustly.data.PreviewTrustlyCallback import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebChromeClient import com.hedvig.android.feature.connect.payment.trustly.sdk.TrustlyWebView @@ -59,9 +59,6 @@ import org.jetbrains.compose.resources.stringResource @Serializable data object TrustlyDestination : Destination -@Serializable -data object TrustlyPayoutDestination : Destination - @Composable internal fun TrustlyDestination( viewModel: MoleculeViewModel, @@ -114,10 +111,10 @@ private fun TrustlyScreen( ) } - TrustlyUiState.FailedToStartSession -> { + is TrustlyUiState.FailedToStartSession -> { HedvigErrorSection( onButtonClick = retryConnectingCard, - title = stringResource(Res.string.something_went_wrong), + title = uiState.errorMessage.message ?: stringResource(Res.string.something_went_wrong), subTitle = null, ) } @@ -227,7 +224,7 @@ private class TrustlyUiStateProvider : CollectionPreviewParameterProvider Date: Fri, 24 Apr 2026 15:13:46 +0200 Subject: [PATCH 28/50] Add missing payout reminder Only comes after checking for pay-in reminder as it takes precedence over payout --- .../android/app/navigation/HedvigNavHost.kt | 2 + .../feature/home/home/navigation/HomeGraph.kt | 2 + .../feature/home/home/ui/HomeDestination.kt | 9 +++++ .../feature/profile/tab/ProfileDestination.kt | 4 ++ .../feature/profile/tab/ProfileGraph.kt | 2 + .../graphql/QueryGetPayinMethodStatus.graphql | 5 +++ .../GetConnectPaymentReminderUseCase.kt | 12 ++++-- .../GetMemberRemindersUseCase.kt | 8 ++++ .../memberreminders/ui/MemberReminderCards.kt | 40 +++++++++++++++++++ 9 files changed, 81 insertions(+), 3 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 824092fa7b..0e85b6b0bb 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -186,6 +186,7 @@ internal fun HedvigNavHost( navController.navigate(ClaimDetailDestination.ClaimOverviewDestination(claimId)) }, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = { navController.navigate(PayoutAccountDestination.Graph) }, navigateToMissingInfo = { contractId: String, type: CoInsuredFlowType -> navController.navigate(CoInsuredAddInfo(contractId, type)) }, @@ -372,6 +373,7 @@ internal fun HedvigNavHost( hedvigDeepLinkContainer = hedvigDeepLinkContainer, hedvigBuildConstants = hedvigBuildConstants, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = { navController.navigate(PayoutAccountDestination.Graph) }, navigateToAddMissingInfo = { contractId: String, type: CoInsuredFlowType -> navController.navigate(CoInsuredAddInfo(contractId, type)) }, diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt index 072b01417f..4aa80cf09e 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt @@ -24,6 +24,7 @@ fun NavGraphBuilder.homeGraph( onNavigateToNewConversation: () -> Unit, navigateToClaimDetails: (claimId: String) -> Unit, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToContactInfo: () -> Unit, navigateToMissingInfo: (String, CoInsuredFlowType) -> Unit, navigateToHelpCenter: () -> Unit, @@ -54,6 +55,7 @@ fun NavGraphBuilder.homeGraph( navigateToClaimDetails(claimId) }, navigateToConnectPayment = dropUnlessResumed { navigateToConnectPayment() }, + navigateToConnectPayout = dropUnlessResumed { navigateToConnectPayout() }, navigateToMissingInfo = dropUnlessResumed { contractId, type -> navigateToMissingInfo(contractId, type) }, navigateToHelpCenter = dropUnlessResumed { navigateToHelpCenter() }, openUrl = openUrl, 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 71a9ae2965..8095f75c6a 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 @@ -158,6 +158,7 @@ internal fun HomeDestination( navigateToClaimChatInDevMode: () -> Unit, onClaimDetailCardClicked: (String) -> Unit, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToHelpCenter: () -> Unit, openUrl: (String) -> Unit, openCrossSellUrl: (String) -> Unit, @@ -180,6 +181,7 @@ internal fun HomeDestination( navigateToClaimChatInDevMode = navigateToClaimChatInDevMode, onClaimDetailCardClicked = onClaimDetailCardClicked, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, navigateToHelpCenter = navigateToHelpCenter, openUrl = openUrl, openCrossSellUrl = openCrossSellUrl, @@ -208,6 +210,7 @@ private fun HomeScreen( navigateToClaimChatInDevMode: () -> Unit, onClaimDetailCardClicked: (String) -> Unit, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToHelpCenter: () -> Unit, openUrl: (String) -> Unit, openCrossSellUrl: (String) -> Unit, @@ -276,6 +279,7 @@ private fun HomeScreen( notificationPermissionState = notificationPermissionState, onClaimDetailCardClicked = onClaimDetailCardClicked, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, navigateToHelpCenter = navigateToHelpCenter, openClaimFlowSheet = startClaimBottomSheetState::show, openAppSettings = openAppSettings, @@ -423,6 +427,7 @@ private fun HomeScreenSuccess( notificationPermissionState: NotificationPermissionState, onClaimDetailCardClicked: (claimId: String) -> Unit, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToHelpCenter: () -> Unit, openClaimFlowSheet: () -> Unit, openAppSettings: () -> Unit, @@ -505,6 +510,7 @@ private fun HomeScreenSuccess( MemberReminderCardsWithoutNotification( memberReminders = memberReminders, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, navigateToAddMissingInfo = navigateToMissingInfo, onNavigateToNewConversation = onNavigateToNewConversation, openUrl = openUrl, @@ -801,6 +807,7 @@ private fun PreviewHomeScreen( navigateToClaimChat = {}, onClaimDetailCardClicked = {}, navigateToConnectPayment = {}, + navigateToConnectPayout = {}, navigateToHelpCenter = {}, openUrl = {}, openCrossSellUrl = {}, @@ -833,6 +840,7 @@ private fun PreviewHomeScreenWithError() { navigateToClaimChat = {}, onClaimDetailCardClicked = {}, navigateToConnectPayment = {}, + navigateToConnectPayout = {}, navigateToHelpCenter = {}, openUrl = {}, openCrossSellUrl = {}, @@ -886,6 +894,7 @@ private fun PreviewHomeScreenAllHomeTextTypes( navigateToClaimChat = {}, onClaimDetailCardClicked = {}, navigateToConnectPayment = {}, + navigateToConnectPayout = {}, navigateToHelpCenter = {}, openUrl = {}, openCrossSellUrl = {}, diff --git a/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileDestination.kt b/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileDestination.kt index 7dae99dc64..fd2a75006d 100644 --- a/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileDestination.kt +++ b/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileDestination.kt @@ -103,6 +103,7 @@ internal fun ProfileDestination( navigateToSettings: () -> Unit, navigateToCertificates: () -> Unit, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToAddMissingInfo: (contractId: String, CoInsuredFlowType) -> Unit, openAppSettings: () -> Unit, openUrl: (String) -> Unit, @@ -122,6 +123,7 @@ internal fun ProfileDestination( navigateToSettings = navigateToSettings, navigateToCertificates = navigateToCertificates, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, navigateToAddMissingInfo = navigateToAddMissingInfo, openAppSettings = openAppSettings, openUrl = openUrl, @@ -144,6 +146,7 @@ private fun ProfileScreen( navigateToSettings: () -> Unit, navigateToCertificates: () -> Unit, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToAddMissingInfo: (contractId: String, CoInsuredFlowType) -> Unit, openAppSettings: () -> Unit, openUrl: (String) -> Unit, @@ -221,6 +224,7 @@ private fun ProfileScreen( MemberReminderCards( memberReminders = memberReminders, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, navigateToAddMissingInfo = navigateToAddMissingInfo, openUrl = openUrl, notificationPermissionState = notificationPermissionState, diff --git a/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileGraph.kt b/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileGraph.kt index 5d152bd500..4e2fbc374d 100644 --- a/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileGraph.kt +++ b/app/feature/feature-profile/src/main/kotlin/com/hedvig/android/feature/profile/tab/ProfileGraph.kt @@ -38,6 +38,7 @@ fun NavGraphBuilder.profileGraph( hedvigDeepLinkContainer: HedvigDeepLinkContainer, hedvigBuildConstants: HedvigBuildConstants, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToAddMissingInfo: (contractId: String, CoInsuredFlowType) -> Unit, navigateToDeleteAccountFeature: () -> Unit, navigateToClaimHistory: () -> Unit, @@ -75,6 +76,7 @@ fun NavGraphBuilder.profileGraph( navController.navigate(Certificates) }, navigateToConnectPayment = dropUnlessResumed { navigateToConnectPayment() }, + navigateToConnectPayout = dropUnlessResumed { navigateToConnectPayout() }, navigateToAddMissingInfo = dropUnlessResumed { contractId: String, type: CoInsuredFlowType -> navigateToAddMissingInfo(contractId, type) }, diff --git a/app/member-reminders/member-reminders-public/src/main/graphql/QueryGetPayinMethodStatus.graphql b/app/member-reminders/member-reminders-public/src/main/graphql/QueryGetPayinMethodStatus.graphql index 18ef1ab779..bcdd51e8fc 100644 --- a/app/member-reminders/member-reminders-public/src/main/graphql/QueryGetPayinMethodStatus.graphql +++ b/app/member-reminders/member-reminders-public/src/main/graphql/QueryGetPayinMethodStatus.graphql @@ -8,5 +8,10 @@ query GetPayinMethodStatus { terminationDueToMissedPayments id } + paymentMethods { + payoutMethods { + status + } + } } } diff --git a/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetConnectPaymentReminderUseCase.kt b/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetConnectPaymentReminderUseCase.kt index 1784a4a4e2..f7c8e3a4f2 100644 --- a/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetConnectPaymentReminderUseCase.kt +++ b/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetConnectPaymentReminderUseCase.kt @@ -44,10 +44,14 @@ internal class GetConnectPaymentReminderUseCaseImpl( return@either PaymentReminder.ShowMissingPaymentsReminder(missingPaymentsContractTerminationDate) } val payStatus = result.currentMember.paymentInformation.status - ensure(payStatus == MemberPaymentConnectionStatus.NEEDS_SETUP) { - ConnectPaymentReminderError.DomainError.AlreadySetup + if (payStatus == MemberPaymentConnectionStatus.NEEDS_SETUP) { + return@either PaymentReminder.ShowConnectPaymentReminder } - PaymentReminder.ShowConnectPaymentReminder + val isMissingPayoutMethod = result.currentMember.paymentMethods.payoutMethods.isEmpty() + if (isMissingPayoutMethod) { + return@either PaymentReminder.ShowConnectPayoutReminder + } + raise(ConnectPaymentReminderError.DomainError.AlreadySetup) }.onLeft { if (it !is ConnectPaymentReminderError.DomainError) { logcat { "GetConnectPaymentReminderUseCase failed with error:$it" } @@ -69,5 +73,7 @@ sealed interface ConnectPaymentReminderError { sealed interface PaymentReminder { data object ShowConnectPaymentReminder : PaymentReminder + data object ShowConnectPayoutReminder : PaymentReminder + data class ShowMissingPaymentsReminder(val terminationDate: LocalDate) : PaymentReminder } diff --git a/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetMemberRemindersUseCase.kt b/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetMemberRemindersUseCase.kt index f0de036c79..f2f39ee9c9 100644 --- a/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetMemberRemindersUseCase.kt +++ b/app/member-reminders/member-reminders-public/src/main/kotlin/com/hedvig/android/memberreminders/GetMemberRemindersUseCase.kt @@ -42,6 +42,10 @@ internal class GetMemberRemindersUseCaseImpl( MemberReminder.PaymentReminder.ConnectPayment() } + PaymentReminder.ShowConnectPayoutReminder -> { + MemberReminder.PaymentReminder.ConnectPayout() + } + is PaymentReminder.ShowMissingPaymentsReminder -> { MemberReminder.PaymentReminder.TerminationDueToMissedPayments( terminationDate = paymentReminder.terminationDate, @@ -126,6 +130,10 @@ sealed interface MemberReminder { data class ConnectPayment( override val id: String = UUID.randomUUID().toString(), ) : PaymentReminder + + data class ConnectPayout( + override val id: String = UUID.randomUUID().toString(), + ) : PaymentReminder } data class UpcomingRenewal( diff --git a/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt b/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt index 2018cc8ebe..33cf6fc4d2 100644 --- a/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt +++ b/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt @@ -48,6 +48,8 @@ import hedvig.resources.CONTRACT_VIEW_CERTIFICATE_BUTTON import hedvig.resources.DASHBOARD_RENEWAL_PROMPTER_BODY import hedvig.resources.MISSING_CONTACT_INFO_CARD_BUTTON import hedvig.resources.MISSING_CONTACT_INFO_CARD_TEXT +import hedvig.resources.PAYOUT_ADD_PAYOUT_METHOD +import hedvig.resources.PAYOUT_MISSING_INFO import hedvig.resources.PROFILE_ALLOW_NOTIFICATIONS_INFO_LABEL import hedvig.resources.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON import hedvig.resources.PUSH_NOTIFICATIONS_ALERT_ACTION_NOT_NOW @@ -73,6 +75,9 @@ fun getMemberReminderMessage(reminder: MemberReminder): String { is MemberReminder.PaymentReminder.ConnectPayment -> stringResource(Res.string.info_card_missing_payment_body) + is MemberReminder.PaymentReminder.ConnectPayout -> + stringResource(Res.string.PAYOUT_MISSING_INFO) + is MemberReminder.PaymentReminder.TerminationDueToMissedPayments -> stringResource(Res.string.info_card_missing_payment_missing_payments_body, reminder.terminationDate) @@ -126,6 +131,7 @@ fun rememberMaxLineCountForReminders( fun MemberReminderCardsWithoutNotification( memberReminders: List, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, openUrl: (String) -> Unit, navigateToAddMissingInfo: (String, CoInsuredFlowType) -> Unit, onNavigateToNewConversation: () -> Unit, @@ -137,6 +143,7 @@ fun MemberReminderCardsWithoutNotification( MemberReminderCards( memberReminders = memberReminders, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, openUrl = openUrl, navigateToAddMissingInfo = navigateToAddMissingInfo, onNavigateToNewConversation = onNavigateToNewConversation, @@ -153,6 +160,7 @@ fun MemberReminderCardsWithoutNotification( fun MemberReminderCards( memberReminders: List, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, openUrl: (String) -> Unit, navigateToAddMissingInfo: (String, CoInsuredFlowType) -> Unit, snoozeNotificationPermissionReminder: () -> Unit, @@ -169,6 +177,7 @@ fun MemberReminderCards( memberReminder = memberReminders.first(), navigateToAddMissingInfo = navigateToAddMissingInfo, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, openUrl = openUrl, onNavigateToNewConversation = onNavigateToNewConversation, snoozeNotificationPermissionReminder = snoozeNotificationPermissionReminder, @@ -200,6 +209,7 @@ fun MemberReminderCards( memberReminder = memberReminders[page], navigateToAddMissingInfo = navigateToAddMissingInfo, navigateToConnectPayment = navigateToConnectPayment, + navigateToConnectPayout = navigateToConnectPayout, openUrl = openUrl, onNavigateToNewConversation = onNavigateToNewConversation, snoozeNotificationPermissionReminder = snoozeNotificationPermissionReminder, @@ -232,6 +242,7 @@ private fun ColumnScope.MemberReminderCard( memberReminder: MemberReminder, navigateToAddMissingInfo: (String, CoInsuredFlowType) -> Unit, navigateToConnectPayment: () -> Unit, + navigateToConnectPayout: () -> Unit, navigateToContactInfo: () -> Unit, navigateToChipId: () -> Unit, openUrl: (String) -> Unit, @@ -262,6 +273,15 @@ private fun ColumnScope.MemberReminderCard( ) } + is MemberReminder.PaymentReminder.ConnectPayout -> { + ReminderCardConnectPayout( + navigateToConnectPayout = navigateToConnectPayout, + modifier = modifier, + minLines = minLines, + memberReminder = memberReminder, + ) + } + is MemberReminder.PaymentReminder.TerminationDueToMissedPayments -> { ReminderCardMissingPayment( memberReminder = memberReminder, @@ -405,6 +425,26 @@ private fun ReminderCardConnectPayment( ) } +@Composable +private fun ReminderCardConnectPayout( + memberReminder: MemberReminder, + navigateToConnectPayout: () -> Unit, + modifier: Modifier = Modifier, + minLines: Int = 1, +) { + val message = getMemberReminderMessage(memberReminder) + HedvigNotificationCard( + message = message, + modifier = modifier, + priority = NotificationPriority.Attention, + style = InfoCardStyle.Button( + buttonText = stringResource(Res.string.PAYOUT_ADD_PAYOUT_METHOD), + onButtonClick = navigateToConnectPayout, + ), + minLines = minLines, + ) +} + @Composable private fun ReminderCardMissingPayment( memberReminder: MemberReminder, From 4d7cf2fb75d9c5f1119f0a20121eb113201aff49 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 24 Apr 2026 15:48:33 +0200 Subject: [PATCH 29/50] Update strings --- .../androidMain/res/values-sv-rSE/strings.xml | 20 ++++++++++++----- .../src/androidMain/res/values/strings.xml | 22 ++++++++++++++----- .../values-sv-rSE/strings.xml | 20 ++++++++++++----- .../composeResources/values/strings.xml | 22 ++++++++++++++----- 4 files changed, 62 insertions(+), 22 deletions(-) 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 673f80885c..5dbfd176c7 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 @@ -80,7 +80,7 @@ Skanna QR-koden med BankID-appen på din telefon, eller logga in med din e-postadress nedan. Utbetalning till ett svenskt bankkonto Bankkonto - Clearing + Clearingnummer Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Tack för ditt meddelande. Vi svarar så snart som möjligt. Just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -146,7 +146,7 @@ Ange byggår Byggår Du + %1$d - Ändra konto + Ändra utbetalningskonto Ändra skyddsnivå Chatta med en specialist Om det inte fungerar eller om du har några andra frågor, vänligen meddela oss så hjälper en av våra specialister till inom kort! @@ -608,6 +608,16 @@ Denna betalning misslyckades och lades till i din betalning den %1$s . Betalningshistorik Betalningssätt + Att betala: %1$s + Vi kunde inte dra betalningen från ditt bankkonto. Betala för att undvika avbrott i ditt skydd. + Granska betalning + Betala för att undvika avbrott + Förfallodag + Se till att det finns tillräckliga medel på kontot för att genomföra betalningen + Betala %1$s + Försenad sedan %1$s + Visa betalningsdetaljer + Försenad betalning Betalning genomförd %1$s dagar Hel period @@ -623,9 +633,9 @@ Betalningshistorik Lägg till utbetalningsmetod Direktutbetalning via Faktura - Direktutbetalning via Swish - Direktutbetalning via Trustly - Det verkar som att du saknar utbetalningsmetod. Lägg till den så att vi kan betala ut till dig. + Snabb utbetalning med Swish + Utbetalning via Trustly + Du har inte lagt till någon utbetalningsmetod än. Lägg till en för att vi ska kunna betala ut till dig. Utbetalningskonto Välj utbetalningsmetod Vi behöver tillåtelse att spela in ljud 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 9d06df2d5a..22400fff12 100644 --- a/app/core/core-resources/src/androidMain/res/values/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values/strings.xml @@ -78,9 +78,9 @@ We use AI to help you as quickly as possible. The answers are generated automatically and may not always be correct.\n\nIf necessary, you’ll be connected to our service team to help you further.\n\nThe details of your insurance are outlined in your insurance letter and terms and conditions. AI and automated messages Scan the QR-code with the BankID app on the phone where it’s installed or log in with email using the button below. - Payout via a Swedish bank account + Payout to a Swedish bank account Bank account - Clearing + Clearing number Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Thank you for reaching out to us. We will answer as soon as possible. Hej - just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -146,7 +146,7 @@ Please enter year of construction Year of construction You + %1$d - Change account + Change payout account Change coverage level Talk to a human If it’s not working or if you have any other questions, please let us know and one of our specialists will help out shortly! @@ -608,6 +608,16 @@ This payment failed and was added to your payment on %1$s. Payment history Payment method + Amount due: %1$s + We couldn\'t collect this payment from your bank account. Pay now to keep your coverage active. + Review payment + Pay now to avoid interruption + Payment due + Ensure your account has enough funds to cover this payment + Pay %1$s + Overdue since %1$s + View payment details + Payment overdue Payment successful %1$s days Full period @@ -623,9 +633,9 @@ Payment history Add payout method Direct payment via Invoice - Direct payment via Swish - Direct payment via Trustly - It looks like you are missing payout method. Add it so we can payout to you + Instant payout with Swish + Payout via Trustly + You haven’t added a payout method yet. Add one to receive payouts. Payout account Choose payout method We need permission to record audio 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 1bb3ed2131..ba5e216da6 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 @@ -80,7 +80,7 @@ Skanna QR-koden med BankID-appen på din telefon, eller logga in med din e-postadress nedan. Utbetalning till ett svenskt bankkonto Bankkonto - Clearing + Clearingnummer Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Tack för ditt meddelande. Vi svarar så snart som möjligt. Just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -146,7 +146,7 @@ Ange byggår Byggår Du + %1$d - Ändra konto + Ändra utbetalningskonto Ändra skyddsnivå Chatta med en specialist Om det inte fungerar eller om du har några andra frågor, vänligen meddela oss så hjälper en av våra specialister till inom kort! @@ -608,6 +608,16 @@ Denna betalning misslyckades och lades till i din betalning den %1$s . Betalningshistorik Betalningssätt + Att betala: %1$s + Vi kunde inte dra betalningen från ditt bankkonto. Betala för att undvika avbrott i ditt skydd. + Granska betalning + Betala för att undvika avbrott + Förfallodag + Se till att det finns tillräckliga medel på kontot för att genomföra betalningen + Betala %1$s + Försenad sedan %1$s + Visa betalningsdetaljer + Försenad betalning Betalning genomförd %1$s dagar Hel period @@ -623,9 +633,9 @@ Betalningshistorik Lägg till utbetalningsmetod Direktutbetalning via Faktura - Direktutbetalning via Swish - Direktutbetalning via Trustly - Det verkar som att du saknar utbetalningsmetod. Lägg till den så att vi kan betala ut till dig. + Snabb utbetalning med Swish + Utbetalning via Trustly + Du har inte lagt till någon utbetalningsmetod än. Lägg till en för att vi ska kunna betala ut till dig. Utbetalningskonto Välj utbetalningsmetod Vi behöver tillåtelse att spela in ljud 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 496464cdb6..73bff99a7c 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml @@ -78,9 +78,9 @@ We use AI to help you as quickly as possible. The answers are generated automatically and may not always be correct.\n\nIf necessary, you’ll be connected to our service team to help you further.\n\nThe details of your insurance are outlined in your insurance letter and terms and conditions. AI and automated messages Scan the QR-code with the BankID app on the phone where it’s installed or log in with email using the button below. - Payout via a Swedish bank account + Payout to a Swedish bank account Bank account - Clearing + Clearing number Hej - just nu har vi lunch-stängt i chatten. Vi svarar så fort vi kan när vi öppnar igen klockan 13. Thank you for reaching out to us. We will answer as soon as possible. Hej - just nu är chatten stängd. Vi svarar så fort vi kan när vi öppnar. @@ -146,7 +146,7 @@ Please enter year of construction Year of construction You + %1$d - Change account + Change payout account Change coverage level Talk to a human If it’s not working or if you have any other questions, please let us know and one of our specialists will help out shortly! @@ -608,6 +608,16 @@ This payment failed and was added to your payment on %1$s. Payment history Payment method + Amount due: %1$s + We couldn't collect this payment from your bank account. Pay now to keep your coverage active. + Review payment + Pay now to avoid interruption + Payment due + Ensure your account has enough funds to cover this payment + Pay %1$s + Overdue since %1$s + View payment details + Payment overdue Payment successful %1$s days Full period @@ -623,9 +633,9 @@ Payment history Add payout method Direct payment via Invoice - Direct payment via Swish - Direct payment via Trustly - It looks like you are missing payout method. Add it so we can payout to you + Instant payout with Swish + Payout via Trustly + You haven’t added a payout method yet. Add one to receive payouts. Payout account Choose payout method We need permission to record audio From d14bcda5402c35173168a44ad48d74174d38a777 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 24 Apr 2026 16:41:43 +0200 Subject: [PATCH 30/50] Add 8XXXX number to match Swedbank --- .../hedvig/android/feature/payoutaccount/data/BankNameLookup.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt index 2117996ec2..f11f4c18f8 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt @@ -73,6 +73,7 @@ internal fun bankNameForClearingNumber(clearingNumber: String): String? { 9955 -> "Kommuninvest" 9956 -> "VP Securities" in 9960..9969 -> "Nordea" + in 80000..89999 -> "Swedbank" else -> null } } From 65b056d32331fe8aa97a56d47f2531aeed9a02ae Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 24 Apr 2026 16:41:57 +0200 Subject: [PATCH 31/50] Fix some strings --- .../data/SetupSwishPayoutUseCase.kt | 2 +- .../setupswish/SetupSwishPayoutDestination.kt | 23 ++++++++++++------- .../setupswish/SetupSwishPayoutViewModel.kt | 7 +++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt index 28fadfac5d..944a26f76f 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt @@ -25,7 +25,7 @@ internal class SetupSwishPayoutUseCase( val output = result.paymentMethodSetupSwishPayout when (output.status) { PaymentMethodSetupStatus.FAILED -> { - raise(ErrorMessage(output.error?.message ?: "Failed to set up Swish payout")) + raise(ErrorMessage(output.error?.message)) } else -> { diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt index 61f196b450..c7df826f10 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt @@ -24,6 +24,11 @@ import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import hedvig.resources.CONTACT_INFO_CHANGES_SAVED +import hedvig.resources.ODYSSEY_PHONE_NUMBER_LABEL +import hedvig.resources.Res +import hedvig.resources.TIER_FLOW_COMMIT_PROCESSING_ERROR_DESCRIPTION +import org.jetbrains.compose.resources.stringResource @Composable internal fun SetupSwishPayoutDestination( @@ -53,9 +58,10 @@ private fun SetupSwishPayoutScreen( showedSnackBar: () -> Unit, navigateUp: () -> Unit, ) { + val changesSaved = stringResource(Res.string.CONTACT_INFO_CHANGES_SAVED) LaunchedEffect(uiState.showSuccessSnackBar) { if (!uiState.showSuccessSnackBar) return@LaunchedEffect - globalSnackBarState.show("Changes saved", NotificationPriority.Campaign) + globalSnackBarState.show(changesSaved, NotificationPriority.Campaign) showedSnackBar() } @@ -68,7 +74,7 @@ private fun SetupSwishPayoutScreen( Column(Modifier.padding(horizontal = 16.dp)) { HedvigTextField( state = uiState.phoneNumberState, - labelText = "Phone number", + labelText = stringResource(Res.string.ODYSSEY_PHONE_NUMBER_LABEL), textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Phone, @@ -82,12 +88,13 @@ private fun SetupSwishPayoutScreen( exit = shrinkVertically(), ) { HedvigNotificationCard( - message = uiState.errorMessage ?: "", + message = uiState.errorMessage?.message + ?: stringResource(Res.string.TIER_FLOW_COMMIT_PROCESSING_ERROR_DESCRIPTION), priority = NotificationPriority.Attention, modifier = Modifier - .padding(horizontal = 16.dp) - .padding(top = 4.dp) - .fillMaxWidth(), + .padding(horizontal = 16.dp) + .padding(top = 4.dp) + .fillMaxWidth(), ) } Spacer(Modifier.height(16.dp)) @@ -97,8 +104,8 @@ private fun SetupSwishPayoutScreen( enabled = !uiState.isLoading && uiState.phoneNumberState.text.length >= 10, isLoading = uiState.isLoading, modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), + .fillMaxWidth() + .padding(horizontal = 16.dp), ) Spacer(Modifier.height(16.dp)) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt index c024917d88..a5b84f2f02 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutViewModel.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope @@ -28,7 +29,7 @@ internal sealed interface SetupSwishPayoutEvent { internal data class SetupSwishPayoutUiState( val phoneNumberState: TextFieldState, val isLoading: Boolean, - val errorMessage: String?, + val errorMessage: ErrorMessage?, val showSuccessSnackBar: Boolean, ) @@ -41,7 +42,7 @@ internal class SetupSwishPayoutPresenter( ): SetupSwishPayoutUiState { val phoneNumberState = remember { lastState.phoneNumberState } var isLoading by remember { mutableStateOf(false) } - var errorMessage by remember { mutableStateOf(null) } + var errorMessage by remember { mutableStateOf(null) } var showSuccessSnackBar by remember { mutableStateOf(false) } var saveIteration by remember { mutableStateOf(null) } @@ -53,7 +54,7 @@ internal class SetupSwishPayoutPresenter( setupSwishPayoutUseCase.invoke(currentSave).fold( ifLeft = { isLoading = false - errorMessage = it.message ?: "Something went wrong, please try again" + errorMessage = it saveIteration = null }, ifRight = { From a4ef2d4855862724db86cf04113450ce0261a652 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 24 Apr 2026 17:00:03 +0200 Subject: [PATCH 32/50] Move bank name to the field's header instead As per the design request --- .../EditBankAccountDestination.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index db950d5327..de26619f7e 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -30,6 +30,7 @@ import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import hedvig.resources.PAYMENTS_ACCOUNT +import hedvig.resources.general_save_button @Composable internal fun EditBankAccountDestination( @@ -74,15 +75,16 @@ private fun EditBankAccountScreen( Column(Modifier.padding(horizontal = 16.dp)) { HedvigTextField( state = uiState.clearingNumberState, - labelText = stringResource(Res.string.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL), + labelText = buildString { + append(stringResource(Res.string.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL)) + if (uiState.bankName != null) { + append(" ") + append(uiState.bankName) + } + }, textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, inputTransformation = uiState.clearingInputTransformation, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - ), - trailingContent = uiState.bankName?.let { - { HedvigText(text = it) } - }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(4.dp)) @@ -113,7 +115,7 @@ private fun EditBankAccountScreen( } Spacer(Modifier.height(16.dp)) HedvigButton( - text = "Save", + text = stringResource(Res.string.general_save_button), onClick = onSave, enabled = !uiState.isLoading && uiState.clearingNumberState.text.isNotBlank() && From 9b7d78face61d86521ad7a211219a4ea7c11c508 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 24 Apr 2026 17:09:20 +0200 Subject: [PATCH 33/50] Only allow submitting a new bank account with long enough input --- .../EditBankAccountDestination.kt | 4 +-- .../EditBankAccountViewModel.kt | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index de26619f7e..53c4edd4c0 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -117,9 +117,7 @@ private fun EditBankAccountScreen( HedvigButton( text = stringResource(Res.string.general_save_button), onClick = onSave, - enabled = !uiState.isLoading && - uiState.clearingNumberState.text.isNotBlank() && - uiState.accountNumberState.text.isNotBlank(), + enabled = uiState.canSave, isLoading = uiState.isLoading, modifier = Modifier .fillMaxWidth() diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index 2c1a5af266..7f027c16f0 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -22,16 +22,16 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class EditBankAccountViewModel( setupNordeaPayoutUseCase: SetupNordeaPayoutUseCase, ) : MoleculeViewModel( - EditBankAccountUiState( - clearingNumberState = TextFieldState(), - accountNumberState = TextFieldState(), - bankName = null, - isLoading = false, - errorMessage = null, - showSuccessSnackBar = false, - ), - EditBankAccountPresenter(setupNordeaPayoutUseCase), - ) + EditBankAccountUiState( + clearingNumberState = TextFieldState(), + accountNumberState = TextFieldState(), + bankName = null, + isLoading = false, + errorMessage = null, + showSuccessSnackBar = false, + ), + EditBankAccountPresenter(setupNordeaPayoutUseCase), +) internal sealed interface EditBankAccountEvent { data object Save : EditBankAccountEvent @@ -47,6 +47,11 @@ internal data class EditBankAccountUiState( val errorMessage: String?, val showSuccessSnackBar: Boolean, ) { + val canSave: Boolean + get() = !isLoading && + clearingNumberState.text.length >= 4 && + accountNumberState.text.length == 10 + // Swedish clearing numbers are 4 digits for most banks, 5 for Swedbank's 8-series val clearingInputTransformation: InputTransformation = InputTransformation.maxLength(5).digitsOnly() From 30b4adb17b54780047be9a199f44cb05dde6580e Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Mon, 27 Apr 2026 14:57:04 +0200 Subject: [PATCH 34/50] Match payout button position to iOS impl --- .../ui/payments/PaymentsDestination.kt | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt index 99994730ce..6455ad1a89 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/payments/PaymentsDestination.kt @@ -93,6 +93,7 @@ import hedvig.resources.PAYMENTS_PAYMENT_DETAILS_INFO_TITLE import hedvig.resources.PAYMENTS_PAYMENT_HISTORY_BUTTON_LABEL import hedvig.resources.PAYMENTS_PROCESSING_PAYMENT import hedvig.resources.PAYMENTS_UPCOMING_PAYMENT +import hedvig.resources.PAYOUT_PAGE_HEADING import hedvig.resources.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_TITLE import hedvig.resources.R import hedvig.resources.Res @@ -314,7 +315,7 @@ private fun PaymentsContent( is ConnectedPaymentInfo.NeedsSetup, ConnectedPaymentInfo.Unknown, is ConnectedPaymentInfo.Active, - -> { + -> { } } } @@ -407,24 +408,6 @@ private fun PaymentsListItems( .padding(vertical = 16.dp) .fillMaxWidth(), ) - if (showPayoutButton) { - HorizontalDivider(modifier = listItemsSideSpacingModifier) - PaymentsListItem( - text = "Payout", - icon = { - Icon( - imageVector = HedvigIcons.PaymentOutline, - contentDescription = null, - modifier = Modifier.size(24.dp), - ) - }, - modifier = Modifier - .clickable(onClick = onPayoutAccountClicked) - .then(listItemsSideSpacingModifier) - .padding(vertical = 16.dp) - .fillMaxWidth(), - ) - } HorizontalDivider(modifier = listItemsSideSpacingModifier) PaymentsListItem( text = stringResource(Res.string.PAYMENTS_PAYMENT_HISTORY_BUTTON_LABEL), @@ -461,6 +444,24 @@ private fun PaymentsListItems( ) } } + if (showPayoutButton) { + HorizontalDivider(modifier = listItemsSideSpacingModifier) + PaymentsListItem( + text = stringResource(Res.string.PAYOUT_PAGE_HEADING), + icon = { + Icon( + imageVector = HedvigIcons.PaymentOutline, + contentDescription = null, + modifier = Modifier.size(24.dp), + ) + }, + modifier = Modifier + .clickable(onClick = onPayoutAccountClicked) + .then(listItemsSideSpacingModifier) + .padding(vertical = 16.dp) + .fillMaxWidth(), + ) + } } } From 6679fe1649db404237f3c362bce6fc4a1a202033 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Mon, 27 Apr 2026 15:25:40 +0200 Subject: [PATCH 35/50] Update schema --- .../android/apollo/octopus/schema.graphqls | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 8542f80023..a3f1209237 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 @@ -2459,6 +2459,8 @@ type Member { claims: [Claim!]! claimsActive: [Claim!]! claimsHistory: [Claim!]! + partnerClaimsActive: [PartnerClaim!]! + partnerClaimsHistory: [PartnerClaim!]! firstName: String! lastName: String! ssn: String @@ -3478,6 +3480,7 @@ input MoveToHouseInput { } type Mutation { registerDirectDebit2(clientContext: RegisterDirectDebitClientContext2): DirectDebitResponse2! + manuallyChargeMember: Date! """ Setup invoice payment method for the member. Kivra will be used as the provider if supported, else mail. """ @@ -3771,6 +3774,22 @@ type Mutation { """ upsellTravelAddonActivate(quoteId: ID!, addonId: ID!): UpsellTravelAddonActivationOutput! } +type PartnerClaim { + id: ID! + externalId: String! + exposureDisplayName: String + status: ClaimStatus + submittedAt: Date + payoutAmount: Money + associatedTypeOfContract: String + claimType: String + handlerEmail: String + displayItems: [ClaimDisplayItem!]! + """ + Terms & conditions for the claim found using claims contractId and dateOfOccurrence, otherwise null. + """ + productVariant: ProductVariant +} type PartnerData { sas: SasPartnerData } @@ -4439,6 +4458,7 @@ type Query { """ conversation(id: UUID!): Conversation claim(id: ID!): Claim + partnerClaim(id: ID!): PartnerClaim claimIntent(id: ID!): ClaimIntent! claimIntentFormFieldSearch(input: ClaimIntentFormFieldSearchInput!): ClaimIntentFormFieldSearchOutput! personalInformation(input: PersonalInformationInput!): PersonalInformation From 32ee77308b7afebf10b2d83e6ddc28d9efcc6c3d Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Mon, 27 Apr 2026 18:39:54 +0200 Subject: [PATCH 36/50] Use bank account details for Trustly payout option too --- .../data/FakePayoutAccountStorage.kt | 7 ++- .../data/GetPayoutAccountUseCase.kt | 61 +++++++++++++------ .../payoutaccount/data/PayoutAccount.kt | 7 ++- .../EditBankAccountDestination.kt | 8 +-- .../EditBankAccountViewModel.kt | 20 +++--- .../PayoutAccountOverviewDestination.kt | 40 ++++++------ .../setupswish/SetupSwishPayoutDestination.kt | 10 +-- 7 files changed, 97 insertions(+), 56 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt index 7419d85d5e..b6f342222e 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt @@ -9,5 +9,10 @@ import octopus.type.PaymentMethodInvoiceDelivery // PayoutAccount.BankAccount("8327", "12345678", "Swedbank") → Bank account display // PayoutAccount.Invoice(PaymentMethodInvoiceDelivery.KIVRA, null) → Invoice display internal object FakePayoutAccountStorage { - var currentMethod: PayoutAccount? = PayoutAccount.Trustly(isPending = false) + var currentMethod: PayoutAccount? = PayoutAccount.Trustly( + clearingNumber = null, + accountNumber = null, + bankName = null, + isPending = false, + ) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index 03f611580c..63cfb8d22a 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -45,39 +45,43 @@ internal class GetPayoutAccountUseCase( val currentMethod = defaultPayoutMethod?.let { method -> val isPending = method.status == MemberPaymentMethodStatus.PENDING when (method.provider) { - MemberPaymentProvider.TRUSTLY -> { - PayoutAccount.Trustly(isPending = isPending) - } - MemberPaymentProvider.SWISH -> { val phoneNumber = method.details?.asPaymentMethodSwishDetails()?.phoneNumber PayoutAccount.SwishPayout(phoneNumber = phoneNumber, isPending = isPending) } - MemberPaymentProvider.NORDEA -> { - val bankAccountDetails = method.details?.asPaymentMethodBankAccountDetails() - val account = bankAccountDetails?.account - val dashIndex = account?.indexOf('-') ?: -1 - val clearingNumber = if (dashIndex >= 0) account?.substring(0, dashIndex) else account - val accountNumber = if (dashIndex >= 0) account?.substring(dashIndex + 1) else null - PayoutAccount.BankAccount( + MemberPaymentProvider.INVOICE -> { + val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() + PayoutAccount.Invoice( + delivery = invoiceDetails?.delivery, + email = invoiceDetails?.email, + isPending = isPending, + ) + } + + MemberPaymentProvider.TRUSTLY -> { + val (clearingNumber, accountNumber, bankName) = parseBankAccountDetails(method) + PayoutAccount.Trustly( clearingNumber = clearingNumber, accountNumber = accountNumber, - bankName = bankAccountDetails?.bank, + bankName = bankName, isPending = isPending, ) } - MemberPaymentProvider.INVOICE -> { - val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() - PayoutAccount.Invoice( - delivery = invoiceDetails?.delivery, - email = invoiceDetails?.email, + MemberPaymentProvider.NORDEA -> { + val (clearingNumber, accountNumber, bankName) = parseBankAccountDetails(method) + PayoutAccount.BankAccount( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankName, isPending = isPending, ) } - else -> null + else -> { + null + } } } @@ -91,3 +95,24 @@ internal class GetPayoutAccountUseCase( ) } } + +private data class ParsedBankAccountDetails( + val clearingNumber: String?, + val accountNumber: String?, + val bankName: String?, +) + +private fun parseBankAccountDetails( + method: GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.PayoutMethod, +): ParsedBankAccountDetails { + val bankAccountDetails = method.details?.asPaymentMethodBankAccountDetails() + val account = bankAccountDetails?.account + val dashIndex = account?.indexOf('-') ?: -1 + val clearingNumber = if (dashIndex >= 0) account?.substring(0, dashIndex) else account + val accountNumber = if (dashIndex >= 0) account?.substring(dashIndex + 1) else null + return ParsedBankAccountDetails( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankAccountDetails?.bank, + ) +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt index 28a52dcd8f..8a7b7b3bdc 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt @@ -5,7 +5,12 @@ import octopus.type.PaymentMethodInvoiceDelivery internal sealed interface PayoutAccount { val isPending: Boolean - data class Trustly(override val isPending: Boolean) : PayoutAccount + data class Trustly( + val clearingNumber: String?, + val accountNumber: String?, + val bankName: String?, + override val isPending: Boolean, + ) : PayoutAccount data class SwishPayout( val phoneNumber: String?, diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index 53c4edd4c0..338beaa5ab 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -18,10 +18,6 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.design.system.hedvig.GlobalSnackBarState -import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE -import hedvig.resources.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL -import hedvig.resources.Res -import org.jetbrains.compose.resources.stringResource import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigScaffold @@ -29,8 +25,12 @@ import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE +import hedvig.resources.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL import hedvig.resources.PAYMENTS_ACCOUNT +import hedvig.resources.Res import hedvig.resources.general_save_button +import org.jetbrains.compose.resources.stringResource @Composable internal fun EditBankAccountDestination( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index 7f027c16f0..466b6b7787 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -22,16 +22,16 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class EditBankAccountViewModel( setupNordeaPayoutUseCase: SetupNordeaPayoutUseCase, ) : MoleculeViewModel( - EditBankAccountUiState( - clearingNumberState = TextFieldState(), - accountNumberState = TextFieldState(), - bankName = null, - isLoading = false, - errorMessage = null, - showSuccessSnackBar = false, - ), - EditBankAccountPresenter(setupNordeaPayoutUseCase), -) + EditBankAccountUiState( + clearingNumberState = TextFieldState(), + accountNumberState = TextFieldState(), + bankName = null, + isLoading = false, + errorMessage = null, + showSuccessSnackBar = false, + ), + EditBankAccountPresenter(setupNordeaPayoutUseCase), + ) internal sealed interface EditBankAccountEvent { data object Save : EditBankAccountEvent diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 9a2a878def..43d7c0aea4 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -108,7 +108,10 @@ private fun PayoutAccountContent( } is PayoutAccount.Trustly -> { - PayoutAccountReadOnlyTextField(label = "Account", text = "Trustly") + PayoutAccountReadOnlyTextField( + label = formatBankAccountLabel(stringResource(Res.string.PAYMENTS_ACCOUNT), currentMethod.bankName), + text = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber), + ) } is PayoutAccount.Invoice -> { @@ -117,21 +120,8 @@ private fun PayoutAccountContent( is PayoutAccount.BankAccount -> { PayoutAccountReadOnlyTextField( - label = stringResource(Res.string.PAYMENTS_ACCOUNT), - text = buildString { - val hasBankNumber = currentMethod.clearingNumber != null && currentMethod.accountNumber != null - if (currentMethod.bankName != null) { - this.append(currentMethod.bankName) - if (hasBankNumber) { - this.append(" ") - } - } - if (hasBankNumber) { - this.append(currentMethod.clearingNumber) - this.append("-") - this.append(currentMethod.accountNumber) - } - }, + label = formatBankAccountLabel(stringResource(Res.string.PAYMENTS_ACCOUNT), currentMethod.bankName), + text = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber), ) } } @@ -166,6 +156,17 @@ private fun PayoutAccountReadOnlyTextField(label: String, text: String, modifier ) } +private fun formatBankAccountLabel(baseLabel: String, bankName: String?): String { + return if (bankName != null) "$baseLabel - $bankName" else baseLabel +} + +private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: String?): String { + return when { + clearingNumber != null && accountNumber != null -> "$clearingNumber-$accountNumber" + else -> clearingNumber.orEmpty() + } +} + @Composable @HedvigPreview private fun PreviewPayoutAccountOverviewScreen( @@ -200,7 +201,12 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), Content( - currentMethod = PayoutAccount.Trustly(isPending = false), + currentMethod = PayoutAccount.Trustly( + clearingNumber = "8327", + accountNumber = "12345678", + bankName = "Mock Swedbank", + isPending = false, + ), availablePayoutMethods = listOf(MemberPaymentProvider.TRUSTLY), ), Content( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt index c7df826f10..32a78385e1 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupswish/SetupSwishPayoutDestination.kt @@ -92,9 +92,9 @@ private fun SetupSwishPayoutScreen( ?: stringResource(Res.string.TIER_FLOW_COMMIT_PROCESSING_ERROR_DESCRIPTION), priority = NotificationPriority.Attention, modifier = Modifier - .padding(horizontal = 16.dp) - .padding(top = 4.dp) - .fillMaxWidth(), + .padding(horizontal = 16.dp) + .padding(top = 4.dp) + .fillMaxWidth(), ) } Spacer(Modifier.height(16.dp)) @@ -104,8 +104,8 @@ private fun SetupSwishPayoutScreen( enabled = !uiState.isLoading && uiState.phoneNumberState.text.length >= 10, isLoading = uiState.isLoading, modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), + .fillMaxWidth() + .padding(horizontal = 16.dp), ) Spacer(Modifier.height(16.dp)) } From 06f653daa334b058bd8b91f8b92f390302404b45 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:10:31 +0200 Subject: [PATCH 37/50] Build payout feature for test session --- .github/workflows/staging.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 316c4105eb..37c7accedd 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -4,6 +4,7 @@ on: branches: - develop - feat/pet-chip-id + - feature/payment-methods workflow_dispatch: concurrency: From 011789b5352eb02afb2b89ba7570c2e6a7538d55 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:10:52 +0200 Subject: [PATCH 38/50] Add fixed "Trustly" text in payout account overview --- .../ui/overview/PayoutAccountOverviewDestination.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 43d7c0aea4..dd6e5a01de 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -109,7 +109,7 @@ private fun PayoutAccountContent( is PayoutAccount.Trustly -> { PayoutAccountReadOnlyTextField( - label = formatBankAccountLabel(stringResource(Res.string.PAYMENTS_ACCOUNT), currentMethod.bankName), + label = formatBankAccountLabel("Trustly", currentMethod.bankName), text = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber), ) } From 8e00601f7c9ba44a8177967dec8a470ac79d8c77 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:11:16 +0200 Subject: [PATCH 39/50] Add dash between bank name and text field title --- .../ui/editbankaccount/EditBankAccountDestination.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index 338beaa5ab..4a4018b0f5 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -78,7 +78,7 @@ private fun EditBankAccountScreen( labelText = buildString { append(stringResource(Res.string.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL)) if (uiState.bankName != null) { - append(" ") + append(" - ") append(uiState.bankName) } }, From e7cc8b74e2eece98766940d71a64f1b59d284774 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:15:38 +0200 Subject: [PATCH 40/50] Show "something went wrong" if the error is present but blank --- .../ui/editbankaccount/EditBankAccountDestination.kt | 4 +++- .../ui/editbankaccount/EditBankAccountViewModel.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index 4a4018b0f5..279ef36913 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -1,5 +1,6 @@ package com.hedvig.android.feature.payoutaccount.ui.editbankaccount +import android.R.attr.priority import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically @@ -30,6 +31,7 @@ import hedvig.resources.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL import hedvig.resources.PAYMENTS_ACCOUNT import hedvig.resources.Res import hedvig.resources.general_save_button +import hedvig.resources.something_went_wrong import org.jetbrains.compose.resources.stringResource @Composable @@ -105,7 +107,7 @@ private fun EditBankAccountScreen( exit = shrinkVertically(), ) { HedvigNotificationCard( - message = uiState.errorMessage ?: "", + message = uiState.errorMessage?.ifBlank { stringResource(Res.string.something_went_wrong) }.orEmpty(), priority = NotificationPriority.Attention, modifier = Modifier .padding(horizontal = 16.dp) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index 466b6b7787..62d940594b 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -82,7 +82,7 @@ internal class EditBankAccountPresenter( setupNordeaPayoutUseCase.invoke(currentSave.first, currentSave.second).fold( ifLeft = { isLoading = false - errorMessage = it.message ?: "Something went wrong, please try again" + errorMessage = it.message ?: "" saveIteration = null }, ifRight = { From 17c9e6c78c4ead37ce7e3e24fe298425a7341d8d Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:15:49 +0200 Subject: [PATCH 41/50] Add payout deep link --- .../android/app/navigation/HedvigNavHost.kt | 1 + .../feature-payout-account/build.gradle.kts | 1 + .../navigation/PayoutAccountGraph.kt | 4 ++ .../core/HedvigDeepLinkContainer.kt | 57 ++++++++++--------- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 0e85b6b0bb..f89d0d6a46 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -350,6 +350,7 @@ internal fun HedvigNavHost( payoutAccountGraph( navController = navController, globalSnackBarState = globalSnackBarState, + hedvigDeepLinkContainer = hedvigDeepLinkContainer, navigateToTrustlyPayout = { builder -> navController.navigate(TrustlyDestination, builder) }, diff --git a/app/feature/feature-payout-account/build.gradle.kts b/app/feature/feature-payout-account/build.gradle.kts index 73a55cf270..105feaab33 100644 --- a/app/feature/feature-payout-account/build.gradle.kts +++ b/app/feature/feature-payout-account/build.gradle.kts @@ -30,4 +30,5 @@ dependencies { implementation(projects.navigationCommon) implementation(projects.navigationCompose) implementation(projects.navigationComposeTyped) + implementation(projects.navigationCore) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index 331f95c077..3eb349be2d 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -15,21 +15,25 @@ import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayo import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayoutViewModel import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutDestination import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel +import com.hedvig.android.navigation.compose.navDeepLinks import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.compose.navgraph import com.hedvig.android.navigation.compose.typedPopBackStack import com.hedvig.android.navigation.compose.typedPopUpTo +import com.hedvig.android.navigation.core.HedvigDeepLinkContainer import octopus.type.MemberPaymentProvider import org.koin.compose.viewmodel.koinViewModel fun NavGraphBuilder.payoutAccountGraph( navController: NavController, globalSnackBarState: GlobalSnackBarState, + hedvigDeepLinkContainer: HedvigDeepLinkContainer, navigateToTrustlyPayout: (builder: NavOptionsBuilder.() -> Unit) -> Unit, navigateUp: () -> Unit, ) { navgraph( startDestination = PayoutAccountDestinations.Overview::class, + deepLinks = navDeepLinks(hedvigDeepLinkContainer.payout) ) { navdestination { val viewModel: PayoutAccountOverviewViewModel = koinViewModel() diff --git a/app/navigation/navigation-core/src/commonMain/kotlin/com/hedvig/android/navigation/core/HedvigDeepLinkContainer.kt b/app/navigation/navigation-core/src/commonMain/kotlin/com/hedvig/android/navigation/core/HedvigDeepLinkContainer.kt index 650003a628..17b59f672a 100644 --- a/app/navigation/navigation-core/src/commonMain/kotlin/com/hedvig/android/navigation/core/HedvigDeepLinkContainer.kt +++ b/app/navigation/navigation-core/src/commonMain/kotlin/com/hedvig/android/navigation/core/HedvigDeepLinkContainer.kt @@ -43,6 +43,7 @@ interface HedvigDeepLinkContainer { val directDebit: List // Same as connectPayment but to support an old link to it val eurobonus: List // The destination allowing to edit your current Eurobonus (SAS) number val payments: List // The payments screen, showing the payments history and the upcoming payment information + val payout: List // Payout connection overview screen, can see existing options and change/add payout options val deleteAccount: List // The screen where the member may request for their account data to be GDPR wiped // The screen where one can change their contact information, like their email and phone. @@ -141,6 +142,7 @@ internal class HedvigDeepLinkContainerImpl( "$baseDeepLinkDomain/eurobonus" } override val payments: List = baseDeepLinkDomains.map { baseDeepLinkDomain -> "$baseDeepLinkDomain/payments" } + override val payout: List = baseDeepLinkDomains.map { baseDeepLinkDomain -> "$baseDeepLinkDomain/payout" } override val deleteAccount: List = baseDeepLinkDomains.map { baseDeepLinkDomain -> "$baseDeepLinkDomain/delete-account" } @@ -200,39 +202,40 @@ internal class HedvigDeepLinkContainerImpl( val HedvigDeepLinkContainer.allDeepLinkUriPatterns: List get() = listOf( - home.first(), - helpCenter.first(), - helpCenterCommonTopic.first(), - helpCenterQuestion.first(), - insurances.first(), + carAddon.first(), + carAddonWithContractId.first(), + changeTierWithContractId.first(), + changeTierWithoutContractId.first(), + chat.first(), + claimDetails.first(), + claimFlow.first(), + connectPayment.first(), + contactInfo.first(), contract.first(), contractWithoutContractId.first(), + conversation.first(), + deleteAccount.first(), + directDebit.first(), editCoInsured.first(), editCoInsuredWithoutContractId.first(), - terminateInsurance.first(), - forever.first(), - profile.first(), - connectPayment.first(), - directDebit.first(), + editCoOwners.first(), eurobonus.first(), - payments.first(), - deleteAccount.first(), - contactInfo.first(), - chat.first(), + forever.first(), + helpCenter.first(), + helpCenterCommonTopic.first(), + helpCenterQuestion.first(), + home.first(), inbox.first(), - conversation.first(), - travelAddon.first(), - travelCertificate.first(), - changeTierWithoutContractId.first(), - changeTierWithContractId.first(), - claimDetails.first(), insuranceEvidence.first(), - claimFlow.first(), + insurances.first(), moveContract.first(), - editCoOwners.first(), - carAddon.first(), - carAddonWithContractId.first(), - travelAddonWithContractId.first(), - petIdWithoutContractId.first(), + payments.first(), + payout.first(), petIdWithContractId.first(), - ) + petIdWithoutContractId.first(), + profile.first(), + terminateInsurance.first(), + travelAddon.first(), + travelAddonWithContractId.first(), + travelCertificate.first(), + ) From 9e567a144971714aa76937df6b2e4df3356f2c2d Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:16:02 +0200 Subject: [PATCH 42/50] Clear error message on new input --- .../ui/editbankaccount/EditBankAccountViewModel.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index 62d940594b..348af2b320 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCase import com.hedvig.android.feature.payoutaccount.data.bankNameForClearingNumber import com.hedvig.android.molecule.public.MoleculePresenter @@ -94,6 +95,17 @@ internal class EditBankAccountPresenter( } } + LaunchedEffect(clearingNumberState) { + snapshotFlow { clearingNumberState.text }.collect { + errorMessage = null + } + } + LaunchedEffect(accountNumberState) { + snapshotFlow { accountNumberState.text }.collect { + errorMessage = null + } + } + CollectEvents { event -> when (event) { EditBankAccountEvent.Save -> { From afa3d04eab1c7738da05aaa0a1ad5dc231fdd62e Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:16:17 +0200 Subject: [PATCH 43/50] Account number should be in the range of 6..12 chars according to backend --- .../EditBankAccountViewModel.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index 348af2b320..d4032daf85 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -23,16 +23,16 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class EditBankAccountViewModel( setupNordeaPayoutUseCase: SetupNordeaPayoutUseCase, ) : MoleculeViewModel( - EditBankAccountUiState( - clearingNumberState = TextFieldState(), - accountNumberState = TextFieldState(), - bankName = null, - isLoading = false, - errorMessage = null, - showSuccessSnackBar = false, - ), - EditBankAccountPresenter(setupNordeaPayoutUseCase), - ) + EditBankAccountUiState( + clearingNumberState = TextFieldState(), + accountNumberState = TextFieldState(), + bankName = null, + isLoading = false, + errorMessage = null, + showSuccessSnackBar = false, + ), + EditBankAccountPresenter(setupNordeaPayoutUseCase), +) internal sealed interface EditBankAccountEvent { data object Save : EditBankAccountEvent @@ -51,7 +51,7 @@ internal data class EditBankAccountUiState( val canSave: Boolean get() = !isLoading && clearingNumberState.text.length >= 4 && - accountNumberState.text.length == 10 + accountNumberState.text.length in 6..12 // Swedish clearing numbers are 4 digits for most banks, 5 for Swedbank's 8-series val clearingInputTransformation: InputTransformation = InputTransformation.maxLength(5).digitsOnly() From e31a643b208705ed787810842b2be201c2ed8237 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:17:24 +0200 Subject: [PATCH 44/50] Delete temp FakePayoutAccountStorage --- .../data/FakePayoutAccountStorage.kt | 18 ------------------ .../data/GetPayoutAccountUseCase.kt | 9 --------- .../data/SetupInvoicePayoutUseCase.kt | 6 ------ .../data/SetupNordeaPayoutUseCase.kt | 7 ------- .../data/SetupSwishPayoutUseCase.kt | 2 -- 5 files changed, 42 deletions(-) delete mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt deleted file mode 100644 index b6f342222e..0000000000 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/FakePayoutAccountStorage.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.hedvig.android.feature.payoutaccount.data - -import octopus.type.PaymentMethodInvoiceDelivery - -// Swap the initial value to test each display scenario in the Overview: -// null → "Connect payout account" button -// PayoutAccount.Trustly → Trustly display -// PayoutAccount.SwishPayout("0701234567") → Swish display -// PayoutAccount.BankAccount("8327", "12345678", "Swedbank") → Bank account display -// PayoutAccount.Invoice(PaymentMethodInvoiceDelivery.KIVRA, null) → Invoice display -internal object FakePayoutAccountStorage { - var currentMethod: PayoutAccount? = PayoutAccount.Trustly( - clearingNumber = null, - accountNumber = null, - bankName = null, - isPending = false, - ) -} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt index 63cfb8d22a..3ad7b40f77 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -24,15 +24,6 @@ internal class GetPayoutAccountUseCase( private val apolloClient: ApolloClient, ) { suspend fun invoke(): Either = either { - return@either PayoutAccountData( - currentMethod = FakePayoutAccountStorage.currentMethod, - availablePayoutMethods = listOf( - MemberPaymentProvider.TRUSTLY, - MemberPaymentProvider.NORDEA, - MemberPaymentProvider.SWISH, - MemberPaymentProvider.INVOICE, - ), - ) val result = apolloClient .query(GetPayoutMethodsQuery()) .fetchPolicy(FetchPolicy.NetworkOnly) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt index e42742a87b..5502cb9051 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt @@ -16,12 +16,6 @@ internal class SetupInvoicePayoutUseCase( private val networkCacheManager: NetworkCacheManager, ) { suspend fun invoke(): Either = either { - FakePayoutAccountStorage.currentMethod = PayoutAccount.Invoice( - delivery = PaymentMethodInvoiceDelivery.KIVRA, - email = null, - isPending = false, - ) - return@either val result = apolloClient .mutation(SetupInvoicePayoutMutation()) .safeExecute(::ErrorMessage) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt index a1478f6683..96c7d79b68 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt @@ -17,13 +17,6 @@ internal class SetupNordeaPayoutUseCase( private val networkCacheManager: NetworkCacheManager, ) { suspend fun invoke(clearingNumber: String, accountNumber: String): Either = either { - FakePayoutAccountStorage.currentMethod = PayoutAccount.BankAccount( - clearingNumber = clearingNumber, - accountNumber = accountNumber, - bankName = bankNameForClearingNumber(clearingNumber), - isPending = false, - ) - return@either val result = apolloClient .mutation(SetupNordeaPayoutMutation(clearingNumber = clearingNumber, accountNumber = accountNumber)) .safeExecute(::ErrorMessage) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt index 944a26f76f..47bbc91c10 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupSwishPayoutUseCase.kt @@ -15,8 +15,6 @@ internal class SetupSwishPayoutUseCase( private val networkCacheManager: NetworkCacheManager, ) { suspend fun invoke(phoneNumber: String): Either = either { - FakePayoutAccountStorage.currentMethod = PayoutAccount.SwishPayout(phoneNumber = phoneNumber, isPending = false) - return@either val result = apolloClient .mutation(SetupSwishPayoutMutation(phoneNumber = phoneNumber)) .safeExecute(::ErrorMessage) From f0285f5ed07827ac2b90bdbc42e8f617fbe8deb3 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 13:45:07 +0200 Subject: [PATCH 45/50] Show an error section with a back button when no payout methods are available, and hide the connect/change button accordingly. --- .../android/app/navigation/HedvigNavHost.kt | 1 + .../navigation/PayoutAccountGraph.kt | 2 + .../PayoutAccountOverviewDestination.kt | 44 +++++++++++++------ .../PayoutAccountOverviewViewModel.kt | 3 +- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index f89d0d6a46..47ebd50893 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -354,6 +354,7 @@ internal fun HedvigNavHost( navigateToTrustlyPayout = { builder -> navController.navigate(TrustlyDestination, builder) }, + navigateBack = popBackStackOrFinish, navigateUp = navController::navigateUp, ) profileGraph( diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index 3eb349be2d..14bb32cacd 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt @@ -29,6 +29,7 @@ fun NavGraphBuilder.payoutAccountGraph( globalSnackBarState: GlobalSnackBarState, hedvigDeepLinkContainer: HedvigDeepLinkContainer, navigateToTrustlyPayout: (builder: NavOptionsBuilder.() -> Unit) -> Unit, + navigateBack: () -> Unit, navigateUp: () -> Unit, ) { navgraph( @@ -47,6 +48,7 @@ fun NavGraphBuilder.payoutAccountGraph( ), ) }, + navigateBack = navigateBack, navigateUp = navigateUp, ) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index dd6e5a01de..1ef57e5bf2 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -30,6 +30,7 @@ import hedvig.resources.PAYMENTS_ACCOUNT import hedvig.resources.PAYOUT_PAGE_HEADING import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD import hedvig.resources.Res +import hedvig.resources.general_back_button import octopus.type.MemberPaymentProvider import octopus.type.PaymentMethodInvoiceDelivery import org.jetbrains.compose.resources.stringResource @@ -38,6 +39,7 @@ import org.jetbrains.compose.resources.stringResource internal fun PayoutAccountOverviewDestination( viewModel: PayoutAccountOverviewViewModel, onConnectPayoutMethodClicked: () -> Unit, + navigateBack: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -45,6 +47,7 @@ internal fun PayoutAccountOverviewDestination( uiState = uiState, onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, onRetry = { viewModel.emit(PayoutAccountOverviewEvent.Retry) }, + navigateBack = navigateBack, navigateUp = navigateUp, ) } @@ -54,6 +57,7 @@ private fun PayoutAccountOverviewScreen( uiState: PayoutAccountOverviewUiState, onConnectPayoutMethodClicked: () -> Unit, onRetry: () -> Unit, + navigateBack: () -> Unit, navigateUp: () -> Unit, ) { HedvigScaffold( @@ -84,6 +88,7 @@ private fun PayoutAccountOverviewScreen( currentMethod = uiState.currentMethod, availablePayoutMethods = uiState.availablePayoutMethods, onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, + navigateBack = navigateBack, modifier = Modifier.weight(1f), ) } @@ -96,12 +101,23 @@ private fun PayoutAccountContent( currentMethod: PayoutAccount?, availablePayoutMethods: List, onConnectPayoutMethodClicked: () -> Unit, + navigateBack: () -> Unit, modifier: Modifier = Modifier, ) { Column(modifier) { Spacer(Modifier.height(8.dp)) when (currentMethod) { - null -> {} + null -> { + if (availablePayoutMethods.isEmpty()) { + Spacer(Modifier.weight(1f)) + HedvigErrorSection( + title = "todo copy title", + subTitle = "todo copy subtitle", + buttonText = stringResource(Res.string.general_back_button), + onButtonClick = navigateBack, + ) + } + } is PayoutAccount.SwishPayout -> { PayoutAccountReadOnlyTextField(label = "Swish", text = currentMethod.phoneNumber.orEmpty()) @@ -126,18 +142,20 @@ private fun PayoutAccountContent( } } Spacer(Modifier.weight(1f)) - HedvigButton( - text = if (currentMethod == null) { - stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD) - } else { - stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL) - }, - onClick = onConnectPayoutMethodClicked, - enabled = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) + if (availablePayoutMethods.isNotEmpty()) { + HedvigButton( + text = if (currentMethod == null) { + stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD) + } else { + stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL) + }, + onClick = onConnectPayoutMethodClicked, + enabled = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } Spacer(Modifier.height(16.dp)) } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt index 61bfcb5b63..e7ef472f82 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewViewModel.kt @@ -3,6 +3,7 @@ package com.hedvig.android.feature.payoutaccount.ui.overview import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -42,7 +43,7 @@ internal class PayoutAccountOverviewPresenter( override fun MoleculePresenterScope.present( lastState: PayoutAccountOverviewUiState, ): PayoutAccountOverviewUiState { - var loadIteration by remember { mutableStateOf(0) } + var loadIteration by remember { mutableIntStateOf(0) } var uiState by remember { mutableStateOf(lastState) } LaunchedEffect(loadIteration) { From 93b276fb82129f41883475bac4795b696519ca92 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 14:14:13 +0200 Subject: [PATCH 46/50] Fix missing parameter --- .../ui/overview/PayoutAccountOverviewDestination.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 1ef57e5bf2..2da89c2cb6 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -197,6 +197,7 @@ private fun PreviewPayoutAccountOverviewScreen( onConnectPayoutMethodClicked = {}, onRetry = {}, navigateUp = {}, + navigateBack = {}, ) } } From 74c9616058491251ffee23dd103e3c9eb79c690b Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 17:01:05 +0200 Subject: [PATCH 47/50] Add placeholder "something went wrong" text on empty state Context: https://hedviginsurance.slack.com/archives/C08L6HMNZRD/p1777381214108389 --- .../overview/PayoutAccountOverviewDestination.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 2da89c2cb6..0320be601e 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -1,5 +1,6 @@ package com.hedvig.android.feature.payoutaccount.ui.overview +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -17,20 +18,24 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigErrorSection import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProgressDebounced +import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface import com.hedvig.android.feature.payoutaccount.data.PayoutAccount import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState.Content import hedvig.resources.CHANGE_PAYOUT_METHOD_BUTTON_LABEL +import hedvig.resources.MY_PAYMENT_UPDATING_MESSAGE import hedvig.resources.PAYMENTS_ACCOUNT import hedvig.resources.PAYOUT_PAGE_HEADING import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD import hedvig.resources.Res import hedvig.resources.general_back_button +import hedvig.resources.something_went_wrong import octopus.type.MemberPaymentProvider import octopus.type.PaymentMethodInvoiceDelivery import org.jetbrains.compose.resources.stringResource @@ -111,8 +116,9 @@ private fun PayoutAccountContent( if (availablePayoutMethods.isEmpty()) { Spacer(Modifier.weight(1f)) HedvigErrorSection( - title = "todo copy title", - subTitle = "todo copy subtitle", + // todo copy when missing current and possible payout methods + title = stringResource(Res.string.something_went_wrong), + subTitle = null, buttonText = stringResource(Res.string.general_back_button), onButtonClick = navigateBack, ) @@ -120,7 +126,10 @@ private fun PayoutAccountContent( } is PayoutAccount.SwishPayout -> { - PayoutAccountReadOnlyTextField(label = "Swish", text = currentMethod.phoneNumber.orEmpty()) + PayoutAccountReadOnlyTextField( + label = "Swish", + text = currentMethod.phoneNumber.orEmpty(), + ) } is PayoutAccount.Trustly -> { From 8be367da83253099ea37b3e86cdbb3c44711da1b Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 17:01:58 +0200 Subject: [PATCH 48/50] Add info card when there is a pending current method --- .../PayoutAccountOverviewDestination.kt | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 0320be601e..80e7c0fdef 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -151,19 +151,30 @@ private fun PayoutAccountContent( } } Spacer(Modifier.weight(1f)) - if (availablePayoutMethods.isNotEmpty()) { - HedvigButton( - text = if (currentMethod == null) { - stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD) - } else { - stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL) - }, - onClick = onConnectPayoutMethodClicked, - enabled = true, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + if (currentMethod?.isPending == true) { + HedvigNotificationCard( + message = stringResource(Res.string.MY_PAYMENT_UPDATING_MESSAGE), + priority = NotificationPriority.Info, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + if (availablePayoutMethods.isNotEmpty()) { + HedvigButton( + text = if (currentMethod == null) { + stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD) + } else { + stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL) + }, + onClick = onConnectPayoutMethodClicked, + enabled = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } } Spacer(Modifier.height(16.dp)) } From f08163c0f9770d30ad211db4d77a153cc33e2dba Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 19:23:59 +0200 Subject: [PATCH 49/50] Add showing pending state to payout overview screen --- .../PayoutAccountOverviewDestination.kt | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 80e7c0fdef..47f3ac80be 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt @@ -33,6 +33,7 @@ import hedvig.resources.MY_PAYMENT_UPDATING_MESSAGE import hedvig.resources.PAYMENTS_ACCOUNT import hedvig.resources.PAYOUT_PAGE_HEADING import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD +import hedvig.resources.REFERRAL_PENDING_STATUS_LABEL import hedvig.resources.Res import hedvig.resources.general_back_button import hedvig.resources.something_went_wrong @@ -126,16 +127,26 @@ private fun PayoutAccountContent( } is PayoutAccount.SwishPayout -> { + val phoneNumber = currentMethod.phoneNumber.orEmpty() PayoutAccountReadOnlyTextField( label = "Swish", - text = currentMethod.phoneNumber.orEmpty(), + text = if (currentMethod.isPending && phoneNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + phoneNumber + }, ) } is PayoutAccount.Trustly -> { + val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) PayoutAccountReadOnlyTextField( label = formatBankAccountLabel("Trustly", currentMethod.bankName), - text = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber), + text = if (currentMethod.isPending && accountNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + accountNumber + }, ) } @@ -144,9 +155,14 @@ private fun PayoutAccountContent( } is PayoutAccount.BankAccount -> { + val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) PayoutAccountReadOnlyTextField( label = formatBankAccountLabel(stringResource(Res.string.PAYMENTS_ACCOUNT), currentMethod.bankName), - text = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber), + text = if (currentMethod.isPending && accountNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + accountNumber + }, ) } } @@ -181,7 +197,11 @@ private fun PayoutAccountContent( } @Composable -private fun PayoutAccountReadOnlyTextField(label: String, text: String, modifier: Modifier = Modifier) { +private fun PayoutAccountReadOnlyTextField( + label: String, + text: String, + modifier: Modifier = Modifier, +) { HedvigTextField( text = text, onValueChange = {}, @@ -239,6 +259,14 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = false), availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = null, isPending = true), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = true), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + ), Content( currentMethod = PayoutAccount.Trustly( clearingNumber = "8327", @@ -257,6 +285,24 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP ), availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), ), + Content( + currentMethod = PayoutAccount.BankAccount( + clearingNumber = null, + accountNumber = null, + bankName = null, + isPending = true, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), + ), + Content( + currentMethod = PayoutAccount.BankAccount( + clearingNumber = "3300", + accountNumber = "1234567", + bankName = "Nordea", + isPending = true, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), + ), Content( currentMethod = PayoutAccount.Invoice( delivery = PaymentMethodInvoiceDelivery.KIVRA, From 1b79f0874b360484cc0caa79cfbb3f77c8a8708c Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Tue, 28 Apr 2026 20:52:38 +0200 Subject: [PATCH 50/50] Merge clearing and account number together Matches the new API and makes it easier for people to copy-paste their number from their banking app --- CLAUDE.md | 12 ++++++++ .../android/apollo/octopus/schema.graphqls | 16 +++++++---- .../main/graphql/SetupNordeaPayout.graphql | 4 +-- .../payoutaccount/data/BankNameLookup.kt | 1 - .../data/SetupNordeaPayoutUseCase.kt | 4 +-- .../EditBankAccountDestination.kt | 20 +++---------- .../EditBankAccountViewModel.kt | 28 +++++-------------- 7 files changed, 37 insertions(+), 48 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index daedd47cb0..834e8c60d5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -442,6 +442,18 @@ dependencies { # String resources in app/core/core-resources/ ``` +**IMPORTANT:** String resource XML files (`strings.xml`) are fully managed by Lokalise and regenerated on every `./gradlew downloadStrings` run. **Never add new strings directly to any `strings.xml` file** — they will be overwritten and lost. + +When new UI text is needed that does not yet exist as a string resource: +1. Hardcode the English string directly in the Kotlin/Compose code. +2. Add a `// TODO: Add "" / "" to Lokalise` comment on the same line or the line above. + +Example: +```kotlin +// TODO: Add "This is some text for feature X" / "Detta är lite text för feature X" to Lokalise +Text("This is some text for feature X") +``` + ## 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 a3f1209237..99ac458a10 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 @@ -2417,6 +2417,9 @@ type LinkInfo { type Location { street: String } +type ManuallyChargeMemberMutationOutput { + userError: UserError +} """ A 'Member' is the central user-like concept of our platform, referring to someone who has bought insurance with Hedvig and is now as we call is a "member". @@ -2449,6 +2452,11 @@ type Member { Payment information for this member. """ paymentInformation: MemberPaymentInformation! + """ + Null if the latest charge was successful or self manual charge is not allowed. + Id of the latest charge if it failed and self manual charge allowed. + """ + missedChargeIdToChargeManually: UUID paymentMethods: MemberPaymentMethods! conversations: [Conversation!]! legacyConversation: Conversation @@ -3480,7 +3488,7 @@ input MoveToHouseInput { } type Mutation { registerDirectDebit2(clientContext: RegisterDirectDebitClientContext2): DirectDebitResponse2! - manuallyChargeMember: Date! + manuallyChargeMember(dueDate: Date!): ManuallyChargeMemberMutationOutput! """ Setup invoice payment method for the member. Kivra will be used as the provider if supported, else mail. """ @@ -3798,7 +3806,7 @@ type PartnerWidgetTrial { } type PaymentMethodBankAccountDetails { """ - The bank account reference - e.g. clearing number and account number. + The bank account reference - e.g. clearing number + account number. """ account: String! """ @@ -3822,10 +3830,6 @@ type PaymentMethodInvoiceDetails { email: String } input PaymentMethodSetupNordeaPayoutInput { - """ - The clearing number for member's bank account. - """ - clearingNumber: String! """ The account number for member's bank account. """ diff --git a/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql b/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql index 4de67f6f87..e5a48aeda0 100644 --- a/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql +++ b/app/feature/feature-payout-account/src/main/graphql/SetupNordeaPayout.graphql @@ -1,6 +1,6 @@ -mutation SetupNordeaPayout($clearingNumber: String!, $accountNumber: String!) { +mutation SetupNordeaPayout($accountNumber: String!) { paymentMethodSetupNordeaPayout( - input: { clearingNumber: $clearingNumber, accountNumber: $accountNumber } + input: { accountNumber: $accountNumber } ) { status error { diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt index f11f4c18f8..2117996ec2 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/BankNameLookup.kt @@ -73,7 +73,6 @@ internal fun bankNameForClearingNumber(clearingNumber: String): String? { 9955 -> "Kommuninvest" 9956 -> "VP Securities" in 9960..9969 -> "Nordea" - in 80000..89999 -> "Swedbank" else -> null } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt index 96c7d79b68..fa6c01c758 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupNordeaPayoutUseCase.kt @@ -16,9 +16,9 @@ internal class SetupNordeaPayoutUseCase( private val apolloClient: ApolloClient, private val networkCacheManager: NetworkCacheManager, ) { - suspend fun invoke(clearingNumber: String, accountNumber: String): Either = either { + suspend fun invoke(accountNumber: String): Either = either { val result = apolloClient - .mutation(SetupNordeaPayoutMutation(clearingNumber = clearingNumber, accountNumber = accountNumber)) + .mutation(SetupNordeaPayoutMutation(accountNumber = accountNumber)) .safeExecute(::ErrorMessage) .bind() diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt index 279ef36913..20b2a66903 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountDestination.kt @@ -27,8 +27,6 @@ import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE -import hedvig.resources.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL -import hedvig.resources.PAYMENTS_ACCOUNT import hedvig.resources.Res import hedvig.resources.general_save_button import hedvig.resources.something_went_wrong @@ -76,28 +74,18 @@ private fun EditBankAccountScreen( Spacer(Modifier.weight(1f)) Column(Modifier.padding(horizontal = 16.dp)) { HedvigTextField( - state = uiState.clearingNumberState, + state = uiState.accountNumberState, labelText = buildString { - append(stringResource(Res.string.BANK_PAYOUT_METHOD_FORM_CLEARING_FIELD_LABEL)) + // TODO: Add "Clearing and account number" / "Clearing och kontonummer" to Lokalise + append("Clearing and account number") if (uiState.bankName != null) { append(" - ") append(uiState.bankName) } }, textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, - inputTransformation = uiState.clearingInputTransformation, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = Modifier.fillMaxWidth(), - ) - Spacer(Modifier.height(4.dp)) - HedvigTextField( - state = uiState.accountNumberState, - labelText = stringResource(Res.string.PAYMENTS_ACCOUNT), - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, inputTransformation = uiState.accountNumberInputTransformation, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - ), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth(), ) } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt index d4032daf85..dbadc383a2 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/editbankaccount/EditBankAccountViewModel.kt @@ -24,7 +24,6 @@ internal class EditBankAccountViewModel( setupNordeaPayoutUseCase: SetupNordeaPayoutUseCase, ) : MoleculeViewModel( EditBankAccountUiState( - clearingNumberState = TextFieldState(), accountNumberState = TextFieldState(), bankName = null, isLoading = false, @@ -41,7 +40,6 @@ internal sealed interface EditBankAccountEvent { } internal data class EditBankAccountUiState( - val clearingNumberState: TextFieldState, val accountNumberState: TextFieldState, val bankName: String?, val isLoading: Boolean, @@ -49,15 +47,10 @@ internal data class EditBankAccountUiState( val showSuccessSnackBar: Boolean, ) { val canSave: Boolean - get() = !isLoading && - clearingNumberState.text.length >= 4 && - accountNumberState.text.length in 6..12 + get() = !isLoading && accountNumberState.text.length in 10..17 - // Swedish clearing numbers are 4 digits for most banks, 5 for Swedbank's 8-series - val clearingInputTransformation: InputTransformation = InputTransformation.maxLength(5).digitsOnly() - - // Swedish account numbers are up to 10 digits - val accountNumberInputTransformation: InputTransformation = InputTransformation.maxLength(10).digitsOnly() + // Combined clearing and account number: 10-17 digits + val accountNumberInputTransformation: InputTransformation = InputTransformation.maxLength(17).digitsOnly() } internal class EditBankAccountPresenter( @@ -67,20 +60,19 @@ internal class EditBankAccountPresenter( override fun MoleculePresenterScope.present( lastState: EditBankAccountUiState, ): EditBankAccountUiState { - val clearingNumberState = remember { lastState.clearingNumberState } val accountNumberState = remember { lastState.accountNumberState } - val bankName = bankNameForClearingNumber(clearingNumberState.text.toString()) + val bankName = bankNameForClearingNumber(accountNumberState.text.toString().take(4)) var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } var showSuccessSnackBar by remember { mutableStateOf(false) } - var saveIteration by remember { mutableStateOf?>(null) } + var saveIteration by remember { mutableStateOf(null) } val currentSave = saveIteration if (currentSave != null) { LaunchedEffect(currentSave) { isLoading = true errorMessage = null - setupNordeaPayoutUseCase.invoke(currentSave.first, currentSave.second).fold( + setupNordeaPayoutUseCase.invoke(currentSave).fold( ifLeft = { isLoading = false errorMessage = it.message ?: "" @@ -95,11 +87,6 @@ internal class EditBankAccountPresenter( } } - LaunchedEffect(clearingNumberState) { - snapshotFlow { clearingNumberState.text }.collect { - errorMessage = null - } - } LaunchedEffect(accountNumberState) { snapshotFlow { accountNumberState.text }.collect { errorMessage = null @@ -110,7 +97,7 @@ internal class EditBankAccountPresenter( when (event) { EditBankAccountEvent.Save -> { if (!isLoading) { - saveIteration = clearingNumberState.text.toString() to accountNumberState.text.toString() + saveIteration = accountNumberState.text.toString() } } @@ -121,7 +108,6 @@ internal class EditBankAccountPresenter( } return EditBankAccountUiState( - clearingNumberState = clearingNumberState, accountNumberState = accountNumberState, bankName = bankName, isLoading = isLoading,