Updated on 2026-08-14
This commit is contained in:
parent
36a3e2f7d6
commit
0cc270adc0
14 changed files with 464 additions and 1 deletions
|
|
@ -22,6 +22,7 @@ import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
|
|||
import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment
|
||||
import com.tangem.tap.features.onboarding.products.twins.ui.TwinsCardsFragment
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment
|
||||
import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.shop.ui.ShopFragment
|
||||
import com.tangem.tap.features.tokens.addCustomToken.AddCustomTokenFragment
|
||||
|
|
@ -112,5 +113,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.QrScan -> QrScanFragment()
|
||||
AppScreen.ReferralProgram -> ReferralFragment()
|
||||
AppScreen.Welcome -> WelcomeFragment()
|
||||
AppScreen.SaveWallet -> SaveWalletBottomSheetFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.BackupMiddleware
|
|||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayMiddleware
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletState
|
||||
import com.tangem.tap.features.send.redux.middlewares.SendMiddleware
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.shop.redux.ShopMiddleware
|
||||
|
|
@ -53,6 +54,7 @@ data class AppState(
|
|||
val walletConnectState: WalletConnectState = WalletConnectState(),
|
||||
val shopState: ShopState = ShopState(),
|
||||
val welcomeState: WelcomeState = WelcomeState(),
|
||||
val saveWalletState: SaveWalletState = SaveWalletState(),
|
||||
) : StateType {
|
||||
|
||||
val domainState: DomainState
|
||||
|
|
|
|||
|
|
@ -23,5 +23,6 @@ enum class AppScreen(
|
|||
WalletConnectSessions,
|
||||
QrScan,
|
||||
ReferralProgram,
|
||||
Welcome
|
||||
Welcome,
|
||||
SaveWallet(isDialogFragment = true),
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface SaveWalletAction : Action {
|
||||
object Save : SaveWalletAction {
|
||||
object Success : SaveWalletAction
|
||||
data class Error(val error: TangemError) : SaveWalletAction
|
||||
}
|
||||
|
||||
object Dismiss : SaveWalletAction
|
||||
|
||||
object CloseError : SaveWalletAction
|
||||
|
||||
object SaveWalletWasShown : SaveWalletAction
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import org.rekotlin.StateType
|
||||
|
||||
class SaveWalletState : StateType
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package com.tangem.tap.features.saveWallet.ui
|
||||
|
||||
import android.content.DialogInterface
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.SnackbarHost
|
||||
import androidx.compose.material.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.core.ui.components.dialogs.EnrollBiometricsDialogContent
|
||||
import com.tangem.core.ui.fragments.ComposeBottomSheetFragment
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.saveWallet.ui.components.SaveWalletScreenContent
|
||||
|
||||
internal class SaveWalletBottomSheetFragment : ComposeBottomSheetFragment<SaveWalletScreenState>() {
|
||||
override val expandedHeightFraction: Float = .95f
|
||||
|
||||
private val viewModel by viewModels<SaveWalletViewModel>()
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
viewModel.dismiss()
|
||||
super.onDismiss(dialog)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun provideState(): State<SaveWalletScreenState> {
|
||||
return viewModel.state.collectAsState()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(
|
||||
modifier: Modifier,
|
||||
state: SaveWalletScreenState,
|
||||
) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val errorMessage by rememberUpdatedState(newValue = state.error?.customMessage)
|
||||
val enrollBiometricsDialog by rememberUpdatedState(newValue = state.enrollBiometricsDialog)
|
||||
|
||||
Box(modifier = modifier) {
|
||||
SaveWalletScreenContent(
|
||||
showProgress = state.showProgress,
|
||||
onSaveWalletClick = viewModel::saveWallet,
|
||||
onCloseClick = this@SaveWalletBottomSheetFragment::dismissAllowingStateLoss,
|
||||
)
|
||||
|
||||
SnackbarHost(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(vertical = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
|
||||
EnrollBiometricsDialog(dialog = enrollBiometricsDialog)
|
||||
|
||||
LaunchedEffect(key1 = errorMessage) {
|
||||
errorMessage?.let {
|
||||
snackbarHostState.showSnackbar(it)
|
||||
viewModel.closeError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EnrollBiometricsDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
dialog: EnrollBiometricsDialog?,
|
||||
) {
|
||||
if (dialog == null) return
|
||||
EnrollBiometricsDialogContent(modifier, dialog)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.tap.features.saveWallet.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
|
||||
@Immutable
|
||||
internal data class SaveWalletScreenState(
|
||||
val showProgress: Boolean = false,
|
||||
val enrollBiometricsDialog: EnrollBiometricsDialog? = null,
|
||||
val error: TangemError? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.tap.features.saveWallet.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletState
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
internal class SaveWalletViewModel : ViewModel(), StoreSubscriber<SaveWalletState> {
|
||||
private val stateInternal = MutableStateFlow(SaveWalletScreenState())
|
||||
val state: StateFlow<SaveWalletScreenState> = stateInternal
|
||||
|
||||
init {
|
||||
subscribeToStoreChanges()
|
||||
store.dispatchOnMain(SaveWalletAction.SaveWalletWasShown)
|
||||
}
|
||||
|
||||
fun saveWallet() {
|
||||
store.dispatch(SaveWalletAction.Save)
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
store.dispatch(SaveWalletAction.Dismiss)
|
||||
}
|
||||
|
||||
fun closeError() {
|
||||
store.dispatch(SaveWalletAction.CloseError)
|
||||
}
|
||||
|
||||
override fun newState(state: SaveWalletState) {
|
||||
stateInternal.update { prevState ->
|
||||
// TODO: Update screen state
|
||||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
private fun subscribeToStoreChanges() {
|
||||
store.subscribe(this) { appState ->
|
||||
appState.skip { old, new -> old.saveWalletState == new.saveWalletState }
|
||||
.select { it.saveWalletState }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
package com.tangem.tap.features.saveWallet.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerH24
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun SaveWalletScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
showProgress: Boolean,
|
||||
onSaveWalletClick: () -> Unit,
|
||||
onCloseClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Hand()
|
||||
IconButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(all = TangemTheme.dimens.spacing8)
|
||||
.size(TangemTheme.dimens.size32),
|
||||
onClick = onCloseClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_close),
|
||||
tint = TangemTheme.colors.icon.secondary,
|
||||
contentDescription = stringResource(id = R.string.common_cancel),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(.7f))
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size56),
|
||||
painter = painterResource(id = R.drawable.ic_fingerprint_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerH24()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.text.accent.copy(alpha = .12f),
|
||||
shape = TangemTheme.shapes.roundedCornersMedium,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
text = stringResource(R.string.save_user_wallet_agreement_new_feature),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
SpacerH24()
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.save_user_wallet_agreement_header),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH12()
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(fraction = .87f),
|
||||
text = stringResource(R.string.save_user_wallet_agreement_description),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = showProgress,
|
||||
text = stringResource(R.string.save_user_wallet_agreement_allow),
|
||||
onClick = onSaveWalletClick,
|
||||
)
|
||||
SpacerH16()
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(fraction = .7f),
|
||||
text = stringResource(R.string.save_user_wallet_agreement_notice),
|
||||
style = TangemTheme.typography.caption,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH16()
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun SaveWalletScreenContentSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
SaveWalletScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing32)
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
color = TangemTheme.colors.background.plain,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
),
|
||||
showProgress = false,
|
||||
onSaveWalletClick = { /* no-op */ },
|
||||
onCloseClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SaveWalletScreenContentPreview_Light() {
|
||||
TangemTheme {
|
||||
SaveWalletScreenContentSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun SaveWalletScreenContentPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
SaveWalletScreenContentSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
Loading…
Add table
Add a link
Reference in a new issue