Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Updated FormatMoneyUseCase #3657

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import com.ivy.navigation.navigation
import com.ivy.navigation.screenScopedViewModel
import com.ivy.ui.R
import com.ivy.wallet.domain.data.CustomExchangeRateState
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What caused this package change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package directive of IvyCurrency data class was wrong. It resides in com.ivy.legacy.domain.data.IvyCurrency but directive was com.ivy.wallet.domain.data.IvyCurrency in the data class file. I fixed it thus these changes appeared as-per correct package directive in places where it's used.

import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData
import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData
import com.ivy.wallet.ui.edit.core.Category
Expand Down
2 changes: 1 addition & 1 deletion screen/home/src/main/java/com/ivy/home/HomeTab.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import com.ivy.navigation.IvyPreview
import com.ivy.navigation.screenScopedViewModel
import com.ivy.ui.R
import com.ivy.ui.rememberScrollPositionListState
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.pure.data.IncomeExpensePair
import com.ivy.wallet.ui.theme.modal.BufferModal
import com.ivy.wallet.ui.theme.modal.BufferModalData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.ivy.legacy.datamodel.Account
import com.ivy.legacy.datamodel.temp.toLegacyDomain
import com.ivy.legacy.datamodel.toEntity
import com.ivy.legacy.utils.toLowerCaseLocal
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.pure.util.nextOrderNum
import com.ivy.wallet.ui.theme.Green
import com.ivy.wallet.ui.theme.IvyDark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import com.ivy.navigation.TransactionsScreen
import com.ivy.navigation.navigation
import com.ivy.ui.R
import com.ivy.ui.time.TimeFormatter
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.ui.theme.Gradient
import com.ivy.wallet.ui.theme.Gray
import com.ivy.wallet.ui.theme.MediumBlack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.ivy.onboarding
import androidx.compose.runtime.Immutable
import com.ivy.data.model.Category
import com.ivy.legacy.data.model.AccountBalance
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData
import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData
import kotlinx.collections.immutable.ImmutableList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.ivy.onboarding

import com.ivy.data.model.Category
import com.ivy.legacy.datamodel.Account
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData
import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import com.ivy.onboarding.steps.OnboardingSetCurrency
import com.ivy.onboarding.steps.OnboardingSplashLogin
import com.ivy.onboarding.steps.OnboardingType
import com.ivy.onboarding.viewmodel.OnboardingViewModel
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData
import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData
import kotlinx.collections.immutable.ImmutableList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.ivy.legacy.IvyWalletPreview
import com.ivy.legacy.utils.setStatusBarDarkTextCompat
import com.ivy.navigation.navigation
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.ui.theme.GradientIvy
import com.ivy.wallet.ui.theme.White
import com.ivy.wallet.ui.theme.components.BackButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import com.ivy.navigation.MainScreen
import com.ivy.navigation.Navigation
import com.ivy.navigation.OnboardingScreen
import com.ivy.onboarding.OnboardingState
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.PreloadDataLogic
import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData
import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.ivy.onboarding.OnboardingDetailState
import com.ivy.onboarding.OnboardingEvent
import com.ivy.onboarding.OnboardingState
import com.ivy.wallet.domain.action.account.AccountsAct
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.CategoryCreator
import com.ivy.wallet.domain.deprecated.logic.PreloadDataLogic
import com.ivy.wallet.domain.deprecated.logic.WalletAccountLogic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import com.ivy.navigation.ReleasesScreen
import com.ivy.navigation.navigation
import com.ivy.navigation.screenScopedViewModel
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.ui.theme.Blue
import com.ivy.wallet.ui.theme.Gradient
import com.ivy.wallet.ui.theme.GradientGreen
Expand Down
65 changes: 59 additions & 6 deletions shared/ui/core/src/main/java/com/ivy/ui/FormatMoneyUseCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ const val THOUSAND = 1_000
const val MILLION = 1_000_000
const val BILLION = 1_000_000_000

/**
* A use case class responsible for formatting currency and cryptocurrency values based on user preferences.
* It supports regular currency formatting with or without decimal places, as well as shortened formats
* (e.g., "1k", "1m"). For cryptocurrency, it formats up to 9 decimal places and removes unnecessary trailing zeros.
*
* @property features Provides feature toggles to customize app behavior.
* @property devicePreferences Manages user-specific preferences for locale and other device settings.
* @property context Application context, used for feature check and resource access.
*/
class FormatMoneyUseCase @Inject constructor(
private val features: Features,
private val devicePreferences: DevicePreferences,
Expand All @@ -23,25 +32,69 @@ class FormatMoneyUseCase @Inject constructor(
private val withoutDecimalFormatter = DecimalFormat("###,###", DecimalFormatSymbols(locale))
private val withDecimalFormatter = DecimalFormat("###,###.00", DecimalFormatSymbols(locale))
private val shortenAmountFormatter = DecimalFormat("###,###.##", DecimalFormatSymbols(locale))
private val cryptoFormatter =
DecimalFormat("###,###,##0.${"0".repeat(9)}", DecimalFormatSymbols(locale))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try fixing the formatter by "#".repeat(9)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


suspend fun format(value: Double, shortenAmount: Boolean): String {
if (abs(value) >= THOUSAND && shortenAmount) {
val result = if (abs(value) >= BILLION) {
/**
* Formats a currency or cryptocurrency amount based on the input parameters.
*
* @param value The numeric value to format.
* @param shortenAmount Flag to indicate if the amount should be shortened (e.g., "1k" for 1,000).
* @param isCrypto Flag to indicate if the value is a cryptocurrency, enabling up to 9 decimal places.
* @return The formatted string representation of the value.
*/
suspend fun format(value: Double, shortenAmount: Boolean, isCrypto: Boolean = false): String {
val result = if (isCrypto) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can directly return, no need for result var

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

formatCrypto(value)
} else if (abs(value) >= THOUSAND && shortenAmount) {
if (abs(value) >= BILLION) {
"${shortenAmountFormatter.format(value / BILLION)}b"
} else if (abs(value) >= MILLION) {
"${shortenAmountFormatter.format(value / MILLION)}m"
} else {
"${shortenAmountFormatter.format(value / THOUSAND)}k"
}
return result
} else {
val showDecimalPoint = features.showDecimalNumber.isEnabled(context)

val formatter = when (showDecimalPoint) {
true -> withDecimalFormatter
false -> withoutDecimalFormatter
}
return formatter.format(value)
formatter.format(value)
}

return result
}

/**
* Formats a cryptocurrency value with up to 9 decimal places, removing unnecessary trailing zeros.
*
* @param value The cryptocurrency value to format.
* @return The formatted cryptocurrency value as a string.
*/
private fun formatCrypto(value: Double): String {
val result = cryptoFormatter.format(value)
return when {
result.lastOrNull() == localDecimalSeparator().firstOrNull() -> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this logic - can we just fix the formatter?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the function, just relying on the suggested formatter now

val newResult = result.dropLast(1)
newResult.ifEmpty { "0" }
}

result.isEmpty() -> {
"0"
}

else -> result
}
}

/**
* Retrieves the local decimal separator based on the user's locale.
*
* @return The decimal separator as a string.
*/
private fun localDecimalSeparator(): String {
return DecimalFormatSymbols(locale).decimalSeparator.toString()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,79 +24,122 @@ class FormatMoneyUseCaseTest {
val amount: Double,
val showDecimal: Boolean,
val shortenAmount: Boolean,
val isCrypto: Boolean,
val locale: Locale,
val expectedOutput: String
) {
ENG_SHOW_DECIMAL(
amount = 1_000.12,
showDecimal = true,
shortenAmount = false,
isCrypto = false,
locale = Locale.ENGLISH,
expectedOutput = "1,000.12"
),
ENG_HIDE_DECIMAL(
amount = 1_000.12,
showDecimal = false,
shortenAmount = false,
isCrypto = false,
locale = Locale.ENGLISH,
expectedOutput = "1,000"
),
GERMAN_SHOW_DECIMAL(
amount = 1_000.12,
showDecimal = true,
shortenAmount = false,
isCrypto = false,
locale = Locale.GERMAN,
expectedOutput = "1.000,12"
),
GERMAN_HIDE_DECIMAL(
amount = 1_000.12,
showDecimal = false,
shortenAmount = false,
isCrypto = false,
locale = Locale.GERMAN,
expectedOutput = "1.000"
),
ENGLISH_1K_SHORT_AMT(
amount = 13_000.10,
showDecimal = true,
shortenAmount = true,
isCrypto = false,
locale = Locale.ENGLISH,
expectedOutput = "13k"
),
ENGLISH_MILLION_SHORT_AMT(
amount = 1_233_500.10,
showDecimal = true,
shortenAmount = true,
isCrypto = false,
locale = Locale.ENGLISH,
expectedOutput = "1.23m"
),
ENGLISH_BILLION_SHORT_AMT(
amount = 1_233_000_000.10,
showDecimal = true,
shortenAmount = true,
isCrypto = false,
locale = Locale.ENGLISH,
expectedOutput = "1.23b"
),
GERMAN_1K_SHORT_AMT(
amount = 13_000.10,
showDecimal = true,
shortenAmount = true,
isCrypto = false,
locale = Locale.GERMAN,
expectedOutput = "13k"
),
GERMAN_MILLION_SHORT_AMT(
amount = 1_233_500.10,
showDecimal = true,
shortenAmount = true,
isCrypto = false,
locale = Locale.GERMAN,
expectedOutput = "1,23m"
),
GERMAN_BILLION_SHORT_AMT(
amount = 1_233_000_000.10,
showDecimal = true,
shortenAmount = true,
isCrypto = false,
locale = Locale.GERMAN,
expectedOutput = "1,23b"
),
ENG_SHOW_DECIMAL_CRYPTO(
amount = 123_456.0,
showDecimal = true,
shortenAmount = false,
isCrypto = true,
locale = Locale.ENGLISH,
expectedOutput = "123,456.000000000"
),
ENG_HIDE_DECIMAL_CRYPTO(
amount = 123_456.0,
showDecimal = false,
shortenAmount = false,
isCrypto = true,
locale = Locale.ENGLISH,
expectedOutput = "123,456.000000000"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't expect such zeros

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the result as-per the suggested formatter expected result

),
GERMAN_SHOW_DECIMAL_CRYPTO(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a tear case for "0.000345 BTC" for example

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

amount = 123_456.0,
showDecimal = true,
shortenAmount = false,
isCrypto = true,
locale = Locale.GERMAN,
expectedOutput = "123.456,000000000"
),
GERMAN_HIDE_DECIMAL_CRYPTO(
amount = 123_456.0,
showDecimal = false,
shortenAmount = false,
isCrypto = true,
locale = Locale.GERMAN,
expectedOutput = "123.456,000000000"
),
}

private lateinit var formatMoneyUseCase: FormatMoneyUseCase
Expand All @@ -114,7 +157,8 @@ class FormatMoneyUseCaseTest {
// when
val result = formatMoneyUseCase.format(
value = testCase.amount,
shortenAmount = testCase.shortenAmount
shortenAmount = testCase.shortenAmount,
isCrypto = testCase.isCrypto
)

// then
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ivy.wallet.domain.data
package com.ivy.legacy.domain.data

import android.icu.util.Currency
import androidx.compose.runtime.Immutable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.ivy.legacy.datamodel.toEntity
import com.ivy.legacy.utils.convertLocalToUTC
import com.ivy.legacy.utils.timeNowUTC
import com.ivy.legacy.utils.toLowerCaseLocal
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.csv.model.RowMapping
import com.ivy.wallet.domain.pure.util.nextOrderNum
import com.opencsv.CSVReaderBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import com.ivy.legacy.utils.keyboardOnlyWindowInsets
import com.ivy.legacy.utils.onScreenStart
import com.ivy.legacy.utils.toLowerCaseLocal
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.ui.theme.GradientGreen
import com.ivy.wallet.ui.theme.GradientIvy
import com.ivy.wallet.ui.theme.Ivy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.ivy.design.l0_system.style
import com.ivy.legacy.IvyWalletComponentPreview
import com.ivy.legacy.utils.format
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.ui.theme.Orange

@Deprecated("Old design system. Use `:ivy-design` and Material3")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import com.ivy.design.l0_system.UI
import com.ivy.design.l0_system.style
import com.ivy.legacy.IvyWalletPreview
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.ui.theme.Gray
import com.ivy.wallet.ui.theme.components.CurrencyPicker
import java.util.UUID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import com.ivy.legacy.utils.selectEndTextFieldValue
import com.ivy.design.utils.thenIf
import com.ivy.legacy.legacy.ui.theme.modal.ModalNameInput
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData
import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanData
import com.ivy.wallet.ui.theme.GradientIvy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import com.ivy.legacy.utils.selectEndTextFieldValue
import com.ivy.legacy.utils.toLowerCaseLocal
import com.ivy.legacy.utils.toUpperCaseLocal
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData
import com.ivy.wallet.ui.theme.Gray
import com.ivy.wallet.ui.theme.Ivy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import com.ivy.design.l0_system.style
import com.ivy.legacy.utils.drawColoredShadow
import com.ivy.legacy.utils.format
import com.ivy.ui.R
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.legacy.domain.data.IvyCurrency
import com.ivy.wallet.ui.theme.Gradient
import com.ivy.wallet.ui.theme.Green
import com.ivy.wallet.ui.theme.MediumBlack
Expand Down
Loading