From 36ce94fb22938b043fe1e9aae26fec9a1cddb861 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Oct 2024 12:15:27 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../core/ui/components/rows/RowComponents.kt | 2 +- features/tester/impl/build.gradle.kts | 3 + .../tester/presentation/TesterActivity.kt | 16 ++ .../ExcludedBlockchainsScreen.kt | 190 ++++++++++++++++++ .../ExcludedBlockchainsViewModel.kt | 37 ++++ .../excludedblockchains/state/BlockchainUM.kt | 10 + .../state/ExcludedBlockchainsScreenUM.kt | 14 ++ .../state/mapper/BlockchainsMappings.kt | 31 +++ .../menu/state/TesterMenuContentState.kt | 1 + .../presentation/menu/ui/TesterMenuScreen.kt | 7 + .../presentation/navigation/TesterScreen.kt | 6 +- .../impl/src/main/res/values/strings.xml | 4 +- 12 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsViewModel.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/BlockchainUM.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/ExcludedBlockchainsScreenUM.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/mapper/BlockchainsMappings.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt index 78c7a724b7..f07edb1802 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt @@ -46,7 +46,7 @@ inline fun RowContentContainer( } @Composable -internal fun RowText( +fun RowText( mainText: String, secondText: String, accentMainText: Boolean, diff --git a/features/tester/impl/build.gradle.kts b/features/tester/impl/build.gradle.kts index 5be818db4b..6897aff661 100644 --- a/features/tester/impl/build.gradle.kts +++ b/features/tester/impl/build.gradle.kts @@ -47,6 +47,9 @@ dependencies { /** Feature Apis */ implementation(projects.features.tester.api) + /* SDK */ + implementation(deps.tangem.blockchain) + /** Other modules */ implementation(projects.common.routing) implementation(projects.libs.crypto) diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt index 10d761ffb9..4ae38f7c83 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt @@ -2,8 +2,10 @@ package com.tangem.feature.tester.presentation import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController @@ -17,6 +19,8 @@ import com.tangem.feature.tester.presentation.actions.TesterActionsScreen import com.tangem.feature.tester.presentation.actions.TesterActionsViewModel import com.tangem.feature.tester.presentation.environments.ui.EnvironmentTogglesScreen import com.tangem.feature.tester.presentation.environments.viewmodels.EnvironmentsTogglesViewModel +import com.tangem.feature.tester.presentation.excludedblockchains.ExcludedBlockchainsScreen +import com.tangem.feature.tester.presentation.excludedblockchains.ExcludedBlockchainsViewModel import com.tangem.feature.tester.presentation.featuretoggles.ui.FeatureTogglesScreen import com.tangem.feature.tester.presentation.featuretoggles.viewmodels.FeatureTogglesViewModel import com.tangem.feature.tester.presentation.menu.state.TesterMenuContentState @@ -70,6 +74,9 @@ internal class TesterActivity : ComposeActivity() { onBackClick = { appRouter.pop { finish() } }, onFeatureTogglesClick = { innerTesterRouter.open(TesterScreen.FEATURE_TOGGLES) }, onEnvironmentTogglesClick = { innerTesterRouter.open(TesterScreen.ENVIRONMENTS_TOGGLES) }, + onExcludedBlockchainsClick = { + innerTesterRouter.open(TesterScreen.EXCLUDED_BLOCKCHAINS) + }, onTesterActionsClick = { innerTesterRouter.open(TesterScreen.TESTER_ACTIONS) }, ), ) @@ -100,6 +107,15 @@ internal class TesterActivity : ComposeActivity() { TesterActionsScreen(state = viewModel.uiState) } + + composable(route = TesterScreen.EXCLUDED_BLOCKCHAINS.name) { + val viewModel = hiltViewModel().apply { + setupNavigation(innerTesterRouter, appFinisher) + } + + val state by viewModel.state.collectAsStateWithLifecycle() + ExcludedBlockchainsScreen(state = state) + } } } } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt new file mode 100644 index 0000000000..2909504146 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt @@ -0,0 +1,190 @@ +package com.tangem.feature.tester.presentation.excludedblockchains + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.FabPosition +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import com.tangem.blockchain.common.Blockchain +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.TangemSwitch +import com.tangem.core.ui.components.appbar.TangemTopAppBar +import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.components.bottomFade +import com.tangem.core.ui.components.fields.SearchBar +import com.tangem.core.ui.components.fields.entity.SearchBarUM +import com.tangem.core.ui.components.notifications.Notification +import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.components.rows.RowContentContainer +import com.tangem.core.ui.components.rows.RowText +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.feature.tester.impl.R +import com.tangem.feature.tester.presentation.excludedblockchains.state.BlockchainUM +import com.tangem.feature.tester.presentation.excludedblockchains.state.ExcludedBlockchainsScreenUM +import com.tangem.feature.tester.presentation.excludedblockchains.state.mapper.toUiModels +import kotlinx.collections.immutable.toPersistentList + +@Composable +internal fun ExcludedBlockchainsScreen(state: ExcludedBlockchainsScreenUM, modifier: Modifier = Modifier) { + Scaffold( + modifier = modifier, + containerColor = TangemTheme.colors.background.primary, + topBar = { + TangemTopAppBar( + title = resourceReference(R.string.excluded_blockchains), + startButton = TopAppBarButtonUM.Back(onBackClicked = state.popBack), + endButton = TopAppBarButtonUM( + iconRes = R.drawable.ic_refresh_24, + onIconClicked = state.onRecoverClick, + ), + ) + }, + floatingActionButtonPosition = FabPosition.Center, + floatingActionButton = { + PrimaryButton( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + text = "Restart App", + onClick = state.onRestartClick, + ) + }, + content = { paddingValues -> + LazyColumn( + modifier = Modifier + .bottomFade() + .padding(paddingValues) + .fillMaxSize(), + contentPadding = PaddingValues( + top = 8.dp, + bottom = 96.dp, + start = 16.dp, + end = 16.dp, + ), + ) { + item { + SearchBar( + modifier = Modifier.padding(bottom = 12.dp), + state = state.search, + ) + } + + item { + TogglesNotification( + modifier = Modifier.padding(vertical = 12.dp), + appVersion = state.appVersion, + isVisible = state.showRecoverWarning, + ) + } + + items( + items = state.blockchains, + key = BlockchainUM::id, + ) { item -> + BlockchainItem( + model = item, + ) + } + } + }, + ) +} + +@Composable +private fun TogglesNotification(appVersion: String, isVisible: Boolean, modifier: Modifier = Modifier) { + if (isVisible) { + Notification( + modifier = modifier, + config = NotificationConfig( + subtitle = resourceReference( + id = R.string.feature_toggles_custom_setup_warning_description, + wrappedList(appVersion), + ), + iconResId = R.drawable.ic_alert_triangle_20, + title = resourceReference(id = R.string.feature_toggles_custom_setup_warning_title), + ), + ) + } +} + +@Composable +private fun BlockchainItem(model: BlockchainUM, modifier: Modifier = Modifier) { + RowContentContainer( + modifier = modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size52), + icon = { + Image( + modifier = Modifier + .align(Alignment.Center) + .size(TangemTheme.dimens.size22), + painter = painterResource(id = model.iconResId), + contentDescription = null, + ) + }, + text = { + RowText( + mainText = model.name, + secondText = model.symbol, + accentMainText = false, + accentSecondText = false, + ) + }, + action = { + TangemSwitch( + checked = model.isExcluded, + onCheckedChange = model.onExcludedStateChange, + ) + }, + ) +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_ExcludedBlockchainsScreen( + @PreviewParameter(ExcludedBlockchainsScreenPreviewProvider::class) params: ExcludedBlockchainsScreenUM, +) { + TangemThemePreview { + ExcludedBlockchainsScreen(params) + } +} + +private class ExcludedBlockchainsScreenPreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + ExcludedBlockchainsScreenUM( + blockchains = Blockchain.entries.toUiModels( + excludedBlockchainsIds = setOf(Blockchain.Ethereum.id, Blockchain.Solana.id), + onExcludedStateChange = { _, _ -> }, + ).toPersistentList(), + showRecoverWarning = true, + appVersion = "1.0.0", + search = SearchBarUM( + placeholderText = resourceReference(R.string.excluded_blockchains_search_placeholder), + query = "", + isActive = false, + onQueryChange = {}, + onActiveChange = {}, + ), + popBack = {}, + onRecoverClick = {}, + onRestartClick = {}, + ), + ) +} +// endregion Preview \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsViewModel.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsViewModel.kt new file mode 100644 index 0000000000..48eed66cbf --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsViewModel.kt @@ -0,0 +1,37 @@ +package com.tangem.feature.tester.presentation.excludedblockchains + +import androidx.lifecycle.ViewModel +import com.tangem.core.toggle.blockchain.MutableExcludedBlockchainsManager +import com.tangem.core.ui.components.fields.entity.SearchBarUM +import com.tangem.core.ui.extensions.stringReference +import com.tangem.feature.tester.presentation.excludedblockchains.state.ExcludedBlockchainsScreenUM +import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@HiltViewModel +internal class ExcludedBlockchainsViewModel @Inject constructor( + private val excludedBlockchainsManager: MutableExcludedBlockchainsManager, +) : ViewModel() { + + val uiState: MutableStateFlow = MutableStateFlow( + value = ExcludedBlockchainsScreenUM( + popBack = {}, + search = SearchBarUM( + placeholderText = stringReference(value = "Filter by name or symbol"), + query = "", + isActive = false, + onQueryChange = {}, + onActiveChange = {}, + ), + blockchains = persistentListOf(), + onRestartClick = {}, + onRestoreClick = {}, + ), + ) + + fun setupNavigation(router: InnerTesterRouter) { + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/BlockchainUM.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/BlockchainUM.kt new file mode 100644 index 0000000000..bf406c0243 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/BlockchainUM.kt @@ -0,0 +1,10 @@ +package com.tangem.feature.tester.presentation.excludedblockchains.state + +internal data class BlockchainUM( + val id: String, + val name: String, + val symbol: String, + val iconResId: Int, + val isExcluded: Boolean, + val onExcludedStateChange: (isExcluded: Boolean) -> Unit, +) \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/ExcludedBlockchainsScreenUM.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/ExcludedBlockchainsScreenUM.kt new file mode 100644 index 0000000000..0513f4b482 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/ExcludedBlockchainsScreenUM.kt @@ -0,0 +1,14 @@ +package com.tangem.feature.tester.presentation.excludedblockchains.state + +import com.tangem.core.ui.components.fields.entity.SearchBarUM +import kotlinx.collections.immutable.PersistentList + +internal data class ExcludedBlockchainsScreenUM( + val popBack: () -> Unit, + val search: SearchBarUM, + val blockchains: PersistentList, + val showRecoverWarning: Boolean, + val appVersion: String, + val onRecoverClick: () -> Unit, + val onRestartClick: () -> Unit, +) \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/mapper/BlockchainsMappings.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/mapper/BlockchainsMappings.kt new file mode 100644 index 0000000000..d27b8b8c7c --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/state/mapper/BlockchainsMappings.kt @@ -0,0 +1,31 @@ +package com.tangem.feature.tester.presentation.excludedblockchains.state.mapper + +import com.tangem.blockchain.common.Blockchain +import com.tangem.core.ui.extensions.getActiveIconRes +import com.tangem.feature.tester.presentation.excludedblockchains.state.BlockchainUM + +internal fun List.toUiModels( + excludedBlockchainsIds: Set, + onExcludedStateChange: (Blockchain, Boolean) -> Unit, +): List = mapNotNull { blockchain -> + blockchain + .takeUnless { it == Blockchain.Unknown || it.isTestnet() } + ?.toUiModel( + isExcluded = blockchain.id in excludedBlockchainsIds, + onExcludedStateChange = { isExcluded -> + onExcludedStateChange(blockchain, isExcluded) + }, + ) +} + .sortedBy(BlockchainUM::name) + .sortedByDescending(BlockchainUM::isExcluded) + +private fun Blockchain.toUiModel(isExcluded: Boolean, onExcludedStateChange: (Boolean) -> Unit): BlockchainUM = + BlockchainUM( + id = id, + name = name, + symbol = currency, + iconResId = getActiveIconRes(id), + isExcluded = isExcluded, + onExcludedStateChange = onExcludedStateChange, + ) \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/state/TesterMenuContentState.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/state/TesterMenuContentState.kt index a074e734b9..2dd6061286 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/state/TesterMenuContentState.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/state/TesterMenuContentState.kt @@ -12,5 +12,6 @@ data class TesterMenuContentState( val onBackClick: () -> Unit, val onFeatureTogglesClick: () -> Unit, val onEnvironmentTogglesClick: () -> Unit, + val onExcludedBlockchainsClick: () -> Unit, val onTesterActionsClick: () -> Unit, ) \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt index 105ea332fd..dd31fe2f08 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt @@ -51,6 +51,12 @@ internal fun TesterMenuScreen(state: TesterMenuContentState) { modifier = Modifier.fillMaxWidth(), ) + PrimaryButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.excluded_blockchains), + onClick = state.onExcludedBlockchainsClick, + ) + PrimaryButton( text = stringResource(R.string.environment_toggles), onClick = state.onEnvironmentTogglesClick, @@ -76,6 +82,7 @@ private fun PreviewTesterMenuScreen() { onBackClick = {}, onFeatureTogglesClick = {}, onEnvironmentTogglesClick = {}, + onExcludedBlockchainsClick = {}, onTesterActionsClick = {}, ), ) diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/TesterScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/TesterScreen.kt index 2d7622855b..5c501ce531 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/TesterScreen.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/TesterScreen.kt @@ -6,5 +6,9 @@ package com.tangem.feature.tester.presentation.navigation [REDACTED_AUTHOR] */ internal enum class TesterScreen { - MENU, FEATURE_TOGGLES, ENVIRONMENTS_TOGGLES, TESTER_ACTIONS + MENU, + FEATURE_TOGGLES, + ENVIRONMENTS_TOGGLES, + TESTER_ACTIONS, + EXCLUDED_BLOCKCHAINS, } \ No newline at end of file diff --git a/features/tester/impl/src/main/res/values/strings.xml b/features/tester/impl/src/main/res/values/strings.xml index 969d34bce4..95ad57f1ad 100644 --- a/features/tester/impl/src/main/res/values/strings.xml +++ b/features/tester/impl/src/main/res/values/strings.xml @@ -3,10 +3,12 @@ Tester menu Feature toggles Custom setup - Feature toggles differs from v%s config + Toggles differs from v%s config Restart app Environment toggles Tester actions Hide all currencies Toggle app theme - %s + Excluded blockchains + Filter by name or symbol