Updated on 2026-08-14

This commit is contained in:
Tangem 2025-01-07 15:28:10 +03:00
parent 728f0e3509
commit 85ca25eb56
3 changed files with 165 additions and 31 deletions

View file

@ -17,7 +17,8 @@ internal data class BlockchainProvidersUM(
val blockchainSymbol: String,
val providers: ImmutableList<ProviderUM>,
val isExpanded: Boolean,
val onDrop: (id: String, prev: Int, current: Int) -> Unit,
val onDrop: (prev: Int, current: Int) -> Unit,
val addPublicProviderDialog: AddPublicProviderDialogUM,
)
@Immutable
@ -28,4 +29,10 @@ internal data class BlockchainProvidersUM(
else -> type::class.java.simpleName
}
}
data class AddPublicProviderDialogUM(
val hasError: Boolean,
val onValueChange: (String) -> Unit,
val onSaveClick: (String) -> Unit,
)
}

View file

@ -5,10 +5,7 @@ import android.content.ClipDescription
import android.view.View
import androidx.compose.animation.*
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.*
import androidx.compose.foundation.draganddrop.dragAndDropSource
import androidx.compose.foundation.draganddrop.dragAndDropTarget
import androidx.compose.foundation.gestures.detectTapGestures
@ -22,14 +19,20 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draganddrop.*
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import com.tangem.core.ui.components.AdditionalTextInputDialogUM
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.components.TextInputDialog
import com.tangem.core.ui.components.rows.RowText
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tester.impl.R
import com.tangem.feature.tester.presentation.common.components.appbar.TopBarWithRefresh
@ -165,10 +168,12 @@ private fun Providers(state: ProvidersUM, isExpanded: Boolean) {
ProviderItem(
index = index,
state = provider,
onDrop = { prev, new -> state.onDrop(state.blockchainId, prev, new) },
onDrop = { prev, new -> state.onDrop(prev, new) },
)
}
}
AddProviderButton(state = state.addPublicProviderDialog)
}
}
}
@ -194,18 +199,8 @@ private fun ProviderItem(index: Int, state: ProviderUM, onDrop: (Int, Int) -> Un
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "${index + 1}.",
color = TangemTheme.colors.text.accent,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = TangemTheme.typography.body2,
)
ProviderItemContainer {
ProviderIndex(index = index)
Box(
modifier = Modifier
@ -243,6 +238,99 @@ private fun ProviderItem(index: Int, state: ProviderUM, onDrop: (Int, Int) -> Un
}
}
@Composable
private fun ProviderIndex(index: Int) {
Text(
text = "${index + 1}.",
color = TangemTheme.colors.text.accent,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = TangemTheme.typography.body2,
)
}
@Composable
private fun AddProviderButton(state: BlockchainProvidersUM.AddPublicProviderDialogUM) {
var isDialogVisible by remember { mutableStateOf(value = false) }
ProviderItemContainer {
Box(
modifier = Modifier
.clip(shape = RoundedCornerShape(16.dp))
.clickable(onClick = { isDialogVisible = true })
.weight(1f)
.border(
border = BorderStroke(width = 2.dp, color = TangemTheme.colors.icon.accent),
shape = RoundedCornerShape(16.dp),
)
.padding(horizontal = 8.dp, vertical = 2.dp),
contentAlignment = Alignment.Center,
) {
Icon(
painter = painterResource(id = R.drawable.ic_plus_24),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
)
}
}
if (isDialogVisible) {
AddPublicProviderDialog(
state = state,
onDismiss = { isDialogVisible = false },
)
}
}
@Composable
private fun AddPublicProviderDialog(state: BlockchainProvidersUM.AddPublicProviderDialogUM, onDismiss: () -> Unit) {
var value by remember { mutableStateOf(value = TextFieldValue()) }
TextInputDialog(
fieldValue = value,
confirmButton = DialogButtonUM(
title = stringResourceSafe(R.string.common_save),
enabled = value.text.isNotBlank() && !state.hasError,
onClick = {
state.onSaveClick(
buildString {
append(value.text.trim())
if (!endsWith("/")) append("/")
},
)
onDismiss()
},
),
onDismissDialog = onDismiss,
onValueChange = {
value = it
state.onValueChange(it.text)
},
textFieldParams = AdditionalTextInputDialogUM(
label = "Url",
placeholder = "https://...",
isError = state.hasError,
errorText = "Invalid url",
),
title = "Public provider",
dismissButton = DialogButtonUM(
title = stringResourceSafe(id = R.string.common_cancel),
onClick = onDismiss,
),
)
}
@Composable
private fun ProviderItemContainer(content: @Composable RowScope.() -> Unit) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
content = content,
)
}
private class ProvidersDnDTarget(
private val index: Int,
private val onActiveStateChange: (Boolean) -> Unit,

View file

@ -1,5 +1,6 @@
package com.tangem.feature.tester.presentation.providers.viewmodel
import androidx.core.util.PatternsCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.blockchain.common.Blockchain
@ -10,8 +11,7 @@ import com.tangem.feature.tester.impl.R
import com.tangem.feature.tester.presentation.common.components.appbar.TopBarWithRefreshUM
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
import com.tangem.feature.tester.presentation.providers.entity.BlockchainProvidersUM
import com.tangem.feature.tester.presentation.providers.entity.BlockchainProvidersUM.ProviderUM
import com.tangem.feature.tester.presentation.providers.entity.BlockchainProvidersUM.ProvidersUM
import com.tangem.feature.tester.presentation.providers.entity.BlockchainProvidersUM.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -78,8 +78,13 @@ internal class BlockchainProvidersViewModel @Inject constructor(
blockchainName = blockchain.fullName,
blockchainSymbol = blockchain.currency,
providers = providers.map(BlockchainProvidersUM::ProviderUM).toImmutableList(),
onDrop = ::onDrop,
isExpanded = false,
onDrop = { prev, current -> onDrop(blockchain.id, prev, current) },
addPublicProviderDialog = AddPublicProviderDialogUM(
hasError = false,
onValueChange = { onPublicProviderUrlChange(id = blockchain.id, url = it) },
onSaveClick = { onAddProviderClick(id = blockchain.id, url = it) },
),
)
}
.toImmutableList()
@ -98,16 +103,9 @@ internal class BlockchainProvidersViewModel @Inject constructor(
viewModelScope.launch {
_state.update {
it.copy(
blockchainProviders = it.blockchainProviders.map { block ->
if (block.blockchainId == id) {
block.copy(providers = providers.toImmutableList())
} else {
block
}
}
.toImmutableList(),
)
it.copyProvidersUM(blockchainId = id) {
copy(providers = providers.toImmutableList())
}
}
manager.update(
@ -160,4 +158,45 @@ internal class BlockchainProvidersViewModel @Inject constructor(
set(current, this[prev])
}
}
private fun onPublicProviderUrlChange(id: String, url: String) {
_state.update {
it.copyProvidersUM(blockchainId = id) {
copy(
addPublicProviderDialog = addPublicProviderDialog.copy(
hasError = !PatternsCompat.WEB_URL.matcher(url).matches(),
),
)
}
}
}
private fun onAddProviderClick(id: String, url: String) {
val providers = _state.value
.blockchainProviders.first { it.blockchainId == id }
.providers
viewModelScope.launch {
manager.update(
blockchain = Blockchain.fromId(id),
providers = listOf(ProviderType.Public(url = url)) + providers.map(ProviderUM::type),
)
}
}
private fun BlockchainProvidersUM.copyProvidersUM(
blockchainId: String,
update: ProvidersUM.() -> ProvidersUM,
): BlockchainProvidersUM {
return copy(
blockchainProviders = _state.value.blockchainProviders.map {
if (it.blockchainId == blockchainId) {
it.update()
} else {
it
}
}
.toImmutableList(),
)
}
}