From 59b4045b71933f13bef9662c67e4bee9bb68e9af Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 15 Apr 2022 00:19:31 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/tap/common/compose/Button.kt | 14 ++ .../com/tangem/tap/common/compose/Theme.kt | 17 ++ .../compose/AddCustomTokenScreen.kt | 49 ++--- .../domain/common/form/FieldsValidators.kt | 2 +- .../addCustomToken/redux/AddCustomTokenHub.kt | 205 ++++++++++++------ .../redux/AddCustomTokenState.kt | 16 +- 6 files changed, 205 insertions(+), 98 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/compose/Theme.kt diff --git a/app/src/main/java/com/tangem/tap/common/compose/Button.kt b/app/src/main/java/com/tangem/tap/common/compose/Button.kt index 992939febe..20f72a0a89 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/Button.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/Button.kt @@ -2,7 +2,9 @@ package com.tangem.tap.common.compose import androidx.compose.foundation.layout.* import androidx.compose.material.* +import androidx.compose.material.ripple.LocalRippleTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -83,6 +85,18 @@ fun PasteButton( } } +/** + * Used for disable ripple if button is enable = false + */ +@Composable +fun ToggledRippleTheme( + isEnabled: Boolean, + content: @Composable () -> Unit, +) { + val theme = LocalRippleTheme provides if (isEnabled) LocalRippleTheme.current else NoRippleTheme() + CompositionLocalProvider(theme) { content() } +} + @Preview @Composable fun ButtonTest() { diff --git a/app/src/main/java/com/tangem/tap/common/compose/Theme.kt b/app/src/main/java/com/tangem/tap/common/compose/Theme.kt new file mode 100644 index 0000000000..f0afe076ba --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/compose/Theme.kt @@ -0,0 +1,17 @@ +package com.tangem.tap.common.compose + +import androidx.compose.material.ripple.RippleAlpha +import androidx.compose.material.ripple.RippleTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +/** +[REDACTED_AUTHOR] + */ +class NoRippleTheme : RippleTheme { + @Composable + override fun defaultColor() = Color.Unspecified + + @Composable + override fun rippleAlpha(): RippleAlpha = RippleAlpha(0.0f, 0.0f, 0.0f, 0.0f) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt index ad66229ed4..83641420f3 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt @@ -25,6 +25,7 @@ import com.tangem.domain.features.addCustomToken.redux.ScreenState import com.tangem.domain.features.addCustomToken.redux.ViewStates import com.tangem.domain.redux.domainStore import com.tangem.tap.common.compose.ComposeDialogManager +import com.tangem.tap.common.compose.ToggledRippleTheme import com.tangem.tap.common.compose.keyboardAsState import com.tangem.tap.features.tokens.addCustomToken.DomainErrorConverter import com.tangem.wallet.R @@ -107,7 +108,7 @@ fun Warnings(warnings: List) { Column { warnings.forEachIndexed { index, item -> val modifier = when (index) { - 0 -> Modifier.padding(16.dp, 16.dp, 16.dp, 0.dp) + 0 -> Modifier.padding(16.dp, 0.dp, 16.dp, 0.dp) warnings.lastIndex -> Modifier.padding(16.dp, 8.dp, 16.dp, 16.dp) else -> Modifier.padding(16.dp, 8.dp, 16.dp, 0.dp) } @@ -120,7 +121,7 @@ fun Warnings(warnings: List) { Text( modifier = Modifier.padding(16.dp), text = warningConverter.convertError(item), - color = colorResource(id = R.color.lightGray0), + color = colorResource(id = R.color.white), fontSize = 14.sp ) } @@ -138,36 +139,34 @@ private fun AddButton(state: MutableState) { } @Composable -fun AddCustomTokenFab( +private fun AddCustomTokenFab( modifier: Modifier = Modifier, isEnabled: Boolean = true, onClick: () -> Unit ) { - val contentColor = if (isEnabled) { - Color.White + val contentColor = Color.White + val backgroundColor = if (isEnabled) { + Color(0xFF1ACE80) } else { - colorResource(id = R.color.darkGray1) + Color(0xFFB9E6D3) } - val backgroundColor = Color(0xFF1ACE80) - ExtendedFloatingActionButton( - modifier = modifier, - icon = { - Icon( - imageVector = Icons.Filled.Add, - tint = contentColor, - contentDescription = "Add", - ) - }, - text = { - Text( - text = stringResource(id = R.string.common_add), - ) - }, - onClick = onClick, - backgroundColor = backgroundColor, - contentColor = contentColor, - ) + ToggledRippleTheme(isEnabled) { + ExtendedFloatingActionButton( + modifier = modifier, + icon = { + Icon( + imageVector = Icons.Filled.Add, + tint = contentColor, + contentDescription = "Add", + ) + }, + text = { Text(text = stringResource(id = R.string.common_add)) }, + onClick = { if (isEnabled) onClick() }, + backgroundColor = backgroundColor, + contentColor = contentColor, + ) + } } data class ScreenFieldData( diff --git a/domain/src/main/java/com/tangem/domain/common/form/FieldsValidators.kt b/domain/src/main/java/com/tangem/domain/common/form/FieldsValidators.kt index 83ba44c309..712fa36bd3 100644 --- a/domain/src/main/java/com/tangem/domain/common/form/FieldsValidators.kt +++ b/domain/src/main/java/com/tangem/domain/common/form/FieldsValidators.kt @@ -26,7 +26,7 @@ class StringIsNotEmptyValidator : CustomTokenValidator() { class TokenContractAddressValidator : CustomTokenValidator() { override fun validate(data: String?): AddCustomTokenError? { - if (data == null || data.isEmpty()) return null + if (data == null || data.isEmpty()) return AddCustomTokenError.FieldIsEmpty return if (EthereumAddressService().validate(data)) { null diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt index 3e0ed80316..de6bb51b6e 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt @@ -2,12 +2,10 @@ package com.tangem.domain.features.addCustomToken.redux import android.webkit.ValueCallback import com.tangem.blockchain.common.Blockchain -import com.tangem.common.extensions.guard -import com.tangem.common.extensions.toHexString +import com.tangem.blockchain.common.DerivationStyle import com.tangem.common.services.Result import com.tangem.domain.DomainDialog import com.tangem.domain.DomainException -import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.form.* @@ -41,6 +39,17 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT return storeState.copy(addCustomTokensState = newHubState) } + private val contractAddressValidator: TokenContractAddressValidator + get() = hubState.getValidator(ContractAddress) + private val nameValidator: TokenNameValidator + get() = hubState.getValidator(Name) + private val symbolValidator: TokenSymbolValidator + get() = hubState.getValidator(Symbol) + private val decimalsValidator: TokenDecimalsValidator + get() = hubState.getValidator(Decimals) + val networkValidator: TokenNetworkValidator + get() = hubState.getValidator(Network) + override suspend fun handleAction( action: Action, storeState: DomainState, @@ -52,76 +61,70 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT is Init.SetAddedCurrencies -> {} is Init.SetOnAddTokenCallback -> {} is OnCreate -> { - hubState.addedCurrencies.guard { - return throwUnAppropriateInitialization("addedTokens") - } +// hubState.addedCurrencies.guard { +// return throwUnAppropriateInitialization("addedTokens") +// } } is OnDestroy -> hubScope.cancel() is OnTokenContractAddressChanged -> { - dispatchOnMain( - Screen.UpdateAddButton( - ViewStates.AddButton(!hubState.allFieldsIsEmpty()) - ) - ) - val contractAddress = action.contractAddress.value - val validator: TokenContractAddressValidator = hubState.getValidator(ContractAddress) - val error = validator.validate(contractAddress) - addOrRemoveError(ContractAddress, error) - - if (error != null || contractAddress.isEmpty()) { - dispatchOnMain(unlockTokenFields()) - return + val address = action.contractAddress.value + when (val error = ContractAddress.validate(address)) { + null -> { + ContractAddress.removeError() + dispatchOnMain(unlockTokenFieldsAction()) + } + AddCustomTokenError.FieldIsEmpty -> { + ContractAddress.removeError() + dispatchOnMain(lockTokenFieldsAction()) + return + } + AddCustomTokenError.InvalidContractAddress -> { + ContractAddress.addError(error) + dispatchOnMain(unlockTokenFieldsAction()) + return + } + else -> {} } + if (!action.contractAddress.isUserInput) return - manageTokenChanges(requestInfoAboutContractAddress(contractAddress)) + manageTokenChanges(requestInfoAboutContractAddress(address)) } is OnTokenNetworkChanged -> { if (!action.blockchainNetwork.isUserInput) return - val contractAddress = hubState.getField(ContractAddress).data.value - manageTokenChanges(requestInfoAboutContractAddress(contractAddress)) + val contractAddress = ContractAddress.getFieldValue() + val error = ContractAddress.validate(contractAddress) + if (error == null) { + manageTokenChanges(requestInfoAboutContractAddress(contractAddress)) + } else { + + } } is OnTokenNameChanged -> { - val validator: TokenNameValidator = hubState.getValidator(Name) - addOrRemoveError(Name, validator.validate(action.tokenName.value)) + Name.addOrRemoveError(Name.validate(action.tokenName.value)) } is OnTokenSymbolChanged -> { - val validator: TokenSymbolValidator = hubState.getValidator(Symbol) - addOrRemoveError(Symbol, validator.validate(action.tokenSymbol.value)) + Symbol.addOrRemoveError(Symbol.validate(action.tokenSymbol.value)) } is OnTokenDecimalsChanged -> { - val validator: TokenDecimalsValidator = hubState.getValidator(Decimals) - addOrRemoveError(Decimals, validator.validate(action.tokenDecimals.value)) + Decimals.addOrRemoveError(Decimals.validate(action.tokenDecimals.value)) } -// is OnTokenDerivationPathChanged -> { -// val validator: TokenDerivationPathValidator = getValidator(DerivationPath, hubState) -// addOrRemoveError(DerivationPath, validator.validate(action.value.value)) -// } is ClearTokenFields -> { - val nameField = hubState.getField(Name) - val symbolField = hubState.getField(Symbol) - val decimalsField = hubState.getField(Decimals) - - nameField.data = Field.Data("", false) - symbolField.data = Field.Data("", false) - decimalsField.data = Field.Data("", false) - + Name.setFieldValue(Field.Data("", false)) + Symbol.setFieldValue(Field.Data("", false)) + Decimals.setFieldValue(Field.Data("", false)) dispatchOnMain(UpdateForm(hubState)) } is FillTokenFields -> { - val networkField = hubState.getField(Network) - val nameField = hubState.getField(Name) - val symbolField = hubState.getField(Symbol) - val decimalsField = hubState.getField(Decimals) - val token = action.token val contract = action.contract val blockchain = Blockchain.fromNetworkId(contract.networkId) ?: Blockchain.Unknown - networkField.data = Field.Data(blockchain, false) - nameField.data = Field.Data(token.name, false) - symbolField.data = Field.Data(token.symbol, false) - decimalsField.data = Field.Data(contract.decimalCount.toString(), false) + + Network.setFieldValue(Field.Data(blockchain, false)) + Name.setFieldValue(Field.Data(token.name, false)) + Symbol.setFieldValue(Field.Data(token.symbol, false)) + Decimals.setFieldValue(Field.Data(contract.decimalCount.toString(), false)) dispatchOnMain(UpdateForm(hubState)) } @@ -129,7 +132,7 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT // if (hubState.allFieldsIsEmpty()) { dispatchOnMain( DomainGlobalAction.ShowDialog(DomainDialog.DialogError( - AddCustomTokenError.FieldIsEmpty + AddCustomTokenError.InvalidDerivationPath ))) return // } @@ -185,7 +188,7 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT toAddWarnings.add(AddCustomTokenWarning.PotentialScamToken) toRemoveWarnings.add(AddCustomTokenWarning.TokenAlreadyAdded) dispatchOnMain(ClearTokenFields) - dispatchOnMain(unlockTokenFields()) + dispatchOnMain(unlockTokenFieldsAction()) } else -> { val token = foundTokens[0] @@ -204,7 +207,7 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT toRemoveWarnings.add(AddCustomTokenWarning.PotentialScamToken) dispatchOnMain(Screen.UpdateAddButton(ViewStates.AddButton(false))) - dispatchOnMain(lockTokenFields()) + dispatchOnMain(lockTokenFieldsAction()) } else { toRemoveWarnings.add(AddCustomTokenWarning.TokenAlreadyAdded) dispatchOnMain(Screen.UpdateAddButton(ViewStates.AddButton(true))) @@ -214,11 +217,11 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT if (tokenContract.active && isStandardDerivation) { toRemoveWarnings.add(AddCustomTokenWarning.PotentialScamToken) dispatchOnMain(FillTokenFields(token, contract)) - dispatchOnMain(lockTokenFields()) + dispatchOnMain(lockTokenFieldsAction()) } else { toAddWarnings.add(AddCustomTokenWarning.PotentialScamToken) dispatchOnMain(ClearTokenFields) - dispatchOnMain(unlockTokenFields()) + dispatchOnMain(unlockTokenFieldsAction()) } } } @@ -236,7 +239,7 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT hubScope.launch { // find how to connect to the upper coroutineContext and dispatch through them dispatchOnMain(FillTokenFields(token, selectedContract)) - dispatchOnMain(lockTokenFields()) + dispatchOnMain(lockTokenFieldsAction()) } }, ) @@ -256,15 +259,69 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT contract: Coins.CheckAddressResponse.Token.Contract ): Boolean = false - private suspend fun addOrRemoveError(id: CustomTokenFieldId, error: AddCustomTokenError?) { - if (error == null) { - dispatchOnMain(FieldError.Remove(id)) - } else { - dispatchOnMain(FieldError.Add(id, error)) + private suspend fun CustomTokenFieldId.addError(error: AddCustomTokenError) { + dispatchOnMain(FieldError.Add(this, error)) + } + + private suspend fun CustomTokenFieldId.removeError() { + dispatchOnMain(FieldError.Remove(this)) + } + + private suspend fun CustomTokenFieldId.addOrRemoveError(error: AddCustomTokenError?) { + when (error) { + null -> removeError() + else -> addError(error) } } - private fun lockTokenFields(): Action { + private inline fun CustomTokenFieldId.getField(): T { + val state = hubState + val value = when (this) { + ContractAddress -> state.getField(this) + Network -> state.getField(this) + Name -> state.getField(this) + Symbol -> state.getField(this) + Decimals -> state.getField(this) + DerivationPath -> state.getField(this) + } + return value as T + } + + private inline fun CustomTokenFieldId.getFieldValue(): T { + val value = when (this) { + ContractAddress -> getField().data.value + Network -> getField().data.value + Name -> getField().data.value + Symbol -> getField().data.value + Decimals -> getField().data.value + DerivationPath -> getField().data.value + } + return value as T + } + + private fun CustomTokenFieldId.setFieldValue(fieldData: Field.Data<*>) { + when (this) { + ContractAddress -> getField().data = fieldData as Field.Data + Network -> getField().data = fieldData as Field.Data + Name -> getField().data = fieldData as Field.Data + Symbol -> getField().data = fieldData as Field.Data + Decimals -> getField().data = fieldData as Field.Data + DerivationPath -> getField().data = fieldData as Field.Data + } + } + + private fun CustomTokenFieldId.validate(value: Any): AddCustomTokenError? { + return when (this) { + ContractAddress -> contractAddressValidator.validate(value as String) + Network -> networkValidator.validate(value as Blockchain) + Name -> nameValidator.validate(value as String) + Symbol -> symbolValidator.validate(value as String) + Decimals -> decimalsValidator.validate(value as String) + DerivationPath -> networkValidator.validate(value as Blockchain) + } + } + + private fun lockTokenFieldsAction(): Action { val state = hubState return Screen.UpdateTokenFields(listOf( Network to state.screenState.network.copy(isEnabled = false), @@ -274,7 +331,7 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT )) } - private fun unlockTokenFields(): Action { + private fun unlockTokenFieldsAction(): Action { val state = hubState return Screen.UpdateTokenFields(listOf( Network to state.screenState.network.copy(isEnabled = true), @@ -284,6 +341,19 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT )) } + private suspend fun toggleAddButtonAction(enable: Boolean) = when (enable) { + true -> unlockAddButtonAction() + else -> lockAddButtonAction() + } + + private suspend fun lockAddButtonAction() { + dispatchOnMain(Screen.UpdateAddButton(ViewStates.AddButton(false))) + } + + private suspend fun unlockAddButtonAction() { + dispatchOnMain(Screen.UpdateAddButton(ViewStates.AddButton(true))) + } + override fun reduceAction(action: Action, state: AddCustomTokenState): AddCustomTokenState { return when (action) { is Init.SetAddedCurrencies -> { @@ -293,11 +363,12 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT state.copy(onTokenAddCallback = action.callback) } is OnCreate -> { - val card = requireNotNull(globalState.scanResponse?.card) +// val card = requireNotNull(globalState.scanResponse?.card) val tangemTechServiceManager = TangemTechServiceManager(TangemTechService()) - tangemTechServiceManager.attachAuthKey(card.cardPublicKey.toHexString()) +// tangemTechServiceManager.attachAuthKey(card.cardPublicKey.toHexString()) state.copy( - derivationStyle = card.derivationStyle, +// derivationStyle = card.derivationStyle, + derivationStyle = DerivationStyle.LEGACY, tangemTechServiceManager = tangemTechServiceManager ) } @@ -435,4 +506,8 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT "AddCustomTokenHub", "$objName must be not NULL" ) } + + companion object { + + } } \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt index 13acabb71e..c919b4cc97 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt @@ -73,12 +73,14 @@ data class AddCustomTokenState( // val errors = mutableMapOf<>() fieldsToCheck.forEach { field -> val error = validator.validate(field.data.value?.toString()) - if (error != null) return true + if (error != null) return false } - return false + return true } - fun allFieldsIsEmpty(): Boolean = networkIsEmpty() && customTokensFieldsIsEmpty() + fun allFieldsIsEmpty(): Boolean { + return networkIsEmpty() && customTokensFieldsIsEmpty() + } private fun getToken(): CompleteData.CustomToken { return CompleteData.CustomToken.Converter(tokenId) @@ -138,11 +140,11 @@ data class AddCustomTokenState( return ScreenState( contractAddressField = ViewStates.TokenField(), network = ViewStates.TokenField(), - name = ViewStates.TokenField(), - symbol = ViewStates.TokenField(), - decimals = ViewStates.TokenField(), + name = ViewStates.TokenField(isEnabled = false), + symbol = ViewStates.TokenField(isEnabled = false), + decimals = ViewStates.TokenField(isEnabled = false), derivationPath = ViewStates.TokenField(), - addButton = ViewStates.AddButton() + addButton = ViewStates.AddButton(isEnabled = false) ) } }