From ee424a551dbc85d3f29d3ff8085965867a49dcc9 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 10 Apr 2026 18:01:40 +0200 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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 }