From 93abd29e1a10bd32c528bd633af206be133312d2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Sep 2023 15:39:54 +0300 Subject: [PATCH] Updated on 2026-08-14 --- core/ui/build.gradle.kts | 1 + .../com/tangem/core/ui/components/QrCode.kt | 29 +++ .../bottomsheets/TangemBottomSheet.kt | 31 +++ .../bottomsheets/TangemBottomSheetConfig.kt | 14 ++ .../TangemBottomSheetConfigContent.kt | 6 + .../TangemBottomSheetDraggableHeader.kt | 33 +++ .../bottomsheets/tokenreceive/AddressModel.kt | 10 + .../tokenreceive/TokenReceiveBottomSheet.kt | 192 ++++++++++++++++++ .../TokenReceiveBottomSheetConfig.kt | 10 + .../com/tangem/core/ui/extensions/Context.kt | 15 ++ .../com/tangem/core/ui/extensions/String.kt | 32 +++ .../com/tangem/core/ui/res/TangemDimens.kt | 1 + .../com/tangem/core/ui/res/TangemShapes.kt | 5 + .../DefaultWalletManagersFacade.kt | 16 ++ .../walletmanager/WalletManagersFacade.kt | 9 + domain/wallets/build.gradle.kts | 1 + .../tokendetails/TokenDetailsPreviewData.kt | 1 + .../tokendetails/state/TokenDetailsState.kt | 2 + .../TokenDetailsSkeletonStateConverter.kt | 1 + .../state/factory/TokenDetailsStateFactory.kt | 31 +++ .../tokendetails/ui/TokenDetailsScreen.kt | 7 + .../viewmodels/TokenDetailsClickIntents.kt | 2 + .../viewmodels/TokenDetailsViewModel.kt | 18 +- .../presentation/common/WalletPreviewData.kt | 5 +- .../wallet/state/WalletMultiCurrencyState.kt | 7 +- .../wallet/state/WalletSingleCurrencyState.kt | 7 +- .../presentation/wallet/state/WalletState.kt | 3 +- .../components/WalletBottomSheetConfig.kt | 148 +++++++------- .../state/factory/WalletStateFactory.kt | 9 +- .../presentation/wallet/ui/WalletScreen.kt | 13 +- .../ui/components/common/WalletBottomSheet.kt | 34 ++-- .../wallet/viewmodels/WalletViewModel.kt | 34 +++- 32 files changed, 609 insertions(+), 118 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/QrCode.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/AddressModel.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheetConfig.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/extensions/Context.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 20d4303b93..bcd3be240f 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -29,4 +29,5 @@ dependencies { implementation(deps.material) implementation(deps.compose.shimmer) implementation(deps.kotlin.immutable.collections) + implementation(deps.zxing.qrCore) } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/QrCode.kt b/core/ui/src/main/java/com/tangem/core/ui/components/QrCode.kt new file mode 100644 index 0000000000..afda35efe7 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/QrCode.kt @@ -0,0 +1,29 @@ +package com.tangem.core.ui.components + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import com.tangem.core.ui.extensions.toQrCode +import com.tangem.core.ui.res.TangemTheme + +@Composable +fun rememberQrPainters( + content: List, + size: Dp = TangemTheme.dimens.size248, + padding: Dp = TangemTheme.dimens.spacing0, +): List { + val density = LocalDensity.current + return remember(content) { + content.map { code -> + BitmapPainter( + code.toQrCode( + sizePx = with(density) { size.roundToPx() }, + paddingPx = with(density) { padding.roundToPx() }, + ).asImageBitmap(), + ) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt new file mode 100644 index 0000000000..b8b5086187 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt @@ -0,0 +1,31 @@ +package com.tangem.core.ui.components.bottomsheets + +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import com.tangem.core.ui.res.TangemTheme + +/** + * Tangem bottom sheet with custom draggable header and config + * + * @param config data model containing logic and ui models + * @param content custom bottom sheet content + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TangemBottomSheet( + config: TangemBottomSheetConfig, + content: @Composable ColumnScope.(TangemBottomSheetConfigContent) -> Unit, +) { + ModalBottomSheet( + onDismissRequest = config.onDismissRequest, + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + containerColor = TangemTheme.colors.background.primary, + shape = TangemTheme.shapes.bottomSheetLarge, + dragHandle = { TangemBottomSheetDraggableHeader() }, + ) { + content(config.content) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt new file mode 100644 index 0000000000..0bb0162ccc --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt @@ -0,0 +1,14 @@ +package com.tangem.core.ui.components.bottomsheets + +/** + * Tangem bottom sheet config + * + * @property isShow flag that determine if bottom sheet is shown + * @property onDismissRequest lambda be invoked when bottom sheet is dismissed + * @property content content config + */ +data class TangemBottomSheetConfig( + val isShow: Boolean, + val onDismissRequest: () -> Unit, + val content: TangemBottomSheetConfigContent, +) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt new file mode 100644 index 0000000000..812d593533 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt @@ -0,0 +1,6 @@ +package com.tangem.core.ui.components.bottomsheets + +/** + * General interface for bottom sheet config model + */ +interface TangemBottomSheetConfigContent \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt new file mode 100644 index 0000000000..5b1e13f57a --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt @@ -0,0 +1,33 @@ +package com.tangem.core.ui.components.bottomsheets + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.ui.res.TangemTheme + +@Composable +fun TangemBottomSheetDraggableHeader() { + Surface( + modifier = Modifier + .height(TangemTheme.dimens.size20), + color = TangemTheme.colors.background.primary, + ) { + Box( + modifier = Modifier + .padding(vertical = TangemTheme.dimens.spacing8) + .size( + width = TangemTheme.dimens.size32, + height = TangemTheme.dimens.size4, + ) + .background( + color = TangemTheme.colors.icon.inactive, + shape = TangemTheme.shapes.roundedCornersSmall, + ), + ) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/AddressModel.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/AddressModel.kt new file mode 100644 index 0000000000..0314b21846 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/AddressModel.kt @@ -0,0 +1,10 @@ +package com.tangem.core.ui.components.bottomsheets.tokenreceive + +data class AddressModel( + val value: String, + val type: Type = Type.Default, +) { + enum class Type { + Legacy, Default + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt new file mode 100644 index 0000000000..beadf33d24 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt @@ -0,0 +1,192 @@ +package com.tangem.core.ui.components.bottomsheets.tokenreceive + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextAlign +import com.tangem.core.ui.R +import com.tangem.core.ui.components.MiddleEllipsisText +import com.tangem.core.ui.components.SecondaryButtonIconStart +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.rememberQrPainters +import com.tangem.core.ui.extensions.shareText +import com.tangem.core.ui.res.TangemTheme + +@Composable +fun TokenReceiveBottomSheet(config: TangemBottomSheetConfig) { + if (config.content is TokenReceiveBottomSheetConfig && config.isShow) { + TangemBottomSheet(config) { content -> + TokenReceiveBottomSheetContent( + content = content as TokenReceiveBottomSheetConfig, + ) + } + } +} + +@Composable +private fun TokenReceiveBottomSheetContent(content: TokenReceiveBottomSheetConfig) { + var selectedAddress by remember { mutableStateOf(content.addresses.first()) } + Column( + modifier = Modifier + .padding( + start = TangemTheme.dimens.spacing24, + top = TangemTheme.dimens.spacing24, + end = TangemTheme.dimens.spacing24, + bottom = TangemTheme.dimens.spacing16, + ), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing24), + ) { + QrCodeContent( + content = content, + onAddressChange = { selectedAddress = it }, + ) + Text( + text = stringResource(R.string.receive_bottom_sheet_warning_message_full, content.name), + color = TangemTheme.colors.text.secondary, + textAlign = TextAlign.Center, + style = TangemTheme.typography.caption, + ) + Row( + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), + ) { + val clipboardManager = LocalClipboardManager.current + val hapticFeedback = LocalHapticFeedback.current + val context = LocalContext.current + SecondaryButtonIconStart( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.common_copy), + iconResId = R.drawable.ic_copy_24, + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + clipboardManager.setText(AnnotatedString(selectedAddress.value)) + }, + ) + SecondaryButtonIconStart( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.common_share), + iconResId = R.drawable.ic_share_24, + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + context.shareText(selectedAddress.value) + }, + ) + } + } +} + +@Suppress("LongMethod") +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun QrCodeContent(content: TokenReceiveBottomSheetConfig, onAddressChange: (AddressModel) -> Unit) { + val qrCodes = rememberQrPainters(content.addresses.map(AddressModel::value)) + val pagerState = rememberPagerState() + val pageCount = content.addresses.count() + + LaunchedEffect(key1 = pagerState.currentPage) { + onAddressChange.invoke(content.addresses[pagerState.currentPage]) + } + + HorizontalPager( + pageCount = pageCount, + state = pagerState, + ) { currentPage -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24), + ) { + Text( + text = stringResource( + R.string.receive_bottom_sheet_warning_message, + getName(content = content, index = pagerState.currentPage), + content.symbol, + content.network, + ), + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + style = TangemTheme.typography.h3, + ) + Image( + painter = qrCodes[currentPage], + contentDescription = null, + contentScale = ContentScale.Fit, + modifier = Modifier + .size(TangemTheme.dimens.size248), + ) + MiddleEllipsisText( + text = content.addresses[currentPage].value, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + style = TangemTheme.typography.subtitle1, + ) + } + } + + if (pageCount > 1) { + val indicatorState = rememberLazyListState() + val selectedColor = TangemTheme.colors.icon.primary1 + val unselectedColor = TangemTheme.colors.icon.informative + LazyRow( + modifier = Modifier + .height(TangemTheme.dimens.size20), + state = indicatorState, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + repeat(pageCount) { iteration -> + item(key = iteration) { + val color = if (pagerState.currentPage == iteration) { + selectedColor + } else { + unselectedColor + } + Box( + modifier = Modifier + .padding( + start = TangemTheme.dimens.spacing4, + top = TangemTheme.dimens.spacing6, + end = TangemTheme.dimens.spacing4, + bottom = TangemTheme.dimens.spacing6, + ) + .background(color, CircleShape) + .size(TangemTheme.dimens.size7), + ) + } + } + } + } +} + +@Composable +private fun getName(content: TokenReceiveBottomSheetConfig, index: Int): String { + return if (content.addresses.size < 2) { + content.name + } else { + "${ + stringResource( + id = when (content.addresses[index].type) { + AddressModel.Type.Default -> R.string.address_type_default + AddressModel.Type.Legacy -> R.string.address_type_legacy + }, + ) + } ${content.name}" + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheetConfig.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheetConfig.kt new file mode 100644 index 0000000000..b1b84dc016 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheetConfig.kt @@ -0,0 +1,10 @@ +package com.tangem.core.ui.components.bottomsheets.tokenreceive + +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent + +class TokenReceiveBottomSheetConfig( + val name: String, + val symbol: String, + val network: String, + val addresses: List, +) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/Context.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/Context.kt new file mode 100644 index 0000000000..17439269f9 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/Context.kt @@ -0,0 +1,15 @@ +package com.tangem.core.ui.extensions + +import android.content.Context +import android.content.Intent +import androidx.core.content.ContextCompat + +fun Context.shareText(text: String) { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, text) + type = "text/plain" + } + val shareIntent = Intent.createChooser(sendIntent, null) + ContextCompat.startActivity(this, shareIntent, null) +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt new file mode 100644 index 0000000000..f5cc3cb971 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/String.kt @@ -0,0 +1,32 @@ +package com.tangem.core.ui.extensions + +import android.graphics.Bitmap +import android.graphics.Color +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.qrcode.QRCodeWriter +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import java.util.Hashtable + +@Suppress("MagicNumber") +fun String.toQrCode(sizePx: Int = 256, paddingPx: Int = 0): Bitmap { + val hintMap = Hashtable() + hintMap[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.M // H = 30% damage + hintMap[EncodeHintType.MARGIN] = paddingPx + + val qrCodeWriter = QRCodeWriter() + + val bitMatrix = qrCodeWriter.encode(this, BarcodeFormat.QR_CODE, sizePx, sizePx, hintMap) + val width = bitMatrix.width + val bmp = Bitmap.createBitmap(width, width, Bitmap.Config.RGB_565) + for (x in 0 until width) { + for (y in 0 until width) { + bmp.setPixel( + y, + x, + if (bitMatrix.get(x, y)) Color.BLACK else Color.WHITE, + ) + } + } + return bmp +} \ No newline at end of file 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 bacd7c615f..3f80bcbc71 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 @@ -81,6 +81,7 @@ data class TangemDimens internal constructor( val size158: Dp = 158.dp, val size164: Dp = 164.dp, val size200: Dp = 200.dp, + val size248: Dp = 248.dp, // endregion Size // region Spacing val spacing0: Dp = 0.dp, diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt index a3802221fb..8bf99452ed 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt @@ -13,6 +13,7 @@ data class TangemShapes internal constructor( val roundedCornersXMedium: Shape, val roundedCornersLarge: Shape, val bottomSheet: Shape, + val bottomSheetLarge: Shape, ) { constructor(dimens: TangemDimens) : this( roundedCornersSmall = RoundedCornerShape(size = dimens.radius2), @@ -25,5 +26,9 @@ data class TangemShapes internal constructor( topStart = dimens.radius16, topEnd = dimens.radius16, ), + bottomSheetLarge = RoundedCornerShape( + topStart = dimens.radius24, + topEnd = dimens.radius24, + ), ) } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index dd1cf7a25b..1bbb724d3d 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -3,6 +3,7 @@ package com.tangem.domain.walletmanager import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.extensions.Result import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager @@ -202,6 +203,21 @@ class DefaultWalletManagersFacade( return walletManager } + override suspend fun getAddress(userWalletId: UserWalletId, network: Network): List
{ + val userWallet = getUserWallet(userWalletId) + val blockchain = Blockchain.fromId(network.id.value) + + return getOrCreateWalletManager( + userWallet = userWallet, + blockchain = blockchain, + derivationPath = network.derivationPath.value, + ) + ?.wallet + ?.addresses + ?.sortedBy { it.type } + .orEmpty() + } + private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set) { if (tokens.isEmpty()) return diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index bcb4409033..dbccd352a6 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -2,6 +2,7 @@ package com.tangem.domain.walletmanager import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.address.Address import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.txhistory.models.PaginationWrapper @@ -70,4 +71,12 @@ interface WalletManagersFacade { blockchain: Blockchain, derivationPath: String?, ): WalletManager? + + /** + * Returns ordered list of addresses for selected wallet for given currency + * + * @param userWalletId selected wallet id + * @param network network of currency + */ + suspend fun getAddress(userWalletId: UserWalletId, network: Network): List
} \ No newline at end of file diff --git a/domain/wallets/build.gradle.kts b/domain/wallets/build.gradle.kts index 474b10e64e..89f73bcf36 100644 --- a/domain/wallets/build.gradle.kts +++ b/domain/wallets/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { // region Domain modules implementation(projects.domain.legacy) implementation(projects.domain.models) + implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) // endregion diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index 7f79abcb99..14e909c9c5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -90,5 +90,6 @@ internal object TokenDetailsPreviewData { dialogConfig = null, pendingTxs = persistentListOf(), pullToRefreshConfig = pullToRefreshConfig, + bottomSheetConfig = null, ) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt index 3c40416911..9c9ff33f4b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt @@ -1,5 +1,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState @@ -16,4 +17,5 @@ internal data class TokenDetailsState( val txHistoryState: TxHistoryState, val dialogConfig: TokenDetailsDialogConfig?, val pullToRefreshConfig: TokenDetailsPullToRefreshConfig, + val bottomSheetConfig: TangemBottomSheetConfig?, ) \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 8d4bd2f378..ce9e0bba6e 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -51,6 +51,7 @@ internal class TokenDetailsSkeletonStateConverter( ), dialogConfig = null, pullToRefreshConfig = createPullToRefresh(), + bottomSheetConfig = null, ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 0ad3b96933..7d29d64f91 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -2,7 +2,11 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory import androidx.paging.PagingData import arrow.core.Either +import com.tangem.blockchain.common.address.Address import com.tangem.common.Provider +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel +import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -131,4 +135,31 @@ internal class TokenDetailsStateFactory( fun getRefreshedState(): TokenDetailsState { return refreshStateConverter.convert(false) } + + fun getStateWithReceiveBottomSheet(currency: CryptoCurrency, addresses: List
): TokenDetailsState { + return currentStateProvider().copy( + bottomSheetConfig = TangemBottomSheetConfig( + isShow = true, + onDismissRequest = clickIntents::onDismissBottomSheet, + content = TokenReceiveBottomSheetConfig( + name = currency.name, + symbol = currency.symbol, + network = currency.network.name, + addresses = addresses.map { + AddressModel( + value = it.value, + type = AddressModel.Type.valueOf(it.type.name), + ) + }, + ), + ), + ) + } + + fun getStateWithClosedBottomSheet(): TokenDetailsState { + val state = currentStateProvider() + return state.copy( + bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), + ) + } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index b47ae3c260..b3d71d3409 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.util.fastForEach import androidx.paging.compose.collectAsLazyPagingItems +import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet import com.tangem.core.ui.components.marketprice.MarketPriceBlock import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.Transaction @@ -95,6 +96,12 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { } TokenDetailsDialogs(state = state) + + state.bottomSheetConfig?.let { config -> + TokenReceiveBottomSheet( + config = config, + ) + } } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt index 52b554feb9..40bef9b4c4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt @@ -25,4 +25,6 @@ interface TokenDetailsClickIntents { fun onReloadClick() fun onExploreClick() + + fun onDismissBottomSheet() } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index baf1dfaa9f..d6256f8a67 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -16,6 +16,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase +import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase @@ -48,6 +49,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, private val removeCurrencyUseCase: RemoveCurrencyUseCase, private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, + private val walletManagersFacade: WalletManagersFacade, private val reduxStateHolder: ReduxStateHolder, savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents { @@ -209,7 +211,17 @@ internal class TokenDetailsViewModel @Inject constructor( } override fun onReceiveClick() { - // TODO: [REDACTED_JIRA] + viewModelScope.launch(dispatchers.io) { + val addresses = walletManagersFacade.getAddress( + userWalletId = wallet.walletId, + network = cryptoCurrency.network, + ) + + uiState = stateFactory.getStateWithReceiveBottomSheet( + currency = cryptoCurrency, + addresses = addresses, + ) + } } override fun onSellClick() { @@ -274,4 +286,8 @@ internal class TokenDetailsViewModel @Inject constructor( uiState = stateFactory.getRefreshedState() }.saveIn(refreshStateJobHolder) } + + override fun onDismissBottomSheet() { + uiState = stateFactory.getStateWithClosedBottomSheet() + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index c12f412baf..a909a17c19 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.common import androidx.paging.PagingData import com.tangem.core.ui.R +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.components.transactions.state.TransactionState @@ -290,10 +291,10 @@ internal object WalletPreviewData { } val bottomSheet by lazy { - WalletBottomSheetConfig( + TangemBottomSheetConfig( isShow = false, onDismissRequest = {}, - content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( + content = WalletBottomSheetConfig.UnlockWallets( onUnlockClick = {}, onScanClick = {}, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt index d22b1b6db6..d0734666c9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.state +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -20,7 +21,7 @@ internal sealed class WalletMultiCurrencyState : WalletState.ContentState() { override val walletsListConfig: WalletsListConfig, override val pullToRefreshConfig: WalletPullToRefreshConfig, override val notifications: ImmutableList, - override val bottomSheetConfig: WalletBottomSheetConfig?, + override val bottomSheetConfig: TangemBottomSheetConfig?, override val tokensListState: WalletTokensListState, val tokenActionsBottomSheet: ActionsBottomSheetConfig?, val onManageTokensClick: () -> Unit, @@ -42,10 +43,10 @@ internal sealed class WalletMultiCurrencyState : WalletState.ContentState() { WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick), ) - override val bottomSheetConfig = WalletBottomSheetConfig( + override val bottomSheetConfig = TangemBottomSheetConfig( isShow = isBottomSheetShow, onDismissRequest = onBottomSheetDismiss, - content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( + content = WalletBottomSheetConfig.UnlockWallets( onUnlockClick = onUnlockClick, onScanClick = onScanClick, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt index 5b09fe934e..8d9c0e265e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state import androidx.compose.runtime.Immutable import androidx.paging.PagingData +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState @@ -31,7 +32,7 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { override val walletsListConfig: WalletsListConfig, override val pullToRefreshConfig: WalletPullToRefreshConfig, override val notifications: ImmutableList, - override val bottomSheetConfig: WalletBottomSheetConfig?, + override val bottomSheetConfig: TangemBottomSheetConfig?, override val buttons: PersistentList, override val txHistoryState: TxHistoryState, val marketPriceBlockState: MarketPriceBlockState, @@ -55,10 +56,10 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick), ) - override val bottomSheetConfig = WalletBottomSheetConfig( + override val bottomSheetConfig = TangemBottomSheetConfig( isShow = isBottomSheetShow, onDismissRequest = onBottomSheetDismiss, - content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( + content = WalletBottomSheetConfig.UnlockWallets( onUnlockClick = onUnlockClick, onScanClick = onScanClick, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt index c9c327f540..6279d481aa 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.state +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.ImmutableList @@ -29,7 +30,7 @@ internal sealed class WalletState { abstract val notifications: ImmutableList /** Bottom sheet config */ - abstract val bottomSheetConfig: WalletBottomSheetConfig? + abstract val bottomSheetConfig: TangemBottomSheetConfig? /** * Util function that allow to make a copy diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt index eef9a95ef0..7b4abe968d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.components import androidx.annotation.DrawableRes import androidx.compose.ui.graphics.Color +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.WrappedList import com.tangem.core.ui.res.TangemColorPalette @@ -10,90 +11,79 @@ import com.tangem.feature.wallet.impl.R /** * Wallet bottom sheet config * - * @property isShow flag that determine if bottom sheet is shown - * @property onDismissRequest lambda be invoked when bottom sheet is dismissed - * @property content content config - * [REDACTED_AUTHOR] */ // TODO: Finalize notification strings [REDACTED_JIRA] -internal data class WalletBottomSheetConfig( - val isShow: Boolean, - val onDismissRequest: () -> Unit, - val content: BottomSheetContentConfig, -) { +sealed class WalletBottomSheetConfig( + open val title: TextReference, + open val subtitle: TextReference, + @DrawableRes open val iconResId: Int, + open val tint: Color? = null, + val primaryButtonConfig: ButtonConfig, + val secondaryButtonConfig: ButtonConfig, +) : TangemBottomSheetConfigContent { - sealed class BottomSheetContentConfig( - open val title: TextReference, - open val subtitle: TextReference, - @DrawableRes open val iconResId: Int, - open val tint: Color? = null, - val primaryButtonConfig: ButtonConfig, - val secondaryButtonConfig: ButtonConfig, - ) { + data class ButtonConfig( + val text: TextReference, + val onClick: () -> Unit, + @DrawableRes val iconResId: Int? = null, + ) - data class ButtonConfig( - val text: TextReference, - val onClick: () -> Unit, - @DrawableRes val iconResId: Int? = null, - ) + data class UnlockWallets( + val onUnlockClick: () -> Unit, + val onScanClick: () -> Unit, + ) : WalletBottomSheetConfig( + title = TextReference.Str(value = "Unlock needed"), + subtitle = TextReference.Str( + value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " + + "incididunt ut labore et dolore magna aliqua.", + ), + iconResId = R.drawable.ic_locked_24, + tint = TangemColorPalette.Black, + primaryButtonConfig = ButtonConfig(text = TextReference.Str(value = "Unlock"), onClick = onUnlockClick), + secondaryButtonConfig = ButtonConfig( + text = TextReference.Str(value = "Scan card"), + onClick = onScanClick, + iconResId = R.drawable.ic_tangem_24, + ), + ) - data class UnlockWallets( - val onUnlockClick: () -> Unit, - val onScanClick: () -> Unit, - ) : BottomSheetContentConfig( - title = TextReference.Str(value = "Unlock needed"), - subtitle = TextReference.Str( - value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " + - "incididunt ut labore et dolore magna aliqua.", - ), - iconResId = R.drawable.ic_locked_24, - tint = TangemColorPalette.Black, - primaryButtonConfig = ButtonConfig(text = TextReference.Str(value = "Unlock"), onClick = onUnlockClick), - secondaryButtonConfig = ButtonConfig( - text = TextReference.Str(value = "Scan card"), - onClick = onScanClick, - iconResId = R.drawable.ic_tangem_24, - ), - ) + data class LikeTangemApp( + val onRateTheAppClick: () -> Unit, + val onShareClick: () -> Unit, + ) : WalletBottomSheetConfig( + title = TextReference.Str(value = "Like Tangem App?"), + subtitle = TextReference.Str(value = "How was your experience with our app? Let us know:"), + iconResId = R.drawable.ic_star_24, + tint = TangemColorPalette.Tangerine, + primaryButtonConfig = ButtonConfig( + text = TextReference.Str(value = "Rate the app"), + onClick = onRateTheAppClick, + ), + secondaryButtonConfig = ButtonConfig( + text = TextReference.Str(value = "Share feedback"), + onClick = onShareClick, + ), + ) - data class LikeTangemApp( - val onRateTheAppClick: () -> Unit, - val onShareClick: () -> Unit, - ) : BottomSheetContentConfig( - title = TextReference.Str(value = "Like Tangem App?"), - subtitle = TextReference.Str(value = "How was your experience with our app? Let us know:"), - iconResId = R.drawable.ic_star_24, - tint = TangemColorPalette.Tangerine, - primaryButtonConfig = ButtonConfig( - text = TextReference.Str(value = "Rate the app"), - onClick = onRateTheAppClick, - ), - secondaryButtonConfig = ButtonConfig( - text = TextReference.Str(value = "Share feedback"), - onClick = onShareClick, - ), - ) - - data class CriticalWarningAlreadySignedHashes( - val onOkClick: () -> Unit, - val onCancelClick: () -> Unit, - ) : BottomSheetContentConfig( - title = TextReference.Res( - id = R.string.warning_important_security_info, - formatArgs = WrappedList(listOf("\u26A0")), - ), - subtitle = TextReference.Res(id = R.string.alert_signed_hashes_message), - iconResId = R.drawable.img_attention_20, - tint = null, - primaryButtonConfig = ButtonConfig( - text = TextReference.Res(id = R.string.common_ok), - onClick = onOkClick, - ), - secondaryButtonConfig = ButtonConfig( - text = TextReference.Res(id = R.string.common_cancel), - onClick = onCancelClick, - ), - ) - } + data class CriticalWarningAlreadySignedHashes( + val onOkClick: () -> Unit, + val onCancelClick: () -> Unit, + ) : WalletBottomSheetConfig( + title = TextReference.Res( + id = R.string.warning_important_security_info, + formatArgs = WrappedList(listOf("\u26A0")), + ), + subtitle = TextReference.Res(id = R.string.alert_signed_hashes_message), + iconResId = R.drawable.img_attention_20, + tint = null, + primaryButtonConfig = ButtonConfig( + text = TextReference.Res(id = R.string.common_ok), + onClick = onOkClick, + ), + secondaryButtonConfig = ButtonConfig( + text = TextReference.Res(id = R.string.common_cancel), + onClick = onCancelClick, + ), + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 2923db5694..062a6baec4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -3,6 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.CurrencyStatusError @@ -18,7 +20,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.ActionsBottomSheetCon import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadedTxHistoryConverter import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadingTxHistoryConverter @@ -161,10 +162,10 @@ internal class WalletStateFactory( fun getRefreshedState(): WalletState = refreshStateConverter.convert(value = false) - fun getStateWithOpenWalletBottomSheet(content: WalletBottomSheetConfig.BottomSheetContentConfig): WalletState { + fun getStateWithOpenWalletBottomSheet(content: TangemBottomSheetConfigContent): WalletState { return when (val state = currentStateProvider() as WalletState.ContentState) { is WalletMultiCurrencyState.Content -> state.copy( - bottomSheetConfig = WalletBottomSheetConfig( + bottomSheetConfig = TangemBottomSheetConfig( isShow = true, onDismissRequest = clickIntents::onDismissBottomSheet, content = content, @@ -175,7 +176,7 @@ internal class WalletStateFactory( onBottomSheetDismiss = clickIntents::onDismissBottomSheet, ) is WalletSingleCurrencyState.Content -> state.copy( - bottomSheetConfig = WalletBottomSheetConfig( + bottomSheetConfig = TangemBottomSheetConfig( isShow = true, onDismissRequest = clickIntents::onDismissBottomSheet, content = content, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 1e02c0f412..b09123933c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -18,6 +18,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.paging.compose.collectAsLazyPagingItems import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet +import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R @@ -25,6 +27,7 @@ import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.OrganizeTokensButtonState import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet @@ -167,7 +170,15 @@ private fun ManageTokensButton(onManageTokensClick: () -> Unit) { private fun WalletBottomSheets(state: WalletState) { val bottomSheetConfig = (state as? WalletState.ContentState)?.bottomSheetConfig if (bottomSheetConfig != null && bottomSheetConfig.isShow) { - WalletBottomSheet(config = bottomSheetConfig) + when (bottomSheetConfig.content) { + is WalletBottomSheetConfig -> { + WalletBottomSheet(config = bottomSheetConfig) + } + + is TokenReceiveBottomSheetConfig -> { + TokenReceiveBottomSheet(config = bottomSheetConfig) + } + } } (state as? WalletMultiCurrencyState.Content)?.let { multiCurrencyState -> diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt index bf78b633da..8794acf686 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt @@ -15,6 +15,8 @@ import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.PrimaryButtonIconStart import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.components.SecondaryButtonIconStart +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData @@ -27,21 +29,17 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBott * [REDACTED_AUTHOR] */ -@OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun WalletBottomSheet(config: WalletBottomSheetConfig) { - ModalBottomSheet( - onDismissRequest = config.onDismissRequest, - sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), - containerColor = TangemTheme.colors.background.primary, - dragHandle = { BottomSheetDefaults.DragHandle() }, - ) { - BottomSheetContent(config = config.content) +internal fun WalletBottomSheet(config: TangemBottomSheetConfig) { + TangemBottomSheet(config) { content -> + BottomSheetContent( + config = content as WalletBottomSheetConfig, + ) } } @Composable -private fun BottomSheetContent(config: WalletBottomSheetConfig.BottomSheetContentConfig) { +private fun BottomSheetContent(config: WalletBottomSheetConfig) { Column( modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) @@ -90,10 +88,7 @@ private fun BottomSheetContent(config: WalletBottomSheetConfig.BottomSheetConten } @Composable -private fun PrimaryButton( - config: WalletBottomSheetConfig.BottomSheetContentConfig.ButtonConfig, - modifier: Modifier = Modifier, -) { +private fun PrimaryButton(config: WalletBottomSheetConfig.ButtonConfig, modifier: Modifier = Modifier) { if (config.iconResId == null) { PrimaryButton( text = config.text.resolveReference(), @@ -111,10 +106,7 @@ private fun PrimaryButton( } @Composable -private fun SecondaryButton( - config: WalletBottomSheetConfig.BottomSheetContentConfig.ButtonConfig, - modifier: Modifier = Modifier, -) { +private fun SecondaryButton(config: WalletBottomSheetConfig.ButtonConfig, modifier: Modifier = Modifier) { if (config.iconResId == null) { SecondaryButton( text = config.text.resolveReference(), @@ -139,7 +131,7 @@ private fun WalletBottomSheetContent_Light( ) { TangemTheme(isDark = false) { // Use preview of content because ModalBottomSheet isn't supported in Preview mode - BottomSheetContent(config = config.content) + BottomSheetContent(config = config) } } @@ -151,10 +143,10 @@ private fun WalletBottomSheetContent_Dark( ) { TangemTheme(isDark = false) { // Use preview of content because ModalBottomSheet isn't supported in Preview mode - BottomSheetContent(config = config.content) + BottomSheetContent(config = config) } } -private class WalletBottomSheetConfigProvider : CollectionPreviewParameterProvider( +private class WalletBottomSheetConfigProvider : CollectionPreviewParameterProvider( collection = listOf(WalletPreviewData.bottomSheet), ) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 4c6416f4bf..a0deea99b7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -7,6 +7,8 @@ import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.core.navigation.AppScreen +import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel +import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency @@ -27,6 +29,7 @@ import com.tangem.domain.tokens.models.Network import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.userwallets.UserWalletBuilder +import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.* @@ -86,6 +89,7 @@ internal class WalletViewModel @Inject constructor( private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase, private val canUseBiometryUseCase: CanUseBiometryUseCase, private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, + private val walletManagersFacade: WalletManagersFacade, private val reduxStateHolder: ReduxStateHolder, private val dispatchers: CoroutineDispatcherProvider, ) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents { @@ -271,7 +275,7 @@ internal class WalletViewModel @Inject constructor( override fun onCriticalWarningAlreadySignedHashesClick() { uiState = stateFactory.getStateWithOpenWalletBottomSheet( - content = WalletBottomSheetConfig.BottomSheetContentConfig.CriticalWarningAlreadySignedHashes( + content = WalletBottomSheetConfig.CriticalWarningAlreadySignedHashes( onOkClick = {}, onCancelClick = {}, ), @@ -284,7 +288,7 @@ internal class WalletViewModel @Inject constructor( override fun onLikeTangemAppClick() { uiState = stateFactory.getStateWithOpenWalletBottomSheet( - content = WalletBottomSheetConfig.BottomSheetContentConfig.LikeTangemApp( + content = WalletBottomSheetConfig.LikeTangemApp( onRateTheAppClick = ::onRateTheAppClick, onShareClick = ::onShareClick, ), @@ -428,7 +432,31 @@ internal class WalletViewModel @Inject constructor( } override fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - // TODO: [REDACTED_JIRA] + val state = uiState as? WalletState.ContentState ?: return + + viewModelScope.launch(dispatchers.io) { + val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) + + val addresses = walletManagersFacade.getAddress( + userWalletId = userWallet.walletId, + network = cryptoCurrencyStatus.currency.network, + ) + + val currency = cryptoCurrencyStatus.currency + uiState = stateFactory.getStateWithOpenWalletBottomSheet( + content = TokenReceiveBottomSheetConfig( + name = currency.name, + symbol = currency.symbol, + network = currency.network.name, + addresses = addresses.map { + AddressModel( + value = it.value, + type = AddressModel.Type.valueOf(it.type.name), + ) + }, + ), + ) + } } override fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {