diff --git a/app/src/main/java/com/tangem/tap/common/compose/BlockchainSpinner.kt b/app/src/main/java/com/tangem/tap/common/compose/BlockchainSpinner.kt new file mode 100644 index 0000000000..5a67842827 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/compose/BlockchainSpinner.kt @@ -0,0 +1,31 @@ +package com.tangem.tap.common.compose + +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.common.form.Field + +@Composable +fun BlockchainSpinner( + @StringRes title: Int, + itemList: List, + selectedItem: Field.Data, + isEnabled: Boolean = true, + textFieldConverter: (Blockchain) -> String, + dropdownItemView: @Composable ((Blockchain) -> Unit)? = null, + onItemSelected: (Blockchain) -> Unit, +) { + OutlinedSpinner( + modifier = Modifier.fillMaxWidth(), + label = stringResource(id = title), + itemList = itemList, + selectedItem = selectedItem, + textFieldConverter = textFieldConverter, + dropdownItemView = dropdownItemView, + isEnabled = isEnabled, + onItemSelected = onItemSelected + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/ComposeDialogManager.kt b/app/src/main/java/com/tangem/tap/common/compose/ComposeDialogManager.kt index d1f540e7e1..80cb2936ec 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/ComposeDialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/ComposeDialogManager.kt @@ -4,8 +4,8 @@ import androidx.compose.foundation.clickable 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.material.LocalTextStyle +import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.* @@ -22,6 +22,7 @@ import com.tangem.domain.DomainStateDialog import com.tangem.domain.redux.domainStore import com.tangem.domain.redux.global.DomainGlobalAction import com.tangem.domain.redux.global.DomainGlobalState +import com.tangem.tap.features.tokens.addCustomToken.compose.SelectTokenNetworkDialog import org.rekotlin.StoreSubscriber @Composable @@ -56,15 +57,7 @@ fun ShowTheDialog(dialogState: MutableState) { val onDismissRequest = { domainStore.dispatch(DomainGlobalAction.ShowDialog(null)) } when (val dialog = dialogState.value) { - is DomainDialog.SelectTokenDialog -> { - SimpleDialog( - title = "Select a token", - items = dialog.items, - itemNameConverter = dialog.itemNameConverter, - onSelect = dialog.onSelect, - onDismissRequest = onDismissRequest - ) - } + is DomainDialog.SelectTokenDialog -> SelectTokenNetworkDialog(dialog, onDismissRequest) } } @@ -75,9 +68,9 @@ fun ShowTheDialog(dialogState: MutableState) { fun SimpleDialog( title: String, items: List, - itemNameConverter: (T) -> String, onSelect: (T) -> Unit, - onDismissRequest: () -> Unit + onDismissRequest: () -> Unit, + itemContent: @Composable (T) -> Unit, ) { Dialog( properties = DialogProperties(false, false), @@ -85,7 +78,7 @@ fun SimpleDialog( ) { Surface( modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(8.dp) + shape = MaterialTheme.shapes.medium ) { Column( modifier = Modifier.padding(22.dp) @@ -112,11 +105,7 @@ fun SimpleDialog( onDismissRequest() }, verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = itemNameConverter(item), - ) - } + ) { itemContent(item) } } } } diff --git a/app/src/main/java/com/tangem/tap/common/compose/OutlinedSpinner.kt b/app/src/main/java/com/tangem/tap/common/compose/OutlinedSpinner.kt index ef87477734..b7eb414062 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/OutlinedSpinner.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/OutlinedSpinner.kt @@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.VoidCallback @@ -14,17 +16,16 @@ import com.tangem.tap.common.extensions.ValueCallback /** [REDACTED_AUTHOR] */ -private class OutlinedSpinner - @OptIn(ExperimentalMaterialApi::class) @Composable fun OutlinedSpinner( modifier: Modifier = Modifier, - title: String, + label: String, itemList: List, selectedItem: Field.Data, onItemSelected: ValueCallback, - itemNameConverter: (T) -> String = { it.toString() }, + textFieldConverter: (T) -> String = { it.toString() }, + dropdownItemView: @Composable ((T) -> Unit)? = null, isEnabled: Boolean = true, onClose: VoidCallback = {} ) { @@ -48,24 +49,27 @@ fun OutlinedSpinner( expanded = rIsExpanded.value, onExpandedChange = { rIsExpanded.value = !rIsExpanded.value }, ) { - OutlinedTextField( - modifier = modifier, - readOnly = true, - enabled = isEnabled, - value = itemNameConverter(rSelectedItem.value), - onValueChange = {}, - label = { Text(title) }, - trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = rIsExpanded.value) }, - ) + ProvideTextStyle(value = TextStyle(color = Color.Blue)) { + OutlinedTextField( + modifier = modifier, + readOnly = true, + enabled = isEnabled, + value = textFieldConverter(rSelectedItem.value), + onValueChange = {}, + label = { Text(label) }, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = rIsExpanded.value) }, + ) + } ExposedDropdownMenu( expanded = rIsExpanded.value, onDismissRequest = onDismissRequest, ) { itemList.forEach { item -> - DropdownMenuItem( - onClick = { onItemSelectedInternal(item) } - ) { - Text(itemNameConverter(item)) + DropdownMenuItem(onClick = { onItemSelectedInternal(item) }) { + when (dropdownItemView) { + null -> Text(textFieldConverter(item)) + else -> dropdownItemView(item) + } } } } @@ -77,7 +81,7 @@ fun OutlinedSpinner( fun TestSpinnerPreview() { Scaffold() { OutlinedSpinner( - title = "Blockchain name", + label = "Blockchain name", itemList = listOf(Blockchain.values()), selectedItem = Field.Data(Blockchain.Avalanche), onItemSelected = {}, diff --git a/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt b/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt index b49579609d..d0819b0817 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt @@ -28,8 +28,6 @@ import com.tangem.tap.common.compose.extensions.stringResourceDefault /** [REDACTED_AUTHOR] */ -private class OutlinedTextFieldWidget - @Composable fun OutlinedTextFieldWidget( modifier: Modifier = Modifier, @@ -90,7 +88,7 @@ private fun OutlinedProgressTextField( val rTextDebouncer = valueDebouncerAsState(debounce, onTextChanged) val rText = remember { mutableStateOf(textFieldData.value) } - fun updateFieldValueAndEmmit(value: String){ + fun updateFieldValueAndEmmit(value: String) { rText.value = value rTextDebouncer.emmit(value) } diff --git a/app/src/main/java/com/tangem/tap/common/compose/Spacer.kt b/app/src/main/java/com/tangem/tap/common/compose/Spacer.kt index cfea07e9c8..b31a15c4ae 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/Spacer.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/Spacer.kt @@ -40,28 +40,28 @@ fun SpacerH24(modifier: Modifier = Modifier) { // ***************************** Vertical @Composable -fun SpacerV(width: Dp, modifier: Modifier = Modifier) { +fun SpacerW(width: Dp, modifier: Modifier = Modifier) { Spacer(modifier = modifier.width(width)) } @Composable -fun SpacerV4(modifier: Modifier = Modifier) { - SpacerV(4.dp, modifier) +fun SpacerW4(modifier: Modifier = Modifier) { + SpacerW(4.dp, modifier) } @Composable -fun SpacerV8(modifier: Modifier = Modifier) { - SpacerV(8.dp, modifier) +fun SpacerW8(modifier: Modifier = Modifier) { + SpacerW(8.dp, modifier) } @Composable -fun SpacerV16(modifier: Modifier = Modifier) { - SpacerV(16.dp, modifier) +fun SpacerW16(modifier: Modifier = Modifier) { + SpacerW(16.dp, modifier) } @Composable -fun SpacerV24(modifier: Modifier = Modifier) { - SpacerV(24.dp, modifier) +fun SpacerW24(modifier: Modifier = Modifier) { + SpacerW(24.dp, modifier) } // ***************************** Size diff --git a/app/src/main/java/com/tangem/tap/common/compose/Undefined.kt b/app/src/main/java/com/tangem/tap/common/compose/Undefined.kt new file mode 100644 index 0000000000..6cd0b70d72 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/compose/Undefined.kt @@ -0,0 +1,27 @@ +package com.tangem.tap.common.compose + +import androidx.compose.foundation.layout.Column +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.sp + +/** +[REDACTED_AUTHOR] + * Compose views are not typically used as a main or base view. + */ + +@Composable +fun TitleSubtitle( + title: String, + subtitle: String +) { + Column() { + Text(text = title) + Text( + text = subtitle, + fontSize = 12.sp, + color = Color.Gray + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesProgressBar.kt b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesProgressBar.kt index 7822c5cee0..d121949529 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesProgressBar.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesProgressBar.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.tangem.tap.common.compose.SpacerV4 +import com.tangem.tap.common.compose.SpacerW4 @Composable fun StoriesProgressBar( @@ -68,7 +68,7 @@ fun StoriesProgressBar( ) {} } if (index != steps) { - SpacerV4() + SpacerW4() } } } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/CustomTokenErrorConverter.kt b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/CustomTokenErrorConverter.kt index 17373aa948..f151530a1b 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/CustomTokenErrorConverter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/CustomTokenErrorConverter.kt @@ -35,10 +35,15 @@ class CustomTokenWarningConverter( override fun convertError(error: DomainError): String { val customTokenWarning = (error as? AddCustomTokenWarning) ?: throw UnsupportedOperationException() - val resId = when (customTokenWarning) { + val rawMessage = when (customTokenWarning) { AddCustomTokenWarning.PotentialScamToken -> R.string.custom_token_validation_error_not_found AddCustomTokenWarning.TokenAlreadyAdded -> R.string.custom_token_validation_error_already_added + AddCustomTokenWarning.Network.CheckAddressRequestError -> "CheckAddressRequestError" + } + return when (rawMessage) { + is Int -> context.getString(rawMessage) + is String -> rawMessage + else -> "Unknown error: ${customTokenWarning::class.java.simpleName}" } - return context.getString(resId) } } \ 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 f1cc9dcfbf..2ad938960a 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 @@ -2,12 +2,10 @@ package com.tangem.tap.features.tokens.addCustomToken.compose import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.Scaffold -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.material.rememberScaffoldState +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -29,10 +27,7 @@ import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState 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.OutlinedTextFieldWidget -import com.tangem.tap.common.compose.SpacerH8 -import com.tangem.tap.common.compose.keyboardObserverAsState +import com.tangem.tap.common.compose.* import com.tangem.tap.features.tokens.addCustomToken.CustomTokenErrorConverter import com.tangem.tap.features.tokens.addCustomToken.CustomTokenWarningConverter import com.tangem.wallet.R @@ -52,12 +47,12 @@ fun AddCustomTokenScreen(state: MutableState) { ) { Box(Modifier.fillMaxSize()) { LazyColumn( - contentPadding = PaddingValues(bottom = 80.dp) + contentPadding = PaddingValues(bottom = 90.dp) ) { item { Surface( modifier = Modifier.padding(16.dp), - shape = RoundedCornerShape(4.dp), + shape = MaterialTheme.shapes.small, elevation = 4.dp, ) { Column( @@ -143,7 +138,7 @@ private fun TokenNameView(screenFieldData: ScreenFieldData) { error = screenFieldData.error, errorConverter = screenFieldData.errorConverter, ) { - domainStore.dispatch(OnTokenFieldChanged(screenFieldData.field.id, Field.Data(it))) + domainStore.dispatch(OnTokenNameChanged(Field.Data(it))) } SpacerH8() } @@ -153,14 +148,14 @@ private fun TokenNetworkView(screenFieldData: ScreenFieldData) { if (!screenFieldData.viewState.isVisible) return val notSelected = stringResource(id = R.string.custom_token_network_input_not_selected) - val networkField = screenFieldData.field as TokenNetworkField + val networkField = screenFieldData.field as TokenBlockchainField - TokenNetworkSpinner( + BlockchainSpinner( title = R.string.custom_token_network_input_title, itemList = networkField.itemList, selectedItem = networkField.data, isEnabled = screenFieldData.viewState.isEnabled, - itemNameConverter = { AddCustomTokenState.convertBlockchainName(it, notSelected) }, + textFieldConverter = { AddCustomTokenState.convertBlockchainName(it, notSelected) }, ) { domainStore.dispatch(OnTokenNetworkChanged(Field.Data(it))) } SpacerH8() } @@ -178,7 +173,7 @@ private fun TokenSymbolView(screenFieldData: ScreenFieldData) { isEnabled = screenFieldData.viewState.isEnabled, error = screenFieldData.error, errorConverter = screenFieldData.errorConverter, - ) { domainStore.dispatch(OnTokenFieldChanged(screenFieldData.field.id, Field.Data(it))) } + ) { domainStore.dispatch(OnTokenSymbolChanged(Field.Data(it))) } SpacerH8() } @@ -207,12 +202,17 @@ private fun TokenDerivationPathView(screenFieldData: ScreenFieldData) { val notSelected = stringResource(id = R.string.custom_token_derivation_path_default) val networkField = screenFieldData.field as TokenDerivationPathField - TokenNetworkSpinner( - title = R.string.custom_token_network_input_title, + BlockchainSpinner( + title = R.string.custom_token_derivation_path_input_title, itemList = networkField.itemList, selectedItem = networkField.data, isEnabled = screenFieldData.viewState.isEnabled, - itemNameConverter = { AddCustomTokenState.convertDerivationPathName(it, notSelected) }, + textFieldConverter = { AddCustomTokenState.convertBlockchainName(it, notSelected) }, + dropdownItemView = { blockchain -> + val derivationPathLabel = AddCustomTokenState.convertDerivationPathLabel(blockchain, notSelected) + val blockchainName = AddCustomTokenState.convertBlockchainName(blockchain, notSelected) + TitleSubtitle(derivationPathLabel, blockchainName) + } ) { domainStore.dispatch(OnTokenDerivationPathChanged(Field.Data(it))) } SpacerH8() } @@ -227,13 +227,13 @@ private fun Warnings(warnings: List) { Column { warnings.forEachIndexed { index, item -> val modifier = when (index) { - 0 -> Modifier.padding(16.dp, 0.dp, 16.dp, 16.dp) - warnings.lastIndex -> Modifier.padding(16.dp, 16.dp, 16.dp, 16.dp) - else -> Modifier.padding(16.dp, 16.dp, 16.dp, 0.dp) + 0 -> Modifier.padding(16.dp, 16.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) } Surface( modifier = modifier.fillMaxWidth(), - shape = RoundedCornerShape(4.dp), + shape = MaterialTheme.shapes.small, color = colorResource(id = R.color.darkGray2), contentColor = colorResource(id = R.color.darkGray3) ) { @@ -248,6 +248,30 @@ private fun Warnings(warnings: List) { } } +@Composable +private fun AddButton( + modifier: Modifier = Modifier, + isEnabled: Boolean, + textId: Int = R.string.common_add, + onClick: () -> Unit, +) { + Button( + textId = textId, + isEnabled = isEnabled, + modifier = modifier + .height(52.dp) + .padding(horizontal = 16.dp) + .fillMaxWidth(), + leadingView = { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = "Add", + ) + }, + onClick = onClick + ) +} + private data class ScreenFieldData( val field: DataField<*>, val error: AddCustomTokenError?, diff --git a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenViews.kt b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenViews.kt deleted file mode 100644 index dee8d3b535..0000000000 --- a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenViews.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.tangem.tap.features.tokens.addCustomToken.compose - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.tangem.blockchain.common.Blockchain -import com.tangem.domain.common.form.Field -import com.tangem.tap.common.compose.Button -import com.tangem.tap.common.compose.OutlinedSpinner -import com.tangem.tap.common.extensions.ValueCallback -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun TokenNetworkSpinner( - title: Int, - itemList: List, - selectedItem: Field.Data, - isEnabled: Boolean = true, - itemNameConverter: (Blockchain) -> String, - onItemSelected: ValueCallback, -) { - - OutlinedSpinner( - modifier = Modifier.fillMaxWidth(), - title = stringResource(id = title), - itemList = itemList, - selectedItem = selectedItem, - itemNameConverter = itemNameConverter, - isEnabled = isEnabled, - onItemSelected = onItemSelected - ) -} - -@Composable -fun AddButton( - modifier: Modifier = Modifier, - isEnabled: Boolean, - textId: Int = R.string.common_add, - onClick: () -> Unit, -) { - Button( - textId = textId, - isEnabled = isEnabled, - modifier = modifier - .height(52.dp) - .padding(horizontal = 16.dp) - .fillMaxWidth(), - leadingView = { - Icon( - imageVector = Icons.Filled.Add, - contentDescription = "Add", - ) - }, - onClick = onClick - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/SelectTokenNetworkDialog.kt b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/SelectTokenNetworkDialog.kt new file mode 100644 index 0000000000..946690c107 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/SelectTokenNetworkDialog.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.features.tokens.addCustomToken.compose + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import com.tangem.domain.DomainDialog +import com.tangem.tap.common.compose.SimpleDialog +import com.tangem.tap.common.compose.TitleSubtitle +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +@Composable +fun SelectTokenNetworkDialog(dialog: DomainDialog.SelectTokenDialog, onDismissRequest: () -> Unit) { + SimpleDialog( + title = stringResource(id = R.string.custom_token_type_network), + items = dialog.items, + onSelect = dialog.onSelect, + onDismissRequest = onDismissRequest + ) { contract -> TitleSubtitle(dialog.networkIdConverter(contract.networkId), contract.address) } +} \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/DomainStateDialog.kt b/domain/src/main/java/com/tangem/domain/DomainStateDialog.kt index c6baa9a431..f2df99a7c1 100644 --- a/domain/src/main/java/com/tangem/domain/DomainStateDialog.kt +++ b/domain/src/main/java/com/tangem/domain/DomainStateDialog.kt @@ -12,7 +12,7 @@ sealed class DomainDialog : DomainStateDialog { data class SelectTokenDialog( val items: List, - val itemNameConverter: (Coins.CheckAddressResponse.Token.Contract) -> String, + val networkIdConverter: (String) -> String, val onSelect: (Coins.CheckAddressResponse.Token.Contract) -> Unit, val onClose: VoidCallback = {} ) : DomainDialog() diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt index dac29b2d3a..0b49f500d7 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt @@ -15,35 +15,36 @@ class AddCustomTokenManager( suspend fun checkAddress( contractAddress: String, networkId: String? = null - ): List { + ): Result> { val result = tangemTechService.coins.checkAddress(contractAddress, networkId) return when (result) { is Result.Success -> { val resultTokens = result.data.tokens - val newTokensList = mutableListOf() - resultTokens.forEach { - val contractsWithTheSameAddress = it.contracts.filter { it.address == contractAddress } + var tokensList = mutableListOf() + resultTokens.forEach { token -> + val contractsWithTheSameAddress = token.contracts + .filter { it.address == contractAddress } + .filter { it.decimalCount != null } if (contractsWithTheSameAddress.isNotEmpty()) { - val newToken = it.copy(contracts = contractsWithTheSameAddress) - newTokensList.add(newToken) + val newToken = token.copy(contracts = contractsWithTheSameAddress) + tokensList.add(newToken) } } - when { + if (tokensList.size > 1) { // https://tangem.slack.com/archives/GMXC6PP71/p1649672562078679 - newTokensList.size > 1 -> listOf(newTokensList[0]) - else -> newTokensList + tokensList = mutableListOf(tokensList[0]) } + Result.Success(tokensList) } - is Result.Failure -> emptyList() + is Result.Failure -> result } } suspend fun tokens(): List { - val result = tangemTechService.coins.tokens() - return when (result) { + return when (val result = tangemTechService.coins.tokens()) { is Result.Success -> { - val currencies = result.data.tokens - currencies.filter { + val tokens = result.data.tokens + tokens.filter { it.contracts.isNullOrEmpty() } } diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/Errors.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/Errors.kt index c7abc4c0d2..b651dd844b 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/Errors.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/Errors.kt @@ -18,4 +18,8 @@ sealed class AddCustomTokenError : AnError(ERROR_CODE_ADD_CUSTOM_TOKEN, "Add cus sealed class AddCustomTokenWarning : AnError(ERROR_CODE_ADD_CUSTOM_TOKEN, "Add custom token - warning") { object PotentialScamToken : AddCustomTokenWarning() object TokenAlreadyAdded : AddCustomTokenWarning() + + sealed class Network : AddCustomTokenWarning() { + object CheckAddressRequestError : Network() + } } \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/FormFields.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/FormFields.kt index b6084347d2..33336b560e 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/FormFields.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/FormFields.kt @@ -21,7 +21,7 @@ data class TokenField( override val id: FieldId, ) : BaseDataField(id, Field.Data("")) -data class TokenNetworkField( +data class TokenBlockchainField( override val id: FieldId, val itemList: List, ) : BaseDataField(id, Field.Data(Blockchain.Unknown)) 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 0077bd1907..f4dc8c9746 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 @@ -25,6 +25,9 @@ import org.rekotlin.Action */ internal class AddCustomTokenHub : BaseStoreHub("AddCustomTokenHub") { + private val hubState: AddCustomTokenState + get() = domainStore.state.addCustomTokensState + override fun getHubState(storeState: DomainState): AddCustomTokenState { return storeState.addCustomTokensState } @@ -41,7 +44,6 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT if (action !is AddCustomTokenAction) return // val card = storeState.globalState.scanResponse?.card // ?: throw IllegalStateException("ScanResponse must be set before showing the AddCustomToken screen") - val hubState = storeState.addCustomTokensState when (action) { is OnCreate -> { @@ -102,7 +104,7 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT // dispatchOnMain() } is FillTokenFields -> { - val networkField = getField(Network, hubState) + val networkField = getField(Network, hubState) val nameField = getField(Name, hubState) val symbolField = getField(Symbol, hubState) val decimalsField = getField(Decimals, hubState) @@ -127,13 +129,22 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT ): List { dispatchOnMain(Screen.UpdateTokenFields(listOf(ContractAddress to ViewStates.TokenField(isLoading = true)))) val tokenManager = hubState.addCustomTokenManager - val field = getField(Network, hubState) + val field = getField(Network, hubState) val selectedNetworkId: String? = field.data.value.let { if (it == Blockchain.Unknown) null else it }?.toNetworkId() - val foundTokens = tokenManager.checkAddress(contractAddress, selectedNetworkId) + +// delay(1000) + val result = when (val foundTokensResult = tokenManager.checkAddress(contractAddress, selectedNetworkId)) { + is Result.Success -> foundTokensResult.data + is Result.Failure -> { +// val warning = AddCustomTokenWarning.Network.CheckAddressRequestError +// dispatchOnMain(Warning.Add(setOf(warning))) + emptyList() + } + } dispatchOnMain(Screen.UpdateTokenFields(listOf(ContractAddress to ViewStates.TokenField(isLoading = false)))) - return foundTokens + return result } private suspend fun checkToken( @@ -171,12 +182,18 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT else -> { val dialog = DomainDialog.SelectTokenDialog( items = contracts, - itemNameConverter = { it.address }, + networkIdConverter = { networkId -> + val blockchain = Blockchain.fromNetworkId(networkId) + if (blockchain == Blockchain.Unknown) { + throw DomainException.SelectTokeNetworkException(networkId) + } + AddCustomTokenState.convertBlockchainName(blockchain, "") + }, onSelect = { selectedContract -> hubScope.launch { // find how to connect to the upper coroutineContext and dispatch through them dispatchOnMain(FillTokenFields(token, selectedContract)) - dispatchOnMain(FillTokenFields(token, selectedContract)) + dispatchOnMain(actionsLockTokenFields()) } }, ) @@ -237,7 +254,7 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT updateFormState(state) } is OnTokenNetworkChanged -> { - val field: TokenNetworkField = getField(Network, state) + val field: TokenBlockchainField = getField(Network, state) field.data = action.value updateFormState(state) } 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 cdf726e361..f9488b54b4 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 @@ -49,22 +49,17 @@ data class AddCustomTokenState( companion object { fun convertBlockchainName(blockchain: Blockchain, unknown: String): String = when (blockchain) { Blockchain.Unknown -> unknown - Blockchain.Cardano -> "Cardano" - Blockchain.CardanoShelley -> "Cardano Shelley" else -> blockchain.fullName } - fun convertDerivationPathName(blockchain: Blockchain, unknown: String): String = when (blockchain) { - Blockchain.Unknown -> unknown - Blockchain.BSC -> "BNB Smart Chain" - Blockchain.Fantom -> "Fantom Opera" - else -> blockchain.fullName + fun convertDerivationPathLabel(blockchain: Blockchain, unknown: String): String { + return blockchain.derivationPath()?.rawPath ?: unknown } private fun createFormFields(): List> { return listOf( TokenField(ContractAddress), - TokenNetworkField(Network, getSupportedNetworks()), + TokenBlockchainField(Network, getSupportedNetworks()), TokenField(Name), TokenField(Symbol), TokenField(Decimals), @@ -84,6 +79,7 @@ data class AddCustomTokenState( private fun getSupportedNetworks(): List { return listOf( + Blockchain.Unknown, Blockchain.Ethereum, Blockchain.BSC, Blockchain.Binance,