Updated on 2026-08-14
This commit is contained in:
parent
82ae40d9f7
commit
b95730613d
4 changed files with 56 additions and 10 deletions
|
|
@ -20,7 +20,9 @@ import com.tangem.features.commonfeatures.impl.choosetoken.market.state.SwapMark
|
|||
import com.tangem.features.commonfeatures.impl.choosetoken.ui.ChooseTokenFullUM
|
||||
import com.tangem.features.commonfeatures.impl.choosetoken.ui.ChooseTokenInitialUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -77,6 +79,13 @@ internal class ChooseTokenModel @Inject constructor(
|
|||
)
|
||||
|
||||
init {
|
||||
if (bridge.settings.isShowMarketBlock) {
|
||||
modelScope.launch {
|
||||
delay(MARKETS_INITIAL_LOAD_DELAY)
|
||||
marketBlockDelegate.loadDefaultMarkets()
|
||||
}
|
||||
}
|
||||
|
||||
addToPortfolioManager.onDismiss.receiveAsFlow()
|
||||
.onEach { marketBlockDelegate.addToPortfolioSlot.dismiss() }
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -128,5 +137,8 @@ internal class ChooseTokenModel @Inject constructor(
|
|||
|
||||
companion object {
|
||||
const val DEBOUNCE_SEARCH_DELAY = 500L
|
||||
|
||||
/** Roughly the bottom sheet entrance animation duration. */
|
||||
private const val MARKETS_INITIAL_LOAD_DELAY = 400L
|
||||
}
|
||||
}
|
||||
|
|
@ -126,9 +126,6 @@ internal class MarketBlockDelegate @AssistedInject constructor(
|
|||
}
|
||||
.launchIn(modelScope)
|
||||
|
||||
// Initial load of default markets
|
||||
defaultMarketsListManager.reload()
|
||||
|
||||
visibleMarketItemIds
|
||||
.mapNotNull { rawIDS ->
|
||||
if (rawIDS.isNotEmpty()) {
|
||||
|
|
@ -157,6 +154,15 @@ internal class MarketBlockDelegate @AssistedInject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the initial load of the default markets list. Not invoked in [init] on purpose:
|
||||
* the caller decides *if* (market block may be disabled entirely, e.g. Transfer flow) and
|
||||
* *when* (deferred past the bottom sheet entrance animation, [REDACTED_TASK_KEY]) to trigger it.
|
||||
*/
|
||||
fun loadDefaultMarkets() {
|
||||
defaultMarketsListManager.reload()
|
||||
}
|
||||
|
||||
private fun createDefaultMarketsFlow(): Flow<SwapMarketState> {
|
||||
val marketsTitle = TextReference.Res(R.string.markets_pulse_common_title)
|
||||
return combine(
|
||||
|
|
|
|||
|
|
@ -19,12 +19,15 @@ import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult
|
|||
import com.tangem.features.commonfeatures.api.choosetoken.model.TokenListUMData
|
||||
import com.tangem.features.commonfeatures.impl.choosetoken.SettingContextUseCase
|
||||
import com.tangem.features.commonfeatures.impl.choosetoken.converter.ChooseTokenListItemConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.mapNotNullValues
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -33,6 +36,7 @@ internal class PortfolioListBlockDelegate @AssistedInject constructor(
|
|||
private val settingContext: SettingContextUseCase,
|
||||
private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
@Assisted private val modelScope: CoroutineScope,
|
||||
@Assisted private val searchQueryState: StateFlow<SearchQuery>,
|
||||
@Assisted private val featureSettings: ChooseTokenBridge.Settings,
|
||||
|
|
@ -48,6 +52,8 @@ internal class PortfolioListBlockDelegate @AssistedInject constructor(
|
|||
|
||||
val portfolioList: SharedFlow<Map<UserWalletId, TokenListUMData>> = buildDataFlow()
|
||||
.distinctUntilChanged()
|
||||
.throttleLatest(windowMs = UM_UPDATES_THROTTLE_MS)
|
||||
.flowOn(dispatchers.default)
|
||||
.shareIn(modelScope, SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
private fun buildDataFlow(): Flow<Map<UserWalletId, TokenListUMData>> = channelFlow {
|
||||
|
|
@ -153,4 +159,13 @@ internal interface ClickIntents {
|
|||
fun onAccountExpandClick(account: Account)
|
||||
|
||||
fun onAccountCollapseClick(account: Account)
|
||||
}
|
||||
|
||||
private const val UM_UPDATES_THROTTLE_MS = 100L
|
||||
|
||||
/** Emits the first value immediately, then at most one (latest) value per [windowMs]. */
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun <T> Flow<T>.throttleLatest(windowMs: Long): Flow<T> = conflate().transformLatest { value ->
|
||||
emit(value)
|
||||
delay(windowMs)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.commonfeatures.impl.choosetoken.ui
|
|||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.BoundsTransform
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.animation.SharedTransitionScope
|
||||
import androidx.compose.animation.SharedTransitionScope.ResizeMode.Companion.scaleToBounds
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.animateIntAsState
|
||||
|
|
@ -160,7 +161,7 @@ private fun Content(state: ChooseTokenFullUM, modifier: Modifier = Modifier) {
|
|||
val nestedScrollConnection = rememberHideKeyboardNestedScrollConnection()
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
TangemSharedTransitionLayout(modifier) {
|
||||
Box(modifier) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
|
@ -228,10 +229,13 @@ private fun SetupMarketScrollTracker(marketsState: SwapMarketState, lazyListStat
|
|||
|
||||
@Composable
|
||||
private fun VisibleItemsTracker(lazyListState: LazyListState, marketState: SwapMarketState.Content) {
|
||||
val visibleItems by remember {
|
||||
val keyToId = remember(marketState.items) {
|
||||
marketState.items.associateBy({ it.getComposeKey() }, { it.id })
|
||||
}
|
||||
val visibleItems by remember(keyToId) {
|
||||
derivedStateOf {
|
||||
lazyListState.layoutInfo.visibleItemsInfo.mapNotNull { itemInfo ->
|
||||
marketState.items.find { it.getComposeKey() == itemInfo.key }?.id
|
||||
(itemInfo.key as? String)?.let(keyToId::get)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -476,7 +480,7 @@ private fun AccountRow(
|
|||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ProvideSharedTransitionScope(Modifier.weight(1f)) {
|
||||
AccountRowSharedTransitionLayout(Modifier.weight(1f)) {
|
||||
val iconSharedContentState = rememberSharedContentState(key = "account-icon-${portfolio.id}")
|
||||
val titleSharedContentState = rememberSharedContentState(key = "account-title-${portfolio.id}")
|
||||
val boundsTransform = BoundsTransform { _, _ -> tween(ACCOUNT_BOUNDS_ANIM_MS) }
|
||||
|
|
@ -528,9 +532,7 @@ private fun AccountRow(
|
|||
)
|
||||
val startStyle = TangemTheme.typography2.captionSemibold12
|
||||
val stopStyle = TangemTheme.typography2.bodySemibold16
|
||||
val textStyle by remember(animationFraction.value) {
|
||||
derivedStateOf { lerp(startStyle, stopStyle, animationFraction.value) }
|
||||
}
|
||||
val textStyle = lerp(startStyle, stopStyle, animationFraction.value)
|
||||
val resizedTitle = when (val titleUM = tokenRowUM.titleUM) {
|
||||
is TangemTokenRowUM.TitleUM.Content -> titleUM.copy(
|
||||
text = styledStringReference(
|
||||
|
|
@ -579,6 +581,17 @@ private fun AccountRow(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
private fun AccountRowSharedTransitionLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable SharedTransitionScope.() -> Unit,
|
||||
) {
|
||||
TangemSharedTransitionLayout(modifier) {
|
||||
ProvideSharedTransitionScope(content = content)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountTail(isExpanded: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue