Updated on 2026-08-14
This commit is contained in:
parent
888d26029b
commit
e1ef1c4b8a
10 changed files with 352 additions and 8 deletions
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.feature.walletsettings.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 com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.wallets.models.UpdateWalletError
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.RenameWalletUseCase
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.feature.walletsettings.entity.RenameWalletUM
|
||||
import com.tangem.feature.walletsettings.impl.R
|
||||
import com.tangem.feature.walletsettings.ui.RenameWalletDialog
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class DefaultRenameWalletComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted params: RenameWalletComponent.Params,
|
||||
private val renameWalletUseCase: RenameWalletUseCase,
|
||||
) : RenameWalletComponent, AppComponentContext by context {
|
||||
|
||||
private val userWalletId = params.userWalletId
|
||||
private val currentWalletName = params.currentName
|
||||
|
||||
override val doOnDismiss: () -> Unit = params.onDismiss
|
||||
|
||||
private val stateFlow: MutableStateFlow<RenameWalletUM> = MutableStateFlow(
|
||||
value = RenameWalletUM(
|
||||
walletNameValue = TextFieldValue(text = params.currentName),
|
||||
isNameCorrect = false,
|
||||
updateValue = ::updateValue,
|
||||
onConfirm = { renameWallet(params.userWalletId) },
|
||||
),
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Dialog() {
|
||||
val model by stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
RenameWalletDialog(
|
||||
model = model,
|
||||
onDismiss = doOnDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateValue(value: TextFieldValue) {
|
||||
stateFlow.update {
|
||||
it.copy(
|
||||
walletNameValue = value,
|
||||
isNameCorrect = value.text.isNotBlank() && value.text != currentWalletName,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renameWallet(userWalletId: UserWalletId) = componentScope.launch {
|
||||
val newName = stateFlow.value.walletNameValue
|
||||
val maybeError = renameWalletUseCase(userWalletId, newName.text).leftOrNull()
|
||||
|
||||
if (maybeError != null) {
|
||||
val message = when (maybeError) {
|
||||
UpdateWalletError.DataError -> resourceReference(
|
||||
id = R.string.common_unknown_error,
|
||||
)
|
||||
UpdateWalletError.NameAlreadyExists -> resourceReference(
|
||||
id = R.string.user_wallet_list_rename_popup_error_already_exists,
|
||||
formatArgs = wrappedList(newName),
|
||||
)
|
||||
}
|
||||
|
||||
messageSender.send(message = SnackbarMessage(message))
|
||||
}
|
||||
|
||||
doOnDismiss()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : RenameWalletComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: RenameWalletComponent.Params,
|
||||
): DefaultRenameWalletComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package com.tangem.feature.walletsettings.component.impl
|
||||
|
||||
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.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
|
||||
import com.tangem.feature.walletsettings.entity.DialogConfig
|
||||
import com.tangem.feature.walletsettings.model.WalletSettingsModel
|
||||
import com.tangem.feature.walletsettings.ui.WalletSettingsScreen
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultWalletSettingsComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted params: WalletSettingsComponent.Params,
|
||||
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
|
||||
) : WalletSettingsComponent, AppComponentContext by context {
|
||||
|
||||
private val model: WalletSettingsModel = getOrCreateModel(params)
|
||||
|
||||
private val dialog = childSlot(
|
||||
source = model.dialogNavigation,
|
||||
serializer = DialogConfig.serializer(),
|
||||
handleBackButton = true,
|
||||
childFactory = ::dialogChild,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
val dialog by dialog.subscribeAsState()
|
||||
|
||||
WalletSettingsScreen(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
dialog = { dialog.child?.instance?.Dialog() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun dialogChild(
|
||||
dialogConfig: DialogConfig,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableDialogComponent = when (dialogConfig) {
|
||||
is DialogConfig.RenameWallet -> {
|
||||
renameWalletComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = RenameWalletComponent.Params(
|
||||
userWalletId = dialogConfig.userWalletId,
|
||||
currentName = dialogConfig.currentName,
|
||||
onDismiss = model.dialogNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : WalletSettingsComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: WalletSettingsComponent.Params,
|
||||
): DefaultWalletSettingsComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.feature.walletsettings.di
|
||||
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
|
||||
import com.tangem.feature.walletsettings.component.impl.DefaultRenameWalletComponent
|
||||
import com.tangem.feature.walletsettings.component.impl.DefaultWalletSettingsComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindWalletSettingsComponentFactory(
|
||||
factory: DefaultWalletSettingsComponent.Factory,
|
||||
): WalletSettingsComponent.Factory
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindRenameWalletComponentFactory(factory: DefaultRenameWalletComponent.Factory): RenameWalletComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.feature.walletsettings.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.feature.walletsettings.model.WalletSettingsModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(WalletSettingsModel::class)
|
||||
fun provideWalletSettingsModel(model: WalletSettingsModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package com.tangem.feature.walletsettings.model
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.tangem.common.routing.AppRoute
|
||||
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.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
|
||||
import com.tangem.feature.walletsettings.entity.DialogConfig
|
||||
import com.tangem.feature.walletsettings.entity.WalletSettingsItemUM
|
||||
import com.tangem.feature.walletsettings.entity.WalletSettingsUM
|
||||
import com.tangem.feature.walletsettings.impl.R
|
||||
import com.tangem.feature.walletsettings.utils.ItemsBuilder
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ComponentScoped
|
||||
internal class WalletSettingsModel @Inject constructor(
|
||||
private val router: Router,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val getWalletUseCase: GetUserWalletUseCase,
|
||||
private val deleteWalletUseCase: DeleteWalletUseCase,
|
||||
private val itemsBuilder: ItemsBuilder,
|
||||
private val paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
val params: WalletSettingsComponent.Params = paramsContainer.require()
|
||||
val dialogNavigation = SlotNavigation<DialogConfig>()
|
||||
|
||||
val state: MutableStateFlow<WalletSettingsUM> = MutableStateFlow(
|
||||
value = WalletSettingsUM(
|
||||
popBack = router::pop,
|
||||
items = persistentListOf(),
|
||||
),
|
||||
)
|
||||
|
||||
init {
|
||||
getWalletUseCase.invokeFlow(params.userWalletId)
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeWallet ->
|
||||
val wallet = maybeWallet.getOrElse { error ->
|
||||
error(
|
||||
"""
|
||||
Failed to get user wallet
|
||||
|- User wallet ID: $params
|
||||
|- Cause: $error
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
state.update { value ->
|
||||
value.copy(items = buildItems(wallet, dialogNavigation))
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun buildItems(
|
||||
userWallet: UserWallet,
|
||||
dialogNavigation: SlotNavigation<DialogConfig>,
|
||||
): PersistentList<WalletSettingsItemUM> = itemsBuilder.buildItems(
|
||||
walletName = userWallet.name,
|
||||
renameWallet = { openRenameWalletDialog(userWallet, dialogNavigation) },
|
||||
forgetWallet = { forgetWallet(userWallet.walletId) },
|
||||
)
|
||||
|
||||
private fun openRenameWalletDialog(userWallet: UserWallet, dialogNavigation: SlotNavigation<DialogConfig>) {
|
||||
val config = DialogConfig.RenameWallet(
|
||||
userWalletId = userWallet.walletId,
|
||||
currentName = userWallet.name,
|
||||
)
|
||||
|
||||
dialogNavigation.activate(config)
|
||||
}
|
||||
|
||||
private fun forgetWallet(userWalletId: UserWalletId) = modelScope.launch {
|
||||
val hasUserWallets = deleteWalletUseCase(userWalletId).getOrElse {
|
||||
messageSender.send(
|
||||
message = SnackbarMessage(resourceReference(R.string.common_unknown_error)),
|
||||
)
|
||||
|
||||
return@launch
|
||||
}
|
||||
|
||||
if (hasUserWallets) {
|
||||
router.pop()
|
||||
} else {
|
||||
router.replaceAll(AppRoute.Home)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue