Updated on 2026-08-14
This commit is contained in:
commit
ac8436460c
136 changed files with 1636 additions and 1060 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit cb7c080185a65b3b25cee30c331b696f784783f9
|
||||
Subproject commit 095b8f4ea0fa02e7ccea93cf0f437346345297ef
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.utils.AnalyticsContextProxy
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.extensions.addContext
|
||||
import com.tangem.tap.common.extensions.eraseContext
|
||||
import com.tangem.tap.common.extensions.removeContext
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultAnalyticsContextProxy : AnalyticsContextProxy {
|
||||
|
||||
override fun setContext(scanResponse: ScanResponse) {
|
||||
Analytics.setContext(scanResponse)
|
||||
}
|
||||
|
||||
override fun eraseContext() {
|
||||
Analytics.eraseContext()
|
||||
}
|
||||
|
||||
override fun addContext(scanResponse: ScanResponse) {
|
||||
Analytics.addContext(scanResponse)
|
||||
}
|
||||
|
||||
override fun removeContext() {
|
||||
Analytics.removeContext()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.tap.common.url
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.content.Intent.FLAG_ACTIVITY_NO_HISTORY
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
|
|
@ -36,9 +34,6 @@ internal class CustomTabsUrlOpener : UrlOpener {
|
|||
)
|
||||
.build()
|
||||
|
||||
// Open CustomTabsActivity as new task without saving into the stack
|
||||
customTabsIntent.intent.setFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_NO_HISTORY)
|
||||
|
||||
customTabsIntent.launchUrl(context, Uri.parse(url))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.di.analytics
|
||||
|
||||
import com.tangem.core.analytics.utils.AnalyticsContextProxy
|
||||
import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase
|
||||
import com.tangem.tap.common.analytics.DefaultAnalyticsContextProxy
|
||||
import com.tangem.tap.common.analytics.DefaultChangeCardAnalyticsContextUseCase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -17,4 +19,8 @@ internal object AnalyticsModule {
|
|||
fun provideChangeCardAnalyticsContextUseCase(): ChangeCardAnalyticsContextUseCase {
|
||||
return DefaultChangeCardAnalyticsContextUseCase()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAnalyticsContextProxy(): AnalyticsContextProxy = DefaultAnalyticsContextProxy()
|
||||
}
|
||||
|
|
@ -184,30 +184,6 @@ internal object SettingsDomainModule {
|
|||
return NeverToInitiallyAskPermissionUseCase(repository = permissionRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIsFirstTimeAskingPermissionUseCase(
|
||||
permissionRepository: PermissionRepository,
|
||||
): IsFirstTimeAskingPermissionUseCase {
|
||||
return IsFirstTimeAskingPermissionUseCase(repository = permissionRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSetFirstTimeAskingPushPermissionUseCase(
|
||||
permissionRepository: PermissionRepository,
|
||||
): SetFirstTimeAskingPermissionUseCase {
|
||||
return SetFirstTimeAskingPermissionUseCase(repository = permissionRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDelayPermissionRequestUseCase(
|
||||
permissionRepository: PermissionRepository,
|
||||
): DelayPermissionRequestUseCase {
|
||||
return DelayPermissionRequestUseCase(repository = permissionRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldAskPermissionUseCase(permissionRepository: PermissionRepository): ShouldAskPermissionUseCase {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.transaction.WalletAddressServiceRepository
|
||||
import com.tangem.domain.transaction.usecase.ParseSharedAddressUseCase
|
||||
import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase
|
||||
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
|
|
@ -95,8 +95,11 @@ internal object WalletsDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesRenameWalletUseCase(userWalletsListManager: UserWalletsListManager): RenameWalletUseCase {
|
||||
return RenameWalletUseCase(userWalletsListManager = userWalletsListManager)
|
||||
fun providesRenameWalletUseCase(
|
||||
userWalletsListManager: UserWalletsListManager,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): RenameWalletUseCase {
|
||||
return RenameWalletUseCase(userWalletsListManager = userWalletsListManager, dispatchers = dispatchers)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal object LegacyScanProcessor {
|
||||
|
||||
|
|
@ -115,24 +116,28 @@ internal object LegacyScanProcessor {
|
|||
} else {
|
||||
scope.launch {
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
disclaimerWillShow()
|
||||
store.dispatchWithMain(
|
||||
DisclaimerAction.Show(
|
||||
from = DisclaimerSource.Home,
|
||||
callback = DisclaimerCallback(
|
||||
onAccept = {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
nextHandler(scanResponse)
|
||||
}
|
||||
},
|
||||
onDismiss = {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
onFailure(TangemSdkError.UserCancelled())
|
||||
}
|
||||
},
|
||||
|
||||
withContext(Dispatchers.Main.immediate) {
|
||||
disclaimerWillShow()
|
||||
|
||||
store.dispatch(
|
||||
DisclaimerAction.Show(
|
||||
from = DisclaimerSource.Home,
|
||||
callback = DisclaimerCallback(
|
||||
onAccept = {
|
||||
scope.launch(Dispatchers.Main.immediate) {
|
||||
nextHandler(scanResponse)
|
||||
}
|
||||
},
|
||||
onDismiss = {
|
||||
scope.launch(Dispatchers.Main.immediate) {
|
||||
onFailure(TangemSdkError.UserCancelled())
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,9 +176,9 @@ class WalletConnectSdkHelper {
|
|||
is Result.Success -> gasLimitResult.data.toBigDecimal().multiply(BigDecimal("1.2"))
|
||||
is Result.Failure -> {
|
||||
(gasLimitResult.error as? Throwable)?.let { Timber.e(it, "getGasLimit failed") }
|
||||
BigDecimal(DEFAULT_MAX_GASLIMIT) // Set high gasLimit if not provided
|
||||
DEFAULT_MAX_GASLIMIT.toBigDecimal() // Set high gasLimit if not provided
|
||||
}
|
||||
else -> BigDecimal(DEFAULT_MAX_GASLIMIT) // Set high gasLimit if not provided
|
||||
else -> DEFAULT_MAX_GASLIMIT.toBigDecimal() // Set high gasLimit if not provided
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
private val _activeSessions: MutableSharedFlow<List<WalletConnectSession>> = MutableSharedFlow()
|
||||
override val activeSessions: Flow<List<WalletConnectSession>> = _activeSessions
|
||||
|
||||
private var currentSessions: List<WalletConnectSession> = emptyList()
|
||||
override var currentSessions: List<WalletConnectSession> = emptyList()
|
||||
private set
|
||||
|
||||
/**
|
||||
* @param projectId Project ID at https://cloud.walletconnect.com/
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ interface LegacyWalletConnectRepository {
|
|||
|
||||
val activeSessions: Flow<List<WalletConnectSession>>
|
||||
|
||||
val currentSessions: List<WalletConnectSession>
|
||||
|
||||
fun init(projectId: String)
|
||||
|
||||
fun setUserNamespaces(userNamespaces: Map<NetworkNamespace, List<Account>>)
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ class WalletConnectInteractor(
|
|||
runCatching {
|
||||
if (accounts.isEmpty()) return
|
||||
isWalletConnectReadyForDeepLinks = true
|
||||
if (deeplinkStack.empty()) return
|
||||
val lastDeeplink = deeplinkStack.pop()
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(lastDeeplink))
|
||||
}.onFailure {
|
||||
|
|
@ -361,6 +362,19 @@ class WalletConnectInteractor(
|
|||
* @param deeplink deeplink to handle
|
||||
*/
|
||||
fun addDeeplink(deeplink: String) {
|
||||
val deeplinkRegex = Regex(WC_PARAM_REGEX)
|
||||
val matched = deeplinkRegex.findAll(deeplink)
|
||||
val sessionTopic = matched.firstOrNull { it.value.contains(WC_TOPIC_QUERY_NAME) }?.groupValues?.lastOrNull()
|
||||
|
||||
val isAlreadyActiveSessionTopic = walletConnectRepository.currentSessions.any { session ->
|
||||
session.topic == sessionTopic
|
||||
}
|
||||
|
||||
if (isAlreadyActiveSessionTopic && sessionTopic != null) {
|
||||
Timber.i("WC already has an active session topic: $deeplink")
|
||||
return
|
||||
}
|
||||
|
||||
if (isWalletConnectReadyForDeepLinks) {
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(deeplink))
|
||||
} else {
|
||||
|
|
@ -406,7 +420,9 @@ class WalletConnectInteractor(
|
|||
return sessionRequestConverter.prepareRequest(sessionRequest, userWalletId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val WC_SCHEME = "wc"
|
||||
private companion object {
|
||||
const val WC_SCHEME = "wc"
|
||||
const val WC_TOPIC_QUERY_NAME = "sessionTopic"
|
||||
const val WC_PARAM_REGEX = "([a-zA-Z\\d-]+)=([a-zA-Z\\d]+)"
|
||||
}
|
||||
}
|
||||
|
|
@ -5,11 +5,8 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
import com.tangem.tap.store
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -25,12 +22,6 @@ internal class CardSettingsFragment : ComposeFragment() {
|
|||
override fun ScreenContent(modifier: Modifier) {
|
||||
val state by viewModel.screenState.collectAsStateWithLifecycle()
|
||||
|
||||
CardSettingsScreen(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
onBackClick = {
|
||||
store.dispatchNavigationAction(AppRouter::pop)
|
||||
},
|
||||
)
|
||||
CardSettingsScreen(modifier = modifier, state = state)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
package com.tangem.tap.features.details.ui.cardsettings
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -13,18 +16,14 @@ import androidx.compose.ui.layout.ContentScale
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun CardSettingsScreen(
|
||||
state: CardSettingsScreenState,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
internal fun CardSettingsScreen(state: CardSettingsScreenState, modifier: Modifier = Modifier) {
|
||||
val needReadCard = state.cardDetails == null
|
||||
|
||||
SettingsScreensScaffold(
|
||||
|
|
@ -37,7 +36,7 @@ internal fun CardSettingsScreen(
|
|||
}
|
||||
},
|
||||
titleRes = R.string.card_settings_title,
|
||||
onBackClick = onBackClick,
|
||||
onBackClick = state.onBackClick,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +179,7 @@ private fun CardSettings(state: CardSettingsScreenState) {
|
|||
// region Preview
|
||||
@Composable
|
||||
private fun CardSettingsScreenStateSample() {
|
||||
CardSettingsScreen(state = CardSettingsScreenState(onScanCardClick = {}, onElementClick = {}), {})
|
||||
CardSettingsScreen(state = CardSettingsScreenState(onBackClick = {}, onScanCardClick = {}, onElementClick = {}))
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ internal data class CardSettingsScreenState(
|
|||
val cardDetails: List<CardInfo>? = null,
|
||||
val onScanCardClick: () -> Unit,
|
||||
val onElementClick: (CardInfo) -> Unit,
|
||||
val onBackClick: () -> Unit,
|
||||
)
|
||||
|
||||
internal sealed class CardInfo(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
|
|
@ -23,12 +24,14 @@ import com.tangem.tap.common.extensions.dispatchNavigationAction
|
|||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.domain.extensions.signedHashesCount
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor
|
||||
import com.tangem.tap.features.details.ui.common.utils.*
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -37,6 +40,7 @@ import javax.inject.Inject
|
|||
internal class CardSettingsViewModel @Inject constructor(
|
||||
private val scanCardProcessor: ScanCardProcessor,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val cardSettingsInteractor: CardSettingsInteractor,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
|
|
@ -44,24 +48,28 @@ internal class CardSettingsViewModel @Inject constructor(
|
|||
?.unbundle(UserWalletId.serializer())
|
||||
?: error("User wallet ID is required for CardSettingsViewModel")
|
||||
|
||||
private val scannedScanResponse = MutableStateFlow<ScanResponse?>(value = null)
|
||||
|
||||
val screenState: MutableStateFlow<CardSettingsScreenState> = MutableStateFlow(getInitialState())
|
||||
|
||||
init {
|
||||
cardSettingsInteractor.scannedScanResponse
|
||||
.filterNotNull()
|
||||
.onEach(::updateCardDetails)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun getInitialState() = CardSettingsScreenState(
|
||||
cardDetails = null,
|
||||
onElementClick = ::handleClickingItem,
|
||||
onScanCardClick = ::scanCard,
|
||||
onBackClick = ::onBackClick,
|
||||
)
|
||||
|
||||
private fun scanCard() = viewModelScope.launch {
|
||||
scanCardProcessor.scan(allowsRequestAccessCodeFromRepository = true)
|
||||
.doOnSuccess { scanResponse ->
|
||||
scannedScanResponse.value = scanResponse
|
||||
|
||||
val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
|
||||
if (userWalletId == scannedUserWalletId || scannedUserWalletId == null) {
|
||||
updateCardDetails(scanResponse)
|
||||
cardSettingsInteractor.initialize(scanResponse)
|
||||
} else {
|
||||
store.dispatchDialogShow(
|
||||
AppDialog.SimpleOkDialogRes(
|
||||
|
|
@ -129,27 +137,8 @@ internal class CardSettingsViewModel @Inject constructor(
|
|||
changeAccessCode()
|
||||
}
|
||||
is CardInfo.ResetToFactorySettings -> {
|
||||
val card = requireNotNull(scannedScanResponse.value) {
|
||||
"Impossible to reset card if ScanResponse is null"
|
||||
}.card
|
||||
|
||||
Analytics.send(Settings.CardSettings.ButtonFactoryReset())
|
||||
store.dispatchNavigationAction {
|
||||
push(
|
||||
route = AppRoute.ResetToFactory(
|
||||
userWalletId = userWalletId,
|
||||
cardId = card.cardId,
|
||||
isActiveBackupStatus = card.backupStatus?.isActive == true,
|
||||
backupCardsCount = when (val status = card.backupStatus) {
|
||||
is CardDTO.BackupStatus.Active -> status.cardCount
|
||||
is CardDTO.BackupStatus.CardLinked,
|
||||
CardDTO.BackupStatus.NoBackup,
|
||||
null,
|
||||
-> 0
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
resetWalletToFactorySettings()
|
||||
}
|
||||
is CardInfo.SecurityMode -> {
|
||||
Analytics.send(Settings.CardSettings.ButtonChangeSecurityMode())
|
||||
|
|
@ -158,16 +147,47 @@ internal class CardSettingsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
is CardInfo.AccessCodeRecovery -> {
|
||||
store.dispatchNavigationAction {
|
||||
push(route = AppRoute.AccessCodeRecovery(userWalletId))
|
||||
}
|
||||
store.dispatchNavigationAction { push(AppRoute.AccessCodeRecovery) }
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetWalletToFactorySettings() {
|
||||
val scanResponse = requireNotNull(cardSettingsInteractor.scannedScanResponse.value) {
|
||||
"Impossible to reset card if ScanResponse is null"
|
||||
}
|
||||
|
||||
if (scanResponse.cardTypesResolver.isTangemTwins()) {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
|
||||
|
||||
cardSettingsInteractor.clear()
|
||||
|
||||
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
|
||||
} else {
|
||||
val card = scanResponse.card
|
||||
|
||||
store.dispatchNavigationAction {
|
||||
push(
|
||||
route = AppRoute.ResetToFactory(
|
||||
userWalletId = userWalletId,
|
||||
cardId = card.cardId,
|
||||
isActiveBackupStatus = card.backupStatus?.isActive == true,
|
||||
backupCardsCount = when (val status = card.backupStatus) {
|
||||
is CardDTO.BackupStatus.Active -> status.cardCount
|
||||
is CardDTO.BackupStatus.CardLinked,
|
||||
CardDTO.BackupStatus.NoBackup,
|
||||
null,
|
||||
-> 0
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeAccessCode() = viewModelScope.launch {
|
||||
val scanResponse = requireNotNull(scannedScanResponse.value) { "Scan response is null" }
|
||||
val scanResponse = requireNotNull(cardSettingsInteractor.scannedScanResponse.value) { "Scan response is null" }
|
||||
|
||||
when (val result = tangemSdkManager.setAccessCode(scanResponse.card.cardId)) {
|
||||
is CompletionResult.Success -> Analytics.send(Settings.CardSettings.UserCodeChanged())
|
||||
|
|
@ -182,4 +202,9 @@ internal class CardSettingsViewModel @Inject constructor(
|
|||
val isNotAllowed = hasPermanentWallet || cardTypesResolver.isStart2Coin()
|
||||
return !isNotAllowed
|
||||
}
|
||||
|
||||
private fun onBackClick() {
|
||||
cardSettingsInteractor.clear()
|
||||
store.dispatchNavigationAction(AppRouter::pop)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,16 @@
|
|||
package com.tangem.tap.features.details.ui.cardsettings.coderecovery
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor
|
||||
import com.tangem.tap.features.details.ui.common.utils.isAccessCodeRecoveryEnabled
|
||||
import com.tangem.tap.store
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -28,25 +21,21 @@ import javax.inject.Inject
|
|||
|
||||
@HiltViewModel
|
||||
internal class AccessCodeRecoveryViewModel @Inject constructor(
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val cardSettingsInteractor: CardSettingsInteractor,
|
||||
) : ViewModel() {
|
||||
|
||||
private val userWalletId = savedStateHandle.get<Bundle>(AppRoute.AccessCodeRecovery.USER_WALLET_ID_KEY)
|
||||
?.unbundle(UserWalletId.serializer())
|
||||
?: error("UserWalletId is required for AccessCodeRecoveryViewModel")
|
||||
private val scannedScanResponse = cardSettingsInteractor.scannedScanResponse.value
|
||||
?: error("Scan response is null")
|
||||
|
||||
val screenState = MutableStateFlow(
|
||||
value = getInitialState(),
|
||||
)
|
||||
|
||||
private fun getInitialState(): AccessCodeRecoveryScreenState {
|
||||
val userWallet = getUserWallet()
|
||||
|
||||
val isEnabled = isAccessCodeRecoveryEnabled(
|
||||
typeResolver = userWallet.scanResponse.cardTypesResolver,
|
||||
card = userWallet.scanResponse.card,
|
||||
typeResolver = scannedScanResponse.cardTypesResolver,
|
||||
card = scannedScanResponse.card,
|
||||
)
|
||||
|
||||
return AccessCodeRecoveryScreenState(
|
||||
|
|
@ -59,11 +48,10 @@ internal class AccessCodeRecoveryViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun saveChanges() = viewModelScope.launch {
|
||||
val userWallet = getUserWallet()
|
||||
val isEnabled = screenState.value.enabledSelection
|
||||
|
||||
tangemSdkManager
|
||||
.setAccessCodeRecoveryEnabled(userWallet.cardId, isEnabled)
|
||||
.setAccessCodeRecoveryEnabled(scannedScanResponse.card.cardId, isEnabled)
|
||||
.doOnSuccess {
|
||||
Analytics.send(
|
||||
Settings.CardSettings.AccessCodeRecoveryChanged(
|
||||
|
|
@ -71,6 +59,16 @@ internal class AccessCodeRecoveryViewModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
cardSettingsInteractor.update { scanResponse ->
|
||||
scanResponse.copy(
|
||||
card = scanResponse.card.copy(
|
||||
userSettings = scanResponse.card.userSettings?.copy(
|
||||
isUserCodeRecoveryAllowed = isEnabled,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
store.dispatchNavigationAction(AppRouter::pop)
|
||||
}
|
||||
}
|
||||
|
|
@ -78,14 +76,9 @@ internal class AccessCodeRecoveryViewModel @Inject constructor(
|
|||
private fun selectOption(isEnabled: Boolean) {
|
||||
screenState.update {
|
||||
it.copy(
|
||||
enabledSelection = isEnabled,
|
||||
isSaveChangesEnabled = isEnabled != it.enabledOnCard,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUserWallet(): UserWallet {
|
||||
return getUserWalletUseCase(userWalletId).getOrElse {
|
||||
error("Unable to get user wallet $userWalletId: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.tap.features.details.ui.cardsettings.domain
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Interactor for sharing logic and data between all card settings screens
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Singleton
|
||||
internal class CardSettingsInteractor @Inject constructor() {
|
||||
|
||||
private val _scannedScanResponse = MutableStateFlow<ScanResponse?>(value = null)
|
||||
val scannedScanResponse: StateFlow<ScanResponse?> = _scannedScanResponse
|
||||
|
||||
fun initialize(scanResponse: ScanResponse) {
|
||||
_scannedScanResponse.value = scanResponse
|
||||
}
|
||||
|
||||
fun update(transform: (ScanResponse) -> ScanResponse) {
|
||||
_scannedScanResponse.update {
|
||||
requireNotNull(it)
|
||||
transform(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
_scannedScanResponse.value = null
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ internal sealed class SettingsItem(
|
|||
data class LinkMoreCards(
|
||||
override val onClick: () -> Unit,
|
||||
) : SettingsItem(
|
||||
iconResId = R.drawable.ic_more_cards,
|
||||
iconResId = R.drawable.ic_more_cards_24,
|
||||
title = resourceReference(R.string.details_row_title_create_backup),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.tap.common.analytics.events.Settings
|
|||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.features.details.redux.ResetCardDialog
|
||||
import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor
|
||||
import com.tangem.tap.features.details.ui.common.utils.getResetToFactoryDescription
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE
|
||||
|
|
@ -44,6 +45,7 @@ internal class ResetCardViewModel @Inject constructor(
|
|||
private val deleteWalletUseCase: DeleteWalletUseCase,
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val cardSettingsInteractor: CardSettingsInteractor,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
|
|
@ -255,6 +257,8 @@ internal class ResetCardViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun finishFullReset() {
|
||||
cardSettingsInteractor.clear()
|
||||
|
||||
val newSelectedWallet = userWalletsListManager.selectedUserWalletSync
|
||||
|
||||
if (newSelectedWallet != null) {
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@ import android.net.Uri
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Disclaimer {
|
||||
fun type(): DisclaimerType
|
||||
fun getUri(): Uri
|
||||
suspend fun accept()
|
||||
suspend fun isAccepted(): Boolean
|
||||
}
|
||||
|
||||
abstract class BaseDisclaimer(
|
||||
protected val dataProvider: DisclaimerDataProvider,
|
||||
private val dataProvider: DisclaimerDataProvider,
|
||||
) : Disclaimer {
|
||||
|
||||
val baseUrl = "https://tangem.com"
|
||||
|
|
@ -26,46 +25,11 @@ abstract class BaseDisclaimer(
|
|||
}
|
||||
|
||||
class DummyDisclaimer : Disclaimer {
|
||||
override fun type(): DisclaimerType = DisclaimerType.Tangem
|
||||
override fun getUri(): Uri = Uri.parse("https://tangem.com/tangem_tos.html")
|
||||
override suspend fun accept() {}
|
||||
override suspend fun isAccepted(): Boolean = false
|
||||
}
|
||||
|
||||
class TangemDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
|
||||
override fun type(): DisclaimerType = DisclaimerType.Tangem
|
||||
override fun getUri(): Uri = Uri.parse("$baseUrl/tangem_tos.html")
|
||||
}
|
||||
|
||||
class Start2CoinDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
|
||||
override fun type(): DisclaimerType = DisclaimerType.Start2Coin
|
||||
override fun getUri(): Uri = Uri.parse("$baseUrl/" + filename(dataProvider.getLanguage(), getRegion()))
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun filename(languageCode: String, regionCode: String?): String {
|
||||
return when {
|
||||
languageCode == "fr" && regionCode == "ch" -> "start2coin-fr-ch-tangem.html"
|
||||
languageCode == "de" && regionCode == "ch" -> "start2coin-de-ch-tangem.html"
|
||||
languageCode == "en" && regionCode == "ch" -> "start2coin-en-ch-tangem.html"
|
||||
languageCode == "it" && regionCode == "ch" -> "start2coin-it-ch-tangem.html"
|
||||
languageCode == "fr" && regionCode == "fr" -> "start2coin-fr-fr-tangem.html"
|
||||
languageCode == "de" && regionCode == "at" -> "start2coin-de-at-tangem.html"
|
||||
regionCode == "fr" -> "start2coin-fr-fr-tangem.html"
|
||||
regionCode == "ch" -> "start2coin-en-ch-tangem.html"
|
||||
regionCode == "at" -> "start2coin-de-at-tangem.html"
|
||||
else -> "start2coin-fr-fr-tangem.html"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRegion(): String? {
|
||||
val cardId = dataProvider.getCardId()
|
||||
if (cardId.isEmpty()) return null
|
||||
|
||||
return when (cardId[1]) {
|
||||
'0' -> "fr"
|
||||
'1' -> "ch"
|
||||
'2' -> "at"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +1,28 @@
|
|||
package com.tangem.tap.features.disclaimer
|
||||
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class DisclaimerType {
|
||||
Tangem,
|
||||
Start2Coin,
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun get(cardDTO: CardDTO): DisclaimerType {
|
||||
return when {
|
||||
cardDTO.isStart2Coin -> Start2Coin
|
||||
else -> Tangem
|
||||
}
|
||||
}
|
||||
}
|
||||
fun CardDTO.createDisclaimer(): Disclaimer {
|
||||
val dataProvider = provideDisclaimerDataProvider(cardId)
|
||||
return TangemDisclaimer(dataProvider)
|
||||
}
|
||||
|
||||
fun DisclaimerType.createDisclaimer(cardDTO: CardDTO): Disclaimer {
|
||||
val dataProvider = provideDisclaimerDataProvider(cardDTO.cardId, this)
|
||||
return when (this) {
|
||||
DisclaimerType.Tangem -> TangemDisclaimer(dataProvider)
|
||||
DisclaimerType.Start2Coin -> Start2CoinDisclaimer(dataProvider)
|
||||
}
|
||||
}
|
||||
|
||||
fun CardDTO.createDisclaimer(): Disclaimer = DisclaimerType.get(this).createDisclaimer(this)
|
||||
|
||||
private fun provideDisclaimerDataProvider(cardId: String, disclaimerType: DisclaimerType): DisclaimerDataProvider {
|
||||
private fun provideDisclaimerDataProvider(cardId: String): DisclaimerDataProvider {
|
||||
val cardRepository = store.inject(DaggerGraphState::cardRepository)
|
||||
return object : DisclaimerDataProvider {
|
||||
override fun getLanguage(): String = Locale.getDefault().language
|
||||
override fun getCardId(): String = cardId
|
||||
|
||||
override suspend fun accept() {
|
||||
when (disclaimerType) {
|
||||
DisclaimerType.Tangem -> cardRepository.acceptTangemTOS()
|
||||
DisclaimerType.Start2Coin -> cardRepository.acceptStart2CoinTOS(cardId)
|
||||
}
|
||||
cardRepository.acceptTangemTOS()
|
||||
}
|
||||
|
||||
override suspend fun isAccepted(): Boolean {
|
||||
return when (disclaimerType) {
|
||||
DisclaimerType.Tangem -> cardRepository.isTangemTOSAccepted()
|
||||
DisclaimerType.Start2Coin -> cardRepository.isStart2CoinTOSAccepted(cardId)
|
||||
}
|
||||
return cardRepository.isTangemTOSAccepted()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.tap.features.home.redux
|
||||
|
||||
import com.google.firebase.analytics.ktx.analytics
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnResult
|
||||
import com.tangem.common.doOnSuccess
|
||||
|
|
@ -61,8 +63,13 @@ private fun handleHomeAction(action: Action) {
|
|||
}
|
||||
is HomeAction.GoToShop -> {
|
||||
Analytics.send(Shop.ScreenOpened())
|
||||
store.dispatchOpenUrl(NEW_BUY_WALLET_URL)
|
||||
|
||||
Firebase.analytics.appInstanceId
|
||||
.addOnSuccessListener {
|
||||
store.dispatchOpenUrl("$NEW_BUY_WALLET_URL&app_instance_id=$it")
|
||||
}
|
||||
.addOnFailureListener {
|
||||
store.dispatchOpenUrl(NEW_BUY_WALLET_URL)
|
||||
}
|
||||
// disabled for now in task [REDACTED_JIRA]
|
||||
// when (action.userCountryCode) {
|
||||
// RUSSIA_COUNTRY_CODE, BELARUS_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL)
|
||||
|
|
|
|||
|
|
@ -32,26 +32,27 @@ import timber.log.Timber
|
|||
*/
|
||||
object OnboardingHelper {
|
||||
suspend fun isOnboardingCase(response: ScanResponse): Boolean {
|
||||
val onboardingManager = store.state.globalState.onboardingState.onboardingManager
|
||||
val onboardingManager =
|
||||
store.state.globalState.onboardingState.onboardingManager ?: OnboardingManager(response)
|
||||
val cardId = response.card.cardId
|
||||
return when {
|
||||
response.cardTypesResolver.isTangemTwins() -> {
|
||||
if (!response.twinsIsTwinned()) {
|
||||
true
|
||||
} else {
|
||||
onboardingManager?.isActivationInProgress(cardId) ?: false
|
||||
onboardingManager.isActivationInProgress(cardId) ?: false
|
||||
}
|
||||
}
|
||||
|
||||
response.cardTypesResolver.isWallet2() || response.cardTypesResolver.isShibaWallet() -> {
|
||||
val emptyWallets = response.card.wallets.isEmpty()
|
||||
val activationInProgress = onboardingManager?.isActivationInProgress(cardId)
|
||||
val activationInProgress = onboardingManager.isActivationInProgress(cardId)
|
||||
val isNoBackup = response.card.backupStatus == CardDTO.BackupStatus.NoBackup &&
|
||||
!DemoHelper.isDemoCard(response)
|
||||
emptyWallets || activationInProgress == true || isNoBackup
|
||||
emptyWallets || activationInProgress || isNoBackup
|
||||
}
|
||||
|
||||
response.card.wallets.isNotEmpty() -> onboardingManager?.isActivationInProgress(cardId) ?: false
|
||||
response.card.wallets.isNotEmpty() -> onboardingManager.isActivationInProgress(cardId) ?: false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ import coil.load
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.ShareElement
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.common.extensions.getDrawableCompat
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
|
||||
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
|
||||
|
|
@ -134,7 +136,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
|
||||
private fun setupTopUpWalletState(state: OnboardingNoteState) = with(mainBinding.onboardingActionContainer) {
|
||||
if (state.isBuyAllowed) {
|
||||
if (availableForBuy(state.scanResponse, state.walletBalance)) {
|
||||
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
|
||||
btnMainAction.icon = null
|
||||
btnMainAction.setOnClickListener {
|
||||
|
|
@ -217,6 +219,11 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun availableForBuy(scanResponse: ScanResponse?, walletBalance: OnboardingWalletBalance): Boolean {
|
||||
scanResponse ?: return false
|
||||
return store.state.globalState.exchangeManager.availableForBuy(scanResponse, walletBalance.currency)
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(OnboardingNoteAction.OnBackPressed)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt
|
|||
|
||||
when (action) {
|
||||
is GlobalAction.Onboarding.Start -> {
|
||||
state = OnboardingNoteState()
|
||||
state = OnboardingNoteState(scanResponse = action.scanResponse)
|
||||
}
|
||||
is OnboardingNoteAction.SetArtworkUrl -> {
|
||||
state = state.copy(cardArtworkUrl = action.artworkUrl)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
package com.tangem.tap.features.onboarding.products.note.redux
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -20,14 +19,11 @@ data class OnboardingNoteState(
|
|||
val currentStep: OnboardingNoteStep = OnboardingNoteStep.None,
|
||||
val steps: List<OnboardingNoteStep> = OnboardingNoteStep.values().toList(),
|
||||
val showConfetti: Boolean = false,
|
||||
val scanResponse: ScanResponse? = null,
|
||||
) : StateType {
|
||||
|
||||
val progress: Int
|
||||
get() = steps.indexOf(currentStep)
|
||||
|
||||
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
|
||||
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
|
||||
}
|
||||
}
|
||||
|
||||
enum class OnboardingNoteStep {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ object TwinCardsMiddleware {
|
|||
val handler = twinsWalletMiddleware
|
||||
}
|
||||
|
||||
private val twinsWalletMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
private val twinsWalletMiddleware: Middleware<AppState> = { dispatch, _ ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handle(action, dispatch)
|
||||
|
|
@ -66,15 +66,15 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
|
||||
fun getScanResponse(): ScanResponse {
|
||||
return when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse
|
||||
CreateTwinWalletMode.RecreateWallet -> globalState.scanResponse
|
||||
is CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse
|
||||
is CreateTwinWalletMode.RecreateWallet -> globalState.scanResponse
|
||||
} ?: throw NullPointerException("ScanResponse can't be NULL")
|
||||
}
|
||||
|
||||
fun updateScanResponse(response: ScanResponse) {
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse = response
|
||||
CreateTwinWalletMode.RecreateWallet -> store.dispatchOnMain(GlobalAction.SaveScanResponse(response))
|
||||
is CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse = response
|
||||
is CreateTwinWalletMode.RecreateWallet -> store.dispatchOnMain(GlobalAction.SaveScanResponse(response))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +105,10 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
mainScope.launch {
|
||||
if (twinCardsState.currentStep is TwinCardsStep.WelcomeOnly) return@launch
|
||||
|
||||
if (twinCardsState.mode is CreateTwinWalletMode.RecreateWallet) {
|
||||
store.dispatch(GlobalAction.SaveScanResponse(twinCardsState.mode.scanResponse))
|
||||
}
|
||||
|
||||
val scanResponse = getScanResponse()
|
||||
onboardingManager?.apply {
|
||||
if (!isActivationStarted(scanResponse.card.cardId)) {
|
||||
|
|
@ -113,7 +117,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
}
|
||||
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
is CreateTwinWalletMode.CreateWallet -> {
|
||||
mainScope.launch {
|
||||
val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase)
|
||||
.invokeSync()
|
||||
|
|
@ -132,7 +136,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
store.dispatch(dispatchAction)
|
||||
}
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
is CreateTwinWalletMode.RecreateWallet -> {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Warning))
|
||||
}
|
||||
}
|
||||
|
|
@ -226,10 +230,10 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
withMainContext {
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
is CreateTwinWalletMode.CreateWallet -> {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.TopUpWallet))
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
is CreateTwinWalletMode.RecreateWallet -> {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done))
|
||||
}
|
||||
}
|
||||
|
|
@ -319,11 +323,11 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
TwinCardsAction.Done -> {
|
||||
val scanResponse = getScanResponse()
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
is CreateTwinWalletMode.CreateWallet -> {
|
||||
store.dispatchOnMain(GlobalAction.Onboarding.Stop)
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(scanResponse)
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
is CreateTwinWalletMode.RecreateWallet -> {
|
||||
scope.launch {
|
||||
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
|
||||
|
||||
|
|
@ -368,8 +372,7 @@ private fun getPopBackScreen(): KClass<out AppRoute> {
|
|||
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
|
||||
return if (userWalletsListManager.hasUserWallets) {
|
||||
val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync }
|
||||
.fold(onSuccess = { true }, onFailure = { false })
|
||||
val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync!! }.getOrElse { false }
|
||||
|
||||
if (isLocked) {
|
||||
AppRoute.Welcome::class
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -34,7 +32,7 @@ data class TwinCardsState(
|
|||
|
||||
val steps: List<TwinCardsStep>
|
||||
get() = when (mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> listOf(
|
||||
is CreateTwinWalletMode.CreateWallet -> listOf(
|
||||
TwinCardsStep.None,
|
||||
TwinCardsStep.CreateFirstWallet,
|
||||
TwinCardsStep.CreateSecondWallet,
|
||||
|
|
@ -42,7 +40,7 @@ data class TwinCardsState(
|
|||
TwinCardsStep.TopUpWallet,
|
||||
TwinCardsStep.Done,
|
||||
)
|
||||
CreateTwinWalletMode.RecreateWallet -> listOf(
|
||||
is CreateTwinWalletMode.RecreateWallet -> listOf(
|
||||
TwinCardsStep.None,
|
||||
TwinCardsStep.CreateFirstWallet,
|
||||
TwinCardsStep.CreateSecondWallet,
|
||||
|
|
@ -56,13 +54,15 @@ data class TwinCardsState(
|
|||
|
||||
val twinningInProgress: Boolean
|
||||
get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet
|
||||
|
||||
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
|
||||
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
|
||||
}
|
||||
}
|
||||
|
||||
enum class CreateTwinWalletMode { CreateWallet, RecreateWallet }
|
||||
sealed class CreateTwinWalletMode {
|
||||
data object CreateWallet : CreateTwinWalletMode()
|
||||
|
||||
data class RecreateWallet(
|
||||
val scanResponse: ScanResponse,
|
||||
) : CreateTwinWalletMode()
|
||||
}
|
||||
|
||||
sealed class TwinCardsStep {
|
||||
object None : TwinCardsStep()
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
|
|||
import com.tangem.tap.domain.twins.TwinsCardWidget
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.onboarding.OnboardingMenuProvider
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
|
|
@ -55,22 +56,17 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
|
|||
|
||||
override fun configureTransitions() {
|
||||
when (store.state.twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
is CreateTwinWalletMode.CreateWallet -> {
|
||||
super.configureTransitions()
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
is CreateTwinWalletMode.RecreateWallet -> {
|
||||
configureDefaultTransactions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadToolbarMenu(): MenuProvider = OnboardingMenuProvider(
|
||||
scanResponseProvider = Provider {
|
||||
store.state.twinCardsState.welcomeOnlyScanResponse
|
||||
?: store.state.globalState.onboardingState.onboardingManager?.scanResponse
|
||||
?: store.state.detailsState.scanResponse
|
||||
?: error("ScanResponse must be not null")
|
||||
},
|
||||
scanResponseProvider = Provider { getActualScanResponse() },
|
||||
)
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
|
|
@ -342,7 +338,7 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
|
|||
else -> {}
|
||||
}
|
||||
|
||||
if (state.isBuyAllowed) {
|
||||
if (availableForBuy(getActualScanResponse(), state.walletBalance)) {
|
||||
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
|
||||
btnMainAction.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.TopUp)
|
||||
|
|
@ -395,8 +391,8 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
|
|||
tvBody.setText(R.string.onboarding_done_body)
|
||||
|
||||
val layout = when (state.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> R.layout.lp_onboarding_done_activation_twins
|
||||
CreateTwinWalletMode.RecreateWallet -> R.layout.lp_onboarding_done
|
||||
is CreateTwinWalletMode.CreateWallet -> R.layout.lp_onboarding_done_activation_twins
|
||||
is CreateTwinWalletMode.RecreateWallet -> R.layout.lp_onboarding_done
|
||||
}
|
||||
updateConstraints(state.currentStep, layout)
|
||||
}
|
||||
|
|
@ -424,6 +420,18 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
|
|||
}
|
||||
}
|
||||
|
||||
private fun availableForBuy(scanResponse: ScanResponse?, walletBalance: OnboardingWalletBalance): Boolean {
|
||||
scanResponse ?: return false
|
||||
return store.state.globalState.exchangeManager.availableForBuy(scanResponse, walletBalance.currency)
|
||||
}
|
||||
|
||||
private fun getActualScanResponse(): ScanResponse {
|
||||
return store.state.twinCardsState.welcomeOnlyScanResponse
|
||||
?: store.state.globalState.onboardingState.onboardingManager?.scanResponse
|
||||
?: store.state.detailsState.scanResponse
|
||||
?: error("ScanResponse must be not null")
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(
|
||||
TwinCardsAction.OnBackPressed { should, popAction ->
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import com.tangem.tap.features.onboarding.OnboardingDialog
|
|||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -44,6 +45,8 @@ object OnboardingWalletMiddleware {
|
|||
val handler = onboardingWalletMiddleware
|
||||
}
|
||||
|
||||
private const val HIDE_PROGRESS_DELAY = 400L
|
||||
|
||||
private val onboardingWalletMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
|
|
@ -161,17 +164,13 @@ private fun handleWalletAction(action: Action) {
|
|||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
|
||||
if (scanResponse == null) {
|
||||
store.dispatchNavigationAction(AppRouter::pop)
|
||||
store.dispatch(HomeAction.ReadCard(scope = action.scope))
|
||||
action.scope.launch {
|
||||
readCard { newScanResponse ->
|
||||
handleFinishOnboardind(newScanResponse)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val backupState = store.state.onboardingWalletState.backupState
|
||||
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(
|
||||
scanResponse = updatedScanResponse,
|
||||
accessCode = backupState.accessCode,
|
||||
backupCardsIds = backupState.backupCardIds,
|
||||
hasBackupError = backupState.hasBackupError,
|
||||
)
|
||||
handleFinishOnboardind(scanResponse)
|
||||
}
|
||||
}
|
||||
is OnboardingWalletAction.ResumeBackup -> {
|
||||
|
|
@ -196,6 +195,43 @@ private fun handleWalletAction(action: Action) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleFinishOnboardind(scanResponse: ScanResponse) {
|
||||
val backupState = store.state.onboardingWalletState.backupState
|
||||
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
|
||||
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(
|
||||
scanResponse = updatedScanResponse,
|
||||
accessCode = backupState.accessCode,
|
||||
backupCardsIds = backupState.backupCardIds,
|
||||
hasBackupError = backupState.hasBackupError,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun readCard(onSuccess: (ScanResponse) -> Unit) {
|
||||
val shouldSaveAccessCodes = store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes()
|
||||
|
||||
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = shouldSaveAccessCodes,
|
||||
)
|
||||
|
||||
store.inject(DaggerGraphState::scanCardProcessor).scan(
|
||||
analyticsSource = com.tangem.core.analytics.models.AnalyticsParam.ScreensSources.Intro,
|
||||
onProgressStateChange = { showProgress ->
|
||||
if (showProgress) {
|
||||
store.dispatch(HomeAction.ScanInProgress(scanInProgress = true))
|
||||
} else {
|
||||
delay(HIDE_PROGRESS_DELAY)
|
||||
store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Unable to scan card")
|
||||
delay(HIDE_PROGRESS_DELAY)
|
||||
store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun loadArtworkForUnfinishedBackup(
|
||||
cardId: String,
|
||||
cardPublicKey: ByteArray,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,7 @@ package com.tangem.tap.features.saveWallet.ui.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -20,16 +14,10 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerH32
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.SpacerHHalf
|
||||
import com.tangem.core.ui.components.SpacerW24
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
|
|
@ -37,10 +25,7 @@ internal fun SaveWalletScreenContent(showProgress: Boolean, onSaveWalletClick: (
|
|||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Header(onCloseClick = onCloseClick)
|
||||
SpacerHHalf()
|
||||
Title(
|
||||
modifier = Modifier
|
||||
.widthIn(max = TangemTheme.dimens.size200),
|
||||
)
|
||||
Title(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing22))
|
||||
SpacerH32()
|
||||
Description(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import androidx.compose.animation.AnimatedVisibility
|
|||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -123,14 +126,14 @@ internal fun HasMoreItem(moreCount: Int) {
|
|||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing4)
|
||||
.align(Alignment.Center)
|
||||
.drawWithContent { if (readyToDraw) drawContent() },
|
||||
text = "+$count",
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Clip,
|
||||
onTextLayout = { textLayoutResult ->
|
||||
if (textLayoutResult.didOverflowHeight) {
|
||||
if (textLayoutResult.hasVisualOverflow) {
|
||||
textStyle = textStyle.copy(fontSize = textStyle.fontSize * 0.9)
|
||||
} else {
|
||||
readyToDraw = true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.network.exchangeServices
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService
|
||||
|
|
@ -32,7 +33,8 @@ internal class BuyExchangeService(
|
|||
|
||||
override fun isSellAllowed(): Boolean = currentService.isSellAllowed()
|
||||
|
||||
override fun availableForBuy(currency: Currency): Boolean = currentService.availableForBuy(currency)
|
||||
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean =
|
||||
currentService.availableForBuy(scanResponse, currency)
|
||||
|
||||
override fun availableForSell(currency: Currency): Boolean = currentService.availableForSell(currency)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.network.exchangeServices
|
|||
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
|
||||
|
|
@ -38,8 +39,8 @@ class CardExchangeRules(
|
|||
}
|
||||
}
|
||||
|
||||
override fun availableForBuy(currency: Currency): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean {
|
||||
val card = scanResponse.card
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> true
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
|
|
@ -38,8 +39,9 @@ class CurrencyExchangeManager(
|
|||
override fun isBuyAllowed(): Boolean = primaryRules.isBuyAllowed() && buyService.isBuyAllowed()
|
||||
override fun isSellAllowed(): Boolean = primaryRules.isSellAllowed() && sellService.isSellAllowed()
|
||||
|
||||
override fun availableForBuy(currency: Currency): Boolean {
|
||||
return primaryRules.availableForBuy(currency) && buyService.availableForBuy(currency)
|
||||
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean {
|
||||
return primaryRules.availableForBuy(scanResponse, currency) &&
|
||||
buyService.availableForBuy(scanResponse, currency)
|
||||
}
|
||||
|
||||
override fun availableForSell(currency: Currency): Boolean {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
package com.tangem.tap.network.exchangeServices
|
||||
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
class DefaultRampManager(private val exchangeService: ExchangeService?) : RampStateManager {
|
||||
|
||||
private val cryptoCurrencyConverter = CryptoCurrencyConverter()
|
||||
override fun availableForBuy(cryptoCurrency: CryptoCurrency): Boolean {
|
||||
override fun availableForBuy(scanResponse: ScanResponse, cryptoCurrency: CryptoCurrency): Boolean {
|
||||
return exchangeService?.availableForBuy(
|
||||
scanResponse,
|
||||
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
|
||||
) ?: false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.network.exchangeServices
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.common.feature.Feature
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
|
|
@ -7,7 +8,7 @@ import com.tangem.tap.domain.model.Currency
|
|||
interface Exchanger {
|
||||
fun isBuyAllowed(): Boolean
|
||||
fun isSellAllowed(): Boolean
|
||||
fun availableForBuy(currency: Currency): Boolean
|
||||
fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean
|
||||
fun availableForSell(currency: Currency): Boolean
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ interface ExchangeService : Feature, Exchanger, ExchangeUrlBuilder {
|
|||
override suspend fun update() {}
|
||||
override fun isBuyAllowed(): Boolean = false
|
||||
override fun isSellAllowed(): Boolean = false
|
||||
override fun availableForBuy(currency: Currency): Boolean = false
|
||||
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean = false
|
||||
override fun availableForSell(currency: Currency): Boolean = false
|
||||
override fun getUrl(
|
||||
action: CurrencyExchangeManager.Action,
|
||||
|
|
@ -45,7 +46,7 @@ interface ExchangeRules : Feature, Exchanger {
|
|||
override fun featureIsSwitchedOn(): Boolean = false
|
||||
override fun isBuyAllowed(): Boolean = false
|
||||
override fun isSellAllowed(): Boolean = false
|
||||
override fun availableForBuy(currency: Currency): Boolean = false
|
||||
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean = false
|
||||
override fun availableForSell(currency: Currency): Boolean = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.extensions.calculateSha512
|
|||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
|
|
@ -28,7 +29,7 @@ internal class MercuryoService(private val environment: MercuryoEnvironment) : E
|
|||
|
||||
override fun isSellAllowed(): Boolean = false
|
||||
|
||||
override fun availableForBuy(currency: Currency): Boolean {
|
||||
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean {
|
||||
if (!isBuyAllowed()) return false
|
||||
|
||||
val mercuryoNetwork = currency.blockchain.mercuryoNetwork()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.common.services.Result
|
|||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.datasource.api.common.createRetrofitInstance
|
||||
import com.tangem.domain.common.extensions.withIOContext
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
|
|
@ -83,7 +84,7 @@ class MoonPayService(
|
|||
return status?.responseUserStatus?.isSellAllowed ?: false
|
||||
}
|
||||
|
||||
override fun availableForBuy(currency: Currency): Boolean = false
|
||||
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean = false
|
||||
|
||||
override fun availableForSell(currency: Currency): Boolean {
|
||||
if (!isSellAllowed()) return false
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class TransactionManagerImpl(
|
|||
@Throws(IllegalStateException::class)
|
||||
override suspend fun getFee(
|
||||
networkId: String,
|
||||
amountToSend: BigDecimal,
|
||||
amountToSend: Amount,
|
||||
currencyToSend: Currency,
|
||||
destinationAddress: String,
|
||||
increaseBy: Int?,
|
||||
|
|
@ -52,7 +52,7 @@ class TransactionManagerImpl(
|
|||
if (walletManager is EthereumOptimisticRollupWalletManager) {
|
||||
return getFeeForOptimismBlockchain(
|
||||
walletManager = walletManager,
|
||||
amount = createAmount(amountToSend, currencyToSend, blockchain),
|
||||
amount = amountToSend,
|
||||
destinationAddress = destinationAddress,
|
||||
data = data,
|
||||
)
|
||||
|
|
@ -61,7 +61,6 @@ class TransactionManagerImpl(
|
|||
walletManager = walletManager,
|
||||
blockchain = blockchain,
|
||||
amountToSend = amountToSend,
|
||||
currency = currencyToSend,
|
||||
destinationAddress = destinationAddress,
|
||||
data = data,
|
||||
increaseBy = increaseBy,
|
||||
|
|
@ -70,8 +69,6 @@ class TransactionManagerImpl(
|
|||
return getFeeForBlockchain(
|
||||
walletManager = walletManager,
|
||||
amountToSend = amountToSend,
|
||||
currency = currencyToSend,
|
||||
blockchain = blockchain,
|
||||
destinationAddress = destinationAddress,
|
||||
)
|
||||
}
|
||||
|
|
@ -87,13 +84,11 @@ class TransactionManagerImpl(
|
|||
|
||||
private suspend fun getFeeForBlockchain(
|
||||
walletManager: WalletManager,
|
||||
amountToSend: BigDecimal,
|
||||
currency: Currency,
|
||||
blockchain: Blockchain,
|
||||
amountToSend: Amount,
|
||||
destinationAddress: String,
|
||||
): ProxyFees {
|
||||
val fee = (walletManager as? TransactionSender)?.getFee(
|
||||
amount = createAmount(amountToSend, currency, blockchain),
|
||||
amount = amountToSend,
|
||||
destination = destinationAddress,
|
||||
) ?: error("Cannot cast to TransactionSender")
|
||||
return when (fee) {
|
||||
|
|
@ -155,17 +150,14 @@ class TransactionManagerImpl(
|
|||
private suspend fun getFeeForEthereumBlockchain(
|
||||
walletManager: EthereumWalletManager,
|
||||
blockchain: Blockchain,
|
||||
amountToSend: BigDecimal,
|
||||
currency: Currency,
|
||||
amountToSend: Amount,
|
||||
destinationAddress: String,
|
||||
data: String?,
|
||||
increaseBy: Int?,
|
||||
): ProxyFees {
|
||||
val gasLimit = getGasLimit(
|
||||
evmWalletManager = walletManager,
|
||||
blockchain = blockchain,
|
||||
amount = amountToSend,
|
||||
currency = currency,
|
||||
destinationAddress = destinationAddress,
|
||||
data = data,
|
||||
).increaseBigIntegerByPercents(increaseBy)
|
||||
|
|
@ -219,23 +211,20 @@ class TransactionManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private suspend fun getGasLimit(
|
||||
evmWalletManager: EthereumWalletManager,
|
||||
blockchain: Blockchain,
|
||||
amount: BigDecimal,
|
||||
currency: Currency,
|
||||
amount: Amount,
|
||||
destinationAddress: String,
|
||||
data: String?,
|
||||
): BigInteger {
|
||||
val result = if (data.isNullOrEmpty()) {
|
||||
evmWalletManager.getGasLimit(
|
||||
amount = createAmount(amount, currency, blockchain),
|
||||
amount = amount,
|
||||
destination = destinationAddress,
|
||||
)
|
||||
} else {
|
||||
evmWalletManager.getGasLimit(
|
||||
amount = createAmount(amount, currency, blockchain),
|
||||
amount = amount,
|
||||
destination = destinationAddress,
|
||||
data = data,
|
||||
)
|
||||
|
|
@ -250,6 +239,18 @@ class TransactionManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getFeeForGas(networkId: String, gas: BigInteger, derivationPath: String?): ProxyFees {
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
|
||||
val walletManager = getActualWalletManager(blockchain, derivationPath)
|
||||
val gasPriceResult = (walletManager as? EthereumWalletManager)?.getGasPrice()
|
||||
?: error("not supported for $blockchain")
|
||||
val gasPrice = when (gasPriceResult) {
|
||||
is Result.Failure -> error("fail to receive gasPrice")
|
||||
is Result.Success -> gasPriceResult.data
|
||||
}
|
||||
return createMultipleProxyFees(gasPrice, gas, blockchain)
|
||||
}
|
||||
|
||||
private suspend fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager {
|
||||
val selectedUserWallet = requireNotNull(
|
||||
userWalletsListManager.selectedUserWalletSync,
|
||||
|
|
@ -316,27 +317,6 @@ class TransactionManagerImpl(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createAmount(amount: BigDecimal, currency: Currency, blockchain: Blockchain): Amount {
|
||||
return when (currency) {
|
||||
is Currency.NativeToken -> {
|
||||
Amount(value = amount, blockchain = blockchain)
|
||||
}
|
||||
is Currency.NonNativeToken -> {
|
||||
Amount(convertNonNativeToken(currency), amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertNonNativeToken(token: Currency.NonNativeToken): Token {
|
||||
return Token(
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
contractAddress = token.contractAddress,
|
||||
decimals = token.decimalCount,
|
||||
id = token.id,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertToProxyAmount(amount: Amount): ProxyAmount {
|
||||
return ProxyAmount(
|
||||
currencySymbol = amount.currencySymbol,
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#000000" android:fillType="evenOdd" android:pathData="M21,11V16H7V11H21ZM21,8V6H7V8H21ZM22.41,4.59C22.04,4.21 21.53,4 21,4H7C5.89,4 5,4.89 5,6V16C5,17.1 5.89,18 7,18H21C21.53,18 22.04,17.79 22.41,17.41C22.79,17.04 23,16.53 23,16V6C23,5.47 22.79,4.96 22.41,4.59ZM18,20H3V9H1V20C1,20.53 1.21,21.04 1.59,21.41C1.96,21.79 2.47,22 3,22H18V20Z"/>
|
||||
</vector>
|
||||
|
|
@ -35,14 +35,7 @@ sealed class AppRoute(val path: String) : Route {
|
|||
@Serializable
|
||||
data class Disclaimer(
|
||||
val isTosAccepted: Boolean,
|
||||
) : AppRoute(path = "/disclaimer${if (isTosAccepted) "/tos_accepted" else ""}"), RouteBundleParams {
|
||||
|
||||
override fun getBundle(): Bundle = bundle(serializer())
|
||||
|
||||
companion object {
|
||||
const val IS_TOS_ACCEPTED_KEY = "isTosAccepted"
|
||||
}
|
||||
}
|
||||
) : AppRoute(path = "/disclaimer${if (isTosAccepted) "/tos_accepted" else ""}")
|
||||
|
||||
@Serializable
|
||||
data object OnboardingNote : AppRoute(path = "/onboarding/note")
|
||||
|
|
@ -177,15 +170,9 @@ sealed class AppRoute(val path: String) : Route {
|
|||
}
|
||||
|
||||
@Serializable
|
||||
data class AccessCodeRecovery(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/access_code_recovery/${userWalletId.stringValue}"), RouteBundleParams {
|
||||
data object AccessCodeRecovery : AppRoute(path = "/access_code_recovery"), RouteBundleParams {
|
||||
|
||||
override fun getBundle(): Bundle = bundle(serializer())
|
||||
|
||||
companion object {
|
||||
const val USER_WALLET_ID_KEY = "userWalletId"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ dependencies {
|
|||
|
||||
/** Domain */
|
||||
implementation(projects.domain.analytics)
|
||||
implementation(projects.domain.models)
|
||||
|
||||
/** Other */
|
||||
implementation(deps.kotlin.coroutines)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.core.analytics.utils
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface AnalyticsContextProxy {
|
||||
|
||||
fun setContext(scanResponse: ScanResponse)
|
||||
|
||||
fun eraseContext()
|
||||
|
||||
fun addContext(scanResponse: ScanResponse)
|
||||
|
||||
fun removeContext()
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.datasource.api.express.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class ExchangeDataResponseWithTxDetails(
|
||||
val dataResponse: ExchangeDataResponse,
|
||||
|
|
@ -50,7 +49,10 @@ data class TxDetails(
|
|||
val txData: String?, // transaction data if DEX, null if CEX
|
||||
|
||||
@Json(name = "txValue")
|
||||
val txValue: BigDecimal, // amount (same as fromAmount)
|
||||
val txValue: String, // amount (same as fromAmount for Coin, but for bridge equal to otherNativeFee)
|
||||
|
||||
@Json(name = "otherNativeFee")
|
||||
val otherNativeFee: String?,
|
||||
|
||||
@Json(name = "externalTxId")
|
||||
val externalTxId: String?, // null if DEX, provider transaction id if CEX
|
||||
|
|
@ -63,6 +65,9 @@ data class TxDetails(
|
|||
|
||||
@Json(name = "txExtraId")
|
||||
val txExtraId: String?,
|
||||
|
||||
@Json(name = "gas")
|
||||
val gas: String?,
|
||||
)
|
||||
|
||||
enum class TxType {
|
||||
|
|
|
|||
|
|
@ -7,17 +7,23 @@ data class ExchangeStatusResponse(
|
|||
@Json(name = "providerId")
|
||||
val providerId: String,
|
||||
|
||||
@Json(name = "externalTxId")
|
||||
val externalTxId: String,
|
||||
|
||||
@Json(name = "status")
|
||||
val status: ExchangeStatus,
|
||||
|
||||
@Json(name = "externalTxId")
|
||||
val externalTxId: String?,
|
||||
|
||||
@Json(name = "externalTxUrl")
|
||||
val externalTxUrl: String,
|
||||
val externalTxUrl: String?,
|
||||
|
||||
@Json(name = "error")
|
||||
val error: ExchangeStatusError?,
|
||||
|
||||
@Json(name = "refundNetwork")
|
||||
val refundNetwork: String? = null,
|
||||
|
||||
@Json(name = "refundContractAddress")
|
||||
val refundContractAddress: String? = null,
|
||||
)
|
||||
|
||||
enum class ExchangeStatus {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ interface TangemTechApi {
|
|||
@Query("contractAddress") contractAddress: String? = null,
|
||||
@Query("exchangeable") exchangeable: Boolean? = null,
|
||||
@Query("networkIds") networkIds: String? = null,
|
||||
@Query("networkId") networkId: String? = null,
|
||||
@Query("active") active: Boolean? = null,
|
||||
@Query("searchText") searchText: String? = null,
|
||||
@Query("offset") offset: Int? = null,
|
||||
|
|
|
|||
|
|
@ -95,8 +95,6 @@ object PreferencesKeys {
|
|||
booleanPreferencesKey(name = "isTokenSwapPromoOkxShown")
|
||||
}
|
||||
|
||||
fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region")
|
||||
|
||||
// region Permission
|
||||
fun getShouldShowPermission(permission: String) = booleanPreferencesKey("shouldShowPushPermission_$permission")
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@ interface SwapTransactionStatusStore {
|
|||
}
|
||||
|
||||
enum class ExchangeAnalyticsStatus(val value: String) {
|
||||
WaitingTxHash("Waiting tx hash"),
|
||||
InProgress("In Progress"),
|
||||
Done("Done"),
|
||||
Fail("Fail"),
|
||||
FailTx("Fail tx"),
|
||||
Unknown("Unknown"),
|
||||
KYC("KYC"),
|
||||
Refunded("Refunded"),
|
||||
Cancelled("Canceled"),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="add_custom_token_choose_network">Netzwerk wählen</string>
|
||||
<string name="add_custom_token_title">Benutzerdefiniertes Token hinzufügen</string>
|
||||
<string name="add_custom_token_title">Benutzerdef. Token hinzuf.</string>
|
||||
<string name="add_tokens_title">Token verwalten</string>
|
||||
<string name="address_qr_code_message_format">Sende nur %1$s ( %2$s ) vom %3$s -Netzwerk an diese Adresse. Die Verwendung anderer Token und Netzwerke kann zum Verlust von Geldern führen.</string>
|
||||
<string name="alert_button_how_to_scan">So scannt man</string>
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
<string name="app_settings_off_saved_wallet_alert_message">Durch das Entfernen der gespeicherten Karte werden alle gespeicherten Wallets und deren Zugangscodes aus der App gelöscht.</string>
|
||||
<string name="app_settings_saved_access_codes">Zugangscode speichern</string>
|
||||
<string name="app_settings_saved_access_codes_footer">Bei Interaktionen mit deiner Karte wird anstelle des Zugangscodes eine biometrische Authentifizierung abgefragt.</string>
|
||||
<string name="app_settings_saved_wallet">Behalten die Wallet in der App</string>
|
||||
<string name="app_settings_saved_wallet">Behalte die Wallet in der App</string>
|
||||
<string name="app_settings_saved_wallet_footer">Aktiviere die Verknüpfung aller Wallets mit der Tangem-App. Die biometrische Authentifizierung ist zum Entsperren der App erforderlich. Das Signieren von Transaktionen erfordert das Antippen deiner Tangem-Karte.</string>
|
||||
<string name="app_settings_theme_mode_dark">Dunkel</string>
|
||||
<string name="app_settings_theme_mode_light">Hell</string>
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
</plurals>
|
||||
<string name="card_settings_access_code_recovery_disabled_description">Deaktiviere diese Option, wenn du nicht möchtest, dass diese Karte zum Zurücksetzen von Zugangscodes auf anderen Karten in dieser Wallets verwendet wird. Bitte beachte, dass du dann auch den Zugangscode auf dieser Karte nicht zurücksetzen kannst.</string>
|
||||
<string name="card_settings_access_code_recovery_enabled_description">Ermöglicht die Verwendung dieser Karte zum Zurücksetzen des Zugangscodes auf anderen Karten in dieser Brieftasche</string>
|
||||
<string name="card_settings_access_code_recovery_title">Wiederherstellung des Zugangscodes</string>
|
||||
<string name="card_settings_access_code_recovery_title">Zugangscodes wiederherstellen</string>
|
||||
<string name="card_settings_action_sheet_reset">Zurücksetzen</string>
|
||||
<string name="card_settings_action_sheet_title">Möchtest du das wirklich tun?</string>
|
||||
<string name="card_settings_change_access_code">Zugangscode ändern</string>
|
||||
|
|
@ -84,14 +84,14 @@
|
|||
<string name="common_cancel">Abbrechen</string>
|
||||
<string name="common_claim_rewards">Stakingbelohnungen beanstpruchen</string>
|
||||
<string name="common_close">Schließen</string>
|
||||
<string name="common_continue">Weitermachen</string>
|
||||
<string name="common_continue">Weiter</string>
|
||||
<string name="common_copy">Kopieren</string>
|
||||
<string name="common_copy_address">Adresse kopieren</string>
|
||||
<string name="common_create">Erstellen</string>
|
||||
<string name="common_custom">Benutzerdefiniert</string>
|
||||
<string name="common_custom">Benutzerdef.</string>
|
||||
<plurals name="common_days">
|
||||
<item quantity="one">Tag</item>
|
||||
<item quantity="other">Tage</item>
|
||||
<item quantity="one">%d tag</item>
|
||||
<item quantity="other">%d tage</item>
|
||||
</plurals>
|
||||
<string name="common_delete">Entfernen</string>
|
||||
<string name="common_disabled">Deaktiviert</string>
|
||||
|
|
@ -107,9 +107,10 @@
|
|||
<string name="common_fee_selector_option_fast">Schnell</string>
|
||||
<string name="common_fee_selector_option_market">Markt</string>
|
||||
<string name="common_fee_selector_option_slow">Langsam</string>
|
||||
<string name="common_fee_selector_title">Geschwindigkeit und Gebühr</string>
|
||||
<string name="common_fee_selector_title">Gebühren</string>
|
||||
<string name="common_generate_addresses">Adressen abrufen</string>
|
||||
<string name="common_go_to_provider">Zum Anbieter gehen</string>
|
||||
<string name="common_go_to_token">Zum Token</string>
|
||||
<string name="common_import">Importieren</string>
|
||||
<string name="common_later">Später</string>
|
||||
<string name="common_locked">Gesperrt</string>
|
||||
|
|
@ -153,7 +154,7 @@
|
|||
<string name="common_today">Heute</string>
|
||||
<string name="common_transaction_failed">Transaktion fehlgeschlagen</string>
|
||||
<string name="common_transactions">Transaktionen</string>
|
||||
<string name="common_transfer">Überweisen</string>
|
||||
<string name="common_transfer">Überweisung</string>
|
||||
<string name="common_understand">Ich verstehe</string>
|
||||
<string name="common_unknown_error">Es ist ein Fehler aufgetreten. Bitte versuche es erneut.</string>
|
||||
<string name="common_unreachable">Nicht erreichbar</string>
|
||||
|
|
@ -225,6 +226,8 @@
|
|||
<string name="express_exchange_by">Getauscht von %s</string>
|
||||
<string name="express_exchange_notification_failed_text">Besuche die Website des Anbieters, um dein Geld zurückzuerhalten</string>
|
||||
<string name="express_exchange_notification_failed_title">Fehler beim Vorgang durch Anbieter</string>
|
||||
<string name="express_exchange_notification_refund_text">Der Transaktionsbetrag wurde aufgrund von OKX- oder Bridge-Regeln in %1$s auf deine Wallet zurückerstattet. %2$s</string>
|
||||
<string name="express_exchange_notification_refund_title">Der Betrag wurde in %1$s (%2$s Netzwerk) zurückerstattet.</string>
|
||||
<string name="express_exchange_notification_verification_text">Besuche die Website des Anbieters zur Überprüfung</string>
|
||||
<string name="express_exchange_notification_verification_title">KYC-Überprüfung durch den Anbieter erforderlich</string>
|
||||
<string name="express_exchange_status_canceled">Abgebrochen</string>
|
||||
|
|
@ -426,7 +429,7 @@
|
|||
<string name="onboarding_button_backup_now">Jetzt sichern</string>
|
||||
<string name="onboarding_button_backup_origin">Scannen der Hauptkarte</string>
|
||||
<string name="onboarding_button_continue_wallet">Weiter zu meiner Wallet</string>
|
||||
<string name="onboarding_button_finalize_backup">Abschließen der Sicherung</string>
|
||||
<string name="onboarding_button_finalize_backup">Backup abschließen</string>
|
||||
<string name="onboarding_button_receive_crypto">Krypto empfangen</string>
|
||||
<string name="onboarding_button_scan_origin_card">Primärkarte scannen</string>
|
||||
<string name="onboarding_button_skip_backup">Für später überspringen</string>
|
||||
|
|
@ -436,7 +439,7 @@
|
|||
<string name="onboarding_create_wallet_header">Erstelle eine Wallet</string>
|
||||
<string name="onboarding_create_wallet_options_button_options">Andere Optionen</string>
|
||||
<string name="onboarding_create_wallet_options_message">Deine Schlüssel(private-keys) werden sicher im Inneren der Karte generiert. Es gibt keine Seed-Phrase, d. h. niemand kann sie exportieren oder stehlen.</string>
|
||||
<string name="onboarding_create_wallet_options_title">Schlüssel privat generieren</string>
|
||||
<string name="onboarding_create_wallet_options_title">Schlüssel anonym generieren</string>
|
||||
<string name="onboarding_done_body">Deine Karte ist aktiviert und einsatzbereit</string>
|
||||
<string name="onboarding_done_header">Erfolgreich!</string>
|
||||
<string name="onboarding_exit_alert_message">In diesem Fall musst du ganz von vorne anfangen.</string>
|
||||
|
|
@ -454,22 +457,22 @@
|
|||
<item quantity="one">leer</item>
|
||||
<item quantity="other">%d Wörter</item>
|
||||
</plurals>
|
||||
<string name="onboarding_seed_import_message">Um deine Wallets zu importieren, gib bitte deineSeed-Phrase in das folgende Feld ein</string>
|
||||
<string name="onboarding_seed_import_message">Um deine Wallets zu importieren, gib bitte deine Seed-Phrase in das folgende Feld ein</string>
|
||||
<string name="onboarding_seed_intro_button_generate">Seed-Phrase generieren</string>
|
||||
<string name="onboarding_seed_intro_button_import">Wallet importieren</string>
|
||||
<string name="onboarding_seed_intro_message">Eine Seed-Phrase ist eine Reihe von Wörtern, mit denen du deine Wallet wiederherstellen kannst. Im Gegensatz zu den von der Karte generierten Schlüsseln sind Seed-Phrasen ungeschützt und können kopiert und gestohlen werden. Die Verwendung dieser Option erfolgt auf eigene Gefahr.</string>
|
||||
<string name="onboarding_seed_intro_title">Seed-Phrase verwenden</string>
|
||||
<string name="onboarding_seed_mnemonic_invalid_checksum">Ungültige Seed-Phrase. Bitte überprüfe die Wortreihenfolge.</string>
|
||||
<string name="onboarding_seed_mnemonic_wrong_words">Ungültige Seed-Phrase. Bitte überprüfe die Rechtschreibung.</string>
|
||||
<string name="onboarding_seed_phrase_intro_legacy">Altbestand</string>
|
||||
<string name="onboarding_seed_phrase_intro_legacy">veralteter Standard</string>
|
||||
<string name="onboarding_seed_user_validation_message">Um zu überprüfen, ob du deine Seed-Phrase richtig aufgeschrieben hast, gib bitte das 2., 7. und 11 Wort ein.</string>
|
||||
<string name="onboarding_seed_user_validation_title">Also, lass uns das überprüfen</string>
|
||||
<string name="onboarding_seed_user_validation_title">Eine letzte Prüfung!</string>
|
||||
<string name="onboarding_subtitle_no_backup_cards">Um den Sicherungsvorgang zu starten, füge bis zu zwei Sicherungskarten hinzu.</string>
|
||||
<string name="onboarding_subtitle_one_backup_card">Du kannst eine weitere Karte hinzufügen oder den Sicherungsvorgang abschließen</string>
|
||||
<string name="onboarding_subtitle_scan_backup_card_format">Bereite die Sicherungskarte mit der Nummer %s vor.</string>
|
||||
<string name="onboarding_subtitle_scan_primary">Scanne die primär-Karte, um den Sicherungsvorgang zu starten.</string>
|
||||
<string name="onboarding_subtitle_scan_primary_card_format">Bereite die primäre Karte mit der Nummer %s vor.</string>
|
||||
<string name="onboarding_subtitle_success_tangem_wallet_onboarding">Deine Wallet-Karte ist konfiguriert und einsatzbereit.</string>
|
||||
<string name="onboarding_subtitle_success_tangem_wallet_onboarding">Deine Tangem-Karte ist konfiguriert und einsatzbereit.</string>
|
||||
<string name="onboarding_subtitle_two_backup_cards">Maximale Anzahl an Karten hinzugefügt. Schließe den Sicherungsvorgang ab.</string>
|
||||
<string name="onboarding_title">Karte aktivieren</string>
|
||||
<string name="onboarding_title_backup_card_format">Sicherungskarte Nr. %d</string>
|
||||
|
|
@ -496,7 +499,7 @@
|
|||
<string name="organize_tokens_group">Gruppe erstellen</string>
|
||||
<string name="organize_tokens_sort_by_balance">Nach Guthaben</string>
|
||||
<string name="organize_tokens_title">Token organisieren</string>
|
||||
<string name="organize_tokens_ungroup">Gruppierung aufheben</string>
|
||||
<string name="organize_tokens_ungroup">Gruppierung aufh.</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Wählen aus der Galerie aus</string>
|
||||
<string name="qr_scanner_camera_denied_settings_button">Einstellungen</string>
|
||||
<string name="qr_scanner_camera_denied_text">Du hast keinen Zugriff auf deine Kamera gewährt</string>
|
||||
|
|
@ -626,7 +629,7 @@
|
|||
<string name="send_summary_tap_hint">Berühre an beliebiger Stelle für Änderungen</string>
|
||||
<string name="send_summary_title">Versende %s</string>
|
||||
<string name="send_summary_transaction_description">Du sendest **%1$s** inklusive der Netzwerkgebühr %2$s</string>
|
||||
<string name="send_summary_transaction_description_no_fiat_fee">Du sendest ** %1$s ** und %2$s</string>
|
||||
<string name="send_summary_transaction_description_no_fiat_fee">Du sendest **%1$s** und %2$s</string>
|
||||
<string name="send_title_currency_format">Senden %s</string>
|
||||
<string name="send_total_label">Gesamt</string>
|
||||
<string name="send_total_subtitle_asset_format">%1$s und %2$s werden gesendet</string>
|
||||
|
|
@ -644,9 +647,10 @@
|
|||
<string name="staking_active">Aktiv</string>
|
||||
<string name="staking_active_footer">Um deine Kryptos zu unstaken, klick hier.</string>
|
||||
<string name="staking_amount_requirement_error">Die Anzahl der zu stakenden Krypros muss mindesten %s betragen</string>
|
||||
<string name="staking_claim_unstaked">nicht gestakte beanspruche</string>
|
||||
<string name="staking_details_annual_percentage_rate">Jährliche prozentuale Rendite</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">Die jährliche prozentuale Rendite, die du durch die Teilnahme am Staking erzielen kannst.</string>
|
||||
<string name="staking_details_apr">Effektiver Jahreszins</string>
|
||||
<string name="staking_details_apy">Jährliche prozentuale Rendite</string>
|
||||
<string name="staking_details_apy_info">Die jährliche prozentuale Rendite, die du durch die Teilnahme am Staking erzielen kannst.</string>
|
||||
<string name="staking_details_available">Verfügbar</string>
|
||||
<string name="staking_details_average_reward_rate">Durchschnittliche Belohnungsquote</string>
|
||||
<string name="staking_details_estimated_profit">%s geschätzter Profit</string>
|
||||
|
|
@ -654,7 +658,6 @@
|
|||
<string name="staking_details_metrics_block_header">Metriken</string>
|
||||
<string name="staking_details_minimum_requirement">Mindestanforderungen</string>
|
||||
<string name="staking_details_no_rewards_to_claim">Keine Belohnungen zu beanspruchen.</string>
|
||||
<string name="staking_details_on_stake">Gestaked</string>
|
||||
<string name="staking_details_reward_claiming">Belohnungen beanspruchen</string>
|
||||
<string name="staking_details_reward_claiming_info">Eine Möglichkeit, Staking-Belohnungen zu erhalten. Es kann automatisch oder manuell beansprucht werden.</string>
|
||||
<string name="staking_details_reward_schedule">Belohnungszeitplan</string>
|
||||
|
|
@ -665,14 +668,28 @@
|
|||
<string name="staking_details_unbonding_period_info">Der Zeitraum, den du nach der Beantragung der Abhebung von Geldern aus dem Staking warten musst, bevor die Token verfügbar werden.</string>
|
||||
<string name="staking_details_warmup_period">Aufwärmphase</string>
|
||||
<string name="staking_details_warmup_period_info">Die zugewiesene Zeit für die Aktivierung der Teilnahme am Staking.</string>
|
||||
<string name="staking_initial_info_title">Stake %s</string>
|
||||
<string name="staking_migrate">Migrieren</string>
|
||||
<string name="staking_native">Natives Staking</string>
|
||||
<string name="staking_notification_earn_rewards_text" formatted="false">Mit Staking kannst du %1s verdienen. Deine Staking-Belohnungen kommen alle ~%2s Tage.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Verdiene Staking-Belohnungen</string>
|
||||
<string name="staking_notification_unstake_text">Die Belohnungen werden sofort nach dem unstaken gestoppt. Der unstakingprozess dauert %s.</string>
|
||||
<string name="staking_rebond">Erneut binden</string>
|
||||
<string name="staking_restake">Erneut staken</string>
|
||||
<string name="staking_restake_rewards">Belohnungen erneut staken</string>
|
||||
<string name="staking_revoke">Widerrufen</string>
|
||||
<string name="staking_revote">Neuwahl</string>
|
||||
<string name="staking_rewards">Belohnungen</string>
|
||||
<string name="staking_stake_locked">Stake gesperrt</string>
|
||||
<string name="staking_stake_more">Mehr staken</string>
|
||||
<string name="staking_unstaked">unstaken</string>
|
||||
<string name="staking_unlocked_locked">gelocktes unlocken</string>
|
||||
<string name="staking_unstaked">Unstaken</string>
|
||||
<string name="staking_unstaked_footer">Prüfe, was nicht eingesetzt wurde, um dein Vermögen zu beanspruchen</string>
|
||||
<string name="staking_unstaking">Staking beenden</string>
|
||||
<string name="staking_validator">Validator/ Prüfer</string>
|
||||
<string name="staking_vote">Abstimmung</string>
|
||||
<string name="staking_vote_locked">Abstimmung gesperrt</string>
|
||||
<string name="staking_withdraw">Zurückziehen</string>
|
||||
<string name="story_awe_description">Bewahre deine Krypto-Assets sicher auf, während die privaten Schlüssel auf deiner Karte bleiben</string>
|
||||
<string name="story_awe_title">Revolutionäre Hardware-Wallet</string>
|
||||
<string name="story_backup_description">Bis zu 3 physische Karten pro Wallet</string>
|
||||
|
|
@ -748,8 +765,8 @@
|
|||
<string name="twins_recreate_warning">Diese Aktion ist unumkehrbar. Du hast keinen Zugriff mehr auf die alte Wallet.</string>
|
||||
<string name="twins_scan_twin_with_number">Tippe auf die Doppelkarte mit der Nummer %s und entferne sie erst am Ende des Vorgangs.</string>
|
||||
<string name="unlock_wallet_description_full">Verwende %s oder scanne eine Karte, um Zugriff auf deine Wallet zu erhalten.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Bleib auf dem Laufenden mit den neuesten Funktionen und Neuigkeiten</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Sei der Erste, der von neuen Aktionen erfährt</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Bleib auf dem Laufenden mit den neuesten Funktionen und Neuigkeiten</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Sei der Erste, der von neuen Aktionen erfährt</string>
|
||||
<string name="user_push_notification_agreement_header">Möchtest du Push-Benachrichtigungen verwenden?</string>
|
||||
<string name="user_wallet_list_add_button">Neues Wallet hinzufügen</string>
|
||||
<string name="user_wallet_list_delete_prompt">Möchtest du diese Wallet wirklich löschen?</string>
|
||||
|
|
@ -805,9 +822,9 @@
|
|||
<string name="warning_backup_errors_title">Aktivierungsfehler</string>
|
||||
<string name="warning_beacon_chain_retirement_content">Laut den Entwicklern des BNB-Netzes wird die Unterstützung für den BEP-2-Standard im Juni 2024 enden. Um den Verlust von Vermögenswerten mit diesem Standard zu vermeiden, konvertiere bitte in den BEP-20 Standard. Nutze gerne unseren Swap-Service, um sie auf das BNB Smart Chain Netzwerk zu übertragen.</string>
|
||||
<string name="warning_beacon_chain_retirement_title">BNB Beacon Chain wird abgeschaltet</string>
|
||||
<string name="warning_button_could_be_better">Könnte besser sein</string>
|
||||
<string name="warning_button_could_be_better">verbesserungswürdig</string>
|
||||
<string name="warning_button_like_it">Gefällt mir</string>
|
||||
<string name="warning_button_ok">OK, ich hab\'s!</string>
|
||||
<string name="warning_button_ok">OK, habe ich verstanden!</string>
|
||||
<string name="warning_button_really_cool">Echt toll!</string>
|
||||
<string name="warning_button_refresh">Aktualisieren</string>
|
||||
<string name="warning_demo_mode_message">Du befindest sich derzeit im Demo-Modus</string>
|
||||
|
|
|
|||
|
|
@ -87,6 +87,10 @@
|
|||
<string name="common_copy_address">Copier l\'adresse</string>
|
||||
<string name="common_create">Créer</string>
|
||||
<string name="common_custom">Personnalisé</string>
|
||||
<plurals name="common_days">
|
||||
<item quantity="one">%d jour</item>
|
||||
<item quantity="other">%d jours</item>
|
||||
</plurals>
|
||||
<string name="common_delete">Supprimer</string>
|
||||
<string name="common_disabled">Désactivé</string>
|
||||
<string name="common_done">Exécuté</string>
|
||||
|
|
@ -272,8 +276,8 @@
|
|||
<string name="give_permission_swap_subtitle" formatted="false">Pour continuer, accordez aux smart contracts de %1s l\'autorisation d\'utiliser votre %2s</string>
|
||||
<string name="give_permission_title">Donner l\'autorisation</string>
|
||||
<string name="give_permission_unlimited">Illimité</string>
|
||||
<string name="home_button_order">Commander une carte</string>
|
||||
<string name="home_button_scan">Scannez la carte</string>
|
||||
<string name="home_button_order">Commandez</string>
|
||||
<string name="home_button_scan">Scannez</string>
|
||||
<string name="initial_message_change_access_code_body">Touchez, pour modifier le code d\'accès</string>
|
||||
<string name="initial_message_change_passcode_body">Touchez, pour modifier le mot de passe</string>
|
||||
<string name="initial_message_create_wallet_body">Pour créer le portefeuille, appuyez sur la carte comme indiqué ci-dessus et ne la retirez pas jusqu\'à la fin de l\'opération</string>
|
||||
|
|
@ -324,6 +328,7 @@
|
|||
<string name="markets_add_to_portfolio_button">Ajouter au portfolio</string>
|
||||
<string name="markets_common_my_portfolio">Mon portfolio</string>
|
||||
<string name="markets_common_title">Marché</string>
|
||||
<string name="markets_generate_addresses_notification">Pour générer des adresses pour les réseaux sélectionnés, vous devez scanner votre carte Tangem.</string>
|
||||
<string name="markets_select_wallet">Sélectionnez un portefeuille</string>
|
||||
<string name="markets_sort_by_title">Trier par</string>
|
||||
<string name="markets_token_details_insights">Idées</string>
|
||||
|
|
@ -371,7 +376,7 @@
|
|||
<string name="onboarding_exit_alert_title">Voulez-vous quitter le processus d\'activation ?</string>
|
||||
<string name="onboarding_getting_started">Initialiser</string>
|
||||
<string name="onboarding_linking_error_card_with_wallets">Un autre portefeuille a déjà été créé sur la carte que vous essayez d\'ajouter. Si vous avez des fonds dans ce portefeuille, veuillez les retirer, puis réinitialiser cette carte et l\'ajouter comme sauvegarde.</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">Création d\'une sauvegarde</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">Sauvegarde en cours</string>
|
||||
<string name="onboarding_seed_button_read_more">En savoir plus sur les seed phrases</string>
|
||||
<plurals name="onboarding_seed_generate_message_words_count">
|
||||
<item quantity="one">Empty</item>
|
||||
|
|
@ -384,7 +389,7 @@
|
|||
</plurals>
|
||||
<string name="onboarding_seed_import_message">Pour importer votre portefeuille, entrez votre seed phrase dans le champ ci-dessous</string>
|
||||
<string name="onboarding_seed_intro_button_generate">Générer une seed phrase</string>
|
||||
<string name="onboarding_seed_intro_button_import">Importer un portefeuille</string>
|
||||
<string name="onboarding_seed_intro_button_import">Importez</string>
|
||||
<string name="onboarding_seed_intro_message">Une seed phrase est une série de mots qui vous permet de récupérer votre portefeuille. Contrairement aux clés générées par la carte, les seed phrases ne sont pas protégées et peuvent être copiées et volées. Utilisez cette option à vos propres risques.</string>
|
||||
<string name="onboarding_seed_intro_title">Utiliser une seed phrase</string>
|
||||
<string name="onboarding_seed_mnemonic_invalid_checksum">Seed phrase invalide. Veuillez vérifier l\'ordre des mots.</string>
|
||||
|
|
@ -559,6 +564,7 @@
|
|||
<string name="send_total_subtitle_fiat_format">≈ %1$s (incl. les commissions : %2$s)</string>
|
||||
<string name="send_total_subtitle_format">Sera envoyé %s</string>
|
||||
<string name="send_transaction_success">La transaction a été signée avec succès et envoyée au nœud de blockchain. Le solde du portefeuille sera mis à jour après un certain temps</string>
|
||||
<string name="send_tron_account_activation_error">%1$s est un actif du réseau Tron. Pour calculer les frais et effectuer une transaction, déposez du Tron (TRX) sur votre compte.</string>
|
||||
<string name="send_validation_invalid_address">Adresse incorrecte</string>
|
||||
<string name="send_wallet_balance_format">%1$s (%2$s)</string>
|
||||
<string name="sent_transaction_sent_title">Transaction envoyée</string>
|
||||
|
|
@ -568,9 +574,8 @@
|
|||
<string name="settings_wallet_name_title">Nom</string>
|
||||
<string name="staking_active">Actif</string>
|
||||
<string name="staking_active_footer">Afin d\'unstaker vos actifs, cliquez ici.</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">Le pourcentage de rendement annuel que vous pouvez gagner en participant au staking.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_apy">APY</string>
|
||||
<string name="staking_details_apy_info">Le pourcentage de rendement annuel que vous pouvez gagner en participant au staking.</string>
|
||||
<string name="staking_details_available">Disponible</string>
|
||||
<string name="staking_details_average_reward_rate">Taux de récompense moyen</string>
|
||||
<string name="staking_details_estimated_profit">%s profit estimatif</string>
|
||||
|
|
@ -578,12 +583,11 @@
|
|||
<string name="staking_details_metrics_block_header">Métriques</string>
|
||||
<string name="staking_details_minimum_requirement">Minimum requis</string>
|
||||
<string name="staking_details_no_rewards_to_claim">Aucune récompense à réclamer</string>
|
||||
<string name="staking_details_on_stake">En jeu</string>
|
||||
<string name="staking_details_reward_claiming">Réclamation de récompense</string>
|
||||
<string name="staking_details_reward_claiming_info">Un moyen de recevoir des récompenses de staking. Il peut être réclamé automatiquement ou manuellement.</string>
|
||||
<string name="staking_details_reward_schedule">Calendrier de récompenses</string>
|
||||
<string name="staking_details_reward_schedule_info">Il s\'agit d\'un calendrier qui détermine le moment où les participants au staking reçoivent leurs récompenses.</string>
|
||||
<string name="staking_details_rewards_to_claim">Récompenses à réclamer : %s</string>
|
||||
<string name="staking_details_rewards_to_claim">Récompenses à réclamer: %s</string>
|
||||
<string name="staking_details_title">Staking %s</string>
|
||||
<string name="staking_details_unbonding_period">Période de détachement</string>
|
||||
<string name="staking_details_unbonding_period_info">La période que vous devez attendre après avoir demandé le retrait des fonds du staking avant que les jetons ne soient disponibles.</string>
|
||||
|
|
@ -641,7 +645,7 @@
|
|||
<string name="token_details_hide_token">Masquer le jeton</string>
|
||||
<string name="token_details_staking_block_subtitle">Le Staking vous permet d\'en gagner %1$s et d\'obtenir des récompenses tous les %2$s jours</string>
|
||||
<string name="token_details_staking_block_title">Gagnez jusqu\'à %s récompense de mise par an</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s jeton dans %%image%% le réseau %2$s</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s jeton dans %%image%% %2$s le réseau</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Jeton dans le %%image%% %1$s réseau</string>
|
||||
<string name="token_details_unable_hide_alert_message">Le jeton %1$s (%2$s) est la principale devise du réseau %3$s et ne peut pas être masqué tant que vous avez d\'autres jetons de ce réseau dans la liste</string>
|
||||
<string name="token_details_unable_hide_alert_title">Impossible de masquer %s</string>
|
||||
|
|
@ -669,6 +673,7 @@
|
|||
<string name="twins_scan_twin_with_number">Appuyez sur la carte jumelle avec le numéro %s et ne la retirez pas jusqu\'à la fin de l\'opération</string>
|
||||
<string name="unlock_wallet_description_full">Utilisez %s ou scannez une carte pour avoir accès à votre portefeuille</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Restez à jour avec les dernières fonctionnalités et actualités</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Soyez le premier informé des nouvelles promotions</string>
|
||||
<string name="user_push_notification_agreement_header">Souhaitez-vous utiliser les notifications push?</string>
|
||||
<string name="user_wallet_list_add_button">Ajouter un nouveau portefeuille</string>
|
||||
<string name="user_wallet_list_delete_prompt">Êtes-vous sûr de vouloir supprimer ce portefeuille ?</string>
|
||||
|
|
@ -724,7 +729,7 @@
|
|||
<string name="warning_backup_errors_title">Erreur d\'activation</string>
|
||||
<string name="warning_beacon_chain_retirement_content">Selon les développeurs du réseau BNB, le support de la norme BEP-2\nprendra fin en juin 2024. Pour éviter de perdre des actifs avec cette norme, veuillez les convertir à la norme BEP-20. Utilisez notre service de d\'échange pour les transférer sur le réseau BNB Smart Chain.</string>
|
||||
<string name="warning_beacon_chain_retirement_title">BNB Beacon Chain va s\'arrêter de fonctionner</string>
|
||||
<string name="warning_button_could_be_better">Pourrait être mieux</string>
|
||||
<string name="warning_button_could_be_better">Pas terrible</string>
|
||||
<string name="warning_button_like_it">J\'aime</string>
|
||||
<string name="warning_button_ok">Ok, je l\'ai!</string>
|
||||
<string name="warning_button_really_cool">Vraiment cool !</string>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
<string name="common_save_changes">Mantieni le modifiche</string>
|
||||
<string name="common_send">Invia</string>
|
||||
<string name="common_success">Con successo</string>
|
||||
<string name="common_warning">Avviso</string>
|
||||
<string name="details_manage_security_access_code">Codice di accesso</string>
|
||||
<string name="details_manage_security_access_code_description">Prima di scansionare la carta sarà necessario inserire il codice di accesso corretto</string>
|
||||
<string name="details_manage_security_long_tap">Mantenimento della carta</string>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@
|
|||
<string name="common_create">作成</string>
|
||||
<string name="common_custom">設定</string>
|
||||
<plurals name="common_days">
|
||||
<item quantity="other">日</item>
|
||||
<item quantity="other">%d 日</item>
|
||||
</plurals>
|
||||
<string name="common_delete">削除</string>
|
||||
<string name="common_disabled">無効</string>
|
||||
|
|
@ -108,6 +108,7 @@
|
|||
<string name="common_fee_selector_title">速度と料金</string>
|
||||
<string name="common_generate_addresses">アドレスを取得する</string>
|
||||
<string name="common_go_to_provider">プロバイダーへ移動</string>
|
||||
<string name="common_go_to_token">トークンへ移動</string>
|
||||
<string name="common_import">インポート</string>
|
||||
<string name="common_later">後で</string>
|
||||
<string name="common_locked">ロックされています</string>
|
||||
|
|
@ -143,7 +144,7 @@
|
|||
<string name="common_stake">ステーキング</string>
|
||||
<string name="common_staking">ステーキング</string>
|
||||
<string name="common_start">始める</string>
|
||||
<string name="common_submit">提出する</string>
|
||||
<string name="common_submit">送信</string>
|
||||
<string name="common_success">成功</string>
|
||||
<string name="common_support">サポート</string>
|
||||
<string name="common_swap">スワップ</string>
|
||||
|
|
@ -223,6 +224,8 @@
|
|||
<string name="express_exchange_by">%sによる交換</string>
|
||||
<string name="express_exchange_notification_failed_text">返金を受けるには、プロバイダーのウェブサイトにアクセスしてください。</string>
|
||||
<string name="express_exchange_notification_failed_title">プロバイダーによる操作が失敗しました。</string>
|
||||
<string name="express_exchange_notification_refund_text">OKXまたはブリッジのルールにより、取引金額は%1$sでウォレットに返金されました。%2$s</string>
|
||||
<string name="express_exchange_notification_refund_title">金額は %1$s(%2$sネットワーク)で返金されました</string>
|
||||
<string name="express_exchange_notification_verification_text">確認するには、プロバイダーのウェブサイトにアクセスしてください。</string>
|
||||
<string name="express_exchange_notification_verification_title">プロバイダーによる本人確認手続きが必要です。</string>
|
||||
<string name="express_exchange_status_canceled">キャンセルされました</string>
|
||||
|
|
@ -335,7 +338,7 @@
|
|||
<string name="markets_available_networks">利用可能なネットワーク</string>
|
||||
<string name="markets_common_my_portfolio">私のポートフォリオ</string>
|
||||
<string name="markets_common_title">マーケット</string>
|
||||
<string name="markets_generate_addresses_notification">選択したネットワークのアドレスを生成するには、Tangemカードをタップする必要があります</string>
|
||||
<string name="markets_generate_addresses_notification">選択したネットワークのアドレスを生成するには、Tangemカードをスキャンする必要があります。</string>
|
||||
<string name="markets_loading_error_title">データを読み込めません…</string>
|
||||
<string name="markets_quick_actions">クイックアクション</string>
|
||||
<string name="markets_search_result_title">結果</string>
|
||||
|
|
@ -446,7 +449,7 @@
|
|||
</plurals>
|
||||
<string name="onboarding_seed_import_message">ウォレットをインポートするには、下のフィールドにシードフレーズを入力してください。</string>
|
||||
<string name="onboarding_seed_intro_button_generate">シードフレーズを生成する</string>
|
||||
<string name="onboarding_seed_intro_button_import">ウォレットをインポートする</string>
|
||||
<string name="onboarding_seed_intro_button_import">ウォレットをインポート</string>
|
||||
<string name="onboarding_seed_intro_message">シードフレーズは、ウォレットを復元できる一連の単語です。カードによって生成される秘密鍵とは異なり、シードフレーズは保護されていないため、コピーされて盗まれる可能性があります。このオプションは自己責任で使用してください。</string>
|
||||
<string name="onboarding_seed_intro_title">シードフレーズを使用する</string>
|
||||
<string name="onboarding_seed_mnemonic_invalid_checksum">無効なシードフレーズです。語順を確認してください。</string>
|
||||
|
|
@ -613,7 +616,7 @@
|
|||
<string name="send_sending">送金中...</string>
|
||||
<string name="send_summary_tap_hint">変更するには任意の箇所をタップしてください</string>
|
||||
<string name="send_summary_title">%sを送金する</string>
|
||||
<string name="send_summary_transaction_description">** %1$s ** を送金する (ネットワーク手数料%2$sを含む)</string>
|
||||
<string name="send_summary_transaction_description">**%1$s** を送金する (ネットワーク手数料%2$sを含む)</string>
|
||||
<string name="send_summary_transaction_description_no_fiat_fee">**%1$s** と %2$s を送金しています。</string>
|
||||
<string name="send_title_currency_format">%sを送信しています</string>
|
||||
<string name="send_total_label">合計</string>
|
||||
|
|
@ -632,17 +635,18 @@
|
|||
<string name="staking_active">アクティブ</string>
|
||||
<string name="staking_active_footer">資産のステーキングを解除するには、ここをクリックしてください。</string>
|
||||
<string name="staking_amount_requirement_error">ステーキング金額は %s 以上である必要があります</string>
|
||||
<string name="staking_claim_unstaked">ステーキング解除分を請求する</string>
|
||||
<string name="staking_details_annual_percentage_rate">APY</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">ステーキングに参加することで得られる年間収益率。</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_apy">APY</string>
|
||||
<string name="staking_details_apy_info">ステーキングに参加することで得られる年間収益率。</string>
|
||||
<string name="staking_details_available">利用可能</string>
|
||||
<string name="staking_details_average_reward_rate">平均報酬率</string>
|
||||
<string name="staking_details_banner_text">ステーキングとは?</string>
|
||||
<string name="staking_details_estimated_profit">%s 推定利益</string>
|
||||
<string name="staking_details_market_rating">市場評価</string>
|
||||
<string name="staking_details_metrics_block_header">指標</string>
|
||||
<string name="staking_details_minimum_requirement">最低要件</string>
|
||||
<string name="staking_details_no_rewards_to_claim">請求できる報酬はありません</string>
|
||||
<string name="staking_details_on_stake">ステーキング中</string>
|
||||
<string name="staking_details_reward_claiming">請求中の報酬</string>
|
||||
<string name="staking_details_reward_claiming_info">ステーキング報酬を受け取る方法。自動または手動で請求できます。</string>
|
||||
<string name="staking_details_reward_schedule">報酬スケジュール</string>
|
||||
|
|
@ -653,14 +657,28 @@
|
|||
<string name="staking_details_unbonding_period_info">ステーキングから資金の引き出しを要求した後、トークンが利用可能になるまでの待機期間。</string>
|
||||
<string name="staking_details_warmup_period">ウォームアップ期間</string>
|
||||
<string name="staking_details_warmup_period_info">ステーキングへの参加を有効にするために割り当てられた時間。</string>
|
||||
<string name="staking_initial_info_title">%sをステーキングする</string>
|
||||
<string name="staking_migrate">移行</string>
|
||||
<string name="staking_native">ネイティブステーキング</string>
|
||||
<string name="staking_notification_earn_rewards_text" formatted="false">ステーキングにより%1sを獲得できます。ステーキング報酬は ~ %2s日ごとに届きます。</string>
|
||||
<string name="staking_notification_earn_rewards_title">ステーキング報酬を獲得</string>
|
||||
<string name="staking_notification_unstake_text">ステーキング解除後、報酬の獲得はすぐに停止します。ステーキング解除プロセスには%sかかります。</string>
|
||||
<string name="staking_rebond">再結束</string>
|
||||
<string name="staking_restake">再度ステーキングする</string>
|
||||
<string name="staking_restake_rewards">報酬をステーキングする</string>
|
||||
<string name="staking_revoke">取り消す</string>
|
||||
<string name="staking_revote">再投票</string>
|
||||
<string name="staking_rewards">報酬</string>
|
||||
<string name="staking_stake_locked">ステーキングはロックされています</string>
|
||||
<string name="staking_stake_more">もっとステーキングする</string>
|
||||
<string name="staking_unlocked_locked">ステーキング解除はロックされています</string>
|
||||
<string name="staking_unstaked">スタックされていない</string>
|
||||
<string name="staking_unstaked_footer">資産を請求するために、unstakedを確認してください</string>
|
||||
<string name="staking_unstaking">ステーキング解除</string>
|
||||
<string name="staking_validator">バリデーター</string>
|
||||
<string name="staking_vote">投票する</string>
|
||||
<string name="staking_vote_locked">投票はロックされています</string>
|
||||
<string name="staking_withdraw">引き出す</string>
|
||||
<string name="story_awe_description">カード内に秘密鍵を保管しながら暗号資産を安全に保管します</string>
|
||||
<string name="story_awe_title">革新的なハードウェアウォレット</string>
|
||||
<string name="story_backup_description">1つのウォレットに最大3枚のカード</string>
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@
|
|||
<string name="common_buy_currency">Перейти на %1$s</string>
|
||||
<string name="common_camera_denied_alert_message">Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности.</string>
|
||||
<string name="common_cancel">Отмена</string>
|
||||
<string name="common_claim_rewards">Вывести награду</string>
|
||||
<string name="common_close">Закрыть</string>
|
||||
<string name="common_continue">Продолжить</string>
|
||||
<string name="common_copy">Копировать</string>
|
||||
|
|
@ -110,6 +111,7 @@
|
|||
<string name="common_fee_selector_title">Скорость и комиссия</string>
|
||||
<string name="common_generate_addresses">Получить адреса</string>
|
||||
<string name="common_go_to_provider">К провайдеру</string>
|
||||
<string name="common_go_to_token">Перейти в токен</string>
|
||||
<string name="common_import">Импортировать</string>
|
||||
<string name="common_later">Позже</string>
|
||||
<string name="common_locked">Заблокирован</string>
|
||||
|
|
@ -142,6 +144,7 @@
|
|||
<string name="common_share">Поделиться</string>
|
||||
<string name="common_sign">Подписать</string>
|
||||
<string name="common_sign_and_send">Подписать и отправить</string>
|
||||
<string name="common_stake">Застейкать</string>
|
||||
<string name="common_staking">Стейкинг</string>
|
||||
<string name="common_start">Начать</string>
|
||||
<string name="common_submit">Отправить</string>
|
||||
|
|
@ -156,6 +159,7 @@
|
|||
<string name="common_understand">Я понял</string>
|
||||
<string name="common_unknown_error">Произошла ошибка. Пожалуйста, попробуйте снова.</string>
|
||||
<string name="common_unreachable">Недоступно</string>
|
||||
<string name="common_unstake">Завершить стейкинг</string>
|
||||
<string name="common_yes">Да</string>
|
||||
<string name="contract_address_copied_message">Адрес контракта скопирован!</string>
|
||||
<string name="currency_subtitle_expanded">Доступные сети</string>
|
||||
|
|
@ -223,6 +227,8 @@
|
|||
<string name="express_exchange_by">Обмен через %s</string>
|
||||
<string name="express_exchange_notification_failed_text">Чтобы вернуть ваши деньги, посетите сайт провайдера</string>
|
||||
<string name="express_exchange_notification_failed_title">Операция не выполнена провайдером</string>
|
||||
<string name="express_exchange_notification_refund_text">Отправленные средства были возвращены в %1$s на ваш кошелек в соответствии с правилами OKX или моста обмена. %2$s</string>
|
||||
<string name="express_exchange_notification_refund_title">Сумма была возвращена в %1$s (%2$s сети)</string>
|
||||
<string name="express_exchange_notification_verification_text">Посетите сайт провайдера для проверки</string>
|
||||
<string name="express_exchange_notification_verification_title">Провайдер запрашивает прохождение верификации</string>
|
||||
<string name="express_exchange_status_canceled">Отменен</string>
|
||||
|
|
@ -277,6 +283,7 @@
|
|||
<string name="give_permission_policy_type_footer">Укажите лимит доступа к выбранному токену</string>
|
||||
<string name="give_permission_rows_amount">Количество %s</string>
|
||||
<string name="give_permission_staking_footer">Функция подтверждения необходима для предоставления другому адресу разрешения на использование определенного количества ваших токенов.По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту StakeKit разрешение использовать ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете осуществить стейкинг токена.</string>
|
||||
<string name="give_permission_staking_subtitle">Чтобы продолжить, вам необходимо разрешить смарт контракту StakeKit использовать ваш %s</string>
|
||||
<string name="give_permission_swap_subtitle" formatted="false">Чтобы продолжить, вам нужно разрешить смарт-контракту %1s использовать ваш %2s</string>
|
||||
<string name="give_permission_title">Дать разрешение</string>
|
||||
<string name="give_permission_unlimited">Безлимитно</string>
|
||||
|
|
@ -327,6 +334,7 @@
|
|||
<string name="manage_tokens_unavailable_vote">Голосовать</string>
|
||||
<string name="manage_tokens_wallet_selector_title">Выберите кошелек</string>
|
||||
<string name="manage_tokens_wallet_support_only_one_network_title">Кошелёк не поддерживает более одной сети</string>
|
||||
<string name="markets_generate_addresses_notification">Чтобы создать адреса для выбранных сетей, необходимо отсканировать свой кошелек Tangem.</string>
|
||||
<string name="markets_token_details_links">Ссылки</string>
|
||||
<string name="markets_token_details_metrics">Метрики</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Вам необходимо установить единый код доступа для защиты всех ваших карт</string>
|
||||
|
|
@ -572,20 +580,49 @@
|
|||
<string name="settings_forget_wallet">Забыть кошелек</string>
|
||||
<string name="settings_forget_wallet_footer">Это приведет к удалению кошелька из приложения. Сам кошелек можно добавить снова.</string>
|
||||
<string name="settings_wallet_name_title">Имя</string>
|
||||
<string name="staking_amount_requirement_error">Сумма для стейкинда должна быть не менее %s</string>
|
||||
<string name="staking_details_apy">APY</string>
|
||||
<string name="staking_details_apy_info">Годовой процентный доход, который вы можете получить от участия в стейкинге.</string>
|
||||
<string name="staking_active">Активно</string>
|
||||
<string name="staking_active_footer">Для завершения стейкинга нажмите сюда</string>
|
||||
<string name="staking_amount_requirement_error">Сумма для стейкинга должна быть не менее %s</string>
|
||||
<string name="staking_claim_unstaked">Забрать средства</string>
|
||||
<string name="staking_details_annual_percentage_rate">APY</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">Годовой процентный доход, который вы можете получить от участия в стейкинге.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_available">Доступно</string>
|
||||
<string name="staking_details_estimated_profit">%s</string>
|
||||
<string name="staking_details_average_reward_rate">Средння ставка вознаграждения</string>
|
||||
<string name="staking_details_estimated_profit">%s оценка доходности</string>
|
||||
<string name="staking_details_market_rating">Позиция в рынке</string>
|
||||
<string name="staking_details_metrics_block_header">Метрики</string>
|
||||
<string name="staking_details_minimum_requirement">Минимальное количество</string>
|
||||
<string name="staking_details_no_rewards_to_claim">Нет вознаграждений к получению</string>
|
||||
<string name="staking_details_reward_claiming">Способ возраграждения</string>
|
||||
<string name="staking_details_reward_claiming_info">Способ получения вознаграждений за стейкинг. Он может быть автоматическим или ручным.</string>
|
||||
<string name="staking_details_reward_claiming_info">Способ получения вознаграждений за стейкинг. Он может быть автоматическим, при котором вознаграждение само зачисляется вам на адрес или в ручную, когда вознаграждение нужно вывести, создав транзакцию на её получение.</string>
|
||||
<string name="staking_details_reward_schedule">Период возрагражения</string>
|
||||
<string name="staking_details_reward_schedule_info">Это период, определяющий, когда участники стейкинга получат свои вознаграждения.</string>
|
||||
<string name="staking_details_rewards_to_claim">Вознаграждение для получения: %s</string>
|
||||
<string name="staking_details_title">Стейкинг %s</string>
|
||||
<string name="staking_details_unbonding_period">Период вывода</string>
|
||||
<string name="staking_details_unbonding_period_info">Период, который необходимо подождать после запроса на вывод средств из стейкинга, прежде чем токены станут доступны.</string>
|
||||
<string name="staking_details_warmup_period">Период прогрева</string>
|
||||
<string name="staking_details_warmup_period_info">Время, необходимое для начала процесса стейкинга.</string>
|
||||
<string name="staking_details_warmup_period_info">Время, необходимое для начала процесса стейкинга и активации процесса начисления наград</string>
|
||||
<string name="staking_initial_info_title">Застейкать %s</string>
|
||||
<string name="staking_migrate">Переместить</string>
|
||||
<string name="staking_native">Нативный стейкинг</string>
|
||||
<string name="staking_notification_earn_rewards_text" formatted="false">Стейкинг дает возможность вам получать %1s. Награда будет зачисляться каждый %2s</string>
|
||||
<string name="staking_notification_earn_rewards_title">Получите награду за стейкинг</string>
|
||||
<string name="staking_notification_unstake_text">Награда перестанет начисляться сразу после завершения стейкинга. Процесс завершения длится %s.</string>
|
||||
<string name="staking_restake">Повторный стейкинг</string>
|
||||
<string name="staking_restake_rewards">Застейкать вознаграждения</string>
|
||||
<string name="staking_revoke">Отозвать</string>
|
||||
<string name="staking_revote">Переголосовать</string>
|
||||
<string name="staking_rewards">Вознаграждения</string>
|
||||
<string name="staking_stake_more">Застейкать еще</string>
|
||||
<string name="staking_unlocked_locked">Разблокировать</string>
|
||||
<string name="staking_unstaked">Выведено из стейкинга</string>
|
||||
<string name="staking_unstaked_footer">Проверьте процесс завершения стейкинга, чтобы вывести свои средства.</string>
|
||||
<string name="staking_unstaking">Завершение стейкинга</string>
|
||||
<string name="staking_validator">Валидатор</string>
|
||||
<string name="staking_vote">Проголосовать</string>
|
||||
<string name="staking_withdraw">Вывод</string>
|
||||
<string name="story_awe_description">Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте.</string>
|
||||
<string name="story_awe_title">Революционный аппаратный кошелек</string>
|
||||
<string name="story_backup_description">До трех карт с одним кошельком</string>
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@
|
|||
<string name="common_fee_selector_title">Швидкість та комісія</string>
|
||||
<string name="common_generate_addresses">Отримати адреси</string>
|
||||
<string name="common_go_to_provider">Перейти до провайдера</string>
|
||||
<string name="common_go_to_token">Перейти до токену</string>
|
||||
<string name="common_import">Імпортувати</string>
|
||||
<string name="common_later">Пізніше</string>
|
||||
<string name="common_locked">Заблокований</string>
|
||||
|
|
@ -229,6 +230,8 @@
|
|||
<string name="express_exchange_by">Обмін через %s</string>
|
||||
<string name="express_exchange_notification_failed_text">Щоб повернути ваші кошти, відвідайте сайт провайдера</string>
|
||||
<string name="express_exchange_notification_failed_title">Операція не виконана провайдером</string>
|
||||
<string name="express_exchange_notification_refund_text">Сума транзакції була повернута в %1$s на ваш гаманець відповідно до правил OKX або мосту обміну. %2$s</string>
|
||||
<string name="express_exchange_notification_refund_title">Сума була повернута в %1$s (%2$s мережі)</string>
|
||||
<string name="express_exchange_notification_verification_text">Відвідайте сайт провайдера для перевірки</string>
|
||||
<string name="express_exchange_notification_verification_title">Провайдер вимагає проходження KYC верифікації</string>
|
||||
<string name="express_exchange_status_canceled">Скасовано</string>
|
||||
|
|
@ -344,7 +347,7 @@
|
|||
<string name="markets_available_networks">Доступні мережі</string>
|
||||
<string name="markets_common_my_portfolio">Моє портфоліо</string>
|
||||
<string name="markets_common_title">Маркет</string>
|
||||
<string name="markets_generate_addresses_notification">Щоб згенерувати адреси для обраних мереж, потрібно прикласти картку Tangem</string>
|
||||
<string name="markets_generate_addresses_notification">Щоб згенерувати адреси для обраних мереж, потрібно відсканувати свою картку Tangem.</string>
|
||||
<string name="markets_loading_error_title">Не вдалося завантажити дані...</string>
|
||||
<string name="markets_quick_actions">Швидкі дії</string>
|
||||
<string name="markets_search_result_title">Результат</string>
|
||||
|
|
@ -464,7 +467,7 @@
|
|||
</plurals>
|
||||
<string name="onboarding_seed_import_message">Щоб імпортувати гаманець, введіть seed-фразу в поле нижче</string>
|
||||
<string name="onboarding_seed_intro_button_generate">Згенерувати seed-фразу</string>
|
||||
<string name="onboarding_seed_intro_button_import">Імпортувати гаманець</string>
|
||||
<string name="onboarding_seed_intro_button_import">Імпорт гаманця</string>
|
||||
<string name="onboarding_seed_intro_message">Seed-фраза — це набір слів, який дозволяє відновити ваш гаманець. На відміну від ключів, що генеруються карткою, seed-фраза не захищена і може бути скопійована та викрадена. Використовуйте цю опцію на свій власний ризик.</string>
|
||||
<string name="onboarding_seed_intro_title">Використовувати seed-фразу</string>
|
||||
<string name="onboarding_seed_mnemonic_invalid_checksum">Невірна seed-фраза. Будь ласка, перевірте порядок слів.</string>
|
||||
|
|
@ -637,15 +640,15 @@
|
|||
<string name="send_sending">Надсилання...</string>
|
||||
<string name="send_summary_tap_hint">Торкніться будь-якого поля, щоб змінити його</string>
|
||||
<string name="send_summary_title">Надіслати %s</string>
|
||||
<string name="send_summary_transaction_description">Ви надсилаєте ** %1$s **, включно з комісію мережі %2$s</string>
|
||||
<string name="send_summary_transaction_description_no_fiat_fee">Ви надсилаєте ** %1$s ** і %2$s</string>
|
||||
<string name="send_summary_transaction_description">Ви надсилаєте **%1$s**, включно з комісію мережі %2$s</string>
|
||||
<string name="send_summary_transaction_description_no_fiat_fee">Ви надсилаєте **%1$s** і %2$s</string>
|
||||
<string name="send_title_currency_format">Надсилання %s</string>
|
||||
<string name="send_total_label">Всього</string>
|
||||
<string name="send_total_subtitle_asset_format">%1$s та %2$s буде надіслано</string>
|
||||
<string name="send_total_subtitle_fiat_format">≈ %1$s (вкл. комісію: %2$s )</string>
|
||||
<string name="send_total_subtitle_format">%s буде надіслано</string>
|
||||
<string name="send_transaction_success">Транзакція успішно підписана і відправлена до блокчейну. Баланс гаманця буде оновлено через деякий час</string>
|
||||
<string name="send_tron_account_activation_error">%1$s — це монета у мережі Tron. Щоб розрахувати комісію та здійснити транзакцію, вам необхідно внести певну кількість Tron(TRX) на свій рахунок.</string>
|
||||
<string name="send_tron_account_activation_error">%1$s — це монета у мережі Tron. Щоб розрахувати комісію та здійснити транзакцію, вам необхідно внести певну кількість Tron (TRX) на свій рахунок.</string>
|
||||
<string name="send_validation_invalid_address">Недійсна адреса</string>
|
||||
<string name="send_wallet_balance_format">%1$s (%2$s)</string>
|
||||
<string name="sent_transaction_sent_title">Трансакцію надіслано</string>
|
||||
|
|
@ -656,9 +659,8 @@
|
|||
<string name="staking_active">Активний</string>
|
||||
<string name="staking_active_footer">Щоб вивести активи зі стейкінгу, натисніть тут.</string>
|
||||
<string name="staking_amount_requirement_error">Сума для стейкінгу має бути не менше %s</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">Річний відсоток, який ви можете отримати, беручи участь у стейкінгу.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_apy">APY</string>
|
||||
<string name="staking_details_apy_info">Річний відсоток, який ви можете отримати, беручи участь у стейкінгу.</string>
|
||||
<string name="staking_details_available">Доступно</string>
|
||||
<string name="staking_details_average_reward_rate">Середня ставка винагороди</string>
|
||||
<string name="staking_details_estimated_profit">~ прибуток за %s</string>
|
||||
|
|
@ -666,7 +668,6 @@
|
|||
<string name="staking_details_metrics_block_header">Метрики</string>
|
||||
<string name="staking_details_minimum_requirement">Мінімальні вимоги</string>
|
||||
<string name="staking_details_no_rewards_to_claim">Немає винагород, щоб отримати</string>
|
||||
<string name="staking_details_on_stake">В стейкінгу</string>
|
||||
<string name="staking_details_reward_claiming">Отримати винагороду</string>
|
||||
<string name="staking_details_reward_claiming_info">Спосіб отримання винагороди за стейкінг. Його можна отримати автоматично або вручну.</string>
|
||||
<string name="staking_details_reward_schedule">Розклад винагород</string>
|
||||
|
|
@ -677,6 +678,7 @@
|
|||
<string name="staking_details_unbonding_period_info">Період, який ви повинні чекати після запиту на виведення коштів зі стейкінгу, перш ніж токени стануть доступними.</string>
|
||||
<string name="staking_details_warmup_period">Період блокування</string>
|
||||
<string name="staking_details_warmup_period_info">Відведений час для активації участі в стейкінгу.</string>
|
||||
<string name="staking_initial_info_title">Стейкінг %s</string>
|
||||
<string name="staking_native">Нативний стейкінг</string>
|
||||
<string name="staking_notification_earn_rewards_text" formatted="false">Стейкінг дозволяє заробляти %1s. Ваші винагороди за стейкінг надходять кожні ~%2s днів.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Отримуйте винагороду за стейкінг</string>
|
||||
|
|
@ -817,7 +819,7 @@
|
|||
<string name="warning_backup_errors_title">Помилка активації</string>
|
||||
<string name="warning_beacon_chain_retirement_content">За рішенням розробників мережі BNB стандарт BEP-2 перестане підтримуватись у червні 2024 року. Щоб не втратити свої активи, їх необхідно конвертувати у стандарт BEP-20. Використовуйте функцію обміну, щоб перевести їх у мережу BNB Smart Chain.</string>
|
||||
<string name="warning_beacon_chain_retirement_title">Відключення мережі BNB Beacon Chain</string>
|
||||
<string name="warning_button_could_be_better">Могло б бути краще</string>
|
||||
<string name="warning_button_could_be_better">Можна краще</string>
|
||||
<string name="warning_button_like_it">Вподобати</string>
|
||||
<string name="warning_button_ok">Зрозуміло!</string>
|
||||
<string name="warning_button_really_cool">Дуже круто!</string>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
<string name="alert_failed_to_send_transaction_message">原因:%s</string>
|
||||
<string name="alert_failed_to_send_transaction_title">無法發送交易</string>
|
||||
<string name="alert_manage_tokens_unsupported_message">此卡不支持%1$s網路上的代幣因為韌體限制</string>
|
||||
<string name="alert_negative_app_rate_sent_message">感謝您的反饋。我們會盡快回复</string>
|
||||
<string name="alert_negative_app_rate_sent_title">你的建議已送出</string>
|
||||
<string name="alert_troubleshooting_scan_card_message">請嘗試完全按照動畫中顯示的方式點擊卡片或請求支持</string>
|
||||
<string name="alert_troubleshooting_scan_card_title">有困難在掃描卡上嗎?</string>
|
||||
<string name="alert_unsupported_card">此卡不適用於此app</string>
|
||||
<string name="app_settings_enable_biometrics_description">轉到設置以在 Tangem App 中啟用生物識別身份驗證</string>
|
||||
|
|
@ -53,6 +56,7 @@
|
|||
<string name="common_create">創造</string>
|
||||
<string name="common_delete">刪除</string>
|
||||
<string name="common_disabled">禁用</string>
|
||||
<string name="common_disconnect">斷開連接</string>
|
||||
<string name="common_done">完成</string>
|
||||
<string name="common_enable">允許</string>
|
||||
<string name="common_enabled">啟用</string>
|
||||
|
|
@ -65,6 +69,7 @@
|
|||
<string name="common_origin_card">主卡片</string>
|
||||
<string name="common_reject">拒絕</string>
|
||||
<string name="common_rename">重新命名</string>
|
||||
<string name="common_retry">重試</string>
|
||||
<string name="common_save_changes">保存設置</string>
|
||||
<string name="common_search">搜索</string>
|
||||
<string name="common_search_tokens">搜尋代幣</string>
|
||||
|
|
@ -83,6 +88,7 @@
|
|||
<string name="common_transactions">交易</string>
|
||||
<string name="common_understand">我了解</string>
|
||||
<string name="common_unreachable">無法觸達</string>
|
||||
<string name="common_warning">警告</string>
|
||||
<string name="common_yes">是</string>
|
||||
<string name="contract_address_copied_message">已複製代幣地址</string>
|
||||
<string name="currency_subtitle_expanded">支持的網路</string>
|
||||
|
|
@ -115,6 +121,7 @@
|
|||
<string name="details_row_title_currency">App Currency</string>
|
||||
<string name="details_row_title_issuer">發行人</string>
|
||||
<string name="details_row_title_signed_hashes">簽署</string>
|
||||
<string name="details_security_management_warning">如果您忘記密碼,您將無法使用您的資金。無法恢復代碼</string>
|
||||
<string name="details_title">更多</string>
|
||||
<string name="disclaimer_error_loading">檢查您的網路連接或切換到其他網絡</string>
|
||||
<string name="disclaimer_title">服務條款</string>
|
||||
|
|
|
|||
|
|
@ -4,21 +4,15 @@
|
|||
<string name="add_custom_token_title">Add custom token</string>
|
||||
<string name="add_tokens_title">Manage tokens</string>
|
||||
<string name="address_qr_code_message_format">Send only %1$s (%2$s) from %3$s network to this address. Using other tokens and networks may result in loss of funds.</string>
|
||||
<string name="alert_app_feedback_sent_message">Thank you for your feedback</string>
|
||||
<string name="alert_app_feedback_sent_title">Sent successfully</string>
|
||||
<string name="alert_button_how_to_scan">How to scan</string>
|
||||
<string name="alert_button_request_support">Request support</string>
|
||||
<string name="alert_button_try_again">Try again</string>
|
||||
<string name="alert_demo_feature_disabled">This feature is disabled in Demo mode</string>
|
||||
<string name="alert_failed_to_send_email_title">Failed to send the email</string>
|
||||
<string name="alert_failed_to_send_transaction_message">Reason: %s</string>
|
||||
<string name="alert_failed_to_send_transaction_title">Can\'t send a transaction</string>
|
||||
<string name="alert_manage_tokens_unsupported_blockchain_by_card_message">The selected does not support the %1$s network</string>
|
||||
<string name="alert_manage_tokens_unsupported_curve_message">To activate the %1$s blockchain\'s cryptographic encryption, you\'ll need to reset the wallet to factory settings. Please withdraw your funds before doing so to ensure that you don\'t lose them, and then complete the reset process. Access to the current wallet will not be possible after the reset.</string>
|
||||
<string name="alert_manage_tokens_unsupported_message">Tokens in %1$s network are not supported by this card due to firmware limitation.</string>
|
||||
<string name="alert_negative_app_rate_sent_message">Thank you for your feedback. We will respond as soon as possible</string>
|
||||
<string name="alert_negative_app_rate_sent_title">Your suggestions were sent</string>
|
||||
<string name="alert_troubleshooting_scan_card_message">Please try to tap the card exactly as shown in the animation or read our simple guide, or request support. If the problem persists, please request support.</string>
|
||||
<string name="alert_troubleshooting_scan_card_title">Are you having difficulty scanning your card?</string>
|
||||
<string name="alert_unsupported_card">This card is not designed to work with this app</string>
|
||||
<string name="app_settings_default_fee">Default Fee</string>
|
||||
|
|
@ -34,12 +28,8 @@
|
|||
<string name="app_settings_theme_mode_dark">Dark</string>
|
||||
<string name="app_settings_theme_mode_light">Light</string>
|
||||
<string name="app_settings_theme_mode_system">System default</string>
|
||||
<string name="app_settings_theme_selection_footer">If system is selected, the app will auto-adjust based on your device\'s system settings</string>
|
||||
<string name="app_settings_theme_selection_system_short">System</string>
|
||||
<string name="app_settings_theme_selector_title">Theme</string>
|
||||
<string name="app_settings_title">App settings</string>
|
||||
<string name="app_settings_warning_subtitle">Go to settings to enable biometric authentication in the Tangem app</string>
|
||||
<string name="app_settings_warning_title">Enable biometric authentication</string>
|
||||
<string name="balance_hidden_description">To hide or show your balances, simply flip your device screen down, or switch it off in Settings</string>
|
||||
<string name="balance_hidden_do_not_show_button">Don\'t show again</string>
|
||||
<string name="balance_hidden_got_it_button">Got it</string>
|
||||
|
|
@ -48,7 +38,6 @@
|
|||
<string name="biometric_lockout_warning_description">Please try again in 30 seconds or scan the card</string>
|
||||
<string name="biometric_lockout_warning_title">Too many attempts</string>
|
||||
<string name="biometric_unavailable_warning">You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings.</string>
|
||||
<string name="biometry_touch_id_reason">Touch ID is used to save your cards in the app</string>
|
||||
<string name="button_start_backup_process">Start backup process</string>
|
||||
<string name="buy_token_description">With your bank card or bank account</string>
|
||||
<plurals name="card_label_card_count">
|
||||
|
|
@ -57,7 +46,6 @@
|
|||
</plurals>
|
||||
<string name="card_settings_access_code_recovery_disabled_description">Disable this option if you don\'t want this card to be used to reset access codes on other cards in this wallet. Please note that this will also prevent you from resetting the access code on this card.</string>
|
||||
<string name="card_settings_access_code_recovery_enabled_description">Allows you to use this card to reset access code on other cards in this wallet</string>
|
||||
<string name="card_settings_access_code_recovery_footer">Disable the ability to reset the access code on this card or other cards in this wallet</string>
|
||||
<string name="card_settings_access_code_recovery_title">Access code recovery</string>
|
||||
<string name="card_settings_action_sheet_reset">Reset</string>
|
||||
<string name="card_settings_action_sheet_title">Are you sure you want to do this?</string>
|
||||
|
|
@ -86,16 +74,13 @@
|
|||
<string name="common_approval">Approval</string>
|
||||
<string name="common_approve">Approve</string>
|
||||
<string name="common_attention">Attention</string>
|
||||
<string name="common_back">Back</string>
|
||||
<string name="common_balance">Balance: %s</string>
|
||||
<string name="common_balance_title">Balance</string>
|
||||
<string name="common_biometric_authentication">biometric authentication</string>
|
||||
<string name="common_biometrics">biometrics</string>
|
||||
<string name="common_buy">Buy</string>
|
||||
<string name="common_buy_currency">Go to %1$s</string>
|
||||
<string name="common_camera_alert_button_settings">Settings</string>
|
||||
<string name="common_camera_denied_alert_message">You have not given access to your camera, please adjust your privacy settings</string>
|
||||
<string name="common_camera_denied_alert_title">Camera access denied</string>
|
||||
<string name="common_cancel">Cancel</string>
|
||||
<string name="common_claim_rewards">Claim rewards</string>
|
||||
<string name="common_close">Close</string>
|
||||
|
|
@ -110,7 +95,6 @@
|
|||
</plurals>
|
||||
<string name="common_delete">Delete</string>
|
||||
<string name="common_disabled">Disabled</string>
|
||||
<string name="common_disconnect">Disconnect</string>
|
||||
<string name="common_done">Done</string>
|
||||
<string name="common_enable">Enable</string>
|
||||
<string name="common_enabled">Enabled</string>
|
||||
|
|
@ -129,7 +113,6 @@
|
|||
<string name="common_go_to_token">Go to token</string>
|
||||
<string name="common_import">Import</string>
|
||||
<string name="common_later">Later</string>
|
||||
<string name="common_learn_and_earn">Learn & Earn</string>
|
||||
<string name="common_locked">Locked</string>
|
||||
<string name="common_main_network">Main network</string>
|
||||
<string name="common_network_fee_title">Network fee</string>
|
||||
|
|
@ -142,14 +125,12 @@
|
|||
<string name="common_origin_card">Primary Card</string>
|
||||
<string name="common_passphrase">Passphrase</string>
|
||||
<string name="common_paste">Paste</string>
|
||||
<string name="common_push">Push</string>
|
||||
<string name="common_range">%1$s-%2$s</string>
|
||||
<string name="common_read_more">Read more</string>
|
||||
<string name="common_receive">Receive</string>
|
||||
<string name="common_reject">Reject</string>
|
||||
<string name="common_reload">Reload</string>
|
||||
<string name="common_rename">Rename</string>
|
||||
<string name="common_retry">Retry</string>
|
||||
<string name="common_save">Save</string>
|
||||
<string name="common_save_changes">Save changes</string>
|
||||
<string name="common_search">Search</string>
|
||||
|
|
@ -178,15 +159,12 @@
|
|||
<string name="common_unknown_error">There was an error. Please try again.</string>
|
||||
<string name="common_unreachable">Unreachable</string>
|
||||
<string name="common_unstake">Unstake</string>
|
||||
<string name="common_warning">Warning</string>
|
||||
<string name="common_yes">Yes</string>
|
||||
<string name="contract_address_copied_message">Contract address copied!</string>
|
||||
<string name="currency_subtitle_expanded">Available networks</string>
|
||||
<string name="custom_token_add_token">Add token</string>
|
||||
<string name="custom_token_contract_address_input_title">Contract address</string>
|
||||
<string name="custom_token_creation_error_empty_fields">Please fill in all the fields</string>
|
||||
<string name="custom_token_creation_error_invalid_contract_address">Contract address is invalid</string>
|
||||
<string name="custom_token_creation_error_invalid_derivation_path">Derivation path is invalid</string>
|
||||
<string name="custom_token_creation_error_network_not_selected">Please select the network</string>
|
||||
<string name="custom_token_creation_error_wrong_decimals">Decimal must be a valid integer, up to %li</string>
|
||||
<string name="custom_token_custom_derivation">Custom derivation</string>
|
||||
|
|
@ -216,7 +194,6 @@
|
|||
<string name="details_manage_security_access_code_description">You will have to submit the correct access code before scanning the card</string>
|
||||
<string name="details_manage_security_long_tap">Long Tap</string>
|
||||
<string name="details_manage_security_long_tap_description">This mechanism protects against proximity attacks on a card. It will enforce a delay between reception and execution of a command. </string>
|
||||
<string name="details_manage_security_long_tap_shorter">Long Tap</string>
|
||||
<string name="details_manage_security_passcode">Passcode</string>
|
||||
<string name="details_manage_security_passcode_description">Before executing any command entailing a change of the card state, you will have to enter the passcode.</string>
|
||||
<string name="details_referral_title">Referral program</string>
|
||||
|
|
@ -225,12 +202,10 @@
|
|||
<string name="details_row_title_cid">Card ID</string>
|
||||
<string name="details_row_title_contact_to_support">Contact support</string>
|
||||
<string name="details_row_title_create_backup">Link More Cards</string>
|
||||
<string name="details_row_title_create_backup_footer">You can synchronize up to three cards into one wallet. It can only be done once.</string>
|
||||
<string name="details_row_title_currency">App Currency</string>
|
||||
<string name="details_row_title_flip_to_hide">Flip-to-Hide Balances</string>
|
||||
<string name="details_row_title_issuer">Issuer</string>
|
||||
<string name="details_row_title_signed_hashes">Signed</string>
|
||||
<string name="details_security_management_warning">If you forget the code you will lose access to your funds. Code recovery is not possible.</string>
|
||||
<string name="details_send_feedback">Send feedback</string>
|
||||
<string name="details_title">Details</string>
|
||||
<string name="disclaimer_error_loading">Check your internet connection or switch to a different network</string>
|
||||
|
|
@ -240,7 +215,6 @@
|
|||
<string name="exchange_tokens_empty_tokens">You haven\'t added any tokens yet. Add tokens via Market to swap</string>
|
||||
<string name="exchange_tokens_unavailable_tokens_header">Cannot be swapped for %s</string>
|
||||
<string name="express_by_provider">Provided by</string>
|
||||
<string name="express_by_provider_placeholder">Provided by %s</string>
|
||||
<string name="express_cex_status_button_title">Status</string>
|
||||
<string name="express_choose_providers_subtitle">Tangem offers token swaps via 3rd-party providers according to each provider\'s terms</string>
|
||||
<string name="express_choose_providers_title">Choose provider</string>
|
||||
|
|
@ -304,7 +278,6 @@
|
|||
<string name="feedback_subject_support">Feedback</string>
|
||||
<string name="feedback_subject_support_tangem">Tangem feedback</string>
|
||||
<string name="feedback_subject_tx_failed">Can\'t send a transaction</string>
|
||||
<string name="feedback_subject_tx_push_failed">Can\'t push a transaction</string>
|
||||
<string name="give_permission_current_transaction">Current transaction</string>
|
||||
<string name="give_permission_fee_footer">The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap.</string>
|
||||
<string name="give_permission_policy_type_footer">Specify the approve limit for the selected token</string>
|
||||
|
|
@ -319,10 +292,8 @@
|
|||
<string name="initial_message_change_access_code_body">To change the access code tap the card as shown above and do not remove until the end of the operation</string>
|
||||
<string name="initial_message_change_passcode_body">To change the passcode tap the card as shown above and do not remove until the end of the operation</string>
|
||||
<string name="initial_message_create_wallet_body">To create the wallet tap the card as shown above and do not remove until the end of the operation</string>
|
||||
<string name="initial_message_purge_wallet_body">To reset to factory settings tap the card as shown above and do not remove until the end of the operation</string>
|
||||
<string name="initial_message_reset_backup_card_header">Tap the card #%s of the wallet</string>
|
||||
<string name="initial_message_scan_header">Tap to scan</string>
|
||||
<string name="initial_message_sign_body">To sign tap the card as shown above and do not remove until the end of the operation</string>
|
||||
<string name="initial_message_sign_header">Tap to sign</string>
|
||||
<string name="initial_message_tap_header">Tap the card</string>
|
||||
<string name="key_invalidated_warning_description">You have updated biometrics, scan your card to enter</string>
|
||||
|
|
@ -334,8 +305,6 @@
|
|||
<string name="koinos_mana_exceeds_koin_balance_title">Mana limit</string>
|
||||
<string name="koinos_mana_level_description">The Koinos network requires Mana for network fees. Your have %1$s/%2$s Mana</string>
|
||||
<string name="koinos_mana_level_title">Mana level</string>
|
||||
<string name="mail_error_no_accounts_body">Please, set up an account to send email</string>
|
||||
<string name="mail_error_no_accounts_title">No Mail accounts</string>
|
||||
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens</string>
|
||||
<string name="main_manage_tokens">Manage tokens</string>
|
||||
<string name="main_scan_card_warning_view_subtitle">To access all the networks you need to scan the card</string>
|
||||
|
|
@ -358,11 +327,6 @@
|
|||
<item quantity="one">%1$d of %2$d wallet</item>
|
||||
<item quantity="other">%1$d of %2$d wallets</item>
|
||||
</plurals>
|
||||
<string name="manage_tokens_number_of_wallets_ios">%d of %#@total_wallets@</string>
|
||||
<plurals name="manage_tokens_number_of_wallets_iostotal_wallets">
|
||||
<item quantity="one">%d wallet</item>
|
||||
<item quantity="other">%d wallets</item>
|
||||
</plurals>
|
||||
<string name="manage_tokens_remove">Remove</string>
|
||||
<string name="manage_tokens_search_placeholder">e.g. BTC I trust, hodl I must</string>
|
||||
<string name="manage_tokens_toast_portfolio_updated">Your portfolio has been updated</string>
|
||||
|
|
@ -454,7 +418,6 @@
|
|||
<string name="onboarding_activation_error_title">Activation error</string>
|
||||
<string name="onboarding_add_tokens">Add tokens</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">You\'ve added one backup card. When backup process is finished you can\'t add more backup cards. If you have one more card, add it to backup. Do you like to continue the backup process?</string>
|
||||
<string name="onboarding_alert_message_old_device">iPhone 7/7+ is not able to create a backup for Tangem Wallet due to some system limitations. Please use another phone to perform this operation. All other functions work stably.</string>
|
||||
<string name="onboarding_backup_exit_warning">The backup process is partly complete. You can\'t exit it now.</string>
|
||||
<string name="onboarding_bottom_sheet_passphrase_description">The passphrase is an advanced security feature that crypto wallets use. It adds an extra word or phrase of your own choosing to your already existing recovery phrase to unlock a brand-new set of addresses.</string>
|
||||
<string name="onboarding_button_add_backup_card">Add a backup card</string>
|
||||
|
|
@ -479,7 +442,6 @@
|
|||
<string name="onboarding_exit_alert_title">Do you want to exit the activation process?</string>
|
||||
<string name="onboarding_getting_started">Getting started</string>
|
||||
<string name="onboarding_linking_error_card_with_wallets">Another wallet has already been created on the card you\'re trying to add. If you have funds in this wallet, please withdraw it and then reset this card and add it as a backup.</string>
|
||||
<string name="onboarding_navbar_save_wallet">Save your wallet</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">Creating a backup</string>
|
||||
<string name="onboarding_seed_button_read_more">Read more about seed phrase</string>
|
||||
<plurals name="onboarding_seed_generate_message_words_count">
|
||||
|
|
@ -499,7 +461,6 @@
|
|||
<string name="onboarding_seed_mnemonic_invalid_checksum">Invalid seed phrase. Please check the word order.</string>
|
||||
<string name="onboarding_seed_mnemonic_wrong_words">Invalid seed phrase. Please check your spelling.</string>
|
||||
<string name="onboarding_seed_phrase_intro_legacy">Legacy</string>
|
||||
<string name="onboarding_seed_screenshot_alert">We do not recommend storing the seed phrase as a screenshot due to the high risk of loss or hacking</string>
|
||||
<string name="onboarding_seed_user_validation_message">To check whether you’ve written down your seed phrase correctly, please enter the 2nd, 7th and 11th words</string>
|
||||
<string name="onboarding_seed_user_validation_title">So, let’s check</string>
|
||||
<string name="onboarding_subtitle_no_backup_cards">To start the backup process add up to two backup cards.</string>
|
||||
|
|
@ -535,9 +496,6 @@
|
|||
<string name="organize_tokens_sort_by_balance">By balance</string>
|
||||
<string name="organize_tokens_title">Organize tokens</string>
|
||||
<string name="organize_tokens_ungroup">Ungroup</string>
|
||||
<string name="push_additional_fee">Additional fee</string>
|
||||
<string name="push_previous_fee">Previous fee</string>
|
||||
<string name="push_tx_address_hint">Previous transaction total including fee</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Select from the gallery</string>
|
||||
<string name="qr_scanner_camera_denied_settings_button">Settings</string>
|
||||
<string name="qr_scanner_camera_denied_text">You have not given access to your camera</string>
|
||||
|
|
@ -548,7 +506,6 @@
|
|||
<string name="referral_button_participate">Participate</string>
|
||||
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Error code: %s. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Error code: %s. Please try again later. If the problem persists — feel free to contact our support.</string>
|
||||
<string name="referral_expected_awards">Upcoming payments</string>
|
||||
<string name="referral_friends_bought_title">Your friends bought</string>
|
||||
<string name="referral_less">Less</string>
|
||||
|
|
@ -584,15 +541,11 @@
|
|||
<string name="russian_bank_card_warning_title">Russian bank cards are not currently accepted</string>
|
||||
<string name="save_user_wallet_agreement_access_description">Log into the app and check your balance without scanning the card</string>
|
||||
<string name="save_user_wallet_agreement_access_title">Access the app</string>
|
||||
<string name="save_user_wallet_agreement_allow">Allow to use %s</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">Allow to use biometrics</string>
|
||||
<string name="save_user_wallet_agreement_code_description">%s will be requested instead of the access code for interactions with your wallet</string>
|
||||
<string name="save_user_wallet_agreement_code_description_biometrics">Biometrics will be requested instead of the access code for interactions with your wallet</string>
|
||||
<string name="save_user_wallet_agreement_code_title">Access code</string>
|
||||
<string name="save_user_wallet_agreement_dont_allow">Don\'t allow</string>
|
||||
<string name="save_user_wallet_agreement_enroll_biometrics_description">It looks like you have biometric authentication disabled, it is necessary to save wallets</string>
|
||||
<string name="save_user_wallet_agreement_enroll_biometrics_title">Enable biometric authorization</string>
|
||||
<string name="save_user_wallet_agreement_header">Would you like to use %s?</string>
|
||||
<string name="save_user_wallet_agreement_header_biometrics">Would you like to use biometrics?</string>
|
||||
<string name="save_user_wallet_agreement_notice">Note that making a transaction with your funds will still require your card</string>
|
||||
<string name="scan_card_settings_button">Scan Card</string>
|
||||
|
|
@ -615,7 +568,6 @@
|
|||
<string name="send_date_format">%1$s, %2$s</string>
|
||||
<string name="send_destination_hint_address">Address</string>
|
||||
<string name="send_destination_tag_field">Destination Tag</string>
|
||||
<string name="send_dismiss_message">Are you sure you want to close the send screen?</string>
|
||||
<string name="send_enter_address_field">Enter address</string>
|
||||
<string name="send_error_address_same_as_wallet">Address is the same as wallet address</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Invalid Tag. It won\'t be added to the transaction.</string>
|
||||
|
|
@ -628,7 +580,6 @@
|
|||
<string name="send_fee_picker_priority">Priority</string>
|
||||
<string name="send_fee_unreachable_error_text">Check your network connection</string>
|
||||
<string name="send_fee_unreachable_error_title">Network fee info unreachable</string>
|
||||
<string name="send_from_wallet">From **%s**</string>
|
||||
<string name="send_from_wallet_android">From</string>
|
||||
<string name="send_gas_limit">Gas limit</string>
|
||||
<string name="send_gas_limit_footer">This is the maximum amount of gas that will be spent to complete a transaction or contract. A gas limit prevents unexpected or unlimited charges when executing a transaction.</string>
|
||||
|
|
@ -693,17 +644,17 @@
|
|||
<string name="staking_active_footer">To unstake your assets, click here.</string>
|
||||
<string name="staking_amount_requirement_error">The amount to stake must be at least %s</string>
|
||||
<string name="staking_claim_unstaked">Claim unstaked</string>
|
||||
<string name="staking_details_annual_percentage_rate">Annual percentage rate</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">The annual percentage return you can earn from participating in staking.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_apy">APY</string>
|
||||
<string name="staking_details_apy_info">The annual percentage return you can earn from participating in staking.</string>
|
||||
<string name="staking_details_available">Available</string>
|
||||
<string name="staking_details_average_reward_rate">Average Reward Rate</string>
|
||||
<string name="staking_details_banner_text">What is Staking?</string>
|
||||
<string name="staking_details_estimated_profit">%s est. profit</string>
|
||||
<string name="staking_details_market_rating">Market rating</string>
|
||||
<string name="staking_details_metrics_block_header">Metrics</string>
|
||||
<string name="staking_details_minimum_requirement">Minimum Requirement</string>
|
||||
<string name="staking_details_no_rewards_to_claim">No rewards to claim</string>
|
||||
<string name="staking_details_on_stake">On stake</string>
|
||||
<string name="staking_details_reward_claiming">Reward claiming</string>
|
||||
<string name="staking_details_reward_claiming_info">A way to receive staking rewards. It can be claimed automatically or manually.</string>
|
||||
<string name="staking_details_reward_schedule">Reward schedule</string>
|
||||
|
|
@ -719,6 +670,7 @@
|
|||
<string name="staking_native">Native staking</string>
|
||||
<string name="staking_notification_earn_rewards_text" formatted="false">Staking allow you to earn %1s. Your staking rewards arrive every ~%2s days.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Earn staking rewards</string>
|
||||
<string name="staking_notification_unstake_text">Rewards stop accruing immediately after you unstake. The unstaking process takes %s.</string>
|
||||
<string name="staking_rebond">Rebond</string>
|
||||
<string name="staking_restake">Restake</string>
|
||||
<string name="staking_restake_rewards">Restake rewards</string>
|
||||
|
|
@ -743,8 +695,6 @@
|
|||
<string name="story_currencies_title">Thousands of Currencies</string>
|
||||
<string name="story_finish_description">Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto.</string>
|
||||
<string name="story_finish_title">The Wallet for Everyone</string>
|
||||
<string name="story_learn_description">Take three lessons, get a discount on your Tangem Wallet, and receive 1INCH tokens to your wallet</string>
|
||||
<string name="story_learn_learn">Learn</string>
|
||||
<string name="story_meet_title">Meet Tangem</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_web3_title">Web 3.0 Compatible</string>
|
||||
|
|
@ -760,7 +710,6 @@
|
|||
<string name="swapping_high_price_impact_description">Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome.</string>
|
||||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_permission_header">Give Permission</string>
|
||||
<string name="swapping_permit_and_swap">Permit and Swap</string>
|
||||
<string name="swapping_success_view_title">In progress</string>
|
||||
<string name="swapping_swap_action">Swap</string>
|
||||
<string name="swapping_to_title">You receive</string>
|
||||
|
|
@ -778,7 +727,6 @@
|
|||
<string name="token_button_unavailability_reason_pending_transaction_send">Sending funds will be available once the pending transaction(s) in network %s is complete</string>
|
||||
<string name="token_button_unavailability_reason_sell_unavailable">Selling %s is not available at the moment. Please check our updates.</string>
|
||||
<string name="token_button_unavailability_reason_staking_unavailable">Staking %s is not available at the moment. Please check our updates.</string>
|
||||
<string name="token_details_choose_address">Choose address</string>
|
||||
<string name="token_details_generate_xpub">Generate XPUB</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
|
|
@ -826,13 +774,6 @@
|
|||
<string name="user_wallet_list_rename_popup_title">Rename Wallet</string>
|
||||
<string name="user_wallet_list_unlock_all">Unlock all</string>
|
||||
<string name="user_wallet_list_unlock_all_with">Unlock all with %s</string>
|
||||
<string name="voice_over_close_network_fee_settings">Close network fee settings</string>
|
||||
<string name="voice_over_nothing_to_paste">Nothing to paste from clipboard</string>
|
||||
<string name="voice_over_open_card_details">Open card details</string>
|
||||
<string name="voice_over_open_network_fee_settings">Open network fee settings</string>
|
||||
<string name="voice_over_open_new_wallet_connect_session">Scan QR code to open new WalletConnect session</string>
|
||||
<string name="voice_over_paste_from_clipboard">Paste address from clipboard</string>
|
||||
<string name="voice_over_scan_qr_with_address">Scan, QR, code with address</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Blockchain is unreachable. Try later</string>
|
||||
<string name="wallet_balance_missing_derivation">Scan the card</string>
|
||||
<string name="wallet_connect_alert_sign_message">Requesting to sign a message.\n\n%s</string>
|
||||
|
|
@ -911,8 +852,6 @@
|
|||
<string name="warning_hedera_missing_token_association_message_brief">This token must be associated with your Hedera account before you can receive it</string>
|
||||
<string name="warning_hedera_missing_token_association_title">Associate your token</string>
|
||||
<string name="warning_hedera_token_association_not_enough_hbar_message">Not enough %s. Top up your Hedera account to associate this token</string>
|
||||
<string name="warning_long_transaction_message">iPhone 7/7+ cannot sign transactions on this network. To complete this operation, please use a different phone.</string>
|
||||
<string name="warning_long_transaction_title">Transaction signing unavailable</string>
|
||||
<string name="warning_low_signatures_message">Only %s signatures are left on this card. You must withdraw all of your funds.</string>
|
||||
<string name="warning_low_signatures_title">Low signature count</string>
|
||||
<string name="warning_manage_tokens_legacy_derivation_message">Tokens on different networks can have different addresses. Double-check that your address matches the network when you transfer funds.</string>
|
||||
|
|
@ -928,10 +867,6 @@
|
|||
<string name="warning_no_backup_title">Missing backup</string>
|
||||
<string name="warning_number_of_signed_hashes_incorrect_message">This card has been previously used for transactions. If received from an untrusted source, consider withdrawing all funds. If it\'s your card, no action is required.</string>
|
||||
<string name="warning_number_of_signed_hashes_incorrect_title">Card has already signed transactions</string>
|
||||
<string name="warning_old_card_message">Funds in Tangem cards issued before September 2019 cannot be retrieved on iPhones due to iOS restrictions. Please use an Android phone for retrieval. Cards issued after September 2019 work correctly on both OS.</string>
|
||||
<string name="warning_old_card_title">iOS restriction for older cards</string>
|
||||
<string name="warning_old_device_old_card_message">Some iPhone 7/7+ models may have NFC issues during certain operations.</string>
|
||||
<string name="warning_old_device_old_card_title">Device incompatibility detected</string>
|
||||
<string name="warning_rate_app_message">Your review keeps us motivated to make Tangem Wallet even better</string>
|
||||
<string name="warning_rate_app_title">Enjoying Tangem?</string>
|
||||
<string name="warning_receive_blocked_hedera_token_association_required_message">You must associate your token before receiving tokens</string>
|
||||
|
|
@ -943,10 +878,6 @@
|
|||
<string name="warning_solana_rent_fee_message">Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Some networks currently are unreachable. Please try again later.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Some networks are unreachable</string>
|
||||
<string name="warning_system_deprecation_title">System update required</string>
|
||||
<string name="warning_system_deprecation_with_date_message">Support for your version of the operating system will end on %s. To receive future app updates you must update it to the latest version.</string>
|
||||
<string name="warning_system_update_message">Tangem recommends installing the latest iOS update for stable and safe operation</string>
|
||||
<string name="warning_system_update_title">System update available</string>
|
||||
<string name="warning_testnet_card_message">This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.</string>
|
||||
<string name="warning_testnet_card_title">For testing purposes only</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Discard</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
package com.tangem.core.ui.components.notifications
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.colorspace.ColorSpaces
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Currency notification component from Design system.
|
||||
*
|
||||
* @param config component config
|
||||
* @param modifier modifier
|
||||
* @param containerColor container color
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=3251-3466&t=eZb7ZPoNE8pQw45Y-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun CurrencyNotification(
|
||||
config: CurrencyNotificationConfig,
|
||||
modifier: Modifier = Modifier,
|
||||
containerColor: Color? = null,
|
||||
) {
|
||||
NotificationBaseContainer(
|
||||
buttonsState = config.buttonsState,
|
||||
onClick = null,
|
||||
onCloseClick = null,
|
||||
modifier = modifier,
|
||||
containerColor = containerColor,
|
||||
) {
|
||||
MainContent(
|
||||
tokenIconState = config.tokenIconState,
|
||||
title = config.title,
|
||||
subtitle = config.subtitle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainContent(
|
||||
tokenIconState: CurrencyIconState,
|
||||
title: TextReference,
|
||||
subtitle: CurrencyNotificationConfig.AnnotatedSubtitle,
|
||||
) {
|
||||
Row {
|
||||
CurrencyIcon(
|
||||
state = tokenIconState,
|
||||
modifier = Modifier.align(alignment = Alignment.CenterVertically),
|
||||
)
|
||||
|
||||
SpacerW(width = TangemTheme.dimens.spacing6)
|
||||
|
||||
TextsBlock(title = title, subtitle = subtitle)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TextsBlock(title: TextReference, subtitle: CurrencyNotificationConfig.AnnotatedSubtitle) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing2)) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.button,
|
||||
)
|
||||
|
||||
val subtitleValue = subtitle.valueProvider()
|
||||
ClickableText(
|
||||
text = subtitleValue,
|
||||
onClick = { subtitle.onClick(subtitleValue, it) },
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_Notification() {
|
||||
TangemThemePreview {
|
||||
CurrencyNotification(
|
||||
config = CurrencyNotificationConfig(
|
||||
title = resourceReference(
|
||||
R.string.express_exchange_notification_refund_title,
|
||||
wrappedList("USDT", "Polygon"),
|
||||
),
|
||||
subtitle = CurrencyNotificationConfig.AnnotatedSubtitle(
|
||||
valueProvider = {
|
||||
buildAnnotatedString {
|
||||
append("Your transaction amount was refunded in USDT to your wallet due to OKX")
|
||||
}
|
||||
},
|
||||
onClick = { _, _ -> },
|
||||
),
|
||||
tokenIconState = CurrencyIconState.TokenIcon(
|
||||
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/usd-coin.png",
|
||||
topBadgeIconResId = R.drawable.ic_polygon_22,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
fallbackTint = Color(1.0f, 1.0f, 1.0f, 1.0f, ColorSpaces.Srgb),
|
||||
fallbackBackground = Color(0.23529412f, 0.28627452f, 0.6117647f, 1.0f, ColorSpaces.Srgb),
|
||||
),
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = stringReference("Go to token"),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.core.ui.components.notifications
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
/**
|
||||
* Currency notification component state
|
||||
*
|
||||
* @property title title
|
||||
* @property subtitle subtitle
|
||||
* @property buttonsState buttons state
|
||||
* @property tokenIconState token icon state
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class CurrencyNotificationConfig(
|
||||
val title: TextReference,
|
||||
val subtitle: AnnotatedSubtitle,
|
||||
val tokenIconState: CurrencyIconState,
|
||||
val buttonsState: NotificationConfig.ButtonsState,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Subtitle as [AnnotatedString]
|
||||
*
|
||||
* @property valueProvider composable function that provides [AnnotatedString]
|
||||
* @property onClick lambda be invoked when text in specified position is clicked
|
||||
*/
|
||||
data class AnnotatedSubtitle(
|
||||
val valueProvider: @Composable () -> AnnotatedString,
|
||||
val onClick: (value: AnnotatedString, position: Int) -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -30,17 +30,19 @@ import com.tangem.core.ui.components.*
|
|||
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState as NotificationButtonsState
|
||||
|
||||
/**
|
||||
* Notification component from Design system.
|
||||
* Use this for Notification with title, subtitle, clickable or not.
|
||||
*
|
||||
* @param config component config
|
||||
* @param modifier modifier
|
||||
* @param iconTint icon tint
|
||||
* @param config component config
|
||||
* @param modifier modifier
|
||||
* @param containerColor container color
|
||||
* @param iconTint icon tint
|
||||
* @param isEnabled flag that defines if component is clickable
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=1045-807&t=6CVvYDJe0sB7wBKE-0"
|
||||
* >Figma component</a>
|
||||
|
|
@ -53,44 +55,33 @@ fun Notification(
|
|||
iconTint: Color? = null,
|
||||
isEnabled: Boolean = true,
|
||||
) {
|
||||
BaseContainer(
|
||||
NotificationBaseContainer(
|
||||
buttonsState = config.buttonsState,
|
||||
onClick = config.onClick,
|
||||
onCloseClick = config.onCloseClick,
|
||||
modifier = modifier,
|
||||
containerColor = containerColor,
|
||||
isEnabled = isEnabled,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
MainContent(
|
||||
iconResId = config.iconResId,
|
||||
iconTint = iconTint,
|
||||
title = config.title,
|
||||
subtitle = config.subtitle,
|
||||
isClickableComponent = isEnabled && config.onClick != null,
|
||||
)
|
||||
|
||||
Buttons(state = config.buttonsState, isEnabled = isEnabled)
|
||||
}
|
||||
|
||||
CloseableIconButton(
|
||||
onClick = config.onCloseClick,
|
||||
modifier = Modifier.align(alignment = Alignment.TopEnd),
|
||||
isEnabled = isEnabled,
|
||||
MainContent(
|
||||
iconResId = config.iconResId,
|
||||
iconTint = iconTint,
|
||||
title = config.title,
|
||||
subtitle = config.subtitle,
|
||||
isClickableComponent = isEnabled && config.onClick != null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BaseContainer(
|
||||
internal fun NotificationBaseContainer(
|
||||
buttonsState: NotificationConfig.ButtonsState?,
|
||||
onClick: (() -> Unit)?,
|
||||
onCloseClick: (() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
isEnabled: Boolean = true,
|
||||
containerColor: Color? = null,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
val tempContainerColor by rememberUpdatedState(
|
||||
newValue = if (buttonsState != null || onClick != null) {
|
||||
|
|
@ -109,7 +100,22 @@ private fun BaseContainer(
|
|||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
color = containerColor ?: tempContainerColor,
|
||||
) {
|
||||
Box(content = content)
|
||||
Box {
|
||||
Column(
|
||||
modifier = Modifier.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
content()
|
||||
|
||||
Buttons(state = buttonsState, isEnabled = isEnabled)
|
||||
}
|
||||
|
||||
CloseableIconButton(
|
||||
onClick = onCloseClick,
|
||||
modifier = Modifier.align(alignment = Alignment.TopEnd),
|
||||
isEnabled = isEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@ private fun LazyListScope.contentItems(
|
|||
when (item) {
|
||||
is TxHistoryState.TxHistoryItemState.GroupTitle -> item.itemKey
|
||||
is TxHistoryState.TxHistoryItemState.Title -> item.onExploreClick.hashCode()
|
||||
is TxHistoryState.TxHistoryItemState.Transaction -> item.state.txHash
|
||||
is TxHistoryState.TxHistoryItemState.Transaction ->
|
||||
item.state.txHash +
|
||||
((item.state as? TransactionState.Content)?.hashCode() ?: "")
|
||||
}
|
||||
},
|
||||
contentType = txHistoryItems.itemContentType { it::class.java },
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ object BigDecimalFormatter {
|
|||
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
|
||||
minimumFractionDigits = 2
|
||||
isGroupingUsed = true
|
||||
roundingMode = RoundingMode.DOWN
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(cryptoAmount).let {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import androidx.compose.runtime.MutableState
|
|||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.google.accompanist.permissions.shouldShowRationale
|
||||
|
||||
/**
|
||||
* Returns push permission requester.
|
||||
|
|
@ -16,22 +15,13 @@ import com.google.accompanist.permissions.shouldShowRationale
|
|||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun requestPushPermission(
|
||||
isFirstTimeAsking: Boolean,
|
||||
pushPermission: String?,
|
||||
isClicked: MutableState<Boolean>,
|
||||
onAllow: () -> Unit,
|
||||
onDeny: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
): () -> Unit {
|
||||
val permissionState = pushPermission?.let { permission ->
|
||||
val tempPermissionState = rememberPermissionState(permission = permission)
|
||||
rememberPermissionState(permission = permission) {
|
||||
when {
|
||||
it -> onAllow()
|
||||
!tempPermissionState.status.shouldShowRationale && !isFirstTimeAsking -> onOpenSettings()
|
||||
else -> onDeny()
|
||||
}
|
||||
}
|
||||
rememberPermissionState(permission = permission)
|
||||
}
|
||||
|
||||
// Check if user granted permission and close bottom sheet
|
||||
|
|
@ -45,7 +35,7 @@ fun requestPushPermission(
|
|||
}
|
||||
|
||||
return if (permissionState == null) {
|
||||
onOpenSettings
|
||||
{}
|
||||
} else {
|
||||
permissionState::launchPermissionRequest
|
||||
}
|
||||
|
|
|
|||
11
core/ui/src/main/res/drawable/ic_more_cards_24.xml
Normal file
11
core/ui/src/main/res/drawable/ic_more_cards_24.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M21,11V16H7V11H21ZM21,8V6H7V8H21ZM22.41,4.59C22.04,4.21 21.53,4 21,4H7C5.89,4 5,4.89 5,6V16C5,17.1 5.89,18 7,18H21C21.53,18 22.04,17.79 22.41,17.41C22.79,17.04 23,16.53 23,16V6C23,5.47 22.79,4.96 22.41,4.59ZM18,20H3V9H1V20C1,20.53 1.21,21.04 1.59,21.41C1.96,21.79 2.47,22 3,22H18V20Z" />
|
||||
</vector>
|
||||
|
|
@ -80,24 +80,10 @@ internal class DefaultCardRepository(
|
|||
return appPreferencesStore.getSyncOrDefault(key = PreferencesKeys.IS_TANGEM_TOS_ACCEPTED_KEY, default = false)
|
||||
}
|
||||
|
||||
override suspend fun isStart2CoinTOSAccepted(cardId: String): Boolean {
|
||||
return appPreferencesStore.getSyncOrDefault(
|
||||
key = PreferencesKeys.getStart2CoinTOSAcceptedKey(region = getRegion(cardId)),
|
||||
default = false,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun acceptTangemTOS() {
|
||||
return appPreferencesStore.store(key = PreferencesKeys.IS_TANGEM_TOS_ACCEPTED_KEY, true)
|
||||
}
|
||||
|
||||
override suspend fun acceptStart2CoinTOS(cardId: String) {
|
||||
appPreferencesStore.store(
|
||||
key = PreferencesKeys.getStart2CoinTOSAcceptedKey(region = getRegion(cardId)),
|
||||
value = true,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun AppPreferencesStore.editUsedCards(cardId: String, update: (UsedCardInfo) -> UsedCardInfo) {
|
||||
editData { mutablePreferences ->
|
||||
val usedCards = mutablePreferences.getUsedCards()
|
||||
|
|
@ -127,16 +113,5 @@ internal class DefaultCardRepository(
|
|||
.firstOrNull { it.cardId == cardId }
|
||||
}
|
||||
|
||||
private fun getRegion(cardId: String): String? {
|
||||
if (cardId.isEmpty()) return null
|
||||
|
||||
return when (cardId[1]) {
|
||||
'0' -> "fr"
|
||||
'1' -> "ch"
|
||||
'2' -> "at"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDefaultUsedCardInfo(cardId: String) = UsedCardInfo(cardId = cardId)
|
||||
}
|
||||
|
|
@ -33,19 +33,17 @@ internal class DefaultPromoRepository(
|
|||
}
|
||||
|
||||
override suspend fun getOkxPromoBanner(): PromoBanner? {
|
||||
// TODO disabled for 5.12, enable for 5.12.1
|
||||
return null
|
||||
// return runCatching(dispatchers.io) {
|
||||
// promoResponseConverter.convert(
|
||||
// tangemApi.getPromotionInfo(OKX)
|
||||
// .getOrThrow(),
|
||||
// )
|
||||
// }.getOrNull()
|
||||
return runCatching(dispatchers.io) {
|
||||
promoResponseConverter.convert(
|
||||
tangemApi.getPromotionInfo(OKX)
|
||||
.getOrThrow(),
|
||||
)
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val CHANGELLY_NAME = "changelly"
|
||||
private const val TRAVALA = "travala"
|
||||
// private const val OKX = "okx"
|
||||
private const val OKX = "okx"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,6 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import android.os.SystemClock
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.getIsFirstTimeAskingPermission
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.getPermissionDaysCount
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.getPermissionLaunchCount
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowInitialPermissionScreen
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowPermission
|
||||
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
||||
|
|
@ -30,47 +25,11 @@ internal class DefaultPermissionRepository(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun isFirstTimeAskingPermission(permission: String): Boolean =
|
||||
appPreferencesStore.getSyncOrDefault(
|
||||
key = getIsFirstTimeAskingPermission(permission),
|
||||
default = true,
|
||||
)
|
||||
|
||||
override suspend fun setFirstTimeAskingPermission(permission: String, value: Boolean) {
|
||||
appPreferencesStore.store(
|
||||
key = getIsFirstTimeAskingPermission(permission),
|
||||
value = value,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun shouldAskPermission(permission: String): Boolean {
|
||||
val shouldAskPermission = appPreferencesStore.getSyncOrDefault(getShouldShowPermission(permission), true)
|
||||
val delayedLaunches = appPreferencesStore.getSyncOrDefault(getPermissionLaunchCount(permission), 0)
|
||||
val delayedDays = appPreferencesStore.getSyncOrDefault(getPermissionDaysCount(permission), 0)
|
||||
val currentLaunchCounter = appPreferencesStore.getSyncOrDefault(PreferencesKeys.APP_LAUNCH_COUNT_KEY, 0)
|
||||
|
||||
val nowMillis = SystemClock.elapsedRealtime()
|
||||
val isDaysDelayed = delayedDays < nowMillis
|
||||
val isLaunchesDelayed = delayedLaunches < currentLaunchCounter
|
||||
return shouldAskPermission && isDaysDelayed && isLaunchesDelayed
|
||||
return appPreferencesStore.getSyncOrDefault(getShouldShowPermission(permission), true)
|
||||
}
|
||||
|
||||
override suspend fun neverAskPermission(permission: String) {
|
||||
appPreferencesStore.store(key = getShouldShowPermission(permission), value = false)
|
||||
}
|
||||
|
||||
override suspend fun delayPermissionAsking(permission: String) {
|
||||
appPreferencesStore.editData {
|
||||
val appLaunchCounter = it.getOrDefault(PreferencesKeys.APP_LAUNCH_COUNT_KEY, 0)
|
||||
val nowMillis = SystemClock.elapsedRealtime()
|
||||
|
||||
it[getPermissionLaunchCount(permission)] = appLaunchCounter + DELAY_LAUNCH_COUNT
|
||||
it[getPermissionDaysCount(permission)] = nowMillis + DELAY_DAYS_COUNT
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val DELAY_LAUNCH_COUNT = 5
|
||||
const val DELAY_DAYS_COUNT = 3L * 24 * 3600 * 1000 // 3 days in millis
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ import kotlinx.coroutines.withContext
|
|||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.FeePaidCurrency as FeePaidSdkCurrency
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
internal class DefaultCurrenciesRepository(
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val tangemExpressApi: TangemExpressApi,
|
||||
|
|
@ -53,6 +53,7 @@ internal class DefaultCurrenciesRepository(
|
|||
|
||||
private val demoConfig = DemoConfig()
|
||||
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory()
|
||||
private val cryptoCurrencyFactory = CryptoCurrencyFactory()
|
||||
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility()
|
||||
|
|
@ -61,18 +62,6 @@ internal class DefaultCurrenciesRepository(
|
|||
private val isMultiCurrencyWalletCurrenciesFetching = MutableStateFlow(
|
||||
value = emptyMap<UserWalletId, Boolean>(),
|
||||
)
|
||||
private val parallelTransactionsEnabledBlockchains = setOf(
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.EthereumTestnet,
|
||||
Blockchain.Polygon,
|
||||
Blockchain.PolygonTestnet,
|
||||
Blockchain.Arbitrum,
|
||||
Blockchain.ArbitrumTestnet,
|
||||
Blockchain.Binance,
|
||||
Blockchain.BinanceTestnet,
|
||||
Blockchain.Tron,
|
||||
Blockchain.TronTestnet,
|
||||
)
|
||||
|
||||
override suspend fun saveTokens(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -137,7 +126,7 @@ internal class DefaultCurrenciesRepository(
|
|||
return newTokens
|
||||
.filterNot { savedCurrencies.hasCoinForToken(it) } // tokens without coins
|
||||
.mapNotNull {
|
||||
CryptoCurrencyFactory().createCoin(
|
||||
cryptoCurrencyFactory.createCoin(
|
||||
blockchain = getBlockchain(networkId = it.network.id),
|
||||
extraDerivationPath = it.network.derivationPath.value,
|
||||
derivationStyleProvider = getUserWallet(userWalletId).scanResponse.derivationStyleProvider,
|
||||
|
|
@ -391,7 +380,8 @@ internal class DefaultCurrenciesRepository(
|
|||
val outgoingTransactions = cryptoCurrencyStatus.value.pendingTransactions.filter { it.isOutgoing }
|
||||
outgoingTransactions.isNotEmpty()
|
||||
}
|
||||
parallelTransactionsEnabledBlockchains.contains(blockchain) -> false
|
||||
blockchain.isEvm() -> false
|
||||
blockchain == Blockchain.Tron || blockchain == Blockchain.TronTestnet -> false
|
||||
else -> coinStatus?.value?.hasCurrentNetworkTransactions == true
|
||||
}
|
||||
}
|
||||
|
|
@ -426,12 +416,44 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
|
||||
override fun createTokenCurrency(cryptoCurrency: CryptoCurrency.Token, network: Network): CryptoCurrency.Token {
|
||||
return CryptoCurrencyFactory().createToken(
|
||||
return cryptoCurrencyFactory.createToken(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
network = network,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun createTokenCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: String,
|
||||
): CryptoCurrency.Token {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val token = withContext(dispatchers.io) {
|
||||
val foundToken = tangemTechApi.getCoins(
|
||||
contractAddress = contractAddress,
|
||||
networkId = networkId,
|
||||
)
|
||||
.getOrThrow()
|
||||
.coins
|
||||
.firstOrNull()
|
||||
?: error("Token not found")
|
||||
val network = foundToken.networks.firstOrNull { it.networkId == networkId } ?: error("Network not found")
|
||||
CryptoCurrencyFactory.Token(
|
||||
symbol = foundToken.symbol,
|
||||
name = foundToken.name,
|
||||
contractAddress = contractAddress,
|
||||
decimals = network.decimalCount?.toInt() ?: error("Decimals not found"),
|
||||
id = foundToken.id,
|
||||
)
|
||||
}
|
||||
return cryptoCurrencyFactory.createToken(
|
||||
token = token,
|
||||
networkId = networkId,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
) ?: error("Unable to create token")
|
||||
}
|
||||
|
||||
private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow<List<CryptoCurrency>> {
|
||||
return userTokensStore.get(userWallet.walletId).map { storedTokens ->
|
||||
responseCurrenciesFactory.createCurrencies(
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class CryptoCurrencyFactory {
|
|||
decimals = cryptoCurrency.decimals,
|
||||
id = cryptoCurrency.id.rawCurrencyId,
|
||||
)
|
||||
val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.id.value) ?: Blockchain.Unknown
|
||||
val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.backendId) ?: Blockchain.Unknown
|
||||
val id = getTokenId(network, sdkToken)
|
||||
return CryptoCurrency.Token(
|
||||
id = id,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import com.tangem.blockchain.blockchains.tron.TransactionType as SdkTransactionType
|
||||
|
||||
|
|
@ -41,7 +40,6 @@ internal class DefaultTransactionRepository(
|
|||
destination: String,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
isSwap: Boolean,
|
||||
txExtras: TransactionExtras?,
|
||||
hash: String?,
|
||||
): TransactionData.Uncompiled? = withContext(coroutineDispatcherProvider.io) {
|
||||
|
|
@ -58,7 +56,6 @@ internal class DefaultTransactionRepository(
|
|||
memo = memo,
|
||||
destination = destination,
|
||||
network = network,
|
||||
isSwap = isSwap,
|
||||
txExtras = txExtras,
|
||||
hash = hash,
|
||||
)
|
||||
|
|
@ -91,7 +88,6 @@ internal class DefaultTransactionRepository(
|
|||
memo = memo,
|
||||
destination = destination,
|
||||
network = network,
|
||||
isSwap = isSwap,
|
||||
txExtras = txExtras,
|
||||
hash = hash,
|
||||
)
|
||||
|
|
@ -158,23 +154,15 @@ internal class DefaultTransactionRepository(
|
|||
memo: String?,
|
||||
destination: String,
|
||||
network: Network,
|
||||
isSwap: Boolean,
|
||||
txExtras: TransactionExtras?,
|
||||
hash: String?,
|
||||
): TransactionData.Uncompiled {
|
||||
// TODO: refactor workaround to use general mechanism in bsdk for build tx for DEX
|
||||
val txAmount = if (isSwap) {
|
||||
createAmountForSwap(amount)
|
||||
} else {
|
||||
amount
|
||||
}
|
||||
|
||||
if (txExtras != null && memo != null) {
|
||||
// throw error for now to avoid programmers errors when use extras
|
||||
error("Both txExtras and memo provided, use only one of them")
|
||||
}
|
||||
val extras = txExtras ?: getMemoExtras(network.id.value, memo)
|
||||
return createTransaction(txAmount, fee, destination).copy(
|
||||
return createTransaction(amount, fee, destination).copy(
|
||||
hash = hash,
|
||||
extras = extras,
|
||||
)
|
||||
|
|
@ -203,19 +191,4 @@ internal class DefaultTransactionRepository(
|
|||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAmountForSwap(amount: Amount): Amount {
|
||||
return when (amount.type) {
|
||||
is AmountType.Coin -> amount
|
||||
else -> {
|
||||
// 1. when creates swap amount for NonNativeToken, amount should be ZERO
|
||||
// 2. Amount has .Coin type, as workaround to use destinationAddress in bsdk, not contractAddress
|
||||
Amount(
|
||||
currencySymbol = amount.currencySymbol,
|
||||
value = BigDecimal.ZERO,
|
||||
decimals = amount.decimals,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,6 +85,7 @@ internal class TxHistoryPagingSource(
|
|||
currency = sourceParams.currency,
|
||||
)
|
||||
.filterUnconfirmedTransaction()
|
||||
.sortedByDescending { it.timestampInMillis }
|
||||
.filterIfTxAlreadyAdded(apiItems = items)
|
||||
|
||||
return if (recentItems.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -26,10 +26,5 @@ interface CardRepository {
|
|||
@Throws
|
||||
suspend fun isTangemTOSAccepted(): Boolean
|
||||
|
||||
@Throws
|
||||
suspend fun isStart2CoinTOSAccepted(cardId: String): Boolean
|
||||
|
||||
suspend fun acceptTangemTOS()
|
||||
|
||||
suspend fun acceptStart2CoinTOS(cardId: String)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.exchange
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
/**
|
||||
|
|
@ -7,7 +8,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
*/
|
||||
interface RampStateManager {
|
||||
|
||||
fun availableForBuy(cryptoCurrency: CryptoCurrency): Boolean
|
||||
fun availableForBuy(scanResponse: ScanResponse, cryptoCurrency: CryptoCurrency): Boolean
|
||||
|
||||
fun availableForSell(cryptoCurrency: CryptoCurrency): Boolean
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||
|
||||
class DelayPermissionRequestUseCase(
|
||||
private val repository: PermissionRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(permission: String): Either<Throwable, Unit> = Either.catch {
|
||||
repository.delayPermissionAsking(permission)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||
|
||||
class IsFirstTimeAskingPermissionUseCase(private val repository: PermissionRepository) {
|
||||
|
||||
suspend operator fun invoke(permission: String): Either<Throwable, Boolean> = Either.catch {
|
||||
repository.isFirstTimeAskingPermission(permission)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||
|
||||
class SetFirstTimeAskingPermissionUseCase(private val repository: PermissionRepository) {
|
||||
|
||||
suspend operator fun invoke(permission: String): Either<Throwable, Unit> = Either.catch {
|
||||
repository.setFirstTimeAskingPermission(permission, false)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,18 +13,6 @@ interface PermissionRepository {
|
|||
*/
|
||||
suspend fun neverInitiallyShowPermissionScreen(permission: String)
|
||||
|
||||
/**
|
||||
* Indicates which time [permission] was asked via platform dialog.
|
||||
* NOTE: Use this method to indicate either reroute to settings or display platform dialog.
|
||||
*/
|
||||
suspend fun isFirstTimeAskingPermission(permission: String): Boolean
|
||||
|
||||
/**
|
||||
* Sets value indicating that [permission] was asked via platform dialog.
|
||||
* NOTE: Use this method to indicate either reroute to settings or display platform dialog.
|
||||
*/
|
||||
suspend fun setFirstTimeAskingPermission(permission: String, value: Boolean)
|
||||
|
||||
/**
|
||||
* Is clear to ask [permission].
|
||||
* User could already granted or permanently denied permission
|
||||
|
|
@ -36,9 +24,4 @@ interface PermissionRepository {
|
|||
* Permanently deny [permission] and never request again
|
||||
*/
|
||||
suspend fun neverAskPermission(permission: String)
|
||||
|
||||
/**
|
||||
* Delay next [permission] request for some time or active sessions
|
||||
*/
|
||||
suspend fun delayPermissionAsking(permission: String)
|
||||
}
|
||||
|
|
@ -79,13 +79,35 @@ class AddCryptoCurrenciesUseCase(
|
|||
.toNonEmptyListOrNull()
|
||||
?: return@either
|
||||
|
||||
catch({ currenciesRepository.addCurrencies(userWalletId, currenciesToAdd) }) {
|
||||
raise(it)
|
||||
}
|
||||
|
||||
addCurrencies(userWalletId, currenciesToAdd)
|
||||
refreshUpdatedNetworks(userWalletId, currenciesToAdd, existingCurrencies)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: String,
|
||||
): Either<Throwable, CryptoCurrency> = either {
|
||||
val existingCurrencies =
|
||||
catch({ currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId) }) {
|
||||
raise(it)
|
||||
}
|
||||
val foundToken = existingCurrencies
|
||||
.filterIsInstance<CryptoCurrency.Token>()
|
||||
.firstOrNull {
|
||||
it.network.backendId == networkId &&
|
||||
!it.isCustom &&
|
||||
it.contractAddress.equals(contractAddress, true)
|
||||
}
|
||||
if (foundToken != null) {
|
||||
return@either foundToken
|
||||
}
|
||||
val tokenToAdd = createTokenCurrency(userWalletId, contractAddress, networkId)
|
||||
addCurrencies(userWalletId, listOf(tokenToAdd))
|
||||
refreshUpdatedNetworks(userWalletId, listOf(tokenToAdd), existingCurrencies)
|
||||
tokenToAdd
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the network statuses for tokens that have corresponding coins in the
|
||||
* [existingCurrencies] list.
|
||||
|
|
@ -117,6 +139,33 @@ class AddCryptoCurrenciesUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<Throwable>.createTokenCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: String,
|
||||
): CryptoCurrency.Token {
|
||||
return catch(
|
||||
block = {
|
||||
currenciesRepository.createTokenCurrency(
|
||||
userWalletId = userWalletId,
|
||||
contractAddress = contractAddress,
|
||||
networkId = networkId,
|
||||
)
|
||||
},
|
||||
catch = {
|
||||
raise(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun Raise<Throwable>.addCurrencies(userWalletId: UserWalletId, tokens: List<CryptoCurrency>) {
|
||||
catch(
|
||||
{ currenciesRepository.addCurrencies(userWalletId, tokens) },
|
||||
) {
|
||||
raise(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the [existingCurrencies] list contains a coin that corresponds
|
||||
* to the given [token].
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
return listOf(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Unreachable) {
|
||||
return getActionsForUnreachableCurrency(cryptoCurrencyStatus, needAssociateAsset)
|
||||
return getActionsForUnreachableCurrency(userWallet, cryptoCurrencyStatus, needAssociateAsset)
|
||||
}
|
||||
|
||||
val activeList = mutableListOf<TokenActionsState.ActionState>()
|
||||
|
|
@ -168,7 +168,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
|
||||
// buy
|
||||
if (rampManager.availableForBuy(cryptoCurrency)) {
|
||||
if (rampManager.availableForBuy(userWallet.scanResponse, cryptoCurrency)) {
|
||||
activeList.add(TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
disabledList.add(
|
||||
|
|
@ -222,6 +222,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
|
||||
private fun getActionsForUnreachableCurrency(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
needAssociateAsset: Boolean,
|
||||
): List<TokenActionsState.ActionState> {
|
||||
|
|
@ -230,7 +231,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
actionsList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
if (rampManager.availableForBuy(cryptoCurrencyStatus.currency)) {
|
||||
if (rampManager.availableForBuy(userWallet.scanResponse, cryptoCurrencyStatus.currency)) {
|
||||
actionsList.add(TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
actionsList.add(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
/**
|
||||
* Repository for everything related to the tokens of user wallet
|
||||
* */
|
||||
@Suppress("TooManyFunctions")
|
||||
interface CurrenciesRepository {
|
||||
|
||||
/**
|
||||
|
|
@ -213,4 +214,13 @@ interface CurrenciesRepository {
|
|||
* Creates token [cryptoCurrency] based on current token and [network] it`s will be added
|
||||
*/
|
||||
fun createTokenCurrency(cryptoCurrency: CryptoCurrency.Token, network: Network): CryptoCurrency.Token
|
||||
|
||||
/**
|
||||
* Creates token [cryptoCurrency] based on [contractAddress] and [networkId] it`s will be added
|
||||
*/
|
||||
suspend fun createTokenCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: String,
|
||||
): CryptoCurrency.Token
|
||||
}
|
||||
|
|
@ -136,4 +136,12 @@ internal class MockCurrenciesRepository(
|
|||
override fun createTokenCurrency(cryptoCurrency: CryptoCurrency.Token, network: Network): CryptoCurrency.Token {
|
||||
return cryptoCurrency
|
||||
}
|
||||
|
||||
override suspend fun createTokenCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: String,
|
||||
): CryptoCurrency.Token {
|
||||
error("not implemented")
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ interface TransactionRepository {
|
|||
destination: String,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
isSwap: Boolean,
|
||||
txExtras: TransactionExtras?,
|
||||
hash: String?,
|
||||
): TransactionData.Uncompiled?
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ class CreateTransactionUseCase(
|
|||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
txExtras: TransactionExtras? = null,
|
||||
isSwap: Boolean = false,
|
||||
hash: String? = null,
|
||||
) = Either.catch {
|
||||
requireNotNull(
|
||||
|
|
@ -35,7 +34,6 @@ class CreateTransactionUseCase(
|
|||
destination = destination,
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
isSwap = isSwap,
|
||||
txExtras = txExtras,
|
||||
hash = hash,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.demo.DemoTransactionSender
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
|
@ -37,7 +38,10 @@ class SendTransactionUseCase(
|
|||
userWallet: UserWallet,
|
||||
network: Network,
|
||||
): Either<SendTransactionError?, String> {
|
||||
val signer = cardSdkConfigRepository.getCommonSigner(cardId = null)
|
||||
val card = userWallet.scanResponse.card
|
||||
val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins
|
||||
|
||||
val signer = cardSdkConfigRepository.getCommonSigner(cardId = card.cardId.takeIf { isCardNotBackedUp })
|
||||
|
||||
val linkedTerminal = cardSdkConfigRepository.isLinkedTerminal()
|
||||
if (userWallet.scanResponse.card.isStart2Coin) {
|
||||
|
|
@ -105,22 +109,7 @@ class SendTransactionUseCase(
|
|||
}
|
||||
val error = result.error as? BlockchainSdkError ?: return SendTransactionError.UnknownError()
|
||||
return when (error) {
|
||||
is BlockchainSdkError.WrappedTangemError -> {
|
||||
if (error.code == USER_CANCELLED_ERROR_CODE) {
|
||||
SendTransactionError.UserCancelledError
|
||||
} else {
|
||||
val tangemError = error.tangemError
|
||||
if (tangemError is TangemSdkError) {
|
||||
val resource = tangemError.localizedDescriptionRes()
|
||||
val resId = resource.resId ?: R.string.common_unknown_error
|
||||
val resArgs = resource.args.map { it.value }
|
||||
val textReference = resourceReference(resId, wrappedList(resArgs))
|
||||
SendTransactionError.TangemSdkError(tangemError.code, textReference)
|
||||
} else {
|
||||
SendTransactionError.BlockchainSdkError(error.code, tangemError.customMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
is BlockchainSdkError.WrappedTangemError -> parseWrappedError(error)
|
||||
is BlockchainSdkError.CreateAccountUnderfunded -> {
|
||||
val minAmount = error.minReserve
|
||||
val minValue = minAmount.value?.toFormattedString(minAmount.decimals).orEmpty()
|
||||
|
|
@ -134,4 +123,26 @@ class SendTransactionUseCase(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseWrappedError(error: BlockchainSdkError.WrappedTangemError): SendTransactionError {
|
||||
return if (error.code == USER_CANCELLED_ERROR_CODE) {
|
||||
SendTransactionError.UserCancelledError
|
||||
} else {
|
||||
when (val tangemError = error.tangemError) {
|
||||
is TangemSdkError -> {
|
||||
val resource = tangemError.localizedDescriptionRes()
|
||||
val resId = resource.resId ?: R.string.common_unknown_error
|
||||
val resArgs = resource.args.map { it.value }
|
||||
val textReference = resourceReference(resId, wrappedList(resArgs))
|
||||
SendTransactionError.TangemSdkError(tangemError.code, textReference)
|
||||
}
|
||||
is BlockchainSdkError.WrappedTangemError -> {
|
||||
parseWrappedError(tangemError) // todo remove when sdk errors are revised
|
||||
}
|
||||
else -> {
|
||||
SendTransactionError.BlockchainSdkError(error.code, tangemError.customMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,25 +8,32 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
|||
import com.tangem.domain.wallets.models.UpdateWalletError
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* Use case for rename user wallet
|
||||
*
|
||||
* @property userWalletsListManager user wallets list manager
|
||||
*/
|
||||
class RenameWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class RenameWalletUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, name: String): Either<UpdateWalletError, UserWallet> =
|
||||
either {
|
||||
val existingNames = userWalletsListManager.userWalletsSync
|
||||
withContext(dispatchers.io) {
|
||||
either {
|
||||
val existingNames = userWalletsListManager.userWalletsSync
|
||||
|
||||
ensure(existingNames.none { it.name == name && it.walletId != userWalletId }) {
|
||||
UpdateWalletError.NameAlreadyExists
|
||||
}
|
||||
ensure(existingNames.none { it.name == name && it.walletId != userWalletId }) {
|
||||
UpdateWalletError.NameAlreadyExists
|
||||
}
|
||||
|
||||
when (val result = userWalletsListManager.update(userWalletId) { it.copy(name = name) }) {
|
||||
is CompletionResult.Failure -> raise(UpdateWalletError.DataError(result.error))
|
||||
is CompletionResult.Success -> result.data
|
||||
when (val result = userWalletsListManager.update(userWalletId) { it.copy(name = name) }) {
|
||||
is CompletionResult.Failure -> raise(UpdateWalletError.DataError(result.error))
|
||||
is CompletionResult.Success -> result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ internal class ItemsBuilder @Inject constructor(
|
|||
DetailsItemUM.Basic.Item(
|
||||
id = "send_feedback",
|
||||
block = BlockUM(
|
||||
text = resourceReference(R.string.details_send_feedback),
|
||||
text = resourceReference(R.string.details_row_title_contact_to_support),
|
||||
iconRes = R.drawable.ic_comment_24,
|
||||
onClick = onClick,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ internal fun List<UserWallet>.toUiModels(
|
|||
isBalancesHidden: Boolean = false,
|
||||
): ImmutableList<UserWalletUM> = this.map { model ->
|
||||
val balance = balances[model.walletId]
|
||||
model.mapToUiModel(
|
||||
|
||||
model.toUiModel(
|
||||
balance = balance,
|
||||
appCurrency = appCurrency,
|
||||
isLoading = isLoading,
|
||||
|
|
@ -30,7 +31,7 @@ internal fun List<UserWallet>.toUiModels(
|
|||
)
|
||||
}.toImmutableList()
|
||||
|
||||
private fun UserWallet.mapToUiModel(
|
||||
private fun UserWallet.toUiModel(
|
||||
balance: TotalFiatBalance?,
|
||||
appCurrency: AppCurrency?,
|
||||
isLoading: Boolean,
|
||||
|
|
@ -66,9 +67,9 @@ private fun UserWallet.getInfo(
|
|||
)
|
||||
|
||||
return when {
|
||||
isBalanceHidden -> combinedReference(cardCountRef, dividerRef, stringReference(STARS))
|
||||
isLocked -> combinedReference(cardCountRef, dividerRef, resourceReference(R.string.common_locked))
|
||||
isLoading -> cardCountRef
|
||||
isBalanceHidden -> combinedReference(cardCountRef, dividerRef, stringReference(STARS))
|
||||
else -> getBalanceInfo(balance, appCurrency, cardCountRef, dividerRef)
|
||||
}
|
||||
}
|
||||
|
|
@ -80,7 +81,7 @@ private fun getBalanceInfo(
|
|||
dividerRef: TextReference,
|
||||
): TextReference {
|
||||
val amount = when (balance) {
|
||||
is TotalFiatBalance.Loaded -> balance.amount.takeIf { balance.isAllAmountsSummarized }
|
||||
is TotalFiatBalance.Loaded -> balance.amount
|
||||
is TotalFiatBalance.Failed,
|
||||
is TotalFiatBalance.Loading,
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
import com.tangem.features.disclaimer.api.components.DisclaimerComponent
|
||||
import com.tangem.features.disclaimer.impl.model.DisclaimerModel
|
||||
import com.tangem.features.disclaimer.impl.ui.DisclaimerScreen
|
||||
|
|
@ -18,7 +17,6 @@ import dagger.assisted.AssistedInject
|
|||
internal class DefaultDisclaimerComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted private val params: DisclaimerComponent.Params,
|
||||
private val appFinisher: AppFinisher,
|
||||
) : DisclaimerComponent, AppComponentContext by context {
|
||||
|
||||
private val model: DisclaimerModel = getOrCreateModel(params)
|
||||
|
|
@ -27,13 +25,7 @@ internal class DefaultDisclaimerComponent @AssistedInject constructor(
|
|||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler {
|
||||
if (params.isTosAccepted) {
|
||||
state.popBack()
|
||||
} else {
|
||||
appFinisher.finish()
|
||||
}
|
||||
}
|
||||
BackHandler(onBack = state.popBack)
|
||||
DisclaimerScreen(state = state)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.decompose.di.ComponentScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.settings.NeverRequestPermissionUseCase
|
||||
import com.tangem.domain.settings.NeverToInitiallyAskPermissionUseCase
|
||||
|
|
@ -17,12 +18,14 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class DisclaimerModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val cardRepository: CardRepository,
|
||||
private val router: Router,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase,
|
||||
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
|
||||
private val appFinisher: AppFinisher,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -33,23 +36,34 @@ internal class DisclaimerModel @Inject constructor(
|
|||
onAccept = ::onAccept,
|
||||
url = DISCLAIMER_URL,
|
||||
isTosAccepted = params.isTosAccepted,
|
||||
popBack = router::pop,
|
||||
popBack = ::popBack,
|
||||
),
|
||||
)
|
||||
|
||||
private fun onAccept(shouldAskPushPermission: Boolean) {
|
||||
modelScope.launch {
|
||||
private fun onAccept(shouldAskPushPermission: Boolean) = modelScope.launch {
|
||||
if (params.isTosAccepted) {
|
||||
router.pop()
|
||||
} else {
|
||||
cardRepository.acceptTangemTOS()
|
||||
|
||||
if (shouldAskPushPermission) {
|
||||
router.push(AppRoute.PushNotification)
|
||||
} else {
|
||||
neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION)
|
||||
neverRequestPermissionUseCase(PUSH_PERMISSION)
|
||||
router.push(AppRoute.Home)
|
||||
router.replaceAll(AppRoute.Home)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun popBack() {
|
||||
if (params.isTosAccepted) {
|
||||
router.pop()
|
||||
} else {
|
||||
appFinisher.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val DISCLAIMER_URL = "https://tangem.com/tangem_tos.html"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,19 +17,15 @@ sealed class PushNotificationAnalyticEvents(
|
|||
),
|
||||
)
|
||||
|
||||
data class ButtonLater(
|
||||
data class ButtonCancel(
|
||||
val source: AnalyticsParam.ScreensSources,
|
||||
) : PushNotificationAnalyticEvents(
|
||||
event = "Button - Later",
|
||||
event = "Button - Cancel",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to source.value,
|
||||
),
|
||||
)
|
||||
|
||||
data object ButtonCancel : PushNotificationAnalyticEvents(
|
||||
event = "Button - Cancel",
|
||||
)
|
||||
|
||||
data class PermissionStatus(
|
||||
val isAllowed: Boolean,
|
||||
) : PushNotificationAnalyticEvents(
|
||||
|
|
|
|||
|
|
@ -26,10 +26,9 @@ internal class PushNotificationsFragment : ComposeFragment() {
|
|||
NavigationBar3ButtonsScrim()
|
||||
PushNotificationsScreen(
|
||||
onRequest = viewModel::onRequest,
|
||||
onRequestLater = viewModel::onRequestLater,
|
||||
onNeverRequest = viewModel::onNeverRequest,
|
||||
onAllowPermission = viewModel::onAllowPermission,
|
||||
onDenyPermission = viewModel::onDenyPermission,
|
||||
onOpenSettings = viewModel::openSettings,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,18 +17,15 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
@Composable
|
||||
internal fun PushNotificationsScreen(
|
||||
onRequest: () -> Unit,
|
||||
onRequestLater: () -> Unit,
|
||||
onNeverRequest: () -> Unit,
|
||||
onAllowPermission: () -> Unit,
|
||||
onDenyPermission: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
) {
|
||||
val isClicked = remember { mutableStateOf(false) }
|
||||
val requestPushPermission = requestPushPermission(
|
||||
isFirstTimeAsking = true,
|
||||
isClicked = isClicked,
|
||||
onAllow = onAllowPermission,
|
||||
onDeny = onDenyPermission,
|
||||
onOpenSettings = onOpenSettings,
|
||||
pushPermission = getPushPermissionOrNull(),
|
||||
)
|
||||
|
||||
|
|
@ -54,8 +51,8 @@ internal fun PushNotificationsScreen(
|
|||
},
|
||||
),
|
||||
secondaryButton = ShowcaseButtonModel(
|
||||
buttonText = resourceReference(R.string.common_later),
|
||||
onClick = onRequestLater,
|
||||
buttonText = resourceReference(R.string.common_cancel),
|
||||
onClick = onNeverRequest,
|
||||
),
|
||||
modifier = Modifier.systemBarsPadding(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,11 +4,8 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.domain.settings.DelayPermissionRequestUseCase
|
||||
import com.tangem.domain.settings.NeverRequestPermissionUseCase
|
||||
import com.tangem.domain.settings.NeverToInitiallyAskPermissionUseCase
|
||||
import com.tangem.domain.settings.SetFirstTimeAskingPermissionUseCase
|
||||
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.features.pushnotifications.impl.navigation.DefaultPushNotificationsRouter
|
||||
|
|
@ -19,12 +16,9 @@ import javax.inject.Inject
|
|||
@Suppress("LongParameterList")
|
||||
@HiltViewModel
|
||||
internal class PushNotificationViewModel @Inject constructor(
|
||||
private val setFirstTimeAskingPermissionUseCase: SetFirstTimeAskingPermissionUseCase,
|
||||
private val delayPermissionRequestUseCase: DelayPermissionRequestUseCase,
|
||||
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
|
||||
private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase,
|
||||
private val router: DefaultPushNotificationsRouter,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val analyticHandler: AnalyticsEventHandler,
|
||||
) : ViewModel(), PushNotificationsClickIntents {
|
||||
|
||||
|
|
@ -32,18 +26,14 @@ internal class PushNotificationViewModel @Inject constructor(
|
|||
analyticHandler.send(
|
||||
PushNotificationAnalyticEvents.ButtonAllow(AnalyticsParam.ScreensSources.Stories),
|
||||
)
|
||||
viewModelScope.launch {
|
||||
setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestLater() {
|
||||
override fun onNeverRequest() {
|
||||
analyticHandler.send(
|
||||
PushNotificationAnalyticEvents.ButtonLater(AnalyticsParam.ScreensSources.Stories),
|
||||
PushNotificationAnalyticEvents.ButtonCancel(AnalyticsParam.ScreensSources.Stories),
|
||||
)
|
||||
viewModelScope.launch {
|
||||
delayPermissionRequestUseCase(PUSH_PERMISSION)
|
||||
setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION)
|
||||
neverRequestPermissionUseCase(PUSH_PERMISSION)
|
||||
neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION)
|
||||
router.openHome()
|
||||
}
|
||||
|
|
@ -65,11 +55,9 @@ internal class PushNotificationViewModel @Inject constructor(
|
|||
PushNotificationAnalyticEvents.PermissionStatus(isAllowed = false),
|
||||
)
|
||||
viewModelScope.launch {
|
||||
delayPermissionRequestUseCase(PUSH_PERMISSION)
|
||||
neverRequestPermissionUseCase(PUSH_PERMISSION)
|
||||
neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION)
|
||||
router.openHome()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openSettings() = settingsManager.openSettings()
|
||||
}
|
||||
|
|
@ -3,11 +3,9 @@ package com.tangem.features.pushnotifications.impl.presentation.viewmodel
|
|||
internal interface PushNotificationsClickIntents {
|
||||
fun onRequest()
|
||||
|
||||
fun onRequestLater()
|
||||
fun onNeverRequest()
|
||||
|
||||
fun onAllowPermission()
|
||||
|
||||
fun onDenyPermission()
|
||||
|
||||
fun openSettings()
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ dependencies {
|
|||
implementation(deps.arrow.core)
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.timber)
|
||||
implementation(deps.tangem.card.core)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.feature.referral.domain
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.domain.card.DerivePublicKeysUseCase
|
||||
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.referral.domain.errors.ReferralError
|
||||
import com.tangem.feature.referral.domain.models.ReferralData
|
||||
import com.tangem.feature.referral.domain.models.TokenData
|
||||
import com.tangem.lib.crypto.UserWalletManager
|
||||
|
|
@ -42,7 +44,7 @@ internal class ReferralInteractorImpl(
|
|||
val cryptoCurrency = repository.getCryptoCurrency(userWalletId = userWallet.walletId, tokenData = tokenData)
|
||||
derivePublicKeysUseCase(userWallet.walletId, listOfNotNull(cryptoCurrency)).getOrElse {
|
||||
Timber.e("Failed to derive public keys: $it")
|
||||
throw it
|
||||
throw it.mapToDomainError()
|
||||
}
|
||||
|
||||
addCryptoCurrenciesUseCase(
|
||||
|
|
@ -67,4 +69,13 @@ internal class ReferralInteractorImpl(
|
|||
tokensForReferral.clear()
|
||||
tokensForReferral.addAll(tokens)
|
||||
}
|
||||
|
||||
private fun Throwable.mapToDomainError(): ReferralError {
|
||||
if (this !is TangemSdkError) return ReferralError.DataError(this)
|
||||
return if (this is TangemSdkError.UserCancelled) {
|
||||
ReferralError.UserCancelledException
|
||||
} else {
|
||||
ReferralError.SdkError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.feature.referral.domain.errors
|
||||
|
||||
sealed class ReferralError : Exception() {
|
||||
data object UserCancelledException : ReferralError()
|
||||
data object SdkError : ReferralError()
|
||||
|
||||
data class DataError(val throwable: Throwable) : ReferralError()
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.referral.analytics.ReferralEvents
|
||||
import com.tangem.feature.referral.domain.ReferralInteractor
|
||||
import com.tangem.feature.referral.domain.errors.ReferralError
|
||||
import com.tangem.feature.referral.domain.models.DiscountType
|
||||
import com.tangem.feature.referral.domain.models.ReferralData
|
||||
import com.tangem.feature.referral.domain.models.ReferralInfo
|
||||
|
|
@ -21,7 +22,6 @@ import com.tangem.feature.referral.models.ReferralStateHolder
|
|||
import com.tangem.feature.referral.models.ReferralStateHolder.ErrorSnackbar
|
||||
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState
|
||||
import com.tangem.feature.referral.router.ReferralRouter
|
||||
import com.tangem.lib.crypto.models.errors.UserCancelledException
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -95,7 +95,7 @@ internal class ReferralViewModel @Inject constructor(
|
|||
runCatching(dispatchers.io) { referralInteractor.startReferral(userWalletId) }
|
||||
.onSuccess(::showContent)
|
||||
.onFailure { throwable ->
|
||||
if (throwable is UserCancelledException) {
|
||||
if (throwable is ReferralError.UserCancelledException) {
|
||||
lastReferralData?.let { referralData ->
|
||||
showContent(referralData)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,15 +20,10 @@ internal object InitialStakingStatePreview {
|
|||
endText = TextReference.Str("15 SOL"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_apy,
|
||||
startText = TextReference.Res(R.string.staking_details_apy),
|
||||
id = R.string.staking_details_annual_percentage_rate,
|
||||
startText = TextReference.Res(R.string.staking_details_annual_percentage_rate),
|
||||
endText = TextReference.Str("2.54-5.12%"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_on_stake,
|
||||
startText = TextReference.Res(R.string.staking_details_on_stake),
|
||||
endText = TextReference.Str("0 SOL"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_unbonding_period,
|
||||
startText = TextReference.Res(R.string.staking_details_unbonding_period),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.tangem.core.ui.extensions.*
|
|||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.staking.impl.R
|
||||
|
|
@ -22,7 +21,6 @@ import com.tangem.features.staking.impl.presentation.state.converters.YieldBalan
|
|||
import com.tangem.features.staking.impl.presentation.state.previewdata.ConfirmationStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
|
@ -91,7 +89,6 @@ internal class SetInitialDataStateTransformer(
|
|||
return listOfNotNull(
|
||||
createAvailableItem(cryptoCurrencyStatus),
|
||||
createApyItem(),
|
||||
createOnStakeItem(cryptoCurrencyStatus, yieldBalance),
|
||||
createUnbondingPeriodItem(),
|
||||
createMinimumRequirementItem(cryptoCurrencyStatus),
|
||||
createRewardClaimingItem(),
|
||||
|
|
@ -116,30 +113,13 @@ internal class SetInitialDataStateTransformer(
|
|||
|
||||
private fun createApyItem(): RoundedListWithDividersItemData {
|
||||
return RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_apy,
|
||||
startText = TextReference.Res(R.string.staking_details_apy),
|
||||
id = R.string.staking_details_annual_percentage_rate,
|
||||
startText = TextReference.Res(R.string.staking_details_annual_percentage_rate),
|
||||
endText = getAprRange(),
|
||||
iconClick = { clickIntents.onInfoClick(InfoType.APY) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun createOnStakeItem(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
yieldBalance: YieldBalance?,
|
||||
): RoundedListWithDividersItemData {
|
||||
return RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_on_stake,
|
||||
startText = TextReference.Res(R.string.staking_details_on_stake),
|
||||
endText = TextReference.Str(
|
||||
value = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = (yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero(),
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createUnbondingPeriodItem(): RoundedListWithDividersItemData {
|
||||
return RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_unbonding_period,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue