Updated on 2026-08-14
This commit is contained in:
commit
536dcb4f3e
9 changed files with 382 additions and 2 deletions
|
|
@ -14,11 +14,11 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.SpacerW32
|
||||
import com.tangem.core.ui.components.TangemSwitch
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsItemsFactory
|
||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item
|
||||
import com.tangem.core.ui.components.TangemSwitch
|
||||
|
||||
@Composable
|
||||
internal fun SettingsSwitchItem(item: Item.Switch, modifier: Modifier = Modifier) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.compose.material3.ModalBottomSheet
|
|||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -20,6 +21,7 @@ import kotlinx.coroutines.launch
|
|||
@Composable
|
||||
inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
|
||||
config: TangemBottomSheetConfig,
|
||||
contentColor: Color = TangemTheme.colors.background.primary,
|
||||
crossinline content: @Composable ColumnScope.(T) -> Unit,
|
||||
) {
|
||||
var isVisible by remember { mutableStateOf(value = config.isShow) }
|
||||
|
|
@ -29,7 +31,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
|
|||
ModalBottomSheet(
|
||||
onDismissRequest = config.onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
containerColor = contentColor,
|
||||
shape = TangemTheme.shapes.bottomSheetLarge,
|
||||
dragHandle = { TangemBottomSheetDraggableHeader() },
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.managetokens.presentation.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
|
||||
@Composable
|
||||
internal fun ChooseWalletBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet<ChooseWalletBottomSheetConfig>(
|
||||
config = config,
|
||||
contentColor = TangemTheme.colors.background.tertiary,
|
||||
) {
|
||||
ChooseWalletScreen(state = it.chooseWalletState)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ChooseWalletBottomSheetConfig(
|
||||
val chooseWalletState: ChooseWalletState.Choose,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.state
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
import com.tangem.managetokens.presentation.common.state.Event
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal data class ManageTokensState(
|
||||
val searchBarState: SearchBarState,
|
||||
val tokens: Flow<PagingData<TokenItemState>>,
|
||||
val isLoading: Boolean,
|
||||
val addCustomTokenButton: AddCustomTokenButton,
|
||||
val chooseWalletState: ChooseWalletState,
|
||||
val derivationNotification: DerivationNotificationState? = null,
|
||||
val selectedToken: TokenItemState.Loaded? = null,
|
||||
val showChooseWalletScreen: Boolean = false,
|
||||
val event: StateEvent<Event>,
|
||||
)
|
||||
|
||||
data class AddCustomTokenButton(
|
||||
val isVisible: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.state.previewdata
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.managetokens.presentation.common.state.previewdata.ChooseWalletStatePreviewData
|
||||
import com.tangem.managetokens.presentation.managetokens.state.AddCustomTokenButton
|
||||
import com.tangem.managetokens.presentation.managetokens.state.ManageTokensState
|
||||
import com.tangem.managetokens.presentation.managetokens.state.SearchBarState
|
||||
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
internal object ManageTokensStatePreviewData {
|
||||
val loadedState: ManageTokensState
|
||||
get() = ManageTokensState(
|
||||
searchBarState = searchState,
|
||||
tokens = flowOf(PagingData.from(tokens)),
|
||||
isLoading = false,
|
||||
addCustomTokenButton = AddCustomTokenButton(true, {}),
|
||||
derivationNotification = DerivationNotificationStatePreviewData.state,
|
||||
event = consumedEvent(),
|
||||
chooseWalletState = ChooseWalletStatePreviewData.state,
|
||||
)
|
||||
|
||||
val loadingState: ManageTokensState
|
||||
get() = loadedState.copy(isLoading = true)
|
||||
|
||||
private val tokens: List<TokenItemState>
|
||||
get() = listOf(
|
||||
TokenItemStatePreviewData.loadedPriceDown,
|
||||
TokenItemStatePreviewData.loadedPriceUp,
|
||||
)
|
||||
|
||||
private val searchState: SearchBarState
|
||||
get() = SearchBarState(
|
||||
query = "",
|
||||
onQueryChange = {},
|
||||
active = false,
|
||||
onActiveChange = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
|
||||
|
||||
@Composable
|
||||
internal fun ChooseNetworkBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet<ChooseNetworkBottomSheetConfig>(
|
||||
config = config,
|
||||
contentColor = TangemTheme.colors.background.tertiary,
|
||||
) {
|
||||
ChooseNetworkScreen(state = it.selectedToken, walletState = it.chooseWalletState)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ChooseNetworkBottomSheetConfig(
|
||||
val selectedToken: TokenItemState.Loaded,
|
||||
val chooseWalletState: ChooseWalletState,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.ui
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.managetokens.presentation.common.state.AlertState
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
import com.tangem.managetokens.presentation.common.ui.ChooseWalletBottomSheet
|
||||
import com.tangem.managetokens.presentation.common.ui.ChooseWalletBottomSheetConfig
|
||||
import com.tangem.managetokens.presentation.common.ui.EventEffect
|
||||
import com.tangem.managetokens.presentation.common.ui.components.Alert
|
||||
import com.tangem.managetokens.presentation.managetokens.state.ManageTokensState
|
||||
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
|
||||
import com.tangem.managetokens.presentation.managetokens.state.previewdata.ManageTokensStatePreviewData
|
||||
import com.tangem.managetokens.presentation.managetokens.ui.components.DerivationNotification
|
||||
import com.tangem.managetokens.presentation.managetokens.ui.components.TokensList
|
||||
import com.tangem.managetokens.presentation.managetokens.ui.components.TokensSearchBar
|
||||
|
||||
@Composable
|
||||
internal fun ManageTokensScreen(state: ManageTokensState) {
|
||||
var alertState by remember { mutableStateOf<AlertState?>(value = null) }
|
||||
|
||||
EventEffect(
|
||||
event = state.event,
|
||||
onAlertStateSet = { alertState = it },
|
||||
)
|
||||
alertState?.let {
|
||||
Alert(state = it, onDismiss = { alertState = null })
|
||||
}
|
||||
|
||||
Content(state)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(state: ManageTokensState) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.padding(top = TangemTheme.dimens.spacing32),
|
||||
) {
|
||||
Column {
|
||||
val listState = rememberLazyListState()
|
||||
val raiseSearchBar by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } }
|
||||
|
||||
val elevation by animateDpAsState(
|
||||
targetValue = if (raiseSearchBar) {
|
||||
TangemTheme.dimens.elevation8
|
||||
} else {
|
||||
TangemTheme.dimens.elevation0
|
||||
},
|
||||
label = "top_bar_elevation",
|
||||
)
|
||||
Surface(
|
||||
elevation = elevation,
|
||||
modifier = Modifier,
|
||||
) {
|
||||
TokensSearchBar(
|
||||
state = state.searchBarState,
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing20),
|
||||
)
|
||||
}
|
||||
val tokens = state.tokens.collectAsLazyPagingItems()
|
||||
TokensList(tokens = tokens, addCustomTokenButton = state.addCustomTokenButton)
|
||||
}
|
||||
state.derivationNotification?.let {
|
||||
DerivationNotification(
|
||||
config = it.config,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter),
|
||||
)
|
||||
}
|
||||
state.selectedToken?.let { selectedToken ->
|
||||
ManageTokensBottomSheet(selectedToken = selectedToken, state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ManageTokensBottomSheet(selectedToken: TokenItemState.Loaded, state: ManageTokensState) {
|
||||
if (state.showChooseWalletScreen && state.chooseWalletState is ChooseWalletState.Choose) {
|
||||
val config = TangemBottomSheetConfig(
|
||||
isShow = true,
|
||||
content = ChooseWalletBottomSheetConfig(state.chooseWalletState),
|
||||
onDismissRequest = state.chooseWalletState.onCloseChoosingWalletClick,
|
||||
)
|
||||
ChooseWalletBottomSheet(config)
|
||||
} else {
|
||||
val config = TangemBottomSheetConfig(
|
||||
isShow = true,
|
||||
content = ChooseNetworkBottomSheetConfig(
|
||||
selectedToken = selectedToken,
|
||||
chooseWalletState = state.chooseWalletState,
|
||||
),
|
||||
onDismissRequest = selectedToken.chooseNetworkState.onCloseChooseNetworkScreen,
|
||||
)
|
||||
ChooseNetworkBottomSheet(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_ManageTokensScreen_LightTheme(
|
||||
@PreviewParameter(ManageTokensConfigProvider::class)
|
||||
state: ManageTokensState,
|
||||
) {
|
||||
TangemTheme(isDark = false) {
|
||||
ManageTokensScreen(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_ManageTokensScreen_DarkTheme(
|
||||
@PreviewParameter(ManageTokensConfigProvider::class)
|
||||
state: ManageTokensState,
|
||||
) {
|
||||
TangemTheme(isDark = true) {
|
||||
ManageTokensScreen(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class ManageTokensConfigProvider : CollectionPreviewParameterProvider<ManageTokensState>(
|
||||
collection = listOf(
|
||||
ManageTokensStatePreviewData.loadingState,
|
||||
ManageTokensStatePreviewData.loadedState,
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.managetokens.impl.R
|
||||
|
||||
@Composable
|
||||
internal fun AddCustomTokenButton(onButtonClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size68)
|
||||
.fillMaxWidth()
|
||||
.clickable { onButtonClick() }
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size36)
|
||||
.background(color = TangemTheme.colors.button.secondary, shape = CircleShape),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_plus_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
SpacerW(width = TangemTheme.dimens.spacing12)
|
||||
Text(
|
||||
text = stringResource(id = R.string.add_custom_token_title),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun AddCustomTokenButton_Preview_Light() {
|
||||
TangemTheme(isDark = false) {
|
||||
AddCustomTokenButton(onButtonClick = { })
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun AddCustomTokenButton_Preview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
AddCustomTokenButton(onButtonClick = { })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.managetokens.impl.R
|
||||
import com.tangem.managetokens.presentation.managetokens.state.AddCustomTokenButton
|
||||
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
|
||||
|
||||
private const val PLACEHOLDER_ITEMS_COUNT = 50
|
||||
|
||||
@Composable
|
||||
internal fun TokensList(tokens: LazyPagingItems<TokenItemState>, addCustomTokenButton: AddCustomTokenButton) {
|
||||
LazyColumn {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(id = R.string.manage_tokens_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
|
||||
if (tokens.loadState.refresh is LoadState.Loading) {
|
||||
items(PLACEHOLDER_ITEMS_COUNT) {
|
||||
TokenRowItem(state = TokenItemState.Loading(it.toString()))
|
||||
}
|
||||
} else {
|
||||
val tokensList = tokens.itemSnapshotList
|
||||
if (tokensList.isEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(id = R.string.manage_tokens_nothing_found),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(items = tokensList.items, key = TokenItemState::id) { token ->
|
||||
TokenRowItem(state = token)
|
||||
}
|
||||
|
||||
if (addCustomTokenButton.isVisible) {
|
||||
item {
|
||||
AddCustomTokenButton(onButtonClick = { addCustomTokenButton.onClick })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue