From 68b03eea71c258a4335f34cebfbb0affdff7b2a6 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 20 May 2026 11:58:20 +0200 Subject: [PATCH 01/30] wip --- .../feature-payin-account/build.gradle.kts | 34 ++ .../src/main/AndroidManifest.xml | 2 + .../main/graphql/SetupInvoicePayin.graphql} | 0 .../account/data/SetupInvoicePayoutUseCase.kt | 35 ++ .../navigation/PayinAccountDestination.kt | 25 ++ .../payin/account/navigation/PayinNavGraph.kt | 97 +++++ .../PayoutAccountOverviewDestination.kt | 340 ++++++++++++++++++ .../PayoutAccountOverviewViewModel.kt | 76 ++++ .../SelectPayinMethodDestination.kt | 94 +++++ .../SetupInvoicePayinDestination.kt} | 22 +- .../SetupInvoicePayoutViewModel.kt | 2 +- .../ui/setupswish/SetupSwishPayinViewModel.kt | 90 +++++ .../setupswish/SetupSwishPayoutDestination.kt | 116 ++++++ .../navigation/PayoutAccountGraph.kt | 15 - .../SelectPayoutMethodDestination.kt | 9 - .../lint-baseline-feature-payin-account.xml | 51 +++ 16 files changed, 981 insertions(+), 27 deletions(-) create mode 100644 app/feature/feature-payin-account/build.gradle.kts create mode 100644 app/feature/feature-payin-account/src/main/AndroidManifest.xml rename app/feature/{feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql => feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql} (100%) create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayoutUseCase.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinAccountDestination.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewDestination.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt rename app/feature/{feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt => feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt} (83%) rename app/feature/{feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount => feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account}/ui/setupinvoice/SetupInvoicePayoutViewModel.kt (97%) create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayoutDestination.kt create mode 100644 hedvig-lint/lint-baseline/lint-baseline-feature-payin-account.xml diff --git a/app/feature/feature-payin-account/build.gradle.kts b/app/feature/feature-payin-account/build.gradle.kts new file mode 100644 index 0000000000..105feaab33 --- /dev/null +++ b/app/feature/feature-payin-account/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("hedvig.android.library") + id("hedvig.gradle.plugin") +} + +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) + 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) + implementation(projects.navigationCore) +} diff --git a/app/feature/feature-payin-account/src/main/AndroidManifest.xml b/app/feature/feature-payin-account/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..568741e54f --- /dev/null +++ b/app/feature/feature-payin-account/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql b/app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql similarity index 100% rename from app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql rename to app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayoutUseCase.kt new file mode 100644 index 0000000000..bbceab4868 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayoutUseCase.kt @@ -0,0 +1,35 @@ +package com.hedvig.android.feature.payin.account.data + +import arrow.core.Either +import arrow.core.raise.either +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.ErrorMessage +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.apollo.safeExecute +import com.hedvig.android.core.common.ErrorMessage +import octopus.SetupInvoicePayoutMutation +import octopus.type.PaymentMethodInvoiceDelivery +import octopus.type.PaymentMethodSetupStatus + +internal class SetupInvoicePayoutUseCase( + private val apolloClient: ApolloClient, + private val networkCacheManager: NetworkCacheManager, +) { + suspend fun invoke(): Either = either { + val result = apolloClient + .mutation(SetupInvoicePayoutMutation()) + .safeExecute(::ErrorMessage) + .bind() + + val output = result.paymentMethodSetupInvoicePayin + when (output.status) { + PaymentMethodSetupStatus.FAILED -> { + raise(ErrorMessage(output.error?.message ?: "Failed to set up invoice payout")) + } + + else -> { + networkCacheManager.clearCache() + } + } + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinAccountDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinAccountDestination.kt new file mode 100644 index 0000000000..6539776797 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinAccountDestination.kt @@ -0,0 +1,25 @@ +package com.hedvig.android.feature.payin.account.navigation + +import com.hedvig.android.navigation.common.Destination +import kotlinx.serialization.Serializable + +sealed interface PayinAccountDestination { + @Serializable + data object Graph : PayinAccountDestination, Destination +} + +internal sealed interface PayinAccountDestinations { + @Serializable + data object Overview : PayinAccountDestinations, Destination + + @Serializable + data class SelectPayinMethod( + val availableProviders: List, + ) : PayinAccountDestinations, Destination + + @Serializable + data object SetupSwishPayin : PayinAccountDestinations, Destination + + @Serializable + data object SetupInvoicePayin : PayinAccountDestinations, Destination +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt new file mode 100644 index 0000000000..5826555180 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -0,0 +1,97 @@ +package com.hedvig.android.feature.payin.account.navigation + +import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptionsBuilder +import com.hedvig.android.design.system.hedvig.GlobalSnackBarState +import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewDestination +import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewUiState +import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewViewModel +import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayinDestination +import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayoutViewModel +import com.hedvig.android.navigation.compose.navDeepLinks +import com.hedvig.android.navigation.compose.navdestination +import com.hedvig.android.navigation.compose.navgraph +import com.hedvig.android.navigation.compose.typedPopBackStack +import com.hedvig.android.navigation.compose.typedPopUpTo +import com.hedvig.android.navigation.core.HedvigDeepLinkContainer +import octopus.type.MemberPaymentProvider +import org.koin.compose.viewmodel.koinViewModel + +fun NavGraphBuilder.payinAccountGraph( + navController: NavController, + globalSnackBarState: GlobalSnackBarState, + hedvigDeepLinkContainer: HedvigDeepLinkContainer, + navigateToConnectPayment: (builder: NavOptionsBuilder.() -> Unit) -> Unit, + navigateUp: () -> Unit, +) { + navgraph( + startDestination = PayinAccountDestinations.Overview::class, + deepLinks = navDeepLinks(hedvigDeepLinkContainer.payout), + ) { + navdestination { + val viewModel: PayoutAccountOverviewViewModel = koinViewModel() + PayoutAccountOverviewDestination( + viewModel = viewModel, + onConnectPayoutMethodClicked = dropUnlessResumed { + val content = viewModel.uiState.value as? PayoutAccountOverviewUiState.Content + navController.navigate( + PayinAccountDestinations.SelectPayinMethod( + availableProviders = content?.availablePayoutMethods?.map { it.rawValue } ?: emptyList(), + ), + ) + }, + navigateToConnectPayment = dropUnlessResumed { + navigateToConnectPayment { + typedPopUpTo { + inclusive = true + } + } + }, + navigateUp = navigateUp, + ) + } + + navdestination { + SelectPayoutMethodDestination( + availableProviders = this.availableProviders.map { MemberPaymentProvider.safeValueOf(it) }, + onTrustlySelected = dropUnlessResumed { + navigateToConnectPayment { + typedPopUpTo { + inclusive = true + } + } + }, + onNordeaSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.EditBankAccount) }, + onSwishSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupSwishPayin) }, + onInvoiceSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupInvoicePayin) }, + navigateUp = navController::navigateUp, + ) + } + + navdestination { + val viewModel: SetupSwishPayoutViewModel = koinViewModel() + SetupSwishPayoutDestination( + viewModel = viewModel, + globalSnackBarState = globalSnackBarState, + onSuccessfullyConnected = { + navController.typedPopBackStack(inclusive = true) + }, + navigateUp = navController::navigateUp, + ) + } + + navdestination { + val viewModel: SetupInvoicePayoutViewModel = koinViewModel() + SetupInvoicePayinDestination( + viewModel = viewModel, + globalSnackBarState = globalSnackBarState, + onSuccessfullyConnected = { + navController.typedPopBackStack(inclusive = true) + }, + navigateUp = navController::navigateUp, + ) + } + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewDestination.kt new file mode 100644 index 0000000000..ee8c3407c8 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewDestination.kt @@ -0,0 +1,340 @@ +package com.hedvig.android.feature.payin.account.ui.overview + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.design.system.hedvig.EmptyState +import com.hedvig.android.design.system.hedvig.EmptyStateDefaults +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.HedvigInformationSection +import com.hedvig.android.design.system.hedvig.HedvigNotificationCard +import com.hedvig.android.design.system.hedvig.HedvigPreview +import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigTextField +import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults +import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import com.hedvig.android.design.system.hedvig.Surface +import com.hedvig.android.feature.payoutaccount.data.PayoutAccount +import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState.Content +import hedvig.resources.CHANGE_PAYOUT_METHOD_BUTTON_LABEL +import hedvig.resources.MY_PAYMENT_UPDATING_MESSAGE +import hedvig.resources.PAYMENTS_ACCOUNT +import hedvig.resources.PAYMENTS_INVOICE +import hedvig.resources.PAYOUT_MISSING_INFO +import hedvig.resources.PAYOUT_NO_PAYOUT_OPTIONS_SUBTITLE +import hedvig.resources.PAYOUT_NO_PAYOUT_OPTIONS_TITLE +import hedvig.resources.PAYOUT_PAGE_HEADING +import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD +import hedvig.resources.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON +import hedvig.resources.REFERRAL_PENDING_STATUS_LABEL +import hedvig.resources.Res +import hedvig.resources.swish +import hedvig.resources.trustly +import octopus.type.MemberPaymentProvider +import octopus.type.PaymentMethodInvoiceDelivery +import org.jetbrains.compose.resources.stringResource + +@Composable +internal fun PayoutAccountOverviewDestination( + viewModel: PayoutAccountOverviewViewModel, + onConnectPayoutMethodClicked: () -> Unit, + navigateToConnectPayment: () -> Unit, + navigateUp: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + PayoutAccountOverviewScreen( + uiState = uiState, + onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, + navigateToConnectPayment = navigateToConnectPayment, + onRetry = { viewModel.emit(PayoutAccountOverviewEvent.Retry) }, + navigateUp = navigateUp, + ) +} + +@Composable +private fun PayoutAccountOverviewScreen( + uiState: PayoutAccountOverviewUiState, + onConnectPayoutMethodClicked: () -> Unit, + navigateToConnectPayment: () -> Unit, + onRetry: () -> Unit, + navigateUp: () -> Unit, +) { + HedvigScaffold( + topAppBarText = stringResource(Res.string.PAYOUT_PAGE_HEADING), + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + when (uiState) { + PayoutAccountOverviewUiState.Loading -> { + HedvigFullScreenCenterAlignedProgressDebounced( + Modifier + .weight(1f) + .wrapContentHeight(), + ) + } + + PayoutAccountOverviewUiState.Error -> { + HedvigErrorSection( + onButtonClick = onRetry, + modifier = Modifier + .weight(1f) + .wrapContentHeight(), + ) + } + + PayoutAccountOverviewUiState.NoPayoutOptions -> { + HedvigInformationSection( + title = stringResource(Res.string.PAYOUT_NO_PAYOUT_OPTIONS_TITLE), + subTitle = stringResource(Res.string.PAYOUT_NO_PAYOUT_OPTIONS_SUBTITLE), + buttonText = stringResource(Res.string.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON), + onButtonClick = navigateToConnectPayment, + modifier = Modifier + .weight(1f) + .wrapContentHeight(), + ) + } + + is Content -> { + PayoutAccountContent( + currentMethod = uiState.currentMethod, + availablePayoutMethods = uiState.availablePayoutMethods, + onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, + modifier = Modifier.weight(1f), + ) + } + } + } +} + +@Composable +private fun PayoutAccountContent( + currentMethod: PayoutAccount?, + availablePayoutMethods: List, + onConnectPayoutMethodClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier) { + Spacer(Modifier.height(8.dp)) + when (currentMethod) { + null -> { + if (availablePayoutMethods.isNotEmpty()) { + Spacer(Modifier.weight(1f)) + EmptyState( + text = stringResource(Res.string.PAYOUT_MISSING_INFO), + description = null, + iconStyle = EmptyStateDefaults.EmptyStateIconStyle.INFO, + ) + } + } + + is PayoutAccount.SwishPayout -> { + val phoneNumber = currentMethod.phoneNumber.orEmpty() + PayoutAccountReadOnlyTextField( + label = stringResource(Res.string.swish), + text = if (currentMethod.isPending && phoneNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + phoneNumber + }, + ) + } + + is PayoutAccount.Trustly -> { + val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) + PayoutAccountReadOnlyTextField( + label = formatBankAccountLabel(stringResource(Res.string.trustly), currentMethod.bankName), + text = if (currentMethod.isPending && accountNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + accountNumber + }, + ) + } + + is PayoutAccount.Invoice -> { + PayoutAccountReadOnlyTextField( + stringResource(Res.string.PAYMENTS_ACCOUNT), + stringResource(Res.string.PAYMENTS_INVOICE), + ) + } + + is PayoutAccount.BankAccount -> { + val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) + PayoutAccountReadOnlyTextField( + label = formatBankAccountLabel(stringResource(Res.string.PAYMENTS_ACCOUNT), currentMethod.bankName), + text = if (currentMethod.isPending && accountNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + accountNumber + }, + ) + } + } + Spacer(Modifier.weight(1f)) + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + if (currentMethod?.isPending == true) { + HedvigNotificationCard( + message = stringResource(Res.string.MY_PAYMENT_UPDATING_MESSAGE), + priority = NotificationPriority.Info, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + if (availablePayoutMethods.isNotEmpty()) { + HedvigButton( + text = if (currentMethod == null) { + stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD) + } else { + stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL) + }, + onClick = onConnectPayoutMethodClicked, + enabled = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + } + Spacer(Modifier.height(16.dp)) + } +} + +@Composable +private fun PayoutAccountReadOnlyTextField(label: String, text: String, modifier: Modifier = Modifier) { + HedvigTextField( + text = text, + onValueChange = {}, + labelText = label, + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, + readOnly = true, + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) +} + +private fun formatBankAccountLabel(baseLabel: String, bankName: String?): String { + return if (bankName != null) "$baseLabel - $bankName" else baseLabel +} + +private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: String?): String { + return when { + clearingNumber != null && accountNumber != null -> "$clearingNumber-$accountNumber" + else -> clearingNumber.orEmpty() + } +} + +@Composable +@HedvigPreview +private fun PreviewPayoutAccountOverviewScreen( + @PreviewParameter(PayoutAccountOverviewUiStateProvider::class) uiState: PayoutAccountOverviewUiState, +) { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + PayoutAccountOverviewScreen( + uiState = uiState, + onConnectPayoutMethodClicked = {}, + navigateToConnectPayment = {}, + onRetry = {}, + navigateUp = {}, + ) + } + } +} + +private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterProvider( + listOf( + PayoutAccountOverviewUiState.Loading, + PayoutAccountOverviewUiState.Error, + PayoutAccountOverviewUiState.NoPayoutOptions, + Content( + currentMethod = null, + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = false), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = false), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = null, isPending = true), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + ), + Content( + currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = true), + availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + ), + Content( + currentMethod = PayoutAccount.Trustly( + clearingNumber = "8327", + accountNumber = "12345678", + bankName = "Mock Swedbank", + isPending = false, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.TRUSTLY), + ), + Content( + currentMethod = PayoutAccount.BankAccount( + clearingNumber = "3300", + accountNumber = "1234567", + bankName = "Nordea", + isPending = false, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), + ), + Content( + currentMethod = PayoutAccount.BankAccount( + clearingNumber = null, + accountNumber = null, + bankName = null, + isPending = true, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), + ), + Content( + currentMethod = PayoutAccount.BankAccount( + clearingNumber = "3300", + accountNumber = "1234567", + bankName = "Nordea", + isPending = true, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), + ), + Content( + currentMethod = PayoutAccount.Invoice( + delivery = PaymentMethodInvoiceDelivery.KIVRA, + email = null, + isPending = false, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE), + ), + Content( + currentMethod = PayoutAccount.Invoice( + delivery = PaymentMethodInvoiceDelivery.MAIL, + email = "user@example.com", + isPending = false, + ), + availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE, MemberPaymentProvider.TRUSTLY), + ), + ), +) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt new file mode 100644 index 0000000000..b0b07e1299 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt @@ -0,0 +1,76 @@ +package com.hedvig.android.feature.payin.account.ui.overview + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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 +import octopus.type.MemberPaymentProvider + +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 object NoPayoutOptions : PayoutAccountOverviewUiState + + data class Content( + val currentMethod: PayoutAccount?, + val availablePayoutMethods: List, + ) : PayoutAccountOverviewUiState +} + +internal class PayoutAccountOverviewPresenter( + private val getPayoutAccountUseCase: GetPayoutAccountUseCase, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present( + lastState: PayoutAccountOverviewUiState, + ): PayoutAccountOverviewUiState { + var loadIteration by remember { mutableIntStateOf(0) } + var uiState by remember { mutableStateOf(lastState) } + + LaunchedEffect(loadIteration) { + uiState = PayoutAccountOverviewUiState.Loading + getPayoutAccountUseCase.invoke().fold( + ifLeft = { uiState = PayoutAccountOverviewUiState.Error }, + ifRight = { data -> + uiState = if (data.currentMethod == null && data.availablePayoutMethods.isEmpty()) { + PayoutAccountOverviewUiState.NoPayoutOptions + } else { + PayoutAccountOverviewUiState.Content( + currentMethod = data.currentMethod, + availablePayoutMethods = data.availablePayoutMethods, + ) + } + }, + ) + } + + CollectEvents { event -> + when (event) { + PayoutAccountOverviewEvent.Retry -> loadIteration++ + } + } + + return uiState + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt new file mode 100644 index 0000000000..8d8fb06104 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -0,0 +1,94 @@ +package com.hedvig.android.feature.payin.account.ui.selectmethod + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +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 com.hedvig.android.design.system.hedvig.HedvigTheme +import hedvig.resources.BANK_PAYOUT_METHOD_CARD_DESCRIPTION +import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE +import hedvig.resources.PAYMENTS_INVOICE +import hedvig.resources.PAYOUT_METHOD_INVOICE_DESCRIPTION +import hedvig.resources.PAYOUT_METHOD_SWISH_DESCRIPTION +import hedvig.resources.PAYOUT_METHOD_TRUSTLY_DESCRIPTION +import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD +import hedvig.resources.Res +import hedvig.resources.swish +import hedvig.resources.trustly +import octopus.type.MemberPaymentProvider +import org.jetbrains.compose.resources.stringResource + +@Composable +internal fun SelectPayinMethodDestination( + availableProviders: List, + onTrustlySelected: () -> Unit, + onSwishSelected: () -> Unit, + onInvoiceSelected: () -> Unit, + navigateUp: () -> Unit, +) { + HedvigScaffold( + topAppBarText = stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD), //todo + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + Spacer(Modifier.height(8.dp)) + Column(Modifier.padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { + for (provider in availableProviders) { + when (provider) { + MemberPaymentProvider.TRUSTLY -> { + PayinMethodRow( + title = stringResource(Res.string.trustly), + subtitle = stringResource(Res.string.PAYOUT_METHOD_TRUSTLY_DESCRIPTION), //todo + onClick = onTrustlySelected, + ) + } + + MemberPaymentProvider.SWISH -> { + PayinMethodRow( + title = stringResource(Res.string.swish), + subtitle = stringResource(Res.string.PAYOUT_METHOD_SWISH_DESCRIPTION), //todo + onClick = onSwishSelected, + ) + } + + MemberPaymentProvider.INVOICE -> { + PayinMethodRow( + title = stringResource(Res.string.PAYMENTS_INVOICE), + subtitle = stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION), //todo + onClick = onInvoiceSelected, + ) + } + + else -> {} + } + } + } + Spacer(Modifier.height(16.dp)) + } +} + +@Composable +private fun PayinMethodRow(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 = HedvigTheme.colorScheme.textSecondary, + ) + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt similarity index 83% rename from app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt rename to app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt index b73f09e543..831dfa751e 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt @@ -1,4 +1,4 @@ -package com.hedvig.android.feature.payoutaccount.ui.setupinvoice +package com.hedvig.android.feature.payin.account.ui.setupinvoice import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically @@ -17,15 +17,18 @@ 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.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import com.hedvig.android.design.system.hedvig.Surface import hedvig.resources.CONTACT_INFO_CHANGES_SAVED import hedvig.resources.PAYMENTS_INVOICE import hedvig.resources.Res import org.jetbrains.compose.resources.stringResource @Composable -internal fun SetupInvoicePayoutDestination( +internal fun SetupInvoicePayinDestination( viewModel: SetupInvoicePayoutViewModel, globalSnackBarState: GlobalSnackBarState, onSuccessfullyConnected: () -> Unit, @@ -92,3 +95,18 @@ private fun SetupInvoicePayoutScreen( Spacer(Modifier.height(16.dp)) } } + + +@Composable +@HedvigPreview +private fun PreviewPayoutAccountOverviewScreen() { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + SetupInvoicePayoutScreen( + uiState = SetupInvoicePayoutUiState(false, null), + globalSnackBarState = GlobalSnackBarState(), + {} + ) + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayoutViewModel.kt similarity index 97% rename from app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutViewModel.kt rename to app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayoutViewModel.kt index 8b9872ba4f..edcc19e326 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayoutViewModel.kt @@ -1,4 +1,4 @@ -package com.hedvig.android.feature.payoutaccount.ui.setupinvoice +package com.hedvig.android.feature.payin.account.ui.setupinvoice import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt new file mode 100644 index 0000000000..32ddaf73d8 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt @@ -0,0 +1,90 @@ +package com.hedvig.android.feature.payin.account.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.core.common.ErrorMessage +import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase +import com.hedvig.android.molecule.public.MoleculePresenter +import com.hedvig.android.molecule.public.MoleculePresenterScope +import com.hedvig.android.molecule.public.MoleculeViewModel + +internal class SetupSwishPayinViewModel( + setupSwishPayoutUseCase: SetupSwishPayoutUseCase, +) : MoleculeViewModel( + SetupSwishPayoutUiState(TextFieldState(), false, null, false), + SetupSwishPayoutPresenter(setupSwishPayoutUseCase), + ) + +internal sealed interface SetupSwishPayoutEvent { + data object Save : SetupSwishPayoutEvent + + data object ShowedSnackBar : SetupSwishPayoutEvent +} + +internal data class SetupSwishPayoutUiState( + val phoneNumberState: TextFieldState, + val isLoading: Boolean, + val errorMessage: ErrorMessage?, + val showSuccessSnackBar: Boolean, +) + +internal class SetupSwishPayoutPresenter( + private val setupSwishPayoutUseCase: SetupSwishPayinUseCase, +) : 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 showSuccessSnackBar 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 + saveIteration = null + }, + ifRight = { + isLoading = false + showSuccessSnackBar = true + saveIteration = null + }, + ) + } + } + + CollectEvents { event -> + when (event) { + SetupSwishPayoutEvent.Save -> { + if (!isLoading) { + saveIteration = phoneNumberState.text.toString() + } + } + + SetupSwishPayoutEvent.ShowedSnackBar -> { + showSuccessSnackBar = false + } + } + } + + return SetupSwishPayoutUiState( + phoneNumberState = phoneNumberState, + isLoading = isLoading, + errorMessage = errorMessage, + showSuccessSnackBar = showSuccessSnackBar, + ) + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayoutDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayoutDestination.kt new file mode 100644 index 0000000000..fdf86594f6 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayoutDestination.kt @@ -0,0 +1,116 @@ +package com.hedvig.android.feature.payin.account.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.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.HedvigTextField +import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults +import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import hedvig.resources.CONTACT_INFO_CHANGES_SAVED +import hedvig.resources.ODYSSEY_PHONE_NUMBER_LABEL +import hedvig.resources.Res +import hedvig.resources.TIER_FLOW_COMMIT_PROCESSING_ERROR_DESCRIPTION +import hedvig.resources.general_save_button +import hedvig.resources.swish +import org.jetbrains.compose.resources.stringResource + +@Composable +internal fun SetupSwishPayinDestination( + viewModel: SetupSwishPayinViewModel, + globalSnackBarState: GlobalSnackBarState, + onSuccessfullyConnected: () -> Unit, + navigateUp: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + SetupSwishPayoutScreen( + uiState = uiState, + globalSnackBarState = globalSnackBarState, + onSave = { viewModel.emit(SetupSwishPayoutEvent.Save) }, + showedSnackBar = { + viewModel.emit(SetupSwishPayoutEvent.ShowedSnackBar) + onSuccessfullyConnected() + }, + navigateUp = navigateUp, + ) +} + +//todo fetch payment methods continuously to see if it already not in pending state + +@Composable +private fun SetupSwishPayoutScreen( + uiState: SetupSwishPayoutUiState, + globalSnackBarState: GlobalSnackBarState, + onSave: () -> Unit, + showedSnackBar: () -> Unit, + navigateUp: () -> Unit, +) { + val changesSaved = stringResource(Res.string.CONTACT_INFO_CHANGES_SAVED) + LaunchedEffect(uiState.showSuccessSnackBar) { + if (!uiState.showSuccessSnackBar) return@LaunchedEffect + globalSnackBarState.show(changesSaved, NotificationPriority.Campaign) + showedSnackBar() + } + + HedvigScaffold( + topAppBarText = stringResource(Res.string.swish), + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + Spacer(Modifier.weight(1f)) + Column(Modifier.padding(horizontal = 16.dp)) { + HedvigTextField( + state = uiState.phoneNumberState, + labelText = stringResource(Res.string.ODYSSEY_PHONE_NUMBER_LABEL), + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Phone, + ), + modifier = Modifier.fillMaxWidth(), + ) + } + AnimatedVisibility( + visible = uiState.errorMessage != null, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + HedvigNotificationCard( + message = uiState.errorMessage?.message + ?: stringResource(Res.string.TIER_FLOW_COMMIT_PROCESSING_ERROR_DESCRIPTION), + priority = NotificationPriority.Attention, + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = 4.dp) + .fillMaxWidth(), + ) + } + Spacer(Modifier.height(16.dp)) + HedvigButton( + text = stringResource(Res.string.general_save_button), + onClick = onSave, + enabled = !uiState.isLoading && uiState.phoneNumberState.text.length >= 10, + 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/navigation/PayoutAccountGraph.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/navigation/PayoutAccountGraph.kt index 458ff71f26..01c2e1b6d9 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 @@ -11,8 +11,6 @@ import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOvervie import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel import com.hedvig.android.feature.payoutaccount.ui.selectmethod.SelectPayoutMethodDestination -import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayoutDestination -import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayoutViewModel import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutDestination import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import com.hedvig.android.navigation.compose.navDeepLinks @@ -70,7 +68,6 @@ fun NavGraphBuilder.payoutAccountGraph( }, onNordeaSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.EditBankAccount) }, onSwishSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.SetupSwishPayout) }, - onInvoiceSelected = dropUnlessResumed { navController.navigate(PayoutAccountDestinations.SetupInvoicePayout) }, navigateUp = navController::navigateUp, ) } @@ -98,17 +95,5 @@ fun NavGraphBuilder.payoutAccountGraph( navigateUp = navController::navigateUp, ) } - - navdestination { - val viewModel: SetupInvoicePayoutViewModel = koinViewModel() - SetupInvoicePayoutDestination( - viewModel = viewModel, - globalSnackBarState = globalSnackBarState, - onSuccessfullyConnected = { - navController.typedPopBackStack(inclusive = true) - }, - navigateUp = navController::navigateUp, - ) - } } } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt index 691e3a936c..aa94ff4d35 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -34,7 +34,6 @@ internal fun SelectPayoutMethodDestination( onTrustlySelected: () -> Unit, onNordeaSelected: () -> Unit, onSwishSelected: () -> Unit, - onInvoiceSelected: () -> Unit, navigateUp: () -> Unit, ) { HedvigScaffold( @@ -70,14 +69,6 @@ internal fun SelectPayoutMethodDestination( ) } - MemberPaymentProvider.INVOICE -> { - PayoutMethodRow( - title = stringResource(Res.string.PAYMENTS_INVOICE), - subtitle = stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION), - onClick = onInvoiceSelected, - ) - } - else -> {} } } diff --git a/hedvig-lint/lint-baseline/lint-baseline-feature-payin-account.xml b/hedvig-lint/lint-baseline/lint-baseline-feature-payin-account.xml new file mode 100644 index 0000000000..a5d7f6b144 --- /dev/null +++ b/hedvig-lint/lint-baseline/lint-baseline-feature-payin-account.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + From c97328790ac06259f443430f1e1948e7f23586a9 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 20 May 2026 13:20:29 +0200 Subject: [PATCH 02/30] setup invoice payin --- .../src/main/graphql/GetPayinMethods.graphql | 42 +++++++++++++++++++ .../main/graphql/SetupInvoicePayin.graphql | 2 +- ...UseCase.kt => SetupInvoicePayinUseCase.kt} | 9 ++-- .../payin/account/navigation/PayinNavGraph.kt | 4 +- .../SetupInvoicePayinDestination.kt | 2 +- ...Model.kt => SetupInvoicePayinViewModel.kt} | 16 +++---- .../data/SetupInvoicePayoutUseCase.kt | 5 +-- 7 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 app/feature/feature-payin-account/src/main/graphql/GetPayinMethods.graphql rename app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/{SetupInvoicePayoutUseCase.kt => SetupInvoicePayinUseCase.kt} (81%) rename app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/{SetupInvoicePayoutViewModel.kt => SetupInvoicePayinViewModel.kt} (85%) diff --git a/app/feature/feature-payin-account/src/main/graphql/GetPayinMethods.graphql b/app/feature/feature-payin-account/src/main/graphql/GetPayinMethods.graphql new file mode 100644 index 0000000000..eaac18a154 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/graphql/GetPayinMethods.graphql @@ -0,0 +1,42 @@ +query GetPayinMethods { + currentMember { + paymentMethods { + payinMethods { + provider + status + isDefault + details { + ... on PaymentMethodBankAccountDetails { + account + bank + } + ... on PaymentMethodSwishDetails { + phoneNumber + } + ... on PaymentMethodInvoiceDetails { + delivery + email + } + } + } + availableMethods { + provider + supportsPayin + isActive + details { + ... on PaymentMethodBankAccountDetails { + account + bank + } + ... on PaymentMethodSwishDetails { + phoneNumber + } + ... on PaymentMethodInvoiceDetails { + delivery + email + } + } + } + } + } +} diff --git a/app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql b/app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql index dd61ac8dcf..cb935fbfd1 100644 --- a/app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql +++ b/app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql @@ -1,4 +1,4 @@ -mutation SetupInvoicePayout { +mutation SetupInvoicePayin { paymentMethodSetupInvoicePayin { status error { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayinUseCase.kt similarity index 81% rename from app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayoutUseCase.kt rename to app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayinUseCase.kt index bbceab4868..bfe420a006 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayoutUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayinUseCase.kt @@ -7,24 +7,23 @@ import com.hedvig.android.apollo.ErrorMessage import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage -import octopus.SetupInvoicePayoutMutation -import octopus.type.PaymentMethodInvoiceDelivery +import octopus.SetupInvoicePayinMutation import octopus.type.PaymentMethodSetupStatus -internal class SetupInvoicePayoutUseCase( +internal class SetupInvoicePayinUseCase( private val apolloClient: ApolloClient, private val networkCacheManager: NetworkCacheManager, ) { suspend fun invoke(): Either = either { val result = apolloClient - .mutation(SetupInvoicePayoutMutation()) + .mutation(SetupInvoicePayinMutation()) .safeExecute(::ErrorMessage) .bind() val output = result.paymentMethodSetupInvoicePayin when (output.status) { PaymentMethodSetupStatus.FAILED -> { - raise(ErrorMessage(output.error?.message ?: "Failed to set up invoice payout")) + raise(ErrorMessage(output.error?.message ?: "Failed to set up invoice payin")) } else -> { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt index 5826555180..6993cc7c52 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -9,7 +9,7 @@ import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOvervie import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewUiState import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewViewModel import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayinDestination -import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayoutViewModel +import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayinViewModel import com.hedvig.android.navigation.compose.navDeepLinks import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.compose.navgraph @@ -83,7 +83,7 @@ fun NavGraphBuilder.payinAccountGraph( } navdestination { - val viewModel: SetupInvoicePayoutViewModel = koinViewModel() + val viewModel: SetupInvoicePayinViewModel = koinViewModel() SetupInvoicePayinDestination( viewModel = viewModel, globalSnackBarState = globalSnackBarState, diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt index 831dfa751e..29fe46468f 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt @@ -29,7 +29,7 @@ import org.jetbrains.compose.resources.stringResource @Composable internal fun SetupInvoicePayinDestination( - viewModel: SetupInvoicePayoutViewModel, + viewModel: SetupInvoicePayinViewModel, globalSnackBarState: GlobalSnackBarState, onSuccessfullyConnected: () -> Unit, navigateUp: () -> Unit, diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayoutViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt similarity index 85% rename from app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayoutViewModel.kt rename to app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt index edcc19e326..b5ebbbd121 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayoutViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt @@ -7,17 +7,17 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import com.hedvig.android.feature.payoutaccount.data.SetupInvoicePayoutUseCase +import com.hedvig.android.feature.payin.account.data.SetupInvoicePayinUseCase import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope import com.hedvig.android.molecule.public.MoleculeViewModel -internal class SetupInvoicePayoutViewModel( - setupInvoicePayoutUseCase: SetupInvoicePayoutUseCase, +internal class SetupInvoicePayinViewModel( + setupInvoicePayinUseCase: SetupInvoicePayinUseCase, ) : MoleculeViewModel( - SetupInvoicePayoutUiState(false, null, false), - SetupInvoicePayoutPresenter(setupInvoicePayoutUseCase), - ) + SetupInvoicePayoutUiState(false, null, false), + SetupInvoicePayoutPresenter(setupInvoicePayinUseCase), +) internal sealed interface SetupInvoicePayoutEvent { data object Connect : SetupInvoicePayoutEvent @@ -32,7 +32,7 @@ internal data class SetupInvoicePayoutUiState( ) internal class SetupInvoicePayoutPresenter( - private val setupInvoicePayoutUseCase: SetupInvoicePayoutUseCase, + private val setupInvoicePayinUseCase: SetupInvoicePayinUseCase, ) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present( @@ -48,7 +48,7 @@ internal class SetupInvoicePayoutPresenter( LaunchedEffect(connectIteration) { isLoading = true errorMessage = null - setupInvoicePayoutUseCase.invoke().fold( + setupInvoicePayinUseCase.invoke().fold( ifLeft = { isLoading = false errorMessage = it.message ?: "Something went wrong, please try again" diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt index 5502cb9051..a81161d98e 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt @@ -7,8 +7,7 @@ import com.hedvig.android.apollo.ErrorMessage import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage -import octopus.SetupInvoicePayoutMutation -import octopus.type.PaymentMethodInvoiceDelivery +import octopus.SetupInvoicePayinMutation import octopus.type.PaymentMethodSetupStatus internal class SetupInvoicePayoutUseCase( @@ -17,7 +16,7 @@ internal class SetupInvoicePayoutUseCase( ) { suspend fun invoke(): Either = either { val result = apolloClient - .mutation(SetupInvoicePayoutMutation()) + .mutation(SetupInvoicePayinMutation()) .safeExecute(::ErrorMessage) .bind() From 4ce872cf423c956f428d1be832684ec1d672a240 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 20 May 2026 13:41:27 +0200 Subject: [PATCH 03/30] di and renaming --- app/app/build.gradle.kts | 1 + .../android/app/di/ApplicationModule.kt | 2 + .../account/data/GetPayinAccountUseCase.kt | 105 ++++++++++++++++++ .../account/data/SetupSwishPayinUseCase.kt | 19 ++++ .../payin/account/di/PayinAccountModule.kt | 24 ++++ .../payin/account/navigation/PayinNavGraph.kt | 22 ++-- ....kt => PayinAccountOverviewDestination.kt} | 27 ++--- .../overview/PayinAccountOverviewViewModel.kt | 76 +++++++++++++ .../PayoutAccountOverviewViewModel.kt | 76 ------------- .../SetupInvoicePayinDestination.kt | 8 +- .../ui/setupswish/SetupSwishPayinViewModel.kt | 1 + .../data/SetupInvoicePayoutUseCase.kt | 34 ------ .../payoutaccount/di/PayoutAccountModule.kt | 4 - 13 files changed, 258 insertions(+), 141 deletions(-) create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt rename app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/{PayoutAccountOverviewDestination.kt => PayinAccountOverviewDestination.kt} (93%) create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt delete mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt delete mode 100644 app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts index d6ea697d9e..9476e93990 100644 --- a/app/app/build.gradle.kts +++ b/app/app/build.gradle.kts @@ -206,6 +206,7 @@ dependencies { implementation(projects.featureMovingflow) implementation(projects.featureRemoveAddons) + implementation(projects.featurePayinAccount) implementation(projects.featurePayoutAccount) implementation(projects.featurePayments) implementation(projects.featureProfile) 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 ff9b0703c5..1972f0d44c 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 @@ -78,6 +78,7 @@ import com.hedvig.android.feature.insurance.certificate.di.insuranceEvidenceModu 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.payin.account.di.payinAccountModule import com.hedvig.android.feature.payments.di.paymentsModule import com.hedvig.android.feature.payoutaccount.di.payoutAccountModule import com.hedvig.android.feature.profile.di.profileModule @@ -346,6 +347,7 @@ val applicationModule = module { notificationBadgeModule, notificationModule, payoutAccountModule, + payinAccountModule, paymentsModule, profileModule, settingsDatastoreModule, diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt new file mode 100644 index 0000000000..593d91f13d --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -0,0 +1,105 @@ +package com.hedvig.android.feature.payin.account.data + +import arrow.core.Either +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.GetPayinMethodsQuery +import octopus.GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod.Details.Companion.asPaymentMethodBankAccountDetails +import octopus.GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod.Details.Companion.asPaymentMethodSwishDetails +import octopus.type.MemberPaymentMethodStatus +import octopus.type.MemberPaymentProvider + +internal data class PayinAccountData( + val currentMethod: PayinAccount?, + val availablePayoutMethods: List, +) + +internal class GetPayinAccountUseCase( + private val apolloClient: ApolloClient, +) { + suspend fun invoke(): Either = either { + val result = apolloClient + .query(GetPayinMethodsQuery()) + .fetchPolicy(FetchPolicy.NetworkOnly) + .safeExecute(::ErrorMessage) + .bind() + + val paymentMethods = result.currentMember.paymentMethods + val defaultPayoutMethod = paymentMethods.payinMethods.firstOrNull { it.isDefault } + // todo. Return the other, non-default payin methods when we can switch to them + val currentMethod = defaultPayoutMethod?.let { method -> + val isPending = method.status == MemberPaymentMethodStatus.PENDING + when (method.provider) { + MemberPaymentProvider.SWISH -> { + val phoneNumber = method.details?.asPaymentMethodSwishDetails()?.phoneNumber + PayinAccount.SwishPayin(phoneNumber = phoneNumber, isPending = isPending) + } + + MemberPaymentProvider.TRUSTLY -> { + val (clearingNumber, accountNumber, bankName) = parseBankAccountDetails(method) + PayinAccount.Trustly( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankName, + isPending = isPending, + ) + } + + else -> { + null + } + } + } + + val availablePayoutMethods = paymentMethods.availableMethods + .filter { it.supportsPayin } + .map { it.provider } + + PayinAccountData( + currentMethod = currentMethod, + availablePayoutMethods = availablePayoutMethods, + ) + } +} + +private data class ParsedBankAccountDetails( + val clearingNumber: String?, + val accountNumber: String?, + val bankName: String?, +) + +private fun parseBankAccountDetails( + method: GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod, +): ParsedBankAccountDetails { + val bankAccountDetails = method.details?.asPaymentMethodBankAccountDetails() + val account = bankAccountDetails?.account + val dashIndex = account?.indexOf('-') ?: -1 + val clearingNumber = if (dashIndex >= 0) account?.substring(0, dashIndex) else account + val accountNumber = if (dashIndex >= 0) account?.substring(dashIndex + 1) else null + return ParsedBankAccountDetails( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankAccountDetails?.bank, + ) +} + +internal sealed interface PayinAccount { + val isPending: Boolean + + data class Trustly( + val clearingNumber: String?, + val accountNumber: String?, + val bankName: String?, + override val isPending: Boolean, + ) : PayinAccount + + data class SwishPayin( + val phoneNumber: String?, + override val isPending: Boolean, + ) : PayinAccount +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt new file mode 100644 index 0000000000..f7e0ca5e7a --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt @@ -0,0 +1,19 @@ +package com.hedvig.android.feature.payin.account.data + +import arrow.core.Either +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.core.common.ErrorMessage + +internal interface SetupSwishPayinUseCase { + suspend fun invoke(): Either +} + +internal class SetupSwishPayinUseCaseImpl( + private val apolloClient: ApolloClient, + private val networkCacheManager: NetworkCacheManager, +): SetupSwishPayinUseCase { + override suspend fun invoke(): Either { + TODO("Not yet implemented") + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt new file mode 100644 index 0000000000..efab20f540 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt @@ -0,0 +1,24 @@ +package com.hedvig.android.feature.payin.account.di + +import com.apollographql.apollo.ApolloClient +import com.hedvig.android.apollo.NetworkCacheManager +import com.hedvig.android.feature.payin.account.data.GetPayinAccountUseCase +import com.hedvig.android.feature.payin.account.data.SetupInvoicePayinUseCase +import com.hedvig.android.feature.payin.account.data.SetupSwishPayinUseCase +import com.hedvig.android.feature.payin.account.data.SetupSwishPayinUseCaseImpl +import com.hedvig.android.feature.payin.account.ui.overview.PayinAccountOverviewViewModel +import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayinViewModel +import com.hedvig.android.feature.payin.account.ui.setupswish.SetupSwishPayinViewModel +import org.koin.core.module.dsl.viewModel +import org.koin.dsl.module + +val payinAccountModule = module { + single { + SetupSwishPayinUseCaseImpl(get(), get()) + } + single {GetPayinAccountUseCase(get())} + single { SetupInvoicePayinUseCase(get(), get()) } + viewModel { SetupInvoicePayinViewModel(get()) } + viewModel { SetupSwishPayinViewModel(get()) } + viewModel { PayinAccountOverviewViewModel(get()) } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt index 6993cc7c52..9ea82a0621 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -5,11 +5,14 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder import com.hedvig.android.design.system.hedvig.GlobalSnackBarState -import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewDestination -import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewUiState -import com.hedvig.android.feature.payin.account.ui.overview.PayoutAccountOverviewViewModel +import com.hedvig.android.feature.payin.account.ui.overview.PayinAccountOverviewDestination +import com.hedvig.android.feature.payin.account.ui.overview.PayinAccountOverviewUiState +import com.hedvig.android.feature.payin.account.ui.overview.PayinAccountOverviewViewModel +import com.hedvig.android.feature.payin.account.ui.selectmethod.SelectPayinMethodDestination import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayinDestination import com.hedvig.android.feature.payin.account.ui.setupinvoice.SetupInvoicePayinViewModel +import com.hedvig.android.feature.payin.account.ui.setupswish.SetupSwishPayinDestination +import com.hedvig.android.feature.payin.account.ui.setupswish.SetupSwishPayinViewModel import com.hedvig.android.navigation.compose.navDeepLinks import com.hedvig.android.navigation.compose.navdestination import com.hedvig.android.navigation.compose.navgraph @@ -31,11 +34,11 @@ fun NavGraphBuilder.payinAccountGraph( deepLinks = navDeepLinks(hedvigDeepLinkContainer.payout), ) { navdestination { - val viewModel: PayoutAccountOverviewViewModel = koinViewModel() - PayoutAccountOverviewDestination( + val viewModel: PayinAccountOverviewViewModel = koinViewModel() + PayinAccountOverviewDestination( viewModel = viewModel, onConnectPayoutMethodClicked = dropUnlessResumed { - val content = viewModel.uiState.value as? PayoutAccountOverviewUiState.Content + val content = viewModel.uiState.value as? PayinAccountOverviewUiState.Content navController.navigate( PayinAccountDestinations.SelectPayinMethod( availableProviders = content?.availablePayoutMethods?.map { it.rawValue } ?: emptyList(), @@ -54,7 +57,7 @@ fun NavGraphBuilder.payinAccountGraph( } navdestination { - SelectPayoutMethodDestination( + SelectPayinMethodDestination( availableProviders = this.availableProviders.map { MemberPaymentProvider.safeValueOf(it) }, onTrustlySelected = dropUnlessResumed { navigateToConnectPayment { @@ -63,7 +66,6 @@ fun NavGraphBuilder.payinAccountGraph( } } }, - onNordeaSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.EditBankAccount) }, onSwishSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupSwishPayin) }, onInvoiceSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupInvoicePayin) }, navigateUp = navController::navigateUp, @@ -71,8 +73,8 @@ fun NavGraphBuilder.payinAccountGraph( } navdestination { - val viewModel: SetupSwishPayoutViewModel = koinViewModel() - SetupSwishPayoutDestination( + val viewModel: SetupSwishPayinViewModel = koinViewModel() + SetupSwishPayinDestination( viewModel = viewModel, globalSnackBarState = globalSnackBarState, onSuccessfullyConnected = { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt similarity index 93% rename from app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewDestination.kt rename to app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index ee8c3407c8..70d0d87bf2 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider @@ -30,8 +29,6 @@ import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface -import com.hedvig.android.feature.payoutaccount.data.PayoutAccount -import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewUiState.Content import hedvig.resources.CHANGE_PAYOUT_METHOD_BUTTON_LABEL import hedvig.resources.MY_PAYMENT_UPDATING_MESSAGE import hedvig.resources.PAYMENTS_ACCOUNT @@ -51,8 +48,8 @@ import octopus.type.PaymentMethodInvoiceDelivery import org.jetbrains.compose.resources.stringResource @Composable -internal fun PayoutAccountOverviewDestination( - viewModel: PayoutAccountOverviewViewModel, +internal fun PayinAccountOverviewDestination( + viewModel: PayinAccountOverviewViewModel, onConnectPayoutMethodClicked: () -> Unit, navigateToConnectPayment: () -> Unit, navigateUp: () -> Unit, @@ -62,14 +59,14 @@ internal fun PayoutAccountOverviewDestination( uiState = uiState, onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, navigateToConnectPayment = navigateToConnectPayment, - onRetry = { viewModel.emit(PayoutAccountOverviewEvent.Retry) }, + onRetry = { viewModel.emit(PayinAccountOverviewEvent.Retry) }, navigateUp = navigateUp, ) } @Composable private fun PayoutAccountOverviewScreen( - uiState: PayoutAccountOverviewUiState, + uiState: PayinAccountOverviewUiState, onConnectPayoutMethodClicked: () -> Unit, navigateToConnectPayment: () -> Unit, onRetry: () -> Unit, @@ -81,7 +78,7 @@ private fun PayoutAccountOverviewScreen( modifier = Modifier.fillMaxSize(), ) { when (uiState) { - PayoutAccountOverviewUiState.Loading -> { + PayinAccountOverviewUiState.Loading -> { HedvigFullScreenCenterAlignedProgressDebounced( Modifier .weight(1f) @@ -89,7 +86,7 @@ private fun PayoutAccountOverviewScreen( ) } - PayoutAccountOverviewUiState.Error -> { + PayinAccountOverviewUiState.Error -> { HedvigErrorSection( onButtonClick = onRetry, modifier = Modifier @@ -98,7 +95,7 @@ private fun PayoutAccountOverviewScreen( ) } - PayoutAccountOverviewUiState.NoPayoutOptions -> { + PayinAccountOverviewUiState.NoPayinOptions -> { HedvigInformationSection( title = stringResource(Res.string.PAYOUT_NO_PAYOUT_OPTIONS_TITLE), subTitle = stringResource(Res.string.PAYOUT_NO_PAYOUT_OPTIONS_SUBTITLE), @@ -244,7 +241,7 @@ private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: Stri @Composable @HedvigPreview private fun PreviewPayoutAccountOverviewScreen( - @PreviewParameter(PayoutAccountOverviewUiStateProvider::class) uiState: PayoutAccountOverviewUiState, + @PreviewParameter(PayoutAccountOverviewUiStateProvider::class) uiState: PayinAccountOverviewUiState, ) { HedvigTheme { Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { @@ -259,11 +256,11 @@ private fun PreviewPayoutAccountOverviewScreen( } } -private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterProvider( +private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterProvider( listOf( - PayoutAccountOverviewUiState.Loading, - PayoutAccountOverviewUiState.Error, - PayoutAccountOverviewUiState.NoPayoutOptions, + PayinAccountOverviewUiState.Loading, + PayinAccountOverviewUiState.Error, + PayinAccountOverviewUiState.NoPayinOptions, Content( currentMethod = null, availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt new file mode 100644 index 0000000000..cfd2781fbf --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt @@ -0,0 +1,76 @@ +package com.hedvig.android.feature.payin.account.ui.overview + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import com.hedvig.android.feature.payin.account.data.GetPayinAccountUseCase +import com.hedvig.android.feature.payin.account.data.PayinAccount +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 PayinAccountOverviewViewModel( + getPayinAccountUseCase: GetPayinAccountUseCase, +) : MoleculeViewModel( + PayinAccountOverviewUiState.Loading, + PayinAccountOverviewPresenter(getPayinAccountUseCase), +) + +internal sealed interface PayinAccountOverviewEvent { + data object Retry : PayinAccountOverviewEvent +} + +internal sealed interface PayinAccountOverviewUiState { + data object Loading : PayinAccountOverviewUiState + + data object Error : PayinAccountOverviewUiState + + data object NoPayinOptions : PayinAccountOverviewUiState + + data class Content( + val currentMethod: PayinAccount?, + val availablePayoutMethods: List, + ) : PayinAccountOverviewUiState +} + +internal class PayinAccountOverviewPresenter( + private val getPayinAccountUseCase: GetPayinAccountUseCase, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present( + lastState: PayinAccountOverviewUiState, + ): PayinAccountOverviewUiState { + var loadIteration by remember { mutableIntStateOf(0) } + var uiState by remember { mutableStateOf(lastState) } + + LaunchedEffect(loadIteration) { + uiState = PayinAccountOverviewUiState.Loading + getPayinAccountUseCase.invoke().fold( + ifLeft = { uiState = PayinAccountOverviewUiState.Error }, + ifRight = { data -> + uiState = if (data.currentMethod == null && data.availablePayoutMethods.isEmpty()) { + PayinAccountOverviewUiState.NoPayinOptions + } else { + PayinAccountOverviewUiState.Content( + currentMethod = data.currentMethod, + availablePayoutMethods = data.availablePayoutMethods, + ) + } + }, + ) + } + + CollectEvents { event -> + when (event) { + PayinAccountOverviewEvent.Retry -> loadIteration++ + } + } + + return uiState + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt deleted file mode 100644 index b0b07e1299..0000000000 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayoutAccountOverviewViewModel.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.hedvig.android.feature.payin.account.ui.overview - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -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 -import octopus.type.MemberPaymentProvider - -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 object NoPayoutOptions : PayoutAccountOverviewUiState - - data class Content( - val currentMethod: PayoutAccount?, - val availablePayoutMethods: List, - ) : PayoutAccountOverviewUiState -} - -internal class PayoutAccountOverviewPresenter( - private val getPayoutAccountUseCase: GetPayoutAccountUseCase, -) : MoleculePresenter { - @Composable - override fun MoleculePresenterScope.present( - lastState: PayoutAccountOverviewUiState, - ): PayoutAccountOverviewUiState { - var loadIteration by remember { mutableIntStateOf(0) } - var uiState by remember { mutableStateOf(lastState) } - - LaunchedEffect(loadIteration) { - uiState = PayoutAccountOverviewUiState.Loading - getPayoutAccountUseCase.invoke().fold( - ifLeft = { uiState = PayoutAccountOverviewUiState.Error }, - ifRight = { data -> - uiState = if (data.currentMethod == null && data.availablePayoutMethods.isEmpty()) { - PayoutAccountOverviewUiState.NoPayoutOptions - } else { - PayoutAccountOverviewUiState.Content( - currentMethod = data.currentMethod, - availablePayoutMethods = data.availablePayoutMethods, - ) - } - }, - ) - } - - CollectEvents { event -> - when (event) { - PayoutAccountOverviewEvent.Retry -> loadIteration++ - } - } - - return uiState - } -} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt index 29fe46468f..82e2ec00ce 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt @@ -103,9 +103,13 @@ private fun PreviewPayoutAccountOverviewScreen() { HedvigTheme { Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { SetupInvoicePayoutScreen( - uiState = SetupInvoicePayoutUiState(false, null), + uiState = SetupInvoicePayoutUiState( + false, + null, + showSuccessSnackBar = false + ), globalSnackBarState = GlobalSnackBarState(), - {} + {}, {} ,{} ) } } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt index 32ddaf73d8..dc942c0abc 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.feature.payin.account.data.SetupSwishPayinUseCase import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt deleted file mode 100644 index a81161d98e..0000000000 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt +++ /dev/null @@ -1,34 +0,0 @@ -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.SetupInvoicePayinMutation -import octopus.type.PaymentMethodSetupStatus - -internal class SetupInvoicePayoutUseCase( - private val apolloClient: ApolloClient, - private val networkCacheManager: NetworkCacheManager, -) { - suspend fun invoke(): Either = either { - val result = apolloClient - .mutation(SetupInvoicePayinMutation()) - .safeExecute(::ErrorMessage) - .bind() - - val output = result.paymentMethodSetupInvoicePayin - when (output.status) { - PaymentMethodSetupStatus.FAILED -> { - raise(ErrorMessage(output.error?.message ?: "Failed to set up invoice payout")) - } - - else -> { - networkCacheManager.clearCache() - } - } - } -} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt index b7d1f52094..580b17fe9b 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/di/PayoutAccountModule.kt @@ -3,12 +3,10 @@ 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.SetupInvoicePayoutUseCase import com.hedvig.android.feature.payoutaccount.data.SetupNordeaPayoutUseCase import com.hedvig.android.feature.payoutaccount.data.SetupSwishPayoutUseCase import com.hedvig.android.feature.payoutaccount.ui.editbankaccount.EditBankAccountViewModel import com.hedvig.android.feature.payoutaccount.ui.overview.PayoutAccountOverviewViewModel -import com.hedvig.android.feature.payoutaccount.ui.setupinvoice.SetupInvoicePayoutViewModel import com.hedvig.android.feature.payoutaccount.ui.setupswish.SetupSwishPayoutViewModel import org.koin.core.module.dsl.viewModel import org.koin.dsl.module @@ -17,9 +15,7 @@ val payoutAccountModule = module { single { GetPayoutAccountUseCase(get()) } single { SetupNordeaPayoutUseCase(get(), get()) } single { SetupSwishPayoutUseCase(get(), get()) } - single { SetupInvoicePayoutUseCase(get(), get()) } viewModel { PayoutAccountOverviewViewModel(get()) } viewModel { EditBankAccountViewModel(get()) } viewModel { SetupSwishPayoutViewModel(get()) } - viewModel { SetupInvoicePayoutViewModel(get()) } } From 3e9a3d1dbdaca6c3c53021046afa215c3c6dab4c Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 20 May 2026 14:18:37 +0200 Subject: [PATCH 04/30] PayinAccountOverviewDestination --- .../account/data/GetPayinAccountUseCase.kt | 21 ++- .../PayinAccountOverviewDestination.kt | 159 +++++------------- .../overview/PayinAccountOverviewViewModel.kt | 11 +- .../SetupInvoicePayinDestination.kt | 19 ++- .../SetupInvoicePayinViewModel.kt | 18 +- ...ation.kt => SetupSwishPayinDestination.kt} | 25 +++ .../ui/setupswish/SetupSwishPayinViewModel.kt | 11 +- .../data/GetPayoutAccountUseCase.kt | 10 -- .../payoutaccount/data/PayoutAccount.kt | 7 - .../PayoutAccountOverviewDestination.kt | 23 --- 10 files changed, 112 insertions(+), 192 deletions(-) rename app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/{SetupSwishPayoutDestination.kt => SetupSwishPayinDestination.kt} (85%) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt index 593d91f13d..333f3fa145 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -10,13 +10,15 @@ import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage import octopus.GetPayinMethodsQuery import octopus.GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod.Details.Companion.asPaymentMethodBankAccountDetails +import octopus.GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod.Details.Companion.asPaymentMethodInvoiceDetails import octopus.GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod.Details.Companion.asPaymentMethodSwishDetails import octopus.type.MemberPaymentMethodStatus import octopus.type.MemberPaymentProvider +import octopus.type.PaymentMethodInvoiceDelivery internal data class PayinAccountData( val currentMethod: PayinAccount?, - val availablePayoutMethods: List, + val availablePayinMethods: List, ) internal class GetPayinAccountUseCase( @@ -50,6 +52,15 @@ internal class GetPayinAccountUseCase( ) } + MemberPaymentProvider.INVOICE -> { + val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() + PayinAccount.Invoice( + delivery = invoiceDetails?.delivery, + email = invoiceDetails?.email, + isPending = isPending, + ) + } + else -> { null } @@ -62,7 +73,7 @@ internal class GetPayinAccountUseCase( PayinAccountData( currentMethod = currentMethod, - availablePayoutMethods = availablePayoutMethods, + availablePayinMethods = availablePayoutMethods, ) } } @@ -102,4 +113,10 @@ internal sealed interface PayinAccount { val phoneNumber: String?, override val isPending: Boolean, ) : PayinAccount + + data class Invoice( + val delivery: PaymentMethodInvoiceDelivery?, + val email: String?, + override val isPending: Boolean, + ) : PayinAccount } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index 70d0d87bf2..d27d4e902f 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -20,7 +20,6 @@ import com.hedvig.android.design.system.hedvig.EmptyStateDefaults 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.HedvigInformationSection import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold @@ -29,51 +28,40 @@ import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface -import hedvig.resources.CHANGE_PAYOUT_METHOD_BUTTON_LABEL -import hedvig.resources.MY_PAYMENT_UPDATING_MESSAGE +import com.hedvig.android.feature.payin.account.data.PayinAccount import hedvig.resources.PAYMENTS_ACCOUNT import hedvig.resources.PAYMENTS_INVOICE -import hedvig.resources.PAYOUT_MISSING_INFO -import hedvig.resources.PAYOUT_NO_PAYOUT_OPTIONS_SUBTITLE -import hedvig.resources.PAYOUT_NO_PAYOUT_OPTIONS_TITLE -import hedvig.resources.PAYOUT_PAGE_HEADING -import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD -import hedvig.resources.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON import hedvig.resources.REFERRAL_PENDING_STATUS_LABEL import hedvig.resources.Res import hedvig.resources.swish import hedvig.resources.trustly import octopus.type.MemberPaymentProvider -import octopus.type.PaymentMethodInvoiceDelivery import org.jetbrains.compose.resources.stringResource @Composable internal fun PayinAccountOverviewDestination( viewModel: PayinAccountOverviewViewModel, onConnectPayoutMethodClicked: () -> Unit, - navigateToConnectPayment: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - PayoutAccountOverviewScreen( + PayinAccountOverviewScreen( uiState = uiState, onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, - navigateToConnectPayment = navigateToConnectPayment, onRetry = { viewModel.emit(PayinAccountOverviewEvent.Retry) }, navigateUp = navigateUp, ) } @Composable -private fun PayoutAccountOverviewScreen( +private fun PayinAccountOverviewScreen( uiState: PayinAccountOverviewUiState, onConnectPayoutMethodClicked: () -> Unit, - navigateToConnectPayment: () -> Unit, onRetry: () -> Unit, navigateUp: () -> Unit, ) { HedvigScaffold( - topAppBarText = stringResource(Res.string.PAYOUT_PAGE_HEADING), + topAppBarText = "Billing account", //todo! navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { @@ -95,23 +83,11 @@ private fun PayoutAccountOverviewScreen( ) } - PayinAccountOverviewUiState.NoPayinOptions -> { - HedvigInformationSection( - title = stringResource(Res.string.PAYOUT_NO_PAYOUT_OPTIONS_TITLE), - subTitle = stringResource(Res.string.PAYOUT_NO_PAYOUT_OPTIONS_SUBTITLE), - buttonText = stringResource(Res.string.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON), - onButtonClick = navigateToConnectPayment, - modifier = Modifier - .weight(1f) - .wrapContentHeight(), - ) - } - - is Content -> { + is PayinAccountOverviewUiState.Content -> { PayoutAccountContent( currentMethod = uiState.currentMethod, - availablePayoutMethods = uiState.availablePayoutMethods, - onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, + availablePayinMethods = uiState.availablePayoutMethods, + onConnectPayinMethodClicked = onConnectPayoutMethodClicked, modifier = Modifier.weight(1f), ) } @@ -121,28 +97,28 @@ private fun PayoutAccountOverviewScreen( @Composable private fun PayoutAccountContent( - currentMethod: PayoutAccount?, - availablePayoutMethods: List, - onConnectPayoutMethodClicked: () -> Unit, + currentMethod: PayinAccount?, + availablePayinMethods: List, + onConnectPayinMethodClicked: () -> Unit, modifier: Modifier = Modifier, ) { Column(modifier) { Spacer(Modifier.height(8.dp)) when (currentMethod) { null -> { - if (availablePayoutMethods.isNotEmpty()) { + if (availablePayinMethods.isNotEmpty()) { Spacer(Modifier.weight(1f)) EmptyState( - text = stringResource(Res.string.PAYOUT_MISSING_INFO), + text = "You haven’t added a billing method yet. Add one to pay for your insurance.", //todo description = null, iconStyle = EmptyStateDefaults.EmptyStateIconStyle.INFO, ) } } - is PayoutAccount.SwishPayout -> { + is PayinAccount.SwishPayin -> { val phoneNumber = currentMethod.phoneNumber.orEmpty() - PayoutAccountReadOnlyTextField( + PayinAccountReadOnlyTextField( label = stringResource(Res.string.swish), text = if (currentMethod.isPending && phoneNumber.isBlank()) { stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) @@ -152,9 +128,9 @@ private fun PayoutAccountContent( ) } - is PayoutAccount.Trustly -> { + is PayinAccount.Trustly -> { val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) - PayoutAccountReadOnlyTextField( + PayinAccountReadOnlyTextField( label = formatBankAccountLabel(stringResource(Res.string.trustly), currentMethod.bankName), text = if (currentMethod.isPending && accountNumber.isBlank()) { stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) @@ -164,44 +140,32 @@ private fun PayoutAccountContent( ) } - is PayoutAccount.Invoice -> { - PayoutAccountReadOnlyTextField( + is PayinAccount.Invoice -> { + PayinAccountReadOnlyTextField( stringResource(Res.string.PAYMENTS_ACCOUNT), stringResource(Res.string.PAYMENTS_INVOICE), ) } - - is PayoutAccount.BankAccount -> { - val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) - PayoutAccountReadOnlyTextField( - label = formatBankAccountLabel(stringResource(Res.string.PAYMENTS_ACCOUNT), currentMethod.bankName), - text = if (currentMethod.isPending && accountNumber.isBlank()) { - stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) - } else { - accountNumber - }, - ) - } } Spacer(Modifier.weight(1f)) Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { if (currentMethod?.isPending == true) { HedvigNotificationCard( - message = stringResource(Res.string.MY_PAYMENT_UPDATING_MESSAGE), + message = "You have just added or changed your billing method, it will appear here soon.", //todo priority = NotificationPriority.Info, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), ) } - if (availablePayoutMethods.isNotEmpty()) { + if (availablePayinMethods.isNotEmpty()) { HedvigButton( text = if (currentMethod == null) { - stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD) + "Select billing method" //todo! } else { - stringResource(Res.string.CHANGE_PAYOUT_METHOD_BUTTON_LABEL) + "Change billing method" //todo! }, - onClick = onConnectPayoutMethodClicked, + onClick = onConnectPayinMethodClicked, enabled = true, modifier = Modifier .fillMaxWidth() @@ -214,7 +178,7 @@ private fun PayoutAccountContent( } @Composable -private fun PayoutAccountReadOnlyTextField(label: String, text: String, modifier: Modifier = Modifier) { +private fun PayinAccountReadOnlyTextField(label: String, text: String, modifier: Modifier = Modifier) { HedvigTextField( text = text, onValueChange = {}, @@ -240,15 +204,14 @@ private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: Stri @Composable @HedvigPreview -private fun PreviewPayoutAccountOverviewScreen( - @PreviewParameter(PayoutAccountOverviewUiStateProvider::class) uiState: PayinAccountOverviewUiState, +private fun PreviewPayinAccountOverviewScreen( + @PreviewParameter(PayinAccountOverviewUiStateProvider::class) uiState: PayinAccountOverviewUiState, ) { HedvigTheme { Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { - PayoutAccountOverviewScreen( + PayinAccountOverviewScreen( uiState = uiState, onConnectPayoutMethodClicked = {}, - navigateToConnectPayment = {}, onRetry = {}, navigateUp = {}, ) @@ -256,33 +219,32 @@ private fun PreviewPayoutAccountOverviewScreen( } } -private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterProvider( +private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterProvider( listOf( PayinAccountOverviewUiState.Loading, PayinAccountOverviewUiState.Error, - PayinAccountOverviewUiState.NoPayinOptions, - Content( + PayinAccountOverviewUiState.Content( currentMethod = null, availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), - Content( - currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = false), + PayinAccountOverviewUiState.Content( + currentMethod = PayinAccount.SwishPayin(phoneNumber = "070-123 45 67", isPending = false), availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), ), - Content( - currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = false), + PayinAccountOverviewUiState.Content( + currentMethod = PayinAccount.SwishPayin(phoneNumber = "070-123 45 67", isPending = false), availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), - Content( - currentMethod = PayoutAccount.SwishPayout(phoneNumber = null, isPending = true), + PayinAccountOverviewUiState.Content( + currentMethod = PayinAccount.SwishPayin(phoneNumber = null, isPending = true), availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), ), - Content( - currentMethod = PayoutAccount.SwishPayout(phoneNumber = "070-123 45 67", isPending = true), + PayinAccountOverviewUiState.Content( + currentMethod = PayinAccount.SwishPayin(phoneNumber = "070-123 45 67", isPending = true), availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), ), - Content( - currentMethod = PayoutAccount.Trustly( + PayinAccountOverviewUiState.Content( + currentMethod = PayinAccount.Trustly( clearingNumber = "8327", accountNumber = "12345678", bankName = "Mock Swedbank", @@ -290,48 +252,5 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP ), availablePayoutMethods = listOf(MemberPaymentProvider.TRUSTLY), ), - Content( - currentMethod = PayoutAccount.BankAccount( - clearingNumber = "3300", - accountNumber = "1234567", - bankName = "Nordea", - isPending = false, - ), - availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), - ), - Content( - currentMethod = PayoutAccount.BankAccount( - clearingNumber = null, - accountNumber = null, - bankName = null, - isPending = true, - ), - availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), - ), - Content( - currentMethod = PayoutAccount.BankAccount( - clearingNumber = "3300", - accountNumber = "1234567", - bankName = "Nordea", - isPending = true, - ), - availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), - ), - Content( - currentMethod = PayoutAccount.Invoice( - delivery = PaymentMethodInvoiceDelivery.KIVRA, - email = null, - isPending = false, - ), - availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE), - ), - Content( - currentMethod = PayoutAccount.Invoice( - delivery = PaymentMethodInvoiceDelivery.MAIL, - email = "user@example.com", - isPending = false, - ), - availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE, MemberPaymentProvider.TRUSTLY), - ), ), ) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt index cfd2781fbf..94427835f3 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt @@ -30,8 +30,6 @@ internal sealed interface PayinAccountOverviewUiState { data object Error : PayinAccountOverviewUiState - data object NoPayinOptions : PayinAccountOverviewUiState - data class Content( val currentMethod: PayinAccount?, val availablePayoutMethods: List, @@ -53,14 +51,11 @@ internal class PayinAccountOverviewPresenter( getPayinAccountUseCase.invoke().fold( ifLeft = { uiState = PayinAccountOverviewUiState.Error }, ifRight = { data -> - uiState = if (data.currentMethod == null && data.availablePayoutMethods.isEmpty()) { - PayinAccountOverviewUiState.NoPayinOptions - } else { - PayinAccountOverviewUiState.Content( + uiState = PayinAccountOverviewUiState.Content( currentMethod = data.currentMethod, - availablePayoutMethods = data.availablePayoutMethods, + availablePayoutMethods = data.availablePayinMethods, ) - } + }, ) } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt index 82e2ec00ce..680955d469 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt @@ -3,6 +3,7 @@ package com.hedvig.android.feature.payin.account.ui.setupinvoice 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 @@ -19,6 +20,7 @@ import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface @@ -35,7 +37,7 @@ internal fun SetupInvoicePayinDestination( navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - SetupInvoicePayoutScreen( + SetupInvoicePayinScreen( uiState = uiState, globalSnackBarState = globalSnackBarState, onConnect = { viewModel.emit(SetupInvoicePayoutEvent.Connect) }, @@ -48,8 +50,8 @@ internal fun SetupInvoicePayinDestination( } @Composable -private fun SetupInvoicePayoutScreen( - uiState: SetupInvoicePayoutUiState, +private fun SetupInvoicePayinScreen( + uiState: SetupInvoicePayinUiState, globalSnackBarState: GlobalSnackBarState, onConnect: () -> Unit, showedSnackBar: () -> Unit, @@ -67,7 +69,10 @@ private fun SetupInvoicePayoutScreen( navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { - Spacer(Modifier.weight(1f)) + //todo: some text here?? + Column (Modifier.weight(1f)) { + HedvigText("You can choose invoice as you pay-in") + } AnimatedVisibility( visible = uiState.errorMessage != null, enter = expandVertically(), @@ -84,7 +89,7 @@ private fun SetupInvoicePayoutScreen( } Spacer(Modifier.height(16.dp)) HedvigButton( - text = "Connect", + text = "Connect", //todo onClick = onConnect, enabled = !uiState.isLoading, isLoading = uiState.isLoading, @@ -102,8 +107,8 @@ private fun SetupInvoicePayoutScreen( private fun PreviewPayoutAccountOverviewScreen() { HedvigTheme { Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { - SetupInvoicePayoutScreen( - uiState = SetupInvoicePayoutUiState( + SetupInvoicePayinScreen( + uiState = SetupInvoicePayinUiState( false, null, showSuccessSnackBar = false diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt index b5ebbbd121..c5364c5f65 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt @@ -14,9 +14,9 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class SetupInvoicePayinViewModel( setupInvoicePayinUseCase: SetupInvoicePayinUseCase, -) : MoleculeViewModel( - SetupInvoicePayoutUiState(false, null, false), - SetupInvoicePayoutPresenter(setupInvoicePayinUseCase), +) : MoleculeViewModel( + SetupInvoicePayinUiState(false, null, false), + SetupInvoicePayinPresenter(setupInvoicePayinUseCase), ) internal sealed interface SetupInvoicePayoutEvent { @@ -25,19 +25,19 @@ internal sealed interface SetupInvoicePayoutEvent { data object ShowedSnackBar : SetupInvoicePayoutEvent } -internal data class SetupInvoicePayoutUiState( +internal data class SetupInvoicePayinUiState( val isLoading: Boolean, val errorMessage: String?, val showSuccessSnackBar: Boolean, ) -internal class SetupInvoicePayoutPresenter( +internal class SetupInvoicePayinPresenter( private val setupInvoicePayinUseCase: SetupInvoicePayinUseCase, -) : MoleculePresenter { +) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present( - lastState: SetupInvoicePayoutUiState, - ): SetupInvoicePayoutUiState { + lastState: SetupInvoicePayinUiState, + ): SetupInvoicePayinUiState { var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } var showSuccessSnackBar by remember { mutableStateOf(false) } @@ -78,7 +78,7 @@ internal class SetupInvoicePayoutPresenter( } } - return SetupInvoicePayoutUiState( + return SetupInvoicePayinUiState( isLoading = isLoading, errorMessage = errorMessage, showSuccessSnackBar = showSuccessSnackBar, diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayoutDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt similarity index 85% rename from app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayoutDestination.kt rename to app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index fdf86594f6..4bee2c08d6 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayoutDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -10,6 +10,7 @@ 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.foundation.text.input.TextFieldState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -20,10 +21,13 @@ 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.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults +import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority +import com.hedvig.android.design.system.hedvig.Surface import hedvig.resources.CONTACT_INFO_CHANGES_SAVED import hedvig.resources.ODYSSEY_PHONE_NUMBER_LABEL import hedvig.resources.Res @@ -114,3 +118,24 @@ private fun SetupSwishPayoutScreen( Spacer(Modifier.height(16.dp)) } } + +@Composable +@HedvigPreview +private fun PreviewSetupSwishPayinScreen() { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + SetupSwishPayoutScreen( + uiState = SetupSwishPayoutUiState( + phoneNumberState = TextFieldState(), + isLoading = false, + errorMessage = null, + showSuccessSnackBar = false, + ), + globalSnackBarState = GlobalSnackBarState(), + onSave = {}, + showedSnackBar = {}, + navigateUp = {}, + ) + } + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt index dc942c0abc..a365317fa4 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt @@ -9,17 +9,16 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.feature.payin.account.data.SetupSwishPayinUseCase -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 SetupSwishPayinViewModel( - setupSwishPayoutUseCase: SetupSwishPayoutUseCase, + setupSwishPayoutUseCase: SetupSwishPayinUseCase, ) : MoleculeViewModel( - SetupSwishPayoutUiState(TextFieldState(), false, null, false), - SetupSwishPayoutPresenter(setupSwishPayoutUseCase), - ) + SetupSwishPayoutUiState(TextFieldState(), false, null, false), + SetupSwishPayoutPresenter(setupSwishPayoutUseCase), +) internal sealed interface SetupSwishPayoutEvent { data object Save : SetupSwishPayoutEvent @@ -52,7 +51,7 @@ internal class SetupSwishPayoutPresenter( LaunchedEffect(currentSave) { isLoading = true errorMessage = null - setupSwishPayoutUseCase.invoke(currentSave).fold( + setupSwishPayoutUseCase.invoke().fold( ifLeft = { isLoading = false errorMessage = it 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 3ad7b40f77..79373b55ce 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/GetPayoutAccountUseCase.kt @@ -10,7 +10,6 @@ import com.hedvig.android.apollo.safeExecute import com.hedvig.android.core.common.ErrorMessage import octopus.GetPayoutMethodsQuery import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.PayoutMethod.Details.Companion.asPaymentMethodBankAccountDetails -import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.PayoutMethod.Details.Companion.asPaymentMethodInvoiceDetails import octopus.GetPayoutMethodsQuery.Data.CurrentMember.PaymentMethods.PayoutMethod.Details.Companion.asPaymentMethodSwishDetails import octopus.type.MemberPaymentMethodStatus import octopus.type.MemberPaymentProvider @@ -41,15 +40,6 @@ internal class GetPayoutAccountUseCase( PayoutAccount.SwishPayout(phoneNumber = phoneNumber, isPending = isPending) } - MemberPaymentProvider.INVOICE -> { - val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() - PayoutAccount.Invoice( - delivery = invoiceDetails?.delivery, - email = invoiceDetails?.email, - isPending = isPending, - ) - } - MemberPaymentProvider.TRUSTLY -> { val (clearingNumber, accountNumber, bankName) = parseBankAccountDetails(method) PayoutAccount.Trustly( 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 8a7b7b3bdc..a947a55ab0 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/PayoutAccount.kt @@ -1,6 +1,5 @@ package com.hedvig.android.feature.payoutaccount.data -import octopus.type.PaymentMethodInvoiceDelivery internal sealed interface PayoutAccount { val isPending: Boolean @@ -23,10 +22,4 @@ internal sealed interface PayoutAccount { val bankName: String?, override val isPending: Boolean, ) : PayoutAccount - - data class Invoice( - val delivery: PaymentMethodInvoiceDelivery?, - val email: String?, - override val isPending: Boolean, - ) : PayoutAccount } diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/overview/PayoutAccountOverviewDestination.kt index 859fd97808..d3f2870caf 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 @@ -167,13 +167,6 @@ private fun PayoutAccountContent( ) } - is PayoutAccount.Invoice -> { - PayoutAccountReadOnlyTextField( - stringResource(Res.string.PAYMENTS_ACCOUNT), - stringResource(Res.string.PAYMENTS_INVOICE), - ) - } - is PayoutAccount.BankAccount -> { val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) PayoutAccountReadOnlyTextField( @@ -320,21 +313,5 @@ private class PayoutAccountOverviewUiStateProvider : CollectionPreviewParameterP ), availablePayoutMethods = listOf(MemberPaymentProvider.NORDEA), ), - Content( - currentMethod = PayoutAccount.Invoice( - delivery = PaymentMethodInvoiceDelivery.KIVRA, - email = null, - isPending = false, - ), - availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE), - ), - Content( - currentMethod = PayoutAccount.Invoice( - delivery = PaymentMethodInvoiceDelivery.MAIL, - email = "user@example.com", - isPending = false, - ), - availablePayoutMethods = listOf(MemberPaymentProvider.INVOICE, MemberPaymentProvider.TRUSTLY), - ), ), ) From fb10cc910aa8c005172bf2d10ba3eb6bbdc35fe5 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 20 May 2026 14:39:31 +0200 Subject: [PATCH 05/30] navgraph change --- .../feature/payin/account/navigation/PayinNavGraph.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt index 9ea82a0621..ccf5914d3f 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -45,13 +45,6 @@ fun NavGraphBuilder.payinAccountGraph( ), ) }, - navigateToConnectPayment = dropUnlessResumed { - navigateToConnectPayment { - typedPopUpTo { - inclusive = true - } - } - }, navigateUp = navigateUp, ) } From d0d5a423cf5eb434220d75f56ba30b70ef442c21 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 20 May 2026 15:41:55 +0200 Subject: [PATCH 06/30] active methods first iteration --- .../android/app/navigation/HedvigNavHost.kt | 12 + .../account/data/GetPayinAccountUseCase.kt | 25 +- .../payin/account/navigation/PayinNavGraph.kt | 17 +- .../PayinAccountOverviewDestination.kt | 236 +++++++++++++----- .../overview/PayinAccountOverviewViewModel.kt | 8 +- .../SelectPayinMethodDestination.kt | 27 +- .../SetupInvoicePayinDestination.kt | 22 +- 7 files changed, 259 insertions(+), 88 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index aff77558a5..d9d6762d11 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.insurances.navigation.insuranceGraph 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.payin.account.navigation.PayinAccountDestination +import com.hedvig.android.feature.payin.account.navigation.payinAccountGraph import com.hedvig.android.feature.payments.navigation.paymentsGraph import com.hedvig.android.feature.payoutaccount.navigation.PayoutAccountDestination import com.hedvig.android.feature.payoutaccount.navigation.payoutAccountGraph @@ -110,6 +112,9 @@ internal fun HedvigNavHost( val navController = hedvigAppState.navController fun navigateToConnectPayment(builder: NavOptionsBuilder.() -> Unit = {}) { + navController.navigate(PayinAccountDestination.Graph, builder) + } + fun navigateToConnectTrustly(builder: NavOptionsBuilder.() -> Unit = {}) { navController.navigate(TrustlyDestination, builder) } val navigateToInbox = { @@ -357,6 +362,13 @@ internal fun HedvigNavHost( navigateToConnectPayment = ::navigateToConnectPayment, navigateUp = navController::navigateUp, ) + payinAccountGraph( + navController = navController, + globalSnackBarState = globalSnackBarState, + hedvigDeepLinkContainer = hedvigDeepLinkContainer, + navigateToConnectTrustly = ::navigateToConnectTrustly, + navigateUp = navController::navigateUp, + ) profileGraph( settingsDestinationNestedGraphs = { deleteAccountGraph(hedvigDeepLinkContainer, navController) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt index 333f3fa145..a52a637ff2 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -17,7 +17,7 @@ import octopus.type.MemberPaymentProvider import octopus.type.PaymentMethodInvoiceDelivery internal data class PayinAccountData( - val currentMethod: PayinAccount?, + val currentMethods: List, val availablePayinMethods: List, ) @@ -32,14 +32,17 @@ internal class GetPayinAccountUseCase( .bind() val paymentMethods = result.currentMember.paymentMethods - val defaultPayoutMethod = paymentMethods.payinMethods.firstOrNull { it.isDefault } - // todo. Return the other, non-default payin methods when we can switch to them - val currentMethod = defaultPayoutMethod?.let { method -> + + val currentMethods: List = paymentMethods.payinMethods.mapNotNull { method -> val isPending = method.status == MemberPaymentMethodStatus.PENDING when (method.provider) { MemberPaymentProvider.SWISH -> { val phoneNumber = method.details?.asPaymentMethodSwishDetails()?.phoneNumber - PayinAccount.SwishPayin(phoneNumber = phoneNumber, isPending = isPending) + PayinAccount.SwishPayin( + phoneNumber = phoneNumber, + isPending = isPending, + isDefault = method.isDefault, + ) } MemberPaymentProvider.TRUSTLY -> { @@ -49,6 +52,7 @@ internal class GetPayinAccountUseCase( accountNumber = accountNumber, bankName = bankName, isPending = isPending, + isDefault = method.isDefault ) } @@ -58,6 +62,7 @@ internal class GetPayinAccountUseCase( delivery = invoiceDetails?.delivery, email = invoiceDetails?.email, isPending = isPending, + isDefault = method.isDefault ) } @@ -67,13 +72,13 @@ internal class GetPayinAccountUseCase( } } - val availablePayoutMethods = paymentMethods.availableMethods + val availablePayinMethods = paymentMethods.availableMethods .filter { it.supportsPayin } .map { it.provider } PayinAccountData( - currentMethod = currentMethod, - availablePayinMethods = availablePayoutMethods, + currentMethods = currentMethods, + availablePayinMethods = availablePayinMethods, ) } } @@ -101,22 +106,26 @@ private fun parseBankAccountDetails( internal sealed interface PayinAccount { val isPending: Boolean + val isDefault: Boolean data class Trustly( val clearingNumber: String?, val accountNumber: String?, val bankName: String?, override val isPending: Boolean, + override val isDefault: Boolean, ) : PayinAccount data class SwishPayin( val phoneNumber: String?, override val isPending: Boolean, + override val isDefault: Boolean, ) : PayinAccount data class Invoice( val delivery: PaymentMethodInvoiceDelivery?, val email: String?, override val isPending: Boolean, + override val isDefault: Boolean, ) : PayinAccount } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt index ccf5914d3f..e7303c6e77 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -26,12 +26,12 @@ fun NavGraphBuilder.payinAccountGraph( navController: NavController, globalSnackBarState: GlobalSnackBarState, hedvigDeepLinkContainer: HedvigDeepLinkContainer, - navigateToConnectPayment: (builder: NavOptionsBuilder.() -> Unit) -> Unit, + navigateToConnectTrustly: (builder: NavOptionsBuilder.() -> Unit) -> Unit, navigateUp: () -> Unit, ) { navgraph( startDestination = PayinAccountDestinations.Overview::class, - deepLinks = navDeepLinks(hedvigDeepLinkContainer.payout), + deepLinks = navDeepLinks(hedvigDeepLinkContainer.connectPayment), ) { navdestination { val viewModel: PayinAccountOverviewViewModel = koinViewModel() @@ -41,11 +41,20 @@ fun NavGraphBuilder.payinAccountGraph( val content = viewModel.uiState.value as? PayinAccountOverviewUiState.Content navController.navigate( PayinAccountDestinations.SelectPayinMethod( - availableProviders = content?.availablePayoutMethods?.map { it.rawValue } ?: emptyList(), + availableProviders = content?.availablePayinMethods?.map { it.rawValue } ?: emptyList(), ), ) }, navigateUp = navigateUp, + onTrustlySelected = dropUnlessResumed { + navigateToConnectTrustly { + typedPopUpTo { + inclusive = true + } + } + }, + onSwishSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupSwishPayin) }, + onInvoiceSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupInvoicePayin) }, ) } @@ -53,7 +62,7 @@ fun NavGraphBuilder.payinAccountGraph( SelectPayinMethodDestination( availableProviders = this.availableProviders.map { MemberPaymentProvider.safeValueOf(it) }, onTrustlySelected = dropUnlessResumed { - navigateToConnectPayment { + navigateToConnectTrustly { typedPopUpTo { inclusive = true } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index d27d4e902f..1b9e599e0f 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -2,6 +2,7 @@ package com.hedvig.android.feature.payin.account.ui.overview import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider @@ -18,14 +20,19 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.design.system.hedvig.EmptyState import com.hedvig.android.design.system.hedvig.EmptyStateDefaults import com.hedvig.android.design.system.hedvig.HedvigButton +import com.hedvig.android.design.system.hedvig.HedvigCard import com.hedvig.android.design.system.hedvig.HedvigErrorSection import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProgressDebounced import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTextField import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.HighlightLabel +import com.hedvig.android.design.system.hedvig.HighlightLabelDefaults +import com.hedvig.android.design.system.hedvig.HorizontalItemsWithMaximumSpaceTaken import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface import com.hedvig.android.feature.payin.account.data.PayinAccount @@ -43,6 +50,9 @@ internal fun PayinAccountOverviewDestination( viewModel: PayinAccountOverviewViewModel, onConnectPayoutMethodClicked: () -> Unit, navigateUp: () -> Unit, + onTrustlySelected: () -> Unit, + onSwishSelected: () -> Unit, + onInvoiceSelected: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() PayinAccountOverviewScreen( @@ -50,6 +60,9 @@ internal fun PayinAccountOverviewDestination( onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, onRetry = { viewModel.emit(PayinAccountOverviewEvent.Retry) }, navigateUp = navigateUp, + onTrustlySelected = onTrustlySelected, + onSwishSelected = onSwishSelected, + onInvoiceSelected = onInvoiceSelected ) } @@ -59,6 +72,9 @@ private fun PayinAccountOverviewScreen( onConnectPayoutMethodClicked: () -> Unit, onRetry: () -> Unit, navigateUp: () -> Unit, + onTrustlySelected: () -> Unit, + onSwishSelected: () -> Unit, + onInvoiceSelected: () -> Unit, ) { HedvigScaffold( topAppBarText = "Billing account", //todo! @@ -85,10 +101,13 @@ private fun PayinAccountOverviewScreen( is PayinAccountOverviewUiState.Content -> { PayoutAccountContent( - currentMethod = uiState.currentMethod, - availablePayinMethods = uiState.availablePayoutMethods, + currentMethods = uiState.currentMethods, + availablePayinMethods = uiState.availablePayinMethods, onConnectPayinMethodClicked = onConnectPayoutMethodClicked, modifier = Modifier.weight(1f), + onTrustlySelected = onTrustlySelected, + onSwishSelected = onSwishSelected, + onInvoiceSelected = onInvoiceSelected ) } } @@ -97,61 +116,81 @@ private fun PayinAccountOverviewScreen( @Composable private fun PayoutAccountContent( - currentMethod: PayinAccount?, + currentMethods: List, availablePayinMethods: List, onConnectPayinMethodClicked: () -> Unit, + onTrustlySelected: () -> Unit, + onSwishSelected: () -> Unit, + onInvoiceSelected: () -> Unit, modifier: Modifier = Modifier, ) { Column(modifier) { Spacer(Modifier.height(8.dp)) - when (currentMethod) { - null -> { - if (availablePayinMethods.isNotEmpty()) { - Spacer(Modifier.weight(1f)) - EmptyState( - text = "You haven’t added a billing method yet. Add one to pay for your insurance.", //todo - description = null, - iconStyle = EmptyStateDefaults.EmptyStateIconStyle.INFO, - ) - } - } - - is PayinAccount.SwishPayin -> { - val phoneNumber = currentMethod.phoneNumber.orEmpty() - PayinAccountReadOnlyTextField( - label = stringResource(Res.string.swish), - text = if (currentMethod.isPending && phoneNumber.isBlank()) { - stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) - } else { - phoneNumber - }, + if (currentMethods.isEmpty()) { + if (availablePayinMethods.isNotEmpty()) { + Spacer(Modifier.weight(1f)) + EmptyState( + text = "You haven’t added a billing method yet. Add one to pay for your insurance.", //todo + description = null, + iconStyle = EmptyStateDefaults.EmptyStateIconStyle.INFO, ) } + } else { + HedvigText("Active billing methods", modifier = Modifier.padding(horizontal = 16.dp)) //todo + Spacer(Modifier.height(16.dp)) + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + currentMethods.forEach { method -> + when (method) { + is PayinAccount.SwishPayin -> { + val phoneNumber = method.phoneNumber.orEmpty() + CurrentPayinMethodRow( + label = stringResource(Res.string.swish), + text = if (method.isPending && phoneNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + phoneNumber + }, + isDefault = method.isDefault, + onClick = onSwishSelected, + modifier = Modifier.padding(horizontal = 16.dp) + ) + } - is PayinAccount.Trustly -> { - val accountNumber = formatBankAccountNumber(currentMethod.clearingNumber, currentMethod.accountNumber) - PayinAccountReadOnlyTextField( - label = formatBankAccountLabel(stringResource(Res.string.trustly), currentMethod.bankName), - text = if (currentMethod.isPending && accountNumber.isBlank()) { - stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) - } else { - accountNumber - }, - ) - } + is PayinAccount.Trustly -> { + val accountNumber = formatBankAccountNumber(method.clearingNumber, method.accountNumber) + CurrentPayinMethodRow( + label = formatBankAccountLabel(stringResource(Res.string.trustly), method.bankName), + text = if (method.isPending && accountNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + accountNumber + }, + isDefault = method.isDefault, + onClick = onTrustlySelected, + modifier = Modifier.padding(horizontal = 16.dp) + ) + } - is PayinAccount.Invoice -> { - PayinAccountReadOnlyTextField( - stringResource(Res.string.PAYMENTS_ACCOUNT), - stringResource(Res.string.PAYMENTS_INVOICE), - ) + is PayinAccount.Invoice -> { + CurrentPayinMethodRow( + stringResource(Res.string.PAYMENTS_ACCOUNT), + stringResource(Res.string.PAYMENTS_INVOICE), + isDefault = method.isDefault, + onClick = onInvoiceSelected, + modifier = Modifier.padding(horizontal = 16.dp) + ) + } + } + } } } Spacer(Modifier.weight(1f)) Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { - if (currentMethod?.isPending == true) { + if (currentMethods.any { it.isPending }) { HedvigNotificationCard( - message = "You have just added or changed your billing method, it will appear here soon.", //todo + message = "You have just added or changed a billing method, it will appear here soon.", //todo priority = NotificationPriority.Info, modifier = Modifier .fillMaxWidth() @@ -160,11 +199,7 @@ private fun PayoutAccountContent( } if (availablePayinMethods.isNotEmpty()) { HedvigButton( - text = if (currentMethod == null) { - "Select billing method" //todo! - } else { - "Change billing method" //todo! - }, + text = "Add a billing method", //todo! onClick = onConnectPayinMethodClicked, enabled = true, modifier = Modifier @@ -191,6 +226,52 @@ private fun PayinAccountReadOnlyTextField(label: String, text: String, modifier: ) } +@Composable +private fun CurrentPayinMethodRow( + label: String, + text: String, + isDefault: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + HedvigCard( + onClick = onClick, + modifier = modifier.fillMaxWidth(), + ) { + HorizontalItemsWithMaximumSpaceTaken( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + startSlot = { + Column { + HedvigText(text = label) + HedvigText( + text = text, + color = HedvigTheme.colorScheme.textSecondary, + ) + } + }, + endSlot = { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + ){ + if (isDefault) { + HighlightLabel( + labelText = "Default", //todo + size = HighlightLabelDefaults.HighLightSize.Small, + color = HighlightLabelDefaults.HighlightColor.Green( + HighlightLabelDefaults.HighlightShade.LIGHT + ) + ) + } + } + }, + spaceBetween = 6.dp + ) + } +} + private fun formatBankAccountLabel(baseLabel: String, bankName: String?): String { return if (bankName != null) "$baseLabel - $bankName" else baseLabel } @@ -214,7 +295,8 @@ private fun PreviewPayinAccountOverviewScreen( onConnectPayoutMethodClicked = {}, onRetry = {}, navigateUp = {}, - ) + {},{}, +{} ) } } } @@ -224,33 +306,59 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr PayinAccountOverviewUiState.Loading, PayinAccountOverviewUiState.Error, PayinAccountOverviewUiState.Content( - currentMethod = null, - availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + currentMethods = emptyList(), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), PayinAccountOverviewUiState.Content( - currentMethod = PayinAccount.SwishPayin(phoneNumber = "070-123 45 67", isPending = false), - availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + currentMethods = listOf( + PayinAccount.SwishPayin( + phoneNumber = "070-123 45 67", + isPending = false, + isDefault = true, + ), + ), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH), ), PayinAccountOverviewUiState.Content( - currentMethod = PayinAccount.SwishPayin(phoneNumber = "070-123 45 67", isPending = false), - availablePayoutMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + currentMethods = listOf( + PayinAccount.SwishPayin( + phoneNumber = "070-123 45 67", + isPending = false, + isDefault = true, + ), + ), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), ), PayinAccountOverviewUiState.Content( - currentMethod = PayinAccount.SwishPayin(phoneNumber = null, isPending = true), - availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + currentMethods = listOf(PayinAccount.SwishPayin(phoneNumber = null, isPending = true, isDefault = true)), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH), ), PayinAccountOverviewUiState.Content( - currentMethod = PayinAccount.SwishPayin(phoneNumber = "070-123 45 67", isPending = true), - availablePayoutMethods = listOf(MemberPaymentProvider.SWISH), + currentMethods = listOf( + PayinAccount.SwishPayin( + phoneNumber = "070-123 45 67", + isPending = true, + isDefault = true, + ), + ), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH), ), PayinAccountOverviewUiState.Content( - currentMethod = PayinAccount.Trustly( - clearingNumber = "8327", - accountNumber = "12345678", - bankName = "Mock Swedbank", - isPending = false, + currentMethods = listOf( + PayinAccount.Trustly( + clearingNumber = "8327", + accountNumber = "12345678", + bankName = "Mock Swedbank", + isPending = false, + isDefault = true, + ), + PayinAccount.SwishPayin( + phoneNumber = "070-123 45 67", + isPending = true, + isDefault = false, + ), ), - availablePayoutMethods = listOf(MemberPaymentProvider.TRUSTLY), + availablePayinMethods = listOf(MemberPaymentProvider.TRUSTLY), ), ), ) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt index 94427835f3..da91d6f161 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt @@ -31,8 +31,8 @@ internal sealed interface PayinAccountOverviewUiState { data object Error : PayinAccountOverviewUiState data class Content( - val currentMethod: PayinAccount?, - val availablePayoutMethods: List, + val currentMethods: List, + val availablePayinMethods: List, ) : PayinAccountOverviewUiState } @@ -52,8 +52,8 @@ internal class PayinAccountOverviewPresenter( ifLeft = { uiState = PayinAccountOverviewUiState.Error }, ifRight = { data -> uiState = PayinAccountOverviewUiState.Content( - currentMethod = data.currentMethod, - availablePayoutMethods = data.availablePayinMethods, + currentMethods = data.currentMethods, + availablePayinMethods = data.availablePayinMethods, ) }, diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index 8d8fb06104..17da4a8c69 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -12,9 +12,11 @@ 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.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.Surface import hedvig.resources.BANK_PAYOUT_METHOD_CARD_DESCRIPTION import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE import hedvig.resources.PAYMENTS_INVOICE @@ -37,7 +39,7 @@ internal fun SelectPayinMethodDestination( navigateUp: () -> Unit, ) { HedvigScaffold( - topAppBarText = stringResource(Res.string.PAYOUT_SELECT_PAYOUT_METHOD), //todo + topAppBarText = "Select billing method to add", //todo navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { @@ -48,7 +50,7 @@ internal fun SelectPayinMethodDestination( MemberPaymentProvider.TRUSTLY -> { PayinMethodRow( title = stringResource(Res.string.trustly), - subtitle = stringResource(Res.string.PAYOUT_METHOD_TRUSTLY_DESCRIPTION), //todo + subtitle = "Connect your bank via Trustly", //todo onClick = onTrustlySelected, ) } @@ -56,7 +58,7 @@ internal fun SelectPayinMethodDestination( MemberPaymentProvider.SWISH -> { PayinMethodRow( title = stringResource(Res.string.swish), - subtitle = stringResource(Res.string.PAYOUT_METHOD_SWISH_DESCRIPTION), //todo + subtitle = "Monthly auto-payments through Swish", //todo onClick = onSwishSelected, ) } @@ -92,3 +94,22 @@ private fun PayinMethodRow(title: String, subtitle: String, onClick: () -> Unit, } } } + +@Composable +@HedvigPreview +private fun PreviewSelectPayinMethodScreen() { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + SelectPayinMethodDestination( + availableProviders = listOf( + MemberPaymentProvider.SWISH, + MemberPaymentProvider.INVOICE, + ), + onTrustlySelected = {}, + onSwishSelected = {}, + onInvoiceSelected = {}, + navigateUp = {}, + ) + } + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt index 680955d469..007cc46e50 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt @@ -3,6 +3,7 @@ package com.hedvig.android.feature.payin.account.ui.setupinvoice import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -12,7 +13,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.design.system.hedvig.GlobalSnackBarState @@ -70,8 +73,17 @@ private fun SetupInvoicePayinScreen( modifier = Modifier.fillMaxSize(), ) { //todo: some text here?? - Column (Modifier.weight(1f)) { - HedvigText("You can choose invoice as you pay-in") + Column( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + HedvigText("You can choose invoice as your billing method. " + + "You will then get a monthly invoice via Kivra or email if you don't have Kivra account.", + textAlign = TextAlign.Center) } AnimatedVisibility( visible = uiState.errorMessage != null, @@ -89,7 +101,7 @@ private fun SetupInvoicePayinScreen( } Spacer(Modifier.height(16.dp)) HedvigButton( - text = "Connect", //todo + text = "Set invoice as billing method", //todo onClick = onConnect, enabled = !uiState.isLoading, isLoading = uiState.isLoading, @@ -111,10 +123,10 @@ private fun PreviewPayoutAccountOverviewScreen() { uiState = SetupInvoicePayinUiState( false, null, - showSuccessSnackBar = false + showSuccessSnackBar = false, ), globalSnackBarState = GlobalSnackBarState(), - {}, {} ,{} + {}, {}, {}, ) } } From 425c908d60c8aa7e14fc3bc667597c754076b4ab Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 20 May 2026 17:04:55 +0200 Subject: [PATCH 07/30] SetAsDefaultUseCase --- .../android/design/system/hedvig/Button.kt | 2 + .../main/graphql/SetAsDefaultMutation.graphql | 7 + .../account/data/GetPayinAccountUseCase.kt | 19 +- .../payin/account/data/SetAsDefaultUseCase.kt | 47 +++++ .../payin/account/di/PayinAccountModule.kt | 17 +- .../payin/account/navigation/PayinNavGraph.kt | 9 - .../PayinAccountOverviewDestination.kt | 163 +++++++++++------- .../overview/PayinAccountOverviewViewModel.kt | 39 ++++- 8 files changed, 224 insertions(+), 79 deletions(-) create mode 100644 app/feature/feature-payin-account/src/main/graphql/SetAsDefaultMutation.graphql create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt diff --git a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/Button.kt b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/Button.kt index d963ed99e7..f41fbd0237 100644 --- a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/Button.kt +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/Button.kt @@ -203,6 +203,7 @@ fun HedvigButtonGhostWithBorder( enabled: Boolean = true, interactionSource: MutableInteractionSource? = null, size: ButtonSize = ButtonSize.Medium, + isLoading: Boolean = false ) { HedvigTextButton( text = text, @@ -215,6 +216,7 @@ fun HedvigButtonGhostWithBorder( ), buttonSize = size, interactionSource = interactionSource, + isLoading = isLoading ) } diff --git a/app/feature/feature-payin-account/src/main/graphql/SetAsDefaultMutation.graphql b/app/feature/feature-payin-account/src/main/graphql/SetAsDefaultMutation.graphql new file mode 100644 index 0000000000..7150867bb7 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/graphql/SetAsDefaultMutation.graphql @@ -0,0 +1,7 @@ +mutation SetAsDefaultPayin($provider: MemberPaymentProvider!) { + paymentMethodSetDefaultPayin( + provider: $provider + ) { + message + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt index a52a637ff2..9c80308f47 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -8,6 +8,7 @@ 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 com.hedvig.android.logger.logcat import octopus.GetPayinMethodsQuery import octopus.GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod.Details.Companion.asPaymentMethodBankAccountDetails import octopus.GetPayinMethodsQuery.Data.CurrentMember.PaymentMethods.PayinMethod.Details.Companion.asPaymentMethodInvoiceDetails @@ -25,6 +26,7 @@ internal class GetPayinAccountUseCase( private val apolloClient: ApolloClient, ) { suspend fun invoke(): Either = either { + logcat { "Mariia: GetPayinAccountUseCase launching" } val result = apolloClient .query(GetPayinMethodsQuery()) .fetchPolicy(FetchPolicy.NetworkOnly) @@ -59,7 +61,7 @@ internal class GetPayinAccountUseCase( MemberPaymentProvider.INVOICE -> { val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() PayinAccount.Invoice( - delivery = invoiceDetails?.delivery, + delivery = invoiceDetails?.delivery?.toDeliveryString(), email = invoiceDetails?.email, isPending = isPending, isDefault = method.isDefault @@ -76,10 +78,12 @@ internal class GetPayinAccountUseCase( .filter { it.supportsPayin } .map { it.provider } - PayinAccountData( + val finalResult = PayinAccountData( currentMethods = currentMethods, availablePayinMethods = availablePayinMethods, ) + logcat { "Mariia: GetPayinAccountUseCase finalResult: $finalResult" } + finalResult } } @@ -123,9 +127,18 @@ internal sealed interface PayinAccount { ) : PayinAccount data class Invoice( - val delivery: PaymentMethodInvoiceDelivery?, + val delivery: String?, val email: String?, override val isPending: Boolean, override val isDefault: Boolean, ) : PayinAccount } + +private fun PaymentMethodInvoiceDelivery?.toDeliveryString(): String? { + return when(this) { + PaymentMethodInvoiceDelivery.KIVRA -> "Kivra" //todo + PaymentMethodInvoiceDelivery.MAIL -> "Email" //todo + PaymentMethodInvoiceDelivery.UNKNOWN__ -> "" + else -> null + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt new file mode 100644 index 0000000000..cee0a1b9db --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt @@ -0,0 +1,47 @@ +package com.hedvig.android.feature.payin.account.data + +import arrow.core.Either +import arrow.core.raise.context.bind +import arrow.core.raise.context.either +import arrow.core.raise.context.raise +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.logger.logcat +import octopus.SetAsDefaultPayinMutation +import octopus.type.MemberPaymentProvider + +internal interface SetAsDefaultUseCase { + suspend fun invoke(provider: MemberPaymentProvider): Either +} + + +internal class SetAsDefaultUseCaseImpl( + private val apolloClient: ApolloClient, + private val getPayinAccountUseCase: GetPayinAccountUseCase, +) : SetAsDefaultUseCase { + override suspend fun invoke(provider: MemberPaymentProvider): Either { + return either { + logcat { "Mariia: Starting SetAsDefaultUseCaseImpl with provider: $provider" } + apolloClient + .mutation(SetAsDefaultPayinMutation(provider)) + .safeExecute(::ErrorMessage) + .fold( + ifLeft = { + logcat { "Mariia: SetAsDefaultUseCaseImpl error: $it" } + logcat { "SetAsDefaultUseCaseImpl error: $it" } + raise(ErrorMessage()) + }, + ifRight = { result -> + val userError = result.paymentMethodSetDefaultPayin?.message + if (userError != null) { + raise(ErrorMessage(userError)) + } + logcat { "Mariia: SetAsDefaultUseCaseImpl launching getPayinAccountUseCase" } + getPayinAccountUseCase.invoke().bind() + }, + ) + } + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt index efab20f540..a3a6fd5a83 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt @@ -3,6 +3,8 @@ package com.hedvig.android.feature.payin.account.di import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.NetworkCacheManager import com.hedvig.android.feature.payin.account.data.GetPayinAccountUseCase +import com.hedvig.android.feature.payin.account.data.SetAsDefaultUseCase +import com.hedvig.android.feature.payin.account.data.SetAsDefaultUseCaseImpl import com.hedvig.android.feature.payin.account.data.SetupInvoicePayinUseCase import com.hedvig.android.feature.payin.account.data.SetupSwishPayinUseCase import com.hedvig.android.feature.payin.account.data.SetupSwishPayinUseCaseImpl @@ -16,9 +18,20 @@ val payinAccountModule = module { single { SetupSwishPayinUseCaseImpl(get(), get()) } - single {GetPayinAccountUseCase(get())} + single { GetPayinAccountUseCase(get()) } + single { + SetAsDefaultUseCaseImpl( + get(), + get(), + ) + } single { SetupInvoicePayinUseCase(get(), get()) } viewModel { SetupInvoicePayinViewModel(get()) } viewModel { SetupSwishPayinViewModel(get()) } - viewModel { PayinAccountOverviewViewModel(get()) } + viewModel { + PayinAccountOverviewViewModel( + get(), + get(), + ) + } } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt index e7303c6e77..73791d7755 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -46,15 +46,6 @@ fun NavGraphBuilder.payinAccountGraph( ) }, navigateUp = navigateUp, - onTrustlySelected = dropUnlessResumed { - navigateToConnectTrustly { - typedPopUpTo { - inclusive = true - } - } - }, - onSwishSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupSwishPayin) }, - onInvoiceSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupInvoicePayin) }, ) } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index 1b9e599e0f..53cf43f0d8 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -17,9 +17,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.design.system.hedvig.ButtonDefaults import com.hedvig.android.design.system.hedvig.EmptyState import com.hedvig.android.design.system.hedvig.EmptyStateDefaults import com.hedvig.android.design.system.hedvig.HedvigButton +import com.hedvig.android.design.system.hedvig.HedvigButtonGhostWithBorder import com.hedvig.android.design.system.hedvig.HedvigCard import com.hedvig.android.design.system.hedvig.HedvigErrorSection import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProgressDebounced @@ -27,8 +30,6 @@ import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigText -import com.hedvig.android.design.system.hedvig.HedvigTextField -import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.HighlightLabel import com.hedvig.android.design.system.hedvig.HighlightLabelDefaults @@ -36,12 +37,11 @@ import com.hedvig.android.design.system.hedvig.HorizontalItemsWithMaximumSpaceTa import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface import com.hedvig.android.feature.payin.account.data.PayinAccount -import hedvig.resources.PAYMENTS_ACCOUNT import hedvig.resources.PAYMENTS_INVOICE import hedvig.resources.REFERRAL_PENDING_STATUS_LABEL import hedvig.resources.Res +import hedvig.resources.something_went_wrong import hedvig.resources.swish -import hedvig.resources.trustly import octopus.type.MemberPaymentProvider import org.jetbrains.compose.resources.stringResource @@ -50,9 +50,6 @@ internal fun PayinAccountOverviewDestination( viewModel: PayinAccountOverviewViewModel, onConnectPayoutMethodClicked: () -> Unit, navigateUp: () -> Unit, - onTrustlySelected: () -> Unit, - onSwishSelected: () -> Unit, - onInvoiceSelected: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() PayinAccountOverviewScreen( @@ -60,9 +57,9 @@ internal fun PayinAccountOverviewDestination( onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, onRetry = { viewModel.emit(PayinAccountOverviewEvent.Retry) }, navigateUp = navigateUp, - onTrustlySelected = onTrustlySelected, - onSwishSelected = onSwishSelected, - onInvoiceSelected = onInvoiceSelected + setAsDefaultPayinMethod = { + viewModel.emit(PayinAccountOverviewEvent.SetDefaultMethod(it)) + }, ) } @@ -72,9 +69,7 @@ private fun PayinAccountOverviewScreen( onConnectPayoutMethodClicked: () -> Unit, onRetry: () -> Unit, navigateUp: () -> Unit, - onTrustlySelected: () -> Unit, - onSwishSelected: () -> Unit, - onInvoiceSelected: () -> Unit, + setAsDefaultPayinMethod: (MemberPaymentProvider) -> Unit, ) { HedvigScaffold( topAppBarText = "Billing account", //todo! @@ -105,9 +100,9 @@ private fun PayinAccountOverviewScreen( availablePayinMethods = uiState.availablePayinMethods, onConnectPayinMethodClicked = onConnectPayoutMethodClicked, modifier = Modifier.weight(1f), - onTrustlySelected = onTrustlySelected, - onSwishSelected = onSwishSelected, - onInvoiceSelected = onInvoiceSelected + setAsDefaultPayinMethod = setAsDefaultPayinMethod, + loadingDefaultProvider = uiState.loadingDefaultProvider, + setDefaultProviderError = uiState.setDefaultProviderError, ) } } @@ -119,9 +114,9 @@ private fun PayoutAccountContent( currentMethods: List, availablePayinMethods: List, onConnectPayinMethodClicked: () -> Unit, - onTrustlySelected: () -> Unit, - onSwishSelected: () -> Unit, - onInvoiceSelected: () -> Unit, + setAsDefaultPayinMethod: (MemberPaymentProvider) -> Unit, + loadingDefaultProvider: MemberPaymentProvider?, + setDefaultProviderError: ErrorMessage?, modifier: Modifier = Modifier, ) { Column(modifier) { @@ -136,7 +131,8 @@ private fun PayoutAccountContent( ) } } else { - HedvigText("Active billing methods", modifier = Modifier.padding(horizontal = 16.dp)) //todo + HedvigText("Active billing methods",//todo + modifier = Modifier.padding(horizontal = 16.dp)) Spacer(Modifier.height(16.dp)) Column( verticalArrangement = Arrangement.spacedBy(8.dp), @@ -153,40 +149,64 @@ private fun PayoutAccountContent( phoneNumber }, isDefault = method.isDefault, - onClick = onSwishSelected, - modifier = Modifier.padding(horizontal = 16.dp) + onClick = { + setAsDefaultPayinMethod(MemberPaymentProvider.SWISH) + }, + modifier = Modifier.padding(horizontal = 16.dp), + loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.SWISH, ) } is PayinAccount.Trustly -> { - val accountNumber = formatBankAccountNumber(method.clearingNumber, method.accountNumber) + val accountNumber = formatBankAccountNumber(method.clearingNumber, method.accountNumber, method.bankName) CurrentPayinMethodRow( - label = formatBankAccountLabel(stringResource(Res.string.trustly), method.bankName), + label = "Autogiro", //todo text = if (method.isPending && accountNumber.isBlank()) { stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) } else { accountNumber }, isDefault = method.isDefault, - onClick = onTrustlySelected, - modifier = Modifier.padding(horizontal = 16.dp) + onClick = { + setAsDefaultPayinMethod(MemberPaymentProvider.TRUSTLY) + }, + modifier = Modifier.padding(horizontal = 16.dp), + loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.TRUSTLY, ) } is PayinAccount.Invoice -> { CurrentPayinMethodRow( - stringResource(Res.string.PAYMENTS_ACCOUNT), stringResource(Res.string.PAYMENTS_INVOICE), + method.delivery ?: "", isDefault = method.isDefault, - onClick = onInvoiceSelected, - modifier = Modifier.padding(horizontal = 16.dp) + onClick = { + setAsDefaultPayinMethod(MemberPaymentProvider.INVOICE) + }, + modifier = Modifier.padding(horizontal = 16.dp), + loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.INVOICE, ) } } } } } - Spacer(Modifier.weight(1f)) + Column( + Modifier + .weight(1f) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + if (setDefaultProviderError != null) { + EmptyState( + text = stringResource(Res.string.something_went_wrong), + description = setDefaultProviderError.message, + modifier = Modifier.padding(horizontal = 16.dp), + iconStyle = EmptyStateDefaults.EmptyStateIconStyle.ERROR, + ) + } + } Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { if (currentMethods.any { it.isPending }) { HedvigNotificationCard( @@ -212,30 +232,16 @@ private fun PayoutAccountContent( } } -@Composable -private fun PayinAccountReadOnlyTextField(label: String, text: String, modifier: Modifier = Modifier) { - HedvigTextField( - text = text, - onValueChange = {}, - labelText = label, - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, - readOnly = true, - modifier = modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) -} - @Composable private fun CurrentPayinMethodRow( label: String, text: String, isDefault: Boolean, + loadingDefaultProvider: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier, ) { HedvigCard( - onClick = onClick, modifier = modifier.fillMaxWidth(), ) { HorizontalItemsWithMaximumSpaceTaken( @@ -254,31 +260,34 @@ private fun CurrentPayinMethodRow( endSlot = { Row( horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically, - ){ + verticalAlignment = Alignment.CenterVertically, + ) { if (isDefault) { HighlightLabel( labelText = "Default", //todo size = HighlightLabelDefaults.HighLightSize.Small, color = HighlightLabelDefaults.HighlightColor.Green( - HighlightLabelDefaults.HighlightShade.LIGHT - ) + HighlightLabelDefaults.HighlightShade.LIGHT, + ), + ) + } else { + HedvigButtonGhostWithBorder( + size = ButtonDefaults.ButtonSize.Small, + text = "Choose as default", //todo + onClick = onClick, + isLoading = loadingDefaultProvider, ) } } }, - spaceBetween = 6.dp + spaceBetween = 6.dp, ) } } -private fun formatBankAccountLabel(baseLabel: String, bankName: String?): String { - return if (bankName != null) "$baseLabel - $bankName" else baseLabel -} - -private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: String?): String { +private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: String?, bankName: String?): String { return when { - clearingNumber != null && accountNumber != null -> "$clearingNumber-$accountNumber" + clearingNumber != null && accountNumber != null && bankName != null -> "$bankName $clearingNumber-$accountNumber" else -> clearingNumber.orEmpty() } } @@ -295,8 +304,8 @@ private fun PreviewPayinAccountOverviewScreen( onConnectPayoutMethodClicked = {}, onRetry = {}, navigateUp = {}, - {},{}, -{} ) + {}, + ) } } } @@ -335,30 +344,56 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr ), PayinAccountOverviewUiState.Content( currentMethods = listOf( + PayinAccount.Trustly( + clearingNumber = "****", + accountNumber = "*45678", + bankName = "Swedbank", + isPending = false, + isDefault = true, + ), PayinAccount.SwishPayin( phoneNumber = "070-123 45 67", - isPending = true, - isDefault = true, + isPending = false, + isDefault = false, ), ), availablePayinMethods = listOf(MemberPaymentProvider.SWISH), + loadingDefaultProvider = MemberPaymentProvider.SWISH, + ), + PayinAccountOverviewUiState.Content( + currentMethods = listOf( + PayinAccount.Trustly( + clearingNumber = "****", + accountNumber = "*45678", + bankName = "Swedbank", + isPending = false, + isDefault = true, + ), + PayinAccount.SwishPayin( + phoneNumber = "070-123 45 67", + isPending = false, + isDefault = false, + ), + ), + availablePayinMethods = listOf(MemberPaymentProvider.TRUSTLY), ), PayinAccountOverviewUiState.Content( currentMethods = listOf( PayinAccount.Trustly( - clearingNumber = "8327", - accountNumber = "12345678", - bankName = "Mock Swedbank", + clearingNumber = "****", + accountNumber = "*45678", + bankName = "Swedbank", isPending = false, isDefault = true, ), PayinAccount.SwishPayin( phoneNumber = "070-123 45 67", - isPending = true, + isPending = false, isDefault = false, ), ), availablePayinMethods = listOf(MemberPaymentProvider.TRUSTLY), + setDefaultProviderError = ErrorMessage("Not possible to set this method as default"), ), ), ) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt index da91d6f161..d855caa2ab 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt @@ -7,8 +7,11 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.feature.payin.account.data.GetPayinAccountUseCase import com.hedvig.android.feature.payin.account.data.PayinAccount +import com.hedvig.android.feature.payin.account.data.SetAsDefaultUseCase +import com.hedvig.android.logger.logcat import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope import com.hedvig.android.molecule.public.MoleculeViewModel @@ -16,13 +19,16 @@ import octopus.type.MemberPaymentProvider internal class PayinAccountOverviewViewModel( getPayinAccountUseCase: GetPayinAccountUseCase, + setAsDefaultUseCase: SetAsDefaultUseCase ) : MoleculeViewModel( PayinAccountOverviewUiState.Loading, - PayinAccountOverviewPresenter(getPayinAccountUseCase), + PayinAccountOverviewPresenter(getPayinAccountUseCase, setAsDefaultUseCase), ) internal sealed interface PayinAccountOverviewEvent { data object Retry : PayinAccountOverviewEvent + + data class SetDefaultMethod(val provider: MemberPaymentProvider): PayinAccountOverviewEvent } internal sealed interface PayinAccountOverviewUiState { @@ -33,17 +39,21 @@ internal sealed interface PayinAccountOverviewUiState { data class Content( val currentMethods: List, val availablePayinMethods: List, + val loadingDefaultProvider: MemberPaymentProvider? =null, + val setDefaultProviderError: ErrorMessage? = null ) : PayinAccountOverviewUiState } internal class PayinAccountOverviewPresenter( private val getPayinAccountUseCase: GetPayinAccountUseCase, + private val setAsDefaultUseCase: SetAsDefaultUseCase ) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present( lastState: PayinAccountOverviewUiState, ): PayinAccountOverviewUiState { var loadIteration by remember { mutableIntStateOf(0) } + var providerToSetAsDefault by remember { mutableStateOf(null) } var uiState by remember { mutableStateOf(lastState) } LaunchedEffect(loadIteration) { @@ -60,9 +70,36 @@ internal class PayinAccountOverviewPresenter( ) } + LaunchedEffect(providerToSetAsDefault) { + val provider = providerToSetAsDefault + if (provider!=null) { + logcat { "Mariia: Starting LaunchedEffect(providerToSetAsDefault) with provider: $provider" } + val currentState = uiState as? PayinAccountOverviewUiState.Content ?: return@LaunchedEffect + uiState = currentState.copy(loadingDefaultProvider = provider) + setAsDefaultUseCase.invoke(provider).fold( + ifLeft = { + providerToSetAsDefault = null + uiState = currentState.copy(setDefaultProviderError = it) + }, + ifRight = { data -> + providerToSetAsDefault = null + uiState = PayinAccountOverviewUiState.Content( + currentMethods = data.currentMethods, + availablePayinMethods = data.availablePayinMethods, + ) + }, + ) + } + } + CollectEvents { event -> when (event) { PayinAccountOverviewEvent.Retry -> loadIteration++ + is PayinAccountOverviewEvent.SetDefaultMethod -> { + val currentState = uiState as? PayinAccountOverviewUiState.Content ?: return@CollectEvents + uiState = currentState.copy(setDefaultProviderError = null) + providerToSetAsDefault = event.provider + } } } From 8aafceb0a856bbb1467b6ea2e9c34289f8585f3c Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Thu, 21 May 2026 10:02:32 +0200 Subject: [PATCH 08/30] default and pending state --- .../account/data/GetPayinAccountUseCase.kt | 7 ++++--- .../payin/account/data/SetAsDefaultUseCase.kt | 1 + .../PayinAccountOverviewDestination.kt | 18 ++++++++++++------ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt index 9c80308f47..14d4076617 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -37,13 +37,14 @@ internal class GetPayinAccountUseCase( val currentMethods: List = paymentMethods.payinMethods.mapNotNull { method -> val isPending = method.status == MemberPaymentMethodStatus.PENDING + val isDefault = !isPending && method.isDefault when (method.provider) { MemberPaymentProvider.SWISH -> { val phoneNumber = method.details?.asPaymentMethodSwishDetails()?.phoneNumber PayinAccount.SwishPayin( phoneNumber = phoneNumber, isPending = isPending, - isDefault = method.isDefault, + isDefault = isDefault, ) } @@ -54,7 +55,7 @@ internal class GetPayinAccountUseCase( accountNumber = accountNumber, bankName = bankName, isPending = isPending, - isDefault = method.isDefault + isDefault = isDefault ) } @@ -64,7 +65,7 @@ internal class GetPayinAccountUseCase( delivery = invoiceDetails?.delivery?.toDeliveryString(), email = invoiceDetails?.email, isPending = isPending, - isDefault = method.isDefault + isDefault = isDefault ) } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt index cee0a1b9db..b427396737 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt @@ -36,6 +36,7 @@ internal class SetAsDefaultUseCaseImpl( ifRight = { result -> val userError = result.paymentMethodSetDefaultPayin?.message if (userError != null) { + logcat { "Mariia: SetAsDefaultUseCaseImpl userError not null: $userError" } raise(ErrorMessage(userError)) } logcat { "Mariia: SetAsDefaultUseCaseImpl launching getPayinAccountUseCase" } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index 53cf43f0d8..99e6dba1b5 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -154,6 +154,7 @@ private fun PayoutAccountContent( }, modifier = Modifier.padding(horizontal = 16.dp), loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.SWISH, + isPending = method.isPending ) } @@ -172,6 +173,7 @@ private fun PayoutAccountContent( }, modifier = Modifier.padding(horizontal = 16.dp), loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.TRUSTLY, + isPending = method.isPending ) } @@ -185,6 +187,7 @@ private fun PayoutAccountContent( }, modifier = Modifier.padding(horizontal = 16.dp), loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.INVOICE, + isPending = method.isPending ) } } @@ -237,6 +240,7 @@ private fun CurrentPayinMethodRow( label: String, text: String, isDefault: Boolean, + isPending: Boolean, loadingDefaultProvider: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier, @@ -271,12 +275,14 @@ private fun CurrentPayinMethodRow( ), ) } else { - HedvigButtonGhostWithBorder( - size = ButtonDefaults.ButtonSize.Small, - text = "Choose as default", //todo - onClick = onClick, - isLoading = loadingDefaultProvider, - ) + if (!isPending) { + HedvigButtonGhostWithBorder( + size = ButtonDefaults.ButtonSize.Small, + text = "Choose as default", //todo + onClick = onClick, + isLoading = loadingDefaultProvider, + ) + } } } }, From d4af5cee3081862a57a55e11f5a9ac224fc07c21 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Thu, 21 May 2026 11:29:22 +0200 Subject: [PATCH 09/30] swish payin impl --- .../hedvig/android/apollo/ApolloCallExt.kt | 18 ++- .../android/app/navigation/HedvigNavHost.kt | 1 + .../graphql/SetupSwishPayinMutation.graphql | 9 ++ .../payin/account/data/SetAsDefaultUseCase.kt | 2 +- .../account/data/SetupSwishPayinUseCase.kt | 55 ++++++++- .../payin/account/navigation/PayinNavGraph.kt | 5 + .../setupswish/SetupSwishPayinDestination.kt | 105 ++++++++++++++---- .../ui/setupswish/SetupSwishPayinViewModel.kt | 48 ++++++-- 8 files changed, 202 insertions(+), 41 deletions(-) create mode 100644 app/feature/feature-payin-account/src/main/graphql/SetupSwishPayinMutation.graphql diff --git a/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt b/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt index e031d5eea0..b300006bfc 100644 --- a/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt +++ b/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt @@ -20,6 +20,7 @@ import com.hedvig.android.apollo.ApolloOperationError.OperationError import com.hedvig.android.apollo.ApolloOperationError.OperationException import com.hedvig.android.apollo.parseResponse import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.logger.logcat import kotlin.jvm.JvmInline import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -127,6 +128,7 @@ private fun IorRaise>.parseRespon } raise(OperationException(exception)) } + logcat{"Mariia: apollo parseResponse data: $data errors: $errors"} return iorFromErrorsAndData(errors.mapToOperationErrors(), data).bind() } @@ -136,6 +138,7 @@ private fun List?.mapToOperationErrors(): Nel? { if (error.extensionErrorType() == ExtensionErrorType.Unauthenticated) { OperationError.Unathenticated } else { + logcat{"Mariia: apollo mapToOperationErrors error: $error"} OperationError.Other( buildString { append(error.message) @@ -175,9 +178,18 @@ private fun iorFromErrorsAndData( data: D?, ): Ior, D> { return when { - errors != null && data != null -> Ior.Both(errors, data) - errors != null -> Ior.Left(errors) - data != null -> Ior.Right(data) + errors != null && data != null -> { + logcat{"Mariia: apollo iorFromErrorsAndData Ior.Both. data: $data errors: $errors"} + Ior.Both(errors, data) + } + errors != null -> { + logcat{"Mariia: apollo iorFromErrorsAndData Ior.Left"} + Ior.Left(errors) + } + data != null -> { + logcat{"Mariia: apollo iorFromErrorsAndData Ior.Right"} + Ior.Right(data) + } else -> error("Non compliant server") } } 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 d9d6762d11..971ac4e3bc 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 @@ -368,6 +368,7 @@ internal fun HedvigNavHost( hedvigDeepLinkContainer = hedvigDeepLinkContainer, navigateToConnectTrustly = ::navigateToConnectTrustly, navigateUp = navController::navigateUp, + openUrl = openUrl ) profileGraph( settingsDestinationNestedGraphs = { diff --git a/app/feature/feature-payin-account/src/main/graphql/SetupSwishPayinMutation.graphql b/app/feature/feature-payin-account/src/main/graphql/SetupSwishPayinMutation.graphql new file mode 100644 index 0000000000..200394d827 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/graphql/SetupSwishPayinMutation.graphql @@ -0,0 +1,9 @@ +mutation SetupSwishPayin($input: PaymentMethodSetupSwishInput!) { + paymentMethodSetupSwishPayin(input: $input) { + error { + message + } + status + url + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt index b427396737..98dd14c735 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt @@ -26,7 +26,7 @@ internal class SetAsDefaultUseCaseImpl( logcat { "Mariia: Starting SetAsDefaultUseCaseImpl with provider: $provider" } apolloClient .mutation(SetAsDefaultPayinMutation(provider)) - .safeExecute(::ErrorMessage) + .safeExecute(::ErrorMessage) //tosdoL user safeExecuteAllowingPartialResponses .fold( ifLeft = { logcat { "Mariia: SetAsDefaultUseCaseImpl error: $it" } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt index f7e0ca5e7a..46ffbd9a84 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt @@ -1,19 +1,66 @@ package com.hedvig.android.feature.payin.account.data import arrow.core.Either +import arrow.core.raise.context.bind +import arrow.core.raise.context.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 com.hedvig.android.logger.logcat +import octopus.SetupSwishPayinMutation +import octopus.type.PaymentMethodSetupStatus +import octopus.type.PaymentMethodSetupSwishInput + internal interface SetupSwishPayinUseCase { - suspend fun invoke(): Either + suspend fun invoke(phoneNumber: String): Either } internal class SetupSwishPayinUseCaseImpl( private val apolloClient: ApolloClient, private val networkCacheManager: NetworkCacheManager, -): SetupSwishPayinUseCase { - override suspend fun invoke(): Either { - TODO("Not yet implemented") +) : SetupSwishPayinUseCase { + override suspend fun invoke(phoneNumber: String): Either = either { + val result = apolloClient + .mutation(SetupSwishPayinMutation(PaymentMethodSetupSwishInput(phoneNumber))) + .safeExecute(::ErrorMessage) + .bind() + + val output = result.paymentMethodSetupSwishPayin + when (output.status) { + PaymentMethodSetupStatus.ACTIVE -> { + logcat { + "Mariia: SetupSwishPayinMutation ACTIVE url: $output.url" + } + networkCacheManager.clearCache() + SetupSwishResponse.Success(output.url) + + } + + PaymentMethodSetupStatus.PENDING -> { + logcat { + "Mariia: SetupSwishPayinMutation PENDING url: $output.url" + } + networkCacheManager.clearCache() + SetupSwishResponse.Pending(output.url) + } + + PaymentMethodSetupStatus.FAILED, PaymentMethodSetupStatus.UNKNOWN__ -> { + logcat { + "SetupSwishPayinMutation failed with: output.error?.message" + } + val userMessage = output.error?.message + SetupSwishResponse.Failure(ErrorMessage(userMessage)) + } + } } } + + +internal sealed interface SetupSwishResponse { + data class Failure(val error: ErrorMessage) : SetupSwishResponse + data class Success(val url: String?) : SetupSwishResponse + data class Pending(val url: String?) : SetupSwishResponse +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt index 73791d7755..bdecc2e0a8 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -28,6 +28,7 @@ fun NavGraphBuilder.payinAccountGraph( hedvigDeepLinkContainer: HedvigDeepLinkContainer, navigateToConnectTrustly: (builder: NavOptionsBuilder.() -> Unit) -> Unit, navigateUp: () -> Unit, + openUrl: (String) -> Unit, ) { navgraph( startDestination = PayinAccountDestinations.Overview::class, @@ -74,6 +75,10 @@ fun NavGraphBuilder.payinAccountGraph( navController.typedPopBackStack(inclusive = true) }, navigateUp = navController::navigateUp, + openUrl = { + navController.typedPopBackStack(inclusive = true) + openUrl(it) + } ) } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index 4bee2c08d6..a4ef479bfa 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -14,10 +14,17 @@ 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.simulateHotReload +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.design.system.hedvig.EmptyState +import com.hedvig.android.design.system.hedvig.EmptyStateDefaults import com.hedvig.android.design.system.hedvig.GlobalSnackBarState import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard @@ -33,6 +40,7 @@ import hedvig.resources.ODYSSEY_PHONE_NUMBER_LABEL import hedvig.resources.Res import hedvig.resources.TIER_FLOW_COMMIT_PROCESSING_ERROR_DESCRIPTION import hedvig.resources.general_save_button +import hedvig.resources.something_went_wrong import hedvig.resources.swish import org.jetbrains.compose.resources.stringResource @@ -42,6 +50,7 @@ internal fun SetupSwishPayinDestination( globalSnackBarState: GlobalSnackBarState, onSuccessfullyConnected: () -> Unit, navigateUp: () -> Unit, + openUrl: (String) -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() SetupSwishPayoutScreen( @@ -53,6 +62,7 @@ internal fun SetupSwishPayinDestination( onSuccessfullyConnected() }, navigateUp = navigateUp, + openUrl = openUrl ) } @@ -65,6 +75,7 @@ private fun SetupSwishPayoutScreen( onSave: () -> Unit, showedSnackBar: () -> Unit, navigateUp: () -> Unit, + openUrl: (String) -> Unit, ) { val changesSaved = stringResource(Res.string.CONTACT_INFO_CHANGES_SAVED) LaunchedEffect(uiState.showSuccessSnackBar) { @@ -78,6 +89,37 @@ private fun SetupSwishPayoutScreen( navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { + Spacer(Modifier.weight(1f)) + if (uiState.error != null) { + EmptyState( + text = stringResource(Res.string.something_went_wrong), + description = uiState.error.message, + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + iconStyle = EmptyStateDefaults.EmptyStateIconStyle.ERROR, + ) + } + if (uiState.successUrl!=null) { + Column( + modifier= Modifier.fillMaxWidth().padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + EmptyState( + text = "The process started", + description = "Please confirm in Swish app", + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + iconStyle = EmptyStateDefaults.EmptyStateIconStyle.SUCCESS_WITH_WARNING, + ) + Spacer(Modifier.height(16.dp)) + HedvigButton( + "Open Swish", + enabled = true, + onClick = { + openUrl(uiState.successUrl) + } + ) + } + + } Spacer(Modifier.weight(1f)) Column(Modifier.padding(horizontal = 16.dp)) { HedvigTextField( @@ -90,26 +132,14 @@ private fun SetupSwishPayoutScreen( modifier = Modifier.fillMaxWidth(), ) } - AnimatedVisibility( - visible = uiState.errorMessage != null, - enter = expandVertically(), - exit = shrinkVertically(), - ) { - HedvigNotificationCard( - message = uiState.errorMessage?.message - ?: stringResource(Res.string.TIER_FLOW_COMMIT_PROCESSING_ERROR_DESCRIPTION), - priority = NotificationPriority.Attention, - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(top = 4.dp) - .fillMaxWidth(), - ) - } + Spacer(Modifier.height(16.dp)) HedvigButton( text = stringResource(Res.string.general_save_button), onClick = onSave, - enabled = !uiState.isLoading && uiState.phoneNumberState.text.length >= 10, + enabled = !uiState.isLoading && + uiState.phoneNumberState.text.length >= 10 && + uiState.successUrl==null, isLoading = uiState.isLoading, modifier = Modifier .fillMaxWidth() @@ -121,21 +151,50 @@ private fun SetupSwishPayoutScreen( @Composable @HedvigPreview -private fun PreviewSetupSwishPayinScreen() { +private fun PreviewSetupSwishPayinScreen( + @PreviewParameter(SetupSwishPayinUiStateProvider::class) uiState: SetupSwishPayoutUiState, +) { HedvigTheme { Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { SetupSwishPayoutScreen( - uiState = SetupSwishPayoutUiState( - phoneNumberState = TextFieldState(), - isLoading = false, - errorMessage = null, - showSuccessSnackBar = false, - ), + uiState = uiState, globalSnackBarState = GlobalSnackBarState(), onSave = {}, showedSnackBar = {}, navigateUp = {}, + {} ) } } } + + +private class SetupSwishPayinUiStateProvider : CollectionPreviewParameterProvider( + listOf( + SetupSwishPayoutUiState( + phoneNumberState = TextFieldState("287334432273"), + isLoading = false, + error = null, + showSuccessSnackBar = false, + ), + SetupSwishPayoutUiState( + phoneNumberState = TextFieldState(), + isLoading = false, + error = ErrorMessage(), + showSuccessSnackBar = false, + ), + SetupSwishPayoutUiState( + phoneNumberState = TextFieldState("837286428"), + isLoading = true, + error = null, + showSuccessSnackBar = false, + ), + SetupSwishPayoutUiState( + phoneNumberState = TextFieldState("83728644428"), + isLoading = false, + error = null, + showSuccessSnackBar = false, + successUrl = "hwdjhew" + ) + ), +) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt index a365317fa4..a2659100cb 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.feature.payin.account.data.SetupSwishPayinUseCase +import com.hedvig.android.feature.payin.account.data.SetupSwishResponse import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope import com.hedvig.android.molecule.public.MoleculeViewModel @@ -16,7 +17,13 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class SetupSwishPayinViewModel( setupSwishPayoutUseCase: SetupSwishPayinUseCase, ) : MoleculeViewModel( - SetupSwishPayoutUiState(TextFieldState(), false, null, false), + SetupSwishPayoutUiState( + phoneNumberState = TextFieldState(), + isLoading = false, + error = null, + showSuccessSnackBar = false, + successUrl = null + ), SetupSwishPayoutPresenter(setupSwishPayoutUseCase), ) @@ -26,11 +33,14 @@ internal sealed interface SetupSwishPayoutEvent { data object ShowedSnackBar : SetupSwishPayoutEvent } -internal data class SetupSwishPayoutUiState( - val phoneNumberState: TextFieldState, - val isLoading: Boolean, - val errorMessage: ErrorMessage?, - val showSuccessSnackBar: Boolean, +internal data class SetupSwishPayoutUiState ( + val showSuccessSnackBar: Boolean, + val successUrl: String? = null, + val phoneNumberState: TextFieldState, + val isLoading: Boolean, + val error: ErrorMessage?, + val resultIsPending: Boolean = false + ) internal class SetupSwishPayoutPresenter( @@ -44,23 +54,40 @@ internal class SetupSwishPayoutPresenter( var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } var showSuccessSnackBar by remember { mutableStateOf(false) } + var resultIsPending by remember { mutableStateOf(false) } var saveIteration by remember { mutableStateOf(null) } + var urlToRedirect by remember { mutableStateOf(null) } + var uiState by remember { mutableStateOf(lastState) } val currentSave = saveIteration if (currentSave != null) { + val currentState = uiState LaunchedEffect(currentSave) { isLoading = true errorMessage = null - setupSwishPayoutUseCase.invoke().fold( + resultIsPending = false + setupSwishPayoutUseCase.invoke(phoneNumberState.text.toString()).fold( ifLeft = { isLoading = false errorMessage = it saveIteration = null }, - ifRight = { + ifRight = { result -> isLoading = false - showSuccessSnackBar = true saveIteration = null + when(result) { + is SetupSwishResponse.Failure -> { + errorMessage = result.error + } + is SetupSwishResponse.Pending -> { + urlToRedirect = result.url + resultIsPending = true + } + is SetupSwishResponse.Success -> { + showSuccessSnackBar = true + urlToRedirect = result.url + } + } }, ) } @@ -83,8 +110,9 @@ internal class SetupSwishPayoutPresenter( return SetupSwishPayoutUiState( phoneNumberState = phoneNumberState, isLoading = isLoading, - errorMessage = errorMessage, + error = errorMessage, showSuccessSnackBar = showSuccessSnackBar, + successUrl = urlToRedirect ) } } From c31397a634736884674d5d841049c32c3a645207 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Thu, 21 May 2026 11:43:31 +0200 Subject: [PATCH 10/30] always show Manage payment methods --- .../MemberPaymentDetailsDestination.kt | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) 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/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt index 1c266595af..9112416eaf 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt @@ -220,30 +220,15 @@ private fun MemberPaymentDetailsSuccessScreen( } Spacer(Modifier.weight(1f)) Spacer(Modifier.height(16.dp)) - when { - paymentDetails.paymentMethod == PaymentMethod.TRUSTLY -> { - HedvigButton( - text = stringResource(R.string.PROFILE_PAYMENT_CHANGE_BANK_ACCOUNT), - onClick = onChangeBankAccount, - enabled = true, - buttonStyle = ButtonDefaults.ButtonStyle.Secondary, - modifier = Modifier - .fillMaxWidth() - .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)), - ) - } - - paymentDetails.paymentMethod == PaymentMethod.INVOICE && account == PaymentAccount.Kivra -> { - HedvigNotificationCard( - message = stringResource(Res.string.KIVRA_NOTIFICATION_BOX_TEXT), - priority = NotificationDefaults.NotificationPriority.Info, - style = NotificationDefaults.InfoCardStyle.Button( - buttonText = stringResource(Res.string.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON), - onButtonClick = onChangeBankAccount, - ), - ) - } - } + HedvigButton( + text = "Manage payment methods",//todo + onClick = onChangeBankAccount, + enabled = true, + buttonStyle = ButtonDefaults.ButtonStyle.Secondary, + modifier = Modifier + .fillMaxWidth() + .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)), + ) Spacer(Modifier.height(16.dp)) } From 6d760c931b665deb91feff535d84fa2abc5d0ef5 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Thu, 21 May 2026 12:02:23 +0200 Subject: [PATCH 11/30] help center nav --- .../kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt | 2 +- .../src/androidMain/res/values-sv-rSE/strings.xml | 4 +++- .../core-resources/src/androidMain/res/values/strings.xml | 2 ++ .../src/commonMain/composeResources/values-sv-rSE/strings.xml | 4 +++- .../src/commonMain/composeResources/values/strings.xml | 2 ++ .../payment/trustly/navigation/ConnectTrustlyPaymentGraph.kt | 2 +- .../android/feature/help/center/data/GetQuickLinksUseCase.kt | 3 ++- .../memberpaymentdetails/MemberPaymentDetailsDestination.kt | 3 ++- 8 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index 971ac4e3bc..ec3f9f45ad 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 @@ -493,7 +493,7 @@ internal fun HedvigNavHost( } QuickLinkConnectPayment -> { - TrustlyDestination + PayinAccountDestination.Graph } QuickLinkTermination -> { diff --git a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml index 1121f472a0..5c6838f5e0 100644 --- a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml @@ -522,6 +522,7 @@ Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut + Manage your billing methods Aktivera ditt försäkringsskydd igen genom att kontakta oss när din betalning har registrerats Få ett prisförslag Se över kontaktuppgifter @@ -680,7 +681,7 @@ Reseintyg Din profil Se guider - I valpguiden hittar du användbara artiklar som hjälper dig med allt från första veterinärbesöket till hur du väljer rätt foder. + I Valpguiden får du svar på de vanligaste frågorna som rör valpens första tid. Utvalda guider Läst Inte hjälpsam @@ -1366,6 +1367,7 @@ Du är inbjuden Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code + Återuppta handlingen Koppla ditt EuroBonus Lägg till ditt nummer Du tjänar 100 poäng per månad för varje aktiv försäkring du har hos Hedvig diff --git a/app/core/core-resources/src/androidMain/res/values/strings.xml b/app/core/core-resources/src/androidMain/res/values/strings.xml index ab249a9e6d..4beb026ee8 100644 --- a/app/core/core-resources/src/androidMain/res/values/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values/strings.xml @@ -522,6 +522,7 @@ Question regarding claim, Vehicle reg. %1$s Preferences Logout + Manage your billing methods Activate your coverage again by contacting us once your payment has been processed Get a price quote Review contact info @@ -1366,6 +1367,7 @@ You’re invited Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code + Resume shopping Connect your EuroBonus Connect your number You earn 100 points per month for each active insurance you have with Hedvig diff --git a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml index ee88808b69..df6c4c7f86 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -522,6 +522,7 @@ Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut + Manage your billing methods Aktivera ditt försäkringsskydd igen genom att kontakta oss när din betalning har registrerats Få ett prisförslag Se över kontaktuppgifter @@ -680,7 +681,7 @@ Reseintyg Din profil Se guider - I valpguiden hittar du användbara artiklar som hjälper dig med allt från första veterinärbesöket till hur du väljer rätt foder. + I Valpguiden får du svar på de vanligaste frågorna som rör valpens första tid. Utvalda guider Läst Inte hjälpsam @@ -1366,6 +1367,7 @@ Du är inbjuden Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code + Återuppta handlingen Koppla ditt EuroBonus Lägg till ditt nummer Du tjänar 100 poäng per månad för varje aktiv försäkring du har hos Hedvig diff --git a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml index ff57e81a6a..7206a8403b 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml @@ -522,6 +522,7 @@ Question regarding claim, Vehicle reg. %1$s Preferences Logout + Manage your billing methods Activate your coverage again by contacting us once your payment has been processed Get a price quote Review contact info @@ -1366,6 +1367,7 @@ You’re invited Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code + Resume shopping Connect your EuroBonus Connect your number You earn 100 points per month for each active insurance you have with Hedvig 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..cd235732ca 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 @@ -15,7 +15,7 @@ fun NavGraphBuilder.connectPaymentGraph( ) { navdestination( deepLinks = navDeepLinks( - hedvigDeepLinkContainer.connectPayment, + //hedvigDeepLinkContainer.connectPayment, hedvigDeepLinkContainer.directDebit, ), ) { diff --git a/app/feature/feature-help-center/src/main/kotlin/com/hedvig/android/feature/help/center/data/GetQuickLinksUseCase.kt b/app/feature/feature-help-center/src/main/kotlin/com/hedvig/android/feature/help/center/data/GetQuickLinksUseCase.kt index 88b57633ba..9ff947e786 100644 --- a/app/feature/feature-help-center/src/main/kotlin/com/hedvig/android/feature/help/center/data/GetQuickLinksUseCase.kt +++ b/app/feature/feature-help-center/src/main/kotlin/com/hedvig/android/feature/help/center/data/GetQuickLinksUseCase.kt @@ -38,6 +38,7 @@ import hedvig.resources.HC_QUICK_ACTIONS_TRAVEL_CERTIFICATE import hedvig.resources.HC_QUICK_ACTIONS_TRAVEL_CERTIFICATE_SUBTITLE import hedvig.resources.HC_QUICK_ACTIONS_UPGRADE_COVERAGE_SUBTITLE import hedvig.resources.HC_QUICK_ACTIONS_UPGRADE_COVERAGE_TITLE +import hedvig.resources.MANAGE_BILLING_METHODS_BUTTON import hedvig.resources.Res import kotlinx.coroutines.flow.first import octopus.AvailableSelfServiceOnContractsQuery @@ -118,7 +119,7 @@ internal class GetQuickLinksUseCase( StandaloneQuickLink( quickLinkDestination = QuickLinkDestination.OuterDestination.QuickLinkConnectPayment, titleRes = Res.string.HC_QUICK_ACTIONS_PAYMENTS_TITLE, - hintTextRes = Res.string.HC_QUICK_ACTIONS_PAYMENTS_SUBTITLE, + hintTextRes = Res.string.MANAGE_BILLING_METHODS_BUTTON, //todo!!! ), ) } 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/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt index 9112416eaf..d2cb93d77b 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt @@ -56,6 +56,7 @@ import com.hedvig.android.placeholder.PlaceholderHighlight import com.hedvig.android.placeholder.shimmer import hedvig.resources.DASHBOARD_OPEN_CHAT import hedvig.resources.KIVRA_NOTIFICATION_BOX_TEXT +import hedvig.resources.MANAGE_BILLING_METHODS_BUTTON import hedvig.resources.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON import hedvig.resources.R import hedvig.resources.Res @@ -221,7 +222,7 @@ private fun MemberPaymentDetailsSuccessScreen( Spacer(Modifier.weight(1f)) Spacer(Modifier.height(16.dp)) HedvigButton( - text = "Manage payment methods",//todo + text = stringResource(Res.string.MANAGE_BILLING_METHODS_BUTTON),//todo onClick = onChangeBankAccount, enabled = true, buttonStyle = ButtonDefaults.ButtonStyle.Secondary, From d6cafcec797d0c095fcaf5904895cc64334f30ad Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Thu, 21 May 2026 13:09:00 +0200 Subject: [PATCH 12/30] ReminderCardConnectPayment copy --- .../hedvig/android/memberreminders/ui/MemberReminderCards.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt b/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt index c6fea8b964..4b279f62fe 100644 --- a/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt +++ b/app/member-reminders/member-reminders-ui/src/main/kotlin/com/hedvig/android/memberreminders/ui/MemberReminderCards.kt @@ -431,7 +431,7 @@ private fun ReminderCardConnectPayment( modifier = modifier, priority = NotificationPriority.Attention, style = InfoCardStyle.Button( - buttonText = stringResource(Res.string.PROFILE_PAYMENT_CONNECT_DIRECT_DEBIT_BUTTON), + buttonText = "Setup payment method", //todo onButtonClick = navigateToConnectPayment, ), minLines = minLines, From 493621d28b54976cdd83b902285d295fef9e5b53 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Thu, 21 May 2026 14:19:31 +0200 Subject: [PATCH 13/30] qrcode and swish icon --- .../design/system/hedvig/EmptyState.kt | 15 +- .../system/hedvig/icon/colored/Swish.kt | 393 ++++++++++++++++++ .../feature-payin-account/build.gradle.kts | 1 + .../account/data/GetPayinAccountUseCase.kt | 15 +- .../payin/account/data/SetAsDefaultUseCase.kt | 3 +- .../account/data/SetupSwishPayinUseCase.kt | 5 +- .../payin/account/di/PayinAccountModule.kt | 2 +- .../payin/account/navigation/PayinNavGraph.kt | 2 +- .../PayinAccountOverviewDestination.kt | 26 +- .../overview/PayinAccountOverviewViewModel.kt | 36 +- .../SelectPayinMethodDestination.kt | 8 +- .../SetupInvoicePayinDestination.kt | 19 +- .../SetupInvoicePayinViewModel.kt | 8 +- .../payin/account/ui/setupswish/QRCode.kt | 59 +++ .../setupswish/SetupSwishPayinDestination.kt | 177 ++++++-- .../ui/setupswish/SetupSwishPayinViewModel.kt | 54 +-- 16 files changed, 701 insertions(+), 122 deletions(-) create mode 100644 app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Swish.kt create mode 100644 app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/QRCode.kt diff --git a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/EmptyState.kt b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/EmptyState.kt index 567de7b4fd..30610c2a98 100644 --- a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/EmptyState.kt +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/EmptyState.kt @@ -1,5 +1,6 @@ package com.hedvig.android.design.system.hedvig +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -37,6 +38,7 @@ import com.hedvig.android.design.system.hedvig.icon.CheckFilled import com.hedvig.android.design.system.hedvig.icon.HedvigIcons import com.hedvig.android.design.system.hedvig.icon.InfoFilled import com.hedvig.android.design.system.hedvig.icon.WarningFilled +import com.hedvig.android.design.system.hedvig.icon.colored.Swish import com.hedvig.android.design.system.hedvig.tokens.EmptyStateTokens @Composable @@ -148,6 +150,15 @@ private fun ColumnScope.EmptyStateIcon(iconStyle: EmptyStateIconStyle) { ) Spacer(Modifier.height(16.dp)) } + + EmptyStateIconStyle.SWISH -> { + Image( + HedvigIcons.Swish, + null, + modifier = Modifier.size(48.dp), + ) + Spacer(Modifier.height(16.dp)) + } } } @@ -161,7 +172,9 @@ object EmptyStateDefaults { SUCCESS, BANK_ID, NO_ICON, - SUCCESS_WITH_WARNING + SUCCESS_WITH_WARNING, + + SWISH } sealed class EmptyStateButtonStyle { diff --git a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Swish.kt b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Swish.kt new file mode 100644 index 0000000000..6eb4b18285 --- /dev/null +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Swish.kt @@ -0,0 +1,393 @@ +package com.hedvig.android.design.system.hedvig.icon.colored + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.icon.CheckFilled +import com.hedvig.android.design.system.hedvig.icon.HedvigIcons + +val HedvigIcons.Swish: ImageVector + get() { + if (_Swish != null) { + return _Swish!! + } + _Swish = ImageVector.Builder( + name = "Swish", + defaultWidth = 420.dp, + defaultHeight = 566.dp, + viewportWidth = 420f, + viewportHeight = 566f + ).apply { + path(fill = SolidColor(Color(0xFF17191F))) { + moveTo(381.4f, 485.9f) + curveToRelative(0f, -2f, 0.4f, -3.9f, 1.1f, -5.7f) + curveToRelative(0.7f, -1.8f, 1.8f, -3.3f, 3f, -4.6f) + curveToRelative(1.3f, -1.3f, 2.8f, -2.4f, 4.5f, -3.1f) + curveToRelative(1.7f, -0.8f, 3.6f, -1.1f, 5.5f, -1.1f) + reflectiveCurveToRelative(3.9f, 0.4f, 5.6f, 1.1f) + curveToRelative(1.7f, 0.8f, 3.3f, 1.8f, 4.6f, 3.1f) + reflectiveCurveToRelative(2.3f, 2.9f, 3.1f, 4.6f) + curveToRelative(0.7f, 1.8f, 1.1f, 3.7f, 1.1f, 5.7f) + reflectiveCurveToRelative(-0.4f, 3.9f, -1.1f, 5.7f) + curveToRelative(-0.7f, 1.8f, -1.8f, 3.3f, -3.1f, 4.6f) + curveToRelative(-1.3f, 1.3f, -2.8f, 2.4f, -4.6f, 3.1f) + curveToRelative(-1.7f, 0.8f, -3.6f, 1.1f, -5.6f, 1.1f) + reflectiveCurveToRelative(-3.8f, -0.4f, -5.5f, -1.1f) + curveToRelative(-1.7f, -0.8f, -3.2f, -1.8f, -4.5f, -3.1f) + curveToRelative(-1.3f, -1.3f, -2.3f, -2.9f, -3f, -4.6f) + curveToRelative(-0.7f, -1.8f, -1.1f, -3.7f, -1.1f, -5.7f) + close() + moveTo(384.3f, 485.9f) + curveToRelative(0f, 1.7f, 0.3f, 3.2f, 0.9f, 4.7f) + curveToRelative(0.6f, 1.4f, 1.4f, 2.7f, 2.4f, 3.8f) + curveToRelative(1f, 1.1f, 2.2f, 1.9f, 3.6f, 2.5f) + curveToRelative(1.4f, 0.6f, 2.9f, 0.9f, 4.5f, 0.9f) + reflectiveCurveToRelative(3.1f, -0.3f, 4.5f, -0.9f) + curveToRelative(1.4f, -0.6f, 2.6f, -1.5f, 3.6f, -2.5f) + reflectiveCurveToRelative(1.8f, -2.3f, 2.4f, -3.8f) + curveToRelative(0.6f, -1.4f, 0.9f, -3f, 0.9f, -4.7f) + reflectiveCurveToRelative(-0.3f, -3.2f, -0.9f, -4.7f) + curveToRelative(-0.6f, -1.4f, -1.4f, -2.7f, -2.4f, -3.8f) + curveToRelative(-1f, -1.1f, -2.2f, -1.9f, -3.6f, -2.5f) + curveToRelative(-1.4f, -0.6f, -2.9f, -0.9f, -4.5f, -0.9f) + reflectiveCurveToRelative(-3.1f, 0.3f, -4.5f, 0.9f) + curveToRelative(-1.4f, 0.6f, -2.6f, 1.5f, -3.6f, 2.5f) + curveToRelative(-1f, 1.1f, -1.8f, 2.3f, -2.4f, 3.8f) + curveToRelative(-0.6f, 1.4f, -0.9f, 3f, -0.9f, 4.7f) + close() + moveTo(390.7f, 479.6f) + curveToRelative(0f, -0.9f, 0.4f, -1.3f, 1.3f, -1.3f) + horizontalLineToRelative(4.5f) + curveToRelative(1.4f, 0f, 2.6f, 0.4f, 3.4f, 1.2f) + curveToRelative(0.9f, 0.8f, 1.3f, 1.9f, 1.3f, 3.4f) + reflectiveCurveToRelative(0f, 1.1f, -0.3f, 1.6f) + curveToRelative(-0.2f, 0.5f, -0.4f, 0.8f, -0.7f, 1.2f) + curveToRelative(-0.3f, 0.3f, -0.6f, 0.6f, -0.9f, 0.8f) + reflectiveCurveToRelative(-0.6f, 0.4f, -1f, 0.4f) + horizontalLineToRelative(0f) + curveToRelative(0f, 0.1f, 0f, 0.2f, 0.1f, 0.2f) + curveToRelative(0f, 0f, 0.1f, 0.1f, 0.2f, 0.3f) + curveToRelative(0f, 0.1f, 0.1f, 0.3f, 0.2f, 0.4f) + lineToRelative(2.1f, 4f) + curveToRelative(0.2f, 0.5f, 0.3f, 0.8f, 0.2f, 1.1f) + curveToRelative(-0.1f, 0.3f, -0.4f, 0.4f, -0.9f, 0.4f) + horizontalLineToRelative(-0.5f) + curveToRelative(-0.7f, 0f, -1.3f, -0.3f, -1.6f, -1f) + lineToRelative(-2.3f, -4.9f) + horizontalLineToRelative(-2.5f) + verticalLineToRelative(4.6f) + curveToRelative(0f, 0.9f, -0.4f, 1.3f, -1.2f, 1.3f) + horizontalLineToRelative(-0.4f) + curveToRelative(-0.8f, 0f, -1.2f, -0.4f, -1.2f, -1.3f) + verticalLineToRelative(-12.5f) + close() + moveTo(396f, 485.4f) + curveToRelative(0.8f, 0f, 1.4f, -0.2f, 1.8f, -0.7f) + reflectiveCurveToRelative(0.6f, -1.1f, 0.6f, -1.9f) + reflectiveCurveToRelative(-0.2f, -1.4f, -0.6f, -1.8f) + curveToRelative(-0.4f, -0.4f, -1f, -0.6f, -1.8f, -0.6f) + horizontalLineToRelative(-2.4f) + verticalLineToRelative(5f) + horizontalLineToRelative(2.4f) + close() + moveTo(279.3f, 495f) + curveToRelative(4.5f, 0f, 8.4f, 0.6f, 11.5f, 1.7f) + curveToRelative(3.2f, 1.2f, 5.6f, 2.2f, 7.4f, 3.2f) + curveToRelative(1.5f, 0.8f, 2.4f, 1.9f, 2.7f, 3.2f) + curveToRelative(0.3f, 1.3f, 0f, 2.7f, -0.7f, 4.3f) + lineToRelative(-1.3f, 2.4f) + curveToRelative(-0.8f, 1.6f, -1.8f, 2.5f, -3.1f, 2.8f) + reflectiveCurveToRelative(-2.7f, 0f, -4.4f, -0.7f) + curveToRelative(-1.5f, -0.7f, -3.3f, -1.4f, -5.5f, -2.2f) + reflectiveCurveToRelative(-4.6f, -1.1f, -7.5f, -1.1f) + reflectiveCurveToRelative(-5.2f, 0.6f, -6.8f, 1.7f) + curveToRelative(-1.6f, 1.2f, -2.4f, 2.8f, -2.4f, 4.9f) + reflectiveCurveToRelative(0.8f, 3.4f, 2.5f, 4.5f) + curveToRelative(1.6f, 1.2f, 3.7f, 2.2f, 6.3f, 3.1f) + curveToRelative(2.5f, 0.9f, 5.2f, 1.8f, 8.1f, 2.9f) + curveToRelative(2.9f, 1f, 5.6f, 2.3f, 8.1f, 3.9f) + reflectiveCurveToRelative(4.6f, 3.6f, 6.3f, 6.1f) + curveToRelative(1.6f, 2.5f, 2.5f, 5.6f, 2.5f, 9.4f) + reflectiveCurveToRelative(-0.6f, 5.8f, -1.8f, 8.3f) + reflectiveCurveToRelative(-2.9f, 4.7f, -5.2f, 6.6f) + curveToRelative(-2.3f, 1.9f, -5f, 3.3f, -8.2f, 4.4f) + curveToRelative(-3.2f, 1.1f, -6.7f, 1.6f, -10.7f, 1.6f) + reflectiveCurveToRelative(-10.1f, -0.8f, -13.9f, -2.4f) + curveToRelative(-3.8f, -1.6f, -6.7f, -3.1f, -8.7f, -4.5f) + curveToRelative(-1.5f, -0.9f, -2.4f, -2f, -2.5f, -3.3f) + curveToRelative(-0.2f, -1.3f, 0.2f, -2.7f, 1.2f, -4.3f) + lineToRelative(1.6f, -2.4f) + curveToRelative(1f, -1.4f, 2.1f, -2.2f, 3.3f, -2.4f) + reflectiveCurveToRelative(2.6f, 0.2f, 4.3f, 1.1f) + curveToRelative(1.6f, 0.9f, 3.7f, 1.9f, 6.2f, 3f) + curveToRelative(2.5f, 1.1f, 5.5f, 1.7f, 9f, 1.7f) + reflectiveCurveToRelative(5.2f, -0.6f, 6.9f, -1.9f) + reflectiveCurveToRelative(2.5f, -2.9f, 2.5f, -5.1f) + reflectiveCurveToRelative(-0.8f, -3.3f, -2.5f, -4.5f) + curveToRelative(-1.6f, -1.1f, -3.7f, -2.1f, -6.3f, -3.1f) + curveToRelative(-2.5f, -0.9f, -5.2f, -1.9f, -8.1f, -3f) + curveToRelative(-2.9f, -1.1f, -5.6f, -2.4f, -8.1f, -4f) + curveToRelative(-2.5f, -1.6f, -4.6f, -3.6f, -6.3f, -6.1f) + curveToRelative(-1.6f, -2.5f, -2.5f, -5.7f, -2.5f, -9.6f) + reflectiveCurveToRelative(0.7f, -6.2f, 2.1f, -8.8f) + curveToRelative(1.4f, -2.6f, 3.2f, -4.7f, 5.6f, -6.4f) + reflectiveCurveToRelative(5.1f, -3f, 8.3f, -3.9f) + curveToRelative(3.2f, -0.9f, 6.5f, -1.3f, 10.1f, -1.3f) + close() + moveTo(81.3f, 495f) + curveToRelative(4.5f, 0f, 8.4f, 0.6f, 11.5f, 1.7f) + reflectiveCurveToRelative(5.6f, 2.2f, 7.4f, 3.2f) + curveToRelative(1.5f, 0.8f, 2.4f, 1.9f, 2.7f, 3.2f) + curveToRelative(0.3f, 1.3f, 0f, 2.7f, -0.7f, 4.3f) + lineToRelative(-1.3f, 2.4f) + curveToRelative(-0.8f, 1.6f, -1.8f, 2.5f, -3.1f, 2.8f) + reflectiveCurveToRelative(-2.7f, 0f, -4.4f, -0.7f) + curveToRelative(-1.5f, -0.7f, -3.3f, -1.4f, -5.5f, -2.2f) + curveToRelative(-2.1f, -0.8f, -4.6f, -1.1f, -7.5f, -1.1f) + reflectiveCurveToRelative(-5.2f, 0.6f, -6.8f, 1.7f) + curveToRelative(-1.6f, 1.2f, -2.4f, 2.8f, -2.4f, 4.9f) + reflectiveCurveToRelative(0.8f, 3.4f, 2.5f, 4.5f) + curveToRelative(1.6f, 1.2f, 3.7f, 2.2f, 6.3f, 3.1f) + curveToRelative(2.5f, 0.9f, 5.2f, 1.8f, 8.1f, 2.9f) + curveToRelative(2.9f, 1f, 5.6f, 2.3f, 8.1f, 3.9f) + curveToRelative(2.5f, 1.6f, 4.6f, 3.6f, 6.3f, 6.1f) + curveToRelative(1.6f, 2.5f, 2.5f, 5.6f, 2.5f, 9.4f) + reflectiveCurveToRelative(-0.6f, 5.8f, -1.8f, 8.3f) + reflectiveCurveToRelative(-2.9f, 4.7f, -5.2f, 6.6f) + curveToRelative(-2.3f, 1.9f, -5f, 3.3f, -8.2f, 4.4f) + curveToRelative(-3.2f, 1.1f, -6.7f, 1.6f, -10.7f, 1.6f) + reflectiveCurveToRelative(-10.1f, -0.8f, -13.9f, -2.4f) + curveToRelative(-3.8f, -1.6f, -6.7f, -3.1f, -8.7f, -4.5f) + curveToRelative(-1.5f, -0.9f, -2.4f, -2f, -2.5f, -3.3f) + curveToRelative(-0.2f, -1.3f, 0.2f, -2.7f, 1.2f, -4.3f) + lineToRelative(1.6f, -2.4f) + curveToRelative(1f, -1.4f, 2.1f, -2.2f, 3.3f, -2.4f) + curveToRelative(1.2f, -0.2f, 2.6f, 0.2f, 4.3f, 1.1f) + curveToRelative(1.6f, 0.9f, 3.7f, 1.9f, 6.2f, 3f) + curveToRelative(2.5f, 1.1f, 5.5f, 1.7f, 9f, 1.7f) + reflectiveCurveToRelative(5.2f, -0.6f, 6.9f, -1.9f) + curveToRelative(1.7f, -1.2f, 2.5f, -2.9f, 2.5f, -5.1f) + reflectiveCurveToRelative(-0.8f, -3.3f, -2.5f, -4.5f) + curveToRelative(-1.6f, -1.1f, -3.7f, -2.1f, -6.3f, -3.1f) + curveToRelative(-2.5f, -0.9f, -5.2f, -1.9f, -8.1f, -3f) + curveToRelative(-2.9f, -1.1f, -5.6f, -2.4f, -8.1f, -4f) + curveToRelative(-2.5f, -1.6f, -4.6f, -3.6f, -6.3f, -6.1f) + reflectiveCurveToRelative(-2.5f, -5.7f, -2.5f, -9.6f) + reflectiveCurveToRelative(0.7f, -6.2f, 2.1f, -8.8f) + curveToRelative(1.4f, -2.6f, 3.2f, -4.7f, 5.6f, -6.4f) + reflectiveCurveToRelative(5.1f, -3f, 8.3f, -3.9f) + curveToRelative(3.2f, -0.9f, 6.5f, -1.3f, 10.1f, -1.3f) + close() + moveTo(324.1f, 470f) + curveToRelative(3.7f, 0f, 5.6f, 1.9f, 5.6f, 5.6f) + verticalLineToRelative(27.4f) + curveToRelative(0f, 0.9f, 0f, 1.7f, 0f, 2.3f) + curveToRelative(0f, 0.7f, -0.1f, 1.3f, -0.2f, 1.8f) + curveToRelative(0f, 0.6f, -0.1f, 1.2f, -0.1f, 1.6f) + horizontalLineToRelative(0.3f) + curveToRelative(0.8f, -1.6f, 1.9f, -3.2f, 3.4f, -4.9f) + curveToRelative(1.5f, -1.6f, 3.2f, -3.1f, 5.2f, -4.5f) + curveToRelative(2f, -1.3f, 4.3f, -2.4f, 6.8f, -3.2f) + curveToRelative(2.5f, -0.8f, 5.3f, -1.2f, 8.2f, -1.2f) + curveToRelative(7.5f, 0f, 13.4f, 2f, 17.5f, 6.1f) + curveToRelative(4.1f, 4.1f, 6.2f, 10.6f, 6.2f, 19.7f) + verticalLineToRelative(38f) + curveToRelative(0f, 3.7f, -1.9f, 5.6f, -5.6f, 5.6f) + horizontalLineToRelative(-5.7f) + curveToRelative(-3.7f, 0f, -5.6f, -1.9f, -5.6f, -5.6f) + verticalLineToRelative(-34.6f) + curveToRelative(0f, -4.2f, -0.7f, -7.5f, -2.1f, -10f) + reflectiveCurveToRelative(-4.3f, -3.8f, -8.5f, -3.8f) + reflectiveCurveToRelative(-5.6f, 0.6f, -8.1f, 1.7f) + curveToRelative(-2.4f, 1.2f, -4.5f, 2.7f, -6.2f, 4.7f) + curveToRelative(-1.7f, 2f, -3f, 4.4f, -3.9f, 7.1f) + curveToRelative(-0.9f, 2.7f, -1.4f, 5.7f, -1.4f, 8.9f) + verticalLineToRelative(25.9f) + curveToRelative(0f, 3.7f, -1.9f, 5.6f, -5.6f, 5.6f) + horizontalLineToRelative(-5.7f) + curveToRelative(-3.7f, 0f, -5.6f, -1.9f, -5.6f, -5.6f) + verticalLineToRelative(-83.2f) + curveToRelative(0f, -3.7f, 1.9f, -5.6f, 5.6f, -5.6f) + horizontalLineToRelative(5.7f) + close() + moveTo(235.7f, 496.6f) + curveToRelative(3.6f, 0f, 5.5f, 1.9f, 5.5f, 5.6f) + verticalLineToRelative(56.6f) + curveToRelative(0f, 3.7f, -1.8f, 5.6f, -5.5f, 5.6f) + horizontalLineToRelative(-5.9f) + curveToRelative(-3.6f, 0f, -5.5f, -1.9f, -5.5f, -5.6f) + verticalLineToRelative(-56.6f) + curveToRelative(0f, -3.7f, 1.8f, -5.6f, 5.5f, -5.6f) + horizontalLineToRelative(5.9f) + close() + moveTo(122.7f, 496.6f) + curveToRelative(3.4f, 0f, 5.4f, 1.6f, 6f, 4.9f) + lineToRelative(10.9f, 39.6f) + curveToRelative(0.2f, 1f, 0.3f, 1.9f, 0.5f, 2.7f) + curveToRelative(0.1f, 0.8f, 0.3f, 1.6f, 0.5f, 2.3f) + curveToRelative(0.2f, 0.8f, 0.3f, 1.6f, 0.4f, 2.3f) + horizontalLineToRelative(0.3f) + curveToRelative(0f, -0.7f, 0.2f, -1.5f, 0.4f, -2.3f) + curveToRelative(0.2f, -0.7f, 0.3f, -1.5f, 0.5f, -2.3f) + curveToRelative(0.1f, -0.8f, 0.3f, -1.7f, 0.6f, -2.7f) + lineToRelative(11.5f, -39.6f) + curveToRelative(0.6f, -3.2f, 2.7f, -4.8f, 6.1f, -4.8f) + horizontalLineToRelative(5.1f) + curveToRelative(3.3f, 0f, 5.3f, 1.6f, 6.1f, 4.8f) + lineToRelative(11.3f, 39.6f) + curveToRelative(0.3f, 1f, 0.5f, 1.9f, 0.6f, 2.7f) + curveToRelative(0.1f, 0.8f, 0.3f, 1.6f, 0.5f, 2.3f) + curveToRelative(0.2f, 0.8f, 0.3f, 1.6f, 0.4f, 2.3f) + horizontalLineToRelative(0.3f) + curveToRelative(0f, -0.7f, 0.2f, -1.5f, 0.4f, -2.3f) + curveToRelative(0.2f, -0.7f, 0.3f, -1.5f, 0.5f, -2.3f) + curveToRelative(0.1f, -0.8f, 0.3f, -1.7f, 0.6f, -2.7f) + lineToRelative(10.8f, -39.6f) + curveToRelative(0.8f, -3.3f, 2.8f, -4.9f, 6.1f, -4.9f) + horizontalLineToRelative(6.1f) + curveToRelative(2f, 0f, 3.5f, 0.6f, 4.3f, 1.7f) + curveToRelative(0.8f, 1.2f, 0.9f, 2.7f, 0.4f, 4.5f) + lineToRelative(-17.4f, 56.9f) + curveToRelative(-0.9f, 3.1f, -3f, 4.7f, -6.3f, 4.7f) + horizontalLineToRelative(-8.9f) + curveToRelative(-3.4f, 0f, -5.5f, -1.6f, -6.3f, -4.8f) + lineToRelative(-10.3f, -33.9f) + curveToRelative(-0.3f, -0.9f, -0.5f, -1.8f, -0.7f, -2.7f) + reflectiveCurveToRelative(-0.4f, -1.7f, -0.5f, -2.4f) + curveToRelative(-0.2f, -0.8f, -0.3f, -1.6f, -0.4f, -2.3f) + horizontalLineToRelative(-0.3f) + curveToRelative(-0.2f, 0.7f, -0.4f, 1.5f, -0.5f, 2.3f) + curveToRelative(-0.2f, 0.7f, -0.4f, 1.5f, -0.5f, 2.4f) + curveToRelative(-0.2f, 0.9f, -0.4f, 1.8f, -0.7f, 2.7f) + lineToRelative(-10.3f, 33.9f) + curveToRelative(-0.8f, 3.2f, -2.8f, 4.8f, -6.1f, 4.8f) + horizontalLineToRelative(-9.2f) + curveToRelative(-3.2f, 0f, -5.2f, -1.6f, -6.1f, -4.7f) + lineToRelative(-17.6f, -56.9f) + curveToRelative(-0.5f, -1.9f, -0.4f, -3.4f, 0.5f, -4.5f) + curveToRelative(0.8f, -1.2f, 2.2f, -1.7f, 4.2f, -1.7f) + horizontalLineToRelative(6.4f) + close() + } + path( + fill = Brush.linearGradient( + colorStops = arrayOf( + 0f to Color(0xFFEF2131), + 1f to Color(0xFFFECF2C) + ), + start = Offset(237.8f, 289.7f), + end = Offset(177.74f, 104.45f) + ), + pathFillType = PathFillType.EvenOdd + ) { + moveTo(119.3f, 399.2f) + curveToRelative(84.3f, 40.3f, 188.3f, 20.4f, 251.2f, -54.5f) + curveToRelative(74.5f, -88.8f, 62.9f, -221.1f, -25.8f, -295.5f) + lineToRelative(-59f, 70.3f) + curveToRelative(69.3f, 58.2f, 78.4f, 161.5f, 20.2f, 230.9f) + curveToRelative(-46.4f, 55.3f, -122.8f, 73.7f, -186.5f, 48.9f) + } + path( + fill = Brush.linearGradient( + colorStops = arrayOf( + 0f to Color(0xFFFBC52C), + 0.3f to Color(0xFFF87130), + 0.6f to Color(0xFFEF52E2), + 1f to Color(0xFF661EEC) + ), + start = Offset(379.9f, 129.59f), + end = Offset(243f, 399.77f) + ), + pathFillType = PathFillType.EvenOdd + ) { + moveTo(119.3f, 399.2f) + curveToRelative(84.3f, 40.3f, 188.3f, 20.4f, 251.2f, -54.5f) + curveToRelative(7.7f, -9.2f, 14.5f, -18.8f, 20.3f, -28.8f) + curveToRelative(9.9f, -61.7f, -11.9f, -126.9f, -63.2f, -169.9f) + curveToRelative(-13f, -10.9f, -27.2f, -19.8f, -41.9f, -26.5f) + curveToRelative(69.3f, 58.2f, 78.4f, 161.5f, 20.2f, 230.9f) + curveToRelative(-46.4f, 55.3f, -122.8f, 73.7f, -186.5f, 48.9f) + } + path( + fill = Brush.linearGradient( + colorStops = arrayOf( + 0f to Color(0xFF78F6D8), + 0.3f to Color(0xFF77D1F6), + 0.6f to Color(0xFF70A4F3), + 1f to Color(0xFF661EEC) + ), + start = Offset(118.21f, 92.5f), + end = Offset(178.27f, 277.75f) + ), + pathFillType = PathFillType.EvenOdd + ) { + moveTo(300.3f, 20.4f) + curveTo(216f, -19.9f, 111.9f, 0f, 49.1f, 74.9f) + curveToRelative(-74.5f, 88.8f, -62.9f, 221.1f, 25.8f, 295.5f) + lineToRelative(59f, -70.3f) + curveToRelative(-69.3f, -58.2f, -78.4f, -161.5f, -20.2f, -230.9f) + curveTo(160.2f, 14f, 236.6f, -4.5f, 300.3f, 20.4f) + } + path( + fill = Brush.linearGradient( + colorStops = arrayOf( + 0f to Color(0xFF536EED), + 0.2f to Color(0xFF54C3EC), + 0.6f to Color(0xFF64D769), + 1f to Color(0xFFFECF2C) + ), + start = Offset(95.13f, 220.03f), + end = Offset(232.03f, -50.15f) + ), + pathFillType = PathFillType.EvenOdd + ) { + moveTo(300.3f, 20.4f) + curveTo(216f, -19.9f, 111.9f, 0f, 49.1f, 74.9f) + curveToRelative(-7.7f, 9.2f, -14.5f, 18.8f, -20.3f, 28.8f) + curveToRelative(-9.9f, 61.7f, 11.9f, 126.9f, 63.2f, 169.9f) + curveToRelative(13f, 10.9f, 27.2f, 19.8f, 41.9f, 26.5f) + curveToRelative(-69.3f, -58.2f, -78.4f, -161.5f, -20.2f, -230.9f) + curveTo(160.2f, 14f, 236.6f, -4.5f, 300.3f, 20.4f) + } + }.build() + + return _Swish!! + } + +@Preview +@Composable +private fun IconPreview() { + HedvigTheme { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + imageVector = HedvigIcons.Swish, + contentDescription = com.hedvig.android.compose.ui.EmptyContentDescription, + modifier = Modifier + .width((24.0).dp) + .height((24.0).dp), + ) + } + } +} + + +@Suppress("ObjectPropertyName") +private var _Swish: ImageVector? = null diff --git a/app/feature/feature-payin-account/build.gradle.kts b/app/feature/feature-payin-account/build.gradle.kts index 105feaab33..ce5712c76a 100644 --- a/app/feature/feature-payin-account/build.gradle.kts +++ b/app/feature/feature-payin-account/build.gradle.kts @@ -31,4 +31,5 @@ dependencies { implementation(projects.navigationCompose) implementation(projects.navigationComposeTyped) implementation(projects.navigationCore) + implementation(libs.zXing) } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt index 14d4076617..d02ff4491a 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -55,7 +55,7 @@ internal class GetPayinAccountUseCase( accountNumber = accountNumber, bankName = bankName, isPending = isPending, - isDefault = isDefault + isDefault = isDefault, ) } @@ -65,7 +65,7 @@ internal class GetPayinAccountUseCase( delivery = invoiceDetails?.delivery?.toDeliveryString(), email = invoiceDetails?.email, isPending = isPending, - isDefault = isDefault + isDefault = isDefault, ) } @@ -136,10 +136,15 @@ internal sealed interface PayinAccount { } private fun PaymentMethodInvoiceDelivery?.toDeliveryString(): String? { - return when(this) { - PaymentMethodInvoiceDelivery.KIVRA -> "Kivra" //todo - PaymentMethodInvoiceDelivery.MAIL -> "Email" //todo + return when (this) { + PaymentMethodInvoiceDelivery.KIVRA -> "Kivra" + + // todo + PaymentMethodInvoiceDelivery.MAIL -> "Email" + + // todo PaymentMethodInvoiceDelivery.UNKNOWN__ -> "" + else -> null } } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt index 98dd14c735..f21b715793 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt @@ -16,7 +16,6 @@ internal interface SetAsDefaultUseCase { suspend fun invoke(provider: MemberPaymentProvider): Either } - internal class SetAsDefaultUseCaseImpl( private val apolloClient: ApolloClient, private val getPayinAccountUseCase: GetPayinAccountUseCase, @@ -26,7 +25,7 @@ internal class SetAsDefaultUseCaseImpl( logcat { "Mariia: Starting SetAsDefaultUseCaseImpl with provider: $provider" } apolloClient .mutation(SetAsDefaultPayinMutation(provider)) - .safeExecute(::ErrorMessage) //tosdoL user safeExecuteAllowingPartialResponses + .safeExecute(::ErrorMessage) // tosdoL user safeExecuteAllowingPartialResponses .fold( ifLeft = { logcat { "Mariia: SetAsDefaultUseCaseImpl error: $it" } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt index 46ffbd9a84..9a1759b6ef 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt @@ -13,7 +13,6 @@ import octopus.SetupSwishPayinMutation import octopus.type.PaymentMethodSetupStatus import octopus.type.PaymentMethodSetupSwishInput - internal interface SetupSwishPayinUseCase { suspend fun invoke(phoneNumber: String): Either } @@ -36,7 +35,6 @@ internal class SetupSwishPayinUseCaseImpl( } networkCacheManager.clearCache() SetupSwishResponse.Success(output.url) - } PaymentMethodSetupStatus.PENDING -> { @@ -58,9 +56,10 @@ internal class SetupSwishPayinUseCaseImpl( } } - internal sealed interface SetupSwishResponse { data class Failure(val error: ErrorMessage) : SetupSwishResponse + data class Success(val url: String?) : SetupSwishResponse + data class Pending(val url: String?) : SetupSwishResponse } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt index a3a6fd5a83..516f144333 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt @@ -31,7 +31,7 @@ val payinAccountModule = module { viewModel { PayinAccountOverviewViewModel( get(), - get(), + get(), ) } } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt index bdecc2e0a8..57b0d62d57 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/navigation/PayinNavGraph.kt @@ -78,7 +78,7 @@ fun NavGraphBuilder.payinAccountGraph( openUrl = { navController.typedPopBackStack(inclusive = true) openUrl(it) - } + }, ) } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index 99e6dba1b5..c4a9772040 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -72,7 +72,7 @@ private fun PayinAccountOverviewScreen( setAsDefaultPayinMethod: (MemberPaymentProvider) -> Unit, ) { HedvigScaffold( - topAppBarText = "Billing account", //todo! + topAppBarText = "Billing account", // todo! navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { @@ -125,14 +125,16 @@ private fun PayoutAccountContent( if (availablePayinMethods.isNotEmpty()) { Spacer(Modifier.weight(1f)) EmptyState( - text = "You haven’t added a billing method yet. Add one to pay for your insurance.", //todo + text = "You haven’t added a billing method yet. Add one to pay for your insurance.", // todo description = null, iconStyle = EmptyStateDefaults.EmptyStateIconStyle.INFO, ) } } else { - HedvigText("Active billing methods",//todo - modifier = Modifier.padding(horizontal = 16.dp)) + HedvigText( + "Active billing methods", // todo + modifier = Modifier.padding(horizontal = 16.dp), + ) Spacer(Modifier.height(16.dp)) Column( verticalArrangement = Arrangement.spacedBy(8.dp), @@ -154,14 +156,14 @@ private fun PayoutAccountContent( }, modifier = Modifier.padding(horizontal = 16.dp), loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.SWISH, - isPending = method.isPending + isPending = method.isPending, ) } is PayinAccount.Trustly -> { val accountNumber = formatBankAccountNumber(method.clearingNumber, method.accountNumber, method.bankName) CurrentPayinMethodRow( - label = "Autogiro", //todo + label = "Autogiro", // todo text = if (method.isPending && accountNumber.isBlank()) { stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) } else { @@ -173,7 +175,7 @@ private fun PayoutAccountContent( }, modifier = Modifier.padding(horizontal = 16.dp), loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.TRUSTLY, - isPending = method.isPending + isPending = method.isPending, ) } @@ -187,7 +189,7 @@ private fun PayoutAccountContent( }, modifier = Modifier.padding(horizontal = 16.dp), loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.INVOICE, - isPending = method.isPending + isPending = method.isPending, ) } } @@ -213,7 +215,7 @@ private fun PayoutAccountContent( Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { if (currentMethods.any { it.isPending }) { HedvigNotificationCard( - message = "You have just added or changed a billing method, it will appear here soon.", //todo + message = "You have just added or changed a billing method, it will appear here soon.", // todo priority = NotificationPriority.Info, modifier = Modifier .fillMaxWidth() @@ -222,7 +224,7 @@ private fun PayoutAccountContent( } if (availablePayinMethods.isNotEmpty()) { HedvigButton( - text = "Add a billing method", //todo! + text = "Add a billing method", // todo! onClick = onConnectPayinMethodClicked, enabled = true, modifier = Modifier @@ -268,7 +270,7 @@ private fun CurrentPayinMethodRow( ) { if (isDefault) { HighlightLabel( - labelText = "Default", //todo + labelText = "Default", // todo size = HighlightLabelDefaults.HighLightSize.Small, color = HighlightLabelDefaults.HighlightColor.Green( HighlightLabelDefaults.HighlightShade.LIGHT, @@ -278,7 +280,7 @@ private fun CurrentPayinMethodRow( if (!isPending) { HedvigButtonGhostWithBorder( size = ButtonDefaults.ButtonSize.Small, - text = "Choose as default", //todo + text = "Choose as default", // todo onClick = onClick, isLoading = loadingDefaultProvider, ) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt index d855caa2ab..c54498db31 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt @@ -19,16 +19,16 @@ import octopus.type.MemberPaymentProvider internal class PayinAccountOverviewViewModel( getPayinAccountUseCase: GetPayinAccountUseCase, - setAsDefaultUseCase: SetAsDefaultUseCase + setAsDefaultUseCase: SetAsDefaultUseCase, ) : MoleculeViewModel( - PayinAccountOverviewUiState.Loading, - PayinAccountOverviewPresenter(getPayinAccountUseCase, setAsDefaultUseCase), -) + PayinAccountOverviewUiState.Loading, + PayinAccountOverviewPresenter(getPayinAccountUseCase, setAsDefaultUseCase), + ) internal sealed interface PayinAccountOverviewEvent { data object Retry : PayinAccountOverviewEvent - data class SetDefaultMethod(val provider: MemberPaymentProvider): PayinAccountOverviewEvent + data class SetDefaultMethod(val provider: MemberPaymentProvider) : PayinAccountOverviewEvent } internal sealed interface PayinAccountOverviewUiState { @@ -39,14 +39,14 @@ internal sealed interface PayinAccountOverviewUiState { data class Content( val currentMethods: List, val availablePayinMethods: List, - val loadingDefaultProvider: MemberPaymentProvider? =null, - val setDefaultProviderError: ErrorMessage? = null + val loadingDefaultProvider: MemberPaymentProvider? = null, + val setDefaultProviderError: ErrorMessage? = null, ) : PayinAccountOverviewUiState } internal class PayinAccountOverviewPresenter( private val getPayinAccountUseCase: GetPayinAccountUseCase, - private val setAsDefaultUseCase: SetAsDefaultUseCase + private val setAsDefaultUseCase: SetAsDefaultUseCase, ) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present( @@ -62,25 +62,24 @@ internal class PayinAccountOverviewPresenter( ifLeft = { uiState = PayinAccountOverviewUiState.Error }, ifRight = { data -> uiState = PayinAccountOverviewUiState.Content( - currentMethods = data.currentMethods, - availablePayinMethods = data.availablePayinMethods, - ) - + currentMethods = data.currentMethods, + availablePayinMethods = data.availablePayinMethods, + ) }, ) } LaunchedEffect(providerToSetAsDefault) { val provider = providerToSetAsDefault - if (provider!=null) { + if (provider != null) { logcat { "Mariia: Starting LaunchedEffect(providerToSetAsDefault) with provider: $provider" } - val currentState = uiState as? PayinAccountOverviewUiState.Content ?: return@LaunchedEffect + val currentState = uiState as? PayinAccountOverviewUiState.Content ?: return@LaunchedEffect uiState = currentState.copy(loadingDefaultProvider = provider) setAsDefaultUseCase.invoke(provider).fold( ifLeft = { providerToSetAsDefault = null uiState = currentState.copy(setDefaultProviderError = it) - }, + }, ifRight = { data -> providerToSetAsDefault = null uiState = PayinAccountOverviewUiState.Content( @@ -94,9 +93,12 @@ internal class PayinAccountOverviewPresenter( CollectEvents { event -> when (event) { - PayinAccountOverviewEvent.Retry -> loadIteration++ + PayinAccountOverviewEvent.Retry -> { + loadIteration++ + } + is PayinAccountOverviewEvent.SetDefaultMethod -> { - val currentState = uiState as? PayinAccountOverviewUiState.Content ?: return@CollectEvents + val currentState = uiState as? PayinAccountOverviewUiState.Content ?: return@CollectEvents uiState = currentState.copy(setDefaultProviderError = null) providerToSetAsDefault = event.provider } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index 17da4a8c69..c3b6e1de0f 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -39,7 +39,7 @@ internal fun SelectPayinMethodDestination( navigateUp: () -> Unit, ) { HedvigScaffold( - topAppBarText = "Select billing method to add", //todo + topAppBarText = "Select billing method to add", // todo navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { @@ -50,7 +50,7 @@ internal fun SelectPayinMethodDestination( MemberPaymentProvider.TRUSTLY -> { PayinMethodRow( title = stringResource(Res.string.trustly), - subtitle = "Connect your bank via Trustly", //todo + subtitle = "Connect your bank via Trustly", // todo onClick = onTrustlySelected, ) } @@ -58,7 +58,7 @@ internal fun SelectPayinMethodDestination( MemberPaymentProvider.SWISH -> { PayinMethodRow( title = stringResource(Res.string.swish), - subtitle = "Monthly auto-payments through Swish", //todo + subtitle = "Monthly auto-payments through Swish", // todo onClick = onSwishSelected, ) } @@ -66,7 +66,7 @@ internal fun SelectPayinMethodDestination( MemberPaymentProvider.INVOICE -> { PayinMethodRow( title = stringResource(Res.string.PAYMENTS_INVOICE), - subtitle = stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION), //todo + subtitle = stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION), // todo onClick = onInvoiceSelected, ) } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt index 007cc46e50..d1059266e9 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt @@ -72,18 +72,20 @@ private fun SetupInvoicePayinScreen( navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { - //todo: some text here?? + // todo: some text here?? Column( modifier = Modifier .weight(1f) .fillMaxWidth() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { - HedvigText("You can choose invoice as your billing method. " + - "You will then get a monthly invoice via Kivra or email if you don't have Kivra account.", - textAlign = TextAlign.Center) + HedvigText( + "You can choose invoice as your billing method. " + + "You will then get a monthly invoice via Kivra or email if you don't have Kivra account.", + textAlign = TextAlign.Center, + ) } AnimatedVisibility( visible = uiState.errorMessage != null, @@ -101,7 +103,7 @@ private fun SetupInvoicePayinScreen( } Spacer(Modifier.height(16.dp)) HedvigButton( - text = "Set invoice as billing method", //todo + text = "Set invoice as billing method", // todo onClick = onConnect, enabled = !uiState.isLoading, isLoading = uiState.isLoading, @@ -113,7 +115,6 @@ private fun SetupInvoicePayinScreen( } } - @Composable @HedvigPreview private fun PreviewPayoutAccountOverviewScreen() { @@ -126,7 +127,9 @@ private fun PreviewPayoutAccountOverviewScreen() { showSuccessSnackBar = false, ), globalSnackBarState = GlobalSnackBarState(), - {}, {}, {}, + {}, + {}, + {}, ) } } diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt index c5364c5f65..43057acb91 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinViewModel.kt @@ -15,9 +15,9 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class SetupInvoicePayinViewModel( setupInvoicePayinUseCase: SetupInvoicePayinUseCase, ) : MoleculeViewModel( - SetupInvoicePayinUiState(false, null, false), - SetupInvoicePayinPresenter(setupInvoicePayinUseCase), -) + SetupInvoicePayinUiState(false, null, false), + SetupInvoicePayinPresenter(setupInvoicePayinUseCase), + ) internal sealed interface SetupInvoicePayoutEvent { data object Connect : SetupInvoicePayoutEvent @@ -32,7 +32,7 @@ internal data class SetupInvoicePayinUiState( ) internal class SetupInvoicePayinPresenter( - private val setupInvoicePayinUseCase: SetupInvoicePayinUseCase, + private val setupInvoicePayinUseCase: SetupInvoicePayinUseCase, ) : MoleculePresenter { @Composable override fun MoleculePresenterScope.present( diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/QRCode.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/QRCode.kt new file mode 100644 index 0000000000..4d65e4355a --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/QRCode.kt @@ -0,0 +1,59 @@ +package com.hedvig.android.feature.payin.account.ui.setupswish + +import android.annotation.SuppressLint +import android.graphics.Bitmap +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.painter.ColorPainter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.unit.IntSize +import com.google.zxing.BarcodeFormat +import com.google.zxing.common.BitMatrix +import com.google.zxing.qrcode.QRCodeWriter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@SuppressLint("ProduceStateDoesNotAssignValue") +@Composable +internal fun QRCode(token: String, modifier: Modifier = Modifier) { + var intSize: IntSize? by remember { mutableStateOf(null) } + val painter by produceState(ColorPainter(Color.Transparent), intSize, token) { + val size = intSize + if (size == null) { + value = ColorPainter(Color.Transparent) + return@produceState + } + val bitmapPainter: BitmapPainter = withContext(Dispatchers.Default) { + val bitMatrix: BitMatrix = QRCodeWriter().encode( + token, + BarcodeFormat.QR_CODE, + size.width, + size.height, + ) + val bitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.RGB_565) + for (x in 0 until size.width) { + for (y in 0 until size.height) { + val color = if (bitMatrix.get(x, y)) android.graphics.Color.BLACK else android.graphics.Color.WHITE + bitmap.setPixel(x, y, color) + } + } + BitmapPainter(bitmap.asImageBitmap()) + } + value = bitmapPainter + } + Image( + painter, + null, + modifier.onSizeChanged { intSize = it }, + ) +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index a4ef479bfa..26ef20cec2 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -3,26 +3,42 @@ package com.hedvig.android.feature.payin.account.ui.setupswish import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.interaction.MutableInteractionSource 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.size import androidx.compose.foundation.text.KeyboardOptions 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 androidx.compose.runtime.simulateHotReload import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.OffsetMapping +import androidx.compose.ui.text.input.TransformedText +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.design.system.hedvig.ButtonDefaults import com.hedvig.android.design.system.hedvig.EmptyState import com.hedvig.android.design.system.hedvig.EmptyStateDefaults import com.hedvig.android.design.system.hedvig.GlobalSnackBarState @@ -62,11 +78,14 @@ internal fun SetupSwishPayinDestination( onSuccessfullyConnected() }, navigateUp = navigateUp, - openUrl = openUrl + openUrl = openUrl, + updateText = { + viewModel.emit(SetupSwishPayoutEvent.UpdateText(it)) + }, ) } -//todo fetch payment methods continuously to see if it already not in pending state +// todo fetch payment methods continuously to see if it already not in pending state @Composable private fun SetupSwishPayoutScreen( @@ -76,7 +95,9 @@ private fun SetupSwishPayoutScreen( showedSnackBar: () -> Unit, navigateUp: () -> Unit, openUrl: (String) -> Unit, + updateText: (String) -> Unit, ) { + val focusManager = LocalFocusManager.current val changesSaved = stringResource(Res.string.CONTACT_INFO_CHANGES_SAVED) LaunchedEffect(uiState.showSuccessSnackBar) { if (!uiState.showSuccessSnackBar) return@LaunchedEffect @@ -98,54 +119,130 @@ private fun SetupSwishPayoutScreen( iconStyle = EmptyStateDefaults.EmptyStateIconStyle.ERROR, ) } - if (uiState.successUrl!=null) { + AnimatedVisibility(uiState.successUrl != null) { + if (uiState.successUrl != null) Column( - modifier= Modifier.fillMaxWidth().padding(horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, ) { EmptyState( text = "The process started", - description = "Please confirm in Swish app", - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), - iconStyle = EmptyStateDefaults.EmptyStateIconStyle.SUCCESS_WITH_WARNING, + description = "Please give your approval in the Swish app", + modifier = Modifier.fillMaxWidth(), + iconStyle = EmptyStateDefaults.EmptyStateIconStyle.SWISH, ) Spacer(Modifier.height(16.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + QRCode( + token = uiState.successUrl, + modifier = Modifier + .size(180.dp), + ) + } + Spacer(Modifier.height(32.dp)) HedvigButton( "Open Swish", enabled = true, onClick = { openUrl(uiState.successUrl) - } + }, + buttonStyle = ButtonDefaults.ButtonStyle.PrimaryAlt, + buttonSize = ButtonDefaults.ButtonSize.Medium, ) } - } Spacer(Modifier.weight(1f)) - Column(Modifier.padding(horizontal = 16.dp)) { - HedvigTextField( - state = uiState.phoneNumberState, - labelText = stringResource(Res.string.ODYSSEY_PHONE_NUMBER_LABEL), - textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Phone, - ), - modifier = Modifier.fillMaxWidth(), - ) + AnimatedVisibility(uiState.successUrl == null) { + Column { + Spacer(Modifier.height(16.dp)) + Column(Modifier.padding(horizontal = 16.dp)) { + var input by remember { mutableStateOf(uiState.phoneNumber) } + val mask = "000-000-00-00" + val maskColor = HedvigTheme.colorScheme.textTertiary + val visualTransformation = ChipIdVisualTransformation(mask, maskColor) + val interactionSource = remember { MutableInteractionSource() } + HedvigTextField( + text = input, + labelText = stringResource(Res.string.ODYSSEY_PHONE_NUMBER_LABEL), + textFieldSize = HedvigTextFieldDefaults.TextFieldSize.Medium, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Phone, + ), + modifier = Modifier.fillMaxWidth(), + visualTransformation = visualTransformation, + errorState = HedvigTextFieldDefaults.ErrorState.NoError, + interactionSource = interactionSource, + onValueChange = { + if (it.length <= 15) { + updateText(it) + input = it + } + }, + ) + } + Spacer(Modifier.height(16.dp)) + HedvigButton( + text = stringResource(Res.string.general_save_button), + onClick = { + focusManager.clearFocus() + onSave() + }, + enabled = !uiState.isLoading && + uiState.phoneNumber.length >= 8 && + uiState.phoneNumber.length <= 15, + isLoading = uiState.isLoading, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } } Spacer(Modifier.height(16.dp)) - HedvigButton( - text = stringResource(Res.string.general_save_button), - onClick = onSave, - enabled = !uiState.isLoading && - uiState.phoneNumberState.text.length >= 10 && - uiState.successUrl==null, - isLoading = uiState.isLoading, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) - Spacer(Modifier.height(16.dp)) + } +} + +private class ChipIdVisualTransformation( + private val mask: String, + private val maskColor: Color, +) : VisualTransformation { + override fun filter(text: AnnotatedString): TransformedText { + val trimmed = if (text.text.length >= 15) text.text.substring(0..14) else text.text + + val annotatedString = buildAnnotatedString { + for (i in trimmed.indices) { + append(trimmed[i]) + if (i in listOf(2, 5, 7)) { + append("-") + } + } + withStyle(SpanStyle(color = maskColor)) { + append(mask.takeLast((mask.length - length).coerceAtLeast(0))) + } + } + + val personalNumberOffsetTranslator = object : OffsetMapping { + override fun originalToTransformed(offset: Int): Int { + return when { + offset <= 2 -> offset + offset <= 5 -> offset + 1 + offset <= 7 -> offset + 2 + else -> offset + 3 + } + } + + override fun transformedToOriginal(offset: Int): Int { + return when { + offset <= 3 -> offset + offset <= 7 -> offset - 1 + offset <= 10 -> offset - 2 + else -> offset - 3 + }.coerceAtMost(text.length) + } + } + return TransformedText(annotatedString, personalNumberOffsetTranslator) } } @@ -162,39 +259,39 @@ private fun PreviewSetupSwishPayinScreen( onSave = {}, showedSnackBar = {}, navigateUp = {}, - {} + {}, + {}, ) } } } - private class SetupSwishPayinUiStateProvider : CollectionPreviewParameterProvider( listOf( SetupSwishPayoutUiState( - phoneNumberState = TextFieldState("287334432273"), + phoneNumber = "287334432273", isLoading = false, error = null, showSuccessSnackBar = false, ), SetupSwishPayoutUiState( - phoneNumberState = TextFieldState(), + phoneNumber = "", isLoading = false, error = ErrorMessage(), showSuccessSnackBar = false, ), SetupSwishPayoutUiState( - phoneNumberState = TextFieldState("837286428"), + phoneNumber = "837286428", isLoading = true, error = null, showSuccessSnackBar = false, ), SetupSwishPayoutUiState( - phoneNumberState = TextFieldState("83728644428"), + phoneNumber = "83728644428", isLoading = false, error = null, showSuccessSnackBar = false, - successUrl = "hwdjhew" - ) + successUrl = "hwdjhew", + ), ), ) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt index a2659100cb..14e24ca1b0 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt @@ -17,30 +17,31 @@ import com.hedvig.android.molecule.public.MoleculeViewModel internal class SetupSwishPayinViewModel( setupSwishPayoutUseCase: SetupSwishPayinUseCase, ) : MoleculeViewModel( - SetupSwishPayoutUiState( - phoneNumberState = TextFieldState(), - isLoading = false, - error = null, - showSuccessSnackBar = false, - successUrl = null - ), - SetupSwishPayoutPresenter(setupSwishPayoutUseCase), -) + SetupSwishPayoutUiState( + phoneNumber = "", + isLoading = false, + error = null, + showSuccessSnackBar = false, + successUrl = null, + ), + SetupSwishPayoutPresenter(setupSwishPayoutUseCase), + ) internal sealed interface SetupSwishPayoutEvent { data object Save : SetupSwishPayoutEvent data object ShowedSnackBar : SetupSwishPayoutEvent -} -internal data class SetupSwishPayoutUiState ( - val showSuccessSnackBar: Boolean, - val successUrl: String? = null, - val phoneNumberState: TextFieldState, - val isLoading: Boolean, - val error: ErrorMessage?, - val resultIsPending: Boolean = false + data class UpdateText(val newText: String) : SetupSwishPayoutEvent +} +internal data class SetupSwishPayoutUiState( + val showSuccessSnackBar: Boolean, + val successUrl: String? = null, + val phoneNumber: String, + val isLoading: Boolean, + val error: ErrorMessage?, + val resultIsPending: Boolean = false, ) internal class SetupSwishPayoutPresenter( @@ -50,7 +51,7 @@ internal class SetupSwishPayoutPresenter( override fun MoleculePresenterScope.present( lastState: SetupSwishPayoutUiState, ): SetupSwishPayoutUiState { - val phoneNumberState = remember { lastState.phoneNumberState } + var phoneNumberState by remember { mutableStateOf(lastState.phoneNumber) } var isLoading by remember { mutableStateOf(false) } var errorMessage by remember { mutableStateOf(null) } var showSuccessSnackBar by remember { mutableStateOf(false) } @@ -61,12 +62,11 @@ internal class SetupSwishPayoutPresenter( val currentSave = saveIteration if (currentSave != null) { - val currentState = uiState LaunchedEffect(currentSave) { isLoading = true errorMessage = null resultIsPending = false - setupSwishPayoutUseCase.invoke(phoneNumberState.text.toString()).fold( + setupSwishPayoutUseCase.invoke(phoneNumberState).fold( ifLeft = { isLoading = false errorMessage = it @@ -75,14 +75,16 @@ internal class SetupSwishPayoutPresenter( ifRight = { result -> isLoading = false saveIteration = null - when(result) { + when (result) { is SetupSwishResponse.Failure -> { errorMessage = result.error } + is SetupSwishResponse.Pending -> { urlToRedirect = result.url resultIsPending = true } + is SetupSwishResponse.Success -> { showSuccessSnackBar = true urlToRedirect = result.url @@ -97,22 +99,26 @@ internal class SetupSwishPayoutPresenter( when (event) { SetupSwishPayoutEvent.Save -> { if (!isLoading) { - saveIteration = phoneNumberState.text.toString() + saveIteration = phoneNumberState } } SetupSwishPayoutEvent.ShowedSnackBar -> { showSuccessSnackBar = false } + + is SetupSwishPayoutEvent.UpdateText -> { + phoneNumberState = event.newText + } } } return SetupSwishPayoutUiState( - phoneNumberState = phoneNumberState, + phoneNumber = phoneNumberState, isLoading = isLoading, error = errorMessage, showSuccessSnackBar = showSuccessSnackBar, - successUrl = urlToRedirect + successUrl = urlToRedirect, ) } } From fff8a290b25600ec90c09065ce28c8bfcfee532a Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Thu, 21 May 2026 14:21:46 +0200 Subject: [PATCH 14/30] don't pass on the error message if not user error --- .../feature/payin/account/data/SetupSwishPayinUseCase.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt index 9a1759b6ef..da4761e1cd 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt @@ -3,6 +3,7 @@ package com.hedvig.android.feature.payin.account.data import arrow.core.Either import arrow.core.raise.context.bind import arrow.core.raise.context.either +import arrow.core.raise.context.raise import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.ErrorMessage import com.hedvig.android.apollo.NetworkCacheManager @@ -24,7 +25,12 @@ internal class SetupSwishPayinUseCaseImpl( override suspend fun invoke(phoneNumber: String): Either = either { val result = apolloClient .mutation(SetupSwishPayinMutation(PaymentMethodSetupSwishInput(phoneNumber))) - .safeExecute(::ErrorMessage) + .safeExecute { + logcat { + "Mariia: SetupSwishPayinMutation error: $it" + } + raise(ErrorMessage()) + } .bind() val output = result.paymentMethodSetupSwishPayin From e7291a321ac739a24ca39196ec3a139846d76c2a Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 02:13:50 +0200 Subject: [PATCH 15/30] safeExecuteAllowingPartialResponses --- .../account/data/SetupSwishPayinUseCase.kt | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt index da4761e1cd..1088ea52a5 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt @@ -1,13 +1,12 @@ package com.hedvig.android.feature.payin.account.data import arrow.core.Either -import arrow.core.raise.context.bind import arrow.core.raise.context.either import arrow.core.raise.context.raise 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.apollo.safeExecuteAllowingPartialResponses import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.logger.logcat import octopus.SetupSwishPayinMutation @@ -23,42 +22,47 @@ internal class SetupSwishPayinUseCaseImpl( private val networkCacheManager: NetworkCacheManager, ) : SetupSwishPayinUseCase { override suspend fun invoke(phoneNumber: String): Either = either { - val result = apolloClient + apolloClient .mutation(SetupSwishPayinMutation(PaymentMethodSetupSwishInput(phoneNumber))) - .safeExecute { - logcat { - "Mariia: SetupSwishPayinMutation error: $it" - } - raise(ErrorMessage()) - } - .bind() + .safeExecuteAllowingPartialResponses() + .fold( + fa = { error -> + logcat { "SetupSwishPayinMutation error: $error" } + raise(ErrorMessage()) + }, + fb = { result -> + val output = result.paymentMethodSetupSwishPayin + when (output.status) { + PaymentMethodSetupStatus.ACTIVE -> { + logcat { + "Mariia: SetupSwishPayinMutation ACTIVE url: $output.url" + } + networkCacheManager.clearCache() + SetupSwishResponse.Success(output.url) + } - val output = result.paymentMethodSetupSwishPayin - when (output.status) { - PaymentMethodSetupStatus.ACTIVE -> { - logcat { - "Mariia: SetupSwishPayinMutation ACTIVE url: $output.url" - } - networkCacheManager.clearCache() - SetupSwishResponse.Success(output.url) - } + PaymentMethodSetupStatus.PENDING -> { + logcat { + "Mariia: SetupSwishPayinMutation PENDING url: $output.url" + } + networkCacheManager.clearCache() + SetupSwishResponse.Pending(output.url) + } - PaymentMethodSetupStatus.PENDING -> { - logcat { - "Mariia: SetupSwishPayinMutation PENDING url: $output.url" - } - networkCacheManager.clearCache() - SetupSwishResponse.Pending(output.url) - } - - PaymentMethodSetupStatus.FAILED, PaymentMethodSetupStatus.UNKNOWN__ -> { - logcat { - "SetupSwishPayinMutation failed with: output.error?.message" - } - val userMessage = output.error?.message - SetupSwishResponse.Failure(ErrorMessage(userMessage)) - } - } + PaymentMethodSetupStatus.FAILED, PaymentMethodSetupStatus.UNKNOWN__ -> { + logcat { + "SetupSwishPayinMutation failed with: output.error?.message" + } + val userMessage = output.error?.message + SetupSwishResponse.Failure(ErrorMessage(userMessage)) + } + } + }, + fab = { errors, _ -> + logcat { "SetupSwishPayinMutation dataa with errors: $errors" } + raise(ErrorMessage()) + }, + ) } } From cb10b86a1b21d99f5c3b048bb4b282e98860aba1 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 02:51:52 +0200 Subject: [PATCH 16/30] icons in overview --- .../design/system/hedvig/icon/Autogiro.kt | 168 +++++++++ .../system/hedvig/icon/colored/Kivra.kt | 333 ++++++++++++++++++ .../account/data/GetPayinAccountUseCase.kt | 6 +- .../PayinAccountOverviewDestination.kt | 88 +++-- 4 files changed, 569 insertions(+), 26 deletions(-) create mode 100644 app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/Autogiro.kt create mode 100644 app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Kivra.kt diff --git a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/Autogiro.kt b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/Autogiro.kt new file mode 100644 index 0000000000..00aab9af82 --- /dev/null +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/Autogiro.kt @@ -0,0 +1,168 @@ +package com.hedvig.android.design.system.hedvig.icon + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.Icon +import com.hedvig.android.design.system.hedvig.icon.colored.Swish + +val HedvigIcons.Autogiro: ImageVector + get() { + if (_NounCardSubscriptionPayment8118834 != null) { + return _NounCardSubscriptionPayment8118834!! + } + _NounCardSubscriptionPayment8118834 = ImageVector.Builder( + name = "NounCardSubscriptionPayment8118834", + defaultWidth = 110.dp, + defaultHeight = 135.dp, + viewportWidth = 110f, + viewportHeight = 135f + ).apply { + path(fill = SolidColor(Color.Black)) { + moveToRelative(93.54f, 57.92f) + verticalLineToRelative(-6.67f) + curveToRelative(0f, -4.71f, -0f, -8.07f, -0.22f, -10.69f) + curveToRelative(-0.21f, -2.59f, -0.61f, -4.19f, -1.26f, -5.46f) + curveToRelative(-1.3f, -2.55f, -3.37f, -4.62f, -5.92f, -5.92f) + curveToRelative(-1.27f, -0.64f, -2.87f, -1.05f, -5.46f, -1.26f) + curveToRelative(-2.62f, -0.21f, -5.98f, -0.22f, -10.69f, -0.22f) + horizontalLineToRelative(-30f) + curveToRelative(-4.71f, 0f, -8.07f, 0f, -10.69f, 0.22f) + curveToRelative(-2.59f, 0.21f, -4.19f, 0.61f, -5.46f, 1.26f) + curveToRelative(-2.55f, 1.3f, -4.62f, 3.37f, -5.92f, 5.92f) + curveToRelative(-0.64f, 1.27f, -1.05f, 2.87f, -1.26f, 5.46f) + curveToRelative(-0.21f, 2.62f, -0.22f, 5.98f, -0.22f, 10.69f) + verticalLineToRelative(13.33f) + curveToRelative(0f, 4.72f, 0f, 8.07f, 0.22f, 10.69f) + curveToRelative(0.21f, 2.59f, 0.61f, 4.19f, 1.26f, 5.46f) + curveToRelative(1.3f, 2.55f, 3.37f, 4.62f, 5.92f, 5.92f) + curveToRelative(1.27f, 0.64f, 2.87f, 1.05f, 5.46f, 1.26f) + curveToRelative(2.62f, 0.21f, 5.98f, 0.21f, 10.69f, 0.21f) + horizontalLineToRelative(17.08f) + curveToRelative(1.73f, 0f, 3.13f, 1.4f, 3.13f, 3.13f) + reflectiveCurveToRelative(-1.4f, 3.13f, -3.13f, 3.13f) + horizontalLineToRelative(-17.08f) + curveToRelative(-4.61f, 0f, -8.27f, 0f, -11.2f, -0.23f) + curveToRelative(-2.97f, -0.25f, -5.49f, -0.75f, -7.79f, -1.92f) + curveToRelative(-3.72f, -1.9f, -6.75f, -4.93f, -8.65f, -8.65f) + curveToRelative(-1.17f, -2.3f, -1.68f, -4.81f, -1.92f, -7.78f) + curveToRelative(-0.24f, -2.94f, -0.24f, -6.59f, -0.24f, -11.2f) + verticalLineToRelative(-13.33f) + curveToRelative(0f, -4.61f, -0f, -8.27f, 0.24f, -11.2f) + curveToRelative(0.24f, -2.97f, 0.75f, -5.49f, 1.92f, -7.79f) + curveToRelative(1.9f, -3.72f, 4.93f, -6.75f, 8.65f, -8.65f) + curveToRelative(2.3f, -1.17f, 4.81f, -1.68f, 7.79f, -1.92f) + curveToRelative(2.93f, -0.24f, 6.59f, -0.24f, 11.2f, -0.24f) + horizontalLineToRelative(30f) + curveToRelative(4.61f, 0f, 8.27f, -0f, 11.2f, 0.24f) + curveToRelative(2.97f, 0.24f, 5.49f, 0.75f, 7.79f, 1.92f) + curveToRelative(3.72f, 1.9f, 6.75f, 4.93f, 8.65f, 8.65f) + curveToRelative(1.17f, 2.3f, 1.68f, 4.81f, 1.92f, 7.79f) + curveToRelative(0.24f, 2.93f, 0.24f, 6.59f, 0.24f, 11.2f) + verticalLineToRelative(6.67f) + curveToRelative(0f, 1.72f, -1.4f, 3.13f, -3.13f, 3.13f) + curveToRelative(-1.73f, 0f, -3.13f, -1.4f, -3.13f, -3.13f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveToRelative(94.58f, 42.29f) + verticalLineToRelative(6.25f) + horizontalLineToRelative(-79.16f) + verticalLineToRelative(-6.25f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveToRelative(46.67f, 71.46f) + curveToRelative(1.72f, 0f, 3.13f, 1.4f, 3.13f, 3.13f) + curveToRelative(0f, 1.73f, -1.4f, 3.13f, -3.13f, 3.13f) + horizontalLineToRelative(-20.84f) + curveToRelative(-1.72f, 0f, -3.13f, -1.4f, -3.13f, -3.13f) + curveToRelative(0f, -1.72f, 1.4f, -3.13f, 3.13f, -3.13f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveToRelative(93.54f, 76.67f) + curveToRelative(0f, -2.88f, -2.33f, -5.21f, -5.21f, -5.21f) + horizontalLineToRelative(-18.75f) + curveToRelative(-1.72f, 0f, -3.13f, -1.4f, -3.13f, -3.13f) + curveToRelative(0f, -1.72f, 1.4f, -3.13f, 3.13f, -3.13f) + horizontalLineToRelative(18.75f) + curveToRelative(6.33f, 0f, 11.46f, 5.13f, 11.46f, 11.46f) + curveToRelative(0f, 1.72f, -1.4f, 3.13f, -3.13f, 3.13f) + curveToRelative(-1.73f, 0f, -3.13f, -1.4f, -3.13f, -3.13f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveToRelative(71.54f, 59.88f) + curveToRelative(1.22f, -1.22f, 3.2f, -1.22f, 4.42f, 0f) + curveToRelative(1.22f, 1.22f, 1.22f, 3.2f, 0f, 4.42f) + lineToRelative(-4.04f, 4.04f) + lineToRelative(4.04f, 4.04f) + curveToRelative(1.22f, 1.22f, 1.22f, 3.2f, 0f, 4.42f) + curveToRelative(-1.22f, 1.22f, -3.2f, 1.22f, -4.42f, 0f) + lineToRelative(-6.25f, -6.25f) + curveToRelative(-1.22f, -1.22f, -1.22f, -3.2f, 0f, -4.42f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveToRelative(70.63f, 82.92f) + curveToRelative(0f, 2.88f, 2.33f, 5.21f, 5.21f, 5.21f) + horizontalLineToRelative(18.75f) + curveToRelative(1.73f, 0f, 3.13f, 1.4f, 3.13f, 3.13f) + reflectiveCurveToRelative(-1.4f, 3.13f, -3.13f, 3.13f) + horizontalLineToRelative(-18.75f) + curveToRelative(-6.33f, 0f, -11.46f, -5.13f, -11.46f, -11.46f) + curveToRelative(0f, -1.73f, 1.4f, -3.13f, 3.13f, -3.13f) + reflectiveCurveToRelative(3.13f, 1.4f, 3.13f, 3.13f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveToRelative(92.63f, 99.71f) + curveToRelative(-1.22f, 1.22f, -3.2f, 1.22f, -4.42f, 0f) + curveToRelative(-1.22f, -1.22f, -1.22f, -3.2f, 0f, -4.42f) + lineToRelative(4.04f, -4.04f) + lineToRelative(-4.04f, -4.04f) + curveToRelative(-1.22f, -1.22f, -1.22f, -3.2f, 0f, -4.42f) + curveToRelative(1.22f, -1.22f, 3.2f, -1.22f, 4.42f, 0f) + lineToRelative(6.25f, 6.25f) + curveToRelative(1.22f, 1.22f, 1.22f, 3.2f, 0f, 4.42f) + close() + } + }.build() + + return _NounCardSubscriptionPayment8118834!! + } + +@Suppress("ObjectPropertyName") +private var _NounCardSubscriptionPayment8118834: ImageVector? = null + +@Preview +@Composable +private fun IconPreview() { + HedvigTheme { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + imageVector = HedvigIcons.Autogiro, + contentDescription = com.hedvig.android.compose.ui.EmptyContentDescription, + modifier = Modifier + .width((24.0).dp) + .height((24.0).dp), + ) + } + } +} diff --git a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Kivra.kt b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Kivra.kt new file mode 100644 index 0000000000..3df5b7911a --- /dev/null +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/colored/Kivra.kt @@ -0,0 +1,333 @@ +package com.hedvig.android.design.system.hedvig.icon.colored + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.icon.HedvigIcons + + +val HedvigIcons.Kivra: ImageVector + get() { + if (_KivraLogo1920X1080Green != null) { + return _KivraLogo1920X1080Green!! + } + _KivraLogo1920X1080Green = ImageVector.Builder( + name = "KivraLogo1920X1080Green", + defaultWidth = 1912.dp, + defaultHeight = 1015.dp, + viewportWidth = 1912f, + viewportHeight = 1015f + ).apply { + path(fill = SolidColor(Color(0xFF75C72E))) { + moveTo(1274f, 292f) + curveTo(1274.9f, 292.2f, 1275.7f, 292.4f, 1276.6f, 292.6f) + curveTo(1303.7f, 299.6f, 1327.7f, 312.8f, 1348f, 332f) + curveTo(1348.8f, 332.7f, 1349.6f, 333.5f, 1350.5f, 334.3f) + curveTo(1376.4f, 358.9f, 1389.9f, 394.6f, 1391.2f, 429.9f) + curveTo(1392.1f, 467.8f, 1380.2f, 502.3f, 1354f, 530f) + curveTo(1353f, 531.1f, 1353f, 531.1f, 1352f, 532.2f) + curveTo(1338.7f, 546.2f, 1319.6f, 558.6f, 1301f, 564f) + curveTo(1300f, 564f, 1299f, 564f, 1298f, 564f) + curveTo(1300.6f, 566.9f, 1303.7f, 568.9f, 1307f, 571.1f) + curveTo(1351.8f, 601.8f, 1380f, 651.6f, 1391.3f, 704f) + curveTo(1391.5f, 704.7f, 1391.6f, 705.5f, 1391.8f, 706.2f) + curveTo(1393.1f, 712.5f, 1393.5f, 718.4f, 1394f, 725f) + curveTo(1366.3f, 725f, 1338.6f, 725f, 1310f, 725f) + curveTo(1308.3f, 716.8f, 1306.7f, 708.5f, 1305f, 700f) + curveTo(1297.1f, 673.4f, 1285.3f, 649.9f, 1267f, 629f) + curveTo(1266.2f, 628.1f, 1265.4f, 627.2f, 1264.6f, 626.2f) + curveTo(1258.9f, 619.7f, 1252.8f, 614.3f, 1246f, 609f) + curveTo(1245.1f, 608.3f, 1244.2f, 607.5f, 1243.2f, 606.8f) + curveTo(1226.4f, 593.6f, 1206.7f, 584.4f, 1186.2f, 578.9f) + curveTo(1183f, 578f, 1183f, 578f, 1182f, 577f) + curveTo(1181.9f, 575.5f, 1181.9f, 574f, 1181.9f, 572.5f) + curveTo(1181.9f, 571f, 1181.9f, 571f, 1181.9f, 569.6f) + curveTo(1181.9f, 568.5f, 1181.9f, 567.5f, 1181.9f, 566.4f) + curveTo(1181.9f, 565.3f, 1181.9f, 564.3f, 1181.9f, 563.2f) + curveTo(1181.9f, 559.8f, 1181.9f, 556.4f, 1181.9f, 552.9f) + curveTo(1181.9f, 550.6f, 1181.9f, 548.3f, 1182f, 546f) + curveTo(1182f, 540.3f, 1182f, 534.7f, 1182f, 529f) + curveTo(1183.2f, 528.9f, 1183.2f, 528.9f, 1184.4f, 528.9f) + curveTo(1201.2f, 528f, 1217.6f, 526.8f, 1234f, 523f) + curveTo(1235.1f, 522.7f, 1235.1f, 522.7f, 1236.3f, 522.5f) + curveTo(1259.9f, 516.8f, 1282.9f, 504.6f, 1296.3f, 483.7f) + curveTo(1308.5f, 462.6f, 1311f, 439.4f, 1305f, 416f) + curveTo(1299.4f, 397.5f, 1287.8f, 382.6f, 1271f, 373f) + curveTo(1250.9f, 362.6f, 1225.7f, 360.1f, 1203.9f, 366.6f) + curveTo(1183f, 373.7f, 1166.3f, 387.1f, 1156f, 406.7f) + curveTo(1147.2f, 424.7f, 1144.9f, 443.9f, 1144.8f, 463.8f) + curveTo(1144.8f, 464.9f, 1144.8f, 466.1f, 1144.8f, 467.2f) + curveTo(1144.8f, 471f, 1144.8f, 474.7f, 1144.8f, 478.4f) + curveTo(1144.8f, 481.1f, 1144.8f, 483.8f, 1144.8f, 486.5f) + curveTo(1144.7f, 492.3f, 1144.7f, 498.1f, 1144.7f, 503.8f) + curveTo(1144.7f, 512.2f, 1144.7f, 520.5f, 1144.6f, 528.9f) + curveTo(1144.6f, 542.4f, 1144.5f, 556f, 1144.5f, 569.5f) + curveTo(1144.5f, 582.7f, 1144.4f, 595.9f, 1144.4f, 609f) + curveTo(1144.4f, 609.8f, 1144.4f, 610.6f, 1144.4f, 611.5f) + curveTo(1144.4f, 615.6f, 1144.3f, 619.6f, 1144.3f, 623.7f) + curveTo(1144.2f, 657.5f, 1144.1f, 691.2f, 1144f, 725f) + curveTo(1117.6f, 725f, 1091.2f, 725f, 1064f, 725f) + curveTo(1063.9f, 688f, 1063.9f, 651f, 1063.8f, 614f) + curveTo(1063.8f, 609.7f, 1063.8f, 605.3f, 1063.8f, 600.9f) + curveTo(1063.8f, 600f, 1063.8f, 599.2f, 1063.8f, 598.3f) + curveTo(1063.8f, 584.2f, 1063.8f, 570.1f, 1063.8f, 556.1f) + curveTo(1063.7f, 541.6f, 1063.7f, 527.2f, 1063.7f, 512.7f) + curveTo(1063.7f, 503.8f, 1063.7f, 494.9f, 1063.7f, 486f) + curveTo(1063.7f, 479.9f, 1063.7f, 473.7f, 1063.7f, 467.6f) + curveTo(1063.7f, 464.1f, 1063.7f, 460.6f, 1063.6f, 457.1f) + curveTo(1063.6f, 438f, 1064.2f, 419.5f, 1069.8f, 401.2f) + curveTo(1070.2f, 399.9f, 1070.2f, 399.9f, 1070.6f, 398.6f) + curveTo(1078.1f, 374f, 1090.2f, 353.5f, 1108f, 335f) + curveTo(1108.7f, 334.2f, 1109.4f, 333.4f, 1110.2f, 332.6f) + curveTo(1126.1f, 315.8f, 1147.4f, 304.6f, 1169f, 297f) + curveTo(1169.7f, 296.7f, 1170.4f, 296.5f, 1171.2f, 296.2f) + curveTo(1176f, 294.6f, 1181f, 293.2f, 1186f, 292f) + curveTo(1186.8f, 291.8f, 1187.6f, 291.6f, 1188.4f, 291.4f) + curveTo(1215.5f, 285.1f, 1247.1f, 285.3f, 1274f, 292f) + close() + } + path(fill = SolidColor(Color(0xFF75C72E))) { + moveTo(141f, 295f) + curveTo(167.4f, 295f, 193.8f, 295f, 221f, 295f) + curveTo(221.3f, 360.3f, 221.7f, 425.7f, 222f, 493f) + curveTo(225f, 489.4f, 227.9f, 485.7f, 231f, 482f) + curveTo(232.6f, 480.1f, 234.3f, 478.3f, 236f, 476.5f) + curveTo(239.3f, 472.9f, 242.4f, 469.3f, 245.5f, 465.5f) + curveTo(249.3f, 460.7f, 253.4f, 456.2f, 257.5f, 451.8f) + curveTo(259.9f, 449.1f, 262.2f, 446.3f, 264.5f, 443.5f) + curveTo(268.3f, 438.7f, 272.4f, 434.2f, 276.5f, 429.8f) + curveTo(278.9f, 427.1f, 281.2f, 424.3f, 283.5f, 421.5f) + curveTo(287.3f, 416.7f, 291.4f, 412.2f, 295.5f, 407.8f) + curveTo(297.9f, 405.1f, 300.2f, 402.3f, 302.5f, 399.5f) + curveTo(306.3f, 394.7f, 310.4f, 390.2f, 314.5f, 385.7f) + curveTo(316.9f, 383.1f, 319.2f, 380.4f, 321.4f, 377.6f) + curveTo(325.1f, 373f, 328.9f, 368.7f, 332.9f, 364.5f) + curveTo(336.7f, 360.4f, 340.2f, 356.1f, 343.8f, 351.7f) + curveTo(346.1f, 348.9f, 348.5f, 346.2f, 351f, 343.5f) + curveTo(354.3f, 339.9f, 357.4f, 336.3f, 360.5f, 332.5f) + curveTo(363.6f, 328.7f, 366.7f, 325.1f, 370f, 321.5f) + curveTo(374f, 317.1f, 377.8f, 312.6f, 381.5f, 308f) + curveTo(382.3f, 307f, 383.2f, 306f, 384f, 305f) + curveTo(384.5f, 304.2f, 384.9f, 303.4f, 385.4f, 302.6f) + curveTo(388f, 298.8f, 390.5f, 295.6f, 395.1f, 294.4f) + curveTo(401.8f, 293.6f, 408.4f, 293.9f, 415.1f, 294f) + curveTo(417.3f, 294f, 419.4f, 294.1f, 421.6f, 294.1f) + curveTo(427.3f, 294.1f, 432.9f, 294.2f, 438.6f, 294.3f) + curveTo(444.4f, 294.4f, 450.2f, 294.5f, 456f, 294.5f) + curveTo(467.3f, 294.6f, 478.7f, 294.8f, 490f, 295f) + curveTo(488.3f, 298.7f, 486.1f, 301.3f, 483.3f, 304.3f) + curveTo(482.4f, 305.2f, 481.5f, 306.2f, 480.6f, 307.2f) + curveTo(479.3f, 308.6f, 479.3f, 308.6f, 478f, 310f) + curveTo(474.7f, 313.7f, 471.5f, 317.4f, 468.3f, 321.2f) + curveTo(462.5f, 328f, 456.5f, 334.7f, 450.4f, 341.3f) + curveTo(446.3f, 345.9f, 442.3f, 350.5f, 438.3f, 355.1f) + curveTo(432.8f, 361.7f, 427.1f, 368.1f, 421.3f, 374.3f) + curveTo(418.2f, 377.6f, 415.4f, 380.9f, 412.6f, 384.4f) + curveTo(408f, 390.2f, 403f, 395.5f, 398f, 400.8f) + curveTo(393.6f, 405.5f, 389.5f, 410.3f, 385.3f, 415.1f) + curveTo(379.8f, 421.7f, 374.1f, 428.1f, 368.3f, 434.3f) + curveTo(364.5f, 438.4f, 360.9f, 442.7f, 357.4f, 447.1f) + curveTo(354f, 451.2f, 350.3f, 455.2f, 346.5f, 459.1f) + curveTo(345.5f, 460.2f, 344.4f, 461.3f, 343.4f, 462.4f) + curveTo(342.1f, 463.9f, 340.7f, 465.3f, 339.3f, 466.6f) + curveTo(337.7f, 470.8f, 338.3f, 471.7f, 340f, 475.7f) + curveTo(341.1f, 478f, 342.3f, 480.3f, 343.4f, 482.6f) + curveTo(344.1f, 483.9f, 344.7f, 485.1f, 345.3f, 486.4f) + curveTo(346.5f, 489f, 347.8f, 491.6f, 349.1f, 494.1f) + curveTo(352f, 500f, 354.8f, 506f, 357.5f, 512f) + curveTo(358.6f, 514.3f, 359.7f, 516.7f, 360.8f, 519f) + curveTo(363.4f, 524.8f, 366.1f, 530.6f, 368.8f, 536.4f) + curveTo(370.4f, 539.9f, 372.1f, 543.4f, 373.7f, 546.8f) + curveTo(375.7f, 551.1f, 377.6f, 555.4f, 379.6f, 559.7f) + curveTo(404.1f, 616.1f, 404.1f, 616.1f, 451.5f, 651.9f) + curveTo(459.7f, 654.5f, 467.4f, 655.3f, 476f, 655.2f) + curveTo(476.9f, 655.2f, 477.8f, 655.2f, 478.8f, 655.2f) + curveTo(485f, 655.1f, 490.8f, 654.6f, 497f, 654f) + curveTo(497f, 677.1f, 497f, 700.2f, 497f, 724f) + curveTo(485.2f, 726.1f, 474.5f, 727.5f, 462.7f, 727.4f) + curveTo(461.9f, 727.4f, 461.2f, 727.4f, 460.4f, 727.4f) + curveTo(448.2f, 727.4f, 436.7f, 726.5f, 425f, 723f) + curveTo(423.9f, 722.7f, 423.9f, 722.7f, 422.8f, 722.4f) + curveTo(401.1f, 715.8f, 383.2f, 703.6f, 367f, 688f) + curveTo(366.2f, 687.2f, 365.3f, 686.5f, 364.5f, 685.7f) + curveTo(333.2f, 656f, 316.1f, 612.6f, 298.4f, 574.3f) + curveTo(296f, 569f, 293.5f, 563.8f, 291.1f, 558.6f) + curveTo(290.4f, 557.2f, 289.8f, 555.9f, 289.1f, 554.5f) + curveTo(288.2f, 552.4f, 287.2f, 550.4f, 286.2f, 548.4f) + curveTo(285.4f, 546.5f, 284.5f, 544.6f, 283.6f, 542.8f) + curveTo(282.8f, 541.2f, 282.8f, 541.2f, 282.1f, 539.5f) + curveTo(281f, 537f, 281f, 537f, 281f, 535f) + curveTo(276f, 538.9f, 272.1f, 543.2f, 268f, 548f) + curveTo(266.1f, 550.3f, 264.2f, 552.5f, 262.3f, 554.8f) + curveTo(261.7f, 555.4f, 261.2f, 556f, 260.7f, 556.6f) + curveTo(256.4f, 561.6f, 251.9f, 566.5f, 247.5f, 571.3f) + curveTo(244.8f, 574.2f, 242.3f, 577.1f, 239.7f, 580f) + curveTo(237f, 583.1f, 234.2f, 586.2f, 231.4f, 589.3f) + curveTo(230.6f, 590.2f, 229.8f, 591.1f, 229f, 592f) + curveTo(228.4f, 592.5f, 227.9f, 593f, 227.3f, 593.6f) + curveTo(222.3f, 598.4f, 221.2f, 602.1f, 221.1f, 608.9f) + curveTo(221.1f, 610.1f, 221.1f, 611.2f, 221.1f, 612.4f) + curveTo(221.1f, 613.6f, 221.1f, 614.8f, 221.1f, 616.1f) + curveTo(221.1f, 619.4f, 221.1f, 622.7f, 221.1f, 626.1f) + curveTo(221.1f, 629.5f, 221.1f, 633f, 221.1f, 636.5f) + curveTo(221f, 643.1f, 221f, 649.7f, 221.1f, 656.2f) + curveTo(221.1f, 664.5f, 221f, 672.7f, 221f, 681f) + curveTo(221f, 695.6f, 221f, 710.3f, 221f, 725f) + curveTo(194.6f, 725f, 168.2f, 725f, 141f, 725f) + curveTo(141f, 583.1f, 141f, 441.2f, 141f, 295f) + close() + } + path(fill = SolidColor(Color(0xFF75C72E))) { + moveTo(1567f, 295f) + curveTo(1596.7f, 295f, 1626.4f, 295f, 1657f, 295f) + curveTo(1661.8f, 303.4f, 1666.6f, 311.8f, 1671f, 320.4f) + curveTo(1675f, 328.2f, 1675f, 328.2f, 1679.1f, 335.8f) + curveTo(1685.1f, 346.8f, 1690.3f, 358.2f, 1695.5f, 369.6f) + curveTo(1697.3f, 373.4f, 1699.1f, 377.3f, 1700.9f, 381.1f) + curveTo(1716.7f, 414.6f, 1730.4f, 449.1f, 1743f, 484f) + curveTo(1743.5f, 485.3f, 1743.5f, 485.3f, 1743.9f, 486.6f) + curveTo(1768.6f, 554.9f, 1787.8f, 624.7f, 1802.1f, 695.9f) + curveTo(1802.3f, 696.8f, 1802.5f, 697.8f, 1802.7f, 698.8f) + curveTo(1803.3f, 701.5f, 1803.8f, 704.2f, 1804.3f, 706.9f) + curveTo(1804.5f, 707.7f, 1804.6f, 708.5f, 1804.8f, 709.3f) + curveTo(1805.8f, 714.6f, 1806.4f, 719.7f, 1807f, 725f) + curveTo(1780.3f, 725f, 1753.5f, 725f, 1726f, 725f) + curveTo(1720.4f, 695.4f, 1720.4f, 695.4f, 1718.7f, 686.3f) + curveTo(1716.1f, 672.4f, 1713.3f, 658.5f, 1710.2f, 644.6f) + curveTo(1710f, 643.9f, 1709.9f, 643.3f, 1709.7f, 642.6f) + curveTo(1708.1f, 635.2f, 1708.1f, 635.2f, 1706f, 628f) + curveTo(1705.2f, 628.1f, 1704.4f, 628.3f, 1703.6f, 628.4f) + curveTo(1687.4f, 631.3f, 1671.4f, 633.9f, 1655f, 635f) + curveTo(1653.9f, 635.1f, 1652.7f, 635.2f, 1651.5f, 635.3f) + curveTo(1638.4f, 636.2f, 1625.4f, 636.2f, 1612.3f, 636.2f) + curveTo(1610.5f, 636.2f, 1610.5f, 636.2f, 1608.7f, 636.2f) + curveTo(1577.4f, 636.1f, 1546.8f, 633.8f, 1516f, 628f) + curveTo(1515.9f, 629.1f, 1515.8f, 630.2f, 1515.6f, 631.4f) + curveTo(1515f, 636.2f, 1514f, 640.9f, 1513f, 645.7f) + curveTo(1512.5f, 647.6f, 1512.1f, 649.5f, 1511.7f, 651.4f) + curveTo(1511.5f, 652.4f, 1511.3f, 653.4f, 1511f, 654.4f) + curveTo(1506.9f, 673.2f, 1503.2f, 692f, 1500.1f, 711f) + curveTo(1499.9f, 711.8f, 1499.8f, 712.6f, 1499.7f, 713.4f) + curveTo(1499.4f, 714.9f, 1499.2f, 716.4f, 1498.9f, 717.9f) + curveTo(1498.1f, 722.8f, 1498.1f, 722.8f, 1497f, 725f) + curveTo(1470.3f, 725f, 1443.5f, 725f, 1416f, 725f) + curveTo(1424.2f, 680.4f, 1424.2f, 680.4f, 1427.8f, 664.8f) + curveTo(1428.1f, 663.4f, 1428.1f, 663.4f, 1428.4f, 662f) + curveTo(1448.5f, 572.7f, 1477.2f, 485.7f, 1531f, 364f) + curveTo(1531.6f, 362.7f, 1532.3f, 361.3f, 1532.9f, 360f) + curveTo(1543.5f, 337.8f, 1554.8f, 316.3f, 1567f, 295f) + close() + moveTo(1611f, 367f) + curveTo(1600.2f, 388f, 1590.3f, 409.2f, 1580.9f, 430.9f) + curveTo(1580f, 432.9f, 1579.1f, 435f, 1578.3f, 437f) + curveTo(1563.9f, 470f, 1552f, 503.8f, 1541f, 538f) + curveTo(1540.8f, 538.7f, 1540.5f, 539.5f, 1540.3f, 540.2f) + curveTo(1539.6f, 542.4f, 1538.9f, 544.6f, 1538.2f, 546.9f) + curveTo(1537.8f, 547.9f, 1537.8f, 547.9f, 1537.5f, 548.9f) + curveTo(1536.6f, 551.7f, 1536f, 554f, 1536f, 557f) + curveTo(1539.5f, 557.6f, 1543.1f, 558.3f, 1546.6f, 558.9f) + curveTo(1547.6f, 559f, 1548.5f, 559.2f, 1549.5f, 559.4f) + curveTo(1569.9f, 562.9f, 1590.3f, 563.4f, 1611f, 563.3f) + curveTo(1612.9f, 563.3f, 1612.9f, 563.3f, 1614.9f, 563.3f) + curveTo(1633.8f, 563.3f, 1652.3f, 562.9f, 1671f, 560f) + curveTo(1671.9f, 559.9f, 1672.9f, 559.7f, 1673.8f, 559.6f) + curveTo(1678.6f, 558.8f, 1683.3f, 558f, 1688f, 557f) + curveTo(1671.9f, 500.9f, 1650.4f, 446.8f, 1625.6f, 394f) + curveTo(1625.1f, 392.9f, 1625.1f, 392.9f, 1624.6f, 391.8f) + curveTo(1620.7f, 383.4f, 1616.6f, 375.1f, 1612f, 367f) + curveTo(1611.7f, 367f, 1611.3f, 367f, 1611f, 367f) + close() + } + path(fill = SolidColor(Color(0xFF75C72E))) { + moveTo(657f, 295f) + curveTo(683.7f, 295f, 710.5f, 295f, 738f, 295f) + curveTo(739.9f, 305.1f, 739.9f, 305.1f, 741.9f, 315.5f) + curveTo(759.9f, 407.5f, 785.9f, 497.9f, 845.1f, 637.5f) + curveTo(845.5f, 638.4f, 845.9f, 639.2f, 846.3f, 640f) + curveTo(846.6f, 640.8f, 846.9f, 641.5f, 847.3f, 642.2f) + curveTo(848f, 644f, 848f, 644f, 848f, 646f) + curveTo(848.7f, 646f, 849.3f, 646f, 850f, 646f) + curveTo(850.3f, 645.2f, 850.5f, 644.3f, 850.8f, 643.5f) + curveTo(852.1f, 639.8f, 853.6f, 636.2f, 855.3f, 632.6f) + curveTo(856f, 631.2f, 856.6f, 629.7f, 857.3f, 628.2f) + curveTo(857.8f, 627f, 857.8f, 627f, 858.4f, 625.8f) + curveTo(874.2f, 591f, 888.4f, 555.6f, 901.1f, 519.5f) + curveTo(902f, 517f, 902.9f, 514.5f, 903.8f, 512f) + curveTo(927.1f, 446.2f, 944.6f, 378.5f, 957.8f, 310f) + curveTo(958.8f, 305f, 959.9f, 300f, 961f, 295f) + curveTo(987.4f, 295f, 1013.8f, 295f, 1041f, 295f) + curveTo(1040.4f, 301.7f, 1039.8f, 308.2f, 1038.5f, 314.8f) + curveTo(1038.4f, 315.6f, 1038.2f, 316.4f, 1038.1f, 317.2f) + curveTo(1037.6f, 319.8f, 1037.1f, 322.5f, 1036.6f, 325.1f) + curveTo(1036.4f, 326.5f, 1036.4f, 326.5f, 1036.1f, 327.9f) + curveTo(1023.6f, 394.5f, 1005.7f, 459.8f, 983.7f, 523.9f) + curveTo(983f, 526f, 982.3f, 528.1f, 981.6f, 530.2f) + curveTo(966.6f, 574.1f, 948.9f, 617.1f, 929f, 659f) + curveTo(928.7f, 659.6f, 928.4f, 660.2f, 928.1f, 660.8f) + curveTo(919.9f, 678.2f, 911.5f, 695.3f, 902f, 712f) + curveTo(901.4f, 713f, 901.4f, 713f, 900.8f, 714.1f) + curveTo(895.4f, 723.6f, 895.4f, 723.6f, 894f, 725f) + curveTo(891.2f, 725.1f, 888.4f, 725.1f, 885.5f, 725.1f) + curveTo(884.6f, 725.1f, 883.8f, 725.1f, 882.8f, 725.1f) + curveTo(879.9f, 725.1f, 876.9f, 725.1f, 874f, 725.1f) + curveTo(872f, 725.1f, 869.9f, 725.1f, 867.9f, 725.1f) + curveTo(862.5f, 725.1f, 857.1f, 725.1f, 851.7f, 725.1f) + curveTo(846.3f, 725.1f, 840.8f, 725.1f, 835.3f, 725f) + curveTo(824.5f, 725f, 813.8f, 725f, 803f, 725f) + curveTo(797.5f, 715.3f, 792.1f, 705.5f, 787.1f, 695.6f) + curveTo(786.6f, 694.6f, 786.1f, 693.6f, 785.6f, 692.6f) + curveTo(784f, 689.4f, 782.4f, 686.3f, 780.9f, 683.1f) + curveTo(780.3f, 682f, 779.8f, 680.9f, 779.2f, 679.8f) + curveTo(769.9f, 661.1f, 761.1f, 642.3f, 752.9f, 623.1f) + curveTo(751.7f, 620.3f, 750.5f, 617.6f, 749.3f, 614.9f) + curveTo(713.8f, 533.2f, 687f, 447.6f, 659.4f, 314.6f) + curveTo(659.3f, 313.9f, 659.2f, 313.2f, 659f, 312.4f) + curveTo(657f, 300.9f, 657f, 300.9f, 657f, 295f) + close() + } + path(fill = SolidColor(Color(0xFF76C72E))) { + moveTo(534f, 295f) + curveTo(560.7f, 295f, 587.5f, 295f, 615f, 295f) + curveTo(615f, 436.9f, 615f, 578.8f, 615f, 725f) + curveTo(588.3f, 725f, 561.5f, 725f, 534f, 725f) + curveTo(534f, 583.1f, 534f, 441.2f, 534f, 295f) + close() + } + }.build() + + return _KivraLogo1920X1080Green!! + } + +@Suppress("ObjectPropertyName") +private var _KivraLogo1920X1080Green: ImageVector? = null + +@Preview +@Composable +private fun IconPreview() { + HedvigTheme { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + imageVector = HedvigIcons.Kivra, + contentDescription = com.hedvig.android.compose.ui.EmptyContentDescription, + modifier = Modifier + .width((24.0).dp) + .height((24.0).dp), + ) + } + } +} diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt index d02ff4491a..3071232b66 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -62,7 +62,7 @@ internal class GetPayinAccountUseCase( MemberPaymentProvider.INVOICE -> { val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() PayinAccount.Invoice( - delivery = invoiceDetails?.delivery?.toDeliveryString(), + delivery = invoiceDetails?.delivery, email = invoiceDetails?.email, isPending = isPending, isDefault = isDefault, @@ -128,14 +128,14 @@ internal sealed interface PayinAccount { ) : PayinAccount data class Invoice( - val delivery: String?, + val delivery: PaymentMethodInvoiceDelivery?, val email: String?, override val isPending: Boolean, override val isDefault: Boolean, ) : PayinAccount } -private fun PaymentMethodInvoiceDelivery?.toDeliveryString(): String? { +fun PaymentMethodInvoiceDelivery?.toDeliveryString(): String? { return when (this) { PaymentMethodInvoiceDelivery.KIVRA -> "Kivra" diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index c4a9772040..cb343a9995 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -1,5 +1,6 @@ package com.hedvig.android.feature.payin.account.ui.overview +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -8,6 +9,8 @@ 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.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -34,15 +37,22 @@ import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.HighlightLabel import com.hedvig.android.design.system.hedvig.HighlightLabelDefaults import com.hedvig.android.design.system.hedvig.HorizontalItemsWithMaximumSpaceTaken +import com.hedvig.android.design.system.hedvig.Icon import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface +import com.hedvig.android.design.system.hedvig.icon.Autogiro +import com.hedvig.android.design.system.hedvig.icon.HedvigIcons +import com.hedvig.android.design.system.hedvig.icon.colored.Kivra +import com.hedvig.android.design.system.hedvig.icon.colored.Swish import com.hedvig.android.feature.payin.account.data.PayinAccount +import com.hedvig.android.feature.payin.account.data.toDeliveryString import hedvig.resources.PAYMENTS_INVOICE import hedvig.resources.REFERRAL_PENDING_STATUS_LABEL import hedvig.resources.Res import hedvig.resources.something_went_wrong import hedvig.resources.swish import octopus.type.MemberPaymentProvider +import octopus.type.PaymentMethodInvoiceDelivery import org.jetbrains.compose.resources.stringResource @Composable @@ -151,12 +161,11 @@ private fun PayoutAccountContent( phoneNumber }, isDefault = method.isDefault, - onClick = { - setAsDefaultPayinMethod(MemberPaymentProvider.SWISH) - }, + onClick = setAsDefaultPayinMethod, modifier = Modifier.padding(horizontal = 16.dp), - loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.SWISH, + loadingDefaultProvider = loadingDefaultProvider, isPending = method.isPending, + provider = MemberPaymentProvider.SWISH, ) } @@ -170,26 +179,24 @@ private fun PayoutAccountContent( accountNumber }, isDefault = method.isDefault, - onClick = { - setAsDefaultPayinMethod(MemberPaymentProvider.TRUSTLY) - }, + onClick = setAsDefaultPayinMethod, modifier = Modifier.padding(horizontal = 16.dp), - loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.TRUSTLY, + loadingDefaultProvider = loadingDefaultProvider, isPending = method.isPending, + provider = MemberPaymentProvider.TRUSTLY, ) } is PayinAccount.Invoice -> { CurrentPayinMethodRow( stringResource(Res.string.PAYMENTS_INVOICE), - method.delivery ?: "", + method.delivery?.toDeliveryString() ?: "", isDefault = method.isDefault, - onClick = { - setAsDefaultPayinMethod(MemberPaymentProvider.INVOICE) - }, + onClick = setAsDefaultPayinMethod, modifier = Modifier.padding(horizontal = 16.dp), - loadingDefaultProvider = loadingDefaultProvider == MemberPaymentProvider.INVOICE, + loadingDefaultProvider = loadingDefaultProvider, isPending = method.isPending, + provider = MemberPaymentProvider.INVOICE, ) } } @@ -243,8 +250,9 @@ private fun CurrentPayinMethodRow( text: String, isDefault: Boolean, isPending: Boolean, - loadingDefaultProvider: Boolean, - onClick: () -> Unit, + loadingDefaultProvider: MemberPaymentProvider?, + onClick: (MemberPaymentProvider) -> Unit, + provider: MemberPaymentProvider, modifier: Modifier = Modifier, ) { HedvigCard( @@ -255,12 +263,38 @@ private fun CurrentPayinMethodRow( .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 12.dp), startSlot = { - Column { - HedvigText(text = label) - HedvigText( - text = text, - color = HedvigTheme.colorScheme.textSecondary, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + when (provider) { + MemberPaymentProvider.TRUSTLY -> Icon( + HedvigIcons.Autogiro, + null, //todo + modifier = Modifier.size(32.dp), + ) + + MemberPaymentProvider.SWISH -> Image( + HedvigIcons.Swish, + null, //todo + modifier = Modifier.size(32.dp), + ) + + MemberPaymentProvider.INVOICE -> Image( + HedvigIcons.Kivra, + null, //todo + modifier = Modifier.size(32.dp), + ) + else -> {} + } + + Spacer(Modifier.width(16.dp)) + Column { + HedvigText(text = label) + HedvigText( + text = text, + color = HedvigTheme.colorScheme.textSecondary, + ) + } } }, endSlot = { @@ -281,8 +315,10 @@ private fun CurrentPayinMethodRow( HedvigButtonGhostWithBorder( size = ButtonDefaults.ButtonSize.Small, text = "Choose as default", // todo - onClick = onClick, - isLoading = loadingDefaultProvider, + onClick = { + onClick(provider) + }, + isLoading = loadingDefaultProvider == provider, ) } } @@ -382,6 +418,12 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr isPending = false, isDefault = false, ), + PayinAccount.Invoice ( + delivery = PaymentMethodInvoiceDelivery.KIVRA, + isPending = false, + isDefault = false, + email = "" + ), ), availablePayinMethods = listOf(MemberPaymentProvider.TRUSTLY), ), From f0da87aff185d1610d685a323377bd46f0c85e08 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 03:17:55 +0200 Subject: [PATCH 17/30] icons in select --- .../SelectPayinMethodDestination.kt | 130 ++++++++++++------ .../setupswish/SetupSwishPayinDestination.kt | 4 +- .../MemberPaymentDetailsViewModel.kt | 4 + 3 files changed, 95 insertions(+), 43 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index c3b6e1de0f..9147695057 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -1,14 +1,18 @@ package com.hedvig.android.feature.payin.account.ui.selectmethod -import androidx.compose.foundation.clickable +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row 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.size +import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.hedvig.android.design.system.hedvig.HedvigCard @@ -16,14 +20,14 @@ import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.Icon import com.hedvig.android.design.system.hedvig.Surface -import hedvig.resources.BANK_PAYOUT_METHOD_CARD_DESCRIPTION -import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE +import com.hedvig.android.design.system.hedvig.icon.Autogiro +import com.hedvig.android.design.system.hedvig.icon.HedvigIcons +import com.hedvig.android.design.system.hedvig.icon.colored.Kivra +import com.hedvig.android.design.system.hedvig.icon.colored.Swish import hedvig.resources.PAYMENTS_INVOICE import hedvig.resources.PAYOUT_METHOD_INVOICE_DESCRIPTION -import hedvig.resources.PAYOUT_METHOD_SWISH_DESCRIPTION -import hedvig.resources.PAYOUT_METHOD_TRUSTLY_DESCRIPTION -import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD import hedvig.resources.Res import hedvig.resources.swish import hedvig.resources.trustly @@ -46,33 +50,12 @@ internal fun SelectPayinMethodDestination( Spacer(Modifier.height(8.dp)) Column(Modifier.padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { for (provider in availableProviders) { - when (provider) { - MemberPaymentProvider.TRUSTLY -> { - PayinMethodRow( - title = stringResource(Res.string.trustly), - subtitle = "Connect your bank via Trustly", // todo - onClick = onTrustlySelected, - ) - } - - MemberPaymentProvider.SWISH -> { - PayinMethodRow( - title = stringResource(Res.string.swish), - subtitle = "Monthly auto-payments through Swish", // todo - onClick = onSwishSelected, - ) - } - - MemberPaymentProvider.INVOICE -> { - PayinMethodRow( - title = stringResource(Res.string.PAYMENTS_INVOICE), - subtitle = stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION), // todo - onClick = onInvoiceSelected, - ) - } - - else -> {} - } + PayinMethodRow( + provider = provider, + onTrustlySelected = onTrustlySelected, + onSwishSelected = onSwishSelected, + onInvoiceSelected = onInvoiceSelected, + ) } } Spacer(Modifier.height(16.dp)) @@ -80,17 +63,81 @@ internal fun SelectPayinMethodDestination( } @Composable -private fun PayinMethodRow(title: String, subtitle: String, onClick: () -> Unit, modifier: Modifier = Modifier) { +private fun PayinMethodRow( + provider: MemberPaymentProvider, + onTrustlySelected: () -> Unit, + onSwishSelected: () -> Unit, + onInvoiceSelected: () -> Unit, + modifier: Modifier = Modifier, +) { HedvigCard( - onClick = onClick, + onClick = { + when (provider) { + MemberPaymentProvider.TRUSTLY -> { + onTrustlySelected() + } + + MemberPaymentProvider.SWISH -> { + onSwishSelected() + } + + MemberPaymentProvider.INVOICE -> { + onInvoiceSelected() + } + + else -> {} + } + }, modifier = modifier.fillMaxWidth(), ) { - Column(Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) { - HedvigText(text = title) - HedvigText( - text = subtitle, - color = HedvigTheme.colorScheme.textSecondary, - ) + Row( + modifier = Modifier.padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + when (provider) { + MemberPaymentProvider.TRUSTLY -> Icon( + HedvigIcons.Autogiro, + null, //todo + modifier = Modifier.size(32.dp), + ) + + MemberPaymentProvider.SWISH -> Image( + HedvigIcons.Swish, + null, //todo + modifier = Modifier.size(32.dp), + ) + + MemberPaymentProvider.INVOICE -> Image( + HedvigIcons.Kivra, + null, //todo + modifier = Modifier.size(32.dp), + ) + + else -> {} + } + + Spacer(Modifier.width(16.dp)) + Column(Modifier.padding(vertical = 12.dp)) { + HedvigText( + text = when (provider) { + MemberPaymentProvider.TRUSTLY -> "Autogiro" // todo + MemberPaymentProvider.SWISH -> stringResource(Res.string.swish) + MemberPaymentProvider.INVOICE -> stringResource(Res.string.PAYMENTS_INVOICE) + else -> "" + }, + ) + HedvigText( + text = when (provider) { + MemberPaymentProvider.TRUSTLY -> "Connect your bank via Trustly" // todo + MemberPaymentProvider.SWISH -> "Monthly auto-payments via Swish" // todo + MemberPaymentProvider.INVOICE -> stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION) + else -> "" + }, + color = HedvigTheme.colorScheme.textSecondary, + ) + } + + } } } @@ -104,6 +151,7 @@ private fun PreviewSelectPayinMethodScreen() { availableProviders = listOf( MemberPaymentProvider.SWISH, MemberPaymentProvider.INVOICE, + MemberPaymentProvider.TRUSTLY, ), onTrustlySelected = {}, onSwishSelected = {}, diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index 26ef20cec2..43b080b172 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -126,8 +126,8 @@ private fun SetupSwishPayoutScreen( horizontalAlignment = Alignment.CenterHorizontally, ) { EmptyState( - text = "The process started", - description = "Please give your approval in the Swish app", + text = "Please give your approval in the Swish app", + description = null, modifier = Modifier.fillMaxWidth(), iconStyle = EmptyStateDefaults.EmptyStateIconStyle.SWISH, ) 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/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt index 9bebee139c..9be2df5caa 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsViewModel.kt @@ -12,6 +12,9 @@ import com.hedvig.android.feature.payments.data.MemberPaymentsDetails import com.hedvig.android.molecule.public.MoleculePresenter import com.hedvig.android.molecule.public.MoleculePresenterScope import com.hedvig.android.molecule.public.MoleculeViewModel +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.WhileSubscribed internal class MemberPaymentDetailsViewModel( getMemberPaymentsDetailsUseCase: GetMemberPaymentsDetailsUseCase, @@ -20,6 +23,7 @@ internal class MemberPaymentDetailsViewModel( presenter = MemberPaymentDetailsPresenter( getMemberPaymentsDetailsUseCase, ), + sharingStarted = SharingStarted.WhileSubscribed(2.seconds), ) private class MemberPaymentDetailsPresenter( From 6762015c425d6ba2372cff4c7acb672ccf4ca6af Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 03:19:12 +0200 Subject: [PATCH 18/30] copy --- .../account/ui/selectmethod/SelectPayinMethodDestination.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index 9147695057..db37cb4964 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -43,7 +43,7 @@ internal fun SelectPayinMethodDestination( navigateUp: () -> Unit, ) { HedvigScaffold( - topAppBarText = "Select billing method to add", // todo + topAppBarText = "Add or change billing method", // todo navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { From 9a1421ad362b7541e6ca435d91012bc93cf848fa Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 03:28:18 +0200 Subject: [PATCH 19/30] bank name --- .../ui/overview/PayinAccountOverviewDestination.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index cb343a9995..fe0f7badf1 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -46,6 +46,7 @@ import com.hedvig.android.design.system.hedvig.icon.colored.Kivra import com.hedvig.android.design.system.hedvig.icon.colored.Swish import com.hedvig.android.feature.payin.account.data.PayinAccount import com.hedvig.android.feature.payin.account.data.toDeliveryString +import com.hedvig.android.logger.logcat import hedvig.resources.PAYMENTS_INVOICE import hedvig.resources.REFERRAL_PENDING_STATUS_LABEL import hedvig.resources.Res @@ -314,7 +315,7 @@ private fun CurrentPayinMethodRow( if (!isPending) { HedvigButtonGhostWithBorder( size = ButtonDefaults.ButtonSize.Small, - text = "Choose as default", // todo + text = "Set as default", // todo onClick = { onClick(provider) }, @@ -330,8 +331,14 @@ private fun CurrentPayinMethodRow( } private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: String?, bankName: String?): String { + logcat { "Mariia: clearingNumber: $clearingNumber accountNumber: $accountNumber bankName: $bankName" } +// return when { +// clearingNumber != null && accountNumber != null && bankName != null -> "$bankName $clearingNumber-$accountNumber" +// clearingNumber != null && bankName != null -> "$bankName $clearingNumber" +// else -> clearingNumber.orEmpty() +// } return when { - clearingNumber != null && accountNumber != null && bankName != null -> "$bankName $clearingNumber-$accountNumber" + bankName != null -> bankName else -> clearingNumber.orEmpty() } } From ff702a1c1eb7fcff71ae72c3e818ba7595e51ca5 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 03:43:05 +0200 Subject: [PATCH 20/30] format phone --- .../PayinAccountOverviewDestination.kt | 18 ++++++++++++++++-- .../setupswish/SetupSwishPayinDestination.kt | 8 ++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index fe0f7badf1..2ec1efcce7 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -159,7 +159,7 @@ private fun PayoutAccountContent( text = if (method.isPending && phoneNumber.isBlank()) { stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) } else { - phoneNumber + formatSwishPhoneNumber(phoneNumber) }, isDefault = method.isDefault, onClick = setAsDefaultPayinMethod, @@ -292,7 +292,7 @@ private fun CurrentPayinMethodRow( Column { HedvigText(text = label) HedvigText( - text = text, + text = text , color = HedvigTheme.colorScheme.textSecondary, ) } @@ -330,6 +330,20 @@ private fun CurrentPayinMethodRow( } } +internal fun formatSwishPhoneNumber(phoneNumber: String): String { + val digits = phoneNumber.take(15) + val sb = StringBuilder() + for (i in digits.indices) { + sb.append(digits[i]) + if (i in setOf(2, 5, 7) + ) { + sb.append("-") + } + } + return sb.toString() +} + + private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: String?, bankName: String?): String { logcat { "Mariia: clearingNumber: $clearingNumber accountNumber: $accountNumber bankName: $bankName" } // return when { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index 43b080b172..075f82ea3c 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -51,6 +51,7 @@ import com.hedvig.android.design.system.hedvig.HedvigTextFieldDefaults import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority import com.hedvig.android.design.system.hedvig.Surface +import com.hedvig.android.feature.payin.account.ui.overview.formatSwishPhoneNumber import hedvig.resources.CONTACT_INFO_CHANGES_SAVED import hedvig.resources.ODYSSEY_PHONE_NUMBER_LABEL import hedvig.resources.Res @@ -212,12 +213,7 @@ private class ChipIdVisualTransformation( val trimmed = if (text.text.length >= 15) text.text.substring(0..14) else text.text val annotatedString = buildAnnotatedString { - for (i in trimmed.indices) { - append(trimmed[i]) - if (i in listOf(2, 5, 7)) { - append("-") - } - } + append(formatSwishPhoneNumber(trimmed)) withStyle(SpanStyle(color = maskColor)) { append(mask.takeLast((mask.length - length).coerceAtLeast(0))) } From 0042aa5402c1d9418a70991aad495219b40f88bc Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 09:51:26 +0200 Subject: [PATCH 21/30] copy --- .../account/ui/overview/PayinAccountOverviewDestination.kt | 2 +- .../account/ui/selectmethod/SelectPayinMethodDestination.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index 2ec1efcce7..d82927fbe8 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -173,7 +173,7 @@ private fun PayoutAccountContent( is PayinAccount.Trustly -> { val accountNumber = formatBankAccountNumber(method.clearingNumber, method.accountNumber, method.bankName) CurrentPayinMethodRow( - label = "Autogiro", // todo + label = "Direct debit", // todo text = if (method.isPending && accountNumber.isBlank()) { stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) } else { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index db37cb4964..500afcb37d 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -120,7 +120,7 @@ private fun PayinMethodRow( Column(Modifier.padding(vertical = 12.dp)) { HedvigText( text = when (provider) { - MemberPaymentProvider.TRUSTLY -> "Autogiro" // todo + MemberPaymentProvider.TRUSTLY -> "Direct debit" // todo MemberPaymentProvider.SWISH -> stringResource(Res.string.swish) MemberPaymentProvider.INVOICE -> stringResource(Res.string.PAYMENTS_INVOICE) else -> "" From 46c46335d3a25d0bfb5f2ead05887c0c077a4b6e Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 10:37:22 +0200 Subject: [PATCH 22/30] icons in select payout method --- .../design/system/hedvig/icon/BankAccount.kt | 120 ++++++++++++++++++ .../SelectPayoutMethodDestination.kt | 86 ++++++++++++- 2 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt diff --git a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt new file mode 100644 index 0000000000..44b6610074 --- /dev/null +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt @@ -0,0 +1,120 @@ +package com.hedvig.android.design.system.hedvig.icon + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.Icon +import com.hedvig.android.design.system.hedvig.icon.colored.Kivra + +val HedvigIcons.BankAccount: ImageVector + get() { + if (_Icons8BankAccount48 != null) { + return _Icons8BankAccount48!! + } + _Icons8BankAccount48 = ImageVector.Builder( + name = "Icons8BankAccount48", + defaultWidth = 48.dp, + defaultHeight = 48.dp, + viewportWidth = 48f, + viewportHeight = 48f + ).apply { + path(fill = SolidColor(Color.Black)) { + moveTo(30.867f, 6.531f) + curveTo(32.209f, 7.176f, 33.545f, 7.832f, 34.875f, 8.5f) + curveTo(35.563f, 8.834f, 36.252f, 9.168f, 36.961f, 9.512f) + curveTo(38.646f, 10.331f, 40.324f, 11.164f, 42f, 12f) + curveTo(42f, 13.98f, 42f, 15.96f, 42f, 18f) + curveTo(40.68f, 18f, 39.36f, 18f, 38f, 18f) + curveTo(38f, 20.31f, 38f, 22.62f, 38f, 25f) + curveTo(36.02f, 25.99f, 36.02f, 25.99f, 34f, 27f) + curveTo(34f, 24.03f, 34f, 21.06f, 34f, 18f) + curveTo(32.68f, 18f, 31.36f, 18f, 30f, 18f) + curveTo(30f, 23.28f, 30f, 28.56f, 30f, 34f) + curveTo(28.68f, 34f, 27.36f, 34f, 26f, 34f) + curveTo(26f, 28.72f, 26f, 23.44f, 26f, 18f) + curveTo(24.68f, 18f, 23.36f, 18f, 22f, 18f) + curveTo(22f, 23.28f, 22f, 28.56f, 22f, 34f) + curveTo(20.68f, 34f, 19.36f, 34f, 18f, 34f) + curveTo(18f, 28.72f, 18f, 23.44f, 18f, 18f) + curveTo(16.68f, 18f, 15.36f, 18f, 14f, 18f) + curveTo(14f, 23.28f, 14f, 28.56f, 14f, 34f) + curveTo(12.68f, 34f, 11.36f, 34f, 10f, 34f) + curveTo(10f, 28.72f, 10f, 23.44f, 10f, 18f) + curveTo(8.68f, 18f, 7.36f, 18f, 6f, 18f) + curveTo(6f, 16.02f, 6f, 14.04f, 6f, 12f) + curveTo(8.369f, 10.819f, 10.744f, 9.655f, 13.125f, 8.5f) + curveTo(13.79f, 8.166f, 14.455f, 7.832f, 15.141f, 7.488f) + curveTo(20.813f, 4.763f, 24.925f, 3.968f, 30.867f, 6.531f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveTo(40f, 39.75f) + curveTo(40.866f, 39.745f, 41.732f, 39.74f, 42.625f, 39.734f) + curveTo(45f, 40f, 45f, 40f, 48f, 42f) + curveTo(48f, 43.98f, 48f, 45.96f, 48f, 48f) + curveTo(42.72f, 48f, 37.44f, 48f, 32f, 48f) + curveTo(32f, 46.02f, 32f, 44.04f, 32f, 42f) + curveTo(35.239f, 39.841f, 36.249f, 39.728f, 40f, 39.75f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveTo(6f, 38f) + curveTo(14.91f, 38f, 23.82f, 38f, 33f, 38f) + curveTo(30f, 42f, 30f, 42f, 27.456f, 42.454f) + curveTo(26.467f, 42.433f, 25.477f, 42.412f, 24.457f, 42.391f) + curveTo(23.384f, 42.378f, 22.311f, 42.365f, 21.205f, 42.352f) + curveTo(20.086f, 42.318f, 18.966f, 42.285f, 17.813f, 42.25f) + curveTo(16.681f, 42.232f, 15.55f, 42.214f, 14.385f, 42.195f) + curveTo(11.589f, 42.148f, 8.795f, 42.082f, 6f, 42f) + curveTo(6f, 40.68f, 6f, 39.36f, 6f, 38f) + close() + } + path(fill = SolidColor(Color.Black)) { + moveTo(40f, 27.875f) + curveTo(43f, 28f, 43f, 28f, 44f, 29f) + curveTo(44.125f, 32f, 44.125f, 32f, 44f, 35f) + curveTo(43f, 36f, 43f, 36f, 40f, 36.125f) + curveTo(37f, 36f, 37f, 36f, 36f, 35f) + curveTo(35.875f, 32f, 35.875f, 32f, 36f, 29f) + curveTo(37f, 28f, 37f, 28f, 40f, 27.875f) + close() + } + }.build() + + return _Icons8BankAccount48!! + } + +@Suppress("ObjectPropertyName") +private var _Icons8BankAccount48: ImageVector? = null + + +@Preview +@Composable +private fun IconPreview() { + HedvigTheme { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + imageVector = HedvigIcons.BankAccount, + contentDescription = com.hedvig.android.compose.ui.EmptyContentDescription, + modifier = Modifier + .width((24.0).dp) + .height((24.0).dp), + ) + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt index aa94ff4d35..bcdefff825 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -1,20 +1,35 @@ package com.hedvig.android.feature.payoutaccount.ui.selectmethod -import androidx.compose.foundation.clickable +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row 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.size +import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment 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.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme +import com.hedvig.android.design.system.hedvig.Icon +import com.hedvig.android.design.system.hedvig.Surface +import com.hedvig.android.design.system.hedvig.icon.Autogiro +import com.hedvig.android.design.system.hedvig.icon.BankAccount +import com.hedvig.android.design.system.hedvig.icon.Card +import com.hedvig.android.design.system.hedvig.icon.HedvigIcons +import com.hedvig.android.design.system.hedvig.icon.Link +import com.hedvig.android.design.system.hedvig.icon.colored.Kivra +import com.hedvig.android.design.system.hedvig.icon.colored.Swish +import com.hedvig.android.design.system.hedvig.icon.flag.FlagSweden import hedvig.resources.BANK_PAYOUT_METHOD_CARD_DESCRIPTION import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE import hedvig.resources.PAYMENTS_INVOICE @@ -50,6 +65,7 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.trustly), subtitle = stringResource(Res.string.PAYOUT_METHOD_TRUSTLY_DESCRIPTION), onClick = onTrustlySelected, + provider = provider ) } @@ -58,6 +74,7 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_TITLE), subtitle = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_DESCRIPTION), onClick = onNordeaSelected, + provider = provider ) } @@ -66,6 +83,7 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.swish), subtitle = stringResource(Res.string.PAYOUT_METHOD_SWISH_DESCRIPTION), onClick = onSwishSelected, + provider = provider ) } @@ -78,16 +96,70 @@ internal fun SelectPayoutMethodDestination( } @Composable -private fun PayoutMethodRow(title: String, subtitle: String, onClick: () -> Unit, modifier: Modifier = Modifier) { +private fun PayoutMethodRow( + provider: MemberPaymentProvider, + 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 = HedvigTheme.colorScheme.textSecondary, + Row( + modifier = Modifier.padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + when (provider) { + MemberPaymentProvider.TRUSTLY -> Icon( + HedvigIcons.Link, + null, //todo + modifier = Modifier.size(32.dp), + ) + + MemberPaymentProvider.SWISH -> Image( + HedvigIcons.Swish, + null, //todo + modifier = Modifier.size(32.dp), + ) + + MemberPaymentProvider.NORDEA -> Icon( + HedvigIcons.BankAccount, + null, //todo + modifier = Modifier.size(32.dp), + ) + + else -> {} + } + + Spacer(Modifier.width(16.dp)) + Column(Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) { + HedvigText(text = title) + HedvigText( + text = subtitle, + color = HedvigTheme.colorScheme.textSecondary, + ) + } + } + } +} + +@Composable +@HedvigPreview +private fun PreviewSelectPayoutMethodScreen() { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + SelectPayoutMethodDestination( + availableProviders = listOf( + MemberPaymentProvider.SWISH, + MemberPaymentProvider.TRUSTLY, + MemberPaymentProvider.NORDEA, + ), + onTrustlySelected = {}, + onNordeaSelected = {}, + onSwishSelected = {}, + navigateUp = {}, ) } } From f2c4c7024d2e6aa11dc86f295d005d3026cb9561 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 10:37:55 +0200 Subject: [PATCH 23/30] ktlint --- .../android/design/system/hedvig/icon/BankAccount.kt | 4 +--- .../ui/selectmethod/SelectPayinMethodDestination.kt | 3 +-- .../ui/selectmethod/SelectPayoutMethodDestination.kt | 12 +++--------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt index 44b6610074..72e884b890 100644 --- a/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt @@ -1,6 +1,5 @@ package com.hedvig.android.design.system.hedvig.icon -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.height @@ -16,7 +15,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.Icon -import com.hedvig.android.design.system.hedvig.icon.colored.Kivra val HedvigIcons.BankAccount: ImageVector get() { @@ -28,7 +26,7 @@ val HedvigIcons.BankAccount: ImageVector defaultWidth = 48.dp, defaultHeight = 48.dp, viewportWidth = 48f, - viewportHeight = 48f + viewportHeight = 48f, ).apply { path(fill = SolidColor(Color.Black)) { moveTo(30.867f, 6.531f) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index 500afcb37d..ceebbdf186 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -30,7 +30,6 @@ import hedvig.resources.PAYMENTS_INVOICE import hedvig.resources.PAYOUT_METHOD_INVOICE_DESCRIPTION import hedvig.resources.Res import hedvig.resources.swish -import hedvig.resources.trustly import octopus.type.MemberPaymentProvider import org.jetbrains.compose.resources.stringResource @@ -86,7 +85,7 @@ private fun PayinMethodRow( } else -> {} - } + } }, modifier = modifier.fillMaxWidth(), ) { diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt index bcdefff825..52a7b0f298 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -22,18 +22,12 @@ import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.Icon import com.hedvig.android.design.system.hedvig.Surface -import com.hedvig.android.design.system.hedvig.icon.Autogiro import com.hedvig.android.design.system.hedvig.icon.BankAccount -import com.hedvig.android.design.system.hedvig.icon.Card import com.hedvig.android.design.system.hedvig.icon.HedvigIcons import com.hedvig.android.design.system.hedvig.icon.Link -import com.hedvig.android.design.system.hedvig.icon.colored.Kivra import com.hedvig.android.design.system.hedvig.icon.colored.Swish -import com.hedvig.android.design.system.hedvig.icon.flag.FlagSweden import hedvig.resources.BANK_PAYOUT_METHOD_CARD_DESCRIPTION import hedvig.resources.BANK_PAYOUT_METHOD_CARD_TITLE -import hedvig.resources.PAYMENTS_INVOICE -import hedvig.resources.PAYOUT_METHOD_INVOICE_DESCRIPTION import hedvig.resources.PAYOUT_METHOD_SWISH_DESCRIPTION import hedvig.resources.PAYOUT_METHOD_TRUSTLY_DESCRIPTION import hedvig.resources.PAYOUT_SELECT_PAYOUT_METHOD @@ -65,7 +59,7 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.trustly), subtitle = stringResource(Res.string.PAYOUT_METHOD_TRUSTLY_DESCRIPTION), onClick = onTrustlySelected, - provider = provider + provider = provider, ) } @@ -74,7 +68,7 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_TITLE), subtitle = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_DESCRIPTION), onClick = onNordeaSelected, - provider = provider + provider = provider, ) } @@ -83,7 +77,7 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.swish), subtitle = stringResource(Res.string.PAYOUT_METHOD_SWISH_DESCRIPTION), onClick = onSwishSelected, - provider = provider + provider = provider, ) } From a2463a0525a8c001f08381c0b62cfc7b80f1cc75 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 11:26:44 +0200 Subject: [PATCH 24/30] border and color for qr --- .../setupswish/SetupSwishPayinDestination.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index 075f82ea3c..e4e2bcba42 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -136,11 +136,19 @@ private fun SetupSwishPayoutScreen( Column( horizontalAlignment = Alignment.CenterHorizontally, ) { - QRCode( - token = uiState.successUrl, - modifier = Modifier - .size(180.dp), - ) + Surface( + color = Color.White, + shape = HedvigTheme.shapes.cornerMedium, + border = HedvigTheme.colorScheme.borderPrimary, + modifier = Modifier.padding(16.dp), + ) { + QRCode( + token = uiState.successUrl, + modifier = Modifier + .size(180.dp) + .padding(16.dp), + ) + } } Spacer(Modifier.height(32.dp)) HedvigButton( From 8f98db763ff0895dab3b4146ec740b71de842a4b Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 11:28:37 +0200 Subject: [PATCH 25/30] border and color for qr --- .../payin/account/ui/setupswish/SetupSwishPayinDestination.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index e4e2bcba42..2556658d3c 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -150,7 +150,7 @@ private fun SetupSwishPayoutScreen( ) } } - Spacer(Modifier.height(32.dp)) + Spacer(Modifier.height(16.dp)) HedvigButton( "Open Swish", enabled = true, From d1c9929cacc40f1b536104de4eb55068d07b4aff Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 13:38:20 +0200 Subject: [PATCH 26/30] copy --- .../ui/selectmethod/SelectPayoutMethodDestination.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt index 52a7b0f298..8515f65068 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt +++ b/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/selectmethod/SelectPayoutMethodDestination.kt @@ -23,6 +23,7 @@ import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.Icon import com.hedvig.android.design.system.hedvig.Surface import com.hedvig.android.design.system.hedvig.icon.BankAccount +import com.hedvig.android.design.system.hedvig.icon.Card import com.hedvig.android.design.system.hedvig.icon.HedvigIcons import com.hedvig.android.design.system.hedvig.icon.Link import com.hedvig.android.design.system.hedvig.icon.colored.Swish @@ -66,7 +67,7 @@ internal fun SelectPayoutMethodDestination( MemberPaymentProvider.NORDEA -> { PayoutMethodRow( title = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_TITLE), - subtitle = stringResource(Res.string.BANK_PAYOUT_METHOD_CARD_DESCRIPTION), + subtitle = "Payout to a bank account", //todo: removed BANK_PAYOUT_METHOD_CARD_DESCRIPTION for demo onClick = onNordeaSelected, provider = provider, ) @@ -119,7 +120,7 @@ private fun PayoutMethodRow( ) MemberPaymentProvider.NORDEA -> Icon( - HedvigIcons.BankAccount, + HedvigIcons.Card, null, //todo modifier = Modifier.size(32.dp), ) From 335c488dc9e58f26240bf8c30430eda38b7fbd31 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 14:08:20 +0200 Subject: [PATCH 27/30] safeExecuteAllowingPartialResponses in SetAsDefaultUseCase.kt --- .../payin/account/data/SetAsDefaultUseCase.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt index f21b715793..64d2b8182f 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt @@ -7,6 +7,7 @@ import arrow.core.raise.context.raise import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.ErrorMessage import com.hedvig.android.apollo.safeExecute +import com.hedvig.android.apollo.safeExecuteAllowingPartialResponses import com.hedvig.android.core.common.ErrorMessage import com.hedvig.android.logger.logcat import octopus.SetAsDefaultPayinMutation @@ -22,25 +23,26 @@ internal class SetAsDefaultUseCaseImpl( ) : SetAsDefaultUseCase { override suspend fun invoke(provider: MemberPaymentProvider): Either { return either { - logcat { "Mariia: Starting SetAsDefaultUseCaseImpl with provider: $provider" } apolloClient .mutation(SetAsDefaultPayinMutation(provider)) - .safeExecute(::ErrorMessage) // tosdoL user safeExecuteAllowingPartialResponses + .safeExecuteAllowingPartialResponses() .fold( - ifLeft = { - logcat { "Mariia: SetAsDefaultUseCaseImpl error: $it" } - logcat { "SetAsDefaultUseCaseImpl error: $it" } + fa = { error -> + logcat { "SetAsDefaultUseCaseImpl error: $error" } raise(ErrorMessage()) }, - ifRight = { result -> + fb = { result -> val userError = result.paymentMethodSetDefaultPayin?.message if (userError != null) { - logcat { "Mariia: SetAsDefaultUseCaseImpl userError not null: $userError" } + logcat { "SetAsDefaultUseCaseImpl userError not null: $userError" } raise(ErrorMessage(userError)) } - logcat { "Mariia: SetAsDefaultUseCaseImpl launching getPayinAccountUseCase" } getPayinAccountUseCase.invoke().bind() }, + fab = {errors, _ -> + logcat { "SetAsDefaultUseCaseImpl data with errors: $errors" } + raise(ErrorMessage()) + } ) } } From cf0c0e0d8ce28d602c789b9b32ab31c71792b2bf Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 14:31:59 +0200 Subject: [PATCH 28/30] copy and phone number digit filtering --- .../androidMain/res/values-sv-rSE/strings.xml | 16 ++++++++++++++-- .../src/androidMain/res/values/strings.xml | 16 ++++++++++++++-- .../composeResources/values-sv-rSE/strings.xml | 16 ++++++++++++++-- .../composeResources/values/strings.xml | 16 ++++++++++++++-- .../overview/PayinAccountOverviewDestination.kt | 16 ++++++++-------- .../selectmethod/SelectPayinMethodDestination.kt | 2 +- .../ui/setupswish/SetupSwishPayinDestination.kt | 7 ++++--- 7 files changed, 69 insertions(+), 20 deletions(-) diff --git a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml index 5c6838f5e0..512bd2005e 100644 --- a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml @@ -522,7 +522,7 @@ Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut - Manage your billing methods + Manage your payment methods Aktivera ditt försäkringsskydd igen genom att kontakta oss när din betalning har registrerats Få ett prisförslag Se över kontaktuppgifter @@ -1280,6 +1280,19 @@ Läs om ditt fullständiga försäkringsskydd nedan. Appinformation Information + Fortsätt köpet + + Erbjudandet löper ut om %1$d dag + Erbjudandet löper ut om %1$d dagar + + + Erbjudandet löper ut om %1$d timme + Erbjudandet löper ut om %1$d timmar + + + Erbjudandet löper ut om %1$d minut + Erbjudandet löper ut om %1$d minuter + Skriv meddelande Autogiro är anslutet! Vi kunde inte ansluta ditt bankkonto. Vänligen försök igen eller skriv till oss direkt i appen. @@ -1367,7 +1380,6 @@ Du är inbjuden Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code - Återuppta handlingen Koppla ditt EuroBonus Lägg till ditt nummer Du tjänar 100 poäng per månad för varje aktiv försäkring du har hos Hedvig diff --git a/app/core/core-resources/src/androidMain/res/values/strings.xml b/app/core/core-resources/src/androidMain/res/values/strings.xml index 4beb026ee8..792da211c2 100644 --- a/app/core/core-resources/src/androidMain/res/values/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values/strings.xml @@ -522,7 +522,7 @@ Question regarding claim, Vehicle reg. %1$s Preferences Logout - Manage your billing methods + Manage your payment methods Activate your coverage again by contacting us once your payment has been processed Get a price quote Review contact info @@ -1280,6 +1280,19 @@ Read the full coverage of your insurances below. App information Information + Continue purchase + + The offer expires in %1$d day + The offer expires in %1$d days + + + The offer expires in %1$d hour + The offer expires in %1$d hours + + + The offer expires in %1$d minute + The offer expires in %1$d minutes + Write message Direct debit connected! We were unable to add your payment method. Please try again or send us a message here in the app. @@ -1367,7 +1380,6 @@ You’re invited Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code - Resume shopping Connect your EuroBonus Connect your number You earn 100 points per month for each active insurance you have with Hedvig diff --git a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml index df6c4c7f86..dcde5a4f26 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -522,7 +522,7 @@ Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut - Manage your billing methods + Manage your payment methods Aktivera ditt försäkringsskydd igen genom att kontakta oss när din betalning har registrerats Få ett prisförslag Se över kontaktuppgifter @@ -1280,6 +1280,19 @@ Läs om ditt fullständiga försäkringsskydd nedan. Appinformation Information + Fortsätt köpet + + Erbjudandet löper ut om %1$d dag + Erbjudandet löper ut om %1$d dagar + + + Erbjudandet löper ut om %1$d timme + Erbjudandet löper ut om %1$d timmar + + + Erbjudandet löper ut om %1$d minut + Erbjudandet löper ut om %1$d minuter + Skriv meddelande Autogiro är anslutet! Vi kunde inte ansluta ditt bankkonto. Vänligen försök igen eller skriv till oss direkt i appen. @@ -1367,7 +1380,6 @@ Du är inbjuden Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code - Återuppta handlingen Koppla ditt EuroBonus Lägg till ditt nummer Du tjänar 100 poäng per månad för varje aktiv försäkring du har hos Hedvig diff --git a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml index 7206a8403b..10578b83ed 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml @@ -522,7 +522,7 @@ Question regarding claim, Vehicle reg. %1$s Preferences Logout - Manage your billing methods + Manage your payment methods Activate your coverage again by contacting us once your payment has been processed Get a price quote Review contact info @@ -1280,6 +1280,19 @@ Read the full coverage of your insurances below. App information Information + Continue purchase + + The offer expires in %1$d day + The offer expires in %1$d days + + + The offer expires in %1$d hour + The offer expires in %1$d hours + + + The offer expires in %1$d minute + The offer expires in %1$d minutes + Write message Direct debit connected! We were unable to add your payment method. Please try again or send us a message here in the app. @@ -1367,7 +1380,6 @@ You’re invited Hedvig Forever You’ve been officially invited to Hedvig Forever and can now invite your friends with your unique code - Resume shopping Connect your EuroBonus Connect your number You earn 100 points per month for each active insurance you have with Hedvig diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index d82927fbe8..ee262575bb 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -83,7 +83,7 @@ private fun PayinAccountOverviewScreen( setAsDefaultPayinMethod: (MemberPaymentProvider) -> Unit, ) { HedvigScaffold( - topAppBarText = "Billing account", // todo! + topAppBarText = "Payment account", // todo! navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { @@ -143,7 +143,7 @@ private fun PayoutAccountContent( } } else { HedvigText( - "Active billing methods", // todo + "Connected payment methods", // todo modifier = Modifier.padding(horizontal = 16.dp), ) Spacer(Modifier.height(16.dp)) @@ -232,7 +232,7 @@ private fun PayoutAccountContent( } if (availablePayinMethods.isNotEmpty()) { HedvigButton( - text = "Add a billing method", // todo! + text = "Add a payment method", // todo! onClick = onConnectPayinMethodClicked, enabled = true, modifier = Modifier @@ -386,7 +386,7 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr PayinAccountOverviewUiState.Content( currentMethods = listOf( PayinAccount.SwishPayin( - phoneNumber = "070-123 45 67", + phoneNumber = "0701234567", isPending = false, isDefault = true, ), @@ -396,7 +396,7 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr PayinAccountOverviewUiState.Content( currentMethods = listOf( PayinAccount.SwishPayin( - phoneNumber = "070-123 45 67", + phoneNumber = "0701234567", isPending = false, isDefault = true, ), @@ -417,7 +417,7 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr isDefault = true, ), PayinAccount.SwishPayin( - phoneNumber = "070-123 45 67", + phoneNumber = "0701234567", isPending = false, isDefault = false, ), @@ -435,7 +435,7 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr isDefault = true, ), PayinAccount.SwishPayin( - phoneNumber = "070-123 45 67", + phoneNumber = "0701234567", isPending = false, isDefault = false, ), @@ -458,7 +458,7 @@ private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterPr isDefault = true, ), PayinAccount.SwishPayin( - phoneNumber = "070-123 45 67", + phoneNumber = "0701234567", isPending = false, isDefault = false, ), diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index ceebbdf186..8976ea1e99 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -42,7 +42,7 @@ internal fun SelectPayinMethodDestination( navigateUp: () -> Unit, ) { HedvigScaffold( - topAppBarText = "Add or change billing method", // todo + topAppBarText = "Add or change payment method", // todo navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt index 2556658d3c..1fa8c9484e 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -184,9 +184,10 @@ private fun SetupSwishPayoutScreen( errorState = HedvigTextFieldDefaults.ErrorState.NoError, interactionSource = interactionSource, onValueChange = { - if (it.length <= 15) { - updateText(it) - input = it + val digitsOnly = it.filter { char -> char.isDigit() } + if (digitsOnly.length <= 15) { + updateText(digitsOnly) + input = digitsOnly } }, ) From c46311139cdf0cac92075869863821de2a12f882 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 14:40:17 +0200 Subject: [PATCH 29/30] remove logs --- .../kotlin/com/hedvig/android/apollo/ApolloCallExt.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt b/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt index b300006bfc..8b3205bba4 100644 --- a/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt +++ b/app/apollo/apollo-core/src/commonMain/kotlin/com/hedvig/android/apollo/ApolloCallExt.kt @@ -128,7 +128,6 @@ private fun IorRaise>.parseRespon } raise(OperationException(exception)) } - logcat{"Mariia: apollo parseResponse data: $data errors: $errors"} return iorFromErrorsAndData(errors.mapToOperationErrors(), data).bind() } @@ -138,7 +137,6 @@ private fun List?.mapToOperationErrors(): Nel? { if (error.extensionErrorType() == ExtensionErrorType.Unauthenticated) { OperationError.Unathenticated } else { - logcat{"Mariia: apollo mapToOperationErrors error: $error"} OperationError.Other( buildString { append(error.message) @@ -179,15 +177,12 @@ private fun iorFromErrorsAndData( ): Ior, D> { return when { errors != null && data != null -> { - logcat{"Mariia: apollo iorFromErrorsAndData Ior.Both. data: $data errors: $errors"} Ior.Both(errors, data) } errors != null -> { - logcat{"Mariia: apollo iorFromErrorsAndData Ior.Left"} Ior.Left(errors) } data != null -> { - logcat{"Mariia: apollo iorFromErrorsAndData Ior.Right"} Ior.Right(data) } else -> error("Non compliant server") From 1d304efb4c224587096c2ead510079b581ac2771 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Fri, 22 May 2026 14:57:23 +0200 Subject: [PATCH 30/30] add multiscreen previews --- .../account/ui/overview/PayinAccountOverviewDestination.kt | 6 +++++- .../account/ui/selectmethod/SelectPayinMethodDestination.kt | 3 ++- .../account/ui/setupinvoice/SetupInvoicePayinDestination.kt | 3 ++- .../memberpaymentdetails/MemberPaymentDetailsDestination.kt | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt index ee262575bb..36c52fc533 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -32,6 +32,7 @@ import com.hedvig.android.design.system.hedvig.HedvigFullScreenCenterAlignedProg import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigShortMultiScreenPreview import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.HighlightLabel @@ -100,6 +101,8 @@ private fun PayinAccountOverviewScreen( HedvigErrorSection( onButtonClick = onRetry, modifier = Modifier + .fillMaxWidth() + .padding(16.dp) .weight(1f) .wrapContentHeight(), ) @@ -139,6 +142,7 @@ private fun PayoutAccountContent( text = "You haven’t added a billing method yet. Add one to pay for your insurance.", // todo description = null, iconStyle = EmptyStateDefaults.EmptyStateIconStyle.INFO, + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp) ) } } else { @@ -358,7 +362,7 @@ private fun formatBankAccountNumber(clearingNumber: String?, accountNumber: Stri } @Composable -@HedvigPreview +@HedvigShortMultiScreenPreview private fun PreviewPayinAccountOverviewScreen( @PreviewParameter(PayinAccountOverviewUiStateProvider::class) uiState: PayinAccountOverviewUiState, ) { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt index 8976ea1e99..9caa88ad2c 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.unit.dp import com.hedvig.android.design.system.hedvig.HedvigCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigShortMultiScreenPreview import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.Icon @@ -142,7 +143,7 @@ private fun PayinMethodRow( } @Composable -@HedvigPreview +@HedvigShortMultiScreenPreview private fun PreviewSelectPayinMethodScreen() { HedvigTheme { Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { diff --git a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt index d1059266e9..378076a95c 100644 --- a/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt @@ -23,6 +23,7 @@ import com.hedvig.android.design.system.hedvig.HedvigButton import com.hedvig.android.design.system.hedvig.HedvigNotificationCard import com.hedvig.android.design.system.hedvig.HedvigPreview import com.hedvig.android.design.system.hedvig.HedvigScaffold +import com.hedvig.android.design.system.hedvig.HedvigShortMultiScreenPreview import com.hedvig.android.design.system.hedvig.HedvigText import com.hedvig.android.design.system.hedvig.HedvigTheme import com.hedvig.android.design.system.hedvig.NotificationDefaults.NotificationPriority @@ -116,7 +117,7 @@ private fun SetupInvoicePayinScreen( } @Composable -@HedvigPreview +@HedvigShortMultiScreenPreview private fun PreviewPayoutAccountOverviewScreen() { HedvigTheme { Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { 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/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt index d2cb93d77b..19415acfed 100644 --- a/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt +++ b/app/feature/feature-payments/src/main/kotlin/com/hedvig/android/feature/payments/ui/memberpaymentdetails/MemberPaymentDetailsDestination.kt @@ -95,7 +95,8 @@ private fun MemberPaymentDetailsScreen( when (uiState) { MemberPaymentDetailsUiState.Failure -> { HedvigErrorSection( - modifier = Modifier.weight(1f), + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp) + .weight(1f), onButtonClick = retry, buttonText = stringResource(R.string.GENERAL_RETRY), )