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..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 @@ -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 @@ -175,9 +176,15 @@ 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 -> { + Ior.Both(errors, data) + } + errors != null -> { + Ior.Left(errors) + } + data != null -> { + Ior.Right(data) + } else -> error("Non compliant server") } } 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/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..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 @@ -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,14 @@ internal fun HedvigNavHost( navigateToConnectPayment = ::navigateToConnectPayment, navigateUp = navController::navigateUp, ) + payinAccountGraph( + navController = navController, + globalSnackBarState = globalSnackBarState, + hedvigDeepLinkContainer = hedvigDeepLinkContainer, + navigateToConnectTrustly = ::navigateToConnectTrustly, + navigateUp = navController::navigateUp, + openUrl = openUrl + ) profileGraph( settingsDestinationNestedGraphs = { deleteAccountGraph(hedvigDeepLinkContainer, navController) @@ -480,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..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,6 +522,7 @@ Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut + 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 @@ -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 @@ -1279,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. 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..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,6 +522,7 @@ Question regarding claim, Vehicle reg. %1$s Preferences Logout + Manage your payment methods Activate your coverage again by contacting us once your payment has been processed Get a price quote Review contact info @@ -1279,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. 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..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,6 +522,7 @@ Fråga angående skadeanmälan - Fordon reg. %1$s Välj land och språk Logga ut + 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 @@ -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 @@ -1279,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. 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..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,6 +522,7 @@ Question regarding claim, Vehicle reg. %1$s Preferences Logout + Manage your payment methods Activate your coverage again by contacting us once your payment has been processed Get a price quote Review contact info @@ -1279,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. 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/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/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/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..72e884b890 --- /dev/null +++ b/app/design-system/design-system-hedvig/src/commonMain/kotlin/com/hedvig/android/design/system/hedvig/icon/BankAccount.kt @@ -0,0 +1,118 @@ +package com.hedvig.android.design.system.hedvig.icon + +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 + +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/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/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-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-payin-account/build.gradle.kts b/app/feature/feature-payin-account/build.gradle.kts new file mode 100644 index 0000000000..ce5712c76a --- /dev/null +++ b/app/feature/feature-payin-account/build.gradle.kts @@ -0,0 +1,35 @@ +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) + implementation(libs.zXing) +} 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-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/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-payout-account/src/main/graphql/SetupInvoicePayout.graphql b/app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql similarity index 73% rename from app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.graphql rename to app/feature/feature-payin-account/src/main/graphql/SetupInvoicePayin.graphql index dd61ac8dcf..cb935fbfd1 100644 --- a/app/feature/feature-payout-account/src/main/graphql/SetupInvoicePayout.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/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/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..3071232b66 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/GetPayinAccountUseCase.kt @@ -0,0 +1,150 @@ +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 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 +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 currentMethods: List, + val availablePayinMethods: List, +) + +internal class GetPayinAccountUseCase( + private val apolloClient: ApolloClient, +) { + suspend fun invoke(): Either = either { + logcat { "Mariia: GetPayinAccountUseCase launching" } + val result = apolloClient + .query(GetPayinMethodsQuery()) + .fetchPolicy(FetchPolicy.NetworkOnly) + .safeExecute(::ErrorMessage) + .bind() + + val paymentMethods = result.currentMember.paymentMethods + + 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 = isDefault, + ) + } + + MemberPaymentProvider.TRUSTLY -> { + val (clearingNumber, accountNumber, bankName) = parseBankAccountDetails(method) + PayinAccount.Trustly( + clearingNumber = clearingNumber, + accountNumber = accountNumber, + bankName = bankName, + isPending = isPending, + isDefault = isDefault, + ) + } + + MemberPaymentProvider.INVOICE -> { + val invoiceDetails = method.details?.asPaymentMethodInvoiceDetails() + PayinAccount.Invoice( + delivery = invoiceDetails?.delivery, + email = invoiceDetails?.email, + isPending = isPending, + isDefault = isDefault, + ) + } + + else -> { + null + } + } + } + + val availablePayinMethods = paymentMethods.availableMethods + .filter { it.supportsPayin } + .map { it.provider } + + val finalResult = PayinAccountData( + currentMethods = currentMethods, + availablePayinMethods = availablePayinMethods, + ) + logcat { "Mariia: GetPayinAccountUseCase finalResult: $finalResult" } + finalResult + } +} + +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 + 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 +} + +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..64d2b8182f --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetAsDefaultUseCase.kt @@ -0,0 +1,49 @@ +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.apollo.safeExecuteAllowingPartialResponses +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 { + apolloClient + .mutation(SetAsDefaultPayinMutation(provider)) + .safeExecuteAllowingPartialResponses() + .fold( + fa = { error -> + logcat { "SetAsDefaultUseCaseImpl error: $error" } + raise(ErrorMessage()) + }, + fb = { result -> + val userError = result.paymentMethodSetDefaultPayin?.message + if (userError != null) { + logcat { "SetAsDefaultUseCaseImpl userError not null: $userError" } + raise(ErrorMessage(userError)) + } + getPayinAccountUseCase.invoke().bind() + }, + fab = {errors, _ -> + logcat { "SetAsDefaultUseCaseImpl data with errors: $errors" } + raise(ErrorMessage()) + } + ) + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayinUseCase.kt similarity index 77% rename from app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt rename to app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayinUseCase.kt index 5502cb9051..bfe420a006 100644 --- a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/data/SetupInvoicePayoutUseCase.kt +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupInvoicePayinUseCase.kt @@ -1,4 +1,4 @@ -package com.hedvig.android.feature.payoutaccount.data +package com.hedvig.android.feature.payin.account.data import arrow.core.Either import arrow.core.raise.either @@ -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/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..1088ea52a5 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/data/SetupSwishPayinUseCase.kt @@ -0,0 +1,75 @@ +package com.hedvig.android.feature.payin.account.data + +import arrow.core.Either +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.safeExecuteAllowingPartialResponses +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(phoneNumber: String): Either +} + +internal class SetupSwishPayinUseCaseImpl( + private val apolloClient: ApolloClient, + private val networkCacheManager: NetworkCacheManager, +) : SetupSwishPayinUseCase { + override suspend fun invoke(phoneNumber: String): Either = either { + apolloClient + .mutation(SetupSwishPayinMutation(PaymentMethodSetupSwishInput(phoneNumber))) + .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) + } + + 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)) + } + } + }, + fab = { errors, _ -> + logcat { "SetupSwishPayinMutation dataa with errors: $errors" } + raise(ErrorMessage()) + }, + ) + } +} + +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 new file mode 100644 index 0000000000..516f144333 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/di/PayinAccountModule.kt @@ -0,0 +1,37 @@ +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 +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 { + SetAsDefaultUseCaseImpl( + get(), + get(), + ) + } + single { SetupInvoicePayinUseCase(get(), get()) } + viewModel { SetupInvoicePayinViewModel(get()) } + viewModel { SetupSwishPayinViewModel(get()) } + viewModel { + PayinAccountOverviewViewModel( + get(), + get(), + ) + } +} 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..57b0d62d57 --- /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.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 +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, + navigateToConnectTrustly: (builder: NavOptionsBuilder.() -> Unit) -> Unit, + navigateUp: () -> Unit, + openUrl: (String) -> Unit, +) { + navgraph( + startDestination = PayinAccountDestinations.Overview::class, + deepLinks = navDeepLinks(hedvigDeepLinkContainer.connectPayment), + ) { + navdestination { + val viewModel: PayinAccountOverviewViewModel = koinViewModel() + PayinAccountOverviewDestination( + viewModel = viewModel, + onConnectPayoutMethodClicked = dropUnlessResumed { + val content = viewModel.uiState.value as? PayinAccountOverviewUiState.Content + navController.navigate( + PayinAccountDestinations.SelectPayinMethod( + availableProviders = content?.availablePayinMethods?.map { it.rawValue } ?: emptyList(), + ), + ) + }, + navigateUp = navigateUp, + ) + } + + navdestination { + SelectPayinMethodDestination( + availableProviders = this.availableProviders.map { MemberPaymentProvider.safeValueOf(it) }, + onTrustlySelected = dropUnlessResumed { + navigateToConnectTrustly { + typedPopUpTo { + inclusive = true + } + } + }, + onSwishSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupSwishPayin) }, + onInvoiceSelected = dropUnlessResumed { navController.navigate(PayinAccountDestinations.SetupInvoicePayin) }, + navigateUp = navController::navigateUp, + ) + } + + navdestination { + val viewModel: SetupSwishPayinViewModel = koinViewModel() + SetupSwishPayinDestination( + viewModel = viewModel, + globalSnackBarState = globalSnackBarState, + onSuccessfullyConnected = { + navController.typedPopBackStack(inclusive = true) + }, + navigateUp = navController::navigateUp, + openUrl = { + navController.typedPopBackStack(inclusive = true) + openUrl(it) + }, + ) + } + + navdestination { + val viewModel: SetupInvoicePayinViewModel = 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/PayinAccountOverviewDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt new file mode 100644 index 0000000000..36c52fc533 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewDestination.kt @@ -0,0 +1,474 @@ +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 +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.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.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 +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 +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 com.hedvig.android.logger.logcat +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 +internal fun PayinAccountOverviewDestination( + viewModel: PayinAccountOverviewViewModel, + onConnectPayoutMethodClicked: () -> Unit, + navigateUp: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + PayinAccountOverviewScreen( + uiState = uiState, + onConnectPayoutMethodClicked = onConnectPayoutMethodClicked, + onRetry = { viewModel.emit(PayinAccountOverviewEvent.Retry) }, + navigateUp = navigateUp, + setAsDefaultPayinMethod = { + viewModel.emit(PayinAccountOverviewEvent.SetDefaultMethod(it)) + }, + ) +} + +@Composable +private fun PayinAccountOverviewScreen( + uiState: PayinAccountOverviewUiState, + onConnectPayoutMethodClicked: () -> Unit, + onRetry: () -> Unit, + navigateUp: () -> Unit, + setAsDefaultPayinMethod: (MemberPaymentProvider) -> Unit, +) { + HedvigScaffold( + topAppBarText = "Payment account", // todo! + navigateUp = navigateUp, + modifier = Modifier.fillMaxSize(), + ) { + when (uiState) { + PayinAccountOverviewUiState.Loading -> { + HedvigFullScreenCenterAlignedProgressDebounced( + Modifier + .weight(1f) + .wrapContentHeight(), + ) + } + + PayinAccountOverviewUiState.Error -> { + HedvigErrorSection( + onButtonClick = onRetry, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .weight(1f) + .wrapContentHeight(), + ) + } + + is PayinAccountOverviewUiState.Content -> { + PayoutAccountContent( + currentMethods = uiState.currentMethods, + availablePayinMethods = uiState.availablePayinMethods, + onConnectPayinMethodClicked = onConnectPayoutMethodClicked, + modifier = Modifier.weight(1f), + setAsDefaultPayinMethod = setAsDefaultPayinMethod, + loadingDefaultProvider = uiState.loadingDefaultProvider, + setDefaultProviderError = uiState.setDefaultProviderError, + ) + } + } + } +} + +@Composable +private fun PayoutAccountContent( + currentMethods: List, + availablePayinMethods: List, + onConnectPayinMethodClicked: () -> Unit, + setAsDefaultPayinMethod: (MemberPaymentProvider) -> Unit, + loadingDefaultProvider: MemberPaymentProvider?, + setDefaultProviderError: ErrorMessage?, + modifier: Modifier = Modifier, +) { + Column(modifier) { + Spacer(Modifier.height(8.dp)) + 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, + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp) + ) + } + } else { + HedvigText( + "Connected payment methods", // todo + modifier = Modifier.padding(horizontal = 16.dp), + ) + 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 { + formatSwishPhoneNumber(phoneNumber) + }, + isDefault = method.isDefault, + onClick = setAsDefaultPayinMethod, + modifier = Modifier.padding(horizontal = 16.dp), + loadingDefaultProvider = loadingDefaultProvider, + isPending = method.isPending, + provider = MemberPaymentProvider.SWISH, + ) + } + + is PayinAccount.Trustly -> { + val accountNumber = formatBankAccountNumber(method.clearingNumber, method.accountNumber, method.bankName) + CurrentPayinMethodRow( + label = "Direct debit", // todo + text = if (method.isPending && accountNumber.isBlank()) { + stringResource(Res.string.REFERRAL_PENDING_STATUS_LABEL) + } else { + accountNumber + }, + isDefault = method.isDefault, + onClick = setAsDefaultPayinMethod, + modifier = Modifier.padding(horizontal = 16.dp), + loadingDefaultProvider = loadingDefaultProvider, + isPending = method.isPending, + provider = MemberPaymentProvider.TRUSTLY, + ) + } + + is PayinAccount.Invoice -> { + CurrentPayinMethodRow( + stringResource(Res.string.PAYMENTS_INVOICE), + method.delivery?.toDeliveryString() ?: "", + isDefault = method.isDefault, + onClick = setAsDefaultPayinMethod, + modifier = Modifier.padding(horizontal = 16.dp), + loadingDefaultProvider = loadingDefaultProvider, + isPending = method.isPending, + provider = MemberPaymentProvider.INVOICE, + ) + } + } + } + } + } + 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( + message = "You have just added or changed a billing method, it will appear here soon.", // todo + priority = NotificationPriority.Info, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + if (availablePayinMethods.isNotEmpty()) { + HedvigButton( + text = "Add a payment method", // todo! + onClick = onConnectPayinMethodClicked, + enabled = true, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) + } + } + Spacer(Modifier.height(16.dp)) + } +} + +@Composable +private fun CurrentPayinMethodRow( + label: String, + text: String, + isDefault: Boolean, + isPending: Boolean, + loadingDefaultProvider: MemberPaymentProvider?, + onClick: (MemberPaymentProvider) -> Unit, + provider: MemberPaymentProvider, + modifier: Modifier = Modifier, +) { + HedvigCard( + modifier = modifier.fillMaxWidth(), + ) { + HorizontalItemsWithMaximumSpaceTaken( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + startSlot = { + 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 = { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + ) { + if (isDefault) { + HighlightLabel( + labelText = "Default", // todo + size = HighlightLabelDefaults.HighLightSize.Small, + color = HighlightLabelDefaults.HighlightColor.Green( + HighlightLabelDefaults.HighlightShade.LIGHT, + ), + ) + } else { + if (!isPending) { + HedvigButtonGhostWithBorder( + size = ButtonDefaults.ButtonSize.Small, + text = "Set as default", // todo + onClick = { + onClick(provider) + }, + isLoading = loadingDefaultProvider == provider, + ) + } + } + } + }, + spaceBetween = 6.dp, + ) + } +} + +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 { +// clearingNumber != null && accountNumber != null && bankName != null -> "$bankName $clearingNumber-$accountNumber" +// clearingNumber != null && bankName != null -> "$bankName $clearingNumber" +// else -> clearingNumber.orEmpty() +// } + return when { + bankName != null -> bankName + else -> clearingNumber.orEmpty() + } +} + +@Composable +@HedvigShortMultiScreenPreview +private fun PreviewPayinAccountOverviewScreen( + @PreviewParameter(PayinAccountOverviewUiStateProvider::class) uiState: PayinAccountOverviewUiState, +) { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + PayinAccountOverviewScreen( + uiState = uiState, + onConnectPayoutMethodClicked = {}, + onRetry = {}, + navigateUp = {}, + {}, + ) + } + } +} + +private class PayinAccountOverviewUiStateProvider : CollectionPreviewParameterProvider( + listOf( + PayinAccountOverviewUiState.Loading, + PayinAccountOverviewUiState.Error, + PayinAccountOverviewUiState.Content( + currentMethods = emptyList(), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + ), + PayinAccountOverviewUiState.Content( + currentMethods = listOf( + PayinAccount.SwishPayin( + phoneNumber = "0701234567", + isPending = false, + isDefault = true, + ), + ), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH), + ), + PayinAccountOverviewUiState.Content( + currentMethods = listOf( + PayinAccount.SwishPayin( + phoneNumber = "0701234567", + isPending = false, + isDefault = true, + ), + ), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH, MemberPaymentProvider.TRUSTLY), + ), + PayinAccountOverviewUiState.Content( + currentMethods = listOf(PayinAccount.SwishPayin(phoneNumber = null, isPending = true, isDefault = true)), + availablePayinMethods = listOf(MemberPaymentProvider.SWISH), + ), + PayinAccountOverviewUiState.Content( + currentMethods = listOf( + PayinAccount.Trustly( + clearingNumber = "****", + accountNumber = "*45678", + bankName = "Swedbank", + isPending = false, + isDefault = true, + ), + PayinAccount.SwishPayin( + phoneNumber = "0701234567", + 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 = "0701234567", + isPending = false, + isDefault = false, + ), + PayinAccount.Invoice ( + delivery = PaymentMethodInvoiceDelivery.KIVRA, + isPending = false, + isDefault = false, + email = "" + ), + ), + availablePayinMethods = listOf(MemberPaymentProvider.TRUSTLY), + ), + PayinAccountOverviewUiState.Content( + currentMethods = listOf( + PayinAccount.Trustly( + clearingNumber = "****", + accountNumber = "*45678", + bankName = "Swedbank", + isPending = false, + isDefault = true, + ), + PayinAccount.SwishPayin( + phoneNumber = "0701234567", + 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 new file mode 100644 index 0000000000..c54498db31 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/overview/PayinAccountOverviewViewModel.kt @@ -0,0 +1,110 @@ +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.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 +import octopus.type.MemberPaymentProvider + +internal class PayinAccountOverviewViewModel( + getPayinAccountUseCase: GetPayinAccountUseCase, + setAsDefaultUseCase: SetAsDefaultUseCase, +) : MoleculeViewModel( + PayinAccountOverviewUiState.Loading, + PayinAccountOverviewPresenter(getPayinAccountUseCase, setAsDefaultUseCase), + ) + +internal sealed interface PayinAccountOverviewEvent { + data object Retry : PayinAccountOverviewEvent + + data class SetDefaultMethod(val provider: MemberPaymentProvider) : PayinAccountOverviewEvent +} + +internal sealed interface PayinAccountOverviewUiState { + data object Loading : PayinAccountOverviewUiState + + data object Error : 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) { + uiState = PayinAccountOverviewUiState.Loading + getPayinAccountUseCase.invoke().fold( + ifLeft = { uiState = PayinAccountOverviewUiState.Error }, + ifRight = { data -> + uiState = PayinAccountOverviewUiState.Content( + currentMethods = data.currentMethods, + availablePayinMethods = data.availablePayinMethods, + ) + }, + ) + } + + 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 + } + } + } + + 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..9caa88ad2c --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/selectmethod/SelectPayinMethodDestination.kt @@ -0,0 +1,163 @@ +package com.hedvig.android.feature.payin.account.ui.selectmethod + +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.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 +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 hedvig.resources.PAYMENTS_INVOICE +import hedvig.resources.PAYOUT_METHOD_INVOICE_DESCRIPTION +import hedvig.resources.Res +import hedvig.resources.swish +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 = "Add or change payment 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) { + PayinMethodRow( + provider = provider, + onTrustlySelected = onTrustlySelected, + onSwishSelected = onSwishSelected, + onInvoiceSelected = onInvoiceSelected, + ) + } + } + Spacer(Modifier.height(16.dp)) + } +} + +@Composable +private fun PayinMethodRow( + provider: MemberPaymentProvider, + onTrustlySelected: () -> Unit, + onSwishSelected: () -> Unit, + onInvoiceSelected: () -> Unit, + modifier: Modifier = Modifier, +) { + HedvigCard( + onClick = { + when (provider) { + MemberPaymentProvider.TRUSTLY -> { + onTrustlySelected() + } + + MemberPaymentProvider.SWISH -> { + onSwishSelected() + } + + MemberPaymentProvider.INVOICE -> { + onInvoiceSelected() + } + + else -> {} + } + }, + modifier = modifier.fillMaxWidth(), + ) { + 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 -> "Direct debit" // 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, + ) + } + + + } + } +} + +@Composable +@HedvigShortMultiScreenPreview +private fun PreviewSelectPayinMethodScreen() { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + SelectPayinMethodDestination( + availableProviders = listOf( + MemberPaymentProvider.SWISH, + MemberPaymentProvider.INVOICE, + MemberPaymentProvider.TRUSTLY, + ), + onTrustlySelected = {}, + onSwishSelected = {}, + onInvoiceSelected = {}, + navigateUp = {}, + ) + } + } +} diff --git a/app/feature/feature-payout-account/src/main/kotlin/com/hedvig/android/feature/payoutaccount/ui/setupinvoice/SetupInvoicePayoutDestination.kt b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupinvoice/SetupInvoicePayinDestination.kt similarity index 63% 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..378076a95c 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,8 +1,10 @@ -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 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 import androidx.compose.foundation.layout.fillMaxWidth @@ -11,28 +13,35 @@ 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 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 +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( - viewModel: SetupInvoicePayoutViewModel, +internal fun SetupInvoicePayinDestination( + viewModel: SetupInvoicePayinViewModel, globalSnackBarState: GlobalSnackBarState, onSuccessfullyConnected: () -> Unit, navigateUp: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - SetupInvoicePayoutScreen( + SetupInvoicePayinScreen( uiState = uiState, globalSnackBarState = globalSnackBarState, onConnect = { viewModel.emit(SetupInvoicePayoutEvent.Connect) }, @@ -45,8 +54,8 @@ internal fun SetupInvoicePayoutDestination( } @Composable -private fun SetupInvoicePayoutScreen( - uiState: SetupInvoicePayoutUiState, +private fun SetupInvoicePayinScreen( + uiState: SetupInvoicePayinUiState, globalSnackBarState: GlobalSnackBarState, onConnect: () -> Unit, showedSnackBar: () -> Unit, @@ -64,7 +73,21 @@ private fun SetupInvoicePayoutScreen( navigateUp = navigateUp, modifier = Modifier.fillMaxSize(), ) { - Spacer(Modifier.weight(1f)) + // todo: some text here?? + 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, enter = expandVertically(), @@ -81,7 +104,7 @@ private fun SetupInvoicePayoutScreen( } Spacer(Modifier.height(16.dp)) HedvigButton( - text = "Connect", + text = "Set invoice as billing method", // todo onClick = onConnect, enabled = !uiState.isLoading, isLoading = uiState.isLoading, @@ -92,3 +115,23 @@ private fun SetupInvoicePayoutScreen( Spacer(Modifier.height(16.dp)) } } + +@Composable +@HedvigShortMultiScreenPreview +private fun PreviewPayoutAccountOverviewScreen() { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + SetupInvoicePayinScreen( + uiState = SetupInvoicePayinUiState( + false, + null, + showSuccessSnackBar = false, + ), + 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/SetupInvoicePayinViewModel.kt similarity index 71% 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/SetupInvoicePayinViewModel.kt index 8b9872ba4f..43057acb91 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/SetupInvoicePayinViewModel.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 @@ -7,16 +7,16 @@ 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, -) : MoleculeViewModel( - SetupInvoicePayoutUiState(false, null, false), - SetupInvoicePayoutPresenter(setupInvoicePayoutUseCase), +internal class SetupInvoicePayinViewModel( + setupInvoicePayinUseCase: 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( - private val setupInvoicePayoutUseCase: SetupInvoicePayoutUseCase, -) : MoleculePresenter { +internal class SetupInvoicePayinPresenter( + private val setupInvoicePayinUseCase: SetupInvoicePayinUseCase, +) : 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) } @@ -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" @@ -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/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 new file mode 100644 index 0000000000..1fa8c9484e --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinDestination.kt @@ -0,0 +1,302 @@ +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 +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 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 +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 + +@Composable +internal fun SetupSwishPayinDestination( + viewModel: SetupSwishPayinViewModel, + globalSnackBarState: GlobalSnackBarState, + onSuccessfullyConnected: () -> Unit, + navigateUp: () -> Unit, + openUrl: (String) -> 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, + openUrl = openUrl, + updateText = { + viewModel.emit(SetupSwishPayoutEvent.UpdateText(it)) + }, + ) +} + +// 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, + 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 + globalSnackBarState.show(changesSaved, NotificationPriority.Campaign) + showedSnackBar() + } + + HedvigScaffold( + topAppBarText = stringResource(Res.string.swish), + 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, + ) + } + AnimatedVisibility(uiState.successUrl != null) { + if (uiState.successUrl != null) + Column( + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + EmptyState( + text = "Please give your approval in the Swish app", + description = null, + modifier = Modifier.fillMaxWidth(), + iconStyle = EmptyStateDefaults.EmptyStateIconStyle.SWISH, + ) + Spacer(Modifier.height(16.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + 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(16.dp)) + HedvigButton( + "Open Swish", + enabled = true, + onClick = { + openUrl(uiState.successUrl) + }, + buttonStyle = ButtonDefaults.ButtonStyle.PrimaryAlt, + buttonSize = ButtonDefaults.ButtonSize.Medium, + ) + } + } + Spacer(Modifier.weight(1f)) + 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 = { + val digitsOnly = it.filter { char -> char.isDigit() } + if (digitsOnly.length <= 15) { + updateText(digitsOnly) + input = digitsOnly + } + }, + ) + } + 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)) + } +} + +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 { + append(formatSwishPhoneNumber(trimmed)) + 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) + } +} + +@Composable +@HedvigPreview +private fun PreviewSetupSwishPayinScreen( + @PreviewParameter(SetupSwishPayinUiStateProvider::class) uiState: SetupSwishPayoutUiState, +) { + HedvigTheme { + Surface(color = HedvigTheme.colorScheme.backgroundPrimary) { + SetupSwishPayoutScreen( + uiState = uiState, + globalSnackBarState = GlobalSnackBarState(), + onSave = {}, + showedSnackBar = {}, + navigateUp = {}, + {}, + {}, + ) + } + } +} + +private class SetupSwishPayinUiStateProvider : CollectionPreviewParameterProvider( + listOf( + SetupSwishPayoutUiState( + phoneNumber = "287334432273", + isLoading = false, + error = null, + showSuccessSnackBar = false, + ), + SetupSwishPayoutUiState( + phoneNumber = "", + isLoading = false, + error = ErrorMessage(), + showSuccessSnackBar = false, + ), + SetupSwishPayoutUiState( + phoneNumber = "837286428", + isLoading = true, + error = null, + showSuccessSnackBar = false, + ), + SetupSwishPayoutUiState( + phoneNumber = "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 new file mode 100644 index 0000000000..14e24ca1b0 --- /dev/null +++ b/app/feature/feature-payin-account/src/main/kotlin/com/hedvig/android/feature/payin/account/ui/setupswish/SetupSwishPayinViewModel.kt @@ -0,0 +1,124 @@ +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.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 + +internal class SetupSwishPayinViewModel( + setupSwishPayoutUseCase: SetupSwishPayinUseCase, +) : MoleculeViewModel( + SetupSwishPayoutUiState( + phoneNumber = "", + isLoading = false, + error = null, + showSuccessSnackBar = false, + successUrl = null, + ), + SetupSwishPayoutPresenter(setupSwishPayoutUseCase), + ) + +internal sealed interface SetupSwishPayoutEvent { + data object Save : SetupSwishPayoutEvent + + data object ShowedSnackBar : SetupSwishPayoutEvent + + 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( + private val setupSwishPayoutUseCase: SetupSwishPayinUseCase, +) : MoleculePresenter { + @Composable + override fun MoleculePresenterScope.present( + lastState: SetupSwishPayoutUiState, + ): SetupSwishPayoutUiState { + 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) } + 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) { + LaunchedEffect(currentSave) { + isLoading = true + errorMessage = null + resultIsPending = false + setupSwishPayoutUseCase.invoke(phoneNumberState).fold( + ifLeft = { + isLoading = false + errorMessage = it + saveIteration = null + }, + ifRight = { result -> + isLoading = false + 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 + } + } + }, + ) + } + } + + CollectEvents { event -> + when (event) { + SetupSwishPayoutEvent.Save -> { + if (!isLoading) { + saveIteration = phoneNumberState + } + } + + SetupSwishPayoutEvent.ShowedSnackBar -> { + showSuccessSnackBar = false + } + + is SetupSwishPayoutEvent.UpdateText -> { + phoneNumberState = event.newText + } + } + } + + return SetupSwishPayoutUiState( + phoneNumber = phoneNumberState, + isLoading = isLoading, + error = errorMessage, + showSuccessSnackBar = showSuccessSnackBar, + successUrl = urlToRedirect, + ) + } +} 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..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 @@ -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 @@ -94,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), ) @@ -220,30 +222,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 = stringResource(Res.string.MANAGE_BILLING_METHODS_BUTTON),//todo + onClick = onChangeBankAccount, + enabled = true, + buttonStyle = ButtonDefaults.ButtonStyle.Secondary, + modifier = Modifier + .fillMaxWidth() + .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)), + ) Spacer(Modifier.height(16.dp)) } 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( 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/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()) } } 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/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), - ), ), ) 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..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 @@ -1,24 +1,34 @@ 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.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 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 @@ -34,7 +44,6 @@ internal fun SelectPayoutMethodDestination( onTrustlySelected: () -> Unit, onNordeaSelected: () -> Unit, onSwishSelected: () -> Unit, - onInvoiceSelected: () -> Unit, navigateUp: () -> Unit, ) { HedvigScaffold( @@ -51,14 +60,16 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.trustly), subtitle = stringResource(Res.string.PAYOUT_METHOD_TRUSTLY_DESCRIPTION), onClick = onTrustlySelected, + provider = provider, ) } 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, ) } @@ -67,14 +78,7 @@ internal fun SelectPayoutMethodDestination( title = stringResource(Res.string.swish), subtitle = stringResource(Res.string.PAYOUT_METHOD_SWISH_DESCRIPTION), onClick = onSwishSelected, - ) - } - - MemberPaymentProvider.INVOICE -> { - PayoutMethodRow( - title = stringResource(Res.string.PAYMENTS_INVOICE), - subtitle = stringResource(Res.string.PAYOUT_METHOD_INVOICE_DESCRIPTION), - onClick = onInvoiceSelected, + provider = provider, ) } @@ -87,16 +91,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.Card, + 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 = {}, ) } } 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, 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 @@ + + + + + + + + + + + + + + + + + + + + + + + +