diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index 8278e828ad..36b3355615 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -979,6 +979,7 @@ Porcentaje de rendimiento anual El porcentaje de rendimiento anual que podrá ganar como participante en el staking. APR + APY Las recompensas se acumulan automáticamente en su saldo de staking diariamente. Disponible Tasa de recompensa promedio diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index b4299c0887..47bac750bc 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -160,7 +160,7 @@ Activer Activé Erreur - Échangez + Echanger Explorez Explorez l\'historique des transactions Explorateur @@ -953,6 +953,7 @@ Pourcentage de rendement annuel Le pourcentage de rendement annuel que vous pouvez gagner en participant au staking. APR + APY Les récompenses s\'accumulent automatiquement sur votre solde de staking quotidiennement. Disponible Taux de récompense moyen diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index bd9bd6be0d..48e34783ae 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -911,6 +911,7 @@ このアドレスには%sのみを送金してください。他のトークンを送信すると、取り返しのつかない損失が発生します。 %2$sネットワークの%1$sのみを送信してください ウォレットや取引所から資金を送金する + 報酬の送付先アドレス 参加する 紹介プログラムに関する情報を読み込めませんでした。しばらくしてからもう一度お試しください。 紹介プログラムに関する情報を読み込めませんでした。エラー コード: %s 。しばらくしてからもう一度お試しください。 @@ -1094,6 +1095,7 @@ 年率 ステーキングに参加することで得られる年間収益率。 APR + APY 報酬は毎日自動的にステーキング残高に蓄積されます。 利用可能 平均報酬率 @@ -1391,8 +1393,8 @@ Tangemアプリで誤ったカードまたはリングが選択されました Dappデータから取引を作成できませんでした。コード: %s 不明なエラーが発生しました。エラーコード: %d 。問題が解決しない場合は、お気軽にサポートにお問い合わせください。 - 複数の取引 - このプロセスを完了するには、Tangemデバイスを数回タップする必要があります。 + マルチパート取引 + 正常に処理するため、取引は複数に分割されます。完了するにはカードを数回タップする必要があります。 WalletConnectセッションが開かれていません おっと。セッションがありません。 WalletConnectセッションのペアリングに失敗しました: %1$s @@ -1404,8 +1406,8 @@ このカードは、WalletConnectセッションの確立には使用できません。 このネットワークはサポートされていません。別のネットワークを選択してください。 ネットワークを選択 - 取引を処理中です - 送金しています... + 取引を処理中です。完了するにはカードを複数回タップしてください。 + 取引進行中 WalletConnectセッション dAppsに接続する WalletConnect @@ -1659,6 +1661,7 @@ 承認を確定する 資産残高に関するテキスト [プレースホルダー] あなたの%sはAaveに預けられています + 受け取った金額%1$s %2$sはAaveに入金されませんでした。 %1$s%% を獲得 利用可能 現在のAPY @@ -1674,6 +1677,7 @@ ネットワーク手数料が上限手数料を超えた場合、手数料が下がるまで取引は成立しません。この制限は後で変更できます。 最大手数料 手数料ポリシー + ネットワーク手数料が現在高すぎます。設定した上限を下回るまで待機しています。 ここに説明を入力してください。1〜3行が理想的です。[プレースホルダー] トークン承認が必要 ネットワーク接続を確認してください diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 979bd1a061..47b4f344fa 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1043,6 +1043,7 @@ Процентная ставка Годовой процентный доход, который вы можете получить от участия в стейкинге. APR + APY Награда автоматически аккумулируется на вашем стейкинг балансе. Доступно Средння ставка вознаграждения diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 90e59315d2..6946a0d572 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -959,6 +959,7 @@ Процентна ставка Річний відсоток, який ви можете отримати, беручи участь у стейкінгу. APR + APY Винагороди автоматично накопичуються на вашому балансі щодня. Доступно Середня ставка винагороди diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 376b83e07c..94d0e6a553 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -33,6 +33,7 @@ Add account Save Account name + Account name already exists Account New account Add account @@ -905,6 +906,7 @@ %s min You get + Service is provided by an external provider. \nTangem is not responsible. You can close this screen and check the transaction status on the token details screen. Up to Via @@ -1116,7 +1118,10 @@ A staking account is a special account where staked SOL tokens are stored. It is created when you delegate your tokens to a validator to participate in transaction validation and receive rewards. A small fee is charged for creating the staking account, which is returned after the staking is completed. Annual percentage rate The annual percentage return you can earn from participating in staking. + Annual percentage yield + APY shows the validator’s annual percentage yield based on its performance APR + APY Rewards automatically accumulate in your staking balance daily. Available Average Reward Rate @@ -1314,6 +1319,7 @@ Token in %%image%% %1$s network The %1$s (%2$s) token is the main currency on the %3$s network and cannot be hidden as long as you have other tokens on this network in the list Unable to hide %s + Show QR code Exchange this token for another at %1$s service fees from February %2$s-%3$s. Swap with Changelly, %s fees Swap now @@ -1788,4 +1794,5 @@ Unable to cover %s fee The interest service isn’t available at the moment. Please try again later. Earnings unavailable + Unable to load chart... diff --git a/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt b/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt index c81e865d8e..2f848f2f16 100644 --- a/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt +++ b/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt @@ -127,7 +127,7 @@ data class AccountList private constructor( companion object { - private const val MAX_ACCOUNTS_COUNT = 20 + const val MAX_ACCOUNTS_COUNT = 20 private const val MAX_MAIN_ACCOUNTS_COUNT = 1 /** diff --git a/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt index 179973c048..9defeeadae 100644 --- a/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt +++ b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt @@ -1,6 +1,7 @@ package com.tangem.domain.appcurrency import arrow.core.Either +import arrow.core.getOrElse import arrow.core.left import arrow.core.right import com.tangem.domain.appcurrency.error.SelectedAppCurrencyError @@ -19,6 +20,10 @@ class GetSelectedAppCurrencyUseCase( .onEmpty { emit(SelectedAppCurrencyError.NoAppCurrencySelected.left()) } } + fun invokeOrDefault(): Flow = invoke() + .map { it.getOrElse { AppCurrency.Default } } + .distinctUntilChanged() + suspend fun invokeSync(): Either { return invoke().firstOrNull() ?: SelectedAppCurrencyError.NoAppCurrencySelected.left() } diff --git a/domain/balance-hiding/src/main/kotlin/com/tangem/domain/balancehiding/GetBalanceHidingSettingsUseCase.kt b/domain/balance-hiding/src/main/kotlin/com/tangem/domain/balancehiding/GetBalanceHidingSettingsUseCase.kt index 6f4c08e5fa..bfd742292f 100644 --- a/domain/balance-hiding/src/main/kotlin/com/tangem/domain/balancehiding/GetBalanceHidingSettingsUseCase.kt +++ b/domain/balance-hiding/src/main/kotlin/com/tangem/domain/balancehiding/GetBalanceHidingSettingsUseCase.kt @@ -2,6 +2,8 @@ package com.tangem.domain.balancehiding import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map class GetBalanceHidingSettingsUseCase( private val balanceHidingRepository: BalanceHidingRepository, @@ -10,4 +12,8 @@ class GetBalanceHidingSettingsUseCase( operator fun invoke(): Flow { return balanceHidingRepository.getBalanceHidingSettingsFlow() } + + fun isBalanceHidden(): Flow = invoke() + .map { it.isBalanceHidden } + .distinctUntilChanged() } \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt index 8a050693bf..85d4b0d97f 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt @@ -78,10 +78,14 @@ internal class AccountCreateEditModel @Inject constructor( isWarning = true, onClick = { router.pop() }, ) + val messageRes = when (params) { + is AccountCreateEditComponent.Params.Create -> R.string.account_unsaved_dialog_message_create + is AccountCreateEditComponent.Params.Edit -> R.string.account_unsaved_dialog_message_edit + } messageSender.send( DialogMessage( title = resourceReference(R.string.account_unsaved_dialog_title), - message = resourceReference(R.string.account_unsaved_dialog_message_create), + message = resourceReference(messageRes), firstActionBuilder = { firstAction }, secondActionBuilder = { secondAction }, ), @@ -120,17 +124,15 @@ internal class AccountCreateEditModel @Inject constructor( } private fun handleAddAccountError(error: AddCryptoPortfolioUseCase.Error) { - if (error is AddCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet && - error.cause is AccountList.Error.DuplicateAccountNames - ) { - // TODO("account") show alert that the account name already exists - return + val duplicateAccountNames = (error as? AddCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet) + ?.cause is AccountList.Error.DuplicateAccountNames + when { + duplicateAccountNames -> showAccountNameExist() + else -> { + showSomethingWrong() + logError(error = AccountFeatureError.CreateAccount.FailedToCreateAccount(cause = error)) + } } - - // TODO: show alert https://www.figma.com/design/09KKG4ZVuFDZhj8WLv5rGJ/%F0%9F%9A%A7-App-experience?node-id=38882-113775&t=vk6TCy4MkYol1cPb-4 - logError( - error = AccountFeatureError.CreateAccount.FailedToCreateAccount(cause = error), - ) } private suspend fun editCryptoPortfolio(params: AccountCreateEditComponent.Params.Edit) { @@ -157,17 +159,15 @@ internal class AccountCreateEditModel @Inject constructor( } private fun handleEditAccountError(error: UpdateCryptoPortfolioUseCase.Error) { - if (error is UpdateCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet && - error.cause is AccountList.Error.DuplicateAccountNames - ) { - // TODO("account") show alert that the account name already exists - return + val duplicateAccountNames = (error as? UpdateCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet) + ?.cause is AccountList.Error.DuplicateAccountNames + when { + duplicateAccountNames -> showAccountNameExist() + else -> { + showSomethingWrong() + logError(error = AccountFeatureError.EditAccount.FailedToEditAccount(cause = error)) + } } - - // TODO: show alert like in create account flow - logError( - error = AccountFeatureError.EditAccount.FailedToEditAccount(cause = error), - ) } private fun showMessage(@StringRes id: Int) { @@ -175,7 +175,10 @@ internal class AccountCreateEditModel @Inject constructor( messageSender.send(ToastMessage(message = message)) } - private fun onCloseClick() = unsaveChangeDialog() + private fun onCloseClick() { + val showConfirmDialog = uiState.value.buttonState.isButtonEnabled + if (showConfirmDialog) unsaveChangeDialog() else router.pop() + } private fun onIconSelect(icon: CryptoPortfolioIcon.Icon) { uiState.value = uiState.value @@ -256,4 +259,20 @@ internal class AccountCreateEditModel @Inject constructor( event = ExceptionAnalyticsEvent(exception = exception, params = params), ) } + + private fun showSomethingWrong() { + val dialogMessage = DialogMessage( + title = resourceReference(R.string.common_something_went_wrong), + message = resourceReference(R.string.account_could_not_create), + ) + messageSender.send(dialogMessage) + } + + private fun showAccountNameExist() { + val dialogMessage = DialogMessage( + title = resourceReference(R.string.common_something_went_wrong), + message = resourceReference(R.string.account_form_name_already_exist_error_description), + ) + messageSender.send(dialogMessage) + } } \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt index 0e92bc9233..2c7168bc93 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt @@ -19,6 +19,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -30,8 +32,8 @@ import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerH24 import com.tangem.core.ui.components.SpacerH8 -import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.components.account.AccountIconSize +import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.components.fields.AutoSizeTextField import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme @@ -43,9 +45,11 @@ import kotlinx.collections.immutable.toImmutableList @Composable internal fun AccountCreateEditContent(state: AccountCreateEditUM, modifier: Modifier = Modifier) { + val keyboardController = LocalSoftwareKeyboardController.current + val focusManager = LocalFocusManager.current Column( modifier = modifier - .background(color = TangemTheme.colors.background.tertiary) + .background(color = TangemTheme.colors.background.secondary) .fillMaxSize() .imePadding() .systemBarsPadding(), @@ -76,6 +80,10 @@ internal fun AccountCreateEditContent(state: AccountCreateEditUM, modifier: Modi color = TangemTheme.colors.text.tertiary, ) } + if (state.buttonState.showProgress) { + focusManager.clearFocus() + keyboardController?.hide() + } PrimaryButton( modifier = Modifier .fillMaxWidth() @@ -161,6 +169,7 @@ private fun AccountColor(colorsState: AccountCreateEditUM.Colors) { contentAlignment = Alignment.Center, modifier = Modifier .wrapContentSize() + .clip(CircleShape) .clickable(onClick = { colorsState.onColorSelect(color) }) .size(48.dp), ) { @@ -208,6 +217,7 @@ private fun AccountIcons(iconsState: AccountCreateEditUM.Icons) { contentAlignment = Alignment.Center, modifier = Modifier .wrapContentSize() + .clip(CircleShape) .clickable(onClick = { iconsState.onIconSelect(icon) }) .size(52.dp), ) { diff --git a/features/account/impl/src/main/java/com/tangem/features/account/fetcher/DefaultPortfolioFetcher.kt b/features/account/impl/src/main/java/com/tangem/features/account/fetcher/DefaultPortfolioFetcher.kt index 4456993f0e..e1ae9bce5c 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/fetcher/DefaultPortfolioFetcher.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/fetcher/DefaultPortfolioFetcher.kt @@ -1,11 +1,9 @@ package com.tangem.features.account.fetcher -import arrow.core.getOrElse import com.tangem.domain.account.models.AccountStatusList import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase -import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isMultiCurrency @@ -60,8 +58,8 @@ internal class DefaultPortfolioFetcher @AssistedInject constructor( .map { it.filterWallets(mode) } .distinctUntilChanged() .flatMapLatest { wallets -> balancesForWallets(wallets) }, - flow2 = appCurrencyFlow(), - flow3 = balanceHidingFlow(), + flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(), + flow3 = getBalanceHidingSettingsUseCase.isBalanceHidden(), ) { balances, appCurrency, isBalanceHiding -> Data( appCurrency = appCurrency, @@ -94,18 +92,6 @@ internal class DefaultPortfolioFetcher @AssistedInject constructor( private fun accountStatusListFlow(wallet: UserWallet): Flow = singleAccountStatusListSupplier(SingleAccountStatusListProducer.Params(wallet.walletId)) - private fun appCurrencyFlow(): Flow { - return getSelectedAppCurrencyUseCase() - .map { it.getOrElse { AppCurrency.Default } } - .distinctUntilChanged() - } - - private fun balanceHidingFlow(): Flow { - return getBalanceHidingSettingsUseCase() - .map { it.isBalanceHidden } - .distinctUntilChanged() - } - @AssistedFactory interface Factory : PortfolioFetcher.Factory { override fun create(mode: Mode, scope: CoroutineScope): DefaultPortfolioFetcher diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt index 950000e443..ff72822f09 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioDataLoader.kt @@ -1,8 +1,6 @@ package com.tangem.features.markets.portfolio.impl.loader -import arrow.core.getOrElse import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase -import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.core.lce.Lce import com.tangem.domain.models.TotalFiatBalance @@ -15,7 +13,6 @@ import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase import com.tangem.domain.tokens.error.TokenListError import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* -import timber.log.Timber import javax.inject.Inject /** @@ -41,8 +38,8 @@ internal class PortfolioDataLoader @Inject constructor( fun load(currencyRawId: CryptoCurrency.RawID): Flow { return combine( flow = getAllWalletsCryptoCurrenciesData(currencyRawId = currencyRawId), - flow2 = getSelectedAppCurrencyFlow(), - flow3 = getBalanceHidingSettingsFlow(), + flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(), + flow3 = getBalanceHidingSettingsUseCase.isBalanceHidden(), ) { walletsWithCurrencies, appCurrency, isBalanceHidden -> PortfolioData( walletsWithCurrencies = walletsWithCurrencies, @@ -113,23 +110,6 @@ internal class PortfolioDataLoader @Inject constructor( .distinctUntilChanged() } - private fun getSelectedAppCurrencyFlow(): Flow { - return getSelectedAppCurrencyUseCase() - .map { - it.getOrElse { e -> - Timber.e("Failed to load app currency: $e") - AppCurrency.Default - } - } - .distinctUntilChanged() - } - - private fun getBalanceHidingSettingsFlow(): Flow { - return getBalanceHidingSettingsUseCase() - .map { it.isBalanceHidden } - .distinctUntilChanged() - } - private fun getWalletsWithTotalBalanceFlow( ids: List, ): Flow>> { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt index 18f0ca746d..d62cce58ae 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt @@ -241,8 +241,8 @@ internal class AvailableSwapPairsModel @Inject constructor( private fun getAppCurrencyAndBalanceHidingFlow(): Flow> { return combine( - flow = getSelectedAppCurrencyUseCase().map { it.getOrElse { AppCurrency.Default } }.distinctUntilChanged(), - flow2 = getBalanceHidingSettingsUseCase().map { it.isBalanceHidden }.distinctUntilChanged(), + flow = getSelectedAppCurrencyUseCase.invokeOrDefault(), + flow2 = getBalanceHidingSettingsUseCase.isBalanceHidden(), transform = ::Pair, ) } diff --git a/features/wallet-settings/impl/build.gradle.kts b/features/wallet-settings/impl/build.gradle.kts index 3bb8904189..e34be56eb8 100644 --- a/features/wallet-settings/impl/build.gradle.kts +++ b/features/wallet-settings/impl/build.gradle.kts @@ -33,6 +33,11 @@ dependencies { implementation(projects.common.ui) /* Project - Domain */ + implementation(projects.domain.account.status) + implementation(projects.domain.appCurrency) + implementation(projects.domain.appCurrency.models) + implementation(projects.domain.balanceHiding) + implementation(projects.domain.balanceHiding.models) implementation(projects.domain.legacy) implementation(projects.domain.card) implementation(projects.domain.models) diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/preview/PreviewWalletSettingsComponent.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/preview/PreviewWalletSettingsComponent.kt index e716a35761..d359b824c4 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/preview/PreviewWalletSettingsComponent.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/preview/PreviewWalletSettingsComponent.kt @@ -3,6 +3,7 @@ package com.tangem.feature.walletsettings.component.preview import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.common.ui.account.AccountIconPreviewData +import com.tangem.common.ui.userwallet.state.UserWalletItemUM import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState import com.tangem.core.analytics.DummyAnalyticsEventHandler import com.tangem.core.decompose.navigation.DummyRouter @@ -20,9 +21,27 @@ import com.tangem.feature.walletsettings.impl.R import com.tangem.feature.walletsettings.ui.WalletSettingsScreen import com.tangem.feature.walletsettings.utils.ItemsBuilder import com.tangem.hot.sdk.model.HotWalletId +import java.util.UUID internal class PreviewWalletSettingsComponent : WalletSettingsComponent { + private val accountName get() = stringReference(value = "Main account") + + private val accountItem: UserWalletItemUM + get() = UserWalletItemUM( + id = UserWalletId(UUID.randomUUID().toString().encodeToByteArray()), + name = accountName, + information = UserWalletItemUM.Information.Loaded(stringReference("12 tokens")), + balance = UserWalletItemUM.Balance.Loaded("$726.04", false), + isEnabled = true, + onClick = { }, + imageState = ImageState.Account( + name = accountName, + icon = AccountIconPreviewData.randomAccountIcon(), + ), + label = null, + ) + private val previewState = WalletSettingsUM( popBack = {}, items = ItemsBuilder( @@ -66,14 +85,7 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent { id = "accounts_header", text = resourceReference(R.string.common_accounts), ).let(::add) - WalletSettingsAccountsUM.Account( - id = "accountId", - accountName = stringReference("Main account"), - accountIconUM = AccountIconPreviewData.randomAccountIcon(), - tokensInfo = stringReference("10 tokens"), - networksInfo = stringReference("2 networks"), - onClick = {}, - ).let(::add) + add(WalletSettingsAccountsUM.Account(accountItem)) WalletSettingsAccountsUM.Footer( id = "accounts_footer", addAccount = AddAccountUM( @@ -86,6 +98,7 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent { iconRes = R.drawable.ic_archive_24, onClick = {}, ), + showDescription = true, description = resourceReference(R.string.account_reorder_description), ).let(::add) } diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt index d2cb70fe36..fe8eafb722 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/entity/WalletSettingsItemUM.kt @@ -1,7 +1,7 @@ package com.tangem.feature.walletsettings.entity import androidx.compose.runtime.Immutable -import com.tangem.common.ui.account.CryptoPortfolioIconUM +import com.tangem.common.ui.userwallet.state.UserWalletItemUM import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState import com.tangem.core.ui.components.block.model.BlockUM import com.tangem.core.ui.extensions.TextReference @@ -65,18 +65,16 @@ internal sealed class WalletSettingsAccountsUM : WalletSettingsItemUM() { ) : WalletSettingsAccountsUM() data class Account( - override val id: String, - val accountName: TextReference, - val accountIconUM: CryptoPortfolioIconUM, - val tokensInfo: TextReference, - val networksInfo: TextReference, - val onClick: () -> Unit, - ) : WalletSettingsAccountsUM() + val state: UserWalletItemUM, + ) : WalletSettingsAccountsUM() { + override val id: String get() = state.id.stringValue + } data class Footer( override val id: String, val addAccount: AddAccountUM, val archivedAccounts: BlockUM, + val showDescription: Boolean, val description: TextReference, ) : WalletSettingsAccountsUM() { diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt index d3a36e9a94..e7864d0edc 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt @@ -38,6 +38,7 @@ import com.tangem.feature.walletsettings.analytics.WalletSettingsAnalyticEvents import com.tangem.feature.walletsettings.component.WalletSettingsComponent import com.tangem.feature.walletsettings.entity.DialogConfig import com.tangem.feature.walletsettings.entity.NetworksAvailableForNotificationBSConfig +import com.tangem.feature.walletsettings.entity.WalletSettingsAccountsUM import com.tangem.feature.walletsettings.entity.WalletSettingsItemUM import com.tangem.feature.walletsettings.entity.WalletSettingsUM import com.tangem.feature.walletsettings.impl.R @@ -123,7 +124,8 @@ internal class WalletSettingsModel @Inject constructor( getWalletNotificationsEnabledUseCase(params.userWalletId), isUpgradeWalletNotificationEnabledUseCase(params.userWalletId), walletCardItemDelegate.cardItemFlow(wallet), - ) { nftEnabled, notificationsEnabled, isUpgradeNotificationEnabled, cardItem -> + accountItemsDelegate.loadAccount(), + ) { nftEnabled, notificationsEnabled, isUpgradeNotificationEnabled, cardItem, accountList -> val isWalletBackedUp = when (wallet) { is UserWallet.Hot -> wallet.backedUp is UserWallet.Cold -> true @@ -138,6 +140,7 @@ internal class WalletSettingsModel @Inject constructor( isNotificationsFeatureEnabled = true, isNotificationsPermissionGranted = isNotificationsPermissionGranted(), isUpgradeNotificationEnabled = isUpgradeNotificationEnabled, + accountList = accountList, ), isWalletBackedUp = isWalletBackedUp, ) @@ -168,6 +171,7 @@ internal class WalletSettingsModel @Inject constructor( isNotificationsEnabled: Boolean, isNotificationsPermissionGranted: Boolean, isUpgradeNotificationEnabled: Boolean, + accountList: List, ): PersistentList { val isMultiCurrency = when (userWallet) { is UserWallet.Cold -> userWallet.isMultiCurrency @@ -219,7 +223,7 @@ internal class WalletSettingsModel @Inject constructor( walletUpgradeDismissed = isUpgradeNotificationEnabled, onUpgradeWalletClick = ::onUpgradeWalletClick, onDismissUpgradeWalletClick = ::onDismissUpgradeWalletClick, - accountsUM = with(accountItemsDelegate) { listOf() }, // todo account + accountsUM = accountList, ) } diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt index 68be89856a..7a01f6760f 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -22,9 +23,8 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.tangem.common.ui.account.AccountRow import com.tangem.common.ui.userwallet.CardImage -import com.tangem.core.ui.components.SpacerH +import com.tangem.common.ui.userwallet.UserWalletItem import com.tangem.core.ui.components.SpacerH8 import com.tangem.core.ui.components.TangemSwitch import com.tangem.core.ui.components.appbar.TangemTopAppBar @@ -292,7 +292,7 @@ private fun AccountsHeader(model: WalletSettingsAccountsUM.Header, modifier: Mod .padding( start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, - top = TangemTheme.dimens.spacing8, + top = TangemTheme.dimens.spacing12, bottom = TangemTheme.dimens.spacing4, ), text = model.text.resolveReference(), @@ -305,21 +305,10 @@ private fun AccountsHeader(model: WalletSettingsAccountsUM.Header, modifier: Mod @Composable private fun AccountItem(model: WalletSettingsAccountsUM.Account, modifier: Modifier = Modifier) { - val subtitle = stringResourceSafe( - id = R.string.account_label_tokens_info, - formatArgs = arrayOf( - model.tokensInfo.resolveReference(), - model.networksInfo.resolveReference(), - ), - ) - AccountRow( + UserWalletItem( + state = model.state, modifier = modifier - .background(color = TangemTheme.colors.background.primary) - .clickable(onClick = model.onClick) - .padding(12.dp), - title = model.accountName, - subtitle = stringReference(subtitle), - icon = model.accountIconUM, + .background(color = TangemTheme.colors.background.primary), ) } @@ -336,18 +325,19 @@ private fun AccountsFooter(model: WalletSettingsAccountsUM.Footer, modifier: Mod ), ) { AddAccountRow(model.addAccount) - SpacerH( - height = TangemTheme.dimens.size0_5, + HorizontalDivider( + thickness = TangemTheme.dimens.size0_5, modifier = Modifier .fillMaxWidth() - .padding(horizontal = TangemTheme.dimens.spacing12) - .background(TangemTheme.colors.stroke.primary), + .padding(horizontal = TangemTheme.dimens.spacing12), + color = TangemTheme.colors.stroke.primary, ) BlockItem( modifier = Modifier.fillMaxWidth(), model = model.archivedAccounts, ) } + if (!model.showDescription) return SpacerH8() Text( modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12), diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt index 0857416626..ed1a0254c5 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt @@ -1,30 +1,81 @@ package com.tangem.feature.walletsettings.utils import com.tangem.common.routing.AppRoute -import com.tangem.common.ui.account.toUM +import com.tangem.common.ui.account.AccountPortfolioItemUMConverter import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.components.block.model.BlockUM -import com.tangem.core.ui.extensions.pluralReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.EventMessageAction +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles +import com.tangem.domain.account.models.AccountList +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer +import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.models.account.Account +import com.tangem.domain.models.account.AccountStatus import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.feature.walletsettings.component.WalletSettingsComponent import com.tangem.feature.walletsettings.entity.WalletSettingsAccountsUM import com.tangem.feature.walletsettings.entity.WalletSettingsAccountsUM.Footer.AddAccountUM import com.tangem.feature.walletsettings.impl.R +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOf import javax.inject.Inject +@Suppress("LongParameterList") @ModelScoped internal class AccountItemsDelegate @Inject constructor( + paramsContainer: ParamsContainer, private val router: Router, private val messageSender: UiMessageSender, + private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier, + private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val accountsFeatureToggles: AccountsFeatureToggles, ) { + val userWalletId = paramsContainer.require().userWalletId - fun buildUiList(userWalletId: UserWalletId, accounts: List): List = buildList { + fun loadAccount(): Flow> { + if (!accountsFeatureToggles.isFeatureEnabled) return flowOf(emptyList()) + val params = SingleAccountStatusListProducer.Params(userWalletId) + return combine( + flow = singleAccountStatusListSupplier(params), + flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(), + flow3 = getBalanceHidingSettingsUseCase.isBalanceHidden(), + ) { accountStatusList, appCurrency, isBalanceHidden -> + buildUiList(accountStatusList, appCurrency, isBalanceHidden) + } + } + + private fun buildUiList( + accountStatusList: AccountStatusList, + appCurrency: AppCurrency, + isBalanceHidden: Boolean, + ): List = buildList { + fun AccountStatus.CryptoPortfolio.mapCryptoPortfolio(): WalletSettingsAccountsUM { + val accountItemUM = AccountPortfolioItemUMConverter( + onClick = { openAccountDetails(this.account) }, + appCurrency = appCurrency, + accountBalance = this.tokenList.totalFiatBalance, + isBalanceHidden = isBalanceHidden, + ).convert(account) + return WalletSettingsAccountsUM.Account(state = accountItemUM) + } + + fun mapAccount(account: AccountStatus): WalletSettingsAccountsUM = when (account) { + is AccountStatus.CryptoPortfolio -> account.mapCryptoPortfolio() + } + + val accounts = accountStatusList.accountStatuses WalletSettingsAccountsUM.Header( id = "accounts_header", text = resourceReference(R.string.common_accounts), @@ -32,7 +83,8 @@ internal class AccountItemsDelegate @Inject constructor( addAll(accounts.map(::mapAccount)) - val addAccountEnabled = true // todo account + val addAccountEnabled = accounts.size < AccountList.MAX_ACCOUNTS_COUNT + val showDescription = accounts.size > 1 WalletSettingsAccountsUM.Footer( id = "accounts_footer", addAccount = AddAccountUM( @@ -47,33 +99,11 @@ internal class AccountItemsDelegate @Inject constructor( iconRes = R.drawable.ic_archive_24, onClick = { openArchivedAccounts(userWalletId) }, ), + showDescription = showDescription, description = resourceReference(R.string.account_reorder_description), ).let(::add) } - private fun mapAccount(account: Account): WalletSettingsAccountsUM = when (account) { - is Account.CryptoPortfolio -> account.mapCryptoPortfolio() - } - - private fun Account.CryptoPortfolio.mapCryptoPortfolio(): WalletSettingsAccountsUM { - return WalletSettingsAccountsUM.Account( - id = accountId.value, - accountName = accountName.toUM().value, - accountIconUM = icon.toUM(), - tokensInfo = pluralReference( - R.plurals.common_tokens_count, - count = tokensCount, - formatArgs = wrappedList(tokensCount), - ), - networksInfo = pluralReference( - R.plurals.common_networks_count, - count = networksCount, - formatArgs = wrappedList(networksCount), - ), - onClick = { openAccountDetails(this) }, - ) - } - private fun openAccountDetails(account: Account) { router.push(AppRoute.AccountDetails(account)) } @@ -96,15 +126,10 @@ internal class AccountItemsDelegate @Inject constructor( title = resourceReference(R.string.account_add_limit_dialog_title), message = resourceReference( id = R.string.account_add_limit_dialog_description, - formatArgs = wrappedList(MAX_ACCOUNT_COUNT.toString()), + formatArgs = wrappedList(AccountList.MAX_ACCOUNTS_COUNT.toString()), ), firstActionBuilder = { firstAction }, ), ) } - - companion object { - // todo account use domain const? - private const val MAX_ACCOUNT_COUNT = 20 - } } \ No newline at end of file