diff --git a/app/src/main/java/com/bitchat/android/wallet/data/Mint.kt b/app/src/main/java/com/bitchat/android/wallet/data/Mint.kt index 87a874c5..ddf75d2e 100644 --- a/app/src/main/java/com/bitchat/android/wallet/data/Mint.kt +++ b/app/src/main/java/com/bitchat/android/wallet/data/Mint.kt @@ -2,6 +2,14 @@ package com.bitchat.android.wallet.data import java.util.Date +/** + * Represents contact information for a mint + */ +data class ContactInfo( + val method: String, // e.g., "email", "twitter", "nostr" + val info: String // e.g., "contact@example.com", "@username", "npub..." +) + /** * Represents a Cashu mint information */ @@ -10,9 +18,9 @@ data class MintInfo( val name: String, val description: String? = null, val descriptionLong: String? = null, - val contact: String? = null, + val contact: List? = null, val version: String, - val nuts: Map, // Simplified - was Map + val nuts: Map? = null, // NUT specifications supported by the mint val motd: String? = null, val icon: String? = null, val time: Date? = null diff --git a/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt b/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt index b9787ae6..fd6156bb 100644 --- a/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt +++ b/app/src/main/java/com/bitchat/android/wallet/service/CashuService.kt @@ -21,7 +21,6 @@ class CashuService { private var currentMintUrl: String? = null private var currentUnit: String = "sat" private var isInitialized = false - private var isCdkAvailable = false private var repository: WalletRepository? = null companion object { @@ -160,38 +159,6 @@ class CashuService { } } - /** - * Check if CDK is available and initialize appropriately - */ - private fun initializeCdkAvailability(): Boolean { - if (isCdkAvailable) return true - - Log.d(TAG, "=== CDK LIBRARY AVAILABILITY CHECK ===") - - try { - // Test CDK availability by calling a simple function - val testMnemonic = generateMnemonic() - Log.d(TAG, "✅ CDK library is fully available and functional!") - Log.d(TAG, "Generated test mnemonic with ${testMnemonic.split(" ").size} words") - isCdkAvailable = true - return true - - } catch (e: UnsatisfiedLinkError) { - Log.w(TAG, "❌ CDK library not available: ${e.message}") - } catch (e: FfiException) { - Log.w(TAG, "❌ CDK FFI error: ${e.message}") - } catch (e: NoClassDefFoundError) { - Log.w(TAG, "❌ CDK classes not found: ${e.message}") - } catch (e: Exception) { - Log.w(TAG, "❌ Unexpected CDK error: ${e.message}") - } - - isCdkAvailable = false - Log.w(TAG, "⚠️ CDK library not available - wallet will not function properly") - Log.d(TAG, "=== END CDK LIBRARY CHECK ===") - return false - } - /** * Initialize the CDK wallet with a mint URL */ @@ -200,11 +167,6 @@ class CashuService { try { Log.d(TAG, "Initializing wallet with mint: $mintUrl") - // Test CDK availability first - if (!initializeCdkAvailability()) { - return@withContext Result.failure(Exception("CDK library not available")) - } - // Create database path using application context val context = getApplicationContext() val dbPath = "${context.filesDir}/cashu_wallet.db" @@ -269,7 +231,7 @@ class CashuService { initializeWallet(mintUrl).getOrThrow() } - val mintInfo = if (isCdkAvailable && wallet != null) { + val mintInfo = if (wallet != null) { try { // Get real mint info from CDK val mintInfoData = wallet!!.getMintInfo() @@ -280,7 +242,7 @@ class CashuService { name = extractMintName(mintUrl), description = "Cashu mint via CDK", descriptionLong = "Real Cashu mint connection via CDK-FFI", - contact = "", + contact = emptyList(), version = "1.0.0", nuts = mapOf( "4" to "true", // NUT-4: Mint quote @@ -318,7 +280,7 @@ class CashuService { initializeWallet(DEFAULT_MINT_URL).getOrThrow() } - if (!isCdkAvailable || wallet == null) { + if (wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } @@ -349,7 +311,7 @@ class CashuService { initializeWallet(DEFAULT_MINT_URL).getOrThrow() } - if (!isCdkAvailable || wallet == null) { + if (wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } @@ -446,7 +408,7 @@ class CashuService { ) // Now receive the token with the properly initialized wallet - if (!isCdkAvailable || wallet == null) { + if (wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } @@ -476,7 +438,7 @@ class CashuService { initializeWallet(DEFAULT_MINT_URL).getOrThrow() } - if (!isCdkAvailable || wallet == null) { + if (wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } @@ -527,7 +489,7 @@ class CashuService { initializeWallet(DEFAULT_MINT_URL).getOrThrow() } - if (!isCdkAvailable || wallet == null) { + if (wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } @@ -574,7 +536,7 @@ class CashuService { initializeWallet(DEFAULT_MINT_URL).getOrThrow() } - if (!isCdkAvailable || wallet == null) { + if (wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } @@ -619,7 +581,7 @@ class CashuService { initializeWallet(DEFAULT_MINT_URL).getOrThrow() } - if (!isCdkAvailable || wallet == null) { + if (wallet == null) { return@withContext Result.failure(Exception("CDK not available")) } @@ -693,14 +655,6 @@ class CashuService { } } - /** - * Check if real CDK is available - */ - fun isCdkAvailable(): Boolean { - initializeCdkAvailability() - return isCdkAvailable - } - // Helper functions private fun getApplicationContext(): Context { diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/MintsScreen.kt b/app/src/main/java/com/bitchat/android/wallet/ui/MintsScreen.kt index d14b280d..c3fe986d 100644 --- a/app/src/main/java/com/bitchat/android/wallet/ui/MintsScreen.kt +++ b/app/src/main/java/com/bitchat/android/wallet/ui/MintsScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.bitchat.android.wallet.data.Mint import com.bitchat.android.wallet.viewmodel.WalletViewModel +import com.bitchat.android.wallet.ui.mintinfo.MintDetailsScreen import java.text.SimpleDateFormat import java.util.* @@ -41,7 +42,41 @@ fun MintsScreen( val showAddMintDialog by viewModel.showAddMintDialog.observeAsState(false) val isLoading by viewModel.isLoading.observeAsState(false) val errorMessage by viewModel.errorMessage.observeAsState() + val showMintDetails by viewModel.showMintDetails.observeAsState(false) + val selectedMintUrl by viewModel.selectedMintUrl.observeAsState() + // Show mint details screen if selected + if (showMintDetails && selectedMintUrl != null) { + MintDetailsScreen( + mintUrl = selectedMintUrl!!, + walletViewModel = viewModel, + onNavigateBack = { viewModel.hideMintDetails() }, + modifier = modifier + ) + } else { + // Show normal mints list + MintsListContent( + viewModel = viewModel, + mints = mints, + activeMint = activeMint, + showAddMintDialog = showAddMintDialog, + isLoading = isLoading, + errorMessage = errorMessage, + modifier = modifier + ) + } +} + +@Composable +private fun MintsListContent( + viewModel: WalletViewModel, + mints: List, + activeMint: String?, + showAddMintDialog: Boolean, + isLoading: Boolean, + errorMessage: String?, + modifier: Modifier = Modifier +) { Column( modifier = modifier .fillMaxSize() @@ -89,14 +124,14 @@ fun MintsScreen( if (mints.isEmpty()) { EmptyMintsCard(onAddClick = { viewModel.showAddMintDialog() }) } else { - MintsList( - mints = mints, - activeMint = activeMint, - onMintSelect = { viewModel.setActiveMint(it) }, - onMintEdit = { mint, newNickname -> - viewModel.updateMintNickname(mint, newNickname) - } - ) + MintsList( + mints = mints, + activeMint = activeMint, + onMintSelect = { viewModel.setActiveMint(it) }, + onMintInfo = { mintUrl -> + viewModel.showMintDetails(mintUrl) + } + ) } // Error message @@ -123,7 +158,7 @@ private fun MintsList( mints: List, activeMint: String?, onMintSelect: (String) -> Unit, - onMintEdit: (String, String) -> Unit + onMintInfo: (String) -> Unit ) { LazyColumn( verticalArrangement = Arrangement.spacedBy(8.dp) @@ -133,7 +168,7 @@ private fun MintsList( mint = mint, isActive = mint.url == activeMint, onSelect = { onMintSelect(mint.url) }, - onEdit = { newNickname -> onMintEdit(mint.url, newNickname) } + onInfo = { onMintInfo(mint.url) } ) } } @@ -144,9 +179,8 @@ private fun MintItem( mint: Mint, isActive: Boolean, onSelect: () -> Unit, - onEdit: (String) -> Unit + onInfo: () -> Unit ) { - var showEditDialog by remember { mutableStateOf(false) } Card( modifier = Modifier @@ -210,11 +244,11 @@ private fun MintItem( ) } - // Edit button - IconButton(onClick = { showEditDialog = true }) { + // Info button + IconButton(onClick = onInfo) { Icon( - imageVector = Icons.Filled.Edit, - contentDescription = "Edit", + imageVector = Icons.Filled.Info, + contentDescription = "Mint Details", tint = Color.Gray, modifier = Modifier.size(16.dp) ) @@ -232,17 +266,7 @@ private fun MintItem( } } - // Edit dialog - if (showEditDialog) { - EditMintDialog( - currentNickname = mint.nickname, - onSave = { - onEdit(it) - showEditDialog = false - }, - onDismiss = { showEditDialog = false } - ) - } + } @Composable @@ -436,100 +460,7 @@ private fun AddMintDialog( } } -@Composable -private fun EditMintDialog( - currentNickname: String, - onSave: (String) -> Unit, - onDismiss: () -> Unit -) { - var nickname by remember { mutableStateOf(currentNickname) } - - Dialog(onDismissRequest = onDismiss) { - Card( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = Color(0xFF1A1A1A)) - ) { - Column( - modifier = Modifier.padding(24.dp) - ) { - Text( - text = "Edit Mint Nickname", - color = Color.White, - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) - - Spacer(modifier = Modifier.height(16.dp)) - - OutlinedTextField( - value = nickname, - onValueChange = { nickname = it }, - label = { - Text( - text = "Nickname", - fontFamily = FontFamily.Monospace - ) - }, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = Color(0xFF00C851), - focusedLabelColor = Color(0xFF00C851), - unfocusedTextColor = Color.White, - focusedTextColor = Color.White - ), - modifier = Modifier.fillMaxWidth() - ) - - Spacer(modifier = Modifier.height(16.dp)) - - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - // Cancel button - Button( - onClick = onDismiss, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent - ), - border = androidx.compose.foundation.BorderStroke( - 1.dp, - Color.Gray - ), - shape = RoundedCornerShape(8.dp) - ) { - Text( - text = "Cancel", - color = Color.Gray, - fontFamily = FontFamily.Monospace - ) - } - - // Save button - Button( - onClick = { onSave(nickname) }, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.buttonColors( - containerColor = Color(0xFF00C851) - ), - shape = RoundedCornerShape(8.dp), - enabled = nickname.isNotEmpty() - ) { - Text( - text = "Save", - color = Color.Black, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ) - } - } - } - } - } -} + @Composable private fun ErrorCard( diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintActionButtons.kt b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintActionButtons.kt new file mode 100644 index 00000000..5ebc40b4 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintActionButtons.kt @@ -0,0 +1,139 @@ +package com.bitchat.android.wallet.ui.mintinfo + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +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.unit.dp +import androidx.compose.ui.unit.sp +import com.bitchat.android.wallet.data.Mint + +/** + * Action buttons section for mint operations + */ +@Composable +fun MintActionButtons( + mint: Mint, + onEditNickname: () -> Unit, + onCopyUrl: () -> Unit, + onDeleteMint: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + // Section divider and title + SectionDivider(title = "ACTIONS") + + // Action buttons container + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + // Edit nickname action + ActionButton( + icon = Icons.Filled.Edit, + label = "Edit Nickname", + onClick = onEditNickname + ) + + // Copy URL action + ActionButton( + icon = Icons.Filled.ContentCopy, + label = "Copy Mint URL", + onClick = onCopyUrl + ) + + // Delete mint action + ActionButton( + icon = Icons.Filled.Delete, + label = "Delete Mint", + onClick = onDeleteMint, + isDestructive = true + ) + } + } +} + +@Composable +private fun ActionButton( + icon: ImageVector, + label: String, + onClick: () -> Unit, + isDestructive: Boolean = false, + modifier: Modifier = Modifier +) { + val iconColor = if (isDestructive) Color(0xFFFF453A) else Color.Gray + val textColor = if (isDestructive) Color(0xFFFF453A) else Color.White + + Row( + modifier = modifier + .fillMaxWidth() + .clickable { onClick() } + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = icon, + contentDescription = label, + tint = iconColor, + modifier = Modifier.size(20.dp) + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Text( + text = label, + color = textColor, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace + ) + } +} + +@Composable +private fun SectionDivider( + title: String, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .weight(1f) + .height(1.dp) + .background(Color(0xFF333333)) + ) + + Text( + text = title, + color = Color.White, + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.Monospace, + modifier = Modifier.padding(horizontal = 10.dp) + ) + + Box( + modifier = Modifier + .weight(1f) + .height(1.dp) + .background(Color(0xFF333333)) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintDetailsDialogs.kt b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintDetailsDialogs.kt new file mode 100644 index 00000000..afca78ed --- /dev/null +++ b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintDetailsDialogs.kt @@ -0,0 +1,301 @@ +package com.bitchat.android.wallet.ui.mintinfo + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +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.Mint + +/** + * Dialog for editing mint nickname + */ +@Composable +fun EditMintNicknameDialog( + currentNickname: String, + onSave: (String) -> Unit, + onDismiss: () -> Unit +) { + var nickname by remember { mutableStateOf(currentNickname) } + + Dialog( + onDismissRequest = onDismiss, + properties = DialogProperties(usePlatformDefaultWidth = false) + ) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color(0xFF1A1A1A)) + ) { + Column( + modifier = Modifier.padding(24.dp) + ) { + // Header + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Edit Mint Nickname", + 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)) + + // Nickname input + OutlinedTextField( + value = nickname, + onValueChange = { nickname = it }, + label = { + Text( + text = "Nickname", + fontFamily = FontFamily.Monospace + ) + }, + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = Color(0xFF00C851), + focusedLabelColor = Color(0xFF00C851), + unfocusedTextColor = Color.White, + focusedTextColor = Color.White + ), + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Action buttons + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + // Cancel button + OutlinedButton( + onClick = onDismiss, + modifier = Modifier.weight(1f), + border = androidx.compose.foundation.BorderStroke( + 1.dp, + Color.Gray + ), + shape = RoundedCornerShape(8.dp) + ) { + Text( + text = "Cancel", + color = Color.Gray, + fontFamily = FontFamily.Monospace + ) + } + + // Save button + Button( + onClick = { onSave(nickname) }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF00C851) + ), + shape = RoundedCornerShape(8.dp), + enabled = nickname.isNotEmpty() + ) { + Text( + text = "Save", + color = Color.Black, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) + } + } + } + } + } +} + +/** + * Dialog for confirming mint deletion + */ +@Composable +fun DeleteMintDialog( + mint: Mint, + onConfirm: () -> Unit, + onDismiss: () -> Unit +) { + Dialog( + onDismissRequest = onDismiss, + properties = DialogProperties(usePlatformDefaultWidth = false) + ) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color(0xFF1A1A1A)) + ) { + Column( + modifier = Modifier.padding(24.dp) + ) { + // Header + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Delete Mint", + color = Color(0xFFFF453A), + 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)) + + // Warning message + Text( + text = "Are you sure you want to delete this mint from your wallet?", + color = Color.White, + fontSize = 16.sp, + fontFamily = FontFamily.Monospace, + lineHeight = 24.sp + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // Mint details + Card( + colors = CardDefaults.cardColors( + containerColor = Color(0x10FFFFFF) + ), + shape = RoundedCornerShape(8.dp) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + // Nickname + MintDetailRow( + label = "Nickname:", + value = mint.nickname + ) + + // URL + MintDetailRow( + label = "URL:", + value = mint.url + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // Warning note + Text( + text = "⚠️ This action cannot be undone. Any tokens from this mint will need to be re-added manually.", + color = Color(0xFFF18408), + fontSize = 14.sp, + fontFamily = FontFamily.Monospace, + lineHeight = 20.sp + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Action buttons + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + // Cancel button + OutlinedButton( + onClick = onDismiss, + modifier = Modifier.weight(1f), + border = androidx.compose.foundation.BorderStroke( + 1.dp, + Color.Gray + ), + shape = RoundedCornerShape(8.dp) + ) { + Text( + text = "Cancel", + color = Color.Gray, + fontFamily = FontFamily.Monospace + ) + } + + // Delete button + Button( + onClick = onConfirm, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFFFF453A) + ), + shape = RoundedCornerShape(8.dp) + ) { + Text( + text = "Delete", + color = Color.White, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) + } + } + } + } + } +} + +@Composable +private fun MintDetailRow( + label: String, + value: String, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.Top + ) { + Text( + text = label, + color = Color.Gray, + fontSize = 14.sp, + fontFamily = FontFamily.Monospace, + modifier = Modifier.width(80.dp) + ) + + Text( + text = value, + color = Color.White, + fontSize = 14.sp, + fontFamily = FontFamily.Monospace, + modifier = Modifier.weight(1f) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintDetailsScreen.kt b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintDetailsScreen.kt new file mode 100644 index 00000000..13e4b317 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintDetailsScreen.kt @@ -0,0 +1,267 @@ +package com.bitchat.android.wallet.ui.mintinfo + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.bitchat.android.wallet.data.Mint +import com.bitchat.android.wallet.viewmodel.WalletViewModel + +/** + * Detailed mint information screen with terminal-like design + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MintDetailsScreen( + mintUrl: String, + walletViewModel: WalletViewModel, + onNavigateBack: () -> Unit, + modifier: Modifier = Modifier +) { + val mints by walletViewModel.mints.observeAsState(emptyList()) + val isLoading by walletViewModel.isLoading.observeAsState(false) + val errorMessage by walletViewModel.errorMessage.observeAsState() + + // Find the mint by URL + val mint = mints.find { it.url == mintUrl } + + // Show error if mint not found + if (mint == null) { + MintNotFoundScreen(onNavigateBack = onNavigateBack) + return + } + + // State for MOTD dismissal + var isMotdDismissed by remember { mutableStateOf(false) } + + // State for dialogs + var showEditDialog by remember { mutableStateOf(false) } + var showDeleteDialog by remember { mutableStateOf(false) } + + Column( + modifier = modifier + .fillMaxSize() + .background(Color.Black) + ) { + // Header with back button + TopAppBar( + title = { + Text( + text = "Mint Details", + color = Color(0xFF00C851), + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Bold + ) + }, + navigationIcon = { + IconButton(onClick = onNavigateBack) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = Color(0xFF00C851) + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.Black + ) + ) + + // Scrollable content + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Mint Header Section + MintHeaderSection(mint = mint) + + // MOTD Section + if (mint.info?.motd?.isNotEmpty() == true) { + MintMotdComponent( + message = mint.info.motd, + isDismissed = isMotdDismissed, + onDismiss = { isMotdDismissed = true } + ) + } + + // Description Section + MintDescriptionSection(mint = mint) + + // Contact Section + if (mint.info?.contact?.isNotEmpty() == true) { + MintContactSection(contacts = mint.info.contact) + } + + // Details Section + MintDetailsSection(mint = mint) + + // Actions Section + MintActionButtons( + mint = mint, + onEditNickname = { showEditDialog = true }, + onCopyUrl = { walletViewModel.copyToClipboard(mint.url) }, + onDeleteMint = { showDeleteDialog = true } + ) + + // Error display + errorMessage?.let { error -> + ErrorCard( + message = error, + onDismiss = { walletViewModel.clearError() } + ) + } + + // Bottom spacing + Spacer(modifier = Modifier.height(32.dp)) + } + } + + // Edit nickname dialog + if (showEditDialog) { + EditMintNicknameDialog( + currentNickname = mint.nickname, + onSave = { newNickname -> + walletViewModel.updateMintNickname(mint.url, newNickname) + showEditDialog = false + }, + onDismiss = { showEditDialog = false } + ) + } + + // Delete mint dialog + if (showDeleteDialog) { + DeleteMintDialog( + mint = mint, + onConfirm = { + walletViewModel.deleteMint(mint.url) + showDeleteDialog = false + onNavigateBack() + }, + onDismiss = { showDeleteDialog = false } + ) + } + + // Loading overlay + if (isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator( + color = Color(0xFF00C851) + ) + } + } +} + +@Composable +private fun MintNotFoundScreen( + onNavigateBack: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.Black) + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = "Error", + tint = Color.Red, + modifier = Modifier.size(64.dp) + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "Mint Not Found", + color = Color.White, + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) + + Text( + text = "The requested mint could not be found", + color = Color.Gray, + fontSize = 16.sp, + fontFamily = FontFamily.Monospace + ) + + Spacer(modifier = Modifier.height(32.dp)) + + Button( + onClick = onNavigateBack, + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF00C851) + ) + ) { + Text( + text = "Go Back", + color = Color.Black, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) + } + } +} + +@Composable +private fun ErrorCard( + message: String, + onDismiss: () -> Unit +) { + Card( + colors = CardDefaults.cardColors( + containerColor = Color(0x30FF0000) + ) + ) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = "Error", + tint = Color.Red, + modifier = Modifier.size(20.dp) + ) + + Spacer(modifier = Modifier.width(12.dp)) + + Text( + text = message, + color = Color.Red, + fontSize = 14.sp, + fontFamily = FontFamily.Monospace, + modifier = Modifier.weight(1f) + ) + + IconButton(onClick = onDismiss) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = "Close", + tint = Color.Red, + modifier = Modifier.size(16.dp) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintInfoSections.kt b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintInfoSections.kt new file mode 100644 index 00000000..753bfd11 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintInfoSections.kt @@ -0,0 +1,436 @@ +package com.bitchat.android.wallet.ui.mintinfo + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +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.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.bitchat.android.wallet.data.ContactInfo +import com.bitchat.android.wallet.data.Mint +import java.text.SimpleDateFormat +import java.util.* + +/** + * Header section showing mint icon and name + */ +@Composable +fun MintHeaderSection( + mint: Mint, + modifier: Modifier = Modifier +) { + Card( + modifier = modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = Color(0xFF1A1A1A).copy(alpha = 0.05f) // Very subtle background + ) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Mint icon/avatar + Card( + modifier = Modifier.size(56.dp), + shape = RoundedCornerShape(28.dp), + colors = CardDefaults.cardColors( + containerColor = Color(0xFF2A2A2A) + ) + ) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + // For now, we'll use the AccountBalance icon as default + // In the future, could load from mint.info?.iconUrl + Icon( + imageVector = Icons.Filled.AccountBalance, + contentDescription = "Mint Icon", + tint = Color(0xFF00C851), + modifier = Modifier.size(32.dp) + ) + } + } + + Spacer(modifier = Modifier.height(12.dp)) + + // Mint name from info or fallback to nickname + Text( + text = mint.info?.name?.takeIf { it.isNotEmpty() } ?: mint.nickname, + color = Color.White, + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) + + // Show nickname separately if different from name + if (mint.info?.name?.isNotEmpty() == true && mint.info.name != mint.nickname) { + Text( + text = "\"${mint.nickname}\"", + color = Color.Gray, + fontSize = 14.sp, + fontFamily = FontFamily.Monospace + ) + } + } + } +} + +/** + * Description section showing mint description and long description + */ +@Composable +fun MintDescriptionSection( + mint: Mint, + modifier: Modifier = Modifier +) { + val info = mint.info + if (info?.description.isNullOrEmpty() && info?.descriptionLong.isNullOrEmpty()) { + return + } + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + // Short description + info?.description?.takeIf { it.isNotEmpty() }?.let { description -> + Text( + text = description, + color = Color.White, + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 24.sp, + fontFamily = FontFamily.Monospace + ) + } + + // Long description + info?.descriptionLong?.takeIf { it.isNotEmpty() }?.let { longDescription -> + Text( + text = longDescription, + color = Color.Gray, + fontSize = 14.sp, + lineHeight = 20.sp, + fontFamily = FontFamily.Monospace + ) + } + } +} + +/** + * Contact section showing mint contact information + */ +@Composable +fun MintContactSection( + contacts: List, + modifier: Modifier = Modifier +) { + if (contacts.isEmpty()) return + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + // Section divider and title + SectionDivider(title = "CONTACT") + + // Contact items + contacts.forEach { contact -> + ContactItem(contact = contact) + } + } +} + +/** + * Details section showing mint technical information + */ +@Composable +fun MintDetailsSection( + mint: Mint, + modifier: Modifier = Modifier +) { + var showAllNuts by remember { mutableStateOf(false) } + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + // Section divider and title + SectionDivider(title = "DETAILS") + + // URL + DetailItem( + icon = Icons.Filled.Link, + label = "URL", + value = mint.url, + onClick = { /* Copy URL handled by parent */ } + ) + + // NUTs (Cashu specifications) + mint.info?.nuts?.let { nuts -> + val supportedNuts = nuts.filter { (_, nutInfo) -> + when (nutInfo) { + is Map<*, *> -> nutInfo["supported"] == true || nutInfo["disabled"] != true + else -> true + } + } + + if (supportedNuts.isNotEmpty()) { + DetailItem( + icon = Icons.Filled.Extension, + label = "NUTs", + value = if (showAllNuts) "Hide" else "Show all", + onClick = { showAllNuts = !showAllNuts } + ) + + if (showAllNuts) { + NutsExpandedSection(nuts = supportedNuts) + } + } + } + + // Version + mint.info?.version?.takeIf { it.isNotEmpty() }?.let { version -> + DetailItem( + icon = Icons.Filled.Info, + label = "Version", + value = version + ) + } + + // Date added + DetailItem( + icon = Icons.Filled.CalendarToday, + label = "Added", + value = formatDate(mint.dateAdded) + ) + } +} + +@Composable +private fun ContactItem( + contact: ContactInfo, + modifier: Modifier = Modifier +) { + val clipboardManager = LocalClipboardManager.current + + Row( + modifier = modifier + .fillMaxWidth() + .clickable { + clipboardManager.setText(AnnotatedString(contact.info)) + } + .padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Contact method icon + val icon = when (contact.method.lowercase()) { + "email" -> Icons.Filled.Email + "twitter", "x" -> Icons.Filled.Share + "nostr" -> Icons.Filled.Public + else -> Icons.Filled.ContactMail + } + + Icon( + imageVector = icon, + contentDescription = contact.method, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + + Spacer(modifier = Modifier.width(12.dp)) + + Text( + text = contact.info, + color = Color.White, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace, + modifier = Modifier.weight(1f), + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + + Icon( + imageVector = Icons.Filled.ContentCopy, + contentDescription = "Copy", + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + } +} + +@Composable +private fun DetailItem( + icon: ImageVector, + label: String, + value: String, + onClick: (() -> Unit)? = null, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .fillMaxWidth() + .then( + if (onClick != null) Modifier.clickable { onClick() } + else Modifier + ) + .padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = icon, + contentDescription = label, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + + Spacer(modifier = Modifier.width(12.dp)) + + Text( + text = label, + color = Color.Gray, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace + ) + + Spacer(modifier = Modifier.weight(1f)) + + Text( + text = value, + color = Color.White, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace, + textAlign = androidx.compose.ui.text.style.TextAlign.Right, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.fillMaxWidth(0.6f) + ) + } +} + +@Composable +private fun NutsExpandedSection( + nuts: Map, + modifier: Modifier = Modifier +) { + val nutNames = mapOf( + "7" to "Token state check", + "8" to "Overpaid Lightning fees", + "9" to "Signature restore", + "10" to "Spending conditions", + "11" to "Pay-To-Pubkey (P2PK)", + "12" to "DLEQ proofs", + "13" to "Deterministic secrets", + "14" to "Hashed Timelock Contracts", + "15" to "Partial multi-path payments", + "16" to "Animated QR codes", + "17" to "WebSocket subscriptions", + "18" to "Payment requests", + "19" to "Cached Responses", + "20" to "Signature on Mint Quote", + "21" to "Clear authentication", + "22" to "Blind authentication" + ) + + Column( + modifier = modifier + .fillMaxWidth() + .padding(start = 32.dp), // Indent under the main detail item + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + nuts.filter { (nutNumber, _) -> + nutNumber.toIntOrNull()?.let { it >= 7 } == true // Only show NUT-7 and above + }.forEach { (nutNumber, _) -> + val nutName = nutNames[nutNumber] ?: "Unknown NUT" + + Card( + shape = RoundedCornerShape(4.dp), + colors = CardDefaults.cardColors( + containerColor = Color(0xFF2A2A2A) + ) + ) { + Row( + modifier = Modifier.padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "$nutNumber:", + color = Color.Gray, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = nutName, + color = Color.White, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace + ) + } + } + } + } +} + +@Composable +private fun SectionDivider( + title: String, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .weight(1f) + .height(1.dp) + .background(Color(0xFF333333)) + ) + + Text( + text = title, + color = Color.White, + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.Monospace, + modifier = Modifier.padding(horizontal = 10.dp) + ) + + Box( + modifier = Modifier + .weight(1f) + .height(1.dp) + .background(Color(0xFF333333)) + ) + } +} + +// Utility function +private fun formatDate(date: Date): String { + return SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(date) +} \ No newline at end of file diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintMotdComponent.kt b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintMotdComponent.kt new file mode 100644 index 00000000..75e907e5 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/wallet/ui/mintinfo/MintMotdComponent.kt @@ -0,0 +1,146 @@ +package com.bitchat.android.wallet.ui.mintinfo + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +/** + * Component for displaying mint MOTD (Message of the Day) with dismiss functionality + */ +@Composable +fun MintMotdComponent( + message: String, + isDismissed: Boolean, + onDismiss: () -> Unit, + modifier: Modifier = Modifier +) { + if (message.isEmpty()) return + + if (!isDismissed) { + // Active MOTD card + Card( + modifier = modifier + .fillMaxWidth() + .clickable(enabled = false) { }, // Prevent accidental clicks + shape = RoundedCornerShape(8.dp), + colors = CardDefaults.cardColors( + containerColor = Color.Black + ), + border = BorderStroke(1.dp, Color(0xFFF18408)) // Orange border like in Vue version + ) { + Row( + modifier = Modifier.padding(12.dp), + verticalAlignment = Alignment.Top + ) { + // Info icon + Icon( + imageVector = Icons.Filled.Info, + contentDescription = "MOTD", + tint = Color(0xFFF18408), + modifier = Modifier.size(24.dp) + ) + + Spacer(modifier = Modifier.width(12.dp)) + + // Content + Column( + modifier = Modifier.weight(1f) + ) { + // Header with title and close button + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Message of the Day", + color = Color(0xFFF18408), + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace + ) + + IconButton( + onClick = onDismiss, + modifier = Modifier.size(24.dp) + ) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = "Dismiss", + tint = Color(0xFFF18408), + modifier = Modifier.size(18.dp) + ) + } + } + + Spacer(modifier = Modifier.height(2.dp)) + + // MOTD message + Text( + text = message, + color = Color(0xFFF18408), + fontSize = 14.sp, + lineHeight = 16.sp, + fontFamily = FontFamily.Monospace + ) + } + } + } + } else { + // Dismissed MOTD - minimal display + Card( + modifier = modifier + .fillMaxWidth() + .clickable { /* Could expand back if needed */ }, + shape = RoundedCornerShape(8.dp), + colors = CardDefaults.cardColors( + containerColor = Color(0xFF1A1A1A) + ) + ) { + Row( + modifier = Modifier.padding(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.Info, + contentDescription = "MOTD", + tint = Color.Gray, + modifier = Modifier.size(24.dp) + ) + + Spacer(modifier = Modifier.width(12.dp)) + + Column { + Text( + text = "Message of the Day", + color = Color.Gray, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily.Monospace + ) + + Text( + text = message, + color = Color.Gray, + fontSize = 14.sp, + lineHeight = 16.sp, + fontFamily = FontFamily.Monospace, + maxLines = 2 // Truncate in dismissed state + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bitchat/android/wallet/viewmodel/MintManager.kt b/app/src/main/java/com/bitchat/android/wallet/viewmodel/MintManager.kt index afa199c4..fcdb6c7d 100644 --- a/app/src/main/java/com/bitchat/android/wallet/viewmodel/MintManager.kt +++ b/app/src/main/java/com/bitchat/android/wallet/viewmodel/MintManager.kt @@ -271,6 +271,43 @@ class MintManager( } } + /** + * Delete a mint from the repository + */ + fun deleteMint(mintUrl: String) { + coroutineScope.launch { + try { + val currentMints = _mints.value ?: emptyList() + val updatedMints = currentMints.filter { it.url != mintUrl } + + // Save updated mints list (remove the mint) + repository.clearAllData() + updatedMints.forEach { mint -> + repository.saveMint(mint).onFailure { error -> + Log.e(TAG, "Failed to save mint during deletion: ${mint.url}", error) + } + } + + // If the deleted mint was active, clear active mint + if (_activeMint.value == mintUrl) { + repository.setActiveMint("") + _activeMint.value = null + } + + // Reload mints + repository.getMints().onSuccess { mintList -> + _mints.value = mintList + } + + Log.d(TAG, "Successfully deleted mint: $mintUrl") + + } catch (e: Exception) { + Log.e(TAG, "Failed to delete mint: $mintUrl", e) + _errorMessage.value = "Failed to delete mint: ${e.message}" + } + } + } + /** * Clear error message */ diff --git a/app/src/main/java/com/bitchat/android/wallet/viewmodel/UIStateManager.kt b/app/src/main/java/com/bitchat/android/wallet/viewmodel/UIStateManager.kt index a46c4744..a8354dc5 100644 --- a/app/src/main/java/com/bitchat/android/wallet/viewmodel/UIStateManager.kt +++ b/app/src/main/java/com/bitchat/android/wallet/viewmodel/UIStateManager.kt @@ -24,6 +24,12 @@ class UIStateManager { private val _showAddMintDialog = MutableLiveData(false) val showAddMintDialog: androidx.lifecycle.LiveData = _showAddMintDialog + private val _showMintDetails = MutableLiveData(false) + val showMintDetails: androidx.lifecycle.LiveData = _showMintDetails + + private val _selectedMintUrl = MutableLiveData(null) + val selectedMintUrl: androidx.lifecycle.LiveData = _selectedMintUrl + // Success animation state private val _showSuccessAnimation = MutableLiveData(false) val showSuccessAnimation: androidx.lifecycle.LiveData = _showSuccessAnimation @@ -84,6 +90,16 @@ class UIStateManager { _showAddMintDialog.value = false } + fun showMintDetails(mintUrl: String) { + _selectedMintUrl.value = mintUrl + _showMintDetails.value = true + } + + fun hideMintDetails() { + _showMintDetails.value = false + _selectedMintUrl.value = null + } + // Animation Management fun showSuccessAnimation(animationData: WalletViewModel.SuccessAnimationData) { 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 e71ce78c..636cd7a2 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 @@ -58,6 +58,8 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) val sendType: LiveData = uiStateManager.sendType val receiveType: LiveData = uiStateManager.receiveType val showAddMintDialog: LiveData = uiStateManager.showAddMintDialog + val showMintDetails: LiveData = uiStateManager.showMintDetails + val selectedMintUrl: LiveData = uiStateManager.selectedMintUrl val showSuccessAnimation: LiveData = uiStateManager.showSuccessAnimation val successAnimationData: LiveData = uiStateManager.successAnimationData val showFailureAnimation: LiveData = uiStateManager.showFailureAnimation @@ -182,6 +184,20 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) fun clearError() = uiStateManager.clearError() + // Mint details navigation + fun showMintDetails(mintUrl: String) = uiStateManager.showMintDetails(mintUrl) + + fun hideMintDetails() = uiStateManager.hideMintDetails() + + // Copy to clipboard functionality + fun copyToClipboard(text: String) { + // This will be handled by the UI components directly using LocalClipboardManager + // We can add a success message here if needed + } + + // Delete mint functionality + fun deleteMint(mintUrl: String) = mintManager.deleteMint(mintUrl) + // Token input management - Delegate to TokenManager fun setTokenInput(token: String) = tokenManager.setTokenInput(token)