From 6ae6f5fdd98e9b70680665e0449c77c8c9335fe4 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Thu, 15 May 2025 14:26:32 -0300 Subject: [PATCH 1/6] feat: Tooltip component --- .../main/java/to/bitkit/ui/components/Text.kt | 3 +- .../java/to/bitkit/ui/components/Tooltip.kt | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/to/bitkit/ui/components/Tooltip.kt diff --git a/app/src/main/java/to/bitkit/ui/components/Text.kt b/app/src/main/java/to/bitkit/ui/components/Text.kt index 9b4c2c826..5789cf225 100644 --- a/app/src/main/java/to/bitkit/ui/components/Text.kt +++ b/app/src/main/java/to/bitkit/ui/components/Text.kt @@ -349,6 +349,7 @@ fun CaptionB( text: String, modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.primary, + textAlign: TextAlign = TextAlign.Start, ) { Text( text = text, @@ -359,7 +360,7 @@ fun CaptionB( letterSpacing = 0.4.sp, fontFamily = InterFontFamily, color = color, - textAlign = TextAlign.Start, + textAlign = textAlign, ), modifier = modifier, ) diff --git a/app/src/main/java/to/bitkit/ui/components/Tooltip.kt b/app/src/main/java/to/bitkit/ui/components/Tooltip.kt new file mode 100644 index 000000000..c0da1d79d --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/components/Tooltip.kt @@ -0,0 +1,54 @@ +package to.bitkit.ui.components + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.PlainTooltip +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults +import androidx.compose.material3.TooltipState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import to.bitkit.ui.theme.Colors + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Tooltip( + text: String, + tooltipState: TooltipState, + modifier: Modifier = Modifier, + content: @Composable (() -> Unit) +) { + + TooltipBox( + modifier = modifier, + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip( + caretSize = DpSize( + width = 16.dp, + height = 12.dp + ), + containerColor = Colors.Black92, + contentColor = Colors.White, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp) + ) { + CaptionB( + text, + color = Colors.White, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 37.dp, vertical = 24.dp) + ) + } + }, + state = tooltipState, + content = content + ) +} From 89ed2c562f3f13f6805c8ff3efb01ecfba76fcdd Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Thu, 15 May 2025 14:27:24 -0300 Subject: [PATCH 2/6] feat: implement tooltip in the qr button --- .../wallets/receive/ReceiveQrScreen.kt | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt index f55a4943b..9b752c192 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt @@ -17,8 +17,10 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Switch +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -27,6 +29,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate @@ -59,6 +62,7 @@ import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.Headline import to.bitkit.ui.components.PrimaryButton import to.bitkit.ui.components.QrCodeImage +import to.bitkit.ui.components.Tooltip import to.bitkit.ui.scaffold.SheetTopBar import to.bitkit.ui.screens.wallets.send.AddTagScreen import to.bitkit.ui.shared.PagerWithIndicator @@ -419,6 +423,7 @@ private fun ReceiveLightningFunds( } } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ReceiveQrSlide( uri: String, @@ -429,6 +434,9 @@ private fun ReceiveQrSlide( val context = LocalContext.current val clipboard = LocalClipboardManager.current + val qrButtonTooltipState = rememberTooltipState() + val coroutineScope = rememberCoroutineScope() + Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier @@ -459,21 +467,29 @@ private fun ReceiveQrSlide( ) } ) - PrimaryButton( - text = stringResource(R.string.common__copy), - size = ButtonSize.Small, - onClick = { clipboard.setText(AnnotatedString(uri)) }, - fullWidth = false, - color = Colors.White10, - icon = { - Icon( - painter = painterResource(R.drawable.ic_copy), - contentDescription = null, - tint = Colors.Brand, - modifier = Modifier.size(18.dp) - ) - } - ) + Tooltip( + text = stringResource(R.string.wallet__receive_copied), + tooltipState = qrButtonTooltipState + ) { + PrimaryButton( + text = stringResource(R.string.common__copy), + size = ButtonSize.Small, + onClick = { + clipboard.setText(AnnotatedString(uri)) + coroutineScope.launch { qrButtonTooltipState.show() } + }, + fullWidth = false, + color = Colors.White10, + icon = { + Icon( + painter = painterResource(R.drawable.ic_copy), + contentDescription = null, + tint = Colors.Brand, + modifier = Modifier.size(18.dp) + ) + } + ) + } PrimaryButton( text = stringResource(R.string.common__share), size = ButtonSize.Small, From 17b28439c2d063b02bbfe83f61d71f3498b9d02a Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Thu, 15 May 2025 14:53:19 -0300 Subject: [PATCH 3/6] feat: implement tooltip in the qr image --- .../to/bitkit/ui/components/QrCodeImage.kt | 40 ++++++++++++++++--- .../wallets/receive/ReceiveQrScreen.kt | 3 +- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt b/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt index 4bb856051..c5d62c758 100644 --- a/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt +++ b/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt @@ -1,5 +1,6 @@ package to.bitkit.ui.components +import android.content.ClipData import android.graphics.Bitmap import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -12,11 +13,14 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -25,9 +29,14 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.ClipEntry +import androidx.compose.ui.platform.LocalClipboard +import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -41,14 +50,22 @@ import to.bitkit.R import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors import androidx.core.graphics.createBitmap +import to.bitkit.ui.shared.util.clickableAlpha +@OptIn(ExperimentalMaterial3Api::class) @Composable fun QrCodeImage( content: String, modifier: Modifier = Modifier, logoPainter: Painter? = null, + tipMessage: String = "", size: Dp = LocalConfiguration.current.screenWidthDp.dp, ) { + val clipboard = LocalClipboardManager.current + + val tooltipState = rememberTooltipState() + val coroutineScope = rememberCoroutineScope() + Box( contentAlignment = Alignment.TopCenter, modifier = modifier @@ -59,11 +76,24 @@ fun QrCodeImage( val bitmap = rememberQrBitmap(content, size) if (bitmap != null) { - Image( - painter = remember(bitmap) { BitmapPainter(bitmap.asImageBitmap()) }, - contentDescription = null, - contentScale = ContentScale.Inside, - ) + Tooltip( + text = tipMessage, + tooltipState = tooltipState + ) { + Image( + painter = remember(bitmap) { BitmapPainter(bitmap.asImageBitmap()) }, + contentDescription = null, + contentScale = ContentScale.Inside, + modifier = if (tipMessage.isNotBlank()) { + Modifier.clickableAlpha { + coroutineScope.launch { + tooltipState.show() + clipboard.setText(AnnotatedString(content)) + } + } + } else Modifier + ) + } logoPainter?.let { Box( contentAlignment = Alignment.Center, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt index 9b752c192..32445eb66 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt @@ -444,7 +444,8 @@ private fun ReceiveQrSlide( QrCodeImage( content = uri, logoPainter = qrLogoPainter, - modifier = Modifier.weight(1f, fill = false) + tipMessage = stringResource(R.string.wallet__receive_copied), + modifier = Modifier.weight(1f, fill = false), ) Spacer(modifier = Modifier.height(16.dp)) From dac2d6c0067ff0278919d4834587ede3b65c262a Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Thu, 15 May 2025 15:13:05 -0300 Subject: [PATCH 4/6] feat: implement tooltip in the CopyAddressCard --- .../wallets/receive/ReceiveQrScreen.kt | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt index 32445eb66..e01d7875b 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt @@ -104,7 +104,7 @@ fun ReceiveQrSheet( val cjitInvoice = remember { mutableStateOf(null) } val showCreateCjit = remember { mutableStateOf(false) } val cjitEntryDetails = remember { mutableStateOf(null) } - val lightningState : LightningState by wallet.lightningState.collectAsStateWithLifecycle() + val lightningState: LightningState by wallet.lightningState.collectAsStateWithLifecycle() LaunchedEffect(Unit) { try { @@ -134,7 +134,7 @@ fun ReceiveQrSheet( LaunchedEffect(Unit) { wallet.walletEffect.collect { effect -> - when(effect) { + when (effect) { WalletViewModelEffects.NavigateGeoBlockScreen -> { navController.navigate(ReceiveRoutes.LOCATION_BLOCK) } @@ -154,6 +154,7 @@ fun ReceiveQrSheet( showCreateCjit.value = false cjitInvoice.value = null } + active && cjitInvoice.value == null -> { showCreateCjit.value = true navController.navigate(ReceiveRoutes.AMOUNT) @@ -549,6 +550,7 @@ private fun CopyValuesSlide( enum class CopyAddressType { ONCHAIN, LIGHTNING } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun CopyAddressCard( title: String, @@ -557,6 +559,10 @@ private fun CopyAddressCard( ) { val clipboard = LocalClipboardManager.current val context = LocalContext.current + + val tooltipState = rememberTooltipState() + val coroutineScope = rememberCoroutineScope() + Column( modifier = Modifier .fillMaxWidth() @@ -576,21 +582,30 @@ private fun CopyAddressCard( Row( horizontalArrangement = Arrangement.spacedBy(16.dp) ) { - PrimaryButton( - text = stringResource(R.string.common__copy), - size = ButtonSize.Small, - onClick = { clipboard.setText(AnnotatedString(address)) }, - fullWidth = false, - color = Colors.White10, - icon = { - Icon( - painter = painterResource(R.drawable.ic_copy), - contentDescription = null, - tint = if (type == CopyAddressType.ONCHAIN) Colors.Brand else Colors.Purple, - modifier = Modifier.size(18.dp) - ) - } - ) + + Tooltip( + text = stringResource(R.string.wallet__receive_copied), + tooltipState = tooltipState + ) { + PrimaryButton( + text = stringResource(R.string.common__copy), + size = ButtonSize.Small, + onClick = { + clipboard.setText(AnnotatedString(address)) + coroutineScope.launch { tooltipState.show() } + }, + fullWidth = false, + color = Colors.White10, + icon = { + Icon( + painter = painterResource(R.drawable.ic_copy), + contentDescription = null, + tint = if (type == CopyAddressType.ONCHAIN) Colors.Brand else Colors.Purple, + modifier = Modifier.size(18.dp) + ) + } + ) + } PrimaryButton( text = stringResource(R.string.common__share), size = ButtonSize.Small, From 727c54e1254fa2d4a2ea13daf9c322686e05c158 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Fri, 16 May 2025 07:20:43 -0300 Subject: [PATCH 5/6] fix: send text to clipboard before displaying tooltip --- app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt b/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt index c5d62c758..b3b8c9642 100644 --- a/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt +++ b/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt @@ -87,8 +87,8 @@ fun QrCodeImage( modifier = if (tipMessage.isNotBlank()) { Modifier.clickableAlpha { coroutineScope.launch { - tooltipState.show() clipboard.setText(AnnotatedString(content)) + tooltipState.show() } } } else Modifier From 67bccdd91fb15c0bbbb5c821875d7d3dcf440db0 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Fri, 16 May 2025 07:27:26 -0300 Subject: [PATCH 6/6] refactor: remove imports --- .../main/java/to/bitkit/ui/components/QrCodeImage.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt b/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt index b3b8c9642..373e7302c 100644 --- a/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt +++ b/app/src/main/java/to/bitkit/ui/components/QrCodeImage.kt @@ -1,13 +1,10 @@ package to.bitkit.ui.components -import android.content.ClipData import android.graphics.Bitmap import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape @@ -29,17 +26,15 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.ClipEntry -import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.core.graphics.createBitmap import com.google.zxing.BarcodeFormat import com.google.zxing.EncodeHintType import com.google.zxing.WriterException @@ -47,10 +42,9 @@ import com.google.zxing.qrcode.QRCodeWriter import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import to.bitkit.R +import to.bitkit.ui.shared.util.clickableAlpha import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors -import androidx.core.graphics.createBitmap -import to.bitkit.ui.shared.util.clickableAlpha @OptIn(ExperimentalMaterial3Api::class) @Composable