Updated on 2026-08-14
This commit is contained in:
parent
50f30e9bbf
commit
2a1369fce6
9 changed files with 223 additions and 0 deletions
|
|
@ -160,6 +160,7 @@ dependencies {
|
|||
implementation(projects.domain.hotWallet)
|
||||
implementation(projects.domain.news)
|
||||
implementation(projects.domain.earn)
|
||||
implementation(projects.domain.tokensync)
|
||||
|
||||
implementation(projects.common)
|
||||
implementation(projects.common.routing)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.tokensync.repository.TokenSyncRepository
|
||||
import com.tangem.domain.tokensync.usecase.AcknowledgeTokenSyncCompletionUseCase
|
||||
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
|
||||
import com.tangem.domain.tokensync.usecase.SyncTokensUseCase
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object TokenSyncDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideObserveTokenSyncUseCase(tokenSyncRepository: TokenSyncRepository): ObserveTokenSyncUseCase {
|
||||
return ObserveTokenSyncUseCase(
|
||||
tokenSyncRepository = tokenSyncRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAcknowledgeTokenSyncCompletionUseCase(
|
||||
tokenSyncRepository: TokenSyncRepository,
|
||||
): AcknowledgeTokenSyncCompletionUseCase {
|
||||
return AcknowledgeTokenSyncCompletionUseCase(
|
||||
tokenSyncRepository = tokenSyncRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSyncTokensUseCase(
|
||||
tokenSyncRepository: TokenSyncRepository,
|
||||
manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||
appCoroutineScope: AppCoroutineScope,
|
||||
): SyncTokensUseCase {
|
||||
return SyncTokensUseCase(
|
||||
tokenSyncRepository = tokenSyncRepository,
|
||||
manageCryptoCurrenciesUseCase = manageCryptoCurrenciesUseCase,
|
||||
appCoroutineScope = appCoroutineScope,
|
||||
)
|
||||
}
|
||||
}
|
||||
20
domain/tokensync/build.gradle.kts
Normal file
20
domain/tokensync/build.gradle.kts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.domain.tokensync"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.domain.core)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.account.status)
|
||||
implementation(projects.core.utils)
|
||||
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.arrow.core)
|
||||
implementation(deps.timber)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.domain.tokensync.model
|
||||
|
||||
sealed class TokenSyncProgress {
|
||||
|
||||
data object Idle : TokenSyncProgress()
|
||||
|
||||
data class InProgress(
|
||||
val completedNetworks: Int,
|
||||
val totalNetworks: Int,
|
||||
) : TokenSyncProgress() {
|
||||
val progressPercent: Int
|
||||
get() = if (totalNetworks > 0) {
|
||||
completedNetworks * 100 / totalNetworks
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
data object Completed : TokenSyncProgress()
|
||||
|
||||
data class Error(val cause: Throwable) : TokenSyncProgress()
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.domain.tokensync.repository
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokensync.model.TokenSyncProgress
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface TokenSyncRepository {
|
||||
|
||||
suspend fun runSync(userWalletId: UserWalletId)
|
||||
|
||||
suspend fun getPendingSyncWalletIds(): List<UserWalletId>
|
||||
|
||||
fun observeSyncProgress(userWalletId: UserWalletId): Flow<TokenSyncProgress>
|
||||
|
||||
fun acknowledgeCompletion(userWalletId: UserWalletId)
|
||||
|
||||
suspend fun clearPendingFlag(userWalletId: UserWalletId)
|
||||
|
||||
suspend fun getDiscoveredCurrencies(userWalletId: UserWalletId): List<CryptoCurrency>
|
||||
|
||||
suspend fun clearDiscoveredTokens(userWalletId: UserWalletId)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.domain.tokensync.usecase
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokensync.repository.TokenSyncRepository
|
||||
|
||||
class AcknowledgeTokenSyncCompletionUseCase(
|
||||
private val tokenSyncRepository: TokenSyncRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId) {
|
||||
tokenSyncRepository.acknowledgeCompletion(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.tokensync.usecase
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokensync.model.TokenSyncProgress
|
||||
import com.tangem.domain.tokensync.repository.TokenSyncRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ObserveTokenSyncUseCase(
|
||||
private val tokenSyncRepository: TokenSyncRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId): Flow<TokenSyncProgress> {
|
||||
return tokenSyncRepository.observeSyncProgress(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.tangem.domain.tokensync.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokensync.repository.TokenSyncRepository
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class SyncTokensUseCase(
|
||||
private val tokenSyncRepository: TokenSyncRepository,
|
||||
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||
private val appCoroutineScope: AppCoroutineScope,
|
||||
) {
|
||||
|
||||
private val activeSyncJobs = ConcurrentHashMap<UserWalletId, Job>()
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId) {
|
||||
activeSyncJobs[userWalletId]?.cancel()
|
||||
activeSyncJobs[userWalletId] = appCoroutineScope.launch {
|
||||
try {
|
||||
tokenSyncRepository.runSync(userWalletId)
|
||||
applyDiscoveredTokens(userWalletId)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Token sync failed for wallet: $userWalletId")
|
||||
} finally {
|
||||
activeSyncJobs.remove(userWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun cancel(userWalletId: UserWalletId): Either<Throwable, Unit> = Either.catch {
|
||||
activeSyncJobs.remove(userWalletId)?.cancel()
|
||||
tokenSyncRepository.clearPendingFlag(userWalletId)
|
||||
tokenSyncRepository.clearDiscoveredTokens(userWalletId)
|
||||
}
|
||||
|
||||
fun applyPendingSyncs() {
|
||||
appCoroutineScope.launch {
|
||||
try {
|
||||
val pendingIds = tokenSyncRepository.getPendingSyncWalletIds()
|
||||
for (walletId in pendingIds) {
|
||||
val isApplied = applyDiscoveredTokens(walletId)
|
||||
if (isApplied) {
|
||||
tokenSyncRepository.clearPendingFlag(walletId)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to apply pending syncs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun applyDiscoveredTokens(userWalletId: UserWalletId): Boolean {
|
||||
val currencies = tokenSyncRepository.getDiscoveredCurrencies(userWalletId)
|
||||
|
||||
if (currencies.isEmpty()) return true
|
||||
|
||||
val accountId = AccountId.forMainCryptoPortfolio(userWalletId)
|
||||
return manageCryptoCurrenciesUseCase(
|
||||
accountId = accountId,
|
||||
add = currencies,
|
||||
).fold(
|
||||
ifRight = {
|
||||
tokenSyncRepository.clearDiscoveredTokens(userWalletId)
|
||||
true
|
||||
},
|
||||
ifLeft = { error ->
|
||||
Timber.e("Failed to apply discovered tokens for wallet: $userWalletId, error: $error")
|
||||
false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -338,6 +338,7 @@ include(":domain:demo:models")
|
|||
include(":domain:settings")
|
||||
include(":domain:tokens")
|
||||
include(":domain:tokens:models")
|
||||
include(":domain:tokensync")
|
||||
include(":domain:wallets")
|
||||
include(":domain:wallets:models")
|
||||
include(":domain:txhistory")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue