From 47412b1d63c4bff9184de93490504413e73a73a3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 12 Nov 2022 00:36:45 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/common/extensions/Navigation.kt | 10 +- .../com/tangem/tap/common/redux/AppState.kt | 2 + .../redux/navigation/NavigationState.kt | 1 + .../redux/WalletSelectorAction.kt | 31 +++ .../redux/WalletSelectorState.kt | 5 + .../ui/WalletSelectorBottomSheetFragment.kt | 80 ++++++ .../ui/WalletSelectorScreenState.kt | 20 ++ .../ui/WalletSelectorViewModel.kt | 143 ++++++++++ .../walletSelector/ui/components/MockData.kt | 30 +++ .../components/RenameWalletDialogContent.kt | 111 ++++++++ .../WallectSelectorScreenContent.kt | 251 ++++++++++++++++++ .../ui/components/WalletItem.kt | 204 ++++++++++++++ .../ui/components/WalletList.kt | 66 +++++ .../ui/model/RenameWalletDialog.kt | 10 + .../walletSelector/ui/model/UserWalletItem.kt | 46 ++++ app/src/main/res/drawable/ic_check_24.xml | 9 + .../main/res/drawable/ic_check_circle_18.xml | 9 + app/src/main/res/drawable/ic_locked_24.xml | 9 + .../res/drawable/ic_pencil_outline_24.xml | 9 + .../com/tangem/core/ui/components/Spacers.kt | 17 ++ .../com/tangem/core/ui/res/TangemDimens.kt | 3 + .../res/values-de/strings_plurals_final.xml | 5 + .../res/values-fr/strings_plurals_final.xml | 6 + .../res/values-it/strings_plurals_final.xml | 5 + .../src/main/res/values-ru/strings_final.xml | 8 + .../res/values-ru/strings_plurals_final.xml | 7 + core/ui/src/main/res/values/strings_final.xml | 8 + .../main/res/values/strings_plurals_final.xml | 7 + 28 files changed, 1110 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorScreenState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletList.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/RenameWalletDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt create mode 100644 app/src/main/res/drawable/ic_check_24.xml create mode 100644 app/src/main/res/drawable/ic_check_circle_18.xml create mode 100644 app/src/main/res/drawable/ic_locked_24.xml create mode 100644 app/src/main/res/drawable/ic_pencil_outline_24.xml diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index dae414e733..3abbbac6d1 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -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() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index d46d70029f..a158f2c872 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt index ba861a55dc..e3d82a292a 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt @@ -25,4 +25,5 @@ enum class AppScreen( ReferralProgram, Welcome, SaveWallet(isDialogFragment = true), + WalletSelector(isDialogFragment = true), } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt new file mode 100644 index 0000000000..51a7253075 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt @@ -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, + ) : WalletSelectorAction + + object AddWallet : WalletSelectorAction { + object Success : WalletSelectorAction + data class Error(val error: TangemError) : WalletSelectorAction + } + + object CloseError : WalletSelectorAction +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorState.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorState.kt new file mode 100644 index 0000000000..c84839277a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorState.kt @@ -0,0 +1,5 @@ +package com.tangem.tap.features.walletSelector.redux + +import org.rekotlin.StateType + +class WalletSelectorState : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt new file mode 100644 index 0000000000..47c6ca9bf2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt @@ -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() { + private val viewModel by viewModels() + + @Composable + override fun provideState(): State { + 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorScreenState.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorScreenState.kt new file mode 100644 index 0000000000..bc3016f8f2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorScreenState.kt @@ -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 = listOf(), + val selectedWalletId: String? = null, + val fiatCurrency: FiatCurrency = FiatCurrency.Default, + val isLocked: Boolean = true, + val editingWalletsIds: List = listOf(), + val renameWalletDialog: RenameWalletDialog? = null, + val showAddCardProgress: Boolean = false, + val showUnlockProgress: Boolean = false, + val error: TangemError? = null, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt new file mode 100644 index 0000000000..cb6d75b0b0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt @@ -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 { + private val stateInternal = MutableStateFlow(WalletSelectorScreenState()) + val state: StateFlow = 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt new file mode 100644 index 0000000000..7fe1623321 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt @@ -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, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt new file mode 100644 index 0000000000..f6aa55d7a0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RenameWalletDialogContent.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt new file mode 100644 index 0000000000..1fe2d1e173 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt new file mode 100644 index 0000000000..6837d47a7f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt @@ -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, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletList.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletList.kt new file mode 100644 index 0000000000..ab1f4f9c40 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletList.kt @@ -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, + selectedWalletId: String?, + checkedWalletIds: List, + 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) -> 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/RenameWalletDialog.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/RenameWalletDialog.kt new file mode 100644 index 0000000000..e3969634ce --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/RenameWalletDialog.kt @@ -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, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt new file mode 100644 index 0000000000..41f0ace5cd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_check_24.xml b/app/src/main/res/drawable/ic_check_24.xml new file mode 100644 index 0000000000..4291149aff --- /dev/null +++ b/app/src/main/res/drawable/ic_check_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_check_circle_18.xml b/app/src/main/res/drawable/ic_check_circle_18.xml new file mode 100644 index 0000000000..2c3e494f73 --- /dev/null +++ b/app/src/main/res/drawable/ic_check_circle_18.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_locked_24.xml b/app/src/main/res/drawable/ic_locked_24.xml new file mode 100644 index 0000000000..2afb4d8a14 --- /dev/null +++ b/app/src/main/res/drawable/ic_locked_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_pencil_outline_24.xml b/app/src/main/res/drawable/ic_pencil_outline_24.xml new file mode 100644 index 0000000000..039a5d5463 --- /dev/null +++ b/app/src/main/res/drawable/ic_pencil_outline_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt index a6425652bb..7b99b3f27e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt @@ -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 diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index a05eb630a4..731400086c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -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, diff --git a/core/ui/src/main/res/values-de/strings_plurals_final.xml b/core/ui/src/main/res/values-de/strings_plurals_final.xml index fa02d656a3..02204c79ec 100644 --- a/core/ui/src/main/res/values-de/strings_plurals_final.xml +++ b/core/ui/src/main/res/values-de/strings_plurals_final.xml @@ -3,4 +3,9 @@ %d card %d cards + + + %d token + %d tokens + diff --git a/core/ui/src/main/res/values-fr/strings_plurals_final.xml b/core/ui/src/main/res/values-fr/strings_plurals_final.xml index f7ce3f895b..e79bd3f0cd 100644 --- a/core/ui/src/main/res/values-fr/strings_plurals_final.xml +++ b/core/ui/src/main/res/values-fr/strings_plurals_final.xml @@ -4,4 +4,10 @@ %d cards %d cards + + + %d token + %d tokens + %d tokens + diff --git a/core/ui/src/main/res/values-it/strings_plurals_final.xml b/core/ui/src/main/res/values-it/strings_plurals_final.xml index fa02d656a3..02204c79ec 100644 --- a/core/ui/src/main/res/values-it/strings_plurals_final.xml +++ b/core/ui/src/main/res/values-it/strings_plurals_final.xml @@ -3,4 +3,9 @@ %d card %d cards + + + %d token + %d tokens + diff --git a/core/ui/src/main/res/values-ru/strings_final.xml b/core/ui/src/main/res/values-ru/strings_final.xml index 20b896eb21..c8db954763 100644 --- a/core/ui/src/main/res/values-ru/strings_final.xml +++ b/core/ui/src/main/res/values-ru/strings_final.xml @@ -152,4 +152,12 @@ Похоже, что у вас отключена биометрическая авторизация, она необходима для сохранения кошельков Включить Отмена + Мультивалютные + Одновалютные + Переименование кошелька + Имя кошелька + Разблокировать все с биометрией + Добавить новый кошелек + %d выбрано + Мои кошельки diff --git a/core/ui/src/main/res/values-ru/strings_plurals_final.xml b/core/ui/src/main/res/values-ru/strings_plurals_final.xml index 53720a4bb6..35486659e4 100644 --- a/core/ui/src/main/res/values-ru/strings_plurals_final.xml +++ b/core/ui/src/main/res/values-ru/strings_plurals_final.xml @@ -5,4 +5,11 @@ %d карт %d карт + + + %d токен + %d токена + %d токенов + %d токенов + diff --git a/core/ui/src/main/res/values/strings_final.xml b/core/ui/src/main/res/values/strings_final.xml index a6fd518ac9..6f508e1d28 100644 --- a/core/ui/src/main/res/values/strings_final.xml +++ b/core/ui/src/main/res/values/strings_final.xml @@ -154,4 +154,12 @@ It looks like you have biometric authorization disabled, it is necessary to save wallets Enable Cancel + Multi-currency + Single-currency + Rename Wallet + Wallet name + Unlock all with biometrics + Add new wallet + %d selected + My Wallets diff --git a/core/ui/src/main/res/values/strings_plurals_final.xml b/core/ui/src/main/res/values/strings_plurals_final.xml index 530ada144e..9fa94fe676 100644 --- a/core/ui/src/main/res/values/strings_plurals_final.xml +++ b/core/ui/src/main/res/values/strings_plurals_final.xml @@ -5,4 +5,11 @@ %d cards %d cards + + + %d tokens + %d token + %d tokens + %d tokens +