Updated on 2026-08-14
This commit is contained in:
parent
c7941ee1f7
commit
2b327fd3e9
32 changed files with 853 additions and 0 deletions
1
domain/earn/.gitignore
vendored
Normal file
1
domain/earn/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
14
domain/earn/build.gradle.kts
Normal file
14
domain/earn/build.gradle.kts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
plugins {
|
||||
alias(deps.plugins.kotlin.jvm)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.domain.core)
|
||||
api(projects.domain.models)
|
||||
api(projects.core.pagination)
|
||||
implementation(projects.domain.common)
|
||||
implementation(projects.domain.networks)
|
||||
implementation(deps.kotlin.serialization)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.earn
|
||||
|
||||
import com.tangem.domain.models.earn.EarnError
|
||||
|
||||
interface EarnErrorResolver {
|
||||
|
||||
fun resolve(throwable: Throwable?): EarnError
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.earn.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Config of request earn tokens.
|
||||
*
|
||||
* @param type of Earn (ex: yield, staking).
|
||||
* @param network networkId.
|
||||
* @param isForEarn flag to load mostly used tokens.
|
||||
*/
|
||||
@Serializable
|
||||
data class EarnTokensListConfig(
|
||||
val type: String,
|
||||
val network: String,
|
||||
val isForEarn: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.domain.earn.model
|
||||
|
||||
import com.tangem.domain.models.earn.EarnTokenWithCurrency
|
||||
import com.tangem.pagination.BatchFlow
|
||||
import com.tangem.pagination.BatchingContext
|
||||
|
||||
typealias EarnTokensBatchingContext = BatchingContext<Int, EarnTokensListConfig, Nothing>
|
||||
|
||||
typealias EarnTokensBatchFlow = BatchFlow<Int, List<EarnTokenWithCurrency>, Nothing>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.domain.earn.repository
|
||||
|
||||
import com.tangem.domain.earn.model.EarnTokensBatchFlow
|
||||
import com.tangem.domain.earn.model.EarnTokensBatchingContext
|
||||
import com.tangem.domain.models.earn.EarnNetworks
|
||||
import com.tangem.domain.models.earn.EarnTopToken
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface EarnRepository {
|
||||
|
||||
fun getEarnTokensBatchFlow(context: EarnTokensBatchingContext, batchSize: Int): EarnTokensBatchFlow
|
||||
|
||||
/**
|
||||
* Load all networks for Earn and store it in data store.
|
||||
*/
|
||||
suspend fun fetchEarnNetworks(type: String)
|
||||
|
||||
fun observeEarnNetworks(): Flow<EarnNetworks>
|
||||
|
||||
/**
|
||||
* Fetch top N earn tokens by isForEarn = true and hold it in runtime store.
|
||||
*/
|
||||
suspend fun fetchTopEarnTokens(limit: Int)
|
||||
|
||||
fun observeTopEarnTokens(): Flow<EarnTopToken>
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.earn.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.earn.repository.EarnRepository
|
||||
|
||||
class FetchEarnNetworksUseCase(
|
||||
private val repository: EarnRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(type: String): Either<Throwable, Unit> {
|
||||
return Either.catch {
|
||||
repository.fetchEarnNetworks(type = type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.domain.earn.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.earn.repository.EarnRepository
|
||||
|
||||
class FetchTopEarnTokensUseCase(
|
||||
private val repository: EarnRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(limit: Int = DEFAULT_LIMIT): Either<Throwable, Unit> {
|
||||
return Either.catch {
|
||||
repository.fetchTopEarnTokens(
|
||||
limit = limit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val DEFAULT_LIMIT = 5
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.tangem.domain.earn.usecase
|
||||
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.earn.repository.EarnRepository
|
||||
import com.tangem.domain.models.earn.EarnNetwork
|
||||
import com.tangem.domain.models.earn.EarnNetworks
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusProducer
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
/**
|
||||
* Observes earn networks with [EarnNetwork.isAdded] enriched from user's wallets
|
||||
* via [multiNetworkStatusSupplier]. Single entry point for all/mine filtering.
|
||||
*/
|
||||
class GetEarnNetworksUseCase(
|
||||
private val earnRepository: EarnRepository,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
|
||||
) {
|
||||
|
||||
operator fun invoke(): Flow<EarnNetworks> {
|
||||
return combine(
|
||||
earnRepository.observeEarnNetworks(),
|
||||
observeMyNetworkIds(),
|
||||
) { earn, myNetworkIds ->
|
||||
earn.map { earnNetworks ->
|
||||
earnNetworks.map { network ->
|
||||
network.copy(isAdded = network.networkId in myNetworkIds)
|
||||
}
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun observeMyNetworkIds(): Flow<Set<String>> {
|
||||
return userWalletsListRepository.userWallets
|
||||
.map { it.orEmpty() }
|
||||
.flatMapLatest { wallets ->
|
||||
val activeWallets = wallets
|
||||
.filterNot(UserWallet::isLocked)
|
||||
.filter(UserWallet::isMultiCurrency)
|
||||
if (activeWallets.isEmpty()) {
|
||||
flowOf(emptySet())
|
||||
} else {
|
||||
val flows = activeWallets.map { wallet ->
|
||||
multiNetworkStatusSupplier(
|
||||
MultiNetworkStatusProducer.Params(userWalletId = wallet.walletId),
|
||||
).map { statuses -> statuses.map { it.network.backendId }.toSet() }
|
||||
}
|
||||
combine(flows) { arrays -> arrays.flatMap { it }.toSet() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.earn.usecase
|
||||
|
||||
import com.tangem.domain.earn.model.EarnTokensBatchFlow
|
||||
import com.tangem.domain.earn.model.EarnTokensBatchingContext
|
||||
import com.tangem.domain.earn.repository.EarnRepository
|
||||
|
||||
/**
|
||||
* Returns BatchFlow of Earn-tokens.
|
||||
*/
|
||||
class GetEarnTokensBatchFlowUseCase(
|
||||
private val repository: EarnRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(context: EarnTokensBatchingContext, batchSize: Int = DEFAULT_BATCH_SIZE): EarnTokensBatchFlow {
|
||||
return repository.getEarnTokensBatchFlow(
|
||||
context = context,
|
||||
batchSize = batchSize,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val DEFAULT_BATCH_SIZE = 20
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.earn.usecase
|
||||
|
||||
import com.tangem.domain.earn.repository.EarnRepository
|
||||
import com.tangem.domain.models.earn.EarnTopToken
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
class GetTopEarnTokensUseCase(
|
||||
private val repository: EarnRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(): Flow<EarnTopToken> {
|
||||
return repository.observeTopEarnTokens().distinctUntilChanged()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue