Updated on 2026-08-14
This commit is contained in:
parent
258af22842
commit
eeec58e676
47 changed files with 658 additions and 123 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||
import com.tangem.domain.markets.GetTokenPriceChartUseCase
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -19,4 +20,10 @@ object MarketsDomainModule {
|
|||
): GetMarketsTokenListFlowUseCase {
|
||||
return GetMarketsTokenListFlowUseCase(marketsTokenRepository = marketsTokenRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetTokenPriceChartUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenPriceChartUseCase {
|
||||
return GetTokenPriceChartUseCase(marketsTokenRepository = marketsTokenRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -70,9 +70,9 @@ private const val GUIDELINES_COUNT = 3
|
|||
fun MarketChart(
|
||||
modifier: Modifier = Modifier,
|
||||
state: MarketChartState = rememberMarketChartState(),
|
||||
splitChartSegmentColor: Color,
|
||||
@FloatRange(from = 0.0, to = 1.0) backgroundSplitChartSegmentColorAlpha: Float,
|
||||
@FloatRange(from = 0.0, to = 1.0) backgroundColorAlpha: Float,
|
||||
splitChartSegmentColor: Color = TangemTheme.colors.icon.inactive,
|
||||
@FloatRange(from = 0.0, to = 1.0) backgroundSplitChartSegmentColorAlpha: Float = 0.24f,
|
||||
@FloatRange(from = 0.0, to = 1.0) backgroundColorAlpha: Float = 0.24f,
|
||||
noChartContent: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
var canvasWidth by remember { mutableIntStateOf(0) }
|
||||
|
|
@ -358,41 +358,47 @@ private fun MarketChartPreview(
|
|||
text = "Change marker highlight side",
|
||||
)
|
||||
}
|
||||
Button(onClick = {
|
||||
coroutineScope.launch {
|
||||
dataProducer.runTransactionSuspend {
|
||||
updateData {
|
||||
MarketChartData.Data(
|
||||
x = it.x,
|
||||
y = it.y.reversed(),
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
dataProducer.runTransactionSuspend {
|
||||
updateData {
|
||||
MarketChartData.Data(
|
||||
x = it.x,
|
||||
y = it.y.reversed(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},) {
|
||||
},
|
||||
) {
|
||||
Text("Change Data")
|
||||
}
|
||||
Button(onClick = {
|
||||
dataProducer.runTransaction {
|
||||
updateLook { it.copy(animationOnDataChange = it.animationOnDataChange.not()) }
|
||||
}
|
||||
},) {
|
||||
Button(
|
||||
onClick = {
|
||||
dataProducer.runTransaction {
|
||||
updateLook { it.copy(animationOnDataChange = it.animationOnDataChange.not()) }
|
||||
}
|
||||
},
|
||||
) {
|
||||
Text("Change animationOnDataChange = ${look.animationOnDataChange}")
|
||||
}
|
||||
|
||||
Button(onClick = {
|
||||
dataProducer.runTransaction {
|
||||
updateLook {
|
||||
it.copy(
|
||||
type = if (it.type == MarketChartLook.Type.Growing) {
|
||||
MarketChartLook.Type.Falling
|
||||
} else {
|
||||
MarketChartLook.Type.Growing
|
||||
},
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
dataProducer.runTransaction {
|
||||
updateLook {
|
||||
it.copy(
|
||||
type = if (it.type == MarketChartLook.Type.Growing) {
|
||||
MarketChartLook.Type.Falling
|
||||
} else {
|
||||
MarketChartLook.Type.Growing
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},) {
|
||||
},
|
||||
) {
|
||||
Text("Change color type")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ internal class DefaultMarketsTokenRepository(
|
|||
) : MarketsTokenRepository {
|
||||
|
||||
private val tokenListConverter = TokenMarketListConverter()
|
||||
private val tokenChartConverter = TokenChartConverter()
|
||||
|
||||
private fun createTokenMarketsFetcher(firstBatchSize: Int, nextBatchSize: Int) = LimitOffsetBatchFetcher(
|
||||
prefetchDistance = firstBatchSize,
|
||||
|
|
@ -89,4 +90,13 @@ internal class DefaultMarketsTokenRepository(
|
|||
updateFetcher = tokenMarketsUpdateFetcher,
|
||||
).toBatchFlow()
|
||||
}
|
||||
|
||||
override suspend fun getChart(interval: PriceChangeInterval, tokenId: String): TokenChart {
|
||||
val response = marketsApi.getCoinChart(
|
||||
currency = tokenId,
|
||||
interval = interval.toRequestParam(),
|
||||
)
|
||||
|
||||
return tokenChartConverter.convert(interval, response.getOrThrow())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.data.markets
|
||||
|
||||
import com.tangem.data.markets.converters.TokenListChartConverter
|
||||
import com.tangem.data.markets.converters.TokenChartConverter
|
||||
import com.tangem.data.markets.converters.TokenMarketChartsConverter
|
||||
import com.tangem.data.markets.converters.TokenQuotesConverter
|
||||
import com.tangem.data.markets.converters.toRequestParam
|
||||
|
|
@ -21,7 +21,7 @@ internal class MarketsBatchUpdateFetcher(
|
|||
private val tangemTechApi: TangemTechApi,
|
||||
) : BatchUpdateFetcher<Int, List<TokenMarket>, TokenMarketUpdateRequest> {
|
||||
|
||||
private val tokenListChartsConverter = TokenMarketChartsConverter(TokenListChartConverter())
|
||||
private val tokenListChartsConverter = TokenMarketChartsConverter(TokenChartConverter())
|
||||
private val tokenQuotesConverter = TokenQuotesConverter()
|
||||
|
||||
override suspend fun BatchUpdateFetcher.UpdateContext<Int, List<TokenMarket>>.fetchUpdateAsync(
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import com.tangem.datasource.api.markets.models.response.TokenMarketChartRespons
|
|||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenChart
|
||||
|
||||
class TokenListChartConverter {
|
||||
class TokenChartConverter {
|
||||
|
||||
fun convert(interval: PriceChangeInterval, value: TokenMarketChartResponse): TokenChart {
|
||||
return TokenChart(
|
||||
interval = interval,
|
||||
priceY = value.prices.values.toList(),
|
||||
timeStamp = value.prices.keys.toList(),
|
||||
timeStamps = value.prices.keys.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.tangem.domain.markets.PriceChangeInterval
|
|||
import com.tangem.domain.markets.TokenMarket
|
||||
|
||||
class TokenMarketChartsConverter(
|
||||
private val tokenListChartConverter: TokenListChartConverter,
|
||||
private val tokenChartConverter: TokenChartConverter,
|
||||
) {
|
||||
|
||||
fun convert(
|
||||
|
|
@ -19,13 +19,13 @@ class TokenMarketChartsConverter(
|
|||
}
|
||||
return when (interval) {
|
||||
PriceChangeInterval.H24 -> chartsToCopy.copy(
|
||||
h24 = tokenListChartConverter.convert(interval, prices),
|
||||
h24 = tokenChartConverter.convert(interval, prices),
|
||||
)
|
||||
PriceChangeInterval.WEEK -> chartsToCopy.copy(
|
||||
week = tokenListChartConverter.convert(interval, prices),
|
||||
week = tokenChartConverter.convert(interval, prices),
|
||||
)
|
||||
PriceChangeInterval.MONTH -> chartsToCopy.copy(
|
||||
month = tokenListChartConverter.convert(interval, prices),
|
||||
month = tokenChartConverter.convert(interval, prices),
|
||||
)
|
||||
else -> error("unsupported interval=$interval. This shouldn't have happened.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import java.math.BigDecimal
|
|||
data class TokenChart(
|
||||
val interval: PriceChangeInterval,
|
||||
val priceY: List<BigDecimal>,
|
||||
val timeStamp: List<Long>,
|
||||
val timeStamps: List<Long>,
|
||||
) {
|
||||
init {
|
||||
require(priceY.size == timeStamp.size)
|
||||
require(priceY.size == timeStamps.size)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ class GetMarketsTokenListFlowUseCase(
|
|||
firstBatchSize = batchFlowType.firstBatchSize,
|
||||
nextBatchSize = batchFlowType.nextBatchSize,
|
||||
)
|
||||
// TODO listen quotes updates flow and update them in other parts of the application
|
||||
}
|
||||
|
||||
enum class BatchFlowType(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.markets
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
|
||||
class GetTokenPriceChartUseCase(
|
||||
private val marketsTokenRepository: MarketsTokenRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(interval: PriceChangeInterval, tokenId: String): Either<Unit, TokenChart> {
|
||||
return Either.catch {
|
||||
marketsTokenRepository.getChart(interval = interval, tokenId = tokenId)
|
||||
}.mapLeft {}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,4 +9,6 @@ interface MarketsTokenRepository {
|
|||
firstBatchSize: Int,
|
||||
nextBatchSize: Int,
|
||||
): TokenListBatchFlow
|
||||
|
||||
suspend fun getChart(interval: PriceChangeInterval, tokenId: String): TokenChart
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import androidx.compose.ui.unit.Dp
|
|||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
|
||||
@Stable
|
||||
interface MarketsListComponent {
|
||||
interface MarketsEntryComponent {
|
||||
|
||||
@Composable
|
||||
fun BottomSheetContent(
|
||||
|
|
@ -18,6 +18,6 @@ interface MarketsListComponent {
|
|||
)
|
||||
|
||||
interface Factory {
|
||||
fun create(context: AppComponentContext): MarketsListComponent
|
||||
fun create(context: AppComponentContext): MarketsEntryComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ dependencies {
|
|||
/* Other */
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.timber)
|
||||
implementation(deps.decompose.ext.compose)
|
||||
|
||||
/* Core */
|
||||
implementation(projects.core.decompose)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package com.tangem.features.markets
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.router.stack.push
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.markets.details.api.MarketsTokenDetailsComponent
|
||||
import com.tangem.features.markets.details.api.toSerializable
|
||||
import com.tangem.features.markets.tokenlist.api.MarketsTokenListComponent
|
||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsListModel
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultMarketsEntryComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
private val marketsEntryChildFactory: MarketsEntryChildFactory,
|
||||
) : MarketsEntryComponent, AppComponentContext by context {
|
||||
|
||||
private val model: MarketsListModel = getOrCreateModel()
|
||||
|
||||
private val stackNavigation = StackNavigation<MarketsEntryChildFactory.Child>()
|
||||
|
||||
val stack: Value<ChildStack<MarketsEntryChildFactory.Child, Any>> = childStack(
|
||||
key = "main",
|
||||
source = stackNavigation,
|
||||
serializer = MarketsEntryChildFactory.Child.serializer(),
|
||||
initialConfiguration = MarketsEntryChildFactory.Child.TokenList,
|
||||
handleBackButton = true,
|
||||
childFactory = { configuration, componentContext ->
|
||||
marketsEntryChildFactory.createChild(
|
||||
child = configuration,
|
||||
appComponentContext = childByContext(componentContext),
|
||||
onTokenSelected = ::marketsListTokenSelected,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun BottomSheetContent(
|
||||
bottomSheetState: State<BottomSheetState>,
|
||||
onHeaderSizeChange: (Dp) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
Children(stack) {
|
||||
when (it.configuration) {
|
||||
is MarketsEntryChildFactory.Child.TokenDetails -> {
|
||||
(it.instance as MarketsTokenDetailsComponent).Content(modifier)
|
||||
}
|
||||
MarketsEntryChildFactory.Child.TokenList -> {
|
||||
(it.instance as MarketsTokenListComponent).BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun marketsListTokenSelected(token: TokenMarket) {
|
||||
stackNavigation.push(
|
||||
configuration = MarketsEntryChildFactory.Child.TokenDetails(
|
||||
params = MarketsTokenDetailsComponent.Params(token.toSerializable()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MarketsEntryComponent.Factory {
|
||||
override fun create(context: AppComponentContext): DefaultMarketsEntryComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.features.markets
|
||||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.features.markets.details.api.MarketsTokenDetailsComponent
|
||||
import com.tangem.features.markets.tokenlist.api.MarketsTokenListComponent
|
||||
import kotlinx.serialization.Serializable
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class MarketsEntryChildFactory @Inject constructor(
|
||||
private val tokenListComponentFactory: MarketsTokenListComponent.Factory,
|
||||
private val tokenDetailsComponentFactory: MarketsTokenDetailsComponent.Factory,
|
||||
) {
|
||||
|
||||
@Serializable
|
||||
sealed interface Child {
|
||||
|
||||
@Serializable
|
||||
data object TokenList : Child
|
||||
|
||||
@Serializable
|
||||
data class TokenDetails(val params: MarketsTokenDetailsComponent.Params) : Child
|
||||
}
|
||||
|
||||
fun createChild(
|
||||
child: Child,
|
||||
appComponentContext: AppComponentContext,
|
||||
onTokenSelected: (TokenMarket) -> Unit,
|
||||
): Any {
|
||||
return when (child) {
|
||||
is Child.TokenDetails -> {
|
||||
tokenDetailsComponentFactory.create(
|
||||
context = appComponentContext,
|
||||
params = child.params,
|
||||
)
|
||||
}
|
||||
is Child.TokenList -> {
|
||||
tokenListComponentFactory.create(
|
||||
context = appComponentContext,
|
||||
onTokenSelected = onTokenSelected,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.features.markets.details.api
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Stable
|
||||
interface MarketsTokenDetailsComponent : ComposableContentComponent {
|
||||
|
||||
@Serializable
|
||||
data class Params(val token: TokenMarketSerializable)
|
||||
|
||||
interface Factory : ComponentFactory<Params, MarketsTokenDetailsComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.features.markets.details.api
|
||||
|
||||
import com.tangem.domain.core.serialization.SerializedBigDecimal
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TokenMarketSerializable(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val marketCap: SerializedBigDecimal?,
|
||||
val tokenQuotes: Quotes,
|
||||
val imageUrl: String,
|
||||
) {
|
||||
|
||||
@Serializable
|
||||
data class Quotes(
|
||||
val currentPrice: SerializedBigDecimal,
|
||||
val h24Percent: SerializedBigDecimal,
|
||||
val weekPercent: SerializedBigDecimal,
|
||||
val monthPercent: SerializedBigDecimal,
|
||||
)
|
||||
}
|
||||
|
||||
fun TokenMarket.toSerializable(): TokenMarketSerializable {
|
||||
return TokenMarketSerializable(
|
||||
id = id,
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
marketCap = marketCap,
|
||||
tokenQuotes = TokenMarketSerializable.Quotes(
|
||||
currentPrice = tokenQuotes.currentPrice,
|
||||
h24Percent = tokenQuotes.h24Percent(),
|
||||
weekPercent = tokenQuotes.weekPercent(),
|
||||
monthPercent = tokenQuotes.monthPercent(),
|
||||
),
|
||||
imageUrl = imageUrlLarge,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.features.markets.details.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.markets.details.api.MarketsTokenDetailsComponent
|
||||
import com.tangem.features.markets.details.impl.model.MarketsTokenDetailsModel
|
||||
import com.tangem.features.markets.details.impl.ui.MarketsTokenDetailsContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Stable
|
||||
internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: MarketsTokenDetailsComponent.Params,
|
||||
) : AppComponentContext by appComponentContext, MarketsTokenDetailsComponent {
|
||||
|
||||
private val model: MarketsTokenDetailsModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
||||
MarketsTokenDetailsContent(
|
||||
state = state,
|
||||
onBackClick = {},
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MarketsTokenDetailsComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: MarketsTokenDetailsComponent.Params,
|
||||
): DefaultMarketsTokenDetailsComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.markets.details.impl.di
|
||||
|
||||
import com.tangem.features.markets.details.api.MarketsTokenDetailsComponent
|
||||
import com.tangem.features.markets.details.impl.DefaultMarketsTokenDetailsComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindMarketsTokenDetailsComponent(
|
||||
factory: DefaultMarketsTokenDetailsComponent.Factory,
|
||||
): MarketsTokenDetailsComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.markets.details.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.markets.details.impl.model.MarketsTokenDetailsModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(MarketsTokenDetailsModel::class)
|
||||
fun provideMarketsTokenDetailsModel(model: MarketsTokenDetailsModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.tangem.features.markets.details.impl.model
|
||||
|
||||
import com.tangem.common.ui.charts.state.MarketChartData
|
||||
import com.tangem.common.ui.charts.state.MarketChartDataProducer
|
||||
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.domain.markets.GetTokenPriceChartUseCase
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.features.markets.details.api.MarketsTokenDetailsComponent
|
||||
import com.tangem.features.markets.details.impl.ui.entity.MarketsTokenDetailsUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class MarketsTokenDetailsModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
|
||||
) : Model() {
|
||||
|
||||
val params = paramsContainer.require<MarketsTokenDetailsComponent.Params>()
|
||||
|
||||
private val chartDataProducer = MarketChartDataProducer.build(dispatcher = dispatchers.default) {
|
||||
chartData = MarketChartData.NoData.Loading
|
||||
}
|
||||
|
||||
val state = MutableStateFlow(
|
||||
MarketsTokenDetailsUM(
|
||||
tokenName = params.token.name,
|
||||
dateTimeText = "datetime",
|
||||
priceChangePercentText = params.token.tokenQuotes.h24Percent.toString(),
|
||||
priceChangeType = if (params.token.tokenQuotes.h24Percent < BigDecimal.ZERO) {
|
||||
PriceChangeType.DOWN
|
||||
} else {
|
||||
PriceChangeType.UP
|
||||
},
|
||||
chartState = MarketsTokenDetailsUM.ChartState(
|
||||
dataProducer = chartDataProducer,
|
||||
chartLook = MarketChartLook(),
|
||||
onLoadRetryClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
init {
|
||||
modelScope.launch {
|
||||
val chart = getTokenPriceChartUseCase.invoke(PriceChangeInterval.H24, params.token.id)
|
||||
|
||||
chart.onRight {
|
||||
chartDataProducer.runTransactionSuspend {
|
||||
chartData = MarketChartData.Data()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.features.markets.details.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.features.markets.details.impl.ui.entity.MarketsTokenDetailsUM
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@Composable
|
||||
internal fun MarketsTokenDetailsContent(
|
||||
state: MarketsTokenDetailsUM,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@Composable
|
||||
private fun Content(state: MarketsTokenDetailsUM, onBackClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column {
|
||||
TangemTopAppBar(
|
||||
title = state.tokenName,
|
||||
startButton = TopAppBarButtonUM.Back(onBackClick),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.features.markets.details.impl.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.common.ui.charts.MarketChart
|
||||
import com.tangem.common.ui.charts.state.rememberMarketChartState
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.markets.details.impl.ui.entity.MarketsTokenDetailsUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.components.UnableToLoadData
|
||||
|
||||
@Composable
|
||||
fun MarketTokenDetailsChart(state: MarketsTokenDetailsUM.ChartState, modifier: Modifier = Modifier) {
|
||||
val chartState = rememberMarketChartState(state.dataProducer)
|
||||
|
||||
MarketChart(
|
||||
modifier = modifier,
|
||||
state = chartState,
|
||||
noChartContent = {
|
||||
UnableToLoadData(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing12)
|
||||
.align(Alignment.Center),
|
||||
onRetryClick = state.onLoadRetryClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.markets.details.impl.ui.entity
|
||||
|
||||
import com.tangem.common.ui.charts.state.MarketChartDataProducer
|
||||
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
|
||||
data class MarketsTokenDetailsUM(
|
||||
val tokenName: String,
|
||||
val dateTimeText: String,
|
||||
val priceChangePercentText: String,
|
||||
val priceChangeType: PriceChangeType,
|
||||
val chartState: ChartState,
|
||||
) {
|
||||
|
||||
data class ChartState(
|
||||
val dataProducer: MarketChartDataProducer,
|
||||
val chartLook: MarketChartLook,
|
||||
val onLoadRetryClick: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.markets.di
|
||||
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.markets.component.impl.DefaultMarketsListComponent
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.markets.DefaultMarketsEntryComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -14,5 +14,5 @@ internal interface ComponentModule {
|
|||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindMarketsListComponent(factory: DefaultMarketsListComponent.Factory): MarketsListComponent.Factory
|
||||
fun bindMarketsListComponent(factory: DefaultMarketsEntryComponent.Factory): MarketsEntryComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.features.markets.tokenlist.api
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
|
||||
@Stable
|
||||
interface MarketsTokenListComponent {
|
||||
|
||||
@Composable
|
||||
fun BottomSheetContent(
|
||||
bottomSheetState: State<BottomSheetState>,
|
||||
onHeaderSizeChange: (Dp) -> Unit,
|
||||
modifier: Modifier,
|
||||
)
|
||||
|
||||
interface Factory {
|
||||
fun create(context: AppComponentContext, onTokenSelected: (TokenMarket) -> Unit): MarketsTokenListComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.component.impl
|
||||
package com.tangem.features.markets.tokenlist.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -9,20 +9,30 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.markets.model.MarketsListModel
|
||||
import com.tangem.features.markets.ui.MarketsList
|
||||
import com.tangem.features.markets.tokenlist.api.MarketsTokenListComponent
|
||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsListModel
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.MarketsList
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
internal class DefaultMarketsListComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
) : MarketsListComponent, AppComponentContext by context {
|
||||
class DefaultMarketsTokenListComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val onTokenSelected: (TokenMarket) -> Unit,
|
||||
) : AppComponentContext by appComponentContext, MarketsTokenListComponent {
|
||||
|
||||
private val model: MarketsListModel = getOrCreateModel()
|
||||
|
||||
init {
|
||||
model.tokenSelected
|
||||
.onEach { onTokenSelected(it) }
|
||||
.launchIn(componentScope)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheetContent(
|
||||
bottomSheetState: State<BottomSheetState>,
|
||||
|
|
@ -45,7 +55,10 @@ internal class DefaultMarketsListComponent @AssistedInject constructor(
|
|||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MarketsListComponent.Factory {
|
||||
override fun create(context: AppComponentContext): DefaultMarketsListComponent
|
||||
interface Factory : MarketsTokenListComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
onTokenSelected: (TokenMarket) -> Unit,
|
||||
): DefaultMarketsTokenListComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.markets.tokenlist.impl.di
|
||||
|
||||
import com.tangem.features.markets.tokenlist.api.MarketsTokenListComponent
|
||||
import com.tangem.features.markets.tokenlist.impl.DefaultMarketsTokenListComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindMarketsTokenListComponent(
|
||||
factory: DefaultMarketsTokenListComponent.Factory,
|
||||
): MarketsTokenListComponent.Factory
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.features.markets.di
|
||||
package com.tangem.features.markets.tokenlist.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.markets.model.MarketsListModel
|
||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsListModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.model
|
||||
package com.tangem.features.markets.tokenlist.impl.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
|
|
@ -7,11 +7,13 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.model.statemanager.MarketsListUMStateManager
|
||||
import com.tangem.features.markets.model.statemanager.MarketsListBatchFlowManager
|
||||
import com.tangem.features.markets.ui.entity.ListUM
|
||||
import com.tangem.features.markets.ui.entity.SortByTypeUM
|
||||
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListUMStateManager
|
||||
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListBatchFlowManager
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.ListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByTypeUM
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
|
|
@ -32,6 +34,8 @@ internal class MarketsListModel @Inject constructor(
|
|||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
) : Model() {
|
||||
|
||||
private var updateQuotesJob = JobHolder()
|
||||
|
||||
private val currentAppCurrency = getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
|
|
@ -47,8 +51,8 @@ internal class MarketsListModel @Inject constructor(
|
|||
onLoadMoreUiItems = { activeListManager.loadMore() },
|
||||
visibleItemsChanged = { visibleItemIds.value = it },
|
||||
onRetryButtonClicked = { activeListManager.reload() },
|
||||
onTokenClick = { onTokenUIClicked(it) },
|
||||
)
|
||||
|
||||
private val mainMarketsListManager = MarketsListBatchFlowManager(
|
||||
getMarketsTokenListFlowUseCase = getMarketsTokenListFlowUseCase,
|
||||
batchFlowType = GetMarketsTokenListFlowUseCase.BatchFlowType.Main,
|
||||
|
|
@ -59,6 +63,7 @@ internal class MarketsListModel @Inject constructor(
|
|||
modelScope = modelScope,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
private val searchMarketsListManager = MarketsListBatchFlowManager(
|
||||
getMarketsTokenListFlowUseCase = getMarketsTokenListFlowUseCase,
|
||||
batchFlowType = GetMarketsTokenListFlowUseCase.BatchFlowType.Search,
|
||||
|
|
@ -72,6 +77,10 @@ internal class MarketsListModel @Inject constructor(
|
|||
|
||||
private var activeListManager: MarketsListBatchFlowManager = mainMarketsListManager
|
||||
|
||||
private val _tokenSelected = MutableSharedFlow<TokenMarket>()
|
||||
|
||||
val tokenSelected = _tokenSelected.asSharedFlow()
|
||||
|
||||
val containerBottomSheetState = MutableStateFlow(BottomSheetState.COLLAPSED)
|
||||
|
||||
val state = marketsListUMStateManager.state.asStateFlow()
|
||||
|
|
@ -169,6 +178,7 @@ internal class MarketsListModel @Inject constructor(
|
|||
}
|
||||
.distinctUntilChanged()
|
||||
.collectLatest { visibleBatchKeys ->
|
||||
// TODO load batch on scroll heat area
|
||||
activeListManager.loadCharts(visibleBatchKeys, marketsListUMStateManager.selectedInterval)
|
||||
}
|
||||
}
|
||||
|
|
@ -210,7 +220,13 @@ internal class MarketsListModel @Inject constructor(
|
|||
mainMarketsListManager.reload()
|
||||
}
|
||||
|
||||
private var updateQuotesJob = JobHolder()
|
||||
private fun onTokenUIClicked(token: MarketsListItemUM) {
|
||||
modelScope.launch {
|
||||
activeListManager.getTokenById(token.id)?.let { found ->
|
||||
_tokenSelected.emit(found)
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun CoroutineScope.loadQuotesWithTimer(timeMillis: Long) {
|
||||
launch {
|
||||
while (true) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.model.converters
|
||||
package com.tangem.features.markets.tokenlist.impl.model.converters
|
||||
|
||||
import com.tangem.common.ui.charts.state.DefaultPointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.MarketChartData
|
||||
|
|
@ -7,8 +7,8 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType
|
|||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.features.markets.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.ui.entity.MarketsListUM.TrendInterval
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListUM.TrendInterval
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
|
@ -110,7 +110,7 @@ internal class MarketsTokenItemConverter(
|
|||
DefaultPointValuesConverter.convert(
|
||||
MarketChartData.Data(
|
||||
y = ct.priceY,
|
||||
x = ct.timeStamp.map { it.toBigDecimal() },
|
||||
x = ct.timeStamps.map { it.toBigDecimal() },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tangem.features.markets.model.statemanager
|
||||
package com.tangem.features.markets.tokenlist.impl.model.statemanager
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.features.markets.model.converters.MarketsTokenItemConverter
|
||||
import com.tangem.features.markets.model.utils.logAction
|
||||
import com.tangem.features.markets.model.utils.logStatus
|
||||
import com.tangem.features.markets.model.utils.logUpdateResults
|
||||
import com.tangem.features.markets.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.ui.entity.MarketsListUM.TrendInterval
|
||||
import com.tangem.features.markets.ui.entity.SortByTypeUM
|
||||
import com.tangem.features.markets.tokenlist.impl.model.converters.MarketsTokenItemConverter
|
||||
import com.tangem.features.markets.tokenlist.impl.model.utils.logAction
|
||||
import com.tangem.features.markets.tokenlist.impl.model.utils.logStatus
|
||||
import com.tangem.features.markets.tokenlist.impl.model.utils.logUpdateResults
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListUM.TrendInterval
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByTypeUM
|
||||
import com.tangem.pagination.*
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -281,6 +281,10 @@ internal class MarketsListBatchFlowManager(
|
|||
.toSet()
|
||||
}
|
||||
|
||||
fun getTokenById(id: String): TokenMarket? {
|
||||
return batchFlow.state.value.data.map { it.data }.flatten().find { it.id == id }
|
||||
}
|
||||
|
||||
private fun SortByTypeUM.toRequestOrder(): TokenMarketListConfig.Order {
|
||||
return when (this) {
|
||||
SortByTypeUM.Rating -> TokenMarketListConfig.Order.ByRating
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.model.statemanager
|
||||
package com.tangem.features.markets.tokenlist.impl.model.statemanager
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
|
|
@ -7,11 +7,11 @@ import com.tangem.core.ui.event.consumedEvent
|
|||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.ui.entity.SortByBottomSheetContentUM
|
||||
import com.tangem.features.markets.ui.entity.ListUM
|
||||
import com.tangem.features.markets.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.ui.entity.MarketsListUM
|
||||
import com.tangem.features.markets.ui.entity.SortByTypeUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByBottomSheetContentUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.ListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByTypeUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
|
|
@ -20,6 +20,7 @@ internal class MarketsListUMStateManager(
|
|||
private val onLoadMoreUiItems: () -> Unit,
|
||||
private val visibleItemsChanged: (itemsKeys: List<String>) -> Unit,
|
||||
private val onRetryButtonClicked: () -> Unit,
|
||||
private val onTokenClick: (MarketsListItemUM) -> Unit,
|
||||
) {
|
||||
|
||||
private var sortByBottomSheetIsShown
|
||||
|
|
@ -106,6 +107,7 @@ internal class MarketsListUMStateManager(
|
|||
showUnder100kTokens = true,
|
||||
onShowTokensUnder100kClicked = { },
|
||||
triggerScrollReset = consumedEvent(),
|
||||
onItemClick = onTokenClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.model.utils
|
||||
package com.tangem.features.markets.tokenlist.impl.model.utils
|
||||
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.domain.markets.TokenMarketListConfig
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui
|
||||
package com.tangem.features.markets.tokenlist.impl.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
|
|
@ -36,13 +36,13 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.ui.components.MarketsListLazyColumn
|
||||
import com.tangem.features.markets.ui.components.MarketsListSortByBottomSheet
|
||||
import com.tangem.features.markets.ui.entity.ListUM
|
||||
import com.tangem.features.markets.ui.entity.MarketsListUM
|
||||
import com.tangem.features.markets.ui.entity.SortByBottomSheetContentUM
|
||||
import com.tangem.features.markets.ui.entity.SortByTypeUM
|
||||
import com.tangem.features.markets.ui.preview.MarketChartListItemPreviewDataProvider
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListLazyColumn
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListSortByBottomSheet
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.ListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByBottomSheetContentUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByTypeUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.preview.MarketChartListItemPreviewDataProvider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
|
@ -242,6 +242,7 @@ private fun Preview() {
|
|||
visibleIdsChanged = {},
|
||||
onShowTokensUnder100kClicked = {},
|
||||
triggerScrollReset = consumedEvent(),
|
||||
onItemClick = {},
|
||||
),
|
||||
searchBar = SearchBarUM(
|
||||
placeholderText = resourceReference(R.string.manage_tokens_search_placeholder),
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.components
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.Animatable
|
||||
|
|
@ -45,8 +45,8 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.windowsize.WindowSizeType
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.ui.preview.MarketChartListItemPreviewDataProvider
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.preview.MarketChartListItemPreviewDataProvider
|
||||
import com.tangem.utils.StringsSigns.MINUS
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.components
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.components
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -21,7 +21,7 @@ import com.tangem.core.ui.event.EventEffect
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.ui.entity.ListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.ListUM
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val LOAD_NEXT_PAGE_ON_END_INDEX = 50
|
||||
|
|
@ -91,7 +91,10 @@ internal fun MarketsListLazyColumn(
|
|||
items = state.items,
|
||||
key = { it.id },
|
||||
) { item ->
|
||||
MarketsListItem(model = item)
|
||||
MarketsListItem(
|
||||
model = item,
|
||||
onClick = { state.onItemClick(item) },
|
||||
)
|
||||
}
|
||||
|
||||
if (isInSearchMode && state.showUnder100kTokens.not()) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.components
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -19,8 +19,8 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.ui.entity.SortByBottomSheetContentUM
|
||||
import com.tangem.features.markets.ui.entity.SortByTypeUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByBottomSheetContentUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByTypeUM
|
||||
|
||||
@Composable
|
||||
fun MarketsListSortByBottomSheet(config: TangemBottomSheetConfig) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.components
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.entity
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.entity
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
|
|
@ -46,6 +46,7 @@ sealed class ListUM {
|
|||
val visibleIdsChanged: (List<String>) -> Unit,
|
||||
val onShowTokensUnder100kClicked: () -> Unit,
|
||||
val triggerScrollReset: StateEvent<Unit>,
|
||||
val onItemClick: (MarketsListItemUM) -> Unit,
|
||||
) : ListUM()
|
||||
|
||||
data object Loading : ListUM()
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.markets.ui.entity
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.entity
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
@file:Suppress("MagicNumber")
|
||||
package com.tangem.features.markets.ui.preview
|
||||
package com.tangem.features.markets.tokenlist.impl.ui.preview
|
||||
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.features.markets.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
||||
|
||||
internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvider<MarketsListItemUM>(
|
||||
collection = listOf(
|
||||
|
|
@ -10,7 +10,7 @@ import com.tangem.core.ui.UiDependencies
|
|||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.features.markets.MarketsFeatureToggles
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
|
@ -32,7 +32,7 @@ internal class WalletFragment : ComposeFragment() {
|
|||
internal lateinit var walletRouter: WalletRouter
|
||||
|
||||
@Inject
|
||||
internal lateinit var marketsListComponentFactory: MarketsListComponent.Factory
|
||||
internal lateinit var marketsEntryComponentFactory: MarketsEntryComponent.Factory
|
||||
|
||||
@Inject
|
||||
internal lateinit var coroutineDispatcherProvider: CoroutineDispatcherProvider
|
||||
|
|
@ -43,7 +43,7 @@ internal class WalletFragment : ComposeFragment() {
|
|||
@Inject
|
||||
internal lateinit var marketsFeatureToggles: MarketsFeatureToggles
|
||||
|
||||
private var marketsListComponent: MarketsListComponent? = null
|
||||
private var marketsEntryComponent: MarketsEntryComponent? = null
|
||||
|
||||
private val _walletRouter: InnerWalletRouter
|
||||
get() = requireNotNull(walletRouter as? InnerWalletRouter) {
|
||||
|
|
@ -61,7 +61,7 @@ internal class WalletFragment : ComposeFragment() {
|
|||
hiltComponentBuilder = componentBuilder,
|
||||
)
|
||||
|
||||
marketsListComponent = marketsListComponentFactory.create(appContext)
|
||||
marketsEntryComponent = marketsEntryComponentFactory.create(appContext)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ internal class WalletFragment : ComposeFragment() {
|
|||
override fun ScreenContent(modifier: Modifier) {
|
||||
_walletRouter.Initialize(
|
||||
onFinish = requireActivity()::finish,
|
||||
marketsListComponent = marketsListComponent,
|
||||
marketsEntryComponent = marketsEntryComponent,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScree
|
|||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/** Default implementation of wallet feature router */
|
||||
|
|
@ -41,7 +41,7 @@ internal class DefaultWalletRouter(
|
|||
override fun getEntryFragment(): Fragment = WalletFragment.create()
|
||||
|
||||
@Composable
|
||||
override fun Initialize(onFinish: () -> Unit, marketsListComponent: MarketsListComponent?) {
|
||||
override fun Initialize(onFinish: () -> Unit, marketsEntryComponent: MarketsEntryComponent?) {
|
||||
this.onFinish = onFinish
|
||||
|
||||
NavHost(
|
||||
|
|
@ -56,7 +56,7 @@ internal class DefaultWalletRouter(
|
|||
|
||||
WalletScreen(
|
||||
state = viewModel.uiState.collectAsStateWithLifecycle().value,
|
||||
marketsListComponent = marketsListComponent,
|
||||
marketsEntryComponent = marketsEntryComponent,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
|
||||
/**
|
||||
|
|
@ -24,7 +24,7 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
* @param onFinish finish activity callback
|
||||
*/
|
||||
@Composable
|
||||
fun Initialize(onFinish: () -> Unit, marketsListComponent: MarketsListComponent?)
|
||||
fun Initialize(onFinish: () -> Unit, marketsEntryComponent: MarketsEntryComponent?)
|
||||
|
||||
/** Pop back stack */
|
||||
fun popBackStack()
|
||||
|
|
|
|||
|
|
@ -70,12 +70,12 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balances
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
internal fun WalletScreen(state: WalletScreenState, marketsListComponent: MarketsListComponent?) {
|
||||
internal fun WalletScreen(state: WalletScreenState, marketsEntryComponent: MarketsEntryComponent?) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
// It means that screen is still initializing
|
||||
|
|
@ -98,7 +98,7 @@ internal fun WalletScreen(state: WalletScreenState, marketsListComponent: Market
|
|||
snackbarHostState = snackbarHostState,
|
||||
isAutoScroll = isAutoScroll,
|
||||
onAutoScrollReset = { isAutoScroll.value = false },
|
||||
marketsListComponent = marketsListComponent,
|
||||
marketsEntryComponent = marketsEntryComponent,
|
||||
alertConfig = alertConfig,
|
||||
)
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ private fun WalletContent(
|
|||
walletsListState: LazyListState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
isAutoScroll: State<Boolean>,
|
||||
marketsListComponent: MarketsListComponent?,
|
||||
marketsEntryComponent: MarketsEntryComponent?,
|
||||
alertConfig: WalletAlertState?,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
) {
|
||||
|
|
@ -216,7 +216,7 @@ private fun WalletContent(
|
|||
)
|
||||
}
|
||||
|
||||
if (marketsListComponent != null) {
|
||||
if (marketsEntryComponent != null) {
|
||||
val bottomSheetState = remember {
|
||||
mutableStateOf(BottomSheetState.COLLAPSED)
|
||||
}
|
||||
|
|
@ -232,7 +232,7 @@ private fun WalletContent(
|
|||
alertConfig = alertConfig,
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
bottomSheetContent = {
|
||||
marketsListComponent.BottomSheetContent(
|
||||
marketsEntryComponent.BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
modifier = Modifier,
|
||||
|
|
@ -638,7 +638,7 @@ private fun WalletScreen_Preview(@PreviewParameter(WalletScreenPreviewProvider::
|
|||
TangemThemePreview {
|
||||
WalletScreen(
|
||||
state = data,
|
||||
marketsListComponent = null,
|
||||
marketsEntryComponent = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue