Updated on 2026-08-14

This commit is contained in:
Tangem 2023-07-12 09:52:20 +03:00
parent 03aa966c1d
commit 80ac1d02c0
26 changed files with 421 additions and 2 deletions

View file

@ -28,6 +28,7 @@ dependencies {
implementation(project(":domain:card"))
implementation(project(":domain:wallets"))
implementation(project(":domain:wallets:models"))
implementation(projects.domain.tokens)
implementation(project(":common"))
implementation(project(":core:analytics"))
implementation(projects.core.analytics.models)
@ -40,6 +41,7 @@ dependencies {
implementation(project(":libs:crypto"))
implementation(project(":libs:auth"))
implementation(project(":data:source:preferences"))
implementation(projects.data.tokens)
/** Features */
implementation(project(":features:onboarding"))

View file

@ -0,0 +1,26 @@
package com.tangem.tap.di.domain
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.repository.TokensRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
@Module
@InstallIn(ViewModelComponent::class)
internal object TokensDomainModule {
@Provides
@ViewModelScoped
fun provideGetTokenListUseCase(
tokensRepository: TokensRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
) = GetTokenListUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers)
}

1
data/tokens/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,17 @@
plugins {
alias(deps.plugins.kotlin.jvm)
alias(deps.plugins.kotlin.kapt)
id("configuration")
}
dependencies {
implementation(projects.domain.tokens)
implementation(projects.domain.wallets.models)
implementation(deps.kotlin.coroutines)
implementation(deps.arrow.core)
implementation(deps.hilt.core)
kapt(deps.hilt.kapt)
}

View file

@ -0,0 +1,30 @@
package com.tangem.data.tokens.di
import com.tangem.data.tokens.repository.DefaultNetworksRepository
import com.tangem.data.tokens.repository.DefaultQuotesRepository
import com.tangem.data.tokens.repository.DefaultTokensRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.repository.TokensRepository
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 TokensDataModule {
@Provides
@Singleton
fun provideTokensRepository(): TokensRepository = DefaultTokensRepository()
@Provides
@Singleton
fun provideQuotesRepository(): QuotesRepository = DefaultQuotesRepository()
@Provides
@Singleton
fun provideNetworksRepository(): NetworksRepository = DefaultNetworksRepository()
}

View file

@ -0,0 +1,25 @@
package com.tangem.data.tokens.repository
import arrow.core.Either
import com.tangem.domain.tokens.error.TokensError
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.tokens.model.Token
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
internal class DefaultNetworksRepository : NetworksRepository {
override fun getNetworks(networksIds: Set<Network.ID>): Either<TokensError, Set<Network>> {
TODO("Not yet implemented")
}
override fun getNetworkStatuses(
userWalletId: UserWalletId,
networks: Map<Network.ID, Set<Token.ID>>,
refresh: Boolean,
): Flow<Either<TokensError, Set<NetworkStatus>>> {
TODO("Not yet implemented")
}
}

View file

@ -0,0 +1,15 @@
package com.tangem.data.tokens.repository
import arrow.core.Either
import com.tangem.domain.tokens.error.TokensError
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.Token
import com.tangem.domain.tokens.repository.QuotesRepository
import kotlinx.coroutines.flow.Flow
internal class DefaultQuotesRepository : QuotesRepository {
override fun getQuotes(tokensIds: Set<Token.ID>, refresh: Boolean): Flow<Either<TokensError, Set<Quote>>> {
TODO("Not yet implemented")
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.data.tokens.repository
import arrow.core.Either
import com.tangem.domain.tokens.error.TokensError
import com.tangem.domain.tokens.model.Token
import com.tangem.domain.tokens.repository.TokensRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
internal class DefaultTokensRepository : TokensRepository {
override suspend fun sortTokens(
userWalletId: UserWalletId,
sortedTokensIds: Set<Token.ID>,
isGrouped: Boolean,
isSortedByBalance: Boolean,
): Either<TokensError, Unit> {
TODO("Not yet implemented")
}
override fun getTokens(userWalletId: UserWalletId, refresh: Boolean): Flow<Either<TokensError, Set<Token>>> {
TODO("Not yet implemented")
}
override fun isTokensGrouped(userWalletId: UserWalletId): Flow<Either<TokensError, Boolean>> {
TODO("Not yet implemented")
}
override fun isTokensSortedByBalance(userWalletId: UserWalletId): Flow<Either<TokensError, Boolean>> {
TODO("Not yet implemented")
}
}

1
domain/tokens/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,10 @@
plugins {
alias(deps.plugins.kotlin.jvm)
id("configuration")
}
dependencies {
implementation(projects.domain.core)
implementation(projects.domain.wallets.models)
implementation(projects.core.utils)
}

View file

@ -0,0 +1,28 @@
// TODO: [REDACTED_JIRA]
@file:Suppress("unused", "unused_parameter")
package com.tangem.domain.tokens
import arrow.core.Either
import arrow.core.left
import com.tangem.domain.tokens.error.TokensError
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.repository.TokensRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
class GetTokenListUseCase(
private val tokensRepository: TokensRepository,
private val quotesRepository: QuotesRepository,
private val networksRepository: NetworksRepository,
private val dispatchers: CoroutineDispatcherProvider,
) {
operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = true): Flow<Either<TokensError, TokenList>> {
return flowOf(TokensError.EmptyTokens.left())
}
}

View file

@ -0,0 +1,6 @@
package com.tangem.domain.tokens.error
sealed class TokensError {
object EmptyTokens : TokensError()
}

View file

@ -0,0 +1,10 @@
package com.tangem.domain.tokens.model
data class Network(
val id: ID,
val name: String,
) {
@JvmInline
value class ID(val value: String)
}

View file

@ -0,0 +1,9 @@
package com.tangem.domain.tokens.model
import arrow.core.NonEmptySet
data class NetworkGroup(
val networkId: Network.ID,
val name: String,
val tokens: NonEmptySet<TokenStatus>,
)

View file

@ -0,0 +1,23 @@
package com.tangem.domain.tokens.model
import java.math.BigDecimal
data class NetworkStatus(
val networkId: Network.ID,
val value: Status,
) {
sealed class Status {
open val amounts: Map<Token.ID, BigDecimal>? = null
}
object Unreachable : Status()
object MissedDerivation : Status()
data class TransactionInProgress(override val amounts: Map<Token.ID, BigDecimal>) : Status()
data class Verified(override val amounts: Map<Token.ID, BigDecimal>) : Status()
data class NoAccount(val amountToCreateAccount: BigDecimal) : Status()
}

View file

@ -0,0 +1,9 @@
package com.tangem.domain.tokens.model
import java.math.BigDecimal
data class Quote(
val tokenId: Token.ID,
val fiatRate: BigDecimal,
val priceChange: BigDecimal,
)

View file

@ -0,0 +1,14 @@
package com.tangem.domain.tokens.model
data class Token(
val id: ID,
val networkId: Network.ID,
val name: String,
val symbol: String,
val isCustom: Boolean,
val isCoin: Boolean,
) {
@JvmInline
value class ID(val value: String)
}

View file

@ -0,0 +1,38 @@
package com.tangem.domain.tokens.model
import arrow.core.NonEmptySet
import java.math.BigDecimal
sealed class TokenList {
open val totalFiatBalance: FiatBalance = FiatBalance.Loading
open val sortedBy: SortType = SortType.NONE
data class GroupedByNetwork(
val groups: NonEmptySet<NetworkGroup>,
override val totalFiatBalance: FiatBalance,
override val sortedBy: SortType,
) : TokenList()
data class Ungrouped(
val tokens: NonEmptySet<TokenStatus>,
override val totalFiatBalance: FiatBalance,
override val sortedBy: SortType,
) : TokenList()
object NotInitialized : TokenList()
enum class SortType {
NONE, BALANCE,
}
sealed class FiatBalance {
object Loading : FiatBalance()
object Failed : FiatBalance()
data class Loaded(
val amount: BigDecimal,
val isAllAmountsSummarized: Boolean,
) : FiatBalance()
}
}

View file

@ -0,0 +1,42 @@
package com.tangem.domain.tokens.model
import java.math.BigDecimal
data class TokenStatus(
val id: Token.ID,
val networkId: Network.ID,
val name: String,
val symbol: String,
val isCoin: Boolean,
val value: Status,
) {
sealed class Status {
open val amount: BigDecimal? = null
open val fiatAmount: BigDecimal? = null
open val priceChange: BigDecimal? = null
open val hasTransactionsInProgress: Boolean = false
}
object Loading : Status()
object Unreachable : Status()
object MissedDerivation : Status()
object NoAccount : Status()
data class Loaded(
override val amount: BigDecimal,
override val fiatAmount: BigDecimal,
override val priceChange: BigDecimal,
override val hasTransactionsInProgress: Boolean,
) : Status()
data class Custom(
override val amount: BigDecimal,
override val fiatAmount: BigDecimal?,
override val priceChange: BigDecimal?,
override val hasTransactionsInProgress: Boolean,
) : Status()
}

View file

@ -0,0 +1,26 @@
package com.tangem.domain.tokens.repository
import arrow.core.Either
import com.tangem.domain.tokens.error.TokensError
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.tokens.model.Token
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
// FIXME: Use Raise as context instead of Effect when context receivers become stable
// [REDACTED_JIRA]
/**
* Repository for everything related to the blockchain networks
* */
interface NetworksRepository {
fun getNetworks(networksIds: Set<Network.ID>): Either<TokensError, Set<Network>>
fun getNetworkStatuses(
userWalletId: UserWalletId,
networks: Map<Network.ID, Set<Token.ID>>,
refresh: Boolean,
): Flow<Either<TokensError, Set<NetworkStatus>>>
}

View file

@ -0,0 +1,18 @@
package com.tangem.domain.tokens.repository
import arrow.core.Either
import com.tangem.domain.tokens.error.TokensError
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.Token
import kotlinx.coroutines.flow.Flow
// FIXME: Use Raise as context instead of Effect when context receivers become stable
// [REDACTED_JIRA]
/**
* Repository for everything related to the quotes of tokens
* */
interface QuotesRepository {
fun getQuotes(tokensIds: Set<Token.ID>, refresh: Boolean): Flow<Either<TokensError, Set<Quote>>>
}

View file

@ -0,0 +1,29 @@
package com.tangem.domain.tokens.repository
import arrow.core.Either
import com.tangem.domain.tokens.error.TokensError
import com.tangem.domain.tokens.model.Token
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
// FIXME: Use Raise as context instead of Effect when context receivers become stable
// [REDACTED_JIRA]
/**
* Repository for everything related to the tokens of user wallet
* */
interface TokensRepository {
suspend fun sortTokens(
userWalletId: UserWalletId,
sortedTokensIds: Set<Token.ID>,
isGrouped: Boolean,
isSortedByBalance: Boolean,
): Either<TokensError, Unit>
fun getTokens(userWalletId: UserWalletId, refresh: Boolean): Flow<Either<TokensError, Set<Token>>>
fun isTokensGrouped(userWalletId: UserWalletId): Flow<Either<TokensError, Boolean>>
fun isTokensSortedByBalance(userWalletId: UserWalletId): Flow<Either<TokensError, Boolean>>
}

View file

@ -50,4 +50,5 @@ dependencies {
implementation(project(":domain:models"))
implementation(project(":domain:wallets"))
implementation(project(":domain:wallets:models"))
implementation(projects.domain.tokens)
}

View file

@ -40,7 +40,8 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation
startDestination = WalletScreens.WALLET.name,
) {
composable(WalletScreens.WALLET.name) {
val viewModel = hiltViewModel<WalletViewModel>().apply { router = this@DefaultWalletRouter }
val viewModel = hiltViewModel<WalletViewModel>()
.apply { router = this@DefaultWalletRouter }
WalletScreen(state = viewModel.uiState)
}

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
@ -19,7 +20,10 @@ import kotlin.properties.Delegates
[REDACTED_AUTHOR]
*/
@HiltViewModel
internal class WalletViewModel @Inject constructor() : ViewModel() {
internal class WalletViewModel @Inject constructor(
@Suppress("unused") // TODO: [REDACTED_JIRA]
private val getTokenListUseCase: GetTokenListUseCase,
) : ViewModel() {
/** Feature router */
var router: InnerWalletRouter by Delegates.notNull()

View file

@ -81,8 +81,10 @@ include(":domain:core")
include(":domain:card")
include(":domain:wallets")
include(":domain:wallets:models")
include(":domain:tokens")
// endregion Domain modules
// region Data modules
include(":data:tokens")
include(":data:source:preferences")
// endregion Data modules