From 3cf222aa32e516f80a7d0c2d366be2e9e83be267 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Aug 2024 12:53:14 +0400 Subject: [PATCH 1/3] Updated on 2026-08-14 --- .../CustomTokenDerivationInputComponent.kt | 3 +- ...aultCustomTokenDerivationInputComponent.kt | 127 ++++++++++++++++++ .../impl/DefaultManageTokensComponent.kt | 8 +- .../managetokens/di/ComponentModule.kt | 16 +-- .../CustomTokenSelectorDialogConfig.kt | 13 ++ ...ig.kt => ManageTokensBottomSheetConfig.kt} | 4 +- .../managetokens/model/ManageTokensModel.kt | 6 +- 7 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenDerivationInputComponent.kt create mode 100644 features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorDialogConfig.kt rename features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/managetokens/{BottomSheetConfig.kt => ManageTokensBottomSheetConfig.kt} (73%) diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenDerivationInputComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenDerivationInputComponent.kt index 1530c3d205..383d94f073 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenDerivationInputComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenDerivationInputComponent.kt @@ -3,12 +3,13 @@ package com.tangem.features.managetokens.component import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableDialogComponent import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath internal interface CustomTokenDerivationInputComponent : ComposableDialogComponent { data class Params( val userWalletId: UserWalletId, - val onConfirm: (String) -> Unit, + val onConfirm: (SelectedDerivationPath) -> Unit, val onDismiss: () -> Unit, ) diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenDerivationInputComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenDerivationInputComponent.kt new file mode 100644 index 0000000000..0695290569 --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenDerivationInputComponent.kt @@ -0,0 +1,127 @@ +package com.tangem.features.managetokens.component.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.text.input.TextFieldValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import arrow.core.getOrElse +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.managetokens.ValidateDerivationPathUseCase +import com.tangem.domain.managetokens.model.exceptoin.DerivationPathValidationException +import com.tangem.domain.tokens.model.Network +import com.tangem.features.managetokens.component.CustomTokenDerivationInputComponent +import com.tangem.features.managetokens.entity.customtoken.CustomDerivationInputUM +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.impl.R +import com.tangem.features.managetokens.ui.dialog.CustomDerivationInputDialog +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.* + +internal class DefaultCustomTokenDerivationInputComponent @AssistedInject constructor( + @Assisted context: AppComponentContext, + @Assisted private val params: CustomTokenDerivationInputComponent.Params, + private val validateDerivationPathUseCase: ValidateDerivationPathUseCase, +) : CustomTokenDerivationInputComponent, AppComponentContext by context { + + private val state: MutableStateFlow = MutableStateFlow( + value = getInitialState(), + ) + + init { + observeValueUpdates() + } + + override fun dismiss() { + params.onDismiss() + } + + @Composable + override fun Dialog() { + val state by state.collectAsStateWithLifecycle() + + CustomDerivationInputDialog( + model = state, + onDismiss = ::dismiss, + ) + } + + @OptIn(FlowPreview::class) + private fun observeValueUpdates() { + state + .map { it.value.text } + .distinctUntilChanged() + .sample(periodMillis = 1_000) + .onEach(::validateValue) + .launchIn(componentScope) + } + + private fun getInitialState(): CustomDerivationInputUM = CustomDerivationInputUM( + value = TextFieldValue(), + error = null, + updateValue = ::updateValue, + isConfirmEnabled = false, + onConfirm = ::confirm, + ) + + private fun validateValue(value: String) { + validateDerivationPathUseCase(value).getOrElse { e -> + updateWithValidationError(e) + return + } + + state.update { state -> + state.copy( + error = null, + isConfirmEnabled = true, + ) + } + } + + private fun updateWithValidationError(e: DerivationPathValidationException) { + state.update { state -> + state.copy( + error = when (e) { + DerivationPathValidationException.Empty -> null + DerivationPathValidationException.Invalid -> { + resourceReference(R.string.custom_token_invalid_derivation_path) + } + }, + isConfirmEnabled = false, + ) + } + } + + private fun updateValue(value: TextFieldValue) { + state.update { state -> + state.copy(value = value) + } + } + + private fun confirm() { + if (state.value.error != null && !state.value.isConfirmEnabled) { + return + } + + val value = state.value.value.text + val model = SelectedDerivationPath( + id = null, + value = Network.DerivationPath.Custom(value), + networkName = stringReference(value = value), + ) + + params.onConfirm(model) + } + + @AssistedFactory + interface Factory : CustomTokenDerivationInputComponent.Factory { + override fun create( + context: AppComponentContext, + params: CustomTokenDerivationInputComponent.Params, + ): DefaultCustomTokenDerivationInputComponent + } +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt index 98df4a4346..2b513d23cb 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt @@ -15,7 +15,7 @@ import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.features.managetokens.component.AddCustomTokenComponent import com.tangem.features.managetokens.component.ManageTokensComponent -import com.tangem.features.managetokens.entity.managetokens.BottomSheetConfig +import com.tangem.features.managetokens.entity.managetokens.ManageTokensBottomSheetConfig import com.tangem.features.managetokens.model.ManageTokensModel import com.tangem.features.managetokens.ui.ManageTokensScreen import dagger.assisted.Assisted @@ -32,7 +32,7 @@ internal class DefaultManageTokensComponent @AssistedInject constructor( private val bottomSheetSlot = childSlot( source = model.bottomSheetNavigation, - serializer = BottomSheetConfig.serializer(), + serializer = ManageTokensBottomSheetConfig.serializer(), handleBackButton = false, childFactory = ::bottomSheetChild, ) @@ -53,10 +53,10 @@ internal class DefaultManageTokensComponent @AssistedInject constructor( } private fun bottomSheetChild( - config: BottomSheetConfig, + config: ManageTokensBottomSheetConfig, componentContext: ComponentContext, ): ComposableBottomSheetComponent = when (config) { - is BottomSheetConfig.AddCustomToken -> { + is ManageTokensBottomSheetConfig.AddCustomToken -> { addCustomTokenComponentFactory.create( context = childByContext(componentContext), params = AddCustomTokenComponent.Params( diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ComponentModule.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ComponentModule.kt index 9de5afa38c..ea21e92af1 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ComponentModule.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ComponentModule.kt @@ -1,13 +1,7 @@ package com.tangem.features.managetokens.di -import com.tangem.features.managetokens.component.AddCustomTokenComponent -import com.tangem.features.managetokens.component.CustomTokenFormComponent -import com.tangem.features.managetokens.component.CustomTokenSelectorComponent -import com.tangem.features.managetokens.component.ManageTokensComponent -import com.tangem.features.managetokens.component.impl.DefaultAddCustomTokenComponent -import com.tangem.features.managetokens.component.impl.DefaultCustomTokenFormComponent -import com.tangem.features.managetokens.component.impl.DefaultCustomTokenSelectorComponent -import com.tangem.features.managetokens.component.impl.DefaultManageTokensComponent +import com.tangem.features.managetokens.component.* +import com.tangem.features.managetokens.component.impl.* import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -39,4 +33,10 @@ internal interface ComponentModule { fun bindCustomTokenFormComponentFactory( factory: DefaultCustomTokenFormComponent.Factory, ): CustomTokenFormComponent.Factory + + @Binds + @Singleton + fun bindCustomTokenDerivationInputComponentFactory( + factory: DefaultCustomTokenDerivationInputComponent.Factory, + ): CustomTokenDerivationInputComponent.Factory } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorDialogConfig.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorDialogConfig.kt new file mode 100644 index 0000000000..19b090720b --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorDialogConfig.kt @@ -0,0 +1,13 @@ +package com.tangem.features.managetokens.entity.customtoken + +import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.serialization.Serializable + +@Serializable +internal sealed class CustomTokenSelectorDialogConfig { + + @Serializable + data class CustomDerivationInput( + val userWalletId: UserWalletId, + ) : CustomTokenSelectorDialogConfig() +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/managetokens/BottomSheetConfig.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/managetokens/ManageTokensBottomSheetConfig.kt similarity index 73% rename from features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/managetokens/BottomSheetConfig.kt rename to features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/managetokens/ManageTokensBottomSheetConfig.kt index 102140a57c..96f7553288 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/managetokens/BottomSheetConfig.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/managetokens/ManageTokensBottomSheetConfig.kt @@ -4,10 +4,10 @@ import com.tangem.domain.wallets.models.UserWalletId import kotlinx.serialization.Serializable @Serializable -internal sealed class BottomSheetConfig { +internal sealed class ManageTokensBottomSheetConfig { @Serializable data class AddCustomToken( val userWalletId: UserWalletId, - ) : BottomSheetConfig() + ) : ManageTokensBottomSheetConfig() } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt index b5a235ee3a..581f40fc1b 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt @@ -18,7 +18,7 @@ import com.tangem.domain.managetokens.SaveManagedTokensUseCase import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.component.ManageTokensComponent import com.tangem.features.managetokens.entity.item.CurrencyItemUM -import com.tangem.features.managetokens.entity.managetokens.BottomSheetConfig +import com.tangem.features.managetokens.entity.managetokens.ManageTokensBottomSheetConfig import com.tangem.features.managetokens.entity.managetokens.ManageTokensTopBarUM import com.tangem.features.managetokens.entity.managetokens.ManageTokensUM import com.tangem.features.managetokens.impl.R @@ -47,7 +47,7 @@ internal class ManageTokensModel @Inject constructor( private val params: ManageTokensComponent.Params = paramsContainer.require() val state: MutableStateFlow = MutableStateFlow(getInitialState(params.userWalletId)) - val bottomSheetNavigation: SlotNavigation = SlotNavigation() + val bottomSheetNavigation: SlotNavigation = SlotNavigation() init { manageTokensListManager.uiItems @@ -228,7 +228,7 @@ internal class ManageTokensModel @Inject constructor( private fun navigateToAddCustomToken() { params.userWalletId?.let { - bottomSheetNavigation.activate(BottomSheetConfig.AddCustomToken(it)) + bottomSheetNavigation.activate(ManageTokensBottomSheetConfig.AddCustomToken(it)) } } From a99b3372700504a4e2dfc35f6ccd22c4f0e28043 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Aug 2024 13:07:15 +0400 Subject: [PATCH 2/3] Updated on 2026-08-14 --- .../component/CustomTokenFormComponent.kt | 2 +- .../component/CustomTokenSelectorComponent.kt | 1 + .../impl/DefaultAddCustomTokenComponent.kt | 15 +- .../DefaultCustomTokenSelectorComponent.kt | 40 +++- .../preview/PreviewAddCustomTokenComponent.kt | 3 +- .../PreviewCustomTokenSelectorComponent.kt | 7 +- .../features/managetokens/di/ModelModule.kt | 12 ++ .../customtoken/AddCustomTokenConfig.kt | 2 +- .../customtoken/CustomTokenSelectorUM.kt | 1 + .../model/CustomTokenFormModel.kt | 11 +- .../model/CustomTokenSelectorModel.kt | 172 ++++++++++++++++++ .../utils/mapper/CurrencyNetworksMapper.kt | 20 +- .../utils/mapper/DerivationPathMapper.kt | 34 ++++ 13 files changed, 296 insertions(+), 24 deletions(-) create mode 100644 features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenSelectorModel.kt create mode 100644 features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/DerivationPathMapper.kt diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt index e9fc3c448f..b7c219e560 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt @@ -12,7 +12,7 @@ internal interface CustomTokenFormComponent : ComposableContentComponent { data class Params( val userWalletId: UserWalletId, val network: SelectedNetwork, - val derivationPath: SelectedDerivationPath, + val derivationPath: SelectedDerivationPath?, val formValues: CustomTokenFormValues, val onSelectNetworkClick: (CustomTokenFormValues) -> Unit, val onSelectDerivationPathClick: (CustomTokenFormValues) -> Unit, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenSelectorComponent.kt index b3e4a159a0..d1f5e8aaf6 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenSelectorComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenSelectorComponent.kt @@ -18,6 +18,7 @@ internal interface CustomTokenSelectorComponent : ComposableContentComponent { data class DerivationPathSelector( val userWalletId: UserWalletId, + val selectedNetwork: SelectedNetwork, val selectedDerivationPath: SelectedDerivationPath?, val onDerivationPathSelected: (SelectedDerivationPath) -> Unit, ) : Params() diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultAddCustomTokenComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultAddCustomTokenComponent.kt index 3679e6f929..bde22e12c6 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultAddCustomTokenComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultAddCustomTokenComponent.kt @@ -12,7 +12,6 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.core.ui.extensions.resourceReference import com.tangem.features.managetokens.component.AddCustomTokenComponent import com.tangem.features.managetokens.component.CustomTokenFormComponent import com.tangem.features.managetokens.component.CustomTokenSelectorComponent @@ -20,7 +19,6 @@ import com.tangem.features.managetokens.entity.customtoken.AddCustomTokenConfig import com.tangem.features.managetokens.entity.customtoken.CustomTokenFormValues import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork -import com.tangem.features.managetokens.impl.R import com.tangem.features.managetokens.ui.AddCustomTokenBottomSheet import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -110,6 +108,9 @@ internal class DefaultAddCustomTokenComponent @AssistedInject constructor( context = childByContext(componentContext), params = CustomTokenSelectorComponent.Params.DerivationPathSelector( userWalletId = config.userWalletId, + selectedNetwork = requireNotNull(config.selectedNetwork) { + "Network is not selected" + }, selectedDerivationPath = config.selectedDerivationPath, onDerivationPathSelected = { derivationPath -> showForm(derivationPath = derivationPath) @@ -122,12 +123,10 @@ internal class DefaultAddCustomTokenComponent @AssistedInject constructor( context = childByContext(componentContext), params = CustomTokenFormComponent.Params( userWalletId = config.userWalletId, - network = config.selectedNetwork ?: error("Network is not selected"), - derivationPath = config.selectedDerivationPath ?: SelectedDerivationPath( - id = config.selectedNetwork.id, - value = config.selectedNetwork.derivationPath, - networkName = resourceReference(R.string.custom_token_derivation_path_default), - ), + network = requireNotNull(config.selectedNetwork) { + "Network is not selected" + }, + derivationPath = config.selectedDerivationPath, formValues = config.formValues, onSelectNetworkClick = ::showNetworkSelector, onSelectDerivationPathClick = ::showDerivationPathSelector, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenSelectorComponent.kt index c748d7363f..2d690a862c 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenSelectorComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultCustomTokenSelectorComponent.kt @@ -4,33 +4,61 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.router.slot.dismiss import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.childByContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.decompose.ComposableDialogComponent +import com.tangem.features.managetokens.component.CustomTokenDerivationInputComponent import com.tangem.features.managetokens.component.CustomTokenSelectorComponent -import com.tangem.features.managetokens.component.preview.PreviewCustomTokenSelectorComponent -import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorUM +import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorDialogConfig +import com.tangem.features.managetokens.model.CustomTokenSelectorModel import com.tangem.features.managetokens.ui.CustomTokenSelectorContent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.MutableStateFlow internal class DefaultCustomTokenSelectorComponent @AssistedInject constructor( @Assisted context: AppComponentContext, @Assisted params: CustomTokenSelectorComponent.Params, + private val customTokenDerivationInputComponentFactory: CustomTokenDerivationInputComponent.Factory, ) : CustomTokenSelectorComponent, AppComponentContext by context { - private val state: MutableStateFlow = MutableStateFlow( - value = PreviewCustomTokenSelectorComponent(params = params).previewState, + private val model: CustomTokenSelectorModel = getOrCreateModel(params) + private val dialogSlot = childSlot( + source = model.dialogNavigation, + serializer = CustomTokenSelectorDialogConfig.serializer(), + childFactory = ::createDialog, ) + private fun createDialog( + config: CustomTokenSelectorDialogConfig, + context: ComponentContext, + ): ComposableDialogComponent = when (config) { + is CustomTokenSelectorDialogConfig.CustomDerivationInput -> customTokenDerivationInputComponentFactory.create( + context = childByContext(context), + params = CustomTokenDerivationInputComponent.Params( + userWalletId = config.userWalletId, + onConfirm = model::selectCustomDerivationPath, + onDismiss = model.dialogNavigation::dismiss, + ), + ) + } + @Composable override fun Content(modifier: Modifier) { - val state by state.collectAsStateWithLifecycle() + val state by model.state.collectAsStateWithLifecycle() + val dialog by dialogSlot.subscribeAsState() CustomTokenSelectorContent( modifier = modifier, model = state, ) + + dialog.child?.instance?.Dialog() } @AssistedFactory diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt index a09d7b2b93..79d2f3bdcd 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt @@ -60,7 +60,8 @@ internal class PreviewAddCustomTokenComponent( PreviewCustomTokenSelectorComponent( params = CustomTokenSelectorComponent.Params.DerivationPathSelector( userWalletId = state.userWalletId, - selectedDerivationPath = state.selectedDerivationPath, + selectedNetwork = state.selectedNetwork!!, + selectedDerivationPath = state.selectedDerivationPath!!, onDerivationPathSelected = {}, ), ).Content(modifier) diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt index 1ebcff8b5d..0a37116d7d 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt @@ -35,7 +35,7 @@ internal class PreviewCustomTokenSelectorComponent( ) DerivationPathUM( - id = d.id.value, + id = d.id?.value ?: "", value = d.value.value.orEmpty(), networkName = d.networkName, isSelected = d.value == params.selectedDerivationPath?.value, @@ -75,7 +75,10 @@ internal class PreviewCustomTokenSelectorComponent( val previewState = CustomTokenSelectorUM( header = when (params) { - is Params.DerivationPathSelector -> CustomTokenSelectorUM.HeaderUM.CustomDerivationButton({}) + is Params.DerivationPathSelector -> CustomTokenSelectorUM.HeaderUM.CustomDerivationButton( + value = null, + onClick = {}, + ) is Params.NetworkSelector -> if (params.selectedNetwork == null) { CustomTokenSelectorUM.HeaderUM.Description } else { diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ModelModule.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ModelModule.kt index 572c2624cb..0f33d5994f 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ModelModule.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/di/ModelModule.kt @@ -2,6 +2,8 @@ package com.tangem.features.managetokens.di import com.tangem.core.decompose.di.DecomposeComponent import com.tangem.core.decompose.model.Model +import com.tangem.features.managetokens.model.CustomTokenFormModel +import com.tangem.features.managetokens.model.CustomTokenSelectorModel import com.tangem.features.managetokens.model.ManageTokensModel import dagger.Binds import dagger.Module @@ -17,4 +19,14 @@ internal interface ModelModule { @IntoMap @ClassKey(ManageTokensModel::class) fun provideManageTokensModel(model: ManageTokensModel): Model + + @Binds + @IntoMap + @ClassKey(CustomTokenFormModel::class) + fun provideCustomTokenFormModel(model: CustomTokenFormModel): Model + + @Binds + @IntoMap + @ClassKey(CustomTokenSelectorModel::class) + fun provideCustomTokenSelectorModel(model: CustomTokenSelectorModel): Model } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/AddCustomTokenConfig.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/AddCustomTokenConfig.kt index 6a43cdc40d..1c5de5c702 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/AddCustomTokenConfig.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/AddCustomTokenConfig.kt @@ -34,7 +34,7 @@ internal data class SelectedNetwork( @Serializable internal data class SelectedDerivationPath( - val id: Network.ID, + val id: Network.ID?, val value: Network.DerivationPath, val networkName: TextReference, ) \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorUM.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorUM.kt index 25c08fac10..d607a8b959 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorUM.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/customtoken/CustomTokenSelectorUM.kt @@ -17,6 +17,7 @@ internal data class CustomTokenSelectorUM( data object Description : HeaderUM() data class CustomDerivationButton( + val value: String?, val onClick: () -> Unit, ) : HeaderUM() } diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt index 25fbf11d48..2fa77b8e92 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt @@ -14,6 +14,7 @@ import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.Network import com.tangem.features.managetokens.component.CustomTokenFormComponent import com.tangem.features.managetokens.entity.customtoken.ClickableFieldUM import com.tangem.features.managetokens.entity.customtoken.CustomTokenFormUM @@ -57,7 +58,7 @@ internal class CustomTokenFormModel @Inject constructor( customCurrencyValidator.createCoin( userWalletId = params.userWalletId, networkId = params.network.id, - derivationPath = params.derivationPath.value, + derivationPath = getDerivationPath(), ) } } @@ -77,7 +78,7 @@ internal class CustomTokenFormModel @Inject constructor( }, derivationPath = ClickableFieldUM( label = resourceReference(R.string.custom_token_derivation_path), - value = if (params.derivationPath.id == params.network.id) { + value = if (params.derivationPath == null || params.derivationPath.id == params.network.id) { resourceReference(R.string.custom_token_derivation_path_default) } else { params.derivationPath.networkName @@ -104,7 +105,7 @@ internal class CustomTokenFormModel @Inject constructor( customCurrencyValidator.validateForm( userWalletId = params.userWalletId, networkId = params.network.id, - derivationPath = params.derivationPath.value, + derivationPath = getDerivationPath(), formValues = formValues, ) } @@ -266,6 +267,10 @@ internal class CustomTokenFormModel @Inject constructor( return formValues.fillValues(form) } + private fun getDerivationPath(): Network.DerivationPath { + return params.derivationPath?.value ?: params.network.derivationPath + } + private fun updateContractAddress(value: String) { state.update { state -> state.updateTokenForm { diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenSelectorModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenSelectorModel.kt new file mode 100644 index 0000000000..611c6a1fe9 --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenSelectorModel.kt @@ -0,0 +1,172 @@ +package com.tangem.features.managetokens.model + +import arrow.core.getOrElse +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.tangem.core.decompose.di.ComponentScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.domain.managetokens.GetSupportedNetworksUseCase +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.managetokens.component.CustomTokenSelectorComponent +import com.tangem.features.managetokens.component.CustomTokenSelectorComponent.Params.DerivationPathSelector +import com.tangem.features.managetokens.component.CustomTokenSelectorComponent.Params.NetworkSelector +import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorDialogConfig +import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorUM +import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorUM.HeaderUM +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork +import com.tangem.features.managetokens.entity.item.DerivationPathUM +import com.tangem.features.managetokens.entity.item.SelectableItemUM +import com.tangem.features.managetokens.impl.R +import com.tangem.features.managetokens.utils.mapper.toCurrencyNetworkModel +import com.tangem.features.managetokens.utils.mapper.toDerivationPathModel +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@ComponentScoped +internal class CustomTokenSelectorModel @Inject constructor( + override val dispatchers: CoroutineDispatcherProvider, + private val getSupportedNetworksUseCase: GetSupportedNetworksUseCase, + private val messageSender: UiMessageSender, + paramsContainer: ParamsContainer, +) : Model() { + + private val params: CustomTokenSelectorComponent.Params = paramsContainer.require() + + val dialogNavigation: SlotNavigation = SlotNavigation() + + val state: MutableStateFlow = MutableStateFlow( + value = getInitialState(), + ) + + init { + loadItems() + } + + private fun getInitialState(): CustomTokenSelectorUM = when (params) { + is NetworkSelector -> CustomTokenSelectorUM( + header = if (params.selectedNetwork == null) { + HeaderUM.Description + } else { + HeaderUM.None + }, + items = persistentListOf(), + ) + is DerivationPathSelector -> CustomTokenSelectorUM( + header = HeaderUM.CustomDerivationButton( + value = (params.selectedDerivationPath?.value as? Network.DerivationPath.Custom)?.value, + onClick = ::showCustomDerivationInput, + ), + items = persistentListOf(), + ) + } + + private fun loadItems() = modelScope.launch { + val items = when (params) { + is NetworkSelector -> loadNetworks(params).toImmutableList() + is DerivationPathSelector -> loadDerivationPaths(params).toImmutableList() + } + + state.update { state -> + state.copy(items = items) + } + } + + private suspend fun loadNetworks(selector: NetworkSelector): List { + return getSupportedNetworks(selector.userWalletId).map { network -> + network.toCurrencyNetworkModel( + isSelected = network.id == selector.selectedNetwork?.id, + onSelectedStateChange = { + val model = SelectedNetwork( + id = network.id, + name = stringReference(network.name), + derivationPath = network.derivationPath, + canHandleTokens = network.canHandleTokens, + ) + + selector.onNetworkSelected(model) + }, + ) + } + } + + private suspend fun loadDerivationPaths(selector: DerivationPathSelector): List { + val derivationPaths = mutableListOf() + val defaultPath = selector.selectedNetwork.let { network -> + network.toDerivationPathModel( + isSelected = network.id == selector.selectedDerivationPath?.id, + onSelectedStateChange = { + val model = SelectedDerivationPath( + id = network.id, + networkName = resourceReference(R.string.custom_token_derivation_path_default), + value = network.derivationPath, + ) + + selector.onDerivationPathSelected(model) + }, + ) + } + + if (defaultPath != null) { + derivationPaths.add(defaultPath) + } + + getSupportedNetworks(selector.userWalletId) + .mapNotNullTo(derivationPaths) { network -> + if (network.id == selector.selectedNetwork.id) { + return@mapNotNullTo null // Skip default path + } + + network.toDerivationPathModel( + isSelected = network.id == selector.selectedDerivationPath?.id, + onSelectedStateChange = { + val model = SelectedDerivationPath( + id = network.id, + networkName = stringReference(network.name), + value = network.derivationPath, + ) + + selector.onDerivationPathSelected(model) + }, + ) + } + + return derivationPaths + } + + private suspend fun getSupportedNetworks(userWalletId: UserWalletId): List { + return getSupportedNetworksUseCase(userWalletId).getOrElse { e -> + val message = SnackbarMessage(message = resourceReference(R.string.common_unknown_error)) + messageSender.send(message) + + emptyList() + } + } + + private fun showCustomDerivationInput() { + val config = when (params) { + is NetworkSelector -> return + is DerivationPathSelector -> CustomTokenSelectorDialogConfig.CustomDerivationInput(params.userWalletId) + } + + dialogNavigation.activate(config) + } + + fun selectCustomDerivationPath(value: SelectedDerivationPath) { + when (params) { + is NetworkSelector -> return + is DerivationPathSelector -> params.onDerivationPathSelected(value) + } + } +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/CurrencyNetworksMapper.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/CurrencyNetworksMapper.kt index 6fdacc516a..2e88d82956 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/CurrencyNetworksMapper.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/CurrencyNetworksMapper.kt @@ -2,6 +2,7 @@ package com.tangem.features.managetokens.utils.mapper import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork +import com.tangem.domain.tokens.model.Network import com.tangem.features.managetokens.entity.item.CurrencyItemUM.Basic.NetworksUM import com.tangem.features.managetokens.entity.item.CurrencyNetworkUM import com.tangem.features.managetokens.utils.ui.getIconRes @@ -15,7 +16,7 @@ internal fun ManagedCryptoCurrency.Token.toUiNetworksModel( return if (isExpanded) { NetworksUM.Expanded( networks = availableNetworks.map { - it.toUiModel( + it.toCurrencyNetworkModel( isSelected = it.network in addedIn, isEditable = isItemsEditable, onSelectedStateChange = onSelectedStateChange, @@ -27,7 +28,22 @@ internal fun ManagedCryptoCurrency.Token.toUiNetworksModel( } } -private fun SourceNetwork.toUiModel( +internal fun Network.toCurrencyNetworkModel( + isSelected: Boolean, + onSelectedStateChange: (Boolean) -> Unit, +): CurrencyNetworkUM { + return CurrencyNetworkUM( + network = this, + name = name, + iconResId = id.getIconRes(isColored = true), + isSelected = isSelected, + type = standardType.name, + isMainNetwork = false, + onSelectedStateChange = onSelectedStateChange, + ) +} + +private fun SourceNetwork.toCurrencyNetworkModel( isSelected: Boolean, isEditable: Boolean, onSelectedStateChange: (SourceNetwork, Boolean) -> Unit, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/DerivationPathMapper.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/DerivationPathMapper.kt new file mode 100644 index 0000000000..2e843ec716 --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/mapper/DerivationPathMapper.kt @@ -0,0 +1,34 @@ +package com.tangem.features.managetokens.utils.mapper + +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.tokens.model.Network +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork +import com.tangem.features.managetokens.entity.item.DerivationPathUM +import com.tangem.features.managetokens.impl.R + +internal fun Network.toDerivationPathModel( + isSelected: Boolean, + onSelectedStateChange: (Boolean) -> Unit, +): DerivationPathUM? { + return DerivationPathUM( + id = id.value, + value = derivationPath.value ?: return null, + networkName = stringReference(name), + isSelected = isSelected, + onSelectedStateChange = onSelectedStateChange, + ) +} + +internal fun SelectedNetwork.toDerivationPathModel( + isSelected: Boolean, + onSelectedStateChange: (Boolean) -> Unit, +): DerivationPathUM? { + return DerivationPathUM( + id = id.value, + value = derivationPath.value ?: return null, + networkName = resourceReference(R.string.custom_token_derivation_path_default), + isSelected = isSelected, + onSelectedStateChange = onSelectedStateChange, + ) +} \ No newline at end of file From f78d948799d5a241446cbd49ac626e87f89fc1d8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Aug 2024 18:06:37 +0400 Subject: [PATCH 3/3] Updated on 2026-08-14 --- .../ui/CustomTokenSelectorContent.kt | 104 ++++++++++++------ 1 file changed, 68 insertions(+), 36 deletions(-) diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenSelectorContent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenSelectorContent.kt index 5984b55cf3..476433fb7d 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenSelectorContent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenSelectorContent.kt @@ -35,6 +35,7 @@ import com.tangem.features.managetokens.component.CustomTokenSelectorComponent import com.tangem.features.managetokens.component.preview.PreviewCustomTokenSelectorComponent import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorUM import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork import com.tangem.features.managetokens.entity.item.CurrencyNetworkUM import com.tangem.features.managetokens.entity.item.DerivationPathUM import com.tangem.features.managetokens.impl.R @@ -110,6 +111,8 @@ private fun Header(header: CustomTokenSelectorUM.HeaderUM, modifier: Modifier = is CustomTokenSelectorUM.HeaderUM.CustomDerivationButton -> { CustomDerivationButton( modifier = modifier.padding(vertical = TangemTheme.dimens.spacing16), + enteredDerivationPath = header.value, + isSelected = header.value != null, onClick = header.onClick, ) } @@ -164,38 +167,41 @@ private fun NetworkItem(model: CurrencyNetworkUM, modifier: Modifier = Modifier) ) }, action = { - AnimatedVisibility( - modifier = Modifier.size(TangemTheme.dimens.size24), - visible = model.isSelected, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_check_24), - tint = TangemTheme.colors.icon.accent, - contentDescription = null, - ) - } + SelectedIcon(isVisible = model.isSelected) }, ) } @Composable -private fun CustomDerivationButton(onClick: () -> Unit, modifier: Modifier = Modifier) { +private fun CustomDerivationButton( + enteredDerivationPath: String?, + isSelected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { InformationBlock( - modifier = modifier.clickable(onClick = onClick), + modifier = modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .clickable(onClick = onClick), title = { - Text( - text = stringResource(id = R.string.custom_token_custom_derivation), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.secondary, - ) + Column( + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), + ) { + Text( + text = stringResource(id = R.string.custom_token_custom_derivation), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.secondary, + ) + Text( + modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), + text = enteredDerivationPath ?: stringResource(id = R.string.custom_token_custom_derivation_title), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + } }, - content = { - Text( - modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), - text = stringResource(id = R.string.custom_token_custom_derivation_title), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.primary1, - ) + action = { + SelectedIcon(isVisible = isSelected) }, ) } @@ -206,23 +212,43 @@ private fun DerivationPathItem(model: DerivationPathUM, modifier: Modifier = Mod modifier = modifier, shape = RectangleShape, title = { - Text( - text = model.networkName.resolveReference(), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.secondary, - ) + Column( + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), + ) { + Text( + text = model.networkName.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.secondary, + ) + + Text( + modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), + text = model.value, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + } }, - content = { - Text( - modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), - text = model.value, - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.primary1, - ) + action = { + SelectedIcon(isVisible = model.isSelected) }, ) } +@Composable +private fun SelectedIcon(isVisible: Boolean, modifier: Modifier = Modifier) { + AnimatedVisibility( + modifier = modifier.size(TangemTheme.dimens.size24), + visible = isVisible, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_check_24), + tint = TangemTheme.colors.icon.accent, + contentDescription = null, + ) + } +} + // region Preview @Composable @Preview(showBackground = true, widthDp = 360) @@ -243,6 +269,12 @@ private class CustomTokenNetworkSelectorComponentPreviewProvider : PreviewCustomTokenSelectorComponent( params = CustomTokenSelectorComponent.Params.DerivationPathSelector( userWalletId = UserWalletId(stringValue = "321"), + selectedNetwork = SelectedNetwork( + id = Network.ID(value = "0"), + name = stringReference("Ethereum"), + derivationPath = Network.DerivationPath.Card("m/44'/0'/0'/0/0"), + canHandleTokens = true, + ), selectedDerivationPath = SelectedDerivationPath( id = Network.ID(value = "0"), value = Network.DerivationPath.Card("m/44'/0'/0'/0/0"),