Updated on 2026-08-14
This commit is contained in:
parent
a43f322c61
commit
332927dc9b
11 changed files with 404 additions and 12 deletions
|
|
@ -11,6 +11,7 @@ private const val WALLET_ID = "Wallet Id"
|
|||
private const val CONTACT_ID = "Contact Id"
|
||||
private const val MODE = "Mode"
|
||||
private const val CONTACTS_COUNT = "Contacts Count"
|
||||
private const val ACTION = "Action"
|
||||
|
||||
sealed class AddressBookEvents(
|
||||
event: String,
|
||||
|
|
@ -162,6 +163,24 @@ sealed class AddressBookEvents(
|
|||
)
|
||||
// endregion
|
||||
|
||||
// region Choose network
|
||||
class SelectAllNetworksTapped(
|
||||
walletId: UserWalletId,
|
||||
action: Action,
|
||||
) : AddressBookEvents(
|
||||
event = "Select All Networks Tapped",
|
||||
params = mapOf(
|
||||
WALLET_ID to walletId.stringValue,
|
||||
ACTION to action.value,
|
||||
),
|
||||
) {
|
||||
enum class Action(val value: String) {
|
||||
SelectAll("Select All"),
|
||||
ClearAll("Clear All"),
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Deletion
|
||||
class AddressRemoved(
|
||||
walletId: UserWalletId,
|
||||
|
|
|
|||
|
|
@ -88,6 +88,14 @@ internal class AddressBookAnalyticsSender @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun sendSelectAllNetworksTapped(action: AddressBookEvents.SelectAllNetworksTapped.Action, scope: CoroutineScope) {
|
||||
scope.launch(dispatcherProvider.default) {
|
||||
analyticsEventHandler.send(
|
||||
AddressBookEvents.SelectAllNetworksTapped(walletId = selectedWalletId(), action = action),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendAddContactTapped(fromSendSuccess: Boolean, scope: CoroutineScope) {
|
||||
scope.launch(dispatcherProvider.default) {
|
||||
analyticsEventHandler.send(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import com.tangem.blockchainsdk.utils.toNetworkId
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.features.addressbook.analytics.AddressBookEvents
|
||||
import com.tangem.features.addressbook.common.AddressBookAnalyticsSender
|
||||
import com.tangem.features.addressbook.selectnetworks.DefaultSelectNetworksComponent
|
||||
import com.tangem.features.addressbook.selectnetworks.state.SelectNetworksStateController
|
||||
import com.tangem.features.addressbook.selectnetworks.state.transformers.UpdateNetworksContentTransformer
|
||||
|
|
@ -22,6 +24,7 @@ internal class SelectNetworksModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val stateController: SelectNetworksStateController,
|
||||
private val analyticsSender: AddressBookAnalyticsSender,
|
||||
) : Model() {
|
||||
|
||||
private val params: DefaultSelectNetworksComponent.Params = paramsContainer.require()
|
||||
|
|
@ -50,16 +53,18 @@ internal class SelectNetworksModel @Inject constructor(
|
|||
onActiveChange = ::onActiveChange,
|
||||
onBackClick = params.onBackClick,
|
||||
onDoneClick = ::onDoneClick,
|
||||
onSelectAllClick = ::onSelectAllClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun subscribeToContent() {
|
||||
combine(query, selectedNetworks) { query, selection ->
|
||||
combine(query, selectedNetworks, isSearchActive) { query, selection, isSearchActive ->
|
||||
UpdateNetworksContentTransformer(
|
||||
matchedBlockchains = matchedBlockchains,
|
||||
query = query,
|
||||
selectedNetworkIds = selection,
|
||||
isSearchActive = isSearchActive,
|
||||
onToggle = ::onToggle,
|
||||
)
|
||||
}
|
||||
|
|
@ -88,6 +93,22 @@ internal class SelectNetworksModel @Inject constructor(
|
|||
selectedNetworks.value = if (networkId in current) current - networkId else current + networkId
|
||||
}
|
||||
|
||||
private fun onSelectAllClick() {
|
||||
val allNetworkIds = matchedBlockchains.map { blockchain -> blockchain.toNetworkId() }.toSet()
|
||||
val isSelectingAll = allNetworkIds.isEmpty() || !selectedNetworks.value.containsAll(allNetworkIds)
|
||||
|
||||
analyticsSender.sendSelectAllNetworksTapped(
|
||||
action = if (isSelectingAll) {
|
||||
AddressBookEvents.SelectAllNetworksTapped.Action.SelectAll
|
||||
} else {
|
||||
AddressBookEvents.SelectAllNetworksTapped.Action.ClearAll
|
||||
},
|
||||
scope = modelScope,
|
||||
)
|
||||
|
||||
selectedNetworks.value = if (isSelectingAll) allNetworkIds else emptySet()
|
||||
}
|
||||
|
||||
private fun onDoneClick() {
|
||||
val selected = selectedNetworks.value
|
||||
if (selected.isEmpty()) return
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ internal class SelectNetworksStateController @Inject constructor() {
|
|||
onCloseClick = {},
|
||||
),
|
||||
networks = persistentListOf(),
|
||||
selectAllButton = SelectNetworksUM.SelectAllButtonUM.Empty,
|
||||
doneButton = TangemButtonUM(
|
||||
text = TextReference.Res(R.string.common_done),
|
||||
type = TangemButtonType.Primary,
|
||||
|
|
@ -43,5 +44,6 @@ internal class SelectNetworksStateController @Inject constructor() {
|
|||
onClick = {},
|
||||
),
|
||||
onBackClick = {},
|
||||
onSelectAllClick = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ internal class UpdateNetworksContentTransformer(
|
|||
private val matchedBlockchains: List<Blockchain>,
|
||||
private val query: String,
|
||||
private val selectedNetworkIds: Set<String>,
|
||||
private val isSearchActive: Boolean,
|
||||
private val onToggle: (networkId: String) -> Unit,
|
||||
) : Transformer<SelectNetworksUM> {
|
||||
|
||||
|
|
@ -36,9 +37,18 @@ internal class UpdateNetworksContentTransformer(
|
|||
}
|
||||
.toImmutableList()
|
||||
|
||||
val isAllSelected = matchedBlockchains.isNotEmpty() &&
|
||||
selectedNetworkIds.size == matchedBlockchains.size
|
||||
val selectAllButton = when {
|
||||
isSearchActive -> SelectNetworksUM.SelectAllButtonUM.Empty
|
||||
isAllSelected -> SelectNetworksUM.SelectAllButtonUM.ClearAll
|
||||
else -> SelectNetworksUM.SelectAllButtonUM.SelectAll
|
||||
}
|
||||
|
||||
// Search field is owned by UpdateSelectNetworksSearchBarTransformer and intentionally left untouched here.
|
||||
return prevState.copy(
|
||||
networks = networks,
|
||||
selectAllButton = selectAllButton,
|
||||
doneButton = prevState.doneButton.copy(isEnabled = selectedNetworkIds.isNotEmpty()),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ internal class UpdateSelectNetworksInitialStateTransformer(
|
|||
private val onActiveChange: (Boolean) -> Unit,
|
||||
private val onBackClick: () -> Unit,
|
||||
private val onDoneClick: () -> Unit,
|
||||
private val onSelectAllClick: () -> Unit,
|
||||
) : Transformer<SelectNetworksUM> {
|
||||
|
||||
override fun transform(prevState: SelectNetworksUM): SelectNetworksUM {
|
||||
|
|
@ -20,6 +21,7 @@ internal class UpdateSelectNetworksInitialStateTransformer(
|
|||
),
|
||||
doneButton = prevState.doneButton.copy(onClick = onDoneClick),
|
||||
onBackClick = onBackClick,
|
||||
onSelectAllClick = onSelectAllClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,15 @@
|
|||
package com.tangem.features.addressbook.selectnetworks.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
|
|
@ -10,6 +17,7 @@ import androidx.compose.foundation.shape.CircleShape
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -25,10 +33,9 @@ import com.tangem.core.ui.ds.topbar.TangemTopBar
|
|||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.ds2.checkbox.TangemCheckmark
|
||||
import com.tangem.core.ui.ds2.search.TangemSearch
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.res.LocalHapticManager
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.addressbook.selectnetworks.ui.state.SelectNetworksUM
|
||||
|
|
@ -79,11 +86,9 @@ internal fun SelectNetworksContent(state: SelectNetworksUM, modifier: Modifier =
|
|||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 4.dp),
|
||||
text = stringResourceSafe(R.string.common_available_networks),
|
||||
style = TangemTheme.typography3.caption.medium,
|
||||
color = TangemTheme.colors3.text.secondary,
|
||||
NetworksHeader(
|
||||
selectAllButton = state.selectAllButton,
|
||||
onSelectAllClick = state.onSelectAllClick,
|
||||
)
|
||||
}
|
||||
items(items = state.networks, key = NetworkItemUM::id) { item ->
|
||||
|
|
@ -105,12 +110,64 @@ internal fun SelectNetworksContent(state: SelectNetworksUM, modifier: Modifier =
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NetworksHeader(
|
||||
selectAllButton: SelectNetworksUM.SelectAllButtonUM,
|
||||
onSelectAllClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val hapticManager = LocalHapticManager.current
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(top = 16.dp, bottom = 4.dp),
|
||||
text = stringResourceSafe(R.string.common_available_networks),
|
||||
style = TangemTheme.typography3.caption.medium,
|
||||
color = TangemTheme.colors3.text.secondary,
|
||||
)
|
||||
AnimatedContent(
|
||||
targetState = selectAllButton,
|
||||
transitionSpec = {
|
||||
fadeIn(animationSpec = tween(durationMillis = 400)) togetherWith
|
||||
fadeOut(animationSpec = tween(durationMillis = 400))
|
||||
},
|
||||
label = "SelectAllButton",
|
||||
) { button ->
|
||||
if (!button.text.isNullOrEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = {
|
||||
hapticManager.perform(TangemHapticEffect.View.SegmentTick)
|
||||
onSelectAllClick()
|
||||
},
|
||||
)
|
||||
.padding(start = 4.dp, top = 16.dp, bottom = 4.dp),
|
||||
text = button.text.resolveReference(),
|
||||
style = TangemTheme.typography3.caption.medium,
|
||||
color = TangemTheme.colors3.text.brand,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NetworkRow(item: NetworkItemUM) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickableSingle(onClick = item.onCheckedChange)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = item.onCheckedChange,
|
||||
)
|
||||
.padding(vertical = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
|
@ -182,6 +239,7 @@ private fun Preview_SelectNetworksContent() {
|
|||
onCheckedChange = {},
|
||||
),
|
||||
),
|
||||
selectAllButton = SelectNetworksUM.SelectAllButtonUM.SelectAll,
|
||||
doneButton = TangemButtonUM(
|
||||
text = TextReference.Res(R.string.common_done),
|
||||
type = TangemButtonType.Primary,
|
||||
|
|
@ -189,6 +247,7 @@ private fun Preview_SelectNetworksContent() {
|
|||
onClick = {},
|
||||
),
|
||||
onBackClick = {},
|
||||
onSelectAllClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,21 @@ package com.tangem.features.addressbook.selectnetworks.ui.state
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||
import com.tangem.core.ui.ds2.search.TangemSearch
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
internal data class SelectNetworksUM(
|
||||
val searchBar: TangemSearch.State,
|
||||
val networks: ImmutableList<NetworkItemUM>,
|
||||
val selectAllButton: SelectAllButtonUM,
|
||||
val doneButton: TangemButtonUM,
|
||||
val onBackClick: () -> Unit,
|
||||
val onSelectAllClick: () -> Unit,
|
||||
) {
|
||||
|
||||
@Immutable
|
||||
|
|
@ -23,4 +28,10 @@ internal data class SelectNetworksUM(
|
|||
val isSelected: Boolean,
|
||||
val onCheckedChange: () -> Unit,
|
||||
)
|
||||
|
||||
enum class SelectAllButtonUM(val text: TextReference) {
|
||||
SelectAll(text = resourceReference(R.string.address_book_select_all_networks)),
|
||||
ClearAll(text = resourceReference(R.string.address_book_clear_all_networks)),
|
||||
Empty(text = TextReference.EMPTY),
|
||||
}
|
||||
}
|
||||
|
|
@ -5,15 +5,19 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.core.decompose.model.MutableParamsContainer
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.features.addressbook.common.AddressBookAnalyticsSender
|
||||
import com.tangem.features.addressbook.selectnetworks.DefaultSelectNetworksComponent
|
||||
import com.tangem.features.addressbook.selectnetworks.state.SelectNetworksStateController
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
|
|
@ -24,6 +28,8 @@ internal class SelectNetworksModelTest {
|
|||
|
||||
private var model: SelectNetworksModel? = null
|
||||
|
||||
private val analyticsSender: AddressBookAnalyticsSender = mockk(relaxed = true)
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
model?.onDestroy()
|
||||
|
|
@ -147,6 +153,7 @@ internal class SelectNetworksModelTest {
|
|||
paramsContainer = paramsContainer,
|
||||
dispatchers = testScope.createTestingCoroutineDispatcherProvider(),
|
||||
stateController = SelectNetworksStateController(),
|
||||
analyticsSender = analyticsSender,
|
||||
).also { model = it }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,251 @@
|
|||
package com.tangem.features.addressbook.selectnetworks.state.transformers
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.ds.button.TangemButtonType
|
||||
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||
import com.tangem.core.ui.ds2.search.TangemSearch
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.addressbook.selectnetworks.ui.state.SelectNetworksUM
|
||||
import com.tangem.features.addressbook.selectnetworks.ui.state.SelectNetworksUM.SelectAllButtonUM
|
||||
import com.tangem.test.core.ProvideTestModels
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class UpdateNetworksContentTransformerTest {
|
||||
|
||||
private val ethereum = Blockchain.Ethereum
|
||||
private val bitcoin = Blockchain.Bitcoin
|
||||
private val polygon = Blockchain.Polygon
|
||||
private val allBlockchains = listOf(ethereum, bitcoin, polygon)
|
||||
|
||||
@Test
|
||||
fun `GIVEN blank query WHEN transform THEN all matched networks shown in order`() {
|
||||
// Arrange
|
||||
val transformer = createTransformer(matchedBlockchains = allBlockchains, query = "")
|
||||
|
||||
// Act
|
||||
val result = transformer.transform(createPrevState())
|
||||
|
||||
// Assert
|
||||
assertThat(result.networks.map { it.id })
|
||||
.containsExactly(ethereum.toNetworkId(), bitcoin.toNetworkId(), polygon.toNetworkId())
|
||||
.inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN network item WHEN transform THEN mapped fields match blockchain`() {
|
||||
// Arrange
|
||||
val transformer = createTransformer(matchedBlockchains = listOf(ethereum), query = "")
|
||||
|
||||
// Act
|
||||
val item = transformer.transform(createPrevState()).networks.single()
|
||||
|
||||
// Assert
|
||||
assertThat(item.id).isEqualTo(ethereum.toNetworkId())
|
||||
assertThat(item.name).isEqualTo(ethereum.fullName)
|
||||
assertThat(item.symbol).isEqualTo(ethereum.currency)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN non-blank query WHEN transform THEN only matching networks shown`() {
|
||||
// Arrange
|
||||
val transformer = createTransformer(matchedBlockchains = allBlockchains, query = "bitcoin")
|
||||
|
||||
// Act
|
||||
val result = transformer.transform(createPrevState())
|
||||
|
||||
// Assert
|
||||
assertThat(result.networks.map { it.id }).containsExactly(bitcoin.toNetworkId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN query matching nothing WHEN transform THEN no networks shown`() {
|
||||
// Arrange
|
||||
val transformer = createTransformer(matchedBlockchains = allBlockchains, query = "no-such-network")
|
||||
|
||||
// Act
|
||||
val result = transformer.transform(createPrevState())
|
||||
|
||||
// Assert
|
||||
assertThat(result.networks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN selected network ids WHEN transform THEN isSelected reflects selection`() {
|
||||
// Arrange
|
||||
val transformer = createTransformer(
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = setOf(ethereum.toNetworkId(), polygon.toNetworkId()),
|
||||
)
|
||||
|
||||
// Act
|
||||
val selectionById = transformer.transform(createPrevState()).networks.associate { it.id to it.isSelected }
|
||||
|
||||
// Assert
|
||||
assertThat(selectionById).containsExactly(
|
||||
ethereum.toNetworkId(), true,
|
||||
bitcoin.toNetworkId(), false,
|
||||
polygon.toNetworkId(), true,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN item WHEN onCheckedChange invoked THEN onToggle called with network id`() {
|
||||
// Arrange
|
||||
val toggled = mutableListOf<String>()
|
||||
val transformer = createTransformer(
|
||||
matchedBlockchains = listOf(ethereum),
|
||||
onToggle = toggled::add,
|
||||
)
|
||||
val item = transformer.transform(createPrevState()).networks.single()
|
||||
|
||||
// Act
|
||||
item.onCheckedChange()
|
||||
|
||||
// Assert
|
||||
assertThat(toggled).containsExactly(ethereum.toNetworkId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN non-empty selection WHEN transform THEN done button enabled`() {
|
||||
// Arrange
|
||||
val transformer = createTransformer(
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = setOf(ethereum.toNetworkId()),
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = transformer.transform(createPrevState(isDoneEnabled = false))
|
||||
|
||||
// Assert
|
||||
assertThat(result.doneButton.isEnabled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN empty selection WHEN transform THEN done button disabled`() {
|
||||
// Arrange
|
||||
val transformer = createTransformer(
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = emptySet(),
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = transformer.transform(createPrevState(isDoneEnabled = true))
|
||||
|
||||
// Assert
|
||||
assertThat(result.doneButton.isEnabled).isFalse()
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun `WHEN transform THEN select-all button state matches selection and search`(model: SelectAllButtonModel) {
|
||||
// Arrange
|
||||
val transformer = createTransformer(
|
||||
matchedBlockchains = model.matchedBlockchains,
|
||||
selectedNetworkIds = model.selectedNetworkIds,
|
||||
isSearchActive = model.isSearchActive,
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = transformer.transform(createPrevState())
|
||||
|
||||
// Assert
|
||||
assertThat(result.selectAllButton).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
SelectAllButtonModel(
|
||||
description = "search active hides the button even when all selected",
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = allBlockchains.map { it.toNetworkId() }.toSet(),
|
||||
isSearchActive = true,
|
||||
expected = SelectAllButtonUM.Empty,
|
||||
),
|
||||
SelectAllButtonModel(
|
||||
description = "search active hides the button when none selected",
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = emptySet(),
|
||||
isSearchActive = true,
|
||||
expected = SelectAllButtonUM.Empty,
|
||||
),
|
||||
SelectAllButtonModel(
|
||||
description = "all selected shows Clear All",
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = allBlockchains.map { it.toNetworkId() }.toSet(),
|
||||
isSearchActive = false,
|
||||
expected = SelectAllButtonUM.ClearAll,
|
||||
),
|
||||
SelectAllButtonModel(
|
||||
description = "partial selection shows Select All",
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = setOf(ethereum.toNetworkId()),
|
||||
isSearchActive = false,
|
||||
expected = SelectAllButtonUM.SelectAll,
|
||||
),
|
||||
SelectAllButtonModel(
|
||||
description = "no selection shows Select All",
|
||||
matchedBlockchains = allBlockchains,
|
||||
selectedNetworkIds = emptySet(),
|
||||
isSearchActive = false,
|
||||
expected = SelectAllButtonUM.SelectAll,
|
||||
),
|
||||
SelectAllButtonModel(
|
||||
description = "no matched networks shows Select All",
|
||||
matchedBlockchains = emptyList(),
|
||||
selectedNetworkIds = emptySet(),
|
||||
isSearchActive = false,
|
||||
expected = SelectAllButtonUM.SelectAll,
|
||||
),
|
||||
)
|
||||
|
||||
private fun createTransformer(
|
||||
matchedBlockchains: List<Blockchain>,
|
||||
query: String = "",
|
||||
selectedNetworkIds: Set<String> = emptySet(),
|
||||
isSearchActive: Boolean = false,
|
||||
onToggle: (String) -> Unit = {},
|
||||
) = UpdateNetworksContentTransformer(
|
||||
matchedBlockchains = matchedBlockchains,
|
||||
query = query,
|
||||
selectedNetworkIds = selectedNetworkIds,
|
||||
isSearchActive = isSearchActive,
|
||||
onToggle = onToggle,
|
||||
)
|
||||
|
||||
private fun createPrevState(isDoneEnabled: Boolean = false) = SelectNetworksUM(
|
||||
searchBar = TangemSearch.State(
|
||||
placeholderText = resourceReference(R.string.common_search),
|
||||
query = "",
|
||||
onQueryChange = {},
|
||||
isActive = false,
|
||||
onActiveChange = {},
|
||||
),
|
||||
networks = persistentListOf(),
|
||||
selectAllButton = SelectAllButtonUM.Empty,
|
||||
doneButton = TangemButtonUM(
|
||||
text = TextReference.Res(R.string.common_done),
|
||||
type = TangemButtonType.Primary,
|
||||
isEnabled = isDoneEnabled,
|
||||
onClick = {},
|
||||
),
|
||||
onBackClick = {},
|
||||
onSelectAllClick = {},
|
||||
)
|
||||
|
||||
internal data class SelectAllButtonModel(
|
||||
val description: String,
|
||||
val matchedBlockchains: List<Blockchain>,
|
||||
val selectedNetworkIds: Set<String>,
|
||||
val isSearchActive: Boolean,
|
||||
val expected: SelectAllButtonUM,
|
||||
) {
|
||||
override fun toString(): String = description
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue