Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-30 12:53:14 +04:00
parent 91d8932543
commit 3cf222aa32
7 changed files with 159 additions and 18 deletions

View file

@ -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,
)

View file

@ -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<CustomDerivationInputUM> = 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
}
}

View file

@ -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(

View file

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

View file

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

View file

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

View file

@ -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<ManageTokensUM> = MutableStateFlow(getInitialState(params.userWalletId))
val bottomSheetNavigation: SlotNavigation<BottomSheetConfig> = SlotNavigation()
val bottomSheetNavigation: SlotNavigation<ManageTokensBottomSheetConfig> = 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))
}
}