Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-02 19:26:30 +03:00
parent a08b715f36
commit 2d1f7a0cd2
18 changed files with 193 additions and 8 deletions

View file

@ -80,6 +80,7 @@ dependencies {
implementation(projects.features.wallet.impl)
implementation(projects.features.tokendetails.api)
implementation(projects.features.tokendetails.impl)
implementation(projects.features.send.api)
/** AndroidX libraries */
implementation(deps.androidx.core.ktx)

View file

@ -13,6 +13,9 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -47,4 +50,11 @@ internal object ActivityModule {
fun provideDefaultRampManager(appStateHolder: AppStateHolder): RampStateManager {
return DefaultRampManager(appStateHolder.exchangeService)
}
@Provides
@Singleton
@DelayedWork
fun provideActivityDelayedWorkCoroutineScope(): CoroutineScope {
return CoroutineScope(SupervisorJob() + Dispatchers.IO)
}
}

View file

@ -0,0 +1,8 @@
@file:Suppress("Filename")
package com.tangem.tap.di
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DelayedWork

View file

@ -173,4 +173,14 @@ internal object TokensDomainModule {
currenciesRepository = currenciesRepository,
)
}
@Provides
@ViewModelScoped
fun provideUpdateDelayedCurrencyStatusUseCase(
networksRepository: NetworksRepository,
): UpdateDelayedNetworkStatusUseCase {
return UpdateDelayedNetworkStatusUseCase(
networksRepository = networksRepository,
)
}
}

View file

@ -270,9 +270,7 @@ private fun sendTransaction(
dispatch(NavigationAction.PopBackTo())
}
scope.launch(Dispatchers.IO) {
updateWallet(walletManager)
delay(timeMillis = 11000) // more than 10000 to avoid throttling
updateWallet(walletManager)
updateAfterTransaction(walletManager)
}
}
is SimpleResult.Failure -> {
@ -414,6 +412,19 @@ private fun updateWarnings(dispatch: (Action) -> Unit) {
dispatch(SendAction.Warnings.Set(warnings))
}
private suspend fun updateAfterTransaction(walletManager: WalletManager) {
val walletFeatureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles)
if (!walletFeatureToggles.isRedesignedScreenEnabled) {
updateWalletsLegacy(walletManager)
}
}
private suspend fun updateWalletsLegacy(walletManager: WalletManager) {
updateWallet(walletManager)
delay(timeMillis = 11000) // more than 10000 to avoid throttling
updateWallet(walletManager)
}
private suspend fun updateWallet(walletManager: WalletManager) {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to update wallet, no user wallet selected")

View file

@ -53,6 +53,7 @@ private class SendReducer : SendInternalReducer {
is SendAction.Dialog.Hide -> sendState.copy(dialog = null)
is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList)
is SendAction.SendSpecificTransaction -> handleSendSpecificTransactionAction(action, sendState)
is SendAction.SendSuccess -> sendState.copy(isSuccessSend = true)
else -> return sendState
}

View file

@ -43,6 +43,7 @@ data class SendState(
val sendButtonState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.DISABLED),
val dialog: StateDialog? = null,
val externalTransactionData: ExternalTransactionData? = null,
val isSuccessSend: Boolean = false,
) : SendScreenState {
override val stateId: StateId = StateId.SEND_SCREEN

View file

@ -76,6 +76,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(viewModel)
sendSubscriber.initViewModel(viewModel)
Analytics.send(Token.Send.ScreenOpened())
}

View file

@ -3,23 +3,39 @@ package com.tangem.tap.features.send.ui
import androidx.lifecycle.*
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.send.navigation.SendRouter
import com.tangem.tap.di.DelayedWork
import com.tangem.tap.features.send.redux.AmountAction
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
@HiltViewModel
internal class SendViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val appStateHolder: AppStateHolder,
private val isBalanceHiddenUseCase: IsBalanceHiddenUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
@DelayedWork private val coroutineScope: CoroutineScope,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver {
private val cryptoCurrency: CryptoCurrency? = savedStateHandle[SendRouter.CRYPTO_CURRENCY_KEY]
override fun onCreate(owner: LifecycleOwner) {
isBalanceHiddenUseCase()
.flowWithLifecycle(owner.lifecycle)
@ -36,4 +52,24 @@ internal class SendViewModel @Inject constructor(
.collect()
}
}
fun updateCurrencyDelayed() {
if (cryptoCurrency != null) {
coroutineScope.launch {
getSelectedWalletUseCase()
.fold(
ifLeft = { Timber.e(it.toString()) },
ifRight = { wallet ->
updateDelayedCurrencyStatusUseCase(wallet.walletId, cryptoCurrency.network, true)
},
)
}
} else {
Timber.w("$TAG: cryptoCurrency is null, legacy flow")
}
}
companion object {
private const val TAG = "SendViewModel"
}
}

View file

@ -18,6 +18,7 @@ import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.send.ui.FeeUiHelper
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.SendViewModel
import com.tangem.tap.features.send.ui.dialogs.*
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.utils.ROUGH_SIGN
@ -29,13 +30,23 @@ import com.tangem.wallet.R
[REDACTED_AUTHOR]
*/
@Suppress("LargeClass")
class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber<SendState>(fragment) {
internal class SendStateSubscriber(
fragment: BaseStoreFragment,
) : FragmentStateSubscriber<SendState>(fragment) {
private var dialog: Dialog? = null
private var sendViewModel: SendViewModel? = null
fun initViewModel(viewModel: SendViewModel) {
sendViewModel = viewModel
}
override fun updateWithNewState(fg: BaseStoreFragment, state: SendState) {
fg.view ?: return
if (fg !is SendFragment) return
if (state.isSuccessSend) {
sendViewModel?.updateCurrencyDelayed()
return
}
val lastChangedStates = state.lastChangedStates.toList()
state.lastChangedStates.clear()

View file

@ -15,6 +15,7 @@ import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.feature.swap.presentation.SwapFragment
import com.tangem.features.send.navigation.SendRouter
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
@ -365,8 +366,8 @@ class TradeCryptoMiddleware {
)
}
}
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send))
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}
@ -415,7 +416,8 @@ class TradeCryptoMiddleware {
is CryptoCurrency.Token -> error("Action.tokenStatus.currency is Token")
}
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send))
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}
}

View file

@ -0,0 +1,59 @@
package com.tangem.domain.tokens
import arrow.core.Either
import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.delay
/**
* Use case responsible for fetching currency status information, including network status
* and quotes for a given cryptocurrency. It provides methods to fetch currency status either
* by providing a specific currency ID or fetching the status of the primary currency.
*
* @param networksRepository The repository for retrieving network-related data.
*/
class UpdateDelayedNetworkStatusUseCase(
private val networksRepository: NetworksRepository,
) {
/**
* Fetches the status of a specific cryptocurrency for a given user wallet.
*
* @param userWalletId The ID of the user's wallet.
* @param network Network of the cryptocurrency.
* @param refresh Indicates whether to force a refresh of the status data.
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
*/
suspend operator fun invoke(
userWalletId: UserWalletId,
network: Network,
refresh: Boolean = false,
): Either<CurrencyStatusError, Unit> {
delay(DELAY_MILLIS)
return either {
fetchNetworkStatus(userWalletId, network, refresh)
}
}
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(
userWalletId: UserWalletId,
network: Network,
refresh: Boolean,
) {
catch(
block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(network), refresh) },
) {
raise(CurrencyStatusError.DataError(it))
}
}
companion object {
private const val DELAY_MILLIS = 11000L
}
}

1
features/send/api/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,17 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
id("kotlin-parcelize")
id("configuration")
}
android {
namespace = "com.tangem.features.send.api"
}
dependencies {
implementation(projects.domain.tokens.models)
/** AndroidX */
implementation(deps.androidx.fragment.ktx)
}

View file

@ -0,0 +1,12 @@
package com.tangem.features.send.navigation
import androidx.fragment.app.Fragment
interface SendRouter {
fun getEntryFragment(): Fragment
companion object {
const val CRYPTO_CURRENCY_KEY = "send_crypto_currency"
}
}

View file

@ -66,4 +66,5 @@ dependencies {
/** Feature Apis */
implementation(projects.features.tokendetails.api)
implementation(projects.features.send.api)
}

View file

@ -69,4 +69,5 @@ dependencies {
/** Feature Apis */
implementation(projects.features.wallet.api)
implementation(projects.features.tokendetails.api)
implementation(projects.features.send.api)
}

View file

@ -91,6 +91,8 @@ include(":features:tokendetails:impl")
include(":features:learn2earn:api")
include(":features:learn2earn:impl")
include(":features:send:api")
// endregion Feature modules
// region Domain modules