Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-21 13:58:56 +03:00
commit 5f68b1cc5a
8 changed files with 71 additions and 73 deletions

View file

@ -1,22 +1,22 @@
package com.tangem.tap.common.redux.legacy
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.redux.LegacyAction
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.details.redux.AppSettingsState
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.*
import org.rekotlin.Middleware
internal object LegacyMiddleware {
@ -42,10 +42,13 @@ internal object LegacyMiddleware {
userWalletsListManager.selectedUserWallet
.distinctUntilChanged()
.onEach { selectedUserWallet ->
val initializedAppSettingsStateContent = initializeAppSettingsState(
shouldSaveUserWallets = walletsRepository.shouldSaveUserWalletsSync(),
)
store.dispatchWithMain(
DetailsAction.PrepareScreen(
scanResponse = selectedUserWallet.scanResponse,
shouldSaveUserWallets = walletsRepository.shouldSaveUserWalletsSync(),
initializedAppSettingsState = initializedAppSettingsStateContent,
),
)
}
@ -58,4 +61,22 @@ internal object LegacyMiddleware {
}
}
}
/**
* LEGACY: We need to initialize [AppSettingsState] async to avoid drawing blocking
* previously it was initialized in runBlocking and blocked details screen
*/
private suspend fun initializeAppSettingsState(shouldSaveUserWallets: Boolean): AppSettingsState {
return AppSettingsState(
isBiometricsAvailable = tangemSdkManager.checkCanUseBiometry(),
saveWallets = shouldSaveUserWallets,
saveAccessCodes = store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes(),
selectedAppCurrency = store.state.globalState.appCurrency,
selectedThemeMode = store.inject(DaggerGraphState::appThemeModeRepository).getAppThemeMode().firstOrNull()
?: AppThemeMode.DEFAULT,
isHidingEnabled = store.inject(DaggerGraphState::balanceHidingRepository)
.getBalanceHidingSettings().isHidingEnabledInSettings,
needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() ?: false,
)
}
}

View file

@ -10,7 +10,7 @@ sealed class DetailsAction : Action {
data class PrepareScreen(
val scanResponse: ScanResponse,
val shouldSaveUserWallets: Boolean,
val initializedAppSettingsState: AppSettingsState,
) : DetailsAction()
sealed class AppSettings : DetailsAction() {

View file

@ -1,13 +1,6 @@
package com.tangem.tap.features.details.redux
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.runBlocking
import org.rekotlin.Action
object DetailsReducer {
@ -36,27 +29,7 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
private fun handlePrepareScreen(action: DetailsAction.PrepareScreen): DetailsState {
return DetailsState(
scanResponse = action.scanResponse,
appSettingsState = AppSettingsState(
isBiometricsAvailable = runBlocking {
tangemSdkManager.checkCanUseBiometry()
},
saveWallets = action.shouldSaveUserWallets,
saveAccessCodes = runBlocking {
store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes()
},
selectedAppCurrency = store.state.globalState.appCurrency,
selectedThemeMode = runBlocking {
store.inject(DaggerGraphState::appThemeModeRepository).getAppThemeMode().firstOrNull()
?: AppThemeMode.DEFAULT
},
isHidingEnabled = runBlocking {
store.inject(DaggerGraphState::balanceHidingRepository)
.getBalanceHidingSettings().isHidingEnabledInSettings
},
needEnrollBiometrics = runBlocking {
runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() ?: false
},
),
appSettingsState = action.initializedAppSettingsState,
)
}

View file

@ -2,10 +2,11 @@ package com.tangem.datasource.local.datastore
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import kotlinx.coroutines.flow.*
import java.util.concurrent.ConcurrentHashMap
internal class RuntimeDataStore<Data : Any> : StringKeyDataStore<Data> {
private val store: MutableSharedFlow<HashMap<String, Data>?> = MutableSharedFlow(replay = 1)
private val store: MutableSharedFlow<ConcurrentHashMap<String, Data>?> = MutableSharedFlow(replay = 1)
init {
store.tryEmit(value = null)
@ -61,7 +62,7 @@ internal class RuntimeDataStore<Data : Any> : StringKeyDataStore<Data> {
override suspend fun remove(keys: Collection<String>) {
updateValue { value ->
HashMap(value.filterKeys { it !in keys })
ConcurrentHashMap(value.filterKeys { it !in keys })
}
}
@ -69,8 +70,10 @@ internal class RuntimeDataStore<Data : Any> : StringKeyDataStore<Data> {
store.emit(value = null)
}
private suspend inline fun updateValue(update: (HashMap<String, Data>) -> HashMap<String, Data>) {
val storedData = store.firstOrNull() ?: hashMapOf()
private suspend inline fun updateValue(
update: (ConcurrentHashMap<String, Data>) -> ConcurrentHashMap<String, Data>,
) {
val storedData = store.firstOrNull() ?: ConcurrentHashMap()
val updatedData = update(storedData)
store.emit(updatedData)

View file

@ -100,7 +100,7 @@ internal class TokenListSortingOperations(
private fun CryptoCurrencyStatus.getTotalBalance(): BigDecimal {
val yieldBalance = value.yieldBalance as? YieldBalance.Data
val totalYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance().orZero()
val totalFiatYieldBalance = if (!BlockchainUtils.isIncludeStakingTotalBalance(currency.network.id.value)) {
val totalFiatYieldBalance = if (BlockchainUtils.isIncludeStakingTotalBalance(currency.network.id.value)) {
totalYieldBalance.multiply(value.fiatRate.orZero())
} else {
BigDecimal.ZERO

View file

@ -23,12 +23,12 @@ import com.tangem.features.details.utils.SocialsBuilder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.version.AppVersionProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import javax.inject.Inject
@ -52,42 +52,45 @@ internal class DetailsModel @Inject constructor(
private val params: DetailsComponent.Params = paramsContainer.require()
private val items: MutableStateFlow<ImmutableList<DetailsItemUM>> = MutableStateFlow(value = persistentListOf())
private val items: MutableStateFlow<ImmutableList<DetailsItemUM>>
val state: MutableStateFlow<DetailsUM> = MutableStateFlow(
value = DetailsUM(
items = items.value,
footer = DetailsFooterUM(
socials = socialsBuilder.buildAll(),
appVersion = getAppVersion(),
),
popBack = router::pop,
),
)
val state: MutableStateFlow<DetailsUM>
init {
// Use to save compatibility with screens that using Redux states
bootstrapScreenState()
val isWalletConnectAvailable = runBlocking {
// danger region, this works immediately, but will be refactored later with WC
checkIsWalletConnectAvailableUseCase(params.userWalletId).getOrElse {
Timber.w("Unable to check WalletConnect availability: $it")
false
}
}
items = MutableStateFlow(
itemsBuilder.buildAll(
isWalletConnectAvailable = isWalletConnectAvailable,
onSupportClick = ::sendFeedback,
onBuyClick = ::onBuyClick,
),
)
state = MutableStateFlow(
value = DetailsUM(
items = items.value,
footer = DetailsFooterUM(
socials = socialsBuilder.buildAll(),
appVersion = getAppVersion(),
),
popBack = router::pop,
),
)
items
.onEach(::updateState)
.launchIn(modelScope)
checkWalletConnectAvailability()
}
private fun checkWalletConnectAvailability() = modelScope.launch {
val isWalletConnectAvailable = checkIsWalletConnectAvailableUseCase(params.userWalletId).getOrElse {
Timber.w("Unable to check WalletConnect availability: $it")
false
}
items.value = itemsBuilder.buildAll(
isWalletConnectAvailable = isWalletConnectAvailable,
onSupportClick = ::sendFeedback,
onBuyClick = ::onBuyClick,
)
}
private fun bootstrapScreenState() {

View file

@ -48,9 +48,7 @@ internal class SetConfirmationStateInitTransformer(
actionType = actionType,
balanceState = balanceState,
confirmationState = StakingStates.ConfirmationState.Data(
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
},
isPrimaryButtonEnabled = false,
innerState = InnerConfirmationStakingState.ASSENT,
feeState = FeeState.Loading,
notifications = persistentListOf(),

View file

@ -28,9 +28,9 @@ androidxWorkManager = "2.9.0"
# endregion AndroidX
# region Compose
compose-runtime = "1.7.8"
compose-foundation = "1.7.8"
compose-material = "1.7.8"
compose-runtime = "1.7.4"
compose-foundation = "1.7.4"
compose-material = "1.7.4"
compose-material3 = "1.3.1"
compose-constraint = "1.0.1"
compose-navigation = "2.7.7"