Updated on 2026-08-14
This commit is contained in:
parent
0cc270adc0
commit
47412b1d63
28 changed files with 1110 additions and 2 deletions
|
|
@ -29,6 +29,7 @@ import com.tangem.tap.features.tokens.addCustomToken.AddCustomTokenFragment
|
|||
import com.tangem.tap.features.tokens.ui.AddTokensFragment
|
||||
import com.tangem.tap.features.wallet.ui.WalletDetailsFragment
|
||||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.tap.features.walletSelector.ui.WalletSelectorBottomSheetFragment
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeFragment
|
||||
import com.tangem.wallet.R
|
||||
import timber.log.Timber
|
||||
|
|
@ -52,10 +53,14 @@ fun FragmentActivity.openFragment(
|
|||
}
|
||||
if (screen.isDialogFragment) {
|
||||
(fragment as DialogFragment).show(transaction, screen.name)
|
||||
if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null)
|
||||
if (addToBackstack) {
|
||||
transaction.addToBackStack(null)
|
||||
}
|
||||
} else {
|
||||
transaction.replace(R.id.fragment_container, fragment, screen.name)
|
||||
if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null)
|
||||
if (addToBackstack && (screen != AppScreen.Home && screen != AppScreen.Welcome)) {
|
||||
transaction.addToBackStack(null)
|
||||
}
|
||||
transaction.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
|
@ -114,5 +119,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.ReferralProgram -> ReferralFragment()
|
||||
AppScreen.Welcome -> WelcomeFragment()
|
||||
AppScreen.SaveWallet -> SaveWalletBottomSheetFragment()
|
||||
AppScreen.WalletSelector -> WalletSelectorBottomSheetFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ import com.tangem.tap.features.tokens.redux.TokensState
|
|||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeState
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
|
||||
import org.rekotlin.Middleware
|
||||
import org.rekotlin.StateType
|
||||
|
||||
|
|
@ -55,6 +56,7 @@ data class AppState(
|
|||
val shopState: ShopState = ShopState(),
|
||||
val welcomeState: WelcomeState = WelcomeState(),
|
||||
val saveWalletState: SaveWalletState = SaveWalletState(),
|
||||
val walletSelectorState: WalletSelectorState = WalletSelectorState(),
|
||||
) : StateType {
|
||||
|
||||
val domainState: DomainState
|
||||
|
|
|
|||
|
|
@ -25,4 +25,5 @@ enum class AppScreen(
|
|||
ReferralProgram,
|
||||
Welcome,
|
||||
SaveWallet(isDialogFragment = true),
|
||||
WalletSelector(isDialogFragment = true),
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.tap.features.walletSelector.redux
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface WalletSelectorAction : Action {
|
||||
object UnlockWithBiometry : WalletSelectorAction {
|
||||
object Success : WalletSelectorAction
|
||||
data class Error(val error: TangemError) : WalletSelectorAction
|
||||
}
|
||||
|
||||
data class SelectWallet(
|
||||
val walletId: String,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class RenameWallet(
|
||||
val walletId: String,
|
||||
val newName: String,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class RemoveWallets(
|
||||
val walletIdsToRemove: List<String>,
|
||||
) : WalletSelectorAction
|
||||
|
||||
object AddWallet : WalletSelectorAction {
|
||||
object Success : WalletSelectorAction
|
||||
data class Error(val error: TangemError) : WalletSelectorAction
|
||||
}
|
||||
|
||||
object CloseError : WalletSelectorAction
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.tap.features.walletSelector.redux
|
||||
|
||||
import org.rekotlin.StateType
|
||||
|
||||
class WalletSelectorState : StateType
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.tangem.tap.features.walletSelector.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.SnackbarHost
|
||||
import androidx.compose.material.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.core.ui.fragments.ComposeBottomSheetFragment
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.walletSelector.ui.components.RenameWalletDialogContent
|
||||
import com.tangem.tap.features.walletSelector.ui.components.WalletSelectorScreenContent
|
||||
import com.tangem.tap.features.walletSelector.ui.model.RenameWalletDialog
|
||||
|
||||
internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment<WalletSelectorScreenState>() {
|
||||
private val viewModel by viewModels<WalletSelectorViewModel>()
|
||||
|
||||
@Composable
|
||||
override fun provideState(): State<WalletSelectorScreenState> {
|
||||
return viewModel.state.collectAsState()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(
|
||||
modifier: Modifier,
|
||||
state: WalletSelectorScreenState,
|
||||
) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val errorMessage by rememberUpdatedState(newValue = state.error?.customMessage)
|
||||
val renameWalletDialog by rememberUpdatedState(newValue = state.renameWalletDialog)
|
||||
|
||||
Box(modifier = modifier) {
|
||||
WalletSelectorScreenContent(
|
||||
state = state,
|
||||
onWalletClick = viewModel::walletClicked,
|
||||
onWalletLongClick = viewModel::walletLongClicked,
|
||||
onUnlockClick = viewModel::unlock,
|
||||
onAddCardClick = viewModel::addWallet,
|
||||
onClearSelectedClick = viewModel::cancelWalletsEditing,
|
||||
onEditSelectedWalletClick = viewModel::renameWallet,
|
||||
onDeleteSelectedWalletsClick = viewModel::deleteWallets,
|
||||
)
|
||||
|
||||
SnackbarHost(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(vertical = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
|
||||
RenameWalletDialog(dialog = renameWalletDialog)
|
||||
|
||||
LaunchedEffect(key1 = errorMessage) {
|
||||
errorMessage?.let {
|
||||
snackbarHostState.showSnackbar(it)
|
||||
viewModel.closeError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenameWalletDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
dialog: RenameWalletDialog?,
|
||||
) {
|
||||
if (dialog == null) return
|
||||
RenameWalletDialogContent(modifier, dialog)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.features.walletSelector.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.features.walletSelector.ui.model.RenameWalletDialog
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
|
||||
@Immutable
|
||||
internal data class WalletSelectorScreenState(
|
||||
val wallets: List<UserWalletItem> = listOf(),
|
||||
val selectedWalletId: String? = null,
|
||||
val fiatCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val isLocked: Boolean = true,
|
||||
val editingWalletsIds: List<String> = listOf(),
|
||||
val renameWalletDialog: RenameWalletDialog? = null,
|
||||
val showAddCardProgress: Boolean = false,
|
||||
val showUnlockProgress: Boolean = false,
|
||||
val error: TangemError? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package com.tangem.tap.features.walletSelector.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
|
||||
import com.tangem.tap.features.walletSelector.ui.model.RenameWalletDialog
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber<WalletSelectorState> {
|
||||
private val stateInternal = MutableStateFlow(WalletSelectorScreenState())
|
||||
val state: StateFlow<WalletSelectorScreenState> = stateInternal
|
||||
|
||||
init {
|
||||
subscribeToStoreChanges()
|
||||
bootstrapWalletListChanges()
|
||||
bootstrapWalletsStoresChanges()
|
||||
}
|
||||
|
||||
fun unlock() {
|
||||
store.dispatch(WalletSelectorAction.UnlockWithBiometry)
|
||||
}
|
||||
|
||||
fun addWallet() {
|
||||
store.dispatch(WalletSelectorAction.AddWallet)
|
||||
}
|
||||
|
||||
fun walletClicked(walletId: String) = with(state.value) {
|
||||
when {
|
||||
isLocked -> Unit
|
||||
editingWalletsIds.isNotEmpty() && !editingWalletsIds.contains(walletId) -> {
|
||||
editWallet(walletId)
|
||||
}
|
||||
editingWalletsIds.isNotEmpty() && editingWalletsIds.contains(walletId) -> {
|
||||
cancelWalletEditing(walletId)
|
||||
}
|
||||
selectedWalletId != walletId -> {
|
||||
store.dispatch(WalletSelectorAction.SelectWallet(walletId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun walletLongClicked(walletId: String) = with(state.value) {
|
||||
if (!isLocked && editingWalletsIds.isEmpty()) {
|
||||
editWallet(walletId)
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelWalletsEditing() {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
editingWalletsIds = emptyList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun renameWallet() = with(state.value) {
|
||||
if (editingWalletsIds.isNotEmpty() && renameWalletDialog == null) {
|
||||
val editedWalletId = editingWalletsIds.first()
|
||||
val editedWallet = wallets.find { it.id == editedWalletId }
|
||||
|
||||
if (editedWallet != null) {
|
||||
val dialog = RenameWalletDialog(
|
||||
currentName = editedWallet.name,
|
||||
onApply = { newName ->
|
||||
store.dispatch(WalletSelectorAction.RenameWallet(editedWalletId, newName))
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
renameWalletDialog = null,
|
||||
editingWalletsIds = emptyList(),
|
||||
)
|
||||
}
|
||||
},
|
||||
onCancel = {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
renameWalletDialog = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
renameWalletDialog = dialog,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteWallets() = with(state.value) {
|
||||
if (editingWalletsIds.isNotEmpty()) {
|
||||
store.dispatch(WalletSelectorAction.RemoveWallets(walletIdsToRemove = editingWalletsIds))
|
||||
}
|
||||
}
|
||||
|
||||
fun closeError() {
|
||||
store.dispatch(WalletSelectorAction.CloseError)
|
||||
}
|
||||
|
||||
override fun newState(state: WalletSelectorState) {
|
||||
// TODO: Update UI state
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
private fun editWallet(walletId: String) {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
editingWalletsIds = prevState.editingWalletsIds + walletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelWalletEditing(walletId: String) {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
editingWalletsIds = prevState.editingWalletsIds - walletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeToStoreChanges() {
|
||||
store.subscribe(this) { appState ->
|
||||
appState.skip { old, new -> old.walletSelectorState == new.walletSelectorState }
|
||||
.select { it.walletSelectorState }
|
||||
}
|
||||
}
|
||||
|
||||
private fun bootstrapWalletListChanges() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private fun bootstrapWalletsStoresChanges() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import com.tangem.tap.features.walletSelector.ui.WalletSelectorScreenState
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
|
||||
internal object MockData {
|
||||
private val userWallet = UserWalletItem(
|
||||
id = "wallet_1",
|
||||
balance = UserWalletItem.Balance(
|
||||
amount = "6781.05 $",
|
||||
isLoading = false,
|
||||
),
|
||||
name = "Wallet",
|
||||
imageUrl = "https://app.tangem.com/cards/card_default.png",
|
||||
type = UserWalletItem.Type.MultiCurrency(tokensCount = 12, cardsInWallet = 3),
|
||||
)
|
||||
|
||||
val userWalletList = listOf(
|
||||
userWallet,
|
||||
userWallet.copy(id = "wallet_2"),
|
||||
userWallet.copy(id = "wallet_3", type = UserWalletItem.Type.MultiCurrency(tokensCount = 2, cardsInWallet = 1)),
|
||||
userWallet.copy(id = "wallet_4", type = UserWalletItem.Type.SingleCurrency(tokenName = "Ethereum")),
|
||||
)
|
||||
|
||||
val state = WalletSelectorScreenState(
|
||||
wallets = userWalletList,
|
||||
selectedWalletId = userWalletList[0].id,
|
||||
isLocked = false,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.tangem.core.ui.components.OutlineTextField
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerH24
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.components.TextButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.walletSelector.ui.model.RenameWalletDialog
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun RenameWalletDialogContent(
|
||||
modifier: Modifier = Modifier,
|
||||
dialog: RenameWalletDialog,
|
||||
) {
|
||||
Dialog(onDismissRequest = dialog.onCancel) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(
|
||||
shape = TangemTheme.shapes.roundedCornersLarge,
|
||||
color = TangemTheme.colors.background.plain,
|
||||
)
|
||||
.padding(all = TangemTheme.dimens.spacing24),
|
||||
) {
|
||||
var value by remember {
|
||||
mutableStateOf(TextFieldValue(text = dialog.currentName))
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.user_wallet_list_rename_popup_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH16()
|
||||
OutlineTextField(
|
||||
value = value,
|
||||
label = stringResource(R.string.user_wallet_list_rename_popup_placeholder),
|
||||
onValueChange = { newValue ->
|
||||
value = newValue
|
||||
},
|
||||
)
|
||||
SpacerH24()
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
TextButton(
|
||||
text = stringResource(id = R.string.common_cancel),
|
||||
onClick = dialog.onCancel,
|
||||
)
|
||||
SpacerW8()
|
||||
TextButton(
|
||||
text = stringResource(id = R.string.common_save),
|
||||
enabled = value.text.isNotEmpty() && value.text != dialog.currentName,
|
||||
onClick = {
|
||||
dialog.onApply(value.text)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun RenameWalletDialogContentSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
RenameWalletDialogContent(dialog = RenameWalletDialog("", {}, {}))
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun RenameWalletDialogContentPreview_Light() {
|
||||
TangemTheme {
|
||||
RenameWalletDialogContentSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun RenameWalletDialogContentPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
RenameWalletDialogContentSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,251 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
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.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconRight
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerH24
|
||||
import com.tangem.core.ui.components.SpacerW16
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.walletSelector.ui.WalletSelectorScreenState
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun WalletSelectorScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
state: WalletSelectorScreenState,
|
||||
onWalletClick: (walletId: String) -> Unit,
|
||||
onWalletLongClick: (walletId: String) -> Unit,
|
||||
onUnlockClick: () -> Unit,
|
||||
onAddCardClick: () -> Unit,
|
||||
onClearSelectedClick: () -> Unit,
|
||||
onEditSelectedWalletClick: () -> Unit,
|
||||
onDeleteSelectedWalletsClick: () -> Unit,
|
||||
) {
|
||||
val editingWalletsSize by rememberUpdatedState(newValue = state.editingWalletsIds.size)
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Hand()
|
||||
SpacerH12()
|
||||
if (editingWalletsSize > 0) {
|
||||
EditWalletsBar(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
editingWalletsSize = editingWalletsSize,
|
||||
onClearSelectedClick = onClearSelectedClick,
|
||||
onEditSelectedWalletClick = onEditSelectedWalletClick,
|
||||
onDeleteSelectedWalletsClick = onDeleteSelectedWalletsClick,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.user_wallet_list_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
SpacerH12()
|
||||
WalletList(
|
||||
wallets = state.wallets,
|
||||
selectedWalletId = state.selectedWalletId,
|
||||
checkedWalletIds = state.editingWalletsIds,
|
||||
isLocked = state.isLocked,
|
||||
onWalletClick = onWalletClick,
|
||||
onWalletLongClick = onWalletLongClick,
|
||||
)
|
||||
SpacerH24()
|
||||
if (state.isLocked) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(R.string.user_wallet_list_unlock_all_face_id),
|
||||
showProgress = state.showUnlockProgress,
|
||||
onClick = onUnlockClick,
|
||||
)
|
||||
SpacerH12()
|
||||
}
|
||||
SecondaryButtonIconRight(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(R.string.user_wallet_list_add_button),
|
||||
showProgress = state.showAddCardProgress,
|
||||
icon = painterResource(id = R.drawable.ic_tangem),
|
||||
onClick = onAddCardClick,
|
||||
)
|
||||
SpacerH16()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EditWalletsBar(
|
||||
modifier: Modifier = Modifier,
|
||||
editingWalletsSize: Int,
|
||||
onClearSelectedClick: () -> Unit,
|
||||
onEditSelectedWalletClick: () -> Unit,
|
||||
onDeleteSelectedWalletsClick: () -> Unit,
|
||||
) {
|
||||
val showEditAction = remember(editingWalletsSize) { editingWalletsSize in 1 until 2 }
|
||||
Row(
|
||||
modifier = modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing8)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onClearSelectedClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_close),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = "Unselect wallets",
|
||||
)
|
||||
}
|
||||
SpacerW16()
|
||||
Text(
|
||||
text = stringResource(id = R.string.user_wallet_list_editing_count, editingWalletsSize),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerWMax()
|
||||
if (showEditAction) {
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onEditSelectedWalletClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_pencil_outline_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = "Edit wallet name",
|
||||
)
|
||||
}
|
||||
SpacerW8()
|
||||
}
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onDeleteSelectedWalletsClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_trash),
|
||||
tint = TangemTheme.colors.icon.warning,
|
||||
contentDescription = "Delete selected wallets",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContentSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(color = TangemTheme.colors.background.primary),
|
||||
) {
|
||||
WalletSelectorScreenContent(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.plain,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
),
|
||||
state = MockData.state.copy(isLocked = true),
|
||||
onWalletClick = { /* no-op */ },
|
||||
onWalletLongClick = { /* no-op */ },
|
||||
onUnlockClick = { /* no-op */ },
|
||||
onAddCardClick = { /* no-op */ },
|
||||
onEditSelectedWalletClick = { /* no-op */ },
|
||||
onClearSelectedClick = { /* no-op */ },
|
||||
onDeleteSelectedWalletsClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContentPreview_Light() {
|
||||
TangemTheme {
|
||||
WalletSelectorScreenContentSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContentPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletSelectorScreenContentSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContent_EditWallets_Sample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
WalletSelectorScreenContent(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.plain,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
),
|
||||
state = MockData.state
|
||||
.copy(editingWalletsIds = listOf(MockData.userWalletList[2].id)),
|
||||
onWalletClick = { /* no-op */ },
|
||||
onWalletLongClick = { /* no-op */ },
|
||||
onUnlockClick = { /* no-op */ },
|
||||
onAddCardClick = { /* no-op */ },
|
||||
onEditSelectedWalletClick = { /* no-op */ },
|
||||
onClearSelectedClick = { /* no-op */ },
|
||||
onDeleteSelectedWalletsClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContent_EditWallets_Preview_Light() {
|
||||
TangemTheme {
|
||||
WalletSelectorScreenContent_EditWallets_Sample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContent_EditWallets_Preview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletSelectorScreenContent_EditWallets_Sample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
@file:OptIn(ExperimentalComposeUiApi::class)
|
||||
|
||||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.SpacerH2
|
||||
import com.tangem.core.ui.components.SpacerW6
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun WalletItem(
|
||||
modifier: Modifier = Modifier,
|
||||
wallet: UserWalletItem,
|
||||
isSelected: Boolean,
|
||||
isChecked: Boolean,
|
||||
isLocked: Boolean,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(TangemTheme.dimens.size62)
|
||||
.height(TangemTheme.dimens.size42),
|
||||
) {
|
||||
val cardImageModifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.width(TangemTheme.dimens.size56)
|
||||
.height(TangemTheme.dimens.size32)
|
||||
.clip(TangemTheme.shapes.roundedCornersSmall)
|
||||
SubcomposeAsyncImage(
|
||||
modifier = cardImageModifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(wallet.imageUrl)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
loading = { CardImagePlaceholder(modifier = cardImageModifier) },
|
||||
error = { CardImagePlaceholder(modifier = cardImageModifier) },
|
||||
contentDescription = null,
|
||||
)
|
||||
when {
|
||||
isChecked -> {
|
||||
Box(
|
||||
modifier = cardImageModifier
|
||||
.background(
|
||||
color = TangemTheme.colors.icon.accent.copy(alpha = .5f),
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_check_24),
|
||||
tint = TangemTheme.colors.icon.primary2,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
isSelected -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.size(TangemTheme.dimens.size18)
|
||||
.background(
|
||||
color = Color.White,
|
||||
shape = CircleShape,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(all = (0.5).dp),
|
||||
painter = painterResource(id = R.drawable.ic_check_circle_18),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
SpacerW8()
|
||||
Column(
|
||||
modifier = Modifier.weight(weight = .6f),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = wallet.name,
|
||||
color = if (isSelected) TangemTheme.colors.text.accent else TangemTheme.colors.text.primary1,
|
||||
style = TangemTypography.subtitle1,
|
||||
)
|
||||
SpacerH2()
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = when (val type = wallet.type) {
|
||||
is UserWalletItem.Type.MultiCurrency -> pluralStringResource(
|
||||
id = R.plurals.card_label_card_count,
|
||||
count = type.cardsInWallet,
|
||||
type.cardsInWallet,
|
||||
)
|
||||
|
||||
is UserWalletItem.Type.SingleCurrency -> type.tokenName
|
||||
},
|
||||
style = TangemTheme.typography.caption,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
SpacerW6()
|
||||
if (isLocked) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing2)
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersMedium,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing8,
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
)
|
||||
.size(TangemTheme.dimens.size20),
|
||||
painter = painterResource(id = R.drawable.ic_locked_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
modifier = Modifier.weight(weight = .4f),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = wallet.balance.amount,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
when (val type = wallet.type) {
|
||||
is UserWalletItem.Type.MultiCurrency -> {
|
||||
SpacerH2()
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = pluralStringResource(
|
||||
id = R.plurals.tokens_count,
|
||||
count = type.tokensCount,
|
||||
type.tokensCount,
|
||||
),
|
||||
style = TangemTheme.typography.caption,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
}
|
||||
|
||||
is UserWalletItem.Type.SingleCurrency -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardImagePlaceholder(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Image(
|
||||
modifier = modifier,
|
||||
painter = painterResource(id = R.drawable.card_placeholder_black),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
@file:OptIn(ExperimentalFoundationApi::class)
|
||||
|
||||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
|
||||
@Composable
|
||||
internal fun WalletList(
|
||||
modifier: Modifier = Modifier,
|
||||
wallets: List<UserWalletItem>,
|
||||
selectedWalletId: String?,
|
||||
checkedWalletIds: List<String>,
|
||||
isLocked: Boolean,
|
||||
onWalletClick: (walletId: String) -> Unit,
|
||||
onWalletLongClick: (walletId: String) -> Unit,
|
||||
) {
|
||||
val multiCurrencyWallets = remember(wallets) {
|
||||
wallets.filter { it.type is UserWalletItem.Type.MultiCurrency }
|
||||
}
|
||||
val singleCurrencyWallets = remember(wallets) {
|
||||
wallets.filter { it.type is UserWalletItem.Type.SingleCurrency }
|
||||
}
|
||||
LazyColumn(modifier = modifier) {
|
||||
val walletSection: (List<UserWalletItem>) -> Unit = { wallets ->
|
||||
itemsIndexed(
|
||||
items = wallets,
|
||||
key = { _, item -> item.id },
|
||||
) { index, wallet ->
|
||||
if (index == 0) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing16),
|
||||
text = wallet.type.headerText.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
WalletItem(
|
||||
modifier = Modifier
|
||||
.combinedClickable(
|
||||
onClick = { onWalletClick(wallet.id) },
|
||||
onLongClick = { onWalletLongClick(wallet.id) },
|
||||
)
|
||||
.padding(all = TangemTheme.dimens.spacing16),
|
||||
wallet = wallet,
|
||||
isSelected = wallet.id == selectedWalletId,
|
||||
isChecked = wallet.id in checkedWalletIds,
|
||||
isLocked = isLocked && wallet.id != selectedWalletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
walletSection(multiCurrencyWallets)
|
||||
walletSection(singleCurrencyWallets)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
internal data class RenameWalletDialog(
|
||||
val currentName: String,
|
||||
val onApply: (newName: String) -> Unit,
|
||||
val onCancel: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Immutable
|
||||
internal data class UserWalletItem(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val imageUrl: String,
|
||||
val balance: Balance,
|
||||
val type: Type,
|
||||
) {
|
||||
@Immutable
|
||||
data class Balance(
|
||||
val amount: String,
|
||||
val isLoading: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
val Loading = Balance(
|
||||
amount = "0.00",
|
||||
isLoading = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface Type {
|
||||
val headerText: TextReference
|
||||
|
||||
@Immutable
|
||||
class MultiCurrency(
|
||||
val tokensCount: Int,
|
||||
val cardsInWallet: Int,
|
||||
) : Type {
|
||||
override val headerText: TextReference
|
||||
get() = TextReference.Res(R.string.user_wallet_list_multi_header)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class SingleCurrency(val tokenName: String) : Type {
|
||||
override val headerText: TextReference
|
||||
get() = TextReference.Res(R.string.user_wallet_list_single_header)
|
||||
}
|
||||
}
|
||||
}
|
||||
9
app/src/main/res/drawable/ic_check_24.xml
Normal file
9
app/src/main/res/drawable/ic_check_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_check_circle_18.xml
Normal file
9
app/src/main/res/drawable/ic_check_circle_18.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M9,1.5C4.9,1.5 1.5,4.9 1.5,9C1.5,13.1 4.9,16.5 9,16.5C13.1,16.5 16.5,13.1 16.5,9C16.5,4.9 13.1,1.5 9,1.5ZM7.5,12.8L3.8,9L4.8,7.9L7.5,10.6L13.2,4.9L14.3,6L7.5,12.8Z" />
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_locked_24.xml
Normal file
9
app/src/main/res/drawable/ic_locked_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M12,17C12.53,17 13.039,16.789 13.414,16.414C13.789,16.039 14,15.53 14,15C14,13.89 13.1,13 12,13C11.47,13 10.961,13.211 10.586,13.586C10.211,13.961 10,14.47 10,15C10,15.53 10.211,16.039 10.586,16.414C10.961,16.789 11.47,17 12,17ZM18,8C18.53,8 19.039,8.211 19.414,8.586C19.789,8.961 20,9.47 20,10V20C20,20.53 19.789,21.039 19.414,21.414C19.039,21.789 18.53,22 18,22H6C5.47,22 4.961,21.789 4.586,21.414C4.211,21.039 4,20.53 4,20V10C4,8.89 4.9,8 6,8H7V6C7,4.674 7.527,3.402 8.464,2.464C9.402,1.527 10.674,1 12,1C12.657,1 13.307,1.129 13.913,1.381C14.52,1.632 15.071,2 15.535,2.464C16,2.929 16.368,3.48 16.619,4.087C16.871,4.693 17,5.343 17,6V8H18ZM12,3C11.204,3 10.441,3.316 9.879,3.879C9.316,4.441 9,5.204 9,6V8H15V6C15,5.204 14.684,4.441 14.121,3.879C13.559,3.316 12.796,3 12,3Z" />
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_pencil_outline_24.xml
Normal file
9
app/src/main/res/drawable/ic_pencil_outline_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M14.1,9L15,9.9L5.9,19H5V18.1L14.1,9ZM17.7,3C17.4,3 17.2,3.1 17,3.3L15.1,5.1L18.9,8.9L20.7,7C21.1,6.7 21.1,6 20.7,5.6L18.4,3.3C18.2,3.1 17.9,3 17.7,3ZM14.1,6.2L3,17.3V21H6.8L17.8,9.9L14.1,6.2Z" />
|
||||
</vector>
|
||||
|
|
@ -4,6 +4,7 @@ package com.tangem.core.ui.components
|
|||
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
|
@ -30,6 +31,11 @@ fun SpacerH(height: Dp, modifier: Modifier = Modifier) {
|
|||
Spacer(modifier = modifier.height(height))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH2(modifier: Modifier = Modifier) {
|
||||
SpacerH(2.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerH4(modifier: Modifier = Modifier) {
|
||||
SpacerH(4.dp, modifier)
|
||||
|
|
@ -78,6 +84,11 @@ fun SpacerW4(modifier: Modifier = Modifier) {
|
|||
SpacerW(4.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW6(modifier: Modifier = Modifier) {
|
||||
SpacerW(6.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW8(modifier: Modifier = Modifier) {
|
||||
SpacerW(8.dp, modifier)
|
||||
|
|
@ -97,6 +108,12 @@ fun SpacerW24(modifier: Modifier = Modifier) {
|
|||
fun SpacerW32(modifier: Modifier = Modifier) {
|
||||
SpacerW(32.dp, modifier)
|
||||
}
|
||||
|
||||
// TODO: Refactor with context receivers
|
||||
@Composable
|
||||
fun RowScope.SpacerWMax(modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.weight(1f))
|
||||
}
|
||||
// endregion Vertical
|
||||
|
||||
// region Size
|
||||
|
|
|
|||
|
|
@ -33,13 +33,16 @@ data class TangemDimens internal constructor(
|
|||
val size8: Dp = 8.dp,
|
||||
val size11: Dp = 11.dp,
|
||||
val size16: Dp = 16.dp,
|
||||
val size18: Dp = 18.dp,
|
||||
val size20: Dp = 20.dp,
|
||||
val size24: Dp = 24.dp,
|
||||
val size28: Dp = 28.dp,
|
||||
val size32: Dp = 32.dp,
|
||||
val size40: Dp = 40.dp,
|
||||
val size42: Dp = 42.dp,
|
||||
val size48: Dp = 48.dp,
|
||||
val size56: Dp = 56.dp,
|
||||
val size62: Dp = 62.dp,
|
||||
val size84: Dp = 84.dp,
|
||||
val size86: Dp = 86.dp,
|
||||
val size96: Dp = 96.dp,
|
||||
|
|
|
|||
|
|
@ -3,4 +3,9 @@
|
|||
<item quantity="one">%d card</item>
|
||||
<item quantity="other">%d cards</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="tokens_count">
|
||||
<item quantity="one">%d token</item>
|
||||
<item quantity="other">%d tokens</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -4,4 +4,10 @@
|
|||
<item quantity="many">%d cards</item>
|
||||
<item quantity="other">%d cards</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="tokens_count">
|
||||
<item quantity="one">%d token</item>
|
||||
<item quantity="many">%d tokens</item>
|
||||
<item quantity="other">%d tokens</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -3,4 +3,9 @@
|
|||
<item quantity="one">%d card</item>
|
||||
<item quantity="other">%d cards</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="tokens_count">
|
||||
<item quantity="one">%d token</item>
|
||||
<item quantity="other">%d tokens</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -152,4 +152,12 @@
|
|||
<string name="save_user_wallet_agreement_enroll_biometrics_description">Похоже, что у вас отключена биометрическая авторизация, она необходима для сохранения кошельков</string>
|
||||
<string name="common_enable">Включить</string>
|
||||
<string name="common_cancel">Отмена</string>
|
||||
<string name="user_wallet_list_multi_header">Мультивалютные</string>
|
||||
<string name="user_wallet_list_single_header">Одновалютные</string>
|
||||
<string name="user_wallet_list_rename_popup_title">Переименование кошелька</string>
|
||||
<string name="user_wallet_list_rename_popup_placeholder">Имя кошелька</string>
|
||||
<string name="user_wallet_list_unlock_all_face_id">Разблокировать все с биометрией</string>
|
||||
<string name="user_wallet_list_add_button">Добавить новый кошелек</string>
|
||||
<string name="user_wallet_list_editing_count">%d выбрано</string>
|
||||
<string name="user_wallet_list_title">Мои кошельки</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,4 +5,11 @@
|
|||
<item quantity="many">%d карт</item>
|
||||
<item quantity="other">%d карт</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="tokens_count">
|
||||
<item quantity="one">%d токен</item>
|
||||
<item quantity="few">%d токена</item>
|
||||
<item quantity="many">%d токенов</item>
|
||||
<item quantity="other">%d токенов</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -154,4 +154,12 @@
|
|||
<string name="save_user_wallet_agreement_enroll_biometrics_description">It looks like you have biometric authorization disabled, it is necessary to save wallets</string>
|
||||
<string name="common_enable">Enable</string>
|
||||
<string name="common_cancel">Cancel</string>
|
||||
<string name="user_wallet_list_multi_header">Multi-currency</string>
|
||||
<string name="user_wallet_list_single_header">Single-currency</string>
|
||||
<string name="user_wallet_list_rename_popup_title">Rename Wallet</string>
|
||||
<string name="user_wallet_list_rename_popup_placeholder">Wallet name</string>
|
||||
<string name="user_wallet_list_unlock_all_face_id">Unlock all with biometrics</string>
|
||||
<string name="user_wallet_list_add_button">Add new wallet</string>
|
||||
<string name="user_wallet_list_editing_count">%d selected</string>
|
||||
<string name="user_wallet_list_title">My Wallets</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,4 +5,11 @@
|
|||
<item quantity="two">%d cards</item>
|
||||
<item quantity="other">%d cards</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="tokens_count">
|
||||
<item quantity="zero">%d tokens</item>
|
||||
<item quantity="one">%d token</item>
|
||||
<item quantity="two">%d tokens</item>
|
||||
<item quantity="other">%d tokens</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue