From 9b7093b8f06e3c938a8eac3647563c46ffb52a8f Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 15 Jul 2025 18:14:51 +0200 Subject: [PATCH] loading state in ui store --- .../android/wallet/ui/ReceiveEcashDialog.kt | 50 ++++++++++++++----- .../android/wallet/ui/SendEcashDialog.kt | 40 +++++++++++---- .../wallet/viewmodel/LightningManager.kt | 18 +++---- .../android/wallet/viewmodel/MintManager.kt | 19 +++---- .../android/wallet/viewmodel/TokenManager.kt | 18 +++---- .../wallet/viewmodel/WalletViewModel.kt | 8 +-- 6 files changed, 98 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveEcashDialog.kt b/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveEcashDialog.kt index d2238a95..047f9a98 100644 --- a/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveEcashDialog.kt +++ b/app/src/main/java/com/bitchat/android/wallet/ui/ReceiveEcashDialog.kt @@ -119,16 +119,30 @@ fun ReceiveEcashDialog( .fillMaxWidth() .height(56.dp), colors = ButtonDefaults.buttonColors( - containerColor = Color(0xFF00C851), + containerColor = if (isLoading) Color(0xFF2A2A2A) else Color(0xFF00C851), disabledContainerColor = Color(0xFF2A2A2A) ), shape = RoundedCornerShape(16.dp) ) { if (isLoading) { - CircularProgressIndicator( - color = Color.Black, - modifier = Modifier.size(24.dp) - ) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + CircularProgressIndicator( + color = Color.White, + modifier = Modifier.size(20.dp), + strokeWidth = 2.dp + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "RECEIVING TOKEN...", + color = Color.White, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + } } else { Row( verticalAlignment = Alignment.CenterVertically @@ -161,8 +175,11 @@ fun ReceiveEcashDialog( OutlinedTextField( value = token, onValueChange = { - viewModel.setTokenInput(it) + if (!isLoading) { + viewModel.setTokenInput(it) + } }, + enabled = !isLoading, label = { Text( text = "Cashu Token", @@ -182,7 +199,10 @@ fun ReceiveEcashDialog( unfocusedBorderColor = Color(0xFF2A2A2A), unfocusedLabelColor = Color.Gray, unfocusedTextColor = Color.White, - focusedTextColor = Color.White + focusedTextColor = Color.White, + disabledBorderColor = Color.Gray, + disabledLabelColor = Color.Gray, + disabledTextColor = Color.Gray ), modifier = Modifier .fillMaxWidth() @@ -193,13 +213,16 @@ fun ReceiveEcashDialog( trailingIcon = { IconButton( onClick = { - // TODO: Implement QR code scanning - } + if (!isLoading) { + // TODO: Implement QR code scanning + } + }, + enabled = !isLoading ) { Icon( imageVector = Icons.Filled.QrCode, contentDescription = "Scan QR", - tint = Color(0xFF00C851) + tint = if (isLoading) Color.Gray else Color(0xFF00C851) ) } } @@ -214,6 +237,7 @@ fun ReceiveEcashDialog( } } }, + enabled = !isLoading, modifier = Modifier .fillMaxWidth() .height(56.dp), @@ -222,7 +246,7 @@ fun ReceiveEcashDialog( ), border = BorderStroke( 2.dp, - Color(0xFF00C851) + if (isLoading) Color.Gray else Color(0xFF00C851) ), shape = RoundedCornerShape(16.dp) ) { @@ -232,13 +256,13 @@ fun ReceiveEcashDialog( Icon( imageVector = Icons.Filled.ContentPaste, contentDescription = "Paste", - tint = Color(0xFF00C851), + tint = if (isLoading) Color.Gray else Color(0xFF00C851), modifier = Modifier.size(24.dp) ) Spacer(modifier = Modifier.width(12.dp)) Text( text = "PASTE FROM CLIPBOARD", - color = Color(0xFF00C851), + color = if (isLoading) Color.Gray else Color(0xFF00C851), fontSize = 16.sp, fontWeight = FontWeight.Bold, fontFamily = FontFamily.Monospace, diff --git a/app/src/main/java/com/bitchat/android/wallet/ui/SendEcashDialog.kt b/app/src/main/java/com/bitchat/android/wallet/ui/SendEcashDialog.kt index 1295c5a4..7300151d 100644 --- a/app/src/main/java/com/bitchat/android/wallet/ui/SendEcashDialog.kt +++ b/app/src/main/java/com/bitchat/android/wallet/ui/SendEcashDialog.kt @@ -137,7 +137,8 @@ fun SendEcashDialog( // Amount input OutlinedTextField( value = amount, - onValueChange = { amount = it }, + onValueChange = { if (!isLoading) amount = it }, + enabled = !isLoading, label = { Text( text = "Amount (sats)", @@ -151,7 +152,10 @@ fun SendEcashDialog( unfocusedBorderColor = Color(0xFF2A2A2A), unfocusedLabelColor = Color.Gray, unfocusedTextColor = Color.White, - focusedTextColor = Color.White + focusedTextColor = Color.White, + disabledBorderColor = Color.Gray, + disabledLabelColor = Color.Gray, + disabledTextColor = Color.Gray ), modifier = Modifier .fillMaxWidth() @@ -162,7 +166,8 @@ fun SendEcashDialog( // Memo input OutlinedTextField( value = memo, - onValueChange = { memo = it }, + onValueChange = { if (!isLoading) memo = it }, + enabled = !isLoading, label = { Text( text = "Memo (optional)", @@ -175,7 +180,10 @@ fun SendEcashDialog( unfocusedBorderColor = Color(0xFF2A2A2A), unfocusedLabelColor = Color.Gray, unfocusedTextColor = Color.White, - focusedTextColor = Color.White + focusedTextColor = Color.White, + disabledBorderColor = Color.Gray, + disabledLabelColor = Color.Gray, + disabledTextColor = Color.Gray ), modifier = Modifier .fillMaxWidth() @@ -195,16 +203,30 @@ fun SendEcashDialog( .fillMaxWidth() .height(56.dp), colors = ButtonDefaults.buttonColors( - containerColor = Color(0xFF00C851), + containerColor = if (isLoading) Color(0xFF2A2A2A) else Color(0xFF00C851), disabledContainerColor = Color(0xFF2A2A2A) ), shape = RoundedCornerShape(16.dp) ) { if (isLoading) { - CircularProgressIndicator( - color = Color.Black, - modifier = Modifier.size(24.dp) - ) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + CircularProgressIndicator( + color = Color.White, + modifier = Modifier.size(20.dp), + strokeWidth = 2.dp + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "CREATING TOKEN...", + color = Color.White, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + letterSpacing = 1.sp + ) + } } else { Row( verticalAlignment = Alignment.CenterVertically diff --git a/app/src/main/java/com/bitchat/android/wallet/viewmodel/LightningManager.kt b/app/src/main/java/com/bitchat/android/wallet/viewmodel/LightningManager.kt index a9f56a88..60b7d401 100644 --- a/app/src/main/java/com/bitchat/android/wallet/viewmodel/LightningManager.kt +++ b/app/src/main/java/com/bitchat/android/wallet/viewmodel/LightningManager.kt @@ -15,7 +15,8 @@ import java.util.* class LightningManager( private val repository: WalletRepository, private val cashuService: CashuService, - private val coroutineScope: CoroutineScope + private val coroutineScope: CoroutineScope, + private val uiStateManager: UIStateManager ) { companion object { private const val TAG = "LightningManager" @@ -34,9 +35,6 @@ class LightningManager( private val _pendingMeltQuotes = MutableLiveData>(emptyList()) val pendingMeltQuotes: androidx.lifecycle.LiveData> = _pendingMeltQuotes - private val _isLoading = MutableLiveData(false) - val isLoading: androidx.lifecycle.LiveData = _isLoading - private val _errorMessage = MutableLiveData(null) val errorMessage: androidx.lifecycle.LiveData = _errorMessage @@ -46,7 +44,7 @@ class LightningManager( fun createMintQuote(amount: Long, description: String? = null) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) cashuService.createMintQuote(amount, description).onSuccess { quote -> _currentMintQuote.value = quote @@ -61,7 +59,7 @@ class LightningManager( _errorMessage.value = "Failed to create invoice: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } @@ -72,7 +70,7 @@ class LightningManager( fun createMeltQuote(invoice: String, onQuoteCreated: () -> Unit = {}) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) cashuService.createMeltQuote(invoice).onSuccess { quote -> _currentMeltQuote.value = quote @@ -88,7 +86,7 @@ class LightningManager( _errorMessage.value = "Failed to process invoice: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } @@ -104,7 +102,7 @@ class LightningManager( ) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) cashuService.payInvoice(quoteId).onSuccess { success -> if (success) { // Update quote status and add transaction @@ -143,7 +141,7 @@ class LightningManager( _errorMessage.value = "Payment failed: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } 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 99a54bc4..afa199c4 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 @@ -16,7 +16,8 @@ import java.util.* class MintManager( private val repository: WalletRepository, private val cashuService: CashuService, - private val coroutineScope: CoroutineScope + private val coroutineScope: CoroutineScope, + private val uiStateManager: UIStateManager ) { companion object { private const val TAG = "MintManager" @@ -120,7 +121,7 @@ class MintManager( */ private suspend fun initializeWalletWithMint(mintUrl: String, onWalletInitialized: () -> Unit) { try { - _isLoading.value = true + uiStateManager.setLoading(true) cashuService.initializeWallet(mintUrl).onSuccess { onWalletInitialized() Log.d(TAG, "Wallet initialized with mint: $mintUrl") @@ -129,7 +130,7 @@ class MintManager( _errorMessage.value = "Failed to connect to mint: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } @@ -154,7 +155,7 @@ class MintManager( fun addMint(mintUrl: String, nickname: String, onSuccess: () -> Unit = {}) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) cashuService.getMintInfo(mintUrl).onSuccess { mintInfo -> val mint = Mint( url = mintUrl, @@ -184,7 +185,7 @@ class MintManager( _errorMessage.value = "Failed to connect to mint: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } @@ -195,7 +196,7 @@ class MintManager( fun setActiveMint(mintUrl: String, onWalletInitialized: () -> Unit = {}) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) repository.setActiveMint(mintUrl).onSuccess { _activeMint.value = mintUrl initializeWalletWithMint(mintUrl, onWalletInitialized) @@ -203,7 +204,7 @@ class MintManager( _errorMessage.value = "Failed to set active mint: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } @@ -235,7 +236,7 @@ class MintManager( fun syncAllMints() { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) val currentMints = _mints.value ?: emptyList() for (mint in currentMints) { @@ -265,7 +266,7 @@ class MintManager( Log.e(TAG, "Error syncing mints", e) _errorMessage.value = "Failed to sync mints: ${e.message}" } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } diff --git a/app/src/main/java/com/bitchat/android/wallet/viewmodel/TokenManager.kt b/app/src/main/java/com/bitchat/android/wallet/viewmodel/TokenManager.kt index 2b8b9ae0..a4c4918b 100644 --- a/app/src/main/java/com/bitchat/android/wallet/viewmodel/TokenManager.kt +++ b/app/src/main/java/com/bitchat/android/wallet/viewmodel/TokenManager.kt @@ -21,7 +21,8 @@ import java.util.* class TokenManager( private val repository: WalletRepository, private val cashuService: CashuService, - private val coroutineScope: CoroutineScope + private val coroutineScope: CoroutineScope, + private val uiStateManager: UIStateManager ) { companion object { private const val TAG = "TokenManager" @@ -37,9 +38,6 @@ class TokenManager( private val _tokenInput = MutableLiveData("") val tokenInput: androidx.lifecycle.LiveData = _tokenInput - private val _isLoading = MutableLiveData(false) - val isLoading: androidx.lifecycle.LiveData = _isLoading - private val _errorMessage = MutableLiveData(null) val errorMessage: androidx.lifecycle.LiveData = _errorMessage @@ -54,7 +52,7 @@ class TokenManager( ) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) cashuService.createToken(amount, memo).onSuccess { token -> _generatedToken.value = token @@ -80,7 +78,7 @@ class TokenManager( _errorMessage.value = "Failed to create token: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } @@ -141,14 +139,14 @@ class TokenManager( fun decodeCashuToken(token: String) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) cashuService.decodeToken(token).onSuccess { decodedToken -> _decodedToken.value = decodedToken }.onFailure { error -> _errorMessage.value = "Invalid token: ${error.message}" } } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } @@ -167,7 +165,7 @@ class TokenManager( ) { coroutineScope.launch { try { - _isLoading.value = true + uiStateManager.setLoading(true) // Get decoded token val decodedToken = _decodedToken.value @@ -257,7 +255,7 @@ class TokenManager( ) onFailure(failureData) } finally { - _isLoading.value = false + uiStateManager.setLoading(false) } } } 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 2004e6a5..690b0df3 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 @@ -24,11 +24,11 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) private val cashuService = CashuService.getInstance() // Manager instances - private val mintManager = MintManager(repository, cashuService, viewModelScope) - private val tokenManager = TokenManager(repository, cashuService, viewModelScope) - private val lightningManager = LightningManager(repository, cashuService, viewModelScope) - private val transactionManager = TransactionManager(repository, viewModelScope) private val uiStateManager = UIStateManager() + private val mintManager = MintManager(repository, cashuService, viewModelScope, uiStateManager) + private val tokenManager = TokenManager(repository, cashuService, viewModelScope, uiStateManager) + private val lightningManager = LightningManager(repository, cashuService, viewModelScope, uiStateManager) + private val transactionManager = TransactionManager(repository, viewModelScope) companion object { private const val TAG = "WalletViewModel"