Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-28 13:22:03 +04:00
parent fea0ea5890
commit 142ba20e35
11 changed files with 203 additions and 52 deletions

View file

@ -1,11 +1,13 @@
package com.tangem.core.ui.decorations
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import com.tangem.core.ui.res.TangemTheme
@ -15,46 +17,49 @@ fun Modifier.roundedShapeItemDecoration(
lastIndex: Int,
addDefaultPadding: Boolean = true,
radius: Dp = TangemTheme.dimens.radius16,
backgroundColor: Color? = null,
): Modifier = composed {
val modifier = if (addDefaultPadding) this.padding(horizontal = TangemTheme.dimens.spacing16) else this
val applyTopPadding: @Composable Modifier.() -> Modifier = {
if (addDefaultPadding) {
padding(top = TangemTheme.dimens.spacing12)
} else {
this
}
}
val applyShape: Modifier.(shape: RoundedCornerShape?) -> Modifier = { shape ->
if (backgroundColor != null) {
if (shape != null) {
background(color = backgroundColor, shape = shape)
} else {
background(color = backgroundColor)
}
} else {
if (shape != null) {
clip(shape = shape)
} else {
this
}
}
}
val isSingleItem = currentIndex == 0 && lastIndex == 0
when {
isSingleItem -> {
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing12)
} else {
Modifier
},
)
.clip(shape = RoundedCornerShape(radius))
.applyTopPadding()
.applyShape(RoundedCornerShape(radius))
}
currentIndex == 0 -> {
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing12)
} else {
Modifier
},
)
.clip(
shape = RoundedCornerShape(
topStart = radius,
topEnd = radius,
),
)
.applyTopPadding()
.applyShape(RoundedCornerShape(topStart = radius, topEnd = radius))
}
currentIndex == lastIndex -> {
modifier
.clip(
shape = RoundedCornerShape(
bottomStart = radius,
bottomEnd = radius,
),
)
modifier.applyShape(RoundedCornerShape(bottomStart = radius, bottomEnd = radius))
}
else -> modifier
else -> modifier.applyShape(null)
}
}

View file

@ -0,0 +1,46 @@
package com.tangem.feature.wallet.presentation.selecttoken
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.feature.wallet.presentation.selecttoken.ui.SelectToken
import com.tangem.feature.wallet.presentation.tokenlist.TokenListComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultSelectTokenComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
tokenListComponentFactory: TokenListComponent.Factory,
@Assisted private val params: SelectTokenComponent.Params,
) : AppComponentContext by appComponentContext, SelectTokenComponent {
private val tokenListComponent: TokenListComponent = tokenListComponentFactory.create(
context = child(key = "token_list"),
params = TokenListComponent.Params(
hasSearchBar = params.hasSearchBar,
userWalletId = params.userWalletId,
onTokenClick = params.onTokenClick,
),
)
@Composable
override fun Content(modifier: Modifier) {
SelectToken(
titleResId = params.titleResId,
onBackClick = router::pop,
tokenListComponent = tokenListComponent,
modifier = modifier,
)
}
@AssistedFactory
interface Factory : SelectTokenComponent.Factory {
override fun create(
context: AppComponentContext,
params: SelectTokenComponent.Params,
): DefaultSelectTokenComponent
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.feature.wallet.presentation.selecttoken
import androidx.annotation.StringRes
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId
/**
* Select token component
*
[REDACTED_AUTHOR]
*/
interface SelectTokenComponent : ComposableContentComponent {
interface Factory : ComponentFactory<Params, SelectTokenComponent>
/**
* Params
*
* @property hasSearchBar flag that indicates if search bar should be shown
* @property userWalletId id of multi-currency wallet
* @property titleResId resource id of title
* @property onTokenClick callback for token click
*/
data class Params(
val hasSearchBar: Boolean,
val userWalletId: UserWalletId,
@StringRes val titleResId: Int,
val onTokenClick: (CryptoCurrencyStatus) -> Unit,
)
}

View file

@ -0,0 +1,18 @@
package com.tangem.feature.wallet.presentation.selecttoken.di
import com.tangem.feature.wallet.presentation.selecttoken.DefaultSelectTokenComponent
import com.tangem.feature.wallet.presentation.selecttoken.SelectTokenComponent
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 SelectTokenComponentModule {
@Binds
@Singleton
fun bindSelectTokenComponentFactory(factory: DefaultSelectTokenComponent.Factory): SelectTokenComponent.Factory
}

View file

@ -0,0 +1,45 @@
package com.tangem.feature.wallet.presentation.selecttoken.ui
import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.tokenlist.TokenListComponent
@Composable
internal fun SelectToken(
@StringRes titleResId: Int,
onBackClick: () -> Unit,
tokenListComponent: TokenListComponent,
modifier: Modifier = Modifier,
) {
BackHandler(onBack = onBackClick)
Column(
modifier = modifier
.background(TangemTheme.colors.background.secondary)
.imePadding()
.systemBarsPadding(),
) {
AppBarWithBackButton(
onBackClick = onBackClick,
text = stringResource(id = titleResId),
iconRes = R.drawable.ic_close_24,
)
tokenListComponent.Content(
contentPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp),
modifier = Modifier,
)
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.tokenlist
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
@ -22,10 +23,10 @@ internal class DefaultTokenListComponent @AssistedInject constructor(
private val model: TokenListModel = getOrCreateModel(params)
@Composable
override fun Content(modifier: Modifier) {
override fun Content(contentPadding: PaddingValues, modifier: Modifier) {
val state by model.state.collectAsStateWithLifecycle()
TokenList(state = state, modifier = modifier)
TokenList(state = state, contentPadding = contentPadding, modifier = modifier)
}
@AssistedFactory

View file

@ -1,14 +1,19 @@
package com.tangem.feature.wallet.presentation.tokenlist
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId
/** Token list component that present list of token for multi-currency wallet */
@Stable
internal interface TokenListComponent : ComposableContentComponent {
internal interface TokenListComponent {
@Composable
fun Content(contentPadding: PaddingValues, modifier: Modifier)
/** Component factory */
interface Factory : ComponentFactory<Params, TokenListComponent>
@ -16,9 +21,9 @@ internal interface TokenListComponent : ComposableContentComponent {
/**
* Params
*
* @property hasSearchBar flag that indicates if search bar should be shown
* @property userWalletId id of multi-currency wallet
* @property onTokenClick callback for token click
* @property hasSearchBar flag that indicates if search bar should be shown
* @property userWalletId id of multi-currency wallet
* @property onTokenClick callback for token click
*/
data class Params(
val hasSearchBar: Boolean,

View file

@ -1,8 +1,8 @@
package com.tangem.feature.wallet.presentation.tokenlist.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
@ -26,8 +26,8 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
[REDACTED_AUTHOR]
*/
@Composable
internal fun TokenList(state: TokenListUM, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
internal fun TokenList(state: TokenListUM, contentPadding: PaddingValues, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier, contentPadding = contentPadding) {
itemsIndexed(
items = state.items,
key = { _, item -> item.id },
@ -42,6 +42,7 @@ internal fun TokenList(state: TokenListUM, modifier: Modifier = Modifier) {
currentIndex = index,
lastIndex = state.items.lastIndex,
addDefaultPadding = false,
backgroundColor = TangemTheme.colors.background.primary,
),
)
},
@ -55,10 +56,10 @@ private fun Preview_TokenList(@PreviewParameter(PreviewTokenListUMProvider::clas
TangemThemePreview {
TokenList(
state = state,
contentPadding = PaddingValues(all = 16.dp),
modifier = Modifier
.fillMaxWidth()
.background(color = TangemTheme.colors.background.secondary)
.padding(16.dp),
.background(color = TangemTheme.colors.background.secondary),
)
}
}

View file

@ -23,7 +23,11 @@ internal class SearchTokensManager @Inject constructor() {
suspend fun update(value: String) {
coroutineScope {
withDebounce(jobHolder) { _query.value = value }
if (value.isEmpty()) {
_query.value = value
} else {
withDebounce(jobHolder) { _query.value = value }
}
}
}
}

View file

@ -64,6 +64,7 @@ private fun LazyListScope.contentItems(
modifier = modifier.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = items.lastIndex,
backgroundColor = TangemTheme.colors.background.primary,
),
)
},

View file

@ -1,6 +1,5 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -8,7 +7,6 @@ import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.fields.SearchBar
import com.tangem.core.ui.components.token.TokenItem
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.common.component.NetworkTitleItem
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState
@ -26,24 +24,19 @@ internal fun MultiCurrencyContentItem(
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
val modifierWithBackground = modifier.background(color = TangemTheme.colors.background.primary)
when (state) {
is TokensListItemState.NetworkGroupTitle -> {
NetworkTitleItem(networkName = state.name.resolveReference(), modifier = modifierWithBackground)
NetworkTitleItem(networkName = state.name.resolveReference(), modifier = modifier)
}
is TokensListItemState.Token -> {
TokenItem(
state = state.state,
isBalanceHidden = isBalanceHidden,
modifier = modifierWithBackground,
modifier = modifier,
)
}
is TokensListItemState.SearchBar -> {
SearchBar(
state = state.searchBarUM,
modifier = modifierWithBackground.padding(all = 12.dp),
)
SearchBar(state = state.searchBarUM, modifier = modifier.padding(all = 12.dp))
}
}
}