Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-25 17:27:19 +03:00
commit 3e5ab44b4c
35 changed files with 225 additions and 59 deletions

@ -1 +1 @@
Subproject commit f5a90466d2245ca5cfde59b49dc7fa82a6b7cbc2
Subproject commit 2a612cd92b1c78b99c917d0fe66342831f801e5e

View file

@ -3,6 +3,7 @@ package com.tangem.tap.common.redux.legacy
import com.tangem.domain.redux.LegacyAction
import com.tangem.tap.common.feedback.RateCanBeBetterEmail
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
import org.rekotlin.Middleware
@ -14,6 +15,11 @@ internal object LegacyMiddleware {
is LegacyAction.SendEmailRateCanBeBetter -> {
store.state.globalState.feedbackManager?.sendEmail(RateCanBeBetterEmail())
}
is LegacyAction.StartOnboardingProcess -> {
store.dispatch(
GlobalAction.Onboarding.Start(action.scanResponse, canSkipBackup = action.canSkipBackup),
)
}
}
next(action)
}

View file

@ -602,11 +602,14 @@ internal class AddCustomTokenViewModel @Inject constructor(
val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain
val networkId = Blockchain.fromNetworkId(networkSelectorValue.toNetworkId())?.id
// todo after move foundToken to CryptoCurrency model, use only id
val savedTokenId = if (token.isCustom) null else token.id.rawCurrencyId
val sameId = if (!token.isCustom) {
// todo after move foundToken to CryptoCurrency model, use only id
foundToken?.id == token.id.rawCurrencyId
} else {
true
}
val sameId = foundToken?.id == savedTokenId
val sameAddress = contractAddress == token.contractAddress
val sameAddress = contractAddress.equals(token.contractAddress, ignoreCase = true)
val sameBlockchain = networkId == token.network.id.value
val isSameDerivationPath = getDerivationPath()?.rawPath == token.network.derivationPath.value

View file

@ -143,9 +143,9 @@ internal object TangemSocialAccounts {
SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_chat"),
SocialNetworkLink(SocialNetwork.Discord, "https://discord.gg/tangem"),
SocialNetworkLink(SocialNetwork.Reddit, "https://www.reddit.com/r/Tangem/"),
SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"),
SocialNetworkLink(SocialNetwork.Instagram, "https://www.instagram.com/tangemwallet"),
SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"),
SocialNetworkLink(SocialNetwork.Facebook, "https://facebook.com/TangemCards/"),
SocialNetworkLink(SocialNetwork.Facebook, "https://www.facebook.com/tangemwallet"),
SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"),
SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/@tangem3890"),
)
@ -154,9 +154,9 @@ internal object TangemSocialAccounts {
SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_chat_ru"),
SocialNetworkLink(SocialNetwork.Discord, "https://discord.gg/tangem"),
SocialNetworkLink(SocialNetwork.Reddit, "https://www.reddit.com/r/Tangem/"),
SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"),
SocialNetworkLink(SocialNetwork.Instagram, "https://www.instagram.com/tangemwallet"),
SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"),
SocialNetworkLink(SocialNetwork.Facebook, "https://facebook.com/TangemCards/"),
SocialNetworkLink(SocialNetwork.Facebook, "https://www.facebook.com/tangemwallet"),
SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"),
SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/@tangem3890"),
)

View file

@ -73,9 +73,9 @@ private fun OrderCardButton(onClick: () -> Unit, modifier: Modifier = Modifier)
}
private val LightBgScanCardButtonColors: ButtonColors = TangemButtonColors(
backgroundColor = TangemColorPalette.Light2,
backgroundColor = TangemColorPalette.Light4,
contentColor = TangemColorPalette.Dark6,
disabledBackgroundColor = TangemColorPalette.Light2,
disabledBackgroundColor = TangemColorPalette.Dark5,
disabledContentColor = TangemColorPalette.Dark6,
)

View file

@ -50,7 +50,8 @@ internal class MainViewModel @Inject constructor(
it.isBalanceHidingNotificationEnabled && it.isBalanceHidden
}
.onEach {
if (state.value.modalNotification?.isShow != true) {
if (state.value.modalNotification?.isShow != true && !it.isUpdateFromToast) {
listenToFlipsUseCase.changeUpdateEnabled(false)
stateHolder.updateWithHiddenBalancesNotification()
reduxNavController.navigate(NavigationAction.NavigateTo(AppScreen.ModalNotification))
}
@ -79,7 +80,9 @@ internal class MainViewModel @Inject constructor(
return@onEach
}
displayBalancesHiddenStatusToast(settings)
if (!settings.isUpdateFromToast) {
displayBalancesHiddenStatusToast(settings)
}
previousSettings = settings
}
@ -96,7 +99,10 @@ internal class MainViewModel @Inject constructor(
override fun onHiddenBalanceToastAction() {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidden = false)
copy(
isBalanceHidden = false,
isUpdateFromToast = true,
)
}
}
}
@ -104,7 +110,10 @@ internal class MainViewModel @Inject constructor(
override fun onShownBalanceToastAction() {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidden = true)
copy(
isBalanceHidden = true,
isUpdateFromToast = true,
)
}
}
}
@ -112,6 +121,7 @@ internal class MainViewModel @Inject constructor(
override fun onHiddenBalanceNotificationAction(isPermanent: Boolean) {
onDismissBottomSheet()
stateHolder.updateWithHiddenBalancesToast(true)
if (isPermanent) {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
@ -122,6 +132,8 @@ internal class MainViewModel @Inject constructor(
}
override fun onDismissBottomSheet() {
listenToFlipsUseCase.changeUpdateEnabled(true)
stateHolder.updateWithoutModalNotification()
stateHolder.updateWithHiddenBalancesToast(true)
}
}

View file

@ -2,12 +2,14 @@ package com.tangem.tap.features.main.ui
import android.content.DialogInterface
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeBottomSheetFragment
import com.tangem.core.ui.theme.AppThemeModeHolder
import com.tangem.tap.features.main.MainViewModel
@ -37,7 +39,11 @@ internal class ModalNotificationBottomSheetFragment : ComposeBottomSheetFragment
when (val content = notification.content) {
is ModalNotification -> ModalNotificationContent(
modifier = modifier,
modifier = modifier
.background(
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.bottomSheet,
),
notification = content,
)
else -> Unit

View file

@ -88,9 +88,9 @@ private fun ModalNotificationContentPreview_Light(
) {
TangemTheme(isDark = false) {
ModalNotificationContent(
param,
notification = param,
modifier = Modifier.background(
color = TangemTheme.colors.background.plain,
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.bottomSheet,
),
)
@ -104,9 +104,9 @@ private fun ModalNotificationContentPreview_Dark(
) {
TangemTheme(isDark = true) {
ModalNotificationContent(
param,
notification = param,
modifier = Modifier.background(
color = TangemTheme.colors.background.plain,
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.bottomSheet,
),
)

View file

@ -2,7 +2,6 @@ package com.tangem.tap.features.send.ui
import androidx.lifecycle.*
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
@ -16,11 +15,10 @@ import com.tangem.tap.proxy.AppStateHolder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@ -30,7 +28,6 @@ internal class SendViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val appStateHolder: AppStateHolder,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase,
@ -44,17 +41,10 @@ internal class SendViewModel @Inject constructor(
getBalanceHidingSettingsUseCase()
.flowWithLifecycle(owner.lifecycle)
.onEach {
withContext(dispatchers.main) {
appStateHolder.mainStore?.dispatch(AmountAction.HideBalance(it.isBalanceHidden))
}
appStateHolder.mainStore?.dispatch(AmountAction.HideBalance(it.isBalanceHidden))
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
viewModelScope.launch {
listenToFlipsUseCase()
.flowWithLifecycle(owner.lifecycle)
.collect()
}
}
fun updateCurrencyDelayed() {

View file

@ -10,7 +10,7 @@
<color name="button_disabled">#303030</color>
<!--<color name="button_positive">#1ACE80</color>-->
<!--<color name="button_positive_disabled">#06311F</color>-->
<color name="button_primary">#F5F5F5</color>
<color name="button_primary">#FFC9C9C9</color>
<color name="button_secondary">#303030</color>
<color name="control_checked">#1ACE80</color>

View file

@ -90,7 +90,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
infuraProjectId = configValues.infuraProjectId,
tronGridApiKey = configValues.tronGridApiKey,
nowNodeCredentials = NowNodeCredentials(configValues.nowNodesApiKey),
getBlockCredentials = GetBlockCredentials(configValues.getBlockApiKey),
getBlockCredentials = createGetBlockCredentials(configValues),
kaspaSecondaryApiUrl = configValues.kaspaSecondaryApiUrl,
tonCenterCredentials = TonCenterCredentials(
mainnetApiKey = configValues.tonCenterKeys.mainnet,
@ -109,4 +109,45 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
tangemExpressApiKey = configValues.tangemExpressApiKey,
)
}
private fun createGetBlockCredentials(configValues: ConfigValueModel): GetBlockCredentials? {
return configValues.getBlockAccessTokens?.let { accessTokens ->
GetBlockCredentials(
xrp = GetBlockAccessToken(jsonRpc = accessTokens.xrp?.jsonRPC),
cardano = GetBlockAccessToken(rosetta = accessTokens.cardano?.rosetta),
avalanche = GetBlockAccessToken(jsonRpc = accessTokens.avalanche?.jsonRPC),
eth = GetBlockAccessToken(jsonRpc = accessTokens.eth?.jsonRPC),
etc = GetBlockAccessToken(jsonRpc = accessTokens.etc?.jsonRPC),
fantom = GetBlockAccessToken(jsonRpc = accessTokens.fantom?.jsonRPC),
rsk = GetBlockAccessToken(jsonRpc = accessTokens.rsk?.jsonRPC),
bsc = GetBlockAccessToken(jsonRpc = accessTokens.bsc?.jsonRPC),
polygon = GetBlockAccessToken(jsonRpc = accessTokens.polygon?.jsonRPC),
gnosis = GetBlockAccessToken(jsonRpc = accessTokens.gnosis?.jsonRPC),
cronos = GetBlockAccessToken(jsonRpc = accessTokens.cronos?.jsonRPC),
solana = GetBlockAccessToken(jsonRpc = accessTokens.solana?.jsonRPC),
stellar = GetBlockAccessToken(rest = accessTokens.stellar?.rest),
ton = GetBlockAccessToken(jsonRpc = accessTokens.ton?.jsonRPC),
tron = GetBlockAccessToken(rest = accessTokens.tron?.rest),
cosmos = GetBlockAccessToken(rest = accessTokens.cosmos?.rest),
near = GetBlockAccessToken(jsonRpc = accessTokens.near?.jsonRPC),
luna = GetBlockAccessToken(rest = accessTokens.luna?.rest),
dogecoin = GetBlockAccessToken(
jsonRpc = accessTokens.dogecoin?.jsonRPC,
blockBookRest = accessTokens.dogecoin?.blockBookRest,
),
litecoin = GetBlockAccessToken(
jsonRpc = accessTokens.litecoin?.jsonRPC,
blockBookRest = accessTokens.litecoin?.blockBookRest,
),
dash = GetBlockAccessToken(
jsonRpc = accessTokens.dash?.jsonRPC,
blockBookRest = accessTokens.dash?.blockBookRest,
),
bitcoin = GetBlockAccessToken(
jsonRpc = accessTokens.bitcoin?.jsonRPC,
blockBookRest = accessTokens.bitcoin?.blockBookRest,
),
)
}
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.datasource.config.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
[REDACTED_AUTHOR]
@ -25,7 +26,7 @@ class ConfigValueModel(
val bscQuiknodeSubdomain: String,
val bscQuiknodeApiKey: String,
val nowNodesApiKey: String,
val getBlockApiKey: String,
@Json(name = "getBlockAccessTokens") val getBlockAccessTokens: GetBlockAccessTokens?,
@Json(name = "tonCenterApiKey") val tonCenterKeys: TonCenterKeys,
val blockcypherTokens: Set<String>?,
val infuraProjectId: String?,
@ -43,6 +44,40 @@ class ConfigValueModel(
val tangemExpressApiKey: String,
)
@JsonClass(generateAdapter = true)
data class GetBlockAccessTokens(
@Json(name = "xrp") val xrp: GetBlockToken?,
@Json(name = "cardano") val cardano: GetBlockToken?,
@Json(name = "avalanche") val avalanche: GetBlockToken?,
@Json(name = "ethereum") val eth: GetBlockToken?,
@Json(name = "ethereumClassic") val etc: GetBlockToken?,
@Json(name = "fantom") val fantom: GetBlockToken?,
@Json(name = "rsk") val rsk: GetBlockToken?,
@Json(name = "bsc") val bsc: GetBlockToken?,
@Json(name = "polygon") val polygon: GetBlockToken?,
@Json(name = "xdai") val gnosis: GetBlockToken?,
@Json(name = "cronos") val cronos: GetBlockToken?,
@Json(name = "solana") val solana: GetBlockToken?,
@Json(name = "stellar") val stellar: GetBlockToken?,
@Json(name = "ton") val ton: GetBlockToken?,
@Json(name = "tron") val tron: GetBlockToken?,
@Json(name = "cosmos-hub") val cosmos: GetBlockToken?,
@Json(name = "near") val near: GetBlockToken?,
@Json(name = "terra-2") val luna: GetBlockToken?,
@Json(name = "dogecoin") val dogecoin: GetBlockToken?,
@Json(name = "litecoin") val litecoin: GetBlockToken?,
@Json(name = "dash") val dash: GetBlockToken?,
@Json(name = "bitcoin") val bitcoin: GetBlockToken?,
)
@JsonClass(generateAdapter = true)
data class GetBlockToken(
@Json(name = "jsonRpc") val jsonRPC: String?,
@Json(name = "blockBookRest") val blockBookRest: String?,
@Json(name = "rest") val rest: String?,
@Json(name = "rosetta") val rosetta: String?,
)
data class AppsFlyer(
val appsFlyerDevKey: String,
val appsFlyerAppID: String,

View file

@ -86,6 +86,7 @@
<string name="common_error">Ошибка</string>
<string name="common_exchange">Обменять</string>
<string name="common_explore_transaction_history">Посмотреть историю транзакций</string>
<string name="common_explore">Обозреватель</string>
<string name="common_explorer">Обозреватель</string>
<string name="common_fee_selector_footer">Сетевые комиссии за транзакции используются для поддержки безопасности сети, поощрения валидаторов, выделения ресурсов и определения приоритета транзакции.</string>
<string name="common_fee_selector_option_custom">Свое</string>

View file

@ -2,9 +2,7 @@ package com.tangem.core.ui.components.bottomsheets.tokenreceive
import android.widget.Toast
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
@ -43,6 +41,7 @@ private fun TokenReceiveBottomSheetContent(content: TokenReceiveBottomSheetConfi
var selectedAddress by remember { mutableStateOf(content.addresses.first()) }
Column(
modifier = Modifier
.verticalScroll(state = rememberScrollState())
.padding(
start = TangemTheme.dimens.spacing24,
top = TangemTheme.dimens.spacing24,

View file

@ -81,7 +81,7 @@ private fun LazyListScope.contentItems(
count = txHistoryItems.itemCount,
key = txHistoryItems.itemKey { item ->
when (item) {
is TxHistoryState.TxHistoryItemState.GroupTitle -> item.title
is TxHistoryState.TxHistoryItemState.GroupTitle -> item.itemKey
is TxHistoryState.TxHistoryItemState.Title -> item.onExploreClick.hashCode()
is TxHistoryState.TxHistoryItemState.Transaction -> item.state.txHash
}

View file

@ -10,6 +10,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
import com.tangem.core.ui.res.TangemTheme
import java.util.UUID
/**
* Transactions block group title
@ -38,7 +39,9 @@ internal fun TxHistoryGroupTitle(config: TxHistoryItemState.GroupTitle, modifier
@Composable
private fun Preview_TransactionsBlockGroupTitle_Light() {
TangemTheme(isDark = false) {
TxHistoryGroupTitle(config = TxHistoryItemState.GroupTitle(title = "Today"))
TxHistoryGroupTitle(
config = TxHistoryItemState.GroupTitle(title = "Today", itemKey = UUID.randomUUID().toString()),
)
}
}
@ -46,6 +49,8 @@ private fun Preview_TransactionsBlockGroupTitle_Light() {
@Composable
private fun Preview_TransactionsBlockGroupTitle_Dark() {
TangemTheme(isDark = true) {
TxHistoryGroupTitle(config = TxHistoryItemState.GroupTitle(title = "Today"))
TxHistoryGroupTitle(
config = TxHistoryItemState.GroupTitle(title = "Today", itemKey = UUID.randomUUID().toString()),
)
}
}

View file

@ -49,8 +49,12 @@ sealed interface TxHistoryState {
* Group title item
*
* @property title title
* @property itemKey key to use in compose
*/
data class GroupTitle(val title: String) : TxHistoryItemState
data class GroupTitle(
val title: String,
val itemKey: String,
) : TxHistoryItemState
/**
* Transaction item

View file

@ -45,6 +45,7 @@ fun getActiveIconRes(blockchainId: String): Int {
"aleph-zero", "aleph-zero/test" -> R.drawable.img_azero_22
"octaspace", "octaspace/test" -> R.drawable.img_octaspace_22
"chia", "chia/test" -> R.drawable.img_chia_22
"NEAR", "NEAR/test" -> R.drawable.img_near_22
"decimal", "decimal/testnet" -> R.drawable.img_decimal_22
else -> R.drawable.ic_alert_24
}
@ -135,6 +136,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
"aleph-zero", "aleph-zero/test" -> R.drawable.ic_azero_22
"octaspace", "octaspace/test" -> R.drawable.ic_octaspace_22
"chia", "chia/test" -> R.drawable.ic_chia_22
"NEAR", "NEAR/test" -> R.drawable.ic_near_22
"decimal", "decimal/test" -> R.drawable.ic_decimal_22
else -> R.drawable.ic_alert_24
}

View file

@ -156,7 +156,7 @@ private fun darkThemeColors(): TangemColors {
attention = TangemColorPalette.Mustard,
),
button = TangemColors.Button(
primary = TangemColorPalette.Light1,
primary = TangemColorPalette.Light4,
secondary = TangemColorPalette.Dark5,
disabled = TangemColorPalette.Dark5,
positiveDisabled = TangemColorPalette.DarkGreen,

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="22dp"
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:pathData="M15.721,5C15.277,5 14.864,5.23 14.632,5.609L12.123,9.333C12.042,9.456 12.075,9.622 12.197,9.703C12.297,9.77 12.429,9.761 12.519,9.683L14.988,7.542C15.029,7.505 15.092,7.509 15.129,7.55C15.146,7.569 15.155,7.593 15.155,7.618V14.323C15.155,14.378 15.11,14.423 15.055,14.423C15.025,14.423 14.997,14.41 14.978,14.387L7.515,5.453C7.272,5.166 6.915,5 6.539,5H6.278C5.572,5 5,5.572 5,6.278V15.722C5,16.428 5.572,17 6.278,17C6.723,17 7.135,16.77 7.368,16.391L9.876,12.667C9.958,12.544 9.925,12.378 9.802,12.297C9.703,12.23 9.571,12.239 9.481,12.317L7.012,14.458C6.971,14.495 6.907,14.491 6.87,14.45C6.854,14.432 6.845,14.407 6.845,14.382V7.676C6.845,7.62 6.89,7.576 6.945,7.576C6.975,7.576 7.003,7.589 7.022,7.612L14.484,16.547C14.727,16.834 15.084,17 15.46,17H15.721C16.427,17 16.999,16.428 17,15.722V6.278C17,5.572 16.428,5 15.722,5H15.721Z"
android:fillColor="#000000" />
</vector>

View file

@ -13,6 +13,8 @@ internal class DefaultBalanceHidingRepository(
private val appPreferencesStore: AppPreferencesStore,
) : BalanceHidingRepository {
override var isUpdateEnabled: Boolean = true
override fun getBalanceHidingSettingsFlow(): Flow<BalanceHidingSettings> {
return appPreferencesStore.getObject(
key = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY,

View file

@ -16,8 +16,7 @@ class DefaultMarketCryptoCurrencyRepository(
return assetsStore.getSyncOrNull(userWalletId)?.find {
it.network == cryptoCurrency.network.backendId &&
it.token == cryptoCurrency.id.rawCurrencyId &&
it.contractAddress == contractAddress &&
it.isActive
it.contractAddress == contractAddress
}?.exchangeAvailable ?: false
}
}

View file

@ -4,4 +4,5 @@ data class BalanceHidingSettings(
val isHidingEnabledInSettings: Boolean,
val isBalanceHidden: Boolean,
val isBalanceHidingNotificationEnabled: Boolean,
val isUpdateFromToast: Boolean = false,
)

View file

@ -24,12 +24,13 @@ class ListenToFlipsUseCase(
},
)
if (balanceHidingSettings.isHidingEnabledInSettings) {
if (balanceHidingSettings.isHidingEnabledInSettings && balanceHidingRepository.isUpdateEnabled) {
catch(
block = {
balanceHidingRepository.storeBalanceHidingSettings(
balanceHidingSettings.copy(
isBalanceHidden = !balanceHidingSettings.isBalanceHidden,
isUpdateFromToast = false,
),
)
},
@ -40,4 +41,8 @@ class ListenToFlipsUseCase(
}
}
}
fun changeUpdateEnabled(isUpdateEnabled: Boolean) {
balanceHidingRepository.isUpdateEnabled = isUpdateEnabled
}
}

View file

@ -5,6 +5,8 @@ import kotlinx.coroutines.flow.Flow
interface BalanceHidingRepository {
var isUpdateEnabled: Boolean
fun getBalanceHidingSettingsFlow(): Flow<BalanceHidingSettings>
suspend fun storeBalanceHidingSettings(isBalanceHidden: BalanceHidingSettings)

View file

@ -224,4 +224,6 @@ private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5
private val excludedBlockchains = listOf(
Blockchain.Unknown,
Blockchain.Ducatus,
Blockchain.Decimal,
Blockchain.DecimalTestnet,
)

View file

@ -1,8 +1,16 @@
package com.tangem.domain.redux
import com.tangem.domain.models.scan.ScanResponse
import org.rekotlin.Action
sealed interface LegacyAction : Action {
object SendEmailRateCanBeBetter : LegacyAction
/**
* Initiate an onboarding process.
* For resuming unfinished backup of standard Wallet see
* BackupAction.CheckForUnfinishedBackup, GlobalAction.Onboarding.StartForUnfinishedBackup
*/
data class StartOnboardingProcess(val scanResponse: ScanResponse, val canSkipBackup: Boolean = true) : LegacyAction
}

View file

@ -201,6 +201,7 @@ class DefaultWalletManagersFacade(
val itemsResult = walletManager.getTransactionsHistory(
request = TransactionHistoryRequest(
address = walletManager.wallet.address,
decimals = currency.decimals,
page = TransactionHistoryRequest.Page(number = page, size = pageSize),
filterType = when (currency) {
is CryptoCurrency.Coin -> TransactionHistoryRequest.FilterType.Coin

View file

@ -94,6 +94,8 @@ sealed class CryptoCurrency : Parcelable {
/** Represents a raw cryptocurrency ID. If it is a custom token, the value will be `null`. */
val rawCurrencyId: String? get() = (suffix as? Suffix.RawID)?.rawId
val contractAddress: String? get() = (suffix as? Suffix.RawID)?.contractAddress
/** Represents a raw cryptocurrency's network ID. */
val rawNetworkId: String
get() = when (body) {

View file

@ -14,6 +14,7 @@ import com.tangem.utils.converter.Converter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import java.util.UUID
internal class TokenDetailsTxHistoryItemFlowConverter(
private val currentStateProvider: Provider<TokenDetailsState>,
@ -71,7 +72,10 @@ internal class TokenDetailsTxHistoryItemFlowConverter(
// If [afterDate] is the first transaction in the flow, add the group title
val afterDate = after.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
if (before is TxHistoryItemState.Title) {
return@insertSeparators TxHistoryItemState.GroupTitle(afterDate)
return@insertSeparators TxHistoryItemState.GroupTitle(
title = afterDate,
itemKey = UUID.randomUUID().toString(),
)
}
/*
@ -80,7 +84,10 @@ internal class TokenDetailsTxHistoryItemFlowConverter(
*/
val beforeDate = before.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
return@insertSeparators if (beforeDate != afterDate) {
TxHistoryItemState.GroupTitle(afterDate)
TxHistoryItemState.GroupTitle(
title = afterDate,
itemKey = UUID.randomUUID().toString(),
)
} else {
null
}

View file

@ -418,7 +418,10 @@ internal object WalletPreviewData {
contentItems = MutableStateFlow(
PagingData.from(
listOf(
TxHistoryState.TxHistoryItemState.GroupTitle("Today"),
TxHistoryState.TxHistoryItemState.GroupTitle(
title = "Today",
itemKey = UUID.randomUUID().toString(),
),
TxHistoryState.TxHistoryItemState.Transaction(
TransactionState.Content(
txHash = UUID.randomUUID().toString(),
@ -432,7 +435,10 @@ internal object WalletPreviewData {
onClick = {},
),
),
TxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"),
TxHistoryState.TxHistoryItemState.GroupTitle(
title = "Yesterday",
itemKey = UUID.randomUUID().toString(),
),
TxHistoryState.TxHistoryItemState.Transaction(
TransactionState.Content(
txHash = UUID.randomUUID().toString(),

View file

@ -45,13 +45,18 @@ internal fun TokenItem(
.tokenClickable(state = state)
.background(color = TangemTheme.colors.background.primary),
) {
TokenIcon(state = state.iconState, modifier = Modifier.layoutId(layoutId = LayoutId.ICON))
TokenIcon(
state = state.iconState,
modifier = Modifier
.layoutId(layoutId = LayoutId.ICON)
.padding(end = TangemTheme.dimens.spacing8),
)
TokenTitle(
state = state.titleState,
modifier = Modifier
.layoutId(layoutId = LayoutId.TITLE)
.padding(horizontal = TangemTheme.dimens.spacing8)
.padding(end = TangemTheme.dimens.spacing8)
.padding(bottom = betweenRowsMargin),
)
@ -67,7 +72,7 @@ internal fun TokenItem(
state = state.cryptoPriceState,
modifier = Modifier
.layoutId(layoutId = LayoutId.CRYPTO_PRICE)
.padding(horizontal = TangemTheme.dimens.spacing8),
.padding(end = TangemTheme.dimens.spacing8),
)
TokenCryptoAmount(
@ -233,7 +238,10 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier
)
cryptoAmount?.placeRelative(
x = layoutWidth - cryptoAmount.width - layoutPadding,
x = when (state) {
is TokenItemState.Draggable -> layoutPadding + icon.width
else -> layoutWidth - cryptoAmount.width - layoutPadding
},
y = layoutHeight - cryptoAmount.height - layoutPadding,
)

View file

@ -20,6 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import java.util.UUID
/**
* Convert from [Flow] of [TxHistoryItem] to [TxHistoryState]
@ -83,7 +84,10 @@ internal class WalletTxHistoryItemFlowConverter(
// If [afterDate] is the first transaction in the flow, add the group title
val afterDate = after.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
if (before is TxHistoryItemState.Title) {
return@insertSeparators TxHistoryItemState.GroupTitle(afterDate)
return@insertSeparators TxHistoryItemState.GroupTitle(
title = afterDate,
itemKey = UUID.randomUUID().toString(),
)
}
/*
@ -92,7 +96,7 @@ internal class WalletTxHistoryItemFlowConverter(
*/
val beforeDate = before.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
return@insertSeparators if (beforeDate != afterDate) {
TxHistoryItemState.GroupTitle(afterDate)
TxHistoryItemState.GroupTitle(title = afterDate, itemKey = UUID.randomUUID().toString())
} else {
null
}

View file

@ -431,6 +431,12 @@ internal class WalletViewModel @Inject constructor(
override fun onBackupCardClick() {
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeBackupYourWalletTapped)
reduxStateHolder.dispatch(
LegacyAction.StartOnboardingProcess(
scanResponse = getSelectedWallet().scanResponse,
canSkipBackup = false,
),
)
router.openOnboardingScreen()
}

View file

@ -88,9 +88,9 @@ spr-client = "3.6.2"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "develop-387"
tangemBlockchainSdk = "develop-396"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-310"
tangemCardSdk = "develop-312"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds
# endregion Tangem