From aa12d8750ed9e35ec653ced0d486293d12bdb0f4 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 15 Jul 2025 01:13:32 +0200 Subject: [PATCH] better receive view but it freezes --- .../android/wallet/ui/QRCodeGenerator.kt | 12 +- .../android/wallet/ui/ReceiveDialog.kt | 678 ++++++++++-------- .../bitchat/android/wallet/ui/WalletScreen.kt | 65 +- .../wallet/viewmodel/WalletViewModel.kt | 21 + 4 files changed, 457 insertions(+), 319 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/QRCodeGenerator.kt b/app/src/main/java/com/bitchat/android/wallet/ui/QRCodeGenerator.kt index 6e699c30..0e48a0ad 100644 --- a/app/src/main/java/com/bitchat/android/wallet/ui/QRCodeGenerator.kt +++ b/app/src/main/java/com/bitchat/android/wallet/ui/QRCodeGenerator.kt @@ -27,10 +27,11 @@ fun QRCodeCanvas( size: Dp = 200.dp ) { val density = LocalDensity.current - val sizePx = with(density) { size.toPx() }.toInt() + // Use a larger fixed size for better quality val qrCodeBitMatrix = remember(text) { try { + val sizePx = with(density) { 512.dp.toPx() }.toInt() // Fixed high resolution val writer = QRCodeWriter() writer.encode(text, BarcodeFormat.QR_CODE, sizePx, sizePx) } catch (e: WriterException) { @@ -40,20 +41,21 @@ fun QRCodeCanvas( Canvas( modifier = modifier - .size(size) .background(Color.White) ) { qrCodeBitMatrix?.let { bitMatrix -> - drawQRCode(bitMatrix, sizePx) + // Use the actual canvas size, not a calculated size + val canvasSize = minOf(this.size.width, this.size.height) + drawQRCode(bitMatrix, canvasSize) } } } private fun DrawScope.drawQRCode( bitMatrix: com.google.zxing.common.BitMatrix, - sizePx: Int + sizePx: Float ) { - val cellSize = sizePx.toFloat() / bitMatrix.width + val cellSize = sizePx / bitMatrix.width for (x in 0 until bitMatrix.width) { for (y in 0 until bitMatrix.height) { diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveDialog.kt b/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveDialog.kt index 513b94fb..031d45e6 100644 --- a/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveDialog.kt +++ b/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveDialog.kt @@ -1,5 +1,6 @@ package com.bitchat.android.wallet.ui +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape @@ -13,132 +14,156 @@ import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties import com.bitchat.android.wallet.data.MintQuote import com.bitchat.android.wallet.data.CashuToken import com.bitchat.android.wallet.viewmodel.WalletViewModel /** - * Receive dialog with options for Cashu tokens or Lightning invoices + * Full-screen receive view with options for Cashu tokens or Lightning invoices */ @Composable -fun ReceiveDialog( +fun ReceiveView( viewModel: WalletViewModel, - onDismiss: () -> Unit + onNavigateBack: () -> Unit ) { val receiveType by viewModel.receiveType.observeAsState(WalletViewModel.ReceiveType.CASHU) val decodedToken by viewModel.decodedToken.observeAsState() val currentMintQuote by viewModel.currentMintQuote.observeAsState() val isLoading by viewModel.isLoading.observeAsState(false) - Dialog( - onDismissRequest = onDismiss, - properties = DialogProperties(usePlatformDefaultWidth = false) + // Determine if we should show type selector + val showTypeSelector = decodedToken == null && currentMintQuote == null + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black) ) { - Card( + Column( modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = Color(0xFF1A1A1A)) + .fillMaxSize() + .padding(16.dp) + .verticalScroll(rememberScrollState()) ) { - Column( + // Header with back button + Row( modifier = Modifier - .padding(24.dp) - .verticalScroll(rememberScrollState()) + .fillMaxWidth() + .padding(bottom = 24.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically ) { - // Header - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + IconButton( + onClick = onNavigateBack, + modifier = Modifier + .size(48.dp) + .clip(RoundedCornerShape(12.dp)) + .background(Color(0xFF1A1A1A)) ) { - Text( - text = "Receive", - color = Color.White, - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) - - IconButton(onClick = onDismiss) { - Icon( - imageVector = Icons.Filled.Close, - contentDescription = "Close", - tint = Color.Gray - ) - } - } - - Spacer(modifier = Modifier.height(16.dp)) - - // Receive type selector - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - FilterChip( - onClick = { viewModel.setReceiveType(WalletViewModel.ReceiveType.CASHU) }, - label = { - Text( - text = "Cashu", - fontFamily = FontFamily.Monospace - ) - }, - selected = receiveType == WalletViewModel.ReceiveType.CASHU, - colors = FilterChipDefaults.filterChipColors( - selectedContainerColor = Color(0xFF00C851), - selectedLabelColor = Color.Black - ), - modifier = Modifier.weight(1f) - ) - - FilterChip( - onClick = { viewModel.setReceiveType(WalletViewModel.ReceiveType.LIGHTNING) }, - label = { - Text( - text = "Lightning", - fontFamily = FontFamily.Monospace - ) - }, - selected = receiveType == WalletViewModel.ReceiveType.LIGHTNING, - colors = FilterChipDefaults.filterChipColors( - selectedContainerColor = Color(0xFF00C851), - selectedLabelColor = Color.Black - ), - modifier = Modifier.weight(1f) + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = Color.White, + modifier = Modifier.size(24.dp) ) } - Spacer(modifier = Modifier.height(24.dp)) + Text( + text = "RECEIVE", + color = Color.White, + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 2.sp + ) - // Content based on receive type - when (receiveType) { - WalletViewModel.ReceiveType.CASHU -> { - CashuReceiveContent( - viewModel = viewModel, - decodedToken = decodedToken, - isLoading = isLoading + // Spacer to center the title + Spacer(modifier = Modifier.width(48.dp)) + } + + // Receive type selector (only show when not displaying QR code or token info) + if (showTypeSelector) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 24.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color(0xFF1A1A1A)) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + FilterChip( + onClick = { viewModel.setReceiveType(WalletViewModel.ReceiveType.CASHU) }, + label = { + Text( + text = "Cashu Token", + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium + ) + }, + selected = receiveType == WalletViewModel.ReceiveType.CASHU, + colors = FilterChipDefaults.filterChipColors( + selectedContainerColor = Color(0xFF00C851), + selectedLabelColor = Color.Black, + containerColor = Color(0xFF2A2A2A), + labelColor = Color.White + ), + modifier = Modifier.weight(1f) ) - } - WalletViewModel.ReceiveType.LIGHTNING -> { - LightningReceiveContent( - viewModel = viewModel, - currentMintQuote = currentMintQuote, - isLoading = isLoading + + FilterChip( + onClick = { viewModel.setReceiveType(WalletViewModel.ReceiveType.LIGHTNING) }, + label = { + Text( + text = "Lightning Invoice", + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium + ) + }, + selected = receiveType == WalletViewModel.ReceiveType.LIGHTNING, + colors = FilterChipDefaults.filterChipColors( + selectedContainerColor = Color(0xFF00C851), + selectedLabelColor = Color.Black, + containerColor = Color(0xFF2A2A2A), + labelColor = Color.White + ), + modifier = Modifier.weight(1f) ) } } } + + // Content based on receive type + when (receiveType) { + WalletViewModel.ReceiveType.CASHU -> { + CashuReceiveContent( + viewModel = viewModel, + decodedToken = decodedToken, + isLoading = isLoading + ) + } + WalletViewModel.ReceiveType.LIGHTNING -> { + LightningReceiveContent( + viewModel = viewModel, + currentMintQuote = currentMintQuote, + isLoading = isLoading + ) + } + } } } } @@ -154,116 +179,126 @@ private fun CashuReceiveContent( if (decodedToken != null) { // Show decoded token info and receive button - Column { - Text( - text = "Cashu Token Details", - color = Color(0xFF00C851), - fontSize = 16.sp, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) - - Spacer(modifier = Modifier.height(16.dp)) - + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Token info card Card( - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(8.dp), - colors = CardDefaults.cardColors(containerColor = Color(0xFF2A2A2A)) + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 32.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color(0xFF1A1A1A)) ) { - Column(modifier = Modifier.padding(16.dp)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = "Amount:", - color = Color.Gray, - fontFamily = FontFamily.Monospace - ) - Text( - text = formatSats(decodedToken.amount.toLong()), - color = Color.White, - fontFamily = FontFamily.Monospace, - fontWeight = FontWeight.Bold - ) - } + Column( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + imageVector = Icons.Filled.Toll, + contentDescription = "Token", + tint = Color(0xFF00C851), + modifier = Modifier.size(48.dp) + ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(16.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = "Mint:", - color = Color.Gray, - fontFamily = FontFamily.Monospace - ) - Text( - text = decodedToken.mint.take(20) + if (decodedToken.mint.length > 20) "..." else "", - color = Color.White, - fontFamily = FontFamily.Monospace - ) - } + Text( + text = "CASHU TOKEN", + color = Color(0xFF00C851), + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Amount + Text( + text = formatSats(decodedToken.amount.toLong()), + color = Color.White, + fontSize = 32.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // Mint info + Text( + text = "From: ${decodedToken.mint.take(30)}${if (decodedToken.mint.length > 30) "..." else ""}", + color = Color.Gray, + fontSize = 14.sp, + fontFamily = FontFamily.Monospace, + textAlign = TextAlign.Center + ) if (!decodedToken.memo.isNullOrEmpty()) { Spacer(modifier = Modifier.height(8.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = "Memo:", - color = Color.Gray, - fontFamily = FontFamily.Monospace - ) - Text( - text = decodedToken.memo, - color = Color.White, - fontFamily = FontFamily.Monospace - ) - } + Text( + text = "Memo: ${decodedToken.memo}", + color = Color.Gray, + fontSize = 14.sp, + fontFamily = FontFamily.Monospace, + textAlign = TextAlign.Center + ) } } } - Spacer(modifier = Modifier.height(24.dp)) - + // Receive button Button( onClick = { viewModel.receiveCashuToken(decodedToken.token) }, enabled = !isLoading, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .height(56.dp), colors = ButtonDefaults.buttonColors( - containerColor = Color(0xFF00C851) + containerColor = Color(0xFF00C851), + disabledContainerColor = Color(0xFF2A2A2A) ), - shape = RoundedCornerShape(8.dp) + shape = RoundedCornerShape(16.dp) ) { if (isLoading) { CircularProgressIndicator( color = Color.Black, - modifier = Modifier.size(16.dp) + modifier = Modifier.size(24.dp) ) } else { - Icon( - imageVector = Icons.Filled.Download, - contentDescription = "Receive", - tint = Color.Black - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = "Receive Token", - color = Color.Black, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.Download, + contentDescription = "Receive", + tint = Color.Black, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "RECEIVE TOKEN", + color = Color.Black, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + } } } } } else { // Show token input - Column { + Column( + modifier = Modifier.fillMaxWidth() + ) { + // Token input field OutlinedTextField( value = token, onValueChange = { @@ -288,11 +323,17 @@ private fun CashuReceiveContent( colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = Color(0xFF00C851), focusedLabelColor = Color(0xFF00C851), + unfocusedBorderColor = Color(0xFF2A2A2A), + unfocusedLabelColor = Color.Gray, unfocusedTextColor = Color.White, focusedTextColor = Color.White ), - modifier = Modifier.fillMaxWidth(), - maxLines = 3, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 24.dp), + minLines = 3, + maxLines = 4, + shape = RoundedCornerShape(16.dp), trailingIcon = { IconButton( onClick = { @@ -300,7 +341,7 @@ private fun CashuReceiveContent( } ) { Icon( - imageVector = Icons.Filled.CameraAlt, + imageVector = Icons.Filled.QrCode, contentDescription = "Scan QR", tint = Color(0xFF00C851) ) @@ -308,8 +349,7 @@ private fun CashuReceiveContent( } ) - Spacer(modifier = Modifier.height(16.dp)) - + // Paste button Button( onClick = { clipboardManager.getText()?.text?.let { clipText -> @@ -319,28 +359,37 @@ private fun CashuReceiveContent( } } }, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .height(56.dp), colors = ButtonDefaults.buttonColors( containerColor = Color.Transparent ), border = androidx.compose.foundation.BorderStroke( - 1.dp, + 2.dp, Color(0xFF00C851) ), - shape = RoundedCornerShape(8.dp) + shape = RoundedCornerShape(16.dp) ) { - Icon( - imageVector = Icons.Filled.ContentPaste, - contentDescription = "Paste", - tint = Color(0xFF00C851) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = "Paste from clipboard", - color = Color(0xFF00C851), - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.ContentPaste, + contentDescription = "Paste", + tint = Color(0xFF00C851), + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "PASTE FROM CLIPBOARD", + color = Color(0xFF00C851), + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + } } } } @@ -358,29 +407,91 @@ private fun LightningReceiveContent( if (currentMintQuote != null) { // Show Lightning invoice QR code and details - Column { - Text( - text = "Lightning Invoice", - color = Color(0xFF00C851), - fontSize = 16.sp, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) - - Spacer(modifier = Modifier.height(16.dp)) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Invoice header + Card( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 24.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color(0xFF1A1A1A)) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + imageVector = Icons.Filled.FlashOn, + contentDescription = "Lightning", + tint = Color(0xFFFFB000), + modifier = Modifier.size(48.dp) + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "LIGHTNING INVOICE", + color = Color(0xFFFFB000), + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Amount + Text( + text = formatSats(currentMintQuote.amount.toLong()), + color = Color.White, + fontSize = 32.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // Status + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(12.dp) + .clip(RoundedCornerShape(6.dp)) + .background(if (currentMintQuote.paid) Color(0xFF00C851) else Color(0xFFFFB000)) + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = if (currentMintQuote.paid) "PAID" else "WAITING FOR PAYMENT", + color = if (currentMintQuote.paid) Color(0xFF00C851) else Color(0xFFFFB000), + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + } + } + } // QR Code Card( modifier = Modifier .fillMaxWidth() - .aspectRatio(1f), - shape = RoundedCornerShape(8.dp), + .aspectRatio(1f) + .padding(bottom = 24.dp), + shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color.White) ) { Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), + modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { QRCodeCanvas( @@ -390,85 +501,50 @@ private fun LightningReceiveContent( } } - Spacer(modifier = Modifier.height(16.dp)) - - // Invoice details - Card( - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(8.dp), - colors = CardDefaults.cardColors(containerColor = Color(0xFF2A2A2A)) - ) { - Column(modifier = Modifier.padding(16.dp)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = "Amount:", - color = Color.Gray, - fontFamily = FontFamily.Monospace - ) - Text( - text = formatSats(currentMintQuote.amount.toLong()), - color = Color.White, - fontFamily = FontFamily.Monospace, - fontWeight = FontWeight.Bold - ) - } - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = "Status:", - color = Color.Gray, - fontFamily = FontFamily.Monospace - ) - Text( - text = if (currentMintQuote.paid) "Paid" else "Waiting...", - color = if (currentMintQuote.paid) Color(0xFF00C851) else Color.Yellow, - fontFamily = FontFamily.Monospace, - fontWeight = FontWeight.Bold - ) - } - } - } - - Spacer(modifier = Modifier.height(16.dp)) - // Copy invoice button Button( onClick = { clipboardManager.setText(AnnotatedString(currentMintQuote.request)) }, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .height(56.dp), colors = ButtonDefaults.buttonColors( containerColor = Color.Transparent ), border = androidx.compose.foundation.BorderStroke( - 1.dp, - Color(0xFF00C851) + 2.dp, + Color(0xFFFFB000) ), - shape = RoundedCornerShape(8.dp) + shape = RoundedCornerShape(16.dp) ) { - Icon( - imageVector = Icons.Filled.ContentCopy, - contentDescription = "Copy", - tint = Color(0xFF00C851) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = "Copy Invoice", - color = Color(0xFF00C851), - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.ContentCopy, + contentDescription = "Copy", + tint = Color(0xFFFFB000), + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "COPY INVOICE", + color = Color(0xFFFFB000), + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + } } } } else { // Show amount input - Column { + Column( + modifier = Modifier.fillMaxWidth() + ) { + // Amount input OutlinedTextField( value = amount, onValueChange = { amount = it }, @@ -482,14 +558,18 @@ private fun LightningReceiveContent( colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = Color(0xFF00C851), focusedLabelColor = Color(0xFF00C851), + unfocusedBorderColor = Color(0xFF2A2A2A), + unfocusedLabelColor = Color.Gray, unfocusedTextColor = Color.White, focusedTextColor = Color.White ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + shape = RoundedCornerShape(16.dp) ) - Spacer(modifier = Modifier.height(16.dp)) - + // Description input OutlinedTextField( value = description, onValueChange = { description = it }, @@ -502,14 +582,18 @@ private fun LightningReceiveContent( colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = Color(0xFF00C851), focusedLabelColor = Color(0xFF00C851), - unfocusedTextColor = Color.White, + unfocusedBorderColor = Color(0xFF2A2A2A), + unfocusedLabelColor = Color.Gray, + unfocusedTextColor = Color.White, focusedTextColor = Color.White ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 32.dp), + shape = RoundedCornerShape(16.dp) ) - Spacer(modifier = Modifier.height(24.dp)) - + // Create invoice button Button( onClick = { amount.toLongOrNull()?.let { amountSats -> @@ -517,30 +601,40 @@ private fun LightningReceiveContent( } }, enabled = !isLoading && amount.toLongOrNull()?.let { it > 0 } == true, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .height(56.dp), colors = ButtonDefaults.buttonColors( - containerColor = Color(0xFF00C851) + containerColor = Color(0xFF00C851), + disabledContainerColor = Color(0xFF2A2A2A) ), - shape = RoundedCornerShape(8.dp) + shape = RoundedCornerShape(16.dp) ) { if (isLoading) { CircularProgressIndicator( color = Color.Black, - modifier = Modifier.size(16.dp) + modifier = Modifier.size(24.dp) ) } else { - Icon( - imageVector = Icons.Filled.FlashOn, - contentDescription = "Create Invoice", - tint = Color.Black - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = "Create Invoice", - color = Color.Black, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.FlashOn, + contentDescription = "Create Invoice", + tint = Color.Black, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "CREATE INVOICE", + color = Color.Black, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + } } } } diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/WalletScreen.kt b/app/src/main/java/com/bitchat/android/wallet/ui/WalletScreen.kt index 03c5b426..221be31f 100644 --- a/app/src/main/java/com/bitchat/android/wallet/ui/WalletScreen.kt +++ b/app/src/main/java/com/bitchat/android/wallet/ui/WalletScreen.kt @@ -25,29 +25,49 @@ fun WalletScreen( modifier: Modifier = Modifier ) { var selectedTab by remember { mutableStateOf(0) } + var showReceiveView by remember { mutableStateOf(false) } val showSendDialog by walletViewModel.showSendDialog.observeAsState(false) val showReceiveDialog by walletViewModel.showReceiveDialog.observeAsState(false) - Column(modifier = modifier.fillMaxSize()) { - // Content - when (selectedTab) { - 0 -> WalletOverview(viewModel = walletViewModel, modifier = Modifier.weight(1f)) - 1 -> TransactionHistory( - transactions = walletViewModel.getAllTransactions().observeAsState(initial = emptyList()).value, - modifier = Modifier.weight(1f) - ) - 2 -> MintsScreen(viewModel = walletViewModel, modifier = Modifier.weight(1f)) - 3 -> WalletSettings( - viewModel = walletViewModel, - onBackClick = { /* No back action needed in tab navigation */ } + if (showReceiveView) { + // Full-screen ReceiveView + ReceiveView( + viewModel = walletViewModel, + onNavigateBack = { + showReceiveView = false + walletViewModel.hideReceiveDialog() + } + ) + } else { + Column(modifier = modifier.fillMaxSize()) { + // Content + when (selectedTab) { + 0 -> WalletOverview(viewModel = walletViewModel, modifier = Modifier.weight(1f)) + 1 -> TransactionHistory( + transactions = walletViewModel.getAllTransactions().observeAsState(initial = emptyList()).value, + modifier = Modifier.weight(1f), + onTransactionClick = { transaction -> + // For lightning receive transactions, open the receive view with the quote + if (transaction.type == com.bitchat.android.wallet.data.TransactionType.LIGHTNING_RECEIVE && + transaction.quote != null) { + walletViewModel.setCurrentMintQuote(transaction.quote!!) + showReceiveView = true + } + } + ) + 2 -> MintsScreen(viewModel = walletViewModel, modifier = Modifier.weight(1f)) + 3 -> WalletSettings( + viewModel = walletViewModel, + onBackClick = { /* No back action needed in tab navigation */ } + ) + } + + // Bottom Navigation + WalletBottomNavigation( + selectedTab = selectedTab, + onTabSelected = { selectedTab = it } ) } - - // Bottom Navigation - WalletBottomNavigation( - selectedTab = selectedTab, - onTabSelected = { selectedTab = it } - ) } // Dialogs @@ -59,10 +79,11 @@ fun WalletScreen( } if (showReceiveDialog) { - ReceiveDialog( - viewModel = walletViewModel, - onDismiss = { walletViewModel.hideReceiveDialog() } - ) + LaunchedEffect(showReceiveDialog) { + if (showReceiveDialog) { + showReceiveView = true + } + } } } diff --git a/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt b/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt index 6e496177..9fc59445 100644 --- a/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt +++ b/app/src/main/java/com/bitchat/android/wallet/viewmodel/WalletViewModel.kt @@ -274,6 +274,11 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) val updatedQuote = quote.copy(paid = true, state = MintQuoteState.PAID) repository.saveMintQuote(updatedQuote) + // Update currentMintQuote if it's the same quote + if (_currentMintQuote.value?.id == quote.id) { + _currentMintQuote.value = updatedQuote + } + val transaction = WalletTransaction( id = UUID.randomUUID().toString(), type = TransactionType.LIGHTNING_RECEIVE, @@ -372,6 +377,22 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) _errorMessage.value = null } + /** + * Set a specific mint quote as current (for reopening from transaction list) + */ + fun setCurrentMintQuote(quoteId: String) { + viewModelScope.launch { + repository.getMintQuotes().onSuccess { quotes -> + val quote = quotes.find { it.id == quoteId } + if (quote != null) { + _currentMintQuote.value = quote + _receiveType.value = ReceiveType.LIGHTNING + showReceiveDialog() + } + } + } + } + // Wallet Operations /**