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)) } }