Updated on 2026-08-14
This commit is contained in:
parent
9bcf93922c
commit
4197574fca
10 changed files with 466 additions and 18 deletions
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.features.hotwallet
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
interface ForgetWalletComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, ForgetWalletComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.features.hotwallet.forgetwallet
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.hotwallet.ForgetWalletComponent
|
||||
import com.tangem.features.hotwallet.forgetwallet.ui.ForgetWalletContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
internal class DefaultForgetWalletComponent @AssistedInject constructor(
|
||||
@Assisted private val context: AppComponentContext,
|
||||
@Assisted private val params: ForgetWalletComponent.Params,
|
||||
) : ForgetWalletComponent, AppComponentContext by context {
|
||||
|
||||
private val model: ForgetWalletModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
ForgetWalletContent(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : ForgetWalletComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: ForgetWalletComponent.Params,
|
||||
): DefaultForgetWalletComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.tangem.features.hotwallet.forgetwallet
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
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.extensions.stringReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
|
||||
import com.tangem.features.hotwallet.ForgetWalletComponent
|
||||
import com.tangem.features.hotwallet.forgetwallet.entity.ForgetWalletUM
|
||||
import com.tangem.features.hotwallet.impl.R
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class ForgetWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val deleteWalletUseCase: DeleteWalletUseCase,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<ForgetWalletComponent.Params>()
|
||||
|
||||
internal val uiState: StateFlow<ForgetWalletUM>
|
||||
field = MutableStateFlow(
|
||||
ForgetWalletUM(
|
||||
onBackClick = { router.pop() },
|
||||
firstCheckboxChecked = false,
|
||||
secondCheckboxChecked = false,
|
||||
onFirstCheckboxClick = ::onFirstCheckboxClick,
|
||||
onSecondCheckboxClick = ::onSecondCheckboxClick,
|
||||
onForgetWalletClick = ::onForgetWalletClick,
|
||||
isForgetButtonEnabled = false,
|
||||
),
|
||||
)
|
||||
|
||||
private fun onFirstCheckboxClick() {
|
||||
uiState.update {
|
||||
val newValue = !it.firstCheckboxChecked
|
||||
it.copy(
|
||||
firstCheckboxChecked = newValue,
|
||||
isForgetButtonEnabled = newValue && it.secondCheckboxChecked,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSecondCheckboxClick() {
|
||||
uiState.update {
|
||||
val newValue = !it.secondCheckboxChecked
|
||||
it.copy(
|
||||
secondCheckboxChecked = newValue,
|
||||
isForgetButtonEnabled = it.firstCheckboxChecked && newValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onForgetWalletClick() {
|
||||
// TODO actualize strings [REDACTED_TASK_KEY]
|
||||
uiMessageSender.send(
|
||||
DialogMessage(
|
||||
title = stringReference("Attention"),
|
||||
message = stringReference("Are you sure you want to do this?"),
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = stringReference("Forget"),
|
||||
isWarning = true,
|
||||
onClick = ::forgetWallet,
|
||||
)
|
||||
},
|
||||
secondActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = stringReference("Cancel"),
|
||||
onClick = {},
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun forgetWallet() {
|
||||
modelScope.launch {
|
||||
val hasUserWallets = deleteWalletUseCase(params.userWalletId)
|
||||
.getOrElse {
|
||||
Timber.e("Unable to delete wallet: $it")
|
||||
|
||||
uiMessageSender.send(
|
||||
message = SnackbarMessage(resourceReference(R.string.common_unknown_error)),
|
||||
)
|
||||
|
||||
return@launch
|
||||
}
|
||||
|
||||
if (hasUserWallets) {
|
||||
router.pop()
|
||||
} else {
|
||||
router.replaceAll(AppRoute.Home())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.features.hotwallet.forgetwallet.di
|
||||
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.hotwallet.ForgetWalletComponent
|
||||
import com.tangem.features.hotwallet.forgetwallet.DefaultForgetWalletComponent
|
||||
import com.tangem.features.hotwallet.forgetwallet.ForgetWalletModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ForgetWalletModule {
|
||||
|
||||
@Binds
|
||||
fun bindForgetWalletComponentFactory(impl: DefaultForgetWalletComponent.Factory): ForgetWalletComponent.Factory
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(ForgetWalletModel::class)
|
||||
fun bindForgetWalletModel(model: ForgetWalletModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.features.hotwallet.forgetwallet.entity
|
||||
|
||||
internal data class ForgetWalletUM(
|
||||
val onBackClick: () -> Unit,
|
||||
val firstCheckboxChecked: Boolean,
|
||||
val secondCheckboxChecked: Boolean,
|
||||
val onFirstCheckboxClick: () -> Unit,
|
||||
val onSecondCheckboxClick: () -> Unit,
|
||||
val onForgetWalletClick: () -> Unit,
|
||||
val isForgetButtonEnabled: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package com.tangem.features.hotwallet.forgetwallet.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.material3.*
|
||||
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.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.hotwallet.forgetwallet.entity.ForgetWalletUM
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun ForgetWalletContent(state: ForgetWalletUM, modifier: Modifier = Modifier) {
|
||||
// TODO actualize strings [REDACTED_TASK_KEY]
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding(),
|
||||
) {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
startButton = TopAppBarButtonUM.Back(state.onBackClick),
|
||||
title = TextReference.EMPTY,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_attention_72),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.warning,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = "Attention",
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Text(
|
||||
text = "This wallet will be permanently removed from your device",
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
CheckboxItem(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
checked = state.firstCheckboxChecked,
|
||||
onCheckedChange = state.onFirstCheckboxClick,
|
||||
text = "I understand that removing my wallet does not delete it—but simply removes it from my device.",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
CheckboxItem(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
checked = state.secondCheckboxChecked,
|
||||
onCheckedChange = state.onSecondCheckboxClick,
|
||||
text = "I understand that if I haven't backed up my wallet before removing it, I may lose access to it.",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
text = "Forget wallet",
|
||||
onClick = state.onForgetWalletClick,
|
||||
enabled = state.isForgetButtonEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CheckboxItem(checked: Boolean, onCheckedChange: () -> Unit, text: String, modifier: Modifier = Modifier) {
|
||||
// TODO actualize strings [REDACTED_TASK_KEY]
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
IconToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = { onCheckedChange() },
|
||||
) {
|
||||
AnimatedContent(
|
||||
targetState = checked,
|
||||
label = "Update checked state",
|
||||
) { isChecked ->
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (isChecked) {
|
||||
R.drawable.ic_accepted_20
|
||||
} else {
|
||||
R.drawable.ic_unticked_20
|
||||
},
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = if (isChecked) {
|
||||
TangemTheme.colors.control.checked
|
||||
} else {
|
||||
TangemTheme.colors.icon.secondary
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(
|
||||
text = text,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewForgetWalletContent() {
|
||||
TangemThemePreview {
|
||||
ForgetWalletContent(
|
||||
state = ForgetWalletUM(
|
||||
onBackClick = {},
|
||||
firstCheckboxChecked = true,
|
||||
secondCheckboxChecked = false,
|
||||
onFirstCheckboxClick = {},
|
||||
onSecondCheckboxClick = {},
|
||||
onForgetWalletClick = {},
|
||||
isForgetButtonEnabled = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
|
|||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
|
|
@ -205,24 +206,7 @@ internal class WalletSettingsModel @Inject constructor(
|
|||
isNFTEnabled = isNFTEnabled,
|
||||
onCheckedNFTChange = ::onCheckedNFTChange,
|
||||
forgetWallet = {
|
||||
val message = DialogMessage(
|
||||
message = resourceReference(
|
||||
id = when (userWallet) {
|
||||
is UserWallet.Cold -> R.string.user_wallet_list_delete_prompt
|
||||
is UserWallet.Hot -> R.string.user_wallet_list_delete_hw_prompt
|
||||
},
|
||||
),
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = resourceReference(R.string.common_delete),
|
||||
isWarning = true,
|
||||
onClick = ::forgetWallet,
|
||||
)
|
||||
},
|
||||
secondActionBuilder = { cancelAction() },
|
||||
)
|
||||
|
||||
messageSender.send(message)
|
||||
onForgetWalletClick(userWallet)
|
||||
},
|
||||
onLinkMoreCardsClick = {
|
||||
when (userWallet) {
|
||||
|
|
@ -403,4 +387,80 @@ internal class WalletSettingsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO actualize strings [REDACTED_TASK_KEY] and remove MaximumLineLength
|
||||
@Suppress("LongMethod", "MaximumLineLength")
|
||||
private fun onForgetWalletClick(userWallet: UserWallet) {
|
||||
val message = when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
DialogMessage(
|
||||
message = resourceReference(
|
||||
id = R.string.user_wallet_list_delete_prompt,
|
||||
),
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = resourceReference(R.string.common_delete),
|
||||
isWarning = true,
|
||||
onClick = ::forgetWallet,
|
||||
)
|
||||
},
|
||||
secondActionBuilder = { cancelAction() },
|
||||
)
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.ic_alert_circle_24) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = stringReference("Forget this wallet?")
|
||||
body = if (userWallet.backedUp) {
|
||||
stringReference("A backup for this wallet exists. Review it before forgetting to make sure you can recover later.")
|
||||
} else {
|
||||
stringReference("If you forget this wallet without a backup, you’ll permanently lose access to your funds.")
|
||||
}
|
||||
}
|
||||
if (userWallet.backedUp) {
|
||||
secondaryButton {
|
||||
text = stringReference("Forget wallet")
|
||||
onClick {
|
||||
router.push(AppRoute.ForgetWallet(userWallet.walletId))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
secondaryButton {
|
||||
text = stringReference("Forget Anyway")
|
||||
onClick {
|
||||
router.push(AppRoute.ForgetWallet(userWallet.walletId))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (userWallet.backedUp) {
|
||||
primaryButton {
|
||||
text = stringReference("View backup")
|
||||
onClick {
|
||||
unlockWalletIfNeedAndProceed {
|
||||
router.push(AppRoute.ViewPhrase(userWallet.walletId))
|
||||
}
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
primaryButton {
|
||||
text = stringReference("Go to backup")
|
||||
onClick {
|
||||
router.push(AppRoute.CreateWalletBackup(userWallet.walletId))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messageSender.send(message)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue