Updated on 2026-08-14
This commit is contained in:
parent
728f0e3509
commit
85ca25eb56
3 changed files with 165 additions and 31 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue