Updated on 2026-08-14

This commit is contained in:
Tangem 2025-01-04 18:35:43 +03:00
parent debcf71da6
commit edfe927c7c
4 changed files with 276 additions and 0 deletions

View file

@ -28,6 +28,8 @@ import com.tangem.feature.tester.presentation.menu.state.TesterMenuUM.ButtonUM
import com.tangem.feature.tester.presentation.menu.ui.TesterMenuScreen
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
import com.tangem.feature.tester.presentation.navigation.TesterScreen
import com.tangem.feature.tester.presentation.providers.ui.BlockchainProvidersScreen
import com.tangem.feature.tester.presentation.providers.viewmodel.BlockchainProvidersViewModel
import com.tangem.features.tester.api.TesterRouter
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.collections.immutable.persistentSetOf
@ -130,6 +132,15 @@ internal class TesterActivity : ComposeActivity() {
val state by viewModel.state.collectAsStateWithLifecycle()
ExcludedBlockchainsScreen(state = state)
}
composable(route = TesterScreen.BLOCKCHAIN_PROVIDERS.name) {
val viewModel = hiltViewModel<BlockchainProvidersViewModel>().apply {
setupNavigation(innerTesterRouter)
}
val state by viewModel.state.collectAsStateWithLifecycle()
BlockchainProvidersScreen(state)
}
}
}
}

View file

@ -0,0 +1,19 @@
package com.tangem.feature.tester.presentation.providers.entity
import kotlinx.collections.immutable.ImmutableList
internal data class BlockchainProvidersUM(
val onBackClick: () -> Unit,
val blockchainProviders: ImmutableList<ProvidersUM>,
) {
data class ProvidersUM(
val blockchainId: String,
val blockchainName: String,
val blockchainSymbol: String,
val providers: ImmutableList<ProviderUM>,
val isExpanded: Boolean,
)
data class ProviderUM(val name: String)
}

View file

@ -0,0 +1,180 @@
package com.tangem.feature.tester.presentation.providers.ui
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.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.painterResource
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.SpacerH8
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.components.rows.RowContentContainer
import com.tangem.core.ui.components.rows.RowText
import com.tangem.core.ui.extensions.getActiveIconRes
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.providers.entity.BlockchainProvidersUM
import com.tangem.feature.tester.presentation.providers.entity.BlockchainProvidersUM.ProviderUM
import com.tangem.feature.tester.presentation.providers.entity.BlockchainProvidersUM.ProvidersUM
private const val CHEVRON_ROTATION_EXPANDED = 180f
private const val CHEVRON_ROTATION_COLLAPSED = 0f
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun BlockchainProvidersScreen(state: BlockchainProvidersUM) {
Box(
modifier = Modifier
.fillMaxSize()
.background(TangemTheme.colors.background.primary),
) {
LazyColumn {
stickyHeader {
AppBarWithBackButton(
onBackClick = state.onBackClick,
text = stringResourceSafe(id = R.string.blockchain_providers),
containerColor = TangemTheme.colors.background.primary,
)
}
items(
items = state.blockchainProviders,
key = ProvidersUM::blockchainId,
contentType = { "BlockchainProviders" },
itemContent = { BlockchainProvidersItem(it) },
)
}
}
}
@Composable
private fun BlockchainProvidersItem(state: ProvidersUM) {
var isExpanded by remember { mutableStateOf(state.isExpanded) }
ExpandableBlockchainRow(
state = state,
modifier = Modifier.clickable { isExpanded = !isExpanded },
content = { Providers(state = state, isExpanded = isExpanded) },
)
}
@Composable
private fun ExpandableBlockchainRow(
state: ProvidersUM,
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
) {
BlockchainRow(state)
content()
}
}
@Composable
private fun BlockchainRow(state: ProvidersUM) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
painter = painterResource(id = getActiveIconRes(state.blockchainId)),
contentDescription = null,
modifier = Modifier.size(TangemTheme.dimens.size36),
)
RowText(
mainText = state.blockchainName,
secondText = state.blockchainSymbol,
accentMainText = true,
accentSecondText = false,
modifier = Modifier.padding(horizontal = 8.dp),
)
}
val rotation by animateFloatAsState(
targetValue = if (state.isExpanded) {
CHEVRON_ROTATION_EXPANDED
} else {
CHEVRON_ROTATION_COLLAPSED
},
label = "chevron_rotation",
)
Icon(
modifier = Modifier
.rotate(rotation)
.size(TangemTheme.dimens.size24),
painter = painterResource(id = R.drawable.ic_chevron_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
}
}
@Composable
private fun Providers(state: ProvidersUM, isExpanded: Boolean) {
AnimatedVisibility(
visible = isExpanded,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
label = "providers_visibility",
) {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
state.providers.fastForEachIndexed { index, provider ->
key(provider.name) {
ProviderItem(index = index, state = provider)
if (index == state.providers.lastIndex) SpacerH8()
}
}
}
}
}
@Composable
private fun ProviderItem(index: Int, state: ProviderUM) {
RowContentContainer(
icon = {
Text(
text = "${index + 1}.",
color = TangemTheme.colors.text.accent,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = TangemTheme.typography.body2,
)
},
text = {
Text(
text = state.name,
color = TangemTheme.colors.text.primary1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = TangemTheme.typography.body2,
)
},
action = {},
)
}

View file

@ -0,0 +1,66 @@
package com.tangem.feature.tester.presentation.providers.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.network.providers.ProviderType
import com.tangem.blockchainsdk.providers.BlockchainProviderTypesStore
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
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
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@HiltViewModel
internal class BlockchainProvidersViewModel @Inject constructor(
blockchainProviderTypesStore: BlockchainProviderTypesStore,
) : ViewModel() {
val state: StateFlow<BlockchainProvidersUM> get() = _state.asStateFlow()
private val _state = MutableStateFlow(
value = BlockchainProvidersUM(onBackClick = {}, blockchainProviders = persistentListOf()),
)
init {
blockchainProviderTypesStore.get()
.onEach { blockchainProviderTypes ->
_state.update { state ->
state.copy(blockchainProviders = blockchainProviderTypes.toProvidersUM())
}
}
.launchIn(viewModelScope)
}
fun setupNavigation(router: InnerTesterRouter) {
_state.update { state ->
state.copy(onBackClick = router::back)
}
}
private fun Map<Blockchain, List<ProviderType>>.toProvidersUM(): ImmutableList<BlockchainProvidersUM.ProvidersUM> {
return map {
val (blockchain, providers) = it
BlockchainProvidersUM.ProvidersUM(
blockchainId = blockchain.id,
blockchainName = blockchain.fullName,
blockchainSymbol = blockchain.currency,
providers = providers.map { providerType ->
BlockchainProvidersUM.ProviderUM(
name = when (providerType) {
is ProviderType.Public -> providerType.url
else -> providerType::class.java.simpleName
},
)
}
.toImmutableList(),
isExpanded = false,
)
}
.toImmutableList()
}
}