Updated on 2026-08-14
This commit is contained in:
parent
a0f1a6591c
commit
e993dc0647
119 changed files with 4449 additions and 720 deletions
|
|
@ -45,8 +45,20 @@ sealed class DetailsAction : Action {
|
|||
}
|
||||
|
||||
sealed class AppSettings : DetailsAction() {
|
||||
data class SwitchPrivacySetting(val enable: Boolean, val setting: PrivacySetting) :
|
||||
AppSettings()
|
||||
data class SwitchPrivacySetting(
|
||||
val enable: Boolean,
|
||||
val setting: PrivacySetting,
|
||||
) : AppSettings() {
|
||||
data class Success(
|
||||
val enable: Boolean,
|
||||
val setting: PrivacySetting,
|
||||
) : AppSettings()
|
||||
}
|
||||
|
||||
object EnrollBiometrics : AppSettings() {
|
||||
object Enroll : AppSettings()
|
||||
object Cancel : AppSettings()
|
||||
}
|
||||
}
|
||||
|
||||
data class ChangeAppCurrency(val fiatCurrency: FiatCurrency) : DetailsAction()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package com.tangem.tap.features.details.redux
|
|||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.flatMap
|
||||
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
|
||||
import com.tangem.domain.common.util.userWalletId
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
|
|
@ -9,44 +12,53 @@ import com.tangem.tap.common.analytics.events.AnalyticsParam
|
|||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
class DetailsMiddleware {
|
||||
private val eraseWalletMiddleware = EraseWalletMiddleware()
|
||||
private val manageSecurityMiddleware = ManageSecurityMiddleware()
|
||||
private val managePrivacyMiddleware = ManagePrivacyMiddleware()
|
||||
val detailsMiddleware: Middleware<AppState> = { _, state ->
|
||||
val detailsMiddleware: Middleware<AppState> = { _, stateProvider ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handleAction(state, action)
|
||||
if (!DemoHelper.tryHandle(stateProvider, action)) {
|
||||
val detailsState = stateProvider()?.detailsState
|
||||
if (detailsState != null) {
|
||||
handleAction(detailsState, action)
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAction(state: () -> AppState?, action: Action) {
|
||||
if (DemoHelper.tryHandle(state, action)) return
|
||||
|
||||
private fun handleAction(state: DetailsState, action: Action) {
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
is DetailsAction.AppSettings -> managePrivacyMiddleware.handle(action)
|
||||
is DetailsAction.AppSettings -> managePrivacyMiddleware.handle(state, action)
|
||||
is DetailsAction.ShowDisclaimer -> {
|
||||
val uri = store.state.detailsState.cardTermsOfUseUrl
|
||||
if (uri != null) {
|
||||
|
|
@ -65,14 +77,17 @@ class DetailsMiddleware {
|
|||
}
|
||||
DetailsAction.ScanCard -> {
|
||||
scope.launch {
|
||||
when (val result = tangemSdkManager.scanCard()) {
|
||||
is CompletionResult.Success -> {
|
||||
val scannedCard = result.data
|
||||
tangemSdkManager.scanCard(
|
||||
cardId = state.scanResponse?.card?.cardId,
|
||||
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes &&
|
||||
state.scanResponse?.card?.isAccessCodeSet == true,
|
||||
)
|
||||
.doOnSuccess { card ->
|
||||
val currentCardId = store.state.globalState.scanResponse?.card
|
||||
?.userWalletId
|
||||
?.stringValue
|
||||
if (scannedCard.userWalletId.stringValue == currentCardId) {
|
||||
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(scannedCard))
|
||||
if (card.userWalletId.stringValue == currentCardId) {
|
||||
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(card))
|
||||
} else {
|
||||
store.dispatchDialogShow(
|
||||
AppDialog.SimpleOkDialogRes(
|
||||
|
|
@ -82,8 +97,6 @@ class DetailsMiddleware {
|
|||
)
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,17 +117,22 @@ class DetailsMiddleware {
|
|||
is DetailsAction.ResetToFactory.Proceed -> {
|
||||
val card = store.state.detailsState.cardSettingsState?.card ?: return
|
||||
scope.launch {
|
||||
when (val result = tangemSdkManager.resetToFactorySettings(card.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
tangemSdkManager.resetToFactorySettings(card.cardId)
|
||||
.flatMap { userWalletsListManager.delete(listOf(card.userWalletId)) }
|
||||
.doOnSuccess {
|
||||
Analytics.send(Settings.CardSettings.FactoryResetFinished())
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
val screen = if (userWalletsListManager.hasSavedUserWallets) {
|
||||
AppScreen.Welcome
|
||||
} else {
|
||||
AppScreen.Home
|
||||
}
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(screen))
|
||||
}
|
||||
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
Analytics.send(Settings.CardSettings.FactoryResetFinished(error)) }
|
||||
.doOnFailure { error ->
|
||||
(error as? TangemSdkError)?.let { sdkError ->
|
||||
Analytics.send(Settings.CardSettings.FactoryResetFinished(sdkError))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
|
|
@ -174,12 +192,109 @@ class DetailsMiddleware {
|
|||
}
|
||||
|
||||
class ManagePrivacyMiddleware {
|
||||
fun handle(action: DetailsAction.AppSettings) {
|
||||
fun handle(state: DetailsState, action: DetailsAction.AppSettings) {
|
||||
when (action) {
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting -> {
|
||||
// TODO()
|
||||
if (tangemSdkManager.canEnrollBiometrics) {
|
||||
store.dispatch(DetailsAction.AppSettings.EnrollBiometrics)
|
||||
}
|
||||
when (action.setting) {
|
||||
PrivacySetting.SaveWallets -> toggleSaveWallets(state, enable = action.enable)
|
||||
PrivacySetting.SaveAccessCode -> toggleSaveAccessCodes(state, enable = action.enable)
|
||||
}
|
||||
}
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting.Success -> Unit
|
||||
is DetailsAction.AppSettings.EnrollBiometrics -> Unit
|
||||
is DetailsAction.AppSettings.EnrollBiometrics.Enroll -> enrollBiometrics()
|
||||
is DetailsAction.AppSettings.EnrollBiometrics.Cancel -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun enrollBiometrics() {
|
||||
store.dispatchOnMain(NavigationAction.OpenBiometricsSettings)
|
||||
}
|
||||
|
||||
private fun toggleSaveWallets(state: DetailsState, enable: Boolean) = scope.launch {
|
||||
if (state.saveWallets == enable) return@launch
|
||||
if (enable) {
|
||||
saveCurrentWallet()
|
||||
} else {
|
||||
deleteSavedWallets()
|
||||
if (state.saveAccessCodes) {
|
||||
deleteSavedAccessCodes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleSaveAccessCodes(state: DetailsState, enable: Boolean) = scope.launch {
|
||||
if (state.saveAccessCodes == enable) return@launch
|
||||
if (enable) {
|
||||
if (!state.saveWallets) {
|
||||
saveCurrentWallet()
|
||||
}
|
||||
saveAccessCodes()
|
||||
} else {
|
||||
deleteSavedAccessCodes()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun saveCurrentWallet() {
|
||||
val scanResponse = store.state.detailsState.scanResponse ?: return
|
||||
val userWallet = UserWalletBuilder(scanResponse).build()
|
||||
|
||||
userWalletsListManager.save(userWallet)
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Wallet saving failed")
|
||||
}
|
||||
.doOnSuccess {
|
||||
preferencesStorage.shouldShowSaveWallet = false
|
||||
preferencesStorage.shouldSaveUserWallets = true
|
||||
store.dispatchOnMain(
|
||||
DetailsAction.AppSettings.SwitchPrivacySetting.Success(
|
||||
setting = PrivacySetting.SaveWallets,
|
||||
enable = true,
|
||||
),
|
||||
)
|
||||
store.onUserWalletSelected(userWallet)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun deleteSavedWallets() {
|
||||
userWalletsListManager.clear()
|
||||
.flatMap { walletStoresManager.clear() }
|
||||
.doOnSuccess {
|
||||
preferencesStorage.shouldSaveUserWallets = false
|
||||
store.dispatchOnMain(
|
||||
DetailsAction.AppSettings.SwitchPrivacySetting.Success(
|
||||
setting = PrivacySetting.SaveWallets,
|
||||
enable = false,
|
||||
),
|
||||
)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveAccessCodes() {
|
||||
preferencesStorage.shouldSaveAccessCodes = true
|
||||
store.dispatchOnMain(
|
||||
DetailsAction.AppSettings.SwitchPrivacySetting.Success(
|
||||
setting = PrivacySetting.SaveAccessCode,
|
||||
enable = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun deleteSavedAccessCodes() {
|
||||
tangemSdkManager.clearSavedUserCodes()
|
||||
.doOnSuccess {
|
||||
preferencesStorage.shouldSaveAccessCodes = false
|
||||
store.dispatchOnMain(
|
||||
DetailsAction.AppSettings.SwitchPrivacySetting.Success(
|
||||
setting = PrivacySetting.SaveAccessCode,
|
||||
enable = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,10 @@ import com.tangem.domain.common.isTangemTwin
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.extensions.isWalletDataSupported
|
||||
import com.tangem.tap.domain.extensions.signedHashesCount
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import org.rekotlin.Action
|
||||
import java.util.*
|
||||
|
||||
|
|
@ -53,6 +56,9 @@ private fun handlePrepareScreen(
|
|||
cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card),
|
||||
createBackupAllowed = action.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup,
|
||||
appCurrency = store.state.globalState.appCurrency,
|
||||
isBiometricsAvailable = tangemSdkManager.canUseBiometry,
|
||||
saveWallets = userWalletsListManager.hasSavedUserWallets,
|
||||
saveAccessCodes = preferencesStorage.shouldSaveAccessCodes,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -151,12 +157,15 @@ private fun handlePrivacyAction(
|
|||
state: DetailsState,
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting -> {
|
||||
when (action.setting) {
|
||||
PrivacySetting.SaveWallets -> state.copy(saveWallets = action.enable)
|
||||
PrivacySetting.SaveAccessCode -> state.copy(saveAccessCodes = action.enable)
|
||||
}
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting -> state
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting.Success -> when (action.setting) {
|
||||
PrivacySetting.SaveWallets -> state.copy(saveWallets = action.enable)
|
||||
PrivacySetting.SaveAccessCode -> state.copy(saveAccessCodes = action.enable)
|
||||
}
|
||||
is DetailsAction.AppSettings.EnrollBiometrics -> state.copy(needEnrollBiometrics = true)
|
||||
is DetailsAction.AppSettings.EnrollBiometrics.Enroll,
|
||||
is DetailsAction.AppSettings.EnrollBiometrics.Cancel,
|
||||
-> state.copy(needEnrollBiometrics = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ data class DetailsState(
|
|||
val appCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val saveWallets: Boolean = false,
|
||||
val saveAccessCodes: Boolean = false,
|
||||
val isBiometricsAvailable: Boolean = false,
|
||||
val needEnrollBiometrics: Boolean = false,
|
||||
) : StateType {
|
||||
|
||||
// if you do not delegate - the application crashes on startup,
|
||||
|
|
|
|||
|
|
@ -1,23 +1,12 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
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.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.material.AlertDialog
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
|
@ -26,6 +15,8 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.dialogs.EnrollBiometricsDialogContent
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.redux.PrivacySetting
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
|
|
@ -64,6 +55,8 @@ private fun AppSettings(
|
|||
)
|
||||
}
|
||||
|
||||
EnrollBiometricsDialog(dialog = state.enrollBiometricsDialog)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize(),
|
||||
|
|
@ -84,6 +77,15 @@ private fun AppSettings(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EnrollBiometricsDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
dialog: EnrollBiometricsDialog?,
|
||||
) {
|
||||
if (dialog == null) return
|
||||
EnrollBiometricsDialogContent(modifier, dialog)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppSettingsElement(
|
||||
state: AppSettingsScreenState,
|
||||
|
|
@ -224,6 +226,7 @@ fun AppSettingsScreenPreview() {
|
|||
PrivacySetting.SaveAccessCode to false,
|
||||
),
|
||||
onSettingToggled = { _, _ -> },
|
||||
enrollBiometricsDialog = null,
|
||||
),
|
||||
onBackPressed = { },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
import com.tangem.tap.features.details.redux.PrivacySetting
|
||||
|
||||
data class AppSettingsScreenState(
|
||||
val settings: Map<PrivacySetting, Boolean>,
|
||||
val enrollBiometricsDialog: EnrollBiometricsDialog?,
|
||||
val onSettingToggled: (PrivacySetting, Boolean) -> Unit,
|
||||
)
|
||||
|
||||
|
||||
)
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
|
|
@ -11,12 +13,14 @@ import org.rekotlin.Store
|
|||
class AppSettingsViewModel(private val store: Store<AppState>) {
|
||||
private val _uiState = MutableStateFlow(updateState(store.state.detailsState))
|
||||
val uiState: StateFlow<AppSettingsScreenState> = _uiState
|
||||
|
||||
fun updateState(state: DetailsState): AppSettingsScreenState {
|
||||
return AppSettingsScreenState(
|
||||
settings = mapOf(
|
||||
PrivacySetting.SaveWallets to state.saveWallets,
|
||||
PrivacySetting.SaveAccessCode to state.saveAccessCodes,
|
||||
),
|
||||
enrollBiometricsDialog = if (state.needEnrollBiometrics) createEnrollBiometricsDialog() else null,
|
||||
onSettingToggled = { privacySetting, enabled -> onSettingsToggled(privacySetting, enabled) },
|
||||
)
|
||||
}
|
||||
|
|
@ -24,4 +28,13 @@ class AppSettingsViewModel(private val store: Store<AppState>) {
|
|||
private fun onSettingsToggled(setting: PrivacySetting, enable: Boolean) {
|
||||
store.dispatch(DetailsAction.AppSettings.SwitchPrivacySetting(enable = enable, setting = setting))
|
||||
}
|
||||
|
||||
private fun createEnrollBiometricsDialog() = EnrollBiometricsDialog(
|
||||
onCancel = {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics.Cancel)
|
||||
},
|
||||
onEnroll = {
|
||||
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics.Enroll)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.tap.features.details.ui.details
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Immutable
|
||||
data class DetailsScreenState(
|
||||
val elements: List<SettingsElement>,
|
||||
val tangemLinks: List<SocialNetworkLink>,
|
||||
|
|
@ -13,6 +15,7 @@ data class DetailsScreenState(
|
|||
val appNameRes: Int = R.string.app_name
|
||||
}
|
||||
|
||||
@Immutable
|
||||
enum class SettingsElement(
|
||||
val iconRes: Int,
|
||||
val titleRes: Int,
|
||||
|
|
@ -30,6 +33,7 @@ enum class SettingsElement(
|
|||
PrivacyPolicy(R.drawable.ic_lock, R.string.details_row_privacy_policy);
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class SocialNetworkLink(
|
||||
val network: SocialNetwork,
|
||||
val url: String,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class DetailsViewModel(private val store: Store<AppState>) {
|
|||
SettingsElement.PrivacyPolicy -> {
|
||||
if (state.privacyPolicyUrl != null) it else null
|
||||
}
|
||||
SettingsElement.AppSettings -> null // TODO: until we implement settings from this screen
|
||||
SettingsElement.AppSettings -> if (state.isBiometricsAvailable) it else null
|
||||
SettingsElement.AppCurrency -> if (state.scanResponse?.card?.isMultiwalletAllowed != true) it else null
|
||||
SettingsElement.TermsOfUse -> if (state.scanResponse?.card?.isStart2Coin == true) it else null
|
||||
else -> it
|
||||
|
|
@ -44,7 +44,7 @@ class DetailsViewModel(private val store: Store<AppState>) {
|
|||
}
|
||||
|
||||
return DetailsScreenState(
|
||||
settings,
|
||||
elements = settings,
|
||||
tangemLinks = getSocialLinks(),
|
||||
tangemVersion = getTangemAppVersion(),
|
||||
appCurrency = state.appCurrency.name,
|
||||
|
|
@ -80,7 +80,7 @@ class DetailsViewModel(private val store: Store<AppState>) {
|
|||
}
|
||||
SettingsElement.AppSettings -> {
|
||||
Analytics.send(Settings.ButtonAppSettings())
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.AppSettings)) //TODO: To be available later
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.AppSettings))
|
||||
}
|
||||
SettingsElement.LinkMoreCards -> {
|
||||
Analytics.send(Settings.ButtonCreateBackup())
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import org.rekotlin.StoreSubscriber
|
|||
class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
||||
|
||||
private var homeState: MutableState<HomeState> = mutableStateOf(store.state.homeState)
|
||||
private var composeView: ComposeView? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -38,20 +37,16 @@ class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
|||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
val context = container?.context ?: return null
|
||||
|
||||
): View {
|
||||
store.dispatch(BackupAction.CheckForUnfinishedBackup)
|
||||
|
||||
composeView = ComposeView(context).apply {
|
||||
return ComposeView(inflater.context).apply {
|
||||
setContent {
|
||||
AppCompatTheme {
|
||||
ScreenContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return composeView
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
|
@ -71,7 +66,6 @@ class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
|||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
rollbackStatusBarIconsColor()
|
||||
composeView = null
|
||||
}
|
||||
|
||||
override fun newState(state: HomeState) {
|
||||
|
|
|
|||
|
|
@ -1,51 +1,33 @@
|
|||
package com.tangem.tap.features.home.redux
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.CardDTO
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.extensions.withIOContext
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.operations.backup.BackupService
|
||||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.backupService
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnResult
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.primaryCardIsSaltPayVisa
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.postUiDelayBg
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.FragmentShareTransition
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerType
|
||||
import com.tangem.tap.features.disclaimer.redux.isAccepted
|
||||
import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
||||
import com.tangem.tap.domain.scanCard.ScanCardProcessor
|
||||
import com.tangem.tap.features.home.BELARUS_COUNTRY_CODE
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.home.redux.HomeMiddleware.Companion.BUY_WALLET_URL
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.onboarding.OnboardingSaltPayHelper
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayExceptionHandler
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
class HomeMiddleware {
|
||||
companion object {
|
||||
|
|
@ -71,34 +53,13 @@ private fun handleHomeAction(action: Action) {
|
|||
store.dispatch(GlobalAction.ExchangeManager.Init)
|
||||
store.dispatch(GlobalAction.FetchUserCountry)
|
||||
}
|
||||
|
||||
is HomeAction.ShouldScanCardOnResume -> {
|
||||
if (action.shouldScanCard) {
|
||||
store.dispatch(HomeAction.ShouldScanCardOnResume(false))
|
||||
postUiDelayBg(700) { store.dispatch(HomeAction.ReadCard) }
|
||||
}
|
||||
}
|
||||
is HomeAction.ReadCard -> {
|
||||
changeButtonState(ButtonState.PROGRESS)
|
||||
val scanCardAction = GlobalAction.ScanCard(
|
||||
onSuccess = { scanResponse ->
|
||||
store.dispatch(HomeAction.ScanInProgress(false))
|
||||
checkForUnfinishedBackupForSaltPay(
|
||||
backupService = backupService,
|
||||
scanResponse = scanResponse,
|
||||
nextHandler = {
|
||||
showDisclaimerIfNeed(
|
||||
scanResponse = scanResponse,
|
||||
nextHandler = ::onScanSuccess,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
onFailure = ::onScanFailure,
|
||||
)
|
||||
store.dispatch(HomeAction.ScanInProgress(true))
|
||||
postUiDelayBg(300) { store.dispatch(scanCardAction) }
|
||||
}
|
||||
is HomeAction.ReadCard -> readCard()
|
||||
is HomeAction.GoToShop -> {
|
||||
when (action.userCountryCode) {
|
||||
RUSSIA_COUNTRY_CODE, BELARUS_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL)
|
||||
|
|
@ -108,134 +69,44 @@ private fun handleHomeAction(action: Action) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It checks only the SaltPay cards. To check for unfinished backups for the standard Wallet cards
|
||||
* see BackupAction.CheckForUnfinishedBackup
|
||||
* If user touches card other than Visa SaltPay - show dialog and block next processing
|
||||
*/
|
||||
private fun checkForUnfinishedBackupForSaltPay(
|
||||
backupService: BackupService,
|
||||
scanResponse: ScanResponse,
|
||||
nextHandler: (ScanResponse) -> Unit,
|
||||
) {
|
||||
if (!backupService.hasIncompletedBackup || !backupService.primaryCardIsSaltPayVisa()) {
|
||||
nextHandler(scanResponse)
|
||||
return
|
||||
}
|
||||
|
||||
fun isTheSamePrimaryCard(card: CardDTO): Boolean {
|
||||
return backupService.primaryCardId?.let { it == card.cardId } ?: false
|
||||
}
|
||||
|
||||
if (scanResponse.isSaltPayWallet() || !isTheSamePrimaryCard(scanResponse.card)) {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
showSaltPayTapVisaLogoCardDialog()
|
||||
} else {
|
||||
nextHandler(scanResponse)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDisclaimerIfNeed(scanResponse: ScanResponse, nextHandler: (ScanResponse) -> Unit) {
|
||||
val disclaimerType = DisclaimerType.get(scanResponse)
|
||||
store.dispatch(DisclaimerAction.SetDisclaimerType(disclaimerType))
|
||||
|
||||
if (disclaimerType.isAccepted()) {
|
||||
nextHandler((scanResponse))
|
||||
} else {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
store.dispatch(
|
||||
DisclaimerAction.Show {
|
||||
private fun readCard() = scope.launch {
|
||||
delay(timeMillis = 200)
|
||||
ScanCardProcessor.scan(
|
||||
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
|
||||
onProgressStateChange = { showProgress ->
|
||||
if (showProgress) {
|
||||
changeButtonState(ButtonState.PROGRESS)
|
||||
nextHandler(scanResponse)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScanSuccess(scanResponse: ScanResponse) {
|
||||
Analytics.send(IntroductionProcess.CardWasScanned())
|
||||
val globalState = store.state.globalState
|
||||
val tapWalletManager = globalState.tapWalletManager
|
||||
tapWalletManager.updateConfigManager(scanResponse)
|
||||
|
||||
Analytics.addParamsInterceptor(BatchIdParamsInterceptor(scanResponse.card.batchId))
|
||||
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
|
||||
if (scanResponse.isSaltPay()) {
|
||||
if (scanResponse.isSaltPayVisa()) {
|
||||
} else {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
}
|
||||
},
|
||||
onScanStateChange = { scanInProgress ->
|
||||
store.dispatch(HomeAction.ScanInProgress(scanInProgress))
|
||||
},
|
||||
onSuccess = { scanResponse ->
|
||||
scope.launch {
|
||||
val (manager, config) = OnboardingSaltPayState.initDependency(scanResponse)
|
||||
val result = OnboardingSaltPayHelper.isOnboardingCase(scanResponse, manager)
|
||||
delay(500)
|
||||
withMainContext {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
val isOnboardingCase = result.data
|
||||
if (isOnboardingCase) {
|
||||
store.dispatch(GlobalAction.Onboarding.Start(scanResponse, canSkipBackup = false))
|
||||
store.dispatch(OnboardingSaltPayAction.SetDependencies(manager, config))
|
||||
store.dispatch(OnboardingSaltPayAction.Update)
|
||||
navigateTo(AppScreen.OnboardingWallet)
|
||||
} else {
|
||||
navigateTo(AppScreen.Wallet)
|
||||
withIOContext { store.onCardScanned(scanResponse) }
|
||||
}
|
||||
if (preferencesStorage.shouldSaveUserWallets) {
|
||||
val userWallet = UserWalletBuilder(scanResponse).build()
|
||||
userWalletsListManager.save(userWallet)
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
store.onCardScanned(scanResponse)
|
||||
}
|
||||
is Result.Failure -> {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
SaltPayExceptionHandler.handle(result.error)
|
||||
.doOnSuccess {
|
||||
store.onUserWalletSelected(userWallet)
|
||||
}
|
||||
}
|
||||
.doOnResult {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
} else {
|
||||
store.onCardScanned(scanResponse)
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (scanResponse.card.backupStatus?.isActive == false) {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
showSaltPayTapVisaLogoCardDialog()
|
||||
} else {
|
||||
navigateTo(AppScreen.Wallet, null)
|
||||
scope.launch { store.onCardScanned(scanResponse) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
|
||||
store.dispatch(GlobalAction.Onboarding.Start(scanResponse, canSkipBackup = true))
|
||||
val appScreen = OnboardingHelper.whereToNavigate(scanResponse)
|
||||
navigateTo(appScreen)
|
||||
} else {
|
||||
if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly))
|
||||
navigateTo(AppScreen.OnboardingTwins)
|
||||
} else {
|
||||
navigateTo(AppScreen.Wallet, null)
|
||||
}
|
||||
scope.launch { store.onCardScanned(scanResponse) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSaltPayTapVisaLogoCardDialog() {
|
||||
store.dispatchDialogShow(
|
||||
AppDialog.SimpleOkDialogRes(
|
||||
headerId = R.string.saltpay_error_empty_backup_title,
|
||||
messageId = R.string.saltpay_error_empty_backup_message,
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun onScanFailure(error: TangemError) {
|
||||
store.dispatch(HomeAction.ScanInProgress(false))
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
}
|
||||
|
||||
private fun changeButtonState(state: ButtonState) {
|
||||
store.dispatch(HomeAction.ChangeScanCardButtonState(IndeterminateProgressButton(state)))
|
||||
}
|
||||
|
||||
private fun navigateTo(screen: AppScreen, transition: FragmentShareTransition? = null) {
|
||||
postUiDelayBg(DELAY_SDK_DIALOG_CLOSE) {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
store.dispatch(NavigationAction.NavigateTo(screen, transition))
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,18 @@ package com.tangem.tap.features.onboarding
|
|||
|
||||
import com.tangem.domain.common.ProductType
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -39,5 +49,42 @@ class OnboardingHelper {
|
|||
ProductType.SaltPay -> AppScreen.OnboardingWallet
|
||||
}
|
||||
}
|
||||
|
||||
fun trySaveWalletAndNavigateToWalletScreen(
|
||||
scanResponse: ScanResponse,
|
||||
accessCode: String? = null,
|
||||
backupCardsIds: List<String>? = null,
|
||||
) {
|
||||
when {
|
||||
userWalletsListManager.hasSavedUserWallets -> scope.launch {
|
||||
delay(timeMillis = 1_200)
|
||||
store.dispatchOnMain(
|
||||
SaveWalletAction.ProvideBackupInfo(
|
||||
scanResponse = scanResponse,
|
||||
accessCode = accessCode,
|
||||
backupCardsIds = backupCardsIds?.toSet(),
|
||||
),
|
||||
)
|
||||
store.dispatchOnMain(SaveWalletAction.Save)
|
||||
}
|
||||
tangemSdkManager.canUseBiometry &&
|
||||
preferencesStorage.shouldShowSaveWallet -> scope.launch {
|
||||
delay(timeMillis = 1_200)
|
||||
store.dispatchOnMain(
|
||||
SaveWalletAction.ProvideBackupInfo(
|
||||
scanResponse = scanResponse,
|
||||
accessCode = accessCode,
|
||||
backupCardsIds = backupCardsIds?.toSet(),
|
||||
),
|
||||
)
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.SaveWallet))
|
||||
}
|
||||
else -> scope.launch {
|
||||
store.onCardScanned(scanResponse)
|
||||
}
|
||||
}
|
||||
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,17 +14,15 @@ import com.tangem.tap.common.extensions.dispatchOnMain
|
|||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.getAddressData
|
||||
import com.tangem.tap.common.extensions.getTopUpUrl
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
|
|
@ -190,10 +188,7 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
|
|||
}
|
||||
OnboardingNoteAction.Done -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
scope.launch {
|
||||
store.onCardScanned(scanResponse)
|
||||
withMainContext { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) }
|
||||
}
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(scanResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,13 +6,11 @@ import com.tangem.domain.common.extensions.withMainContext
|
|||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -130,10 +128,7 @@ private fun handleOtherCardsAction(action: Action) {
|
|||
}
|
||||
OnboardingOtherCardsAction.Done -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
scope.launch {
|
||||
store.onCardScanned(onboardingManager.scanResponse)
|
||||
withMainContext { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) }
|
||||
}
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(onboardingManager.scanResponse)
|
||||
}
|
||||
is OnboardingOtherCardsAction.Confetti.Hide,
|
||||
is OnboardingOtherCardsAction.SetArtworkUrl,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ sealed class TwinCardsAction : Action {
|
|||
data class LaunchThirdStep(val message: Message) : TwinCardsAction()
|
||||
}
|
||||
|
||||
|
||||
// for the onboarding
|
||||
data class SetPairCardId(val cardId: String) : TwinCardsAction()
|
||||
object TopUp : TwinCardsAction()
|
||||
|
|
@ -53,6 +52,10 @@ sealed class TwinCardsAction : Action {
|
|||
data class SetWalletManager(val walletManager: WalletManager) : TwinCardsAction()
|
||||
object Done : TwinCardsAction()
|
||||
|
||||
data class SaveScannedTwinCardAndNavigateToWallet(
|
||||
val scanResponse: ScanResponse,
|
||||
) : TwinCardsAction()
|
||||
|
||||
sealed class Balance {
|
||||
object Update : TwinCardsAction()
|
||||
data class Set(val balance: OnboardingWalletBalance) : TwinCardsAction()
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import com.tangem.tap.common.analytics.events.AnalyticsParam
|
|||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.getAddressData
|
||||
import com.tangem.tap.common.extensions.getTopUpUrl
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
|
@ -88,7 +89,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
|
||||
when (action) {
|
||||
is TwinCardsAction.Init -> {
|
||||
if (twinCardsState.currentStep == TwinCardsStep.WelcomeOnly) return
|
||||
if (twinCardsState.currentStep is TwinCardsStep.WelcomeOnly) return
|
||||
|
||||
val scanResponse = getScanResponse()
|
||||
onboardingManager?.apply {
|
||||
|
|
@ -141,7 +142,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
}
|
||||
is TwinCardsAction.SetStepOfScreen -> {
|
||||
when (action.step) {
|
||||
TwinCardsStep.WelcomeOnly, TwinCardsStep.Welcome -> {
|
||||
is TwinCardsStep.WelcomeOnly, TwinCardsStep.Welcome -> {
|
||||
Analytics.send(Onboarding.Twins.ScreenOpened())
|
||||
preferencesStorage.saveTwinsOnboardingShown()
|
||||
}
|
||||
|
|
@ -289,21 +290,24 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
store.dispatchOpenUrl(topUpUrl)
|
||||
}
|
||||
TwinCardsAction.Done -> {
|
||||
scope.launch {
|
||||
val scanResponse = getScanResponse()
|
||||
store.onCardScanned(scanResponse)
|
||||
withMainContext {
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
val scanResponse = getScanResponse()
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
store.dispatchOnMain(GlobalAction.Onboarding.Stop)
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(
|
||||
scanResponse = scanResponse,
|
||||
backupCardsIds = listOfNotNull(twinCardsState.twinCardsManager?.secondCardPublicKey),
|
||||
)
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.SaveScannedTwinCardAndNavigateToWallet -> {
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(
|
||||
scanResponse = action.scanResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding.products.twins.redux
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
|
|
@ -62,9 +63,19 @@ data class TwinCardsState(
|
|||
|
||||
enum class CreateTwinWalletMode { CreateWallet, RecreateWallet }
|
||||
|
||||
enum class TwinCardsStep {
|
||||
None, WelcomeOnly, Welcome, Warning, CreateFirstWallet, CreateSecondWallet, CreateThirdWallet,
|
||||
sealed class TwinCardsStep {
|
||||
object None : TwinCardsStep()
|
||||
data class WelcomeOnly(
|
||||
val scanResponse: ScanResponse,
|
||||
) : TwinCardsStep()
|
||||
|
||||
object Welcome : TwinCardsStep()
|
||||
object Warning : TwinCardsStep()
|
||||
object CreateFirstWallet : TwinCardsStep()
|
||||
object CreateSecondWallet : TwinCardsStep()
|
||||
object CreateThirdWallet : TwinCardsStep()
|
||||
|
||||
// for the onboarding
|
||||
TopUpWallet, Done
|
||||
object TopUpWallet : TwinCardsStep()
|
||||
object Done : TwinCardsStep()
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import coil.load
|
|||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tap.common.AndroidAssetReader
|
||||
|
|
@ -23,8 +24,6 @@ import com.tangem.tap.common.extensions.getDrawableCompat
|
|||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.common.redux.navigation.ShareElement
|
||||
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
|
||||
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
|
||||
|
|
@ -43,7 +42,7 @@ import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding
|
|||
class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
||||
|
||||
private val mainBinding by lazy { binding.vMain }
|
||||
private var previousStep = TwinCardsStep.None
|
||||
private var previousStep: TwinCardsStep = TwinCardsStep.None
|
||||
|
||||
private lateinit var twinsWidget: TwinsCardWidget
|
||||
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
|
||||
|
|
@ -127,7 +126,7 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
pbBinding.pbState.progress = state.progress
|
||||
|
||||
when (state.currentStep) {
|
||||
TwinCardsStep.WelcomeOnly -> setupWelcomeOnlyState(state)
|
||||
is TwinCardsStep.WelcomeOnly -> setupWelcomeOnlyState(state, state.currentStep.scanResponse)
|
||||
TwinCardsStep.Welcome -> setupWelcomeState(state)
|
||||
TwinCardsStep.Warning -> setupWarningState(state)
|
||||
TwinCardsStep.CreateFirstWallet -> setupCreateFirstWalletState(state)
|
||||
|
|
@ -157,9 +156,9 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupWelcomeOnlyState(state: TwinCardsState) {
|
||||
private fun setupWelcomeOnlyState(state: TwinCardsState, scanResponse: ScanResponse) {
|
||||
setupWelcomeState(state) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
store.dispatch(TwinCardsAction.SaveScannedTwinCardAndNavigateToWallet(scanResponse))
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.None))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.SaltPayDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
|
||||
|
|
@ -69,17 +70,14 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
|
|||
Analytics.send(Onboarding.Started())
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
card == null -> {
|
||||
// it's possible when found unfinished backup for standard Wallet cards
|
||||
store.dispatch(OnboardingWalletAction.ResumeBackup)
|
||||
}
|
||||
|
||||
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup -> {
|
||||
store.dispatch(OnboardingWalletAction.ResumeBackup)
|
||||
}
|
||||
|
||||
card.wallets.isNotEmpty() && card.backupStatus?.isActive == true -> {
|
||||
when {
|
||||
// check for unfinished backup for saltPay cards. See more
|
||||
|
|
@ -162,8 +160,11 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
|
|||
} else {
|
||||
val backupState = store.state.onboardingWalletState.backupState
|
||||
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
|
||||
scope.launch { globalState.tapWalletManager.onCardScanned(updatedScanResponse) }
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(
|
||||
scanResponse = updatedScanResponse,
|
||||
accessCode = backupState.accessCode,
|
||||
backupCardsIds = backupState.backupCardIds,
|
||||
)
|
||||
}
|
||||
}
|
||||
is OnboardingWalletAction.ResumeBackup -> {
|
||||
|
|
@ -233,6 +234,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
|
||||
when (action) {
|
||||
is BackupAction.StartBackup -> {
|
||||
tangemSdkManager.setAccessCodeRequestPolicy(useBiometricsForAccessCode = false)
|
||||
Analytics.send(Onboarding.Backup.Started())
|
||||
backupService.discardSavedBackup()
|
||||
val primaryCard = scanResponse?.primaryCard
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.domain.common.ScanResponse
|
|||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface SaveWalletAction : Action {
|
||||
data class ProvideAdditionalInfo(
|
||||
data class ProvideBackupInfo(
|
||||
val scanResponse: ScanResponse,
|
||||
val accessCode: String?,
|
||||
val backupCardsIds: Set<String>?,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.common.flatMap
|
|||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
||||
|
|
@ -34,10 +35,10 @@ internal class SaveWalletMiddleware {
|
|||
private fun handleAction(action: SaveWalletAction, state: SaveWalletState) {
|
||||
when (action) {
|
||||
is SaveWalletAction.Save -> saveWalletIfBiometricsEnrolled(state)
|
||||
is SaveWalletAction.Save.Success -> popBack()
|
||||
is SaveWalletAction.EnrollBiometrics.Enroll -> enrollBiometrics()
|
||||
is SaveWalletAction.SaveWalletWasShown -> saveWalletWasShown()
|
||||
is SaveWalletAction.ProvideAdditionalInfo,
|
||||
is SaveWalletAction.Save.Success,
|
||||
is SaveWalletAction.ProvideBackupInfo,
|
||||
is SaveWalletAction.Dismiss,
|
||||
is SaveWalletAction.CloseError,
|
||||
is SaveWalletAction.Save.Error,
|
||||
|
|
@ -60,52 +61,56 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
|
||||
private fun saveWallet(state: SaveWalletState) {
|
||||
val scanResponse = state.additionalInfo?.scanResponse
|
||||
val scanResponse = state.backupInfo?.scanResponse
|
||||
?: store.state.globalState.scanResponse
|
||||
?: return
|
||||
|
||||
scope.launch {
|
||||
val userWallet = UserWalletBuilder(scanResponse)
|
||||
.setBackupCardsIds(backupCardsIds = state.additionalInfo?.backupCardsIds)
|
||||
.setBackupCardsIds(backupCardsIds = state.backupInfo?.backupCardsIds)
|
||||
.build()
|
||||
|
||||
userWalletsListManager.save(userWallet)
|
||||
.flatMap {
|
||||
trySaveAccessCode(
|
||||
userWallet = userWallet,
|
||||
additionalInfo = state.additionalInfo,
|
||||
)
|
||||
}
|
||||
saveAccessCodeIfNeeded(state.backupInfo?.accessCode, userWallet.cardsInWallet)
|
||||
.flatMap { userWalletsListManager.save(userWallet) }
|
||||
.doOnFailure { error ->
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Error(error))
|
||||
}
|
||||
.doOnSuccess {
|
||||
preferencesStorage.shouldSaveUserWallets = true
|
||||
preferencesStorage.shouldSaveAccessCodes = true
|
||||
|
||||
val isSavedWalletSelected =
|
||||
userWalletsListManager.selectedUserWalletSync?.walletId == userWallet.walletId
|
||||
|
||||
if (isSavedWalletSelected) {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
} else {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.WalletSelector))
|
||||
}
|
||||
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Success)
|
||||
store.onUserWalletSelected(userWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun popBack() {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
}
|
||||
|
||||
private fun saveWalletWasShown() {
|
||||
preferencesStorage.shouldShowSaveWallet = false
|
||||
}
|
||||
|
||||
private suspend fun trySaveAccessCode(
|
||||
userWallet: UserWallet,
|
||||
additionalInfo: SaveWalletState.WalletAdditionalInfo?,
|
||||
private suspend fun saveAccessCodeIfNeeded(
|
||||
accessCode: String?,
|
||||
cardsInWallet: Set<String>,
|
||||
): CompletionResult<Unit> {
|
||||
return when {
|
||||
additionalInfo?.accessCode != null -> {
|
||||
tangemSdkManager.saveAccessCode(
|
||||
accessCode = additionalInfo.accessCode,
|
||||
cardsIds = userWallet.cardsInWallet,
|
||||
)
|
||||
accessCode != null -> {
|
||||
tangemSdkManager.unlockBiometricKeys()
|
||||
.flatMap {
|
||||
tangemSdkManager.saveAccessCode(
|
||||
accessCode = accessCode,
|
||||
cardsIds = cardsInWallet,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
CompletionResult.Success(Unit)
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ internal object SaveWalletReducer {
|
|||
|
||||
private fun internalReduce(action: SaveWalletAction, state: SaveWalletState): SaveWalletState {
|
||||
return when (action) {
|
||||
is SaveWalletAction.ProvideAdditionalInfo -> state.copy(
|
||||
additionalInfo = SaveWalletState.WalletAdditionalInfo(
|
||||
is SaveWalletAction.ProvideBackupInfo -> state.copy(
|
||||
backupInfo = SaveWalletState.WalletBackupInfo(
|
||||
scanResponse = action.scanResponse,
|
||||
accessCode = action.accessCode,
|
||||
backupCardsIds = action.backupCardsIds,
|
||||
|
|
@ -27,11 +27,11 @@ internal object SaveWalletReducer {
|
|||
isSaveInProgress = false,
|
||||
)
|
||||
is SaveWalletAction.Save.Success -> state.copy(
|
||||
additionalInfo = null,
|
||||
backupInfo = null,
|
||||
isSaveInProgress = false,
|
||||
)
|
||||
is SaveWalletAction.Dismiss -> state.copy(
|
||||
additionalInfo = null,
|
||||
backupInfo = null,
|
||||
)
|
||||
is SaveWalletAction.CloseError -> state.copy(
|
||||
error = null,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import com.tangem.domain.common.ScanResponse
|
|||
import org.rekotlin.StateType
|
||||
|
||||
data class SaveWalletState(
|
||||
val additionalInfo: WalletAdditionalInfo? = null,
|
||||
val backupInfo: WalletBackupInfo? = null,
|
||||
val isSaveInProgress: Boolean = false,
|
||||
val needEnrollBiometrics: Boolean = false,
|
||||
val error: TangemError? = null,
|
||||
) : StateType {
|
||||
data class WalletAdditionalInfo(
|
||||
data class WalletBackupInfo(
|
||||
val scanResponse: ScanResponse,
|
||||
val accessCode: String?,
|
||||
val backupCardsIds: Set<String>?,
|
||||
|
|
|
|||
|
|
@ -6,13 +6,7 @@ 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.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
|
|
|
|||
|
|
@ -5,26 +5,18 @@ import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras
|
|||
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.TransactionError
|
||||
import com.tangem.blockchain.common.TransactionSender
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.CardDTO
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -36,27 +28,17 @@ import com.tangem.tap.domain.extensions.minimalAmount
|
|||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.DemoTransactionSender
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
|
||||
import com.tangem.tap.features.send.redux.AmountAction
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.FeeAction.RequestFee
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.features.send.redux.SendActionUi
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
import com.tangem.tap.features.send.redux.states.ExternalTransactionData
|
||||
import com.tangem.tap.features.send.redux.states.MainCurrencyType
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdk
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import java.util.*
|
||||
|
|
@ -231,13 +213,9 @@ private fun sendTransaction(
|
|||
dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
scope.launch(Dispatchers.IO) {
|
||||
withContext(Dispatchers.Main) {
|
||||
dispatch(WalletAction.LoadWallet(BlockchainNetwork.fromWalletManager(walletManager)))
|
||||
}
|
||||
updateWallet(walletManager)
|
||||
delay(11000) // more than 10000 to avoid throttling
|
||||
withContext(Dispatchers.Main) {
|
||||
dispatch(WalletAction.LoadWallet(BlockchainNetwork.fromWalletManager(walletManager)))
|
||||
}
|
||||
updateWallet(walletManager)
|
||||
}
|
||||
}
|
||||
is SimpleResult.Failure -> {
|
||||
|
|
@ -327,4 +305,17 @@ private fun updateWarnings(dispatch: (Action) -> Unit) {
|
|||
|
||||
val warnings = warningsManager.getWarnings(WarningMessage.Location.SendScreen, listOf(blockchain))
|
||||
dispatch(SendAction.Warnings.Set(warnings))
|
||||
}
|
||||
|
||||
private suspend fun updateWallet(walletManager: WalletManager) {
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) {
|
||||
walletCurrenciesManager.update(
|
||||
userWallet = selectedUserWallet,
|
||||
blockchainNetwork = blockchainNetwork,
|
||||
)
|
||||
} else {
|
||||
store.dispatchOnMain(WalletAction.LoadWallet(BlockchainNetwork.fromWalletManager(walletManager)))
|
||||
}
|
||||
}
|
||||
|
|
@ -30,25 +30,10 @@ import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget
|
|||
import com.tangem.tap.common.toggleWidget.ViewStateWidget
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.CheckClipboard
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.PasteAddressPayId
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.SetTruncateHandler
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.TruncateOrRestore
|
||||
import com.tangem.tap.features.send.redux.AmountAction
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi.CheckAmountToSend
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi.SetMainCurrency
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi.SetMaxAmount
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi.ToggleMainCurrency
|
||||
import com.tangem.tap.features.send.redux.FeeActionUi.ChangeIncludeFee
|
||||
import com.tangem.tap.features.send.redux.FeeActionUi.ChangeSelectedFee
|
||||
import com.tangem.tap.features.send.redux.FeeActionUi.ToggleControlsVisibility
|
||||
import com.tangem.tap.features.send.redux.ReceiptAction
|
||||
import com.tangem.tap.features.send.redux.ReleaseSendState
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.features.send.redux.SendActionUi
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.*
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi.*
|
||||
import com.tangem.tap.features.send.redux.FeeActionUi.*
|
||||
import com.tangem.tap.features.send.redux.states.FeeType
|
||||
import com.tangem.tap.features.send.redux.states.MainCurrencyType
|
||||
import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber
|
||||
|
|
@ -60,12 +45,7 @@ import com.tangem.wallet.R
|
|||
import com.tangem.wallet.databinding.FragmentSendBinding
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.text.DecimalFormatSymbols
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.common.flatMap
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.DomainWrapped
|
||||
|
|
@ -18,8 +19,7 @@ import com.tangem.domain.features.addCustomToken.CustomCurrency
|
|||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.assetReader
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.ManageTokens
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
|
|
@ -34,9 +34,6 @@ import com.tangem.tap.domain.tokens.LoadAvailableCoinsService
|
|||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -49,14 +46,12 @@ class TokensMiddleware {
|
|||
when (action) {
|
||||
is TokensAction.LoadCurrencies -> handleLoadCurrencies(action.scanResponse)
|
||||
is TokensAction.SaveChanges -> handleSaveChanges(action)
|
||||
is TokensAction.PrepareAndNavigateToAddCustomToken -> {
|
||||
handleAddingCustomToken(action)
|
||||
}
|
||||
is TokensAction.PrepareAndNavigateToAddCustomToken -> handleAddingCustomToken()
|
||||
is TokensAction.SetSearchInput -> {
|
||||
Analytics.send(ManageTokens.TokenSearched())
|
||||
handleLoadCurrencies(
|
||||
scanResponse = store.state.globalState.scanResponse,
|
||||
newSearchInput = action.searchInput
|
||||
newSearchInput = action.searchInput,
|
||||
)
|
||||
}
|
||||
is TokensAction.LoadMore -> {
|
||||
|
|
@ -106,8 +101,8 @@ class TokensMiddleware {
|
|||
.filter(supportedBlockchains.toSet())
|
||||
store.dispatchOnMain(
|
||||
TokensAction.LoadCurrencies.Success(
|
||||
currencies, loadCoinsResult.data.moreAvailable
|
||||
)
|
||||
currencies, loadCoinsResult.data.moreAvailable,
|
||||
),
|
||||
)
|
||||
}
|
||||
is Result.Failure -> store.dispatchOnMain(TokensAction.LoadCurrencies.Failure)
|
||||
|
|
@ -116,14 +111,14 @@ class TokensMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleSaveChanges(action: TokensAction.SaveChanges) {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
private fun handleSaveChanges(action: TokensAction.SaveChanges) = scope.launch {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return@launch
|
||||
|
||||
val currentTokens = store.state.tokensState.addedWallets.toNonCustomTokensWithBlockchains(
|
||||
scanResponse.card.derivationStyle
|
||||
scanResponse.card.derivationStyle,
|
||||
)
|
||||
val currentBlockchains = store.state.tokensState.addedWallets.toNonCustomBlockchains(
|
||||
scanResponse.card.derivationStyle
|
||||
scanResponse.card.derivationStyle,
|
||||
)
|
||||
|
||||
val blockchainsToAdd = action.addedBlockchains.filter { !currentBlockchains.contains(it) }
|
||||
|
|
@ -149,7 +144,7 @@ class TokensMiddleware {
|
|||
) {
|
||||
store.dispatchDebugErrorNotification("Nothing to save")
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
return
|
||||
return@launch
|
||||
}
|
||||
|
||||
val currencyList = convertToCurrencies(
|
||||
|
|
@ -201,8 +196,9 @@ class TokensMiddleware {
|
|||
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.derivePublicKeys(
|
||||
scanResponse.card.cardId,
|
||||
derivations
|
||||
cardId = scanResponse.card.cardId,
|
||||
derivations = derivations,
|
||||
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
|
||||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
|
|
@ -221,6 +217,7 @@ class TokensMiddleware {
|
|||
)
|
||||
store.dispatchOnMain(GlobalAction.SaveScanNoteResponse(updatedScanResponse))
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
|
||||
onSuccess(updatedScanResponse)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
@ -275,50 +272,78 @@ class TokensMiddleware {
|
|||
scanResponse: ScanResponse,
|
||||
currencyList: List<Currency>,
|
||||
) {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
val derivationStyle = scanResponse.card.derivationStyle
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) {
|
||||
val updatedUserWallet = selectedUserWallet.copy(
|
||||
scanResponse = scanResponse,
|
||||
)
|
||||
|
||||
val addActions = currencyList.mapIndexedNotNull { index, currency ->
|
||||
when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val derivationPath = currency.derivationPath?.let { DerivationPath(it) }
|
||||
|
||||
val derivationParams = derivationStyle?.let {
|
||||
when (derivationPath) {
|
||||
null -> DerivationParams.Default(derivationStyle)
|
||||
else -> DerivationParams.Custom(derivationPath)
|
||||
}
|
||||
scope.launch {
|
||||
userWalletsListManager.update(updatedUserWallet)
|
||||
.flatMap {
|
||||
walletCurrenciesManager.addCurrencies(
|
||||
userWallet = updatedUserWallet,
|
||||
currenciesToAdd = currencyList,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
val derivationStyle = scanResponse.card.derivationStyle
|
||||
|
||||
val addActions = currencyList.mapIndexedNotNull { index, currency ->
|
||||
when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val derivationPath = currency.derivationPath?.let { DerivationPath(it) }
|
||||
|
||||
val derivationParams = derivationStyle?.let {
|
||||
when (derivationPath) {
|
||||
null -> DerivationParams.Default(derivationStyle)
|
||||
else -> DerivationParams.Custom(derivationPath)
|
||||
}
|
||||
}
|
||||
val walletManager = factory.makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = currency.blockchain,
|
||||
derivationParams = derivationParams,
|
||||
) ?: return@mapIndexedNotNull null
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
WalletAction.MultiWallet.AddBlockchain(
|
||||
blockchain = blockchainNetwork,
|
||||
walletManager = walletManager,
|
||||
save = index == currencyList.lastIndex,
|
||||
)
|
||||
}
|
||||
is Currency.Token -> {
|
||||
val rawDerivationPath = currency.derivationPath
|
||||
?: currency.blockchain.derivationPath(derivationStyle)?.rawPath
|
||||
val blockchainNetwork =
|
||||
BlockchainNetwork(currency.blockchain, rawDerivationPath, listOf(currency.token))
|
||||
WalletAction.MultiWallet.AddToken(
|
||||
token = currency.token,
|
||||
blockchain = blockchainNetwork,
|
||||
save = index == currencyList.lastIndex,
|
||||
)
|
||||
}
|
||||
val walletManager = factory.makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = currency.blockchain,
|
||||
derivationParams = derivationParams,
|
||||
) ?: return@mapIndexedNotNull null
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
WalletAction.MultiWallet.AddBlockchain(
|
||||
blockchain = blockchainNetwork,
|
||||
walletManager = walletManager,
|
||||
save = index == currencyList.lastIndex,
|
||||
)
|
||||
}
|
||||
is Currency.Token -> {
|
||||
val rawDerivationPath = currency.derivationPath
|
||||
?: currency.blockchain.derivationPath(derivationStyle)?.rawPath
|
||||
val blockchainNetwork =
|
||||
BlockchainNetwork(currency.blockchain, rawDerivationPath, listOf(currency.token))
|
||||
WalletAction.MultiWallet.AddToken(
|
||||
token = currency.token,
|
||||
blockchain = blockchainNetwork,
|
||||
save = index == currencyList.lastIndex,
|
||||
)
|
||||
}
|
||||
}
|
||||
addActions.forEach { store.dispatchOnMain(it) }
|
||||
}
|
||||
addActions.forEach { store.dispatchOnMain(it) }
|
||||
}
|
||||
|
||||
private fun removeCurrenciesIfNeeded(currencies: List<Currency>) {
|
||||
if (currencies.isNotEmpty()) store.dispatch(WalletAction.MultiWallet.RemoveWallets(currencies))
|
||||
private suspend fun removeCurrenciesIfNeeded(currencies: List<Currency>) {
|
||||
when {
|
||||
currencies.isEmpty() -> Unit
|
||||
userWalletsListManager.hasSavedUserWallets -> {
|
||||
walletCurrenciesManager.removeCurrencies(
|
||||
userWallet = userWalletsListManager.selectedUserWalletSync!!,
|
||||
currenciesToRemove = currencies,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
store.dispatch(WalletAction.MultiWallet.RemoveWallets(currencies))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNeedToDerive(scanResponse: ScanResponse, currency: Currency): Boolean {
|
||||
|
|
@ -327,7 +352,7 @@ class TokensMiddleware {
|
|||
} ?: false
|
||||
}
|
||||
|
||||
private fun handleAddingCustomToken(action: TokensAction.PrepareAndNavigateToAddCustomToken) {
|
||||
private fun handleAddingCustomToken() = scope.launch {
|
||||
val onAddCustomToken = fun(customCurrency: CustomCurrency) {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
package com.tangem.tap.features.wallet.redux
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.domain.common.CardDTO
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.models.WalletDialog
|
||||
|
|
@ -27,7 +26,8 @@ sealed class WalletAction : Action {
|
|||
|
||||
object LoadData : WalletAction() {
|
||||
object Refresh : WalletAction()
|
||||
data class Failure(val error: TapError) : WalletAction()
|
||||
object Success : WalletAction()
|
||||
data class Failure(val error: TapError?) : WalletAction()
|
||||
}
|
||||
|
||||
data class LoadWallet(
|
||||
|
|
@ -54,20 +54,28 @@ sealed class WalletAction : Action {
|
|||
sealed class MultiWallet : WalletAction() {
|
||||
data class SetIsMultiwalletAllowed(val isMultiwalletAllowed: Boolean) : MultiWallet()
|
||||
|
||||
data class AddBlockchains(
|
||||
val blockchains: List<BlockchainNetwork>,
|
||||
val walletManagers: List<WalletManager>,
|
||||
) : MultiWallet()
|
||||
|
||||
data class AddTokens(
|
||||
val tokens: List<Token>,
|
||||
val blockchain: BlockchainNetwork,
|
||||
) : MultiWallet()
|
||||
|
||||
data class AddBlockchain(
|
||||
val blockchain: BlockchainNetwork,
|
||||
val walletManager: WalletManager?,
|
||||
val save: Boolean,
|
||||
) : MultiWallet()
|
||||
|
||||
data class AddBlockchains(
|
||||
val blockchains: List<BlockchainNetwork>, val walletManagers: List<WalletManager>, val save: Boolean,
|
||||
data class AddToken(
|
||||
val token: Token,
|
||||
val blockchain: BlockchainNetwork,
|
||||
val save: Boolean,
|
||||
) : MultiWallet()
|
||||
|
||||
data class AddTokens(val tokens: List<Token>, val blockchain: BlockchainNetwork, val save: Boolean) :
|
||||
MultiWallet()
|
||||
|
||||
data class AddToken(val token: Token, val blockchain: BlockchainNetwork, val save: Boolean) : MultiWallet()
|
||||
data class SaveCurrencies(
|
||||
val blockchainNetworks: List<BlockchainNetwork>, val card: CardDTO? = null,
|
||||
) : MultiWallet()
|
||||
|
|
@ -161,6 +169,8 @@ sealed class WalletAction : Action {
|
|||
|
||||
object CreateWallet : WalletAction()
|
||||
object EmptyWallet : WalletAction()
|
||||
object ChangeWallet : WalletAction()
|
||||
object ShowSaveWalletIfNeeded : WalletAction()
|
||||
|
||||
sealed class TradeCryptoAction : WalletAction() {
|
||||
object Sell : TradeCryptoAction()
|
||||
|
|
@ -190,4 +200,8 @@ sealed class WalletAction : Action {
|
|||
object ChooseAppCurrency : AppCurrencyAction()
|
||||
data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction()
|
||||
}
|
||||
|
||||
data class UserWalletChanged(val userWallet: UserWallet) : WalletAction()
|
||||
data class WalletStoresChanged(val walletStores: List<WalletStoreModel>) : WalletAction()
|
||||
data class TotalFiatBalanceChanged(val balance: TotalFiatBalance) : WalletAction()
|
||||
}
|
||||
|
|
@ -14,16 +14,13 @@ import com.tangem.tap.common.toggleWidget.WidgetState
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.models.WalletWarning
|
||||
import com.tangem.tap.features.wallet.models.*
|
||||
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
|
||||
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import org.rekotlin.StateType
|
||||
import java.math.BigDecimal
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
|
@ -80,6 +77,9 @@ data class WalletState(
|
|||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
|
||||
val hasSavedWallets: Boolean
|
||||
get() = userWalletsListManager.hasSavedUserWallets
|
||||
|
||||
fun getWalletManager(currency: Currency?): WalletManager? {
|
||||
if (currency?.blockchain == null) return null
|
||||
return getWalletStore(currency)?.walletManager
|
||||
|
|
@ -242,11 +242,11 @@ data class WalletState(
|
|||
totalBalance = TotalBalance(
|
||||
state = walletsData.findProgressState(),
|
||||
fiatAmount = walletsData.calculateTotalFiatAmount(),
|
||||
fiatCurrency = store.state.globalState.appCurrency
|
||||
)
|
||||
fiatCurrency = store.state.globalState.appCurrency,
|
||||
),
|
||||
)
|
||||
} else this.copy(
|
||||
totalBalance = null
|
||||
totalBalance = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ import com.tangem.tap.domain.TapWalletManager
|
|||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.models.WalletDialog
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
|
||||
import com.tangem.tap.persistence.FiatCurrenciesPrefStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AppCurrencyMiddleware(
|
||||
|
|
@ -65,11 +67,19 @@ class AppCurrencyMiddleware(
|
|||
|
||||
private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) {
|
||||
Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(action.fiatCurrency)))
|
||||
tapWalletManager.rates.clear()
|
||||
fiatCurrenciesPrefStorage.saveAppCurrency(action.fiatCurrency)
|
||||
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
store.dispatch(DetailsAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
store.dispatch(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) {
|
||||
scope.launch {
|
||||
tapWalletManager.loadData(selectedUserWallet, refresh = true)
|
||||
}
|
||||
} else {
|
||||
tapWalletManager.rates.clear()
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<CurrenciesResponse.Currency>.mapToUiModel(): List<FiatCurrency> {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ package com.tangem.tap.features.wallet.redux.middlewares
|
|||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken
|
||||
|
|
@ -17,6 +19,8 @@ import com.tangem.tap.common.redux.navigation.AppScreen
|
|||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.scanCard.ScanCardProcessor
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
|
|
@ -26,9 +30,6 @@ import com.tangem.tap.features.wallet.redux.WalletAction
|
|||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.models.WalletDialog
|
||||
import com.tangem.tap.features.wallet.redux.reducers.toWallet
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userTokensRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -53,7 +54,7 @@ class MultiWalletMiddleware {
|
|||
addTokens(listOf(action.token), action.blockchain, walletState, globalState, action.save)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddTokens -> {
|
||||
addTokens(action.tokens, action.blockchain, walletState, globalState, action.save)
|
||||
addTokens(action.tokens, action.blockchain, walletState, globalState, save = false)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchain -> {
|
||||
action.walletManager?.let {
|
||||
|
|
@ -121,19 +122,27 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.RemoveWallet -> {
|
||||
val currency = action.currency
|
||||
val card = globalState.scanResponse?.card.guard {
|
||||
store.dispatchErrorNotification(TapError.UnsupportedState("card is NULL"))
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
return
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) scope.launch {
|
||||
walletCurrenciesManager.removeCurrency(
|
||||
userWallet = selectedUserWallet,
|
||||
currencyToRemove = action.currency,
|
||||
)
|
||||
} else {
|
||||
val currency = action.currency
|
||||
val card = globalState.scanResponse?.card.guard {
|
||||
store.dispatchErrorNotification(TapError.UnsupportedState("card is NULL"))
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
return
|
||||
}
|
||||
var currencies = walletState?.currencies ?: emptyList()
|
||||
currencies = currencies.filterNot { it == currency }
|
||||
if (currency.isBlockchain()) {
|
||||
currencies
|
||||
.filter { it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath }
|
||||
}
|
||||
scope.launch { userTokensRepository.saveUserTokens(card, currencies) }
|
||||
}
|
||||
var currencies = walletState?.currencies ?: emptyList()
|
||||
currencies = currencies.filterNot { it == currency }
|
||||
if (currency.isBlockchain()) {
|
||||
currencies
|
||||
.filter { it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath }
|
||||
}
|
||||
scope.launch { userTokensRepository.saveUserTokens(card, currencies) }
|
||||
}
|
||||
is WalletAction.MultiWallet.RemoveWallets -> {
|
||||
val card = globalState.scanResponse?.card.guard {
|
||||
|
|
@ -154,11 +163,36 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.ScanToGetDerivations -> {
|
||||
store.dispatch(WalletAction.Scan)
|
||||
val selectedWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedWallet != null) {
|
||||
scanAndUpdateCard(selectedWallet, walletState)
|
||||
} else {
|
||||
store.dispatch(WalletAction.Scan)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanAndUpdateCard(
|
||||
selectedWallet: UserWallet,
|
||||
state: WalletState?,
|
||||
) = scope.launch {
|
||||
ScanCardProcessor.scan(
|
||||
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
|
||||
cardId = selectedWallet.cardId,
|
||||
additionalBlockchainsToDerive = state?.missingDerivations?.map { it.blockchain },
|
||||
) { scanResponse ->
|
||||
val userWallet = selectedWallet.copy(
|
||||
scanResponse = scanResponse,
|
||||
)
|
||||
|
||||
userWalletsListManager.update(userWallet)
|
||||
.doOnSuccess {
|
||||
store.state.globalState.tapWalletManager.loadData(userWallet, refresh = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDummyBalances(walletManagers: List<WalletManager>) {
|
||||
walletManagers.forEach {
|
||||
if (it.wallet.fundsAvailable(AmountType.Coin) == BigDecimal.ZERO) {
|
||||
|
|
|
|||
|
|
@ -11,17 +11,10 @@ import com.tangem.common.services.Result
|
|||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.dispatchToastNotification
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.shareText
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -29,25 +22,20 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.failedRates
|
||||
import com.tangem.tap.domain.loadedRates
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.tap.features.wallet.models.filterByCoin
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.getSendableAmounts
|
||||
import com.tangem.tap.features.wallet.models.*
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.network.NetworkStateChanged
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -219,20 +207,34 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.LoadData,
|
||||
is WalletAction.LoadData.Refresh -> {
|
||||
is WalletAction.LoadData.Refresh,
|
||||
-> {
|
||||
val selectedWallet = userWalletsListManager.selectedUserWalletSync
|
||||
scope.launch {
|
||||
val scanNoteResponse = globalState.scanResponse ?: return@launch
|
||||
if (walletState.walletsData.isNotEmpty()) {
|
||||
globalState.tapWalletManager.reloadData(scanNoteResponse)
|
||||
if (selectedWallet != null) {
|
||||
globalState.tapWalletManager.loadData(
|
||||
userWallet = selectedWallet,
|
||||
refresh = action is WalletAction.LoadData.Refresh,
|
||||
)
|
||||
} else {
|
||||
globalState.tapWalletManager.loadData(scanNoteResponse)
|
||||
val scanNoteResponse = globalState.scanResponse ?: return@launch
|
||||
if (walletState.walletsData.isNotEmpty()) {
|
||||
globalState.tapWalletManager.reloadData(scanNoteResponse)
|
||||
} else {
|
||||
globalState.tapWalletManager.loadData(scanNoteResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is NetworkStateChanged -> {
|
||||
globalState.scanResponse?.let { scanNoteResponse ->
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
|
||||
scope.launch { globalState.tapWalletManager.loadData(scanNoteResponse) }
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
|
||||
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) scope.launch {
|
||||
globalState.tapWalletManager.loadData(selectedUserWallet)
|
||||
} else {
|
||||
globalState.scanResponse?.let { scanNoteResponse ->
|
||||
scope.launch { globalState.tapWalletManager.loadData(scanNoteResponse) }
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.CopyAddress -> {
|
||||
|
|
@ -255,7 +257,7 @@ class WalletMiddleware {
|
|||
if (newAction is PrepareSendScreen && newAction.walletManager == null) {
|
||||
store.dispatch(NavigationAction.PopBackTo(screen = AppScreen.Home))
|
||||
FirebaseCrashlytics.getInstance().recordException(
|
||||
IllegalStateException("PrepareSendScreen: walletManager is null")
|
||||
IllegalStateException("PrepareSendScreen: walletManager is null"),
|
||||
)
|
||||
store.dispatchToastNotification(R.string.internal_error_wallet_manager_not_found)
|
||||
} else {
|
||||
|
|
@ -265,6 +267,75 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.ShowSaveWalletIfNeeded -> {
|
||||
showSaveWalletIfNeeded()
|
||||
}
|
||||
is WalletAction.ChangeWallet -> {
|
||||
changeWallet()
|
||||
}
|
||||
is WalletAction.UserWalletChanged -> {
|
||||
scope.launch {
|
||||
globalState.tapWalletManager.loadData(action.userWallet)
|
||||
}
|
||||
}
|
||||
is WalletAction.WalletStoresChanged -> {
|
||||
scope.launch(Dispatchers.Default) {
|
||||
fetchTotalFiatBalance(action.walletStores, walletState)
|
||||
findMissedDerivations(action.walletStores)
|
||||
tryToShowAppRatingWarning(action.walletStores)
|
||||
}
|
||||
}
|
||||
is WalletAction.TotalFiatBalanceChanged -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchTotalFiatBalance(walletStores: List<WalletStoreModel>, state: WalletState) {
|
||||
scope.launch {
|
||||
val totalFiatBalance = totalFiatBalanceCalculator.calculate(
|
||||
prevAmount = state.totalBalance?.fiatAmount ?: BigDecimal.ZERO,
|
||||
walletStores = walletStores,
|
||||
)
|
||||
store.dispatchOnMain(WalletAction.TotalFiatBalanceChanged(totalFiatBalance))
|
||||
}
|
||||
}
|
||||
|
||||
private fun findMissedDerivations(wallStores: List<WalletStoreModel>) {
|
||||
scope.launch {
|
||||
val missedDerivations = wallStores
|
||||
.filter { store ->
|
||||
store.walletsData.any { it.status is WalletDataModel.MissedDerivation }
|
||||
}
|
||||
.map { it.blockchainNetwork }
|
||||
|
||||
store.dispatchOnMain(WalletAction.MultiWallet.AddMissingDerivations(missedDerivations))
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToShowAppRatingWarning(walletStores: List<WalletStoreModel>) {
|
||||
warningsMiddleware.tryToShowAppRatingWarning(
|
||||
hasNonZeroWallets = walletStores
|
||||
.flatMap { it.walletsData }
|
||||
.any { it.status.amount.isGreaterThan(BigDecimal.ZERO) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSaveWalletIfNeeded() {
|
||||
if (preferencesStorage.shouldShowSaveWallet
|
||||
&& tangemSdkManager.canUseBiometry
|
||||
&& store.state.navigationState.backStack.lastOrNull() == AppScreen.Wallet
|
||||
) {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.SaveWallet))
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeWallet() {
|
||||
when {
|
||||
userWalletsListManager.hasSavedUserWallets -> {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.WalletSelector))
|
||||
}
|
||||
else -> {
|
||||
store.dispatch(WalletAction.Scan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,11 +67,8 @@ class WarningsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
fun tryToShowAppRatingWarning(wallet: Wallet) {
|
||||
val nonZeroWalletsCount = wallet.amounts.filter {
|
||||
it.value.value?.isGreaterThan(BigDecimal.ZERO) ?: false
|
||||
}.size
|
||||
if (nonZeroWalletsCount > 0) {
|
||||
fun tryToShowAppRatingWarning(hasNonZeroWallets: Boolean) {
|
||||
if (hasNonZeroWallets) {
|
||||
preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds()
|
||||
}
|
||||
if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) {
|
||||
|
|
@ -79,6 +76,13 @@ class WarningsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
fun tryToShowAppRatingWarning(wallet: Wallet) {
|
||||
val nonZeroWalletsCount = wallet.amounts.filter {
|
||||
it.value.value?.isGreaterThan(BigDecimal.ZERO) ?: false
|
||||
}.size
|
||||
tryToShowAppRatingWarning(hasNonZeroWallets = nonZeroWalletsCount > 0)
|
||||
}
|
||||
|
||||
private fun showCardWarningsIfNeeded(globalState: GlobalState?) {
|
||||
globalState?.scanResponse?.let { scanResponse ->
|
||||
val card = scanResponse.card
|
||||
|
|
|
|||
|
|
@ -9,17 +9,9 @@ import com.tangem.tap.common.extensions.toFiatString
|
|||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.models.filterByToken
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.models.*
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.features.wallet.ui.TokenData
|
||||
|
|
@ -174,9 +166,13 @@ class MultiWalletReducer {
|
|||
is WalletAction.MultiWallet.ShowWalletBackupWarning -> state.copy(
|
||||
showBackupWarning = action.show,
|
||||
)
|
||||
is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(missingDerivations = action.blockchains)
|
||||
is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(
|
||||
missingDerivations = action.blockchains,
|
||||
)
|
||||
is WalletAction.MultiWallet.BackupWallet -> state
|
||||
is WalletAction.MultiWallet.ScanToGetDerivations -> state
|
||||
is WalletAction.MultiWallet.ScanToGetDerivations -> state.copy(
|
||||
state = ProgressState.Loading,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,34 +3,31 @@ package com.tangem.tap.features.wallet.redux.reducers
|
|||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.extensions.mapNotNullValues
|
||||
import com.tangem.domain.common.CardDTO
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.toFiatRateString
|
||||
import com.tangem.tap.common.extensions.toFiatString
|
||||
import com.tangem.tap.common.extensions.toFiatValue
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.common.extensions.toFormattedFiatValue
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.getArtworkUrl
|
||||
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAddresses
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.features.wallet.ui.TokenData
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.Action
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -124,7 +121,10 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
|
|||
),
|
||||
)
|
||||
}
|
||||
else -> { /* no-op */
|
||||
else -> {
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Error,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -297,8 +297,8 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
|
|||
newState = newState.updateWalletData(
|
||||
selectedWalletData?.copy(
|
||||
walletAddresses = WalletAddresses(
|
||||
address,
|
||||
walletAddresses.list,
|
||||
selectedAddress = address,
|
||||
list = walletAddresses.list,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -319,13 +319,164 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
|
|||
}
|
||||
is WalletAction.UserTokens.Loading -> newState = newState.copy(loadingUserTokens = true)
|
||||
is WalletAction.UserTokens.Loaded -> newState = newState.copy(loadingUserTokens = false)
|
||||
else -> { /* no-op */
|
||||
is WalletAction.UserWalletChanged -> with(action.userWallet) {
|
||||
val card = scanResponse.card
|
||||
newState = WalletState(
|
||||
cardId = card.cardId,
|
||||
isMultiwalletAllowed = card.isMultiwalletAllowed,
|
||||
cardImage = Artwork(
|
||||
artworkId = artworkUrl,
|
||||
),
|
||||
isTestnet = card.isTestCard,
|
||||
state = ProgressState.Loading,
|
||||
wallets = newState.wallets,
|
||||
showBackupWarning = card.isMultiwalletAllowed &&
|
||||
card.settings.isBackupAllowed &&
|
||||
card.backupStatus == CardDTO.BackupStatus.NoBackup,
|
||||
)
|
||||
}
|
||||
is WalletAction.WalletStoresChanged -> {
|
||||
newState = newState.copy(
|
||||
wallets = action.walletStores.mapToReduxModel(newState.isMultiwalletAllowed),
|
||||
)
|
||||
}
|
||||
is WalletAction.TotalFiatBalanceChanged -> {
|
||||
newState = newState.copy(
|
||||
totalBalance = action.balance.mapToReduxModel(),
|
||||
)
|
||||
}
|
||||
is WalletAction.LoadData.Success -> {
|
||||
val selectedCurrency = if (!newState.isMultiwalletAllowed) {
|
||||
newState.wallets.firstOrNull()
|
||||
?.walletsData
|
||||
?.firstOrNull()
|
||||
?.currency
|
||||
} else {
|
||||
newState.selectedCurrency
|
||||
}
|
||||
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done,
|
||||
selectedCurrency = selectedCurrency,
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
appStateHolder.walletState = newState
|
||||
return newState
|
||||
}
|
||||
|
||||
@JvmName("walletStoreModelToReduxModel")
|
||||
private fun List<WalletStoreModel>.mapToReduxModel(
|
||||
isMultiWalletAllowed: Boolean,
|
||||
): List<WalletStore> {
|
||||
return this.map { walletStoreModel ->
|
||||
with(walletStoreModel) {
|
||||
WalletStore(
|
||||
walletManager = walletManager,
|
||||
blockchainNetwork = blockchainNetwork,
|
||||
walletsData = walletsData.mapToReduxModel(isMultiWalletAllowed, walletStoreModel.walletRent),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName("walletDataModelToReduxModel")
|
||||
private fun List<WalletDataModel>.mapToReduxModel(
|
||||
isMultiWalletAllowed: Boolean,
|
||||
walletRent: WalletStoreModel.WalletRent?,
|
||||
): List<WalletData> {
|
||||
return this.map { walletDataModel ->
|
||||
with(walletDataModel) {
|
||||
val amount = status.amount
|
||||
val amountFormatted = amount.toFormattedCurrencyString(
|
||||
decimals = currency.decimals,
|
||||
currency = currency.currencySymbol,
|
||||
)
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
val fiatAmount = fiatRate?.let { status.amount.toFiatValue(it) }
|
||||
val fiatAmountFormatted = fiatAmount
|
||||
?.takeIf { !status.isErrorStatus }
|
||||
?.toFormattedFiatValue(appCurrency.symbol)
|
||||
val fiatRateFormatted = fiatRate?.toFiatRateString(appCurrency.symbol)
|
||||
|
||||
WalletData(
|
||||
currency = currency,
|
||||
walletAddresses = walletAddresses.getOrNull(0)?.let { selectedAddress ->
|
||||
WalletAddresses(
|
||||
selectedAddress = selectedAddress,
|
||||
list = walletAddresses,
|
||||
)
|
||||
},
|
||||
existentialDepositString = existentialDeposit?.toPlainString(),
|
||||
fiatRate = fiatRate,
|
||||
fiatRateString = fiatRateFormatted,
|
||||
pendingTransactions = status.pendingTransactions,
|
||||
mainButton = WalletMainButton.SendButton(
|
||||
enabled = !status.amount.isZero() && status.pendingTransactions.isEmpty(),
|
||||
),
|
||||
walletRent = walletRent?.let {
|
||||
WalletRent(
|
||||
minRentValue = "${it.rent.stripZeroPlainString()} ${currency.blockchain.currency}",
|
||||
rentExemptValue = "${it.exemptionAmount.stripZeroPlainString()} ${currency.blockchain.currency}",
|
||||
)
|
||||
},
|
||||
currencyData = BalanceWidgetData(
|
||||
status = when (status) {
|
||||
is WalletDataModel.Loading -> BalanceStatus.Loading
|
||||
is WalletDataModel.NoAccount -> BalanceStatus.NoAccount
|
||||
is WalletDataModel.Refreshing -> BalanceStatus.Refreshing
|
||||
is WalletDataModel.SameCurrencyTransactionInProgress -> BalanceStatus.SameCurrencyTransactionInProgress
|
||||
is WalletDataModel.TransactionInProgress -> BalanceStatus.TransactionInProgress
|
||||
is WalletDataModel.Unreachable,
|
||||
is WalletDataModel.MissedDerivation,
|
||||
-> BalanceStatus.Unreachable
|
||||
is WalletDataModel.VerifiedOnline -> BalanceStatus.VerifiedOnline
|
||||
},
|
||||
currency = currency.currencyName,
|
||||
currencySymbol = currency.currencySymbol,
|
||||
blockchainAmount = status.amount,
|
||||
amount = amount,
|
||||
amountFormatted = amountFormatted,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmountFormatted,
|
||||
token = when {
|
||||
!isMultiWalletAllowed && currency is Currency.Token -> {
|
||||
TokenData(
|
||||
amount = amount,
|
||||
amountFormatted = amountFormatted,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmountFormatted,
|
||||
tokenSymbol = currency.currencySymbol,
|
||||
fiatRate = fiatRate,
|
||||
fiatRateString = fiatRateFormatted,
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
},
|
||||
amountToCreateAccount = (status as? WalletDataModel.NoAccount)
|
||||
?.amountToCreateAccount
|
||||
?.toString(),
|
||||
errorMessage = status.errorMessage,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TotalFiatBalance.mapToReduxModel(): TotalBalance {
|
||||
return TotalBalance(
|
||||
state = when (this) {
|
||||
is TotalFiatBalance.Loading -> ProgressState.Loading
|
||||
is TotalFiatBalance.Refreshing -> ProgressState.Refreshing
|
||||
is TotalFiatBalance.Error -> ProgressState.Error
|
||||
is TotalFiatBalance.Loaded -> ProgressState.Done
|
||||
},
|
||||
fiatAmount = amount,
|
||||
fiatCurrency = store.state.globalState.appCurrency,
|
||||
)
|
||||
}
|
||||
|
||||
fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null): WalletAddresses? {
|
||||
if (wallet == null) return null
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ data class BalanceWidgetData(
|
|||
)
|
||||
|
||||
data class TokenData(
|
||||
val amountFormatted: String,
|
||||
val amountFormatted: String?,
|
||||
val amount: BigDecimal? = null,
|
||||
val tokenSymbol: String,
|
||||
val fiatAmountFormatted: String? = null,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.*
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.ColorRes
|
||||
|
|
@ -22,33 +18,26 @@ import com.tangem.tap.common.TestActions
|
|||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.DetailsScreen
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.appendIfNotNull
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.toQrCode
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.getQRReceiveMessage
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
|
||||
import com.tangem.tap.features.wallet.ui.images.load
|
||||
import com.tangem.tap.features.wallet.ui.test.TestWallet
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManagerSafe
|
||||
import com.tangem.tap.walletCurrenciesManager
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentWalletDetailsBinding
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
||||
|
|
@ -158,15 +147,19 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
binding.srlWalletDetails.setOnRefreshListener {
|
||||
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
|
||||
Analytics.send(Token.Refreshed())
|
||||
store.dispatch(
|
||||
WalletAction.LoadWallet(
|
||||
blockchain = BlockchainNetwork(
|
||||
selectedWallet.currency.blockchain,
|
||||
selectedWallet.currency.derivationPath,
|
||||
emptyList(),
|
||||
),
|
||||
),
|
||||
val blockchainNetwork = BlockchainNetwork(
|
||||
blockchain = selectedWallet.currency.blockchain,
|
||||
derivationPath = selectedWallet.currency.derivationPath,
|
||||
tokens = emptyList(),
|
||||
)
|
||||
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) scope.launch {
|
||||
walletCurrenciesManager.update(selectedUserWallet, blockchainNetwork)
|
||||
} else {
|
||||
store.dispatch(
|
||||
WalletAction.LoadWallet(blockchainNetwork),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.view.View
|
|||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.transition.TransitionInflater
|
||||
|
|
@ -41,6 +42,7 @@ import com.tangem.tap.features.wallet.ui.wallet.SaltPaySingleWalletView
|
|||
import com.tangem.tap.features.wallet.ui.wallet.SingleWalletView
|
||||
import com.tangem.tap.features.wallet.ui.wallet.WalletView
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
|
@ -54,6 +56,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
|
||||
private var walletView: WalletView = SingleWalletView()
|
||||
|
||||
private val viewModel by viewModels<WalletViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
|
@ -63,7 +67,13 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
this,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
val popBackTo = if (userWalletsListManager.hasSavedUserWallets) {
|
||||
userWalletsListManager.lock()
|
||||
AppScreen.Welcome
|
||||
} else {
|
||||
AppScreen.Home
|
||||
}
|
||||
store.dispatch(NavigationAction.PopBackTo(popBackTo))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -78,6 +88,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
state.select { it.walletState }
|
||||
}
|
||||
walletView.setFragment(this, binding)
|
||||
viewModel.launch()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
|
@ -91,7 +102,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
|
||||
store.dispatch(WalletAction.ChangeWallet)
|
||||
}
|
||||
setupWarningsRecyclerView()
|
||||
walletView.changeWalletView(this, binding)
|
||||
|
|
@ -162,6 +173,9 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
store.dispatch(WalletAction.LoadData.Refresh)
|
||||
}
|
||||
}
|
||||
|
||||
val navigationIconRes = if (state.hasSavedWallets) R.drawable.ic_wallet_24 else R.drawable.ic_tap_card_24
|
||||
binding.toolbar.setNavigationIcon(navigationIconRes)
|
||||
}
|
||||
|
||||
private fun showWarningsIfPresent(warnings: List<WarningMessage>) {
|
||||
|
|
@ -211,9 +225,9 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
store.state.globalState.scanResponse?.let { scanNoteResponse ->
|
||||
store.dispatch(
|
||||
DetailsAction.PrepareScreen(
|
||||
scanNoteResponse,
|
||||
store.state.walletState.walletManagers.map { it.wallet },
|
||||
CardTou(),
|
||||
scanResponse = scanNoteResponse,
|
||||
wallets = store.state.walletState.walletManagers.map { it.wallet },
|
||||
cardTou = CardTou(),
|
||||
),
|
||||
)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class WalletViewModel : ViewModel() {
|
||||
fun launch() {
|
||||
bootstrapSelectedWalletStoresChanges()
|
||||
bootstrapShowSaveWalletIfNeeded()
|
||||
}
|
||||
|
||||
private fun bootstrapSelectedWalletStoresChanges() {
|
||||
userWalletsListManager.selectedUserWallet
|
||||
.flatMapLatest { selectedWallet ->
|
||||
walletStoresManager.get(selectedWallet.walletId)
|
||||
}
|
||||
.onEach { walletStores ->
|
||||
store.dispatch(WalletAction.WalletStoresChanged(walletStores))
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun bootstrapShowSaveWalletIfNeeded() {
|
||||
viewModelScope.launch {
|
||||
delay(timeMillis = 1_800)
|
||||
store.dispatchOnMain(WalletAction.ShowSaveWalletIfNeeded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,6 @@ import androidx.core.view.isVisible
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.Portfolio
|
||||
|
|
@ -28,15 +26,6 @@ class WalletAdapter : ListAdapter<WalletData, WalletAdapter.WalletsViewHolder>(D
|
|||
return currentList[position].currencyData.currencySymbol?.hashCode()?.toLong() ?: 0
|
||||
}
|
||||
|
||||
fun submitList(
|
||||
list: List<WalletData>,
|
||||
primaryBlockchain: Blockchain?,
|
||||
primaryToken: Token? = null,
|
||||
) {
|
||||
// We used this method to sort the list of currencies. Sorting is disabled for now.
|
||||
super.submitList(list)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletsViewHolder {
|
||||
val layout = ItemCurrencyWalletBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false,
|
||||
|
|
@ -73,15 +62,7 @@ class WalletAdapter : ListAdapter<WalletData, WalletAdapter.WalletsViewHolder>(D
|
|||
BalanceStatus.Unreachable -> {
|
||||
root.getString(R.string.wallet_balance_blockchain_unreachable)
|
||||
}
|
||||
BalanceStatus.NoAccount,
|
||||
BalanceStatus.VerifiedOnline,
|
||||
BalanceStatus.SameCurrencyTransactionInProgress,
|
||||
BalanceStatus.EmptyCard,
|
||||
BalanceStatus.UnknownBlockchain,
|
||||
BalanceStatus.Loading,
|
||||
BalanceStatus.Refreshing,
|
||||
null,
|
||||
-> null
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (status == null || status == BalanceStatus.Loading) {
|
||||
|
|
@ -101,8 +82,8 @@ class WalletAdapter : ListAdapter<WalletData, WalletAdapter.WalletsViewHolder>(D
|
|||
)
|
||||
|
||||
lContent.tvCurrency.text = wallet.currencyData.currency
|
||||
lContent.tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted
|
||||
lContent.tvAmount.text = wallet.currencyData.amountFormatted
|
||||
lContent.tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted ?: "—"
|
||||
lContent.tvAmount.text = wallet.currencyData.amountFormatted ?: "—"
|
||||
|
||||
lContent.tvStatus.isVisible = statusMessage != null
|
||||
lContent.tvStatus.text = statusMessage
|
||||
|
|
|
|||
|
|
@ -70,5 +70,7 @@ fun CurrencyIconView.load(
|
|||
blockchain = currency.blockchain,
|
||||
getLocalImage = true,
|
||||
).load()
|
||||
} else {
|
||||
isBlockchainBadgeVisible = false
|
||||
}
|
||||
}
|
||||
|
|
@ -9,11 +9,7 @@ import com.tangem.domain.common.TapWorkarounds.isTestCard
|
|||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
import com.tangem.tap.common.analytics.events.Portfolio
|
||||
import com.tangem.tap.common.extensions.animateVisibility
|
||||
import com.tangem.tap.common.extensions.formatAmountAsSpannedString
|
||||
import com.tangem.tap.common.extensions.getQuantityString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
|
|
@ -73,16 +69,17 @@ class MultiWalletView : WalletView() {
|
|||
walletsAdapter.setHasStableIds(true)
|
||||
binding?.rvMultiwallet?.layoutManager = LinearLayoutManager(fragment.requireContext())
|
||||
binding?.rvMultiwallet?.adapter = walletsAdapter
|
||||
binding?.rvMultiwallet?.itemAnimator = null
|
||||
}
|
||||
|
||||
override fun onNewState(state: WalletState) {
|
||||
val fragment = fragment ?: return
|
||||
val binding = binding ?: return
|
||||
|
||||
handleTotalBalance(binding, state.totalBalance)
|
||||
handleTotalBalance(binding, state.totalBalance, state.state)
|
||||
handleBackupWarning(binding, state.showBackupWarning)
|
||||
handleRescanWarning(binding, state.missingDerivations.isNotEmpty())
|
||||
walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken)
|
||||
walletsAdapter.submitList(state.walletsData)
|
||||
|
||||
binding.pbLoadingUserTokens.show(state.loadingUserTokens)
|
||||
|
||||
|
|
@ -135,11 +132,14 @@ class MultiWalletView : WalletView() {
|
|||
private fun handleTotalBalance(
|
||||
binding: FragmentWalletBinding,
|
||||
totalBalance: TotalBalance?,
|
||||
progressState: ProgressState,
|
||||
) = with(binding.lCardTotalBalance) {
|
||||
root.isVisible = totalBalance != null
|
||||
if (totalBalance != null) {
|
||||
// Skip changes when on refreshing state
|
||||
if (totalBalance.state == ProgressState.Refreshing) return@with
|
||||
if (totalBalance.state == ProgressState.Refreshing ||
|
||||
progressState == ProgressState.Refreshing
|
||||
) return@with
|
||||
|
||||
if (totalBalance.state == ProgressState.Loading) {
|
||||
veilBalance.veil()
|
||||
|
|
|
|||
|
|
@ -3,12 +3,7 @@ package com.tangem.tap.features.wallet.ui.wallet
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
|
||||
import com.tangem.tap.common.extensions.getQuantityString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
|
|
@ -38,9 +33,12 @@ class SingleWalletView : WalletView() {
|
|||
btnAddToken.hide()
|
||||
rvPendingTransaction.hide()
|
||||
pbLoadingUserTokens.hide()
|
||||
lCardTotalBalance.root.hide()
|
||||
lSingleWalletBalance.root.hide()
|
||||
lWalletRescanWarning.root.hide()
|
||||
lCardBalance.root.show()
|
||||
lAddress.root.show()
|
||||
lSingleWalletBalance.root.hide()
|
||||
rowButtons.show()
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
|
|
|
|||
|
|
@ -6,13 +6,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
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.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue