From 544c243291b2ff16d5a54da91eeef3803104f7a1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 24 Aug 2023 16:19:54 +0800 Subject: [PATCH] Updated on 2026-08-14 --- .../ui/WalletSelectorBottomSheetFragment.kt | 24 ++-- .../components/RenameWalletDialogContent.kt | 69 ----------- .../wallets/RenameWalletDialogContent.kt | 59 +++++++++ .../presentation/common/WalletPreviewData.kt | 12 +- .../router/DefaultWalletRouter.kt | 4 + .../presentation/router/InnerWalletRouter.kt | 3 + .../state/components/WalletCardState.kt | 47 +++++-- .../WalletLoadedTokensListConverter.kt | 4 +- .../factory/WalletRefreshStateConverter.kt | 2 + ...letSingleCurrencyLoadedBalanceConverter.kt | 9 +- .../factory/WalletSkeletonStateConverter.kt | 4 +- .../state/factory/WalletStateFactory.kt | 6 +- .../wallet/ui/components/common/WalletCard.kt | 117 ++++++++++++++++-- .../utils/FiatBalanceToWalletCardConverter.kt | 11 +- .../wallet/utils/TokenListErrorConverter.kt | 23 +++- .../wallet/viewmodels/WalletViewModel.kt | 5 +- 16 files changed, 270 insertions(+), 129 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt index ecb5291d26..e631d96a83 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt @@ -7,29 +7,19 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.SnackbarHost import androidx.compose.material.SnackbarHostState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.fragment.app.viewModels import com.tangem.core.analytics.Analytics +import com.tangem.core.ui.components.wallets.RenameWalletDialogContent import com.tangem.core.ui.fragments.ComposeBottomSheetFragment import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.common.analytics.events.MyWallets import com.tangem.tap.features.details.ui.cardsettings.resolveReference -import com.tangem.tap.features.walletSelector.ui.components.BiometricsDisabledWarningContent -import com.tangem.tap.features.walletSelector.ui.components.BiometricsLockoutWarningContent -import com.tangem.tap.features.walletSelector.ui.components.KeyInvalidatedWarningContent -import com.tangem.tap.features.walletSelector.ui.components.RemoveWalletDialogContent -import com.tangem.tap.features.walletSelector.ui.components.RenameWalletDialogContent -import com.tangem.tap.features.walletSelector.ui.components.WalletSelectorScreenContent +import com.tangem.tap.features.walletSelector.ui.components.* import com.tangem.tap.features.walletSelector.ui.model.DialogModel import com.tangem.tap.features.walletSelector.ui.model.WarningModel import dagger.hilt.android.AndroidEntryPoint @@ -90,7 +80,13 @@ internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment RemoveWalletDialogContent(dialog) - is DialogModel.RenameWalletDialog -> RenameWalletDialogContent(dialog) + is DialogModel.RenameWalletDialog -> { + RenameWalletDialogContent( + name = dialog.currentName, + onConfirm = dialog.onConfirm, + onDismiss = dialog.onDismiss, + ) + } is WarningModel.BiometricsLockoutWarning -> BiometricsLockoutWarningContent(dialog) is WarningModel.KeyInvalidatedWarning -> KeyInvalidatedWarningContent(dialog) is WarningModel.BiometricsDisabledWarning -> BiometricsDisabledWarningContent(dialog) diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt deleted file mode 100644 index 7bc58f301a..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.AdditionalTextInputDialogParams -import com.tangem.core.ui.components.DialogButton -import com.tangem.core.ui.components.TextInputDialog -import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.walletSelector.ui.model.DialogModel -import com.tangem.wallet.R - -@Composable -internal fun RenameWalletDialogContent(dialog: DialogModel.RenameWalletDialog) { - var value by remember { - mutableStateOf(TextFieldValue(text = dialog.currentName)) - } - - TextInputDialog( - fieldValue = value, - confirmButton = DialogButton( - title = stringResource(id = R.string.common_ok), - enabled = value.text.isNotEmpty() && value.text != dialog.currentName, - onClick = { dialog.onConfirm(value.text) }, - ), - onDismissDialog = dialog.onDismiss, - onValueChange = { newValue -> - value = newValue - }, - title = stringResource(R.string.user_wallet_list_rename_popup_title), - dismissButton = DialogButton( - title = stringResource(id = R.string.common_cancel), - onClick = dialog.onDismiss, - ), - textFieldParams = AdditionalTextInputDialogParams( - label = stringResource(R.string.user_wallet_list_rename_popup_placeholder), - ), - ) -} - -// region Preview -@Composable -private fun RenameWalletDialogContentSample(modifier: Modifier = Modifier) { - Column( - modifier = modifier, - ) { - RenameWalletDialogContent(dialog = DialogModel.RenameWalletDialog("", {}, {})) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun RenameWalletDialogContentPreview_Light() { - TangemTheme { - RenameWalletDialogContentSample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun RenameWalletDialogContentPreview_Dark() { - TangemTheme(isDark = true) { - RenameWalletDialogContentSample() - } -} -// endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt new file mode 100644 index 0000000000..eabd0fbeb8 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt @@ -0,0 +1,59 @@ +package com.tangem.core.ui.components.wallets + +import androidx.compose.runtime.* +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.components.AdditionalTextInputDialogParams +import com.tangem.core.ui.components.DialogButton +import com.tangem.core.ui.components.TextInputDialog +import com.tangem.core.ui.res.TangemTheme + +/** + * Rename a wallet dialog + * + * @param name wallet name + * @param onConfirm lambda be invoked when Confirm button is clicked + * @param onDismiss lambda be invoked when dialog is dismissed + */ +@Composable +fun RenameWalletDialogContent(name: String, onConfirm: (newName: String) -> Unit, onDismiss: () -> Unit) { + var value by remember { mutableStateOf(TextFieldValue(text = name)) } + + TextInputDialog( + fieldValue = value, + confirmButton = DialogButton( + title = stringResource(id = R.string.common_ok), + enabled = value.text.isNotEmpty() && value.text != name, + onClick = { onConfirm(value.text) }, + ), + onDismissDialog = onDismiss, + onValueChange = { value = it }, + title = stringResource(R.string.user_wallet_list_rename_popup_title), + dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss), + textFieldParams = AdditionalTextInputDialogParams( + label = stringResource(R.string.user_wallet_list_rename_popup_placeholder), + ), + ) +} + +// region Preview + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun RenameWalletDialogContentPreview_Light() { + TangemTheme(isDark = false) { + RenameWalletDialogContent(name = "", onConfirm = {}, onDismiss = {}) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun RenameWalletDialogContentPreview_Dark() { + TangemTheme(isDark = true) { + RenameWalletDialogContent(name = "", onConfirm = {}, onDismiss = {}) + } +} + +// endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index f076a7996d..91d8ccbd5d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -35,7 +35,8 @@ internal object WalletPreviewData { balance = "8923,05 $", additionalInfo = TextReference.Str("3 cards • Seed phrase"), imageResId = R.drawable.ill_businessman_3d, - onClick = null, + onRenameClick = { _, _ -> }, + onDeleteClick = {}, ) } @@ -44,7 +45,8 @@ internal object WalletPreviewData { id = UserWalletId("321"), title = "Wallet 1", imageResId = R.drawable.ill_businessman_3d, - onClick = null, + onRenameClick = { _, _ -> }, + onDeleteClick = {}, ) } @@ -53,7 +55,8 @@ internal object WalletPreviewData { id = UserWalletId("42"), title = "Wallet 1", imageResId = R.drawable.ill_businessman_3d, - onClick = null, + onRenameClick = { _, _ -> }, + onDeleteClick = {}, ) } @@ -62,7 +65,8 @@ internal object WalletPreviewData { id = UserWalletId("24"), title = "Wallet 1", imageResId = R.drawable.ill_businessman_3d, - onClick = null, + onRenameClick = { _, _ -> }, + onDeleteClick = {}, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 84ca2ae72b..1aa417f71f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -113,6 +113,10 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation ) } + override fun openStoriesScreen() { + navigationStateHolder.navigate(action = NavigationAction.NavigateTo(screen = AppScreen.Home)) + } + private companion object { const val BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN = 2 } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 4a0d24bbaa..510e8034ef 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -44,4 +44,7 @@ internal interface InnerWalletRouter : WalletRouter { /** Open token details screen */ fun openTokenDetails(currency: CryptoCurrency) + + /** Open stories screen */ + fun openStoriesScreen() } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt index 4ac301eb5c..e25b851c21 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt @@ -22,8 +22,11 @@ internal sealed interface WalletCardState { @get:DrawableRes val imageResId: Int? - /** Lambda be invoked when card is clicked */ - val onClick: (() -> Unit)? + /** Lambda be invoked when Rename button is clicked */ + val onRenameClick: (UserWalletId, String) -> Unit + + /** Lambda be invoked when Delete button is clicked */ + val onDeleteClick: (UserWalletId) -> Unit /** * Wallet card content state @@ -32,7 +35,8 @@ internal sealed interface WalletCardState { * @property title wallet name * @property additionalInfo wallet additional info * @property imageResId wallet image resource id - * @property onClick lambda be invoked when wallet card is clicked + * @property onRenameClick lambda be invoked when Rename button is clicked + * @property onDeleteClick lambda be invoked when Delete button is clicked * @property balance wallet balance */ data class Content( @@ -40,7 +44,8 @@ internal sealed interface WalletCardState { override val title: String, override val additionalInfo: TextReference, override val imageResId: Int?, - override val onClick: (() -> Unit)? = null, + override val onRenameClick: (UserWalletId, String) -> Unit, + override val onDeleteClick: (UserWalletId) -> Unit, val balance: String, ) : WalletCardState @@ -51,14 +56,16 @@ internal sealed interface WalletCardState { * @property title wallet name * @property additionalInfo wallet additional info * @property imageResId wallet image resource id - * @property onClick lambda be invoked when wallet card is clicked + * @property onRenameClick lambda be invoked when Rename button is clicked + * @property onDeleteClick lambda be invoked when Delete button is clicked */ data class HiddenContent( override val id: UserWalletId, override val title: String, override val additionalInfo: TextReference = HIDDEN_BALANCE_TEXT, override val imageResId: Int?, - override val onClick: (() -> Unit)?, + override val onRenameClick: (UserWalletId, String) -> Unit, + override val onDeleteClick: (UserWalletId) -> Unit, ) : WalletCardState /** @@ -68,14 +75,16 @@ internal sealed interface WalletCardState { * @property title wallet name * @property additionalInfo wallet additional info * @property imageResId wallet image resource id - * @property onClick lambda be invoked when wallet card is clicked + * @property onRenameClick lambda be invoked when Rename button is clicked + * @property onDeleteClick lambda be invoked when Delete button is clicked */ data class LockedContent( override val id: UserWalletId, override val title: String, override val additionalInfo: TextReference? = null, override val imageResId: Int?, - override val onClick: (() -> Unit)?, + override val onRenameClick: (UserWalletId, String) -> Unit, + override val onDeleteClick: (UserWalletId) -> Unit, ) : WalletCardState /** @@ -85,14 +94,16 @@ internal sealed interface WalletCardState { * @property title wallet name * @property additionalInfo wallet additional info * @property imageResId wallet image resource id - * @property onClick lambda be invoked when wallet card is clicked + * @property onRenameClick lambda be invoked when Rename button is clicked + * @property onDeleteClick lambda be invoked when Delete button is clicked */ data class Error( override val id: UserWalletId, override val title: String, override val additionalInfo: TextReference = EMPTY_BALANCE_TEXT, override val imageResId: Int?, - override val onClick: (() -> Unit)?, + override val onRenameClick: (UserWalletId, String) -> Unit, + override val onDeleteClick: (UserWalletId) -> Unit, ) : WalletCardState /** @@ -102,16 +113,28 @@ internal sealed interface WalletCardState { * @property title wallet name * @property additionalInfo wallet additional info * @property imageResId wallet image resource id - * @property onClick lambda be invoked when wallet card is clicked + * @property onRenameClick lambda be invoked when Rename button is clicked + * @property onDeleteClick lambda be invoked when Delete button is clicked */ data class Loading( override val id: UserWalletId, override val title: String, override val additionalInfo: TextReference? = null, override val imageResId: Int?, - override val onClick: (() -> Unit)? = null, + override val onRenameClick: (UserWalletId, String) -> Unit, + override val onDeleteClick: (UserWalletId) -> Unit, ) : WalletCardState + fun copySealed(title: String = this.title): WalletCardState { + return when (this) { + is Content -> copy(title = title) + is Error -> copy(title = title) + is HiddenContent -> copy(title = title) + is Loading -> copy(title = title) + is LockedContent -> copy(title = title) + } + } + companion object { val HIDDEN_BALANCE_TEXT by lazy { TextReference.Str(value = "•••") } val EMPTY_BALANCE_TEXT by lazy { TextReference.Str(value = "—") } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt index 1f72ea4c23..a98b75e4f8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt @@ -31,7 +31,7 @@ internal class WalletLoadedTokensListConverter( cardTypeResolverProvider: Provider, currentWalletProvider: Provider, clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenListStateConverter = TokenListToWalletStateConverter( currentStateProvider = currentStateProvider, @@ -46,7 +46,7 @@ internal class WalletLoadedTokensListConverter( currentStateProvider = currentStateProvider, ) - override fun convert(value: LoadedTokensListModel): WalletMultiCurrencyState.Content { + override fun convert(value: LoadedTokensListModel): WalletState { return value.tokenListEither.fold( ifLeft = tokenListErrorStateConverter::convert, ifRight = { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt index abf72d36c2..faa6a40114 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt @@ -67,6 +67,8 @@ internal class WalletRefreshStateConverter( title = selectedWallet.title, additionalInfo = additionalInfo, imageResId = selectedWallet.imageResId, + onRenameClick = selectedWallet.onRenameClick, + onDeleteClick = selectedWallet.onDeleteClick, ), ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index 821fb20f9b..6e1cc58e52 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -95,7 +95,8 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( currencyAmount = status.amount, ), imageResId = selectedWallet.imageResId, - onClick = selectedWallet.onClick, + onRenameClick = selectedWallet.onRenameClick, + onDeleteClick = selectedWallet.onDeleteClick, balance = formatFiatAmount(status = status, appCurrency = appCurrencyProvider()), ) } @@ -104,7 +105,8 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( id = selectedWallet.id, title = selectedWallet.title, imageResId = selectedWallet.imageResId, - onClick = selectedWallet.onClick, + onRenameClick = selectedWallet.onRenameClick, + onDeleteClick = selectedWallet.onDeleteClick, ) } is CryptoCurrencyStatus.MissedDerivation, @@ -116,7 +118,8 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( id = selectedWallet.id, title = selectedWallet.title, imageResId = selectedWallet.imageResId, - onClick = selectedWallet.onClick, + onRenameClick = selectedWallet.onRenameClick, + onDeleteClick = selectedWallet.onDeleteClick, ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index 10026ccc62..a783f78001 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -97,7 +97,7 @@ internal class WalletSkeletonStateConverter( // If wallet is initialized, return it, otherwise return loading state if (initializedWallet !is WalletCardState.Loading) { - initializedWallet + initializedWallet.copySealed(title = wallet.name) } else { createWalletLoadingState(wallet) } @@ -118,6 +118,8 @@ internal class WalletSkeletonStateConverter( null }, imageResId = WalletImageResolver.resolve(cardTypesResolver = cardTypeResolver), + onRenameClick = clickIntents::onRenameClick, + onDeleteClick = clickIntents::onDeleteClick, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 200a79c740..52787d6a81 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -200,11 +200,12 @@ internal class WalletStateFactory( id = walletCardState.id, title = walletCardState.title, imageResId = walletCardState.imageResId, - onClick = walletCardState.onClick, additionalInfo = WalletAdditionalInfoFactory.resolve( cardTypesResolver = cardTypeResolver, wallet = currentWalletProvider(), ), + onRenameClick = walletCardState.onRenameClick, + onDeleteClick = walletCardState.onDeleteClick, ) } .toImmutableList(), @@ -227,7 +228,8 @@ internal class WalletStateFactory( id = walletCardState.id, title = walletCardState.title, imageResId = walletCardState.imageResId, - onClick = walletCardState.onClick, + onRenameClick = walletCardState.onRenameClick, + onDeleteClick = walletCardState.onDeleteClick, ) } .toImmutableList(), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index 3d0facde96..494e36cee2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -1,23 +1,38 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.Image -import androidx.compose.foundation.background +import androidx.compose.foundation.* +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Edit +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayoutScope @@ -25,8 +40,10 @@ import androidx.constraintlayout.compose.Dimension import com.tangem.core.ui.components.FontSizeRange import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.ResizableText +import com.tangem.core.ui.components.wallets.RenameWalletDialogContent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemDimens import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R @@ -44,7 +61,12 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCard @Composable internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) { @Suppress("DestructuringDeclarationWithTooManyEntries") - CardContainer(onClick = state.onClick, modifier = modifier) { + CardContainer( + name = state.title, + onDeleteClick = { state.onDeleteClick(state.id) }, + onRenameClick = { state.onRenameClick(state.id, it) }, + modifier = modifier, + ) { val (title, balance, additionalText, image) = createRefs() val contentVerticalMargin = TangemTheme.dimens.spacing12 @@ -92,16 +114,43 @@ internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) { @Composable private fun CardContainer( - onClick: (() -> Unit)?, + name: String, + onDeleteClick: () -> Unit, + onRenameClick: (String) -> Unit, modifier: Modifier = Modifier, - content: @Composable ConstraintLayoutScope.() -> Unit, + content: @Composable (ConstraintLayoutScope.() -> Unit), ) { + var isMenuVisible by rememberSaveable { mutableStateOf(value = false) } + var pressOffset by remember { mutableStateOf(value = DpOffset.Zero) } + var itemHeight by remember { mutableStateOf(value = 0.dp) } + + val density = LocalDensity.current + val interactionSource = remember { MutableInteractionSource() } + val haptic = LocalHapticFeedback.current + Surface( - modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size108), + modifier = modifier + .defaultMinSize(minHeight = TangemTheme.dimens.size108) + .onSizeChanged { itemHeight = with(density) { it.height.toDp() } } + .clip(shape = TangemTheme.shapes.roundedCornersXMedium) + .indication(interactionSource = interactionSource, indication = LocalIndication.current) + .pointerInput(true) { + detectTapGestures( + onLongPress = { + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + isMenuVisible = true + pressOffset = DpOffset(x = it.x.toDp(), y = it.y.toDp()) + }, + onPress = { + val press = PressInteraction.Press(it) + interactionSource.emit(press) + tryAwaitRelease() + interactionSource.emit(PressInteraction.Release(press)) + }, + ) + }, shape = TangemTheme.shapes.roundedCornersXMedium, color = TangemTheme.colors.background.primary, - onClick = onClick ?: {}, - enabled = onClick != null, ) { ConstraintLayout( modifier = Modifier @@ -111,6 +160,50 @@ private fun CardContainer( content() } } + + var isRenameWalletDialogVisible by rememberSaveable { mutableStateOf(value = false) } + + DropdownMenu( + expanded = isMenuVisible, + onDismissRequest = { isMenuVisible = false }, + modifier = Modifier.background(color = TangemTheme.colors.background.secondary), + offset = pressOffset.copy(y = pressOffset.y - itemHeight), + ) { + MenuItem( + textResId = R.string.common_rename, + imageVector = Icons.Outlined.Edit, + onClick = { + isMenuVisible = false + isRenameWalletDialogVisible = true + }, + ) + MenuItem(textResId = R.string.common_delete, imageVector = Icons.Outlined.Delete, onClick = onDeleteClick) + } + + if (isRenameWalletDialogVisible) { + RenameWalletDialogContent( + name = name, + onConfirm = { + onRenameClick(it) + isRenameWalletDialogVisible = false + }, + onDismiss = { isRenameWalletDialogVisible = false }, + ) + } +} + +@Composable +private fun MenuItem(@StringRes textResId: Int, imageVector: ImageVector, onClick: () -> Unit) { + DropdownMenuItem( + text = { Text(text = stringResource(id = textResId), style = TangemTheme.typography.subtitle2) }, + modifier = Modifier.background(color = TangemTheme.colors.background.secondary), + trailingIcon = { Icon(imageVector = imageVector, contentDescription = null) }, + onClick = onClick, + colors = MenuDefaults.itemColors( + textColor = TangemTheme.colors.text.primary1, + trailingIconColor = TangemColorPalette.Dark6, + ), + ) } @Composable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt index 621b82f941..05340d37ed 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt @@ -27,7 +27,7 @@ internal class FiatBalanceToWalletCardConverter( } private fun WalletCardState.toLoadingWalletCardState(): WalletCardState { - return WalletCardState.Loading(id, title, additionalInfo, imageResId, onClick) + return WalletCardState.Loading(id, title, additionalInfo, imageResId, onRenameClick, onDeleteClick) } private fun WalletCardState.toErrorWalletCardState(): WalletCardState { @@ -35,7 +35,8 @@ internal class FiatBalanceToWalletCardConverter( id = id, title = title, imageResId = imageResId, - onClick = onClick, + onDeleteClick = onDeleteClick, + onRenameClick = onRenameClick, additionalInfo = WalletAdditionalInfoFactory.resolve( cardTypesResolver = cardTypeResolverProvider(), wallet = currentWalletProvider(), @@ -50,7 +51,8 @@ internal class FiatBalanceToWalletCardConverter( title = currentState.title, additionalInfo = currentState.additionalInfo ?: WalletCardState.HIDDEN_BALANCE_TEXT, imageResId = currentState.imageResId, - onClick = currentState.onClick, + onRenameClick = currentState.onRenameClick, + onDeleteClick = currentState.onDeleteClick, ) } else { val appCurrency = appCurrencyProvider() @@ -63,7 +65,8 @@ internal class FiatBalanceToWalletCardConverter( wallet = currentWalletProvider(), ), imageResId = currentState.imageResId, - onClick = currentState.onClick, + onRenameClick = currentState.onRenameClick, + onDeleteClick = currentState.onDeleteClick, balance = formatFiatAmount( fiatAmount = this.amount, fiatCurrencyCode = appCurrency.code, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt index ac32cdb876..82709f83a7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.utils import com.tangem.common.Provider import com.tangem.domain.tokens.error.TokenListError import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.utils.converter.Converter @@ -10,11 +11,23 @@ import kotlinx.collections.immutable.persistentListOf internal class TokenListErrorConverter( private val currentStateProvider: Provider, -) : Converter { +) : Converter { - override fun convert(value: TokenListError): WalletMultiCurrencyState.Content { - return requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content).copy( - tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), - ) + override fun convert(value: TokenListError): WalletState { + return when (val state = currentStateProvider()) { + is WalletMultiCurrencyState.Content -> { + state.copy( + tokensListState = WalletTokensListState.Content( + items = persistentListOf(), + onOrganizeTokensClick = null, + ), + ) + } + is WalletMultiCurrencyState.Locked, + is WalletSingleCurrencyState.Content, + is WalletSingleCurrencyState.Locked, + is WalletState.Initial, + -> state + } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 551b652d6d..8e578615f1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -471,7 +471,10 @@ internal class WalletViewModel @Inject constructor( override fun onDeleteClick(userWalletId: UserWalletId) { viewModelScope.launch(dispatchers.io) { - deleteWalletUseCase(userWalletId) + val either = deleteWalletUseCase(userWalletId) + + val state = requireNotNull(uiState as? WalletState.ContentState) + if (state.walletsListConfig.wallets.size <= 1 && either.isRight()) router.openStoriesScreen() } }