Updated on 2026-08-14
This commit is contained in:
parent
bc8a0ba20d
commit
a733dd0e52
13 changed files with 158 additions and 25 deletions
|
|
@ -11,6 +11,10 @@ import com.tangem.datasource.local.onramp.paymentmethods.DefaultOnrampPaymentMet
|
|||
import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.quotes.DefaultOnrampQuotesStore
|
||||
import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore
|
||||
import com.tangem.datasource.local.onramp.sepa.DefaultOnrampCurrentCountryByIPStore
|
||||
import com.tangem.datasource.local.onramp.sepa.DefaultOnrampSepaAvailabilityStore
|
||||
import com.tangem.datasource.local.onramp.sepa.OnrampCurrentCountryByIPStore
|
||||
import com.tangem.datasource.local.onramp.sepa.OnrampSepaAvailabilityStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -50,4 +54,16 @@ internal object OnrampStoreModule {
|
|||
fun provideOnrampCurrencies(): OnrampCurrenciesStore {
|
||||
return DefaultOnrampCurrenciesStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOnrampSepaAvailableStore(): OnrampSepaAvailabilityStore {
|
||||
return DefaultOnrampSepaAvailabilityStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOnrampCurrentCountryByIPStore(): OnrampCurrentCountryByIPStore {
|
||||
return DefaultOnrampCurrentCountryByIPStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.datasource.local.onramp.sepa
|
||||
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
|
||||
internal class DefaultOnrampCurrentCountryByIPStore(
|
||||
val dataStore: StringKeyDataStore<OnrampCountry>,
|
||||
) : OnrampCurrentCountryByIPStore, StringKeyDataStoreDecorator<Unit, OnrampCountry>(
|
||||
wrappedDataStore = dataStore,
|
||||
) {
|
||||
|
||||
override suspend fun getSyncOrNull(): OnrampCountry? {
|
||||
return getSyncOrNull(Unit)
|
||||
}
|
||||
|
||||
override suspend fun store(value: OnrampCountry) {
|
||||
return store(Unit, value)
|
||||
}
|
||||
|
||||
override fun provideStringKey(key: Unit) = "KEY"
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.datasource.local.onramp.sepa
|
||||
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
||||
|
||||
internal class DefaultOnrampSepaAvailabilityStore(
|
||||
val dataStore: StringKeyDataStore<Boolean>,
|
||||
) : OnrampSepaAvailabilityStore, StringKeyDataStoreDecorator<OnrampSepaAvailabilityStoreKey, Boolean>(
|
||||
wrappedDataStore = dataStore,
|
||||
) {
|
||||
override fun provideStringKey(key: OnrampSepaAvailabilityStoreKey) = with(key) {
|
||||
buildString {
|
||||
append(userWallet.walletId.toString())
|
||||
append("_")
|
||||
append(country.code)
|
||||
append("_")
|
||||
append(cryptoCurrency.id.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.datasource.local.onramp.sepa
|
||||
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
|
||||
interface OnrampCurrentCountryByIPStore {
|
||||
suspend fun getSyncOrNull(): OnrampCountry?
|
||||
suspend fun store(value: OnrampCountry)
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.datasource.local.onramp.sepa
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface OnrampSepaAvailabilityStore {
|
||||
suspend fun getSyncOrNull(key: OnrampSepaAvailabilityStoreKey): Boolean?
|
||||
fun get(key: OnrampSepaAvailabilityStoreKey): Flow<Boolean>
|
||||
suspend fun store(key: OnrampSepaAvailabilityStoreKey, value: Boolean)
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.local.onramp.sepa
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
|
||||
data class OnrampSepaAvailabilityStoreKey(
|
||||
val userWallet: UserWallet,
|
||||
val country: OnrampCountry,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
)
|
||||
|
|
@ -27,10 +27,12 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
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.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -216,7 +218,7 @@ internal fun TextsBlock(
|
|||
SpacerH(height = TangemTheme.dimens.spacing2)
|
||||
}
|
||||
|
||||
val subtitleText = subtitle.resolveReference()
|
||||
val subtitleText = subtitle.resolveAnnotatedReference()
|
||||
if (subtitleText.isNotEmpty()) {
|
||||
Text(
|
||||
text = subtitleText,
|
||||
|
|
@ -459,5 +461,15 @@ private class NotificationConfigProvider : CollectionPreviewParameterProvider<No
|
|||
subtitle = resourceReference(id = R.string.information_generated_with_ai),
|
||||
iconResId = R.drawable.ic_magic_28,
|
||||
),
|
||||
NotificationConfig(
|
||||
title = resourceReference(R.string.notification_sepa_title),
|
||||
subtitle = resourceReference(R.string.notification_sepa_text),
|
||||
iconResId = R.drawable.img_notification_sepa,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_sepa_button),
|
||||
onClick = { },
|
||||
),
|
||||
iconSize = 54.dp,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -30,6 +30,9 @@ import com.tangem.datasource.local.onramp.currencies.OnrampCurrenciesStore
|
|||
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
|
||||
import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore
|
||||
import com.tangem.datasource.local.onramp.sepa.OnrampCurrentCountryByIPStore
|
||||
import com.tangem.datasource.local.onramp.sepa.OnrampSepaAvailabilityStore
|
||||
import com.tangem.datasource.local.onramp.sepa.OnrampSepaAvailabilityStoreKey
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObject
|
||||
|
|
@ -64,6 +67,8 @@ internal class DefaultOnrampRepository(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val paymentMethodsStore: OnrampPaymentMethodsStore,
|
||||
private val onrampSepaAvailabilityStore: OnrampSepaAvailabilityStore,
|
||||
private val onrampCurrentCountryByIPStore: OnrampCurrentCountryByIPStore,
|
||||
private val pairsStore: OnrampPairsStore,
|
||||
private val quotesStore: OnrampQuotesStore,
|
||||
private val countriesStore: OnrampCountriesStore,
|
||||
|
|
@ -123,8 +128,17 @@ internal class DefaultOnrampRepository(
|
|||
result
|
||||
}
|
||||
|
||||
override suspend fun getCountryByIp(userWallet: UserWallet): OnrampCountry = withContext(dispatchers.io) {
|
||||
onrampApi.getCountryByIp(
|
||||
override suspend fun getCountryByIp(userWallet: UserWallet, fromCache: Boolean): OnrampCountry =
|
||||
withContext(dispatchers.io) {
|
||||
if (fromCache) {
|
||||
val country = onrampCurrentCountryByIPStore.getSyncOrNull()
|
||||
|
||||
if (country != null) {
|
||||
return@withContext country
|
||||
}
|
||||
}
|
||||
|
||||
val country = onrampApi.getCountryByIp(
|
||||
userWalletId = userWallet.walletId.stringValue,
|
||||
refCode = ExpressUtils.getRefCode(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -133,6 +147,10 @@ internal class DefaultOnrampRepository(
|
|||
)
|
||||
.getOrThrow()
|
||||
.let(countryConverter::convert)
|
||||
|
||||
onrampCurrentCountryByIPStore.store(country)
|
||||
|
||||
country
|
||||
}
|
||||
|
||||
override suspend fun getStatus(userWallet: UserWallet, txId: String): OnrampStatus = withContext(dispatchers.io) {
|
||||
|
|
@ -263,11 +281,22 @@ internal class DefaultOnrampRepository(
|
|||
|
||||
override suspend fun hasSepaMethod(
|
||||
userWallet: UserWallet,
|
||||
currency: OnrampCurrency,
|
||||
country: OnrampCountry,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Boolean {
|
||||
return withContext(dispatchers.io) {
|
||||
val key = OnrampSepaAvailabilityStoreKey(
|
||||
userWallet = userWallet,
|
||||
country = country,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
)
|
||||
|
||||
val cachedValue = onrampSepaAvailabilityStore.getSyncOrNull(key)
|
||||
|
||||
if (cachedValue != null) {
|
||||
return@withContext cachedValue
|
||||
}
|
||||
|
||||
val onrampPairs =
|
||||
safeApiCall(
|
||||
call = {
|
||||
|
|
@ -300,6 +329,8 @@ internal class DefaultOnrampRepository(
|
|||
.flatMap { it.paymentMethods }
|
||||
.any { it == SEPA_METHOD_ID }
|
||||
|
||||
onrampSepaAvailabilityStore.store(key, hasSepaMethod)
|
||||
|
||||
hasSepaMethod
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import com.tangem.datasource.local.onramp.currencies.OnrampCurrenciesStore
|
|||
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
|
||||
import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore
|
||||
import com.tangem.datasource.local.onramp.sepa.OnrampCurrentCountryByIPStore
|
||||
import com.tangem.datasource.local.onramp.sepa.OnrampSepaAvailabilityStore
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
|
|
@ -52,6 +54,8 @@ internal object OnrampDataModule {
|
|||
currenciesStore: OnrampCurrenciesStore,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
dataSignatureVerifier: DataSignatureVerifier,
|
||||
onrampSepaAvailabilityStore: OnrampSepaAvailabilityStore,
|
||||
onrampCurrentCountryByIPStore: OnrampCurrentCountryByIPStore,
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
): OnrampRepository {
|
||||
return DefaultOnrampRepository(
|
||||
|
|
@ -60,6 +64,8 @@ internal object OnrampDataModule {
|
|||
dispatchers = dispatchers,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
paymentMethodsStore = paymentMethodsStore,
|
||||
onrampSepaAvailabilityStore = onrampSepaAvailabilityStore,
|
||||
onrampCurrentCountryByIPStore = onrampCurrentCountryByIPStore,
|
||||
pairsStore = pairsStore,
|
||||
quotesStore = quotesStore,
|
||||
currenciesStore = currenciesStore,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,12 @@ class GetOnrampCountryUseCase(
|
|||
}
|
||||
|
||||
suspend fun invokeSync(userWallet: UserWallet): Either<OnrampError, OnrampCountry> {
|
||||
return Either.catch { repository.getDefaultCountrySync() ?: repository.getCountryByIp(userWallet) }
|
||||
return Either.catch {
|
||||
repository.getDefaultCountrySync() ?: repository.getCountryByIp(
|
||||
userWallet = userWallet,
|
||||
fromCache = true,
|
||||
)
|
||||
}
|
||||
.mapLeft(errorResolver::resolve)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
import com.tangem.domain.onramp.model.OnrampCurrency
|
||||
|
||||
class OnrampSepaAvailableUseCase(
|
||||
private val repository: OnrampRepository,
|
||||
|
|
@ -14,7 +13,6 @@ class OnrampSepaAvailableUseCase(
|
|||
|
||||
suspend operator fun invoke(
|
||||
userWallet: UserWallet,
|
||||
currency: OnrampCurrency,
|
||||
country: OnrampCountry,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Boolean {
|
||||
|
|
@ -25,7 +23,6 @@ class OnrampSepaAvailableUseCase(
|
|||
return Either.catch {
|
||||
repository.hasSepaMethod(
|
||||
userWallet = userWallet,
|
||||
currency = currency,
|
||||
country = country,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,14 +13,9 @@ interface OnrampRepository {
|
|||
fun getCurrencies(): Flow<List<OnrampCurrency>>
|
||||
fun getCountries(): Flow<List<OnrampCountry>>
|
||||
suspend fun getCountriesSync(): List<OnrampCountry>?
|
||||
suspend fun getCountryByIp(userWallet: UserWallet): OnrampCountry
|
||||
suspend fun getCountryByIp(userWallet: UserWallet, fromCache: Boolean = false): OnrampCountry
|
||||
suspend fun getStatus(userWallet: UserWallet, txId: String): OnrampStatus
|
||||
suspend fun hasSepaMethod(
|
||||
userWallet: UserWallet,
|
||||
currency: OnrampCurrency,
|
||||
country: OnrampCountry,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Boolean
|
||||
suspend fun hasSepaMethod(userWallet: UserWallet, country: OnrampCountry, cryptoCurrency: CryptoCurrency): Boolean
|
||||
suspend fun fetchCurrencies(userWallet: UserWallet)
|
||||
suspend fun fetchCountries(userWallet: UserWallet): List<OnrampCountry>
|
||||
suspend fun fetchPaymentMethodsIfAbsent(userWallet: UserWallet)
|
||||
|
|
|
|||
|
|
@ -254,7 +254,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
val isSepaAvailable = onrampSepaAvailableUseCase(
|
||||
userWallet = userWallet,
|
||||
country = country,
|
||||
currency = country.defaultCurrency,
|
||||
cryptoCurrency = bitcoinCurrency,
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue