Updated on 2026-08-14
This commit is contained in:
parent
a3f7c6dcc4
commit
99dfa2a392
12 changed files with 189 additions and 36 deletions
|
|
@ -4,11 +4,11 @@ import android.content.Context
|
|||
import androidx.datastore.core.DataStoreFactory
|
||||
import androidx.datastore.dataStoreFile
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.api.tangemTech.models.YieldMarketsResponse
|
||||
import com.tangem.datasource.local.yieldsupply.DefaultYieldMarketsStore
|
||||
import com.tangem.datasource.local.yieldsupply.YieldMarketsStore
|
||||
import com.tangem.datasource.utils.MoshiDataStoreSerializer
|
||||
import com.tangem.datasource.utils.listTypes
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -34,7 +34,7 @@ object YieldSupplyModule {
|
|||
persistenceStore = DataStoreFactory.create(
|
||||
serializer = MoshiDataStoreSerializer(
|
||||
moshi = moshi,
|
||||
types = listTypes<YieldMarketToken>(),
|
||||
types = listTypes<YieldMarketsResponse.MarketDto>(),
|
||||
defaultValue = emptyList(),
|
||||
),
|
||||
produceFile = { context.dataStoreFile(fileName = "yield_markets_cache") },
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
package com.tangem.datasource.local.yieldsupply
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import com.tangem.datasource.api.tangemTech.models.YieldMarketsResponse
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
||||
internal class DefaultYieldMarketsStore(
|
||||
private val persistenceStore: DataStore<List<YieldMarketToken>>,
|
||||
private val persistenceStore: DataStore<List<YieldMarketsResponse.MarketDto>>,
|
||||
) : YieldMarketsStore {
|
||||
|
||||
override fun get(): Flow<List<YieldMarketToken>> = persistenceStore.data
|
||||
override fun get(): Flow<List<YieldMarketsResponse.MarketDto>> = persistenceStore.data
|
||||
|
||||
override suspend fun getSyncOrNull(): List<YieldMarketToken>? {
|
||||
override suspend fun getSyncOrNull(): List<YieldMarketsResponse.MarketDto>? {
|
||||
return persistenceStore.data.firstOrNull()
|
||||
}
|
||||
|
||||
override suspend fun store(items: List<YieldMarketToken>) {
|
||||
override suspend fun store(items: List<YieldMarketsResponse.MarketDto>) {
|
||||
persistenceStore.updateData { _ -> items }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package com.tangem.datasource.local.yieldsupply
|
||||
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import com.tangem.datasource.api.tangemTech.models.YieldMarketsResponse
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface YieldMarketsStore {
|
||||
|
||||
fun get(): Flow<List<YieldMarketToken>>
|
||||
fun get(): Flow<List<YieldMarketsResponse.MarketDto>>
|
||||
|
||||
suspend fun getSyncOrNull(): List<YieldMarketToken>?
|
||||
suspend fun getSyncOrNull(): List<YieldMarketsResponse.MarketDto>?
|
||||
|
||||
suspend fun store(items: List<YieldMarketToken>)
|
||||
suspend fun store(items: List<YieldMarketsResponse.MarketDto>)
|
||||
}
|
||||
|
|
@ -27,18 +27,20 @@ internal class DefaultYieldSupplyMarketRepository(
|
|||
) : YieldSupplyMarketRepository {
|
||||
|
||||
override suspend fun getCachedMarkets(): List<YieldMarketToken>? = withContext(dispatchers.io) {
|
||||
store.getSyncOrNull()?.enrichNetworkIds()
|
||||
val cache = store.getSyncOrNull().orEmpty()
|
||||
val domain = cache.map(YieldMarketTokenConverter::convert)
|
||||
domain.enrichNetworkIds()
|
||||
}
|
||||
|
||||
override suspend fun updateMarkets(): List<YieldMarketToken> = withContext(dispatchers.io) {
|
||||
val response = yieldSupplyApi.getYieldMarkets().getOrThrow()
|
||||
val domain = response.marketDtos.map(YieldMarketTokenConverter::convert)
|
||||
store.store(domain)
|
||||
store.store(response.marketDtos)
|
||||
domain
|
||||
}
|
||||
|
||||
override fun getMarketsFlow(): Flow<List<YieldMarketToken>> = store.get().map {
|
||||
it.enrichNetworkIds()
|
||||
it.map(YieldMarketTokenConverter::convert).enrichNetworkIds()
|
||||
}
|
||||
|
||||
override suspend fun getTokenStatus(cryptoCurrencyToken: CryptoCurrency.Token): YieldMarketTokenStatus {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.features.yield.supply.impl.main.entity
|
||||
|
||||
internal enum class LoadingStatusMode {
|
||||
Initial,
|
||||
LoadApy,
|
||||
}
|
||||
|
|
@ -8,8 +8,6 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
|
|
@ -20,8 +18,10 @@ import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
|||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
|
||||
import com.tangem.features.yield.supply.impl.main.entity.LoadingStatusMode
|
||||
import com.tangem.features.yield.supply.impl.main.model.transformers.YieldSupplyTokenStatusFailureTransformer
|
||||
import com.tangem.features.yield.supply.impl.main.model.transformers.YieldSupplyTokenStatusSuccessTransformer
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.DelayedWork
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -29,6 +29,7 @@ import kotlinx.coroutines.delay
|
|||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import com.tangem.utils.transformer.update
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
|
@ -102,24 +103,20 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadTokenStatus(cryptoCurrency: CryptoCurrency.Token) {
|
||||
private fun loadTokenStatus(mode: LoadingStatusMode) {
|
||||
val cryptoCurrencyToken = cryptoCurrency as? CryptoCurrency.Token ?: return
|
||||
modelScope.launch(dispatchers.default) {
|
||||
yieldSupplyGetTokenStatusUseCase(cryptoCurrency)
|
||||
yieldSupplyGetTokenStatusUseCase(cryptoCurrencyToken)
|
||||
.onRight { tokenStatus ->
|
||||
val newState = if (tokenStatus.isActive) {
|
||||
YieldSupplyUM.Initial(
|
||||
title = resourceReference(
|
||||
id = R.string.yield_module_token_details_earn_notification_title,
|
||||
formatArgs = wrappedList(tokenStatus.apy),
|
||||
),
|
||||
onClick = ::onStartEarningClick,
|
||||
)
|
||||
} else {
|
||||
YieldSupplyUM.Unavailable
|
||||
}
|
||||
uiState.update { newState }
|
||||
uiState.update(
|
||||
YieldSupplyTokenStatusSuccessTransformer(
|
||||
tokenStatus = tokenStatus,
|
||||
onStartEarningClick = ::onStartEarningClick,
|
||||
mode = mode,
|
||||
),
|
||||
)
|
||||
}.onLeft {
|
||||
uiState.update { YieldSupplyUM.Unavailable }
|
||||
uiState.update(YieldSupplyTokenStatusFailureTransformer(mode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -178,8 +175,10 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
|
||||
uiState.update { yieldSupplyUM }
|
||||
|
||||
if (yieldSupplyUM is YieldSupplyUM.Loading) {
|
||||
(cryptoCurrency as? CryptoCurrency.Token)?.let(::loadTokenStatus)
|
||||
when (yieldSupplyUM) {
|
||||
is YieldSupplyUM.Loading -> loadTokenStatus(LoadingStatusMode.Initial)
|
||||
is YieldSupplyUM.Content -> loadTokenStatus(LoadingStatusMode.LoadApy)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.features.yield.supply.impl.main.model.transformers
|
||||
|
||||
import com.tangem.features.yield.supply.impl.main.entity.LoadingStatusMode
|
||||
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class YieldSupplyTokenStatusFailureTransformer(
|
||||
private val mode: LoadingStatusMode,
|
||||
) : Transformer<YieldSupplyUM> {
|
||||
|
||||
override fun transform(prevState: YieldSupplyUM): YieldSupplyUM {
|
||||
return when (mode) {
|
||||
LoadingStatusMode.Initial -> YieldSupplyUM.Unavailable
|
||||
LoadingStatusMode.LoadApy -> prevState // TODO apply correct UI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.features.yield.supply.impl.main.model.transformers
|
||||
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketTokenStatus
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.main.entity.LoadingStatusMode
|
||||
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class YieldSupplyTokenStatusSuccessTransformer(
|
||||
private val tokenStatus: YieldMarketTokenStatus,
|
||||
private val onStartEarningClick: () -> Unit,
|
||||
private val mode: LoadingStatusMode,
|
||||
) : Transformer<YieldSupplyUM> {
|
||||
|
||||
override fun transform(prevState: YieldSupplyUM): YieldSupplyUM {
|
||||
if (!tokenStatus.isActive) return YieldSupplyUM.Unavailable
|
||||
|
||||
return when (mode) {
|
||||
LoadingStatusMode.Initial -> {
|
||||
YieldSupplyUM.Initial(
|
||||
title = resourceReference(
|
||||
id = R.string.yield_module_token_details_earn_notification_title,
|
||||
formatArgs = wrappedList(tokenStatus.apy),
|
||||
),
|
||||
onClick = onStartEarningClick,
|
||||
)
|
||||
}
|
||||
LoadingStatusMode.LoadApy -> {
|
||||
if (prevState is YieldSupplyUM.Content) {
|
||||
prevState.copy(
|
||||
rewardsApy = stringReference("${tokenStatus.apy}%"),
|
||||
)
|
||||
} else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,16 +8,19 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.decompose.ComposableModularContentComponent
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.yield.supply.impl.subcomponents.active.model.YieldSupplyActiveModel
|
||||
import com.tangem.features.yield.supply.impl.subcomponents.active.ui.YieldSupplyActiveContent
|
||||
import com.tangem.features.yield.supply.impl.subcomponents.active.ui.YieldSupplyActiveTitle
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.chart.DefaultYieldSupplyChartComponent
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
internal class YieldSupplyActiveComponent(
|
||||
|
|
@ -26,6 +29,12 @@ internal class YieldSupplyActiveComponent(
|
|||
) : ComposableModularContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: YieldSupplyActiveModel = getOrCreateModel(params = params)
|
||||
private val chartComponent = DefaultYieldSupplyChartComponent(
|
||||
appComponentContext = child("chartComponent"),
|
||||
params = DefaultYieldSupplyChartComponent.Params(
|
||||
cryptoCurrency = params.cryptoCurrencyStatusFlow.value.currency as CryptoCurrency.Token,
|
||||
),
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Title() {
|
||||
|
|
@ -37,7 +46,12 @@ internal class YieldSupplyActiveComponent(
|
|||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val isBalanceHidden by params.isBalanceHiddenFlow.collectAsStateWithLifecycle()
|
||||
|
||||
YieldSupplyActiveContent(state = state, isBalanceHidden = isBalanceHidden, modifier = Modifier)
|
||||
YieldSupplyActiveContent(
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
chartComponent = chartComponent,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -10,4 +10,5 @@ internal data class YieldSupplyActiveContentUM(
|
|||
val subtitle: TextReference,
|
||||
val subtitleLink: TextReference,
|
||||
val notificationUM: NotificationUM?,
|
||||
val apy: TextReference? = null,
|
||||
)
|
||||
|
|
@ -5,18 +5,22 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetProtocolBalanceUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveComponent
|
||||
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
|
|
@ -24,6 +28,7 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val yieldSupplyGetProtocolBalanceUseCase: YieldSupplyGetProtocolBalanceUseCase,
|
||||
private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params: YieldSupplyActiveComponent.Params = paramsContainer.require()
|
||||
|
|
@ -87,6 +92,8 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
null
|
||||
}
|
||||
|
||||
loadApy()
|
||||
|
||||
uiState.update {
|
||||
it.copy(
|
||||
notificationUM = approvalNotification,
|
||||
|
|
@ -104,6 +111,21 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun loadApy() {
|
||||
val cryptoCurrencyToken = cryptoCurrency as? CryptoCurrency.Token ?: return
|
||||
modelScope.launch(dispatchers.default) {
|
||||
yieldSupplyGetTokenStatusUseCase(cryptoCurrencyToken).onRight { tokenStatus ->
|
||||
uiState.update {
|
||||
it.copy(
|
||||
apy = TextReference.Str("${tokenStatus.apy}%"),
|
||||
)
|
||||
}
|
||||
}.onLeft {
|
||||
Timber.e("Error loading token status")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val AAVEV3_PREFIX = "a"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -15,6 +16,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.withLink
|
||||
|
|
@ -25,6 +27,7 @@ import androidx.compose.ui.unit.dp
|
|||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -35,6 +38,7 @@ import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSu
|
|||
internal fun YieldSupplyActiveContent(
|
||||
state: YieldSupplyActiveContentUM,
|
||||
isBalanceHidden: Boolean,
|
||||
chartComponent: ComposableContentComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -62,6 +66,9 @@ internal fun YieldSupplyActiveContent(
|
|||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
||||
CurrentApy(state.apy)
|
||||
chartComponent.Content(Modifier.padding(bottom = 12.dp))
|
||||
}
|
||||
YieldSupplyActiveMyFunds(state = state, isBalanceHidden = isBalanceHidden)
|
||||
|
||||
|
|
@ -76,6 +83,45 @@ internal fun YieldSupplyActiveContent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) {
|
||||
Row(modifier = modifier.padding(vertical = 12.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
modifier = modifier.weight(1.0f),
|
||||
text = stringResourceSafe(R.string.yield_module_earn_sheet_current_apy_title),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
AnimatedContent(
|
||||
targetState = apy?.resolveReference(),
|
||||
label = "CurrentApy",
|
||||
) { apyText ->
|
||||
if (apyText == null) {
|
||||
TextShimmer(
|
||||
modifier = modifier.width(56.dp),
|
||||
text = "",
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
} else {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_arrow_up_8),
|
||||
tint = TangemTheme.colors.text.accent,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
)
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = apyText,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun YieldSupplyActiveMyFunds(state: YieldSupplyActiveContentUM, isBalanceHidden: Boolean) {
|
||||
Column(
|
||||
|
|
@ -193,7 +239,11 @@ private fun YieldSupplyActiveBottomSheet_Preview(
|
|||
@PreviewParameter(YieldSupplyActiveBottomSheetPreviewProvider::class) params: YieldSupplyActiveContentUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
YieldSupplyActiveContent(params, true)
|
||||
YieldSupplyActiveContent(
|
||||
state = params,
|
||||
isBalanceHidden = true,
|
||||
chartComponent = ComposableContentComponent.EMPTY,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue