Updated on 2026-08-14

This commit is contained in:
Tangem 2024-02-08 14:06:22 +03:00
parent a7b2af9832
commit 5d5e5038dd
12 changed files with 129 additions and 117 deletions

View file

@ -1,59 +0,0 @@
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

View file

@ -46,7 +46,7 @@ internal object WalletPreviewData {
content = TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
onRenameClick = { _, _ -> },
onRenameClick = { _ -> },
onDeleteClick = {},
cardCount = 1,
)
@ -57,7 +57,7 @@ internal object WalletPreviewData {
id = UserWalletId("321"),
title = "Wallet 1",
imageResId = R.drawable.ill_wallet2_cards3_120_106,
onRenameClick = { _, _ -> },
onRenameClick = { _ -> },
onDeleteClick = {},
)
}
@ -67,7 +67,7 @@ internal object WalletPreviewData {
id = UserWalletId("24"),
title = "Wallet 1",
imageResId = R.drawable.ill_wallet2_cards3_120_106,
onRenameClick = { _, _ -> },
onRenameClick = { _ -> },
onDeleteClick = {},
)
}

View file

@ -7,40 +7,60 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.feature.wallet.impl.R
@Immutable
internal sealed class WalletAlertState {
internal sealed interface WalletAlertState {
abstract val title: TextReference?
abstract val message: TextReference
open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
open val isWarningConfirmButton: Boolean = false
abstract val onConfirmClick: (() -> Unit)?
@Immutable
sealed class Basic : WalletAlertState {
abstract val title: TextReference?
abstract val message: TextReference
open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
open val isWarningConfirmButton: Boolean = false
abstract val onConfirmClick: (() -> Unit)?
}
@Immutable
sealed class TextInput : WalletAlertState {
abstract val title: TextReference
abstract val label: TextReference
open val text: String = ""
open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
abstract val onConfirmClick: (String) -> Unit
}
data class DefaultAlert(
override val title: TextReference,
override val message: TextReference,
override val onConfirmClick: (() -> Unit)?,
) : WalletAlertState()
) : Basic()
data class RemoveWalletAlert(override val onConfirmClick: (() -> Unit)?) : WalletAlertState() {
data class RenameWalletAlert(
override val text: String,
override val onConfirmClick: (String) -> Unit,
) : TextInput() {
override val title: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_title)
override val label: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_placeholder)
}
data class RemoveWalletAlert(override val onConfirmClick: (() -> Unit)?) : Basic() {
override val title: TextReference? = null
override val message: TextReference = resourceReference(id = R.string.user_wallet_list_delete_prompt)
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_delete)
override val isWarningConfirmButton: Boolean = true
}
object WrongCardIsScanned : WalletAlertState() {
object WrongCardIsScanned : Basic() {
override val title: TextReference = resourceReference(R.string.common_warning)
override val message: TextReference = resourceReference(R.string.error_wrong_wallet_tapped)
override val onConfirmClick: (() -> Unit)? = null
}
object RescanWallets : WalletAlertState() {
object RescanWallets : Basic() {
override val title: TextReference = resourceReference(R.string.common_attention)
override val message: TextReference = resourceReference(R.string.key_invalidated_warning_description)
override val onConfirmClick: (() -> Unit)? = null
}
object VisaBalancesInfo : WalletAlertState() {
object VisaBalancesInfo : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Available balance is actual funds available, considering pending transactions, " +
@ -49,7 +69,7 @@ internal sealed class WalletAlertState {
override val onConfirmClick: (() -> Unit)? = null
}
object VisaLimitsInfo : WalletAlertState() {
object VisaLimitsInfo : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Limits are needed to control costs, improve security, manage risk. " +

View file

@ -24,7 +24,7 @@ internal sealed interface WalletCardState {
val imageResId: Int?
/** Lambda be invoked when Rename button is clicked */
val onRenameClick: (UserWalletId, String) -> Unit
val onRenameClick: (UserWalletId) -> Unit
/** Lambda be invoked when Delete button is clicked */
val onDeleteClick: (UserWalletId) -> Unit
@ -46,7 +46,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId, String) -> Unit,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
val cardCount: Int?,
val balance: String,
@ -67,7 +67,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId, String) -> Unit,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
) : WalletCardState
@ -85,7 +85,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo? = defaultAdditionalInfo,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId, String) -> Unit,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
) : WalletCardState {
@ -109,7 +109,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo? = null,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId, String) -> Unit,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
) : WalletCardState

View file

@ -119,7 +119,7 @@ internal class WalletSkeletonStateConverter(
title = name,
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this),
imageResId = createImageResId(),
onRenameClick = clickIntents::onRenameClick,
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
)
}
@ -130,7 +130,7 @@ internal class WalletSkeletonStateConverter(
title = name,
additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null,
imageResId = createImageResId(),
onRenameClick = clickIntents::onRenameClick,
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
)
}

View file

@ -90,7 +90,7 @@ internal class InitializeWalletsTransformer(
title = name,
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this),
imageResId = WalletImageResolver.resolve(userWallet = this),
onRenameClick = clickIntents::onRenameClick,
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
)
}

View file

@ -84,7 +84,7 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
title = name,
additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null,
imageResId = WalletImageResolver.resolve(userWallet = this),
onRenameClick = clickIntents::onRenameClick,
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
)
}

View file

@ -1,20 +1,26 @@
package com.tangem.feature.wallet.presentation.wallet.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import com.tangem.core.ui.components.AdditionalTextInputDialogParams
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButton
import com.tangem.core.ui.components.TextInputDialog
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState
@Composable
internal fun WalletAlert(state: WalletAlertState, onDismiss: () -> Unit) {
DefaultAlert(state = state, onDismiss = onDismiss)
when (state) {
is WalletAlertState.Basic -> BasicAlert(state, onDismiss)
is WalletAlertState.TextInput -> TextInputAlert(state, onDismiss)
}
}
@Composable
private fun DefaultAlert(state: WalletAlertState, onDismiss: () -> Unit) {
private fun BasicAlert(state: WalletAlertState.Basic, onDismiss: () -> Unit) {
val confirmButton: DialogButton
val dismissButton: DialogButton?
@ -48,4 +54,28 @@ private fun DefaultAlert(state: WalletAlertState, onDismiss: () -> Unit) {
title = state.title?.resolveReference(),
dismissButton = dismissButton,
)
}
@Composable
private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> Unit) {
var value by remember { mutableStateOf(TextFieldValue(text = state.text)) }
TextInputDialog(
fieldValue = value,
confirmButton = DialogButton(
title = state.confirmButtonText.resolveReference(),
enabled = value.text.isNotEmpty() && value.text != state.text,
onClick = {
state.onConfirmClick(value.text)
onDismiss()
},
),
onDismissDialog = onDismiss,
onValueChange = { value = it },
title = state.title.resolveReference(),
dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss),
textFieldParams = AdditionalTextInputDialogParams(
label = state.label.resolveReference(),
),
)
}

View file

@ -4,8 +4,11 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.foundation.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.*
@ -31,13 +34,17 @@ import androidx.compose.ui.text.style.TextOverflow
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.*
import androidx.constraintlayout.compose.*
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintLayoutScope
import androidx.constraintlayout.compose.Dimension
import com.tangem.common.Strings
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.TangemDimens
@ -61,9 +68,8 @@ private const val HALF_OF_ITEM_WIDTH = 0.5
internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
@Suppress("DestructuringDeclarationWithTooManyEntries")
CardContainer(
name = state.title,
onDeleteClick = { state.onDeleteClick(state.id) },
onRenameClick = { state.onRenameClick(state.id, it) },
onRenameClick = { state.onRenameClick(state.id) },
isLockedState = state is WalletCardState.LockedContent,
modifier = modifier,
) { itemSize ->
@ -141,9 +147,8 @@ internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifi
@Composable
private fun CardContainer(
name: String,
onDeleteClick: () -> Unit,
onRenameClick: (String) -> Unit,
onRenameClick: () -> Unit,
isLockedState: Boolean,
modifier: Modifier = Modifier,
content: @Composable (ConstraintLayoutScope.(IntSize) -> Unit),
@ -196,8 +201,6 @@ private fun CardContainer(
}
}
var isRenameWalletDialogVisible by rememberSaveable { mutableStateOf(value = false) }
val itemHeight by remember(itemSize.height) {
mutableStateOf(value = with(density) { itemSize.height.toDp() })
}
@ -206,20 +209,9 @@ private fun CardContainer(
pressOffset = pressOffset,
itemHeight = itemHeight,
onDismissRequest = { isMenuVisible = false },
onShowRenameWalletDialogClick = { isRenameWalletDialogVisible = true },
onShowRenameWalletDialogClick = onRenameClick,
onDeleteClick = onDeleteClick,
)
if (isRenameWalletDialogVisible) {
RenameWalletDialogContent(
name = name,
onConfirm = {
onRenameClick(it)
isRenameWalletDialogVisible = false
},
onDismiss = { isRenameWalletDialogVisible = false },
)
}
}
@Suppress("LongParameterList")
@ -365,7 +357,7 @@ private fun AdditionalInfoText(text: TextReference) {
}
private fun Modifier.nonContentAdditionalInfoSize(dimens: TangemDimens): Modifier {
return size(width = dimens.size84, height = dimens.size16)
return this.size(width = dimens.size84, height = dimens.size16)
}
@Composable

View file

@ -41,7 +41,9 @@ internal interface WalletClickIntents {
fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onRenameClick(userWalletId: UserWalletId, name: String)
fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId)
fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String)
fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId)

View file

@ -958,9 +958,23 @@ internal class WalletViewModel @Inject constructor(
}
}
override fun onRenameClick(userWalletId: UserWalletId, name: String) {
override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) {
val state = uiState as? WalletState.ContentState ?: return
uiState = stateFactory.getStateAndTriggerEvent(
state = uiState,
event = WalletEvent.ShowAlert(
state = WalletAlertState.RenameWalletAlert(
text = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex].title,
onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) },
),
),
setUiState = { uiState = it },
)
}
override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) {
viewModelScope.launch(dispatchers.io) {
updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name) })
updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name = name) })
}
}

View file

@ -20,7 +20,9 @@ import javax.inject.Inject
internal interface WalletCardClickIntents {
fun onRenameClick(userWalletId: UserWalletId, name: String)
fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId)
fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String)
fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId)
@ -39,15 +41,28 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
) : BaseWalletClickIntents(), WalletCardClickIntents {
override fun onRenameClick(userWalletId: UserWalletId, name: String) {
override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) {
analyticsEventHandler.send(MainScreen.EditWalletTapped)
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.RenameWalletAlert(
text = stateHolder.getSelectedWallet().walletCardState.title,
onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) },
),
),
)
}
override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) {
viewModelScope.launch(dispatchers.main) {
updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name) })
updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name = name) })
}
}
override fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) {
analyticsEventHandler.send(MainScreen.DeleteWalletTapped)
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.RemoveWalletAlert(
@ -58,8 +73,6 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
}
override fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) {
analyticsEventHandler.send(MainScreen.DeleteWalletTapped)
viewModelScope.launch(dispatchers.main) {
walletScreenContentLoader.cancel(userWalletId)
deleteWalletUseCase(userWalletId)