mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 13:05:19 +00:00
nice
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"artifactType": {
|
||||||
|
"type": "APK",
|
||||||
|
"kind": "Directory"
|
||||||
|
},
|
||||||
|
"applicationId": "com.bitchat.android",
|
||||||
|
"variantName": "release",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "SINGLE",
|
||||||
|
"filters": [],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 3,
|
||||||
|
"versionName": "0.7",
|
||||||
|
"outputFile": "app-release.apk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"elementType": "File",
|
||||||
|
"baselineProfiles": [
|
||||||
|
{
|
||||||
|
"minApi": 28,
|
||||||
|
"maxApi": 30,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/1/app-release.dm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minApi": 31,
|
||||||
|
"maxApi": 2147483647,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/0/app-release.dm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minSdkVersionForDexing": 26
|
||||||
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
package com.bitchat.android.wallet.viewmodel
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.bitchat.android.wallet.data.*
|
||||||
|
import com.bitchat.android.wallet.repository.WalletRepository
|
||||||
|
import com.bitchat.android.wallet.service.CashuService
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all lightning-related operations for the wallet
|
||||||
|
*/
|
||||||
|
class LightningManager(
|
||||||
|
private val repository: WalletRepository,
|
||||||
|
private val cashuService: CashuService,
|
||||||
|
private val coroutineScope: CoroutineScope
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "LightningManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observable data
|
||||||
|
private val _currentMintQuote = MutableLiveData<MintQuote?>(null)
|
||||||
|
val currentMintQuote: androidx.lifecycle.LiveData<MintQuote?> = _currentMintQuote
|
||||||
|
|
||||||
|
private val _currentMeltQuote = MutableLiveData<MeltQuote?>(null)
|
||||||
|
val currentMeltQuote: androidx.lifecycle.LiveData<MeltQuote?> = _currentMeltQuote
|
||||||
|
|
||||||
|
private val _pendingMintQuotes = MutableLiveData<List<MintQuote>>(emptyList())
|
||||||
|
val pendingMintQuotes: androidx.lifecycle.LiveData<List<MintQuote>> = _pendingMintQuotes
|
||||||
|
|
||||||
|
private val _pendingMeltQuotes = MutableLiveData<List<MeltQuote>>(emptyList())
|
||||||
|
val pendingMeltQuotes: androidx.lifecycle.LiveData<List<MeltQuote>> = _pendingMeltQuotes
|
||||||
|
|
||||||
|
private val _isLoading = MutableLiveData<Boolean>(false)
|
||||||
|
val isLoading: androidx.lifecycle.LiveData<Boolean> = _isLoading
|
||||||
|
|
||||||
|
private val _errorMessage = MutableLiveData<String?>(null)
|
||||||
|
val errorMessage: androidx.lifecycle.LiveData<String?> = _errorMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Lightning mint quote (for receiving)
|
||||||
|
*/
|
||||||
|
fun createMintQuote(amount: Long, description: String? = null) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
cashuService.createMintQuote(amount, description).onSuccess { quote ->
|
||||||
|
_currentMintQuote.value = quote
|
||||||
|
|
||||||
|
// Save quote for tracking
|
||||||
|
repository.saveMintQuote(quote).onSuccess {
|
||||||
|
loadPendingQuotes()
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save mint quote", error)
|
||||||
|
_errorMessage.value = "Failed to save invoice: ${error.message}"
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Failed to create invoice: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Lightning melt quote (for sending)
|
||||||
|
*/
|
||||||
|
fun createMeltQuote(invoice: String, onQuoteCreated: () -> Unit = {}) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
cashuService.createMeltQuote(invoice).onSuccess { quote ->
|
||||||
|
_currentMeltQuote.value = quote
|
||||||
|
|
||||||
|
// Save quote for tracking
|
||||||
|
repository.saveMeltQuote(quote).onSuccess {
|
||||||
|
loadPendingQuotes()
|
||||||
|
onQuoteCreated()
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save melt quote", error)
|
||||||
|
_errorMessage.value = "Failed to save quote: ${error.message}"
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Failed to process invoice: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pay Lightning invoice (melt)
|
||||||
|
*/
|
||||||
|
fun payLightningInvoice(
|
||||||
|
quoteId: String,
|
||||||
|
onTransactionSaved: () -> Unit,
|
||||||
|
onBalanceRefresh: () -> Unit,
|
||||||
|
onPaymentComplete: () -> Unit
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
cashuService.payInvoice(quoteId).onSuccess { success ->
|
||||||
|
if (success) {
|
||||||
|
// Update quote status and add transaction
|
||||||
|
val quote = _currentMeltQuote.value
|
||||||
|
if (quote != null) {
|
||||||
|
val updatedQuote = quote.copy(paid = true, state = MeltQuoteState.PAID)
|
||||||
|
repository.saveMeltQuote(updatedQuote).onSuccess {
|
||||||
|
val transaction = WalletTransaction(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
type = TransactionType.LIGHTNING_SEND,
|
||||||
|
amount = quote.amount,
|
||||||
|
unit = quote.unit,
|
||||||
|
status = TransactionStatus.CONFIRMED,
|
||||||
|
timestamp = Date(),
|
||||||
|
description = "Lightning payment sent",
|
||||||
|
quote = quote.id,
|
||||||
|
fee = quote.feeReserve
|
||||||
|
)
|
||||||
|
repository.saveTransaction(transaction).onSuccess {
|
||||||
|
onTransactionSaved()
|
||||||
|
onBalanceRefresh()
|
||||||
|
onPaymentComplete()
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save transaction", error)
|
||||||
|
_errorMessage.value = "Failed to save transaction: ${error.message}"
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save melt quote", error)
|
||||||
|
_errorMessage.value = "Failed to save quote: ${error.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_errorMessage.value = "Payment failed"
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Payment failed: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check pending quotes for updates
|
||||||
|
*/
|
||||||
|
suspend fun checkPendingQuotes(
|
||||||
|
onTransactionSaved: () -> Unit,
|
||||||
|
onBalanceRefresh: () -> Unit
|
||||||
|
) {
|
||||||
|
// Check mint quotes
|
||||||
|
repository.getMintQuotes().onSuccess { quotes ->
|
||||||
|
val unpaidQuotes = quotes.filter { !it.paid }
|
||||||
|
for (quote in unpaidQuotes) {
|
||||||
|
cashuService.checkAndMintQuote(quote.id).onSuccess { success ->
|
||||||
|
if (success) {
|
||||||
|
// Update quote status and add transaction
|
||||||
|
val updatedQuote = quote.copy(paid = true, state = MintQuoteState.PAID)
|
||||||
|
repository.saveMintQuote(updatedQuote).onSuccess {
|
||||||
|
// 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,
|
||||||
|
amount = quote.amount,
|
||||||
|
unit = quote.unit,
|
||||||
|
status = TransactionStatus.CONFIRMED,
|
||||||
|
timestamp = Date(),
|
||||||
|
description = "Lightning payment received",
|
||||||
|
quote = quote.id
|
||||||
|
)
|
||||||
|
repository.saveTransaction(transaction).onSuccess {
|
||||||
|
onTransactionSaved()
|
||||||
|
onBalanceRefresh()
|
||||||
|
|
||||||
|
Log.d(TAG, "Mint quote ${quote.id} was paid and minted")
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save transaction for mint quote ${quote.id}", error)
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save updated mint quote ${quote.id}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_pendingMintQuotes.value = unpaidQuotes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Melt quotes typically don't need polling as they're immediately processed
|
||||||
|
repository.getMeltQuotes().onSuccess { quotes ->
|
||||||
|
_pendingMeltQuotes.value = quotes.filter { it.state != MeltQuoteState.PAID }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load pending quotes from repository
|
||||||
|
*/
|
||||||
|
fun loadPendingQuotes() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
repository.getMintQuotes().onSuccess { quotes ->
|
||||||
|
_pendingMintQuotes.value = quotes.filter { !it.paid }
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.getMeltQuotes().onSuccess { quotes ->
|
||||||
|
_pendingMeltQuotes.value = quotes.filter { it.state != MeltQuoteState.PAID }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a specific mint quote as current (for reopening from transaction list)
|
||||||
|
*/
|
||||||
|
fun setCurrentMintQuote(quoteId: String, onQuoteFound: () -> Unit) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
repository.getMintQuotes().onSuccess { quotes ->
|
||||||
|
val quote = quotes.find { it.id == quoteId }
|
||||||
|
if (quote != null) {
|
||||||
|
_currentMintQuote.value = quote
|
||||||
|
onQuoteFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear current mint quote
|
||||||
|
*/
|
||||||
|
fun clearCurrentMintQuote() {
|
||||||
|
_currentMintQuote.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear current melt quote
|
||||||
|
*/
|
||||||
|
fun clearCurrentMeltQuote() {
|
||||||
|
_currentMeltQuote.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear error message
|
||||||
|
*/
|
||||||
|
fun clearError() {
|
||||||
|
_errorMessage.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current mint quote
|
||||||
|
*/
|
||||||
|
fun getCurrentMintQuote(): MintQuote? = _currentMintQuote.value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current melt quote
|
||||||
|
*/
|
||||||
|
fun getCurrentMeltQuote(): MeltQuote? = _currentMeltQuote.value
|
||||||
|
}
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
package com.bitchat.android.wallet.viewmodel
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.bitchat.android.wallet.data.Mint
|
||||||
|
import com.bitchat.android.wallet.repository.WalletRepository
|
||||||
|
import com.bitchat.android.wallet.service.CashuService
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all mint-related operations for the wallet
|
||||||
|
*/
|
||||||
|
class MintManager(
|
||||||
|
private val repository: WalletRepository,
|
||||||
|
private val cashuService: CashuService,
|
||||||
|
private val coroutineScope: CoroutineScope
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "MintManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observable data
|
||||||
|
private val _mints = MutableLiveData<List<Mint>>(emptyList())
|
||||||
|
val mints: androidx.lifecycle.LiveData<List<Mint>> = _mints
|
||||||
|
|
||||||
|
private val _activeMint = MutableLiveData<String?>(null)
|
||||||
|
val activeMint: androidx.lifecycle.LiveData<String?> = _activeMint
|
||||||
|
|
||||||
|
private val _isLoading = MutableLiveData<Boolean>(false)
|
||||||
|
val isLoading: androidx.lifecycle.LiveData<Boolean> = _isLoading
|
||||||
|
|
||||||
|
private val _errorMessage = MutableLiveData<String?>(null)
|
||||||
|
val errorMessage: androidx.lifecycle.LiveData<String?> = _errorMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize with default mint if no mints are configured
|
||||||
|
*/
|
||||||
|
fun initializeDefaultWallet(onWalletInitialized: () -> Unit) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
// Check if we have mints configured
|
||||||
|
repository.getMints().onSuccess { mintList ->
|
||||||
|
if (mintList.isEmpty()) {
|
||||||
|
Log.d(TAG, "No mints found, initializing with default mint")
|
||||||
|
// Add default mint
|
||||||
|
addDefaultMint(onWalletInitialized)
|
||||||
|
} else {
|
||||||
|
_mints.value = mintList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have an active mint
|
||||||
|
repository.getActiveMint().onSuccess { activeMintUrl ->
|
||||||
|
if (activeMintUrl.isNullOrEmpty()) {
|
||||||
|
Log.d(TAG, "No active mint, setting default")
|
||||||
|
// Use default mint URL from CashuService
|
||||||
|
cashuService.initializeWallet("https://testnut.cashu.space").onSuccess {
|
||||||
|
onWalletInitialized()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_activeMint.value = activeMintUrl
|
||||||
|
initializeWalletWithMint(activeMintUrl, onWalletInitialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error initializing default wallet", e)
|
||||||
|
_errorMessage.value = "Failed to initialize wallet: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add default mint for testing
|
||||||
|
*/
|
||||||
|
private fun addDefaultMint(onWalletInitialized: () -> Unit) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val defaultMintUrl = "https://mint.minibits.cash/Bitcoin"
|
||||||
|
val defaultMintNickname = "Minibits"
|
||||||
|
|
||||||
|
try {
|
||||||
|
cashuService.getMintInfo(defaultMintUrl).onSuccess { mintInfo ->
|
||||||
|
val mint = Mint(
|
||||||
|
url = defaultMintUrl,
|
||||||
|
nickname = defaultMintNickname,
|
||||||
|
info = mintInfo,
|
||||||
|
keysets = emptyList(),
|
||||||
|
active = true,
|
||||||
|
dateAdded = Date()
|
||||||
|
)
|
||||||
|
|
||||||
|
repository.saveMint(mint).onSuccess {
|
||||||
|
repository.setActiveMint(defaultMintUrl)
|
||||||
|
_activeMint.value = defaultMintUrl
|
||||||
|
repository.getMints().onSuccess { mintList ->
|
||||||
|
_mints.value = mintList
|
||||||
|
}
|
||||||
|
// Initialize wallet with this mint
|
||||||
|
initializeWalletWithMint(defaultMintUrl, onWalletInitialized)
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to add default mint", error)
|
||||||
|
// Fallback - still initialize wallet even if mint info fails
|
||||||
|
cashuService.initializeWallet(defaultMintUrl).onSuccess {
|
||||||
|
_activeMint.value = defaultMintUrl
|
||||||
|
onWalletInitialized()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Exception adding default mint", e)
|
||||||
|
_errorMessage.value = "Failed to add default mint: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize wallet with specific mint
|
||||||
|
*/
|
||||||
|
private suspend fun initializeWalletWithMint(mintUrl: String, onWalletInitialized: () -> Unit) {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
cashuService.initializeWallet(mintUrl).onSuccess {
|
||||||
|
onWalletInitialized()
|
||||||
|
Log.d(TAG, "Wallet initialized with mint: $mintUrl")
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to initialize wallet with mint: $mintUrl", error)
|
||||||
|
_errorMessage.value = "Failed to connect to mint: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load mints from repository
|
||||||
|
*/
|
||||||
|
fun loadMints() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
repository.getMints().onSuccess { mintList ->
|
||||||
|
_mints.value = mintList
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.getActiveMint().onSuccess { mintUrl ->
|
||||||
|
_activeMint.value = mintUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new mint
|
||||||
|
*/
|
||||||
|
fun addMint(mintUrl: String, nickname: String, onSuccess: () -> Unit = {}) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
cashuService.getMintInfo(mintUrl).onSuccess { mintInfo ->
|
||||||
|
val mint = Mint(
|
||||||
|
url = mintUrl,
|
||||||
|
nickname = nickname.ifEmpty { mintInfo.name },
|
||||||
|
info = mintInfo,
|
||||||
|
keysets = emptyList(), // Will be populated by CDK
|
||||||
|
active = true,
|
||||||
|
dateAdded = Date()
|
||||||
|
)
|
||||||
|
|
||||||
|
repository.saveMint(mint).onSuccess {
|
||||||
|
// Reload mints
|
||||||
|
repository.getMints().onSuccess { mintList ->
|
||||||
|
_mints.value = mintList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set as active mint if it's the first one
|
||||||
|
if (_activeMint.value.isNullOrEmpty()) {
|
||||||
|
setActiveMint(mintUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccess()
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Failed to save mint: ${error.message}"
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Failed to connect to mint: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the active mint
|
||||||
|
*/
|
||||||
|
fun setActiveMint(mintUrl: String, onWalletInitialized: () -> Unit = {}) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
repository.setActiveMint(mintUrl).onSuccess {
|
||||||
|
_activeMint.value = mintUrl
|
||||||
|
initializeWalletWithMint(mintUrl, onWalletInitialized)
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Failed to set active mint: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update mint nickname
|
||||||
|
*/
|
||||||
|
fun updateMintNickname(mintUrl: String, newNickname: String) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val currentMints = _mints.value ?: emptyList()
|
||||||
|
val mintToUpdate = currentMints.find { it.url == mintUrl }
|
||||||
|
if (mintToUpdate != null) {
|
||||||
|
val updatedMint = mintToUpdate.copy(nickname = newNickname)
|
||||||
|
repository.saveMint(updatedMint).onSuccess {
|
||||||
|
// Reload mints
|
||||||
|
repository.getMints().onSuccess { mintList ->
|
||||||
|
_mints.value = mintList
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Failed to update mint nickname: ${error.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync all mints - refresh mint information and keysets
|
||||||
|
*/
|
||||||
|
fun syncAllMints() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
val currentMints = _mints.value ?: emptyList()
|
||||||
|
|
||||||
|
for (mint in currentMints) {
|
||||||
|
try {
|
||||||
|
cashuService.getMintInfo(mint.url).onSuccess { mintInfo ->
|
||||||
|
val updatedMint = mint.copy(
|
||||||
|
info = mintInfo,
|
||||||
|
lastSync = Date()
|
||||||
|
)
|
||||||
|
repository.saveMint(updatedMint).onSuccess {
|
||||||
|
// Mint updated successfully
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save updated mint ${mint.url}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error syncing mint ${mint.url}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload mints list
|
||||||
|
repository.getMints().onSuccess { mintList ->
|
||||||
|
_mints.value = mintList
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error syncing mints", e)
|
||||||
|
_errorMessage.value = "Failed to sync mints: ${e.message}"
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear error message
|
||||||
|
*/
|
||||||
|
fun clearError() {
|
||||||
|
_errorMessage.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current active mint URL
|
||||||
|
*/
|
||||||
|
fun getCurrentActiveMint(): String? = _activeMint.value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current mints list
|
||||||
|
*/
|
||||||
|
fun getCurrentMints(): List<Mint> = _mints.value ?: emptyList()
|
||||||
|
}
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
package com.bitchat.android.wallet.viewmodel
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.bitchat.android.wallet.data.CashuToken
|
||||||
|
import com.bitchat.android.wallet.data.Mint
|
||||||
|
import com.bitchat.android.wallet.data.WalletTransaction
|
||||||
|
import com.bitchat.android.wallet.data.TransactionType
|
||||||
|
import com.bitchat.android.wallet.data.TransactionStatus
|
||||||
|
import com.bitchat.android.wallet.repository.WalletRepository
|
||||||
|
import com.bitchat.android.wallet.service.CashuService
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all token-related operations for the wallet
|
||||||
|
*/
|
||||||
|
class TokenManager(
|
||||||
|
private val repository: WalletRepository,
|
||||||
|
private val cashuService: CashuService,
|
||||||
|
private val coroutineScope: CoroutineScope
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TokenManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observable data
|
||||||
|
private val _generatedToken = MutableLiveData<String?>(null)
|
||||||
|
val generatedToken: androidx.lifecycle.LiveData<String?> = _generatedToken
|
||||||
|
|
||||||
|
private val _decodedToken = MutableLiveData<CashuToken?>(null)
|
||||||
|
val decodedToken: androidx.lifecycle.LiveData<CashuToken?> = _decodedToken
|
||||||
|
|
||||||
|
private val _tokenInput = MutableLiveData<String>("")
|
||||||
|
val tokenInput: androidx.lifecycle.LiveData<String> = _tokenInput
|
||||||
|
|
||||||
|
private val _isLoading = MutableLiveData<Boolean>(false)
|
||||||
|
val isLoading: androidx.lifecycle.LiveData<Boolean> = _isLoading
|
||||||
|
|
||||||
|
private val _errorMessage = MutableLiveData<String?>(null)
|
||||||
|
val errorMessage: androidx.lifecycle.LiveData<String?> = _errorMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Cashu token
|
||||||
|
*/
|
||||||
|
fun createCashuToken(
|
||||||
|
amount: Long,
|
||||||
|
memo: String? = null,
|
||||||
|
onTransactionSaved: () -> Unit,
|
||||||
|
onBalanceRefresh: () -> Unit
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
cashuService.createToken(amount, memo).onSuccess { token ->
|
||||||
|
_generatedToken.value = token
|
||||||
|
|
||||||
|
// Add transaction
|
||||||
|
val transaction = WalletTransaction(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
type = TransactionType.CASHU_SEND,
|
||||||
|
amount = BigDecimal(amount),
|
||||||
|
unit = "sat",
|
||||||
|
status = TransactionStatus.CONFIRMED,
|
||||||
|
timestamp = Date(),
|
||||||
|
description = memo ?: "Cashu token sent",
|
||||||
|
token = token
|
||||||
|
)
|
||||||
|
repository.saveTransaction(transaction).onSuccess {
|
||||||
|
onTransactionSaved()
|
||||||
|
onBalanceRefresh()
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save transaction", error)
|
||||||
|
_errorMessage.value = "Failed to save transaction: ${error.message}"
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Failed to create token: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Cashu token for payments (with callbacks for /pay command)
|
||||||
|
*/
|
||||||
|
fun createCashuTokenForPayment(
|
||||||
|
amount: Long,
|
||||||
|
memo: String? = null,
|
||||||
|
onSuccess: (String) -> Unit,
|
||||||
|
onError: (String) -> Unit,
|
||||||
|
onTransactionSaved: () -> Unit,
|
||||||
|
onBalanceRefresh: () -> Unit
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
cashuService.createToken(amount, memo).onSuccess { token ->
|
||||||
|
|
||||||
|
// Add transaction
|
||||||
|
val transaction = WalletTransaction(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
type = TransactionType.CASHU_SEND,
|
||||||
|
amount = BigDecimal(amount),
|
||||||
|
unit = "sat",
|
||||||
|
status = TransactionStatus.CONFIRMED,
|
||||||
|
timestamp = Date(),
|
||||||
|
description = memo ?: "Cashu token sent",
|
||||||
|
token = token
|
||||||
|
)
|
||||||
|
|
||||||
|
repository.saveTransaction(transaction).onSuccess {
|
||||||
|
onTransactionSaved()
|
||||||
|
onBalanceRefresh()
|
||||||
|
|
||||||
|
// Success callback with token
|
||||||
|
onSuccess(token)
|
||||||
|
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save transaction", error)
|
||||||
|
onError("Failed to save transaction: ${error.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
}.onFailure { error ->
|
||||||
|
onError("Failed to create token: ${error.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Exception creating payment token", e)
|
||||||
|
onError(e.message ?: "Unknown error occurred")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a Cashu token to show information
|
||||||
|
*/
|
||||||
|
fun decodeCashuToken(token: String) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
cashuService.decodeToken(token).onSuccess { decodedToken ->
|
||||||
|
_decodedToken.value = decodedToken
|
||||||
|
}.onFailure { error ->
|
||||||
|
_errorMessage.value = "Invalid token: ${error.message}"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a Cashu token
|
||||||
|
*/
|
||||||
|
fun receiveCashuToken(
|
||||||
|
token: String,
|
||||||
|
currentMints: List<Mint>,
|
||||||
|
onSuccess: (WalletViewModel.SuccessAnimationData) -> Unit,
|
||||||
|
onFailure: (WalletViewModel.FailureAnimationData) -> Unit,
|
||||||
|
onTransactionSaved: () -> Unit,
|
||||||
|
onBalanceRefresh: () -> Unit,
|
||||||
|
onMintsUpdated: () -> Unit
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
|
||||||
|
// Get decoded token
|
||||||
|
val decodedToken = _decodedToken.value
|
||||||
|
if (decodedToken == null) {
|
||||||
|
val failureData = WalletViewModel.FailureAnimationData(
|
||||||
|
errorMessage = "Failed to decode token",
|
||||||
|
operationType = "Token Receive"
|
||||||
|
)
|
||||||
|
onFailure(failureData)
|
||||||
|
clearTokenInput()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the CashuService.receiveToken with mint management
|
||||||
|
cashuService.receiveToken(
|
||||||
|
token = token,
|
||||||
|
autoAdd = true, // Enable automatic mint addition
|
||||||
|
currentMints = currentMints,
|
||||||
|
decodedToken = decodedToken
|
||||||
|
).onSuccess { amount ->
|
||||||
|
|
||||||
|
// Add transaction
|
||||||
|
val transaction = WalletTransaction(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
type = TransactionType.CASHU_RECEIVE,
|
||||||
|
amount = BigDecimal(amount),
|
||||||
|
unit = "sat",
|
||||||
|
status = TransactionStatus.CONFIRMED,
|
||||||
|
timestamp = Date(),
|
||||||
|
description = "Cashu token received",
|
||||||
|
token = token
|
||||||
|
)
|
||||||
|
repository.saveTransaction(transaction).onSuccess {
|
||||||
|
onTransactionSaved()
|
||||||
|
onBalanceRefresh()
|
||||||
|
// Reload mints in case a new one was added
|
||||||
|
onMintsUpdated()
|
||||||
|
|
||||||
|
// Clear token input immediately
|
||||||
|
clearTokenInput()
|
||||||
|
|
||||||
|
// Show success animation
|
||||||
|
val animationData = WalletViewModel.SuccessAnimationData(
|
||||||
|
type = WalletViewModel.SuccessAnimationType.CASHU_RECEIVED,
|
||||||
|
amount = amount,
|
||||||
|
unit = "sat",
|
||||||
|
description = "Cashu token received successfully!"
|
||||||
|
)
|
||||||
|
onSuccess(animationData)
|
||||||
|
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to save transaction", error)
|
||||||
|
|
||||||
|
// Clear token input
|
||||||
|
clearTokenInput()
|
||||||
|
|
||||||
|
// Show failure animation for transaction save error
|
||||||
|
val failureData = WalletViewModel.FailureAnimationData(
|
||||||
|
errorMessage = "Failed to save transaction: ${error.message}",
|
||||||
|
operationType = "Token Receive"
|
||||||
|
)
|
||||||
|
onFailure(failureData)
|
||||||
|
}
|
||||||
|
}.onFailure { error ->
|
||||||
|
Log.e(TAG, "Failed to receive token", error)
|
||||||
|
|
||||||
|
// Clear token input
|
||||||
|
clearTokenInput()
|
||||||
|
|
||||||
|
// Show failure animation for token receive error
|
||||||
|
val failureData = WalletViewModel.FailureAnimationData(
|
||||||
|
errorMessage = error.message ?: "Unknown error occurred",
|
||||||
|
operationType = "Token Receive"
|
||||||
|
)
|
||||||
|
onFailure(failureData)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Exception in receiveCashuToken", e)
|
||||||
|
|
||||||
|
// Clear token input
|
||||||
|
clearTokenInput()
|
||||||
|
|
||||||
|
// Show failure animation for unexpected error
|
||||||
|
val failureData = WalletViewModel.FailureAnimationData(
|
||||||
|
errorMessage = e.message ?: "Unexpected error occurred",
|
||||||
|
operationType = "Token Receive"
|
||||||
|
)
|
||||||
|
onFailure(failureData)
|
||||||
|
} finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set token input and trigger decoding if it's a Cashu token
|
||||||
|
*/
|
||||||
|
fun setTokenInput(token: String) {
|
||||||
|
_tokenInput.value = token
|
||||||
|
if (token.isNotEmpty() && token.startsWith("cashu")) {
|
||||||
|
decodeCashuToken(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear token input and decoded token
|
||||||
|
*/
|
||||||
|
fun clearTokenInput() {
|
||||||
|
_tokenInput.value = ""
|
||||||
|
_decodedToken.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear generated token
|
||||||
|
*/
|
||||||
|
fun clearGeneratedToken() {
|
||||||
|
_generatedToken.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set decoded token directly (for external integrations)
|
||||||
|
*/
|
||||||
|
fun setDecodedToken(token: CashuToken) {
|
||||||
|
_decodedToken.value = token
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear error message
|
||||||
|
*/
|
||||||
|
fun clearError() {
|
||||||
|
_errorMessage.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current token input
|
||||||
|
*/
|
||||||
|
fun getCurrentTokenInput(): String = _tokenInput.value ?: ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current decoded token
|
||||||
|
*/
|
||||||
|
fun getCurrentDecodedToken(): CashuToken? = _decodedToken.value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current generated token
|
||||||
|
*/
|
||||||
|
fun getCurrentGeneratedToken(): String? = _generatedToken.value
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.bitchat.android.wallet.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.bitchat.android.wallet.data.WalletTransaction
|
||||||
|
import com.bitchat.android.wallet.repository.WalletRepository
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all transaction-related operations for the wallet
|
||||||
|
*/
|
||||||
|
class TransactionManager(
|
||||||
|
private val repository: WalletRepository,
|
||||||
|
private val coroutineScope: CoroutineScope
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TransactionManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observable data
|
||||||
|
private val _transactions = MutableLiveData<List<WalletTransaction>>(emptyList())
|
||||||
|
val transactions: androidx.lifecycle.LiveData<List<WalletTransaction>> = _transactions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load recent transaction history (last 10)
|
||||||
|
*/
|
||||||
|
fun loadTransactions() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
repository.getLastTransactions(10).onSuccess { txList ->
|
||||||
|
_transactions.value = txList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get full transaction history (not just last 10)
|
||||||
|
*/
|
||||||
|
fun getAllTransactions(): androidx.lifecycle.LiveData<List<WalletTransaction>> {
|
||||||
|
val allTransactions = MutableLiveData<List<WalletTransaction>>()
|
||||||
|
coroutineScope.launch {
|
||||||
|
repository.getAllTransactions().onSuccess { txList ->
|
||||||
|
allTransactions.value = txList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allTransactions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a new transaction
|
||||||
|
*/
|
||||||
|
fun saveTransaction(transaction: WalletTransaction, onSuccess: () -> Unit, onError: (String) -> Unit) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
repository.saveTransaction(transaction).onSuccess {
|
||||||
|
loadTransactions() // Refresh the transaction list
|
||||||
|
onSuccess()
|
||||||
|
}.onFailure { error ->
|
||||||
|
onError(error.message ?: "Failed to save transaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current transactions list
|
||||||
|
*/
|
||||||
|
fun getCurrentTransactions(): List<WalletTransaction> = _transactions.value ?: emptyList()
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package com.bitchat.android.wallet.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all UI state for the wallet
|
||||||
|
*/
|
||||||
|
class UIStateManager {
|
||||||
|
|
||||||
|
// Send/Receive dialog state
|
||||||
|
private val _showSendDialog = MutableLiveData<Boolean>(false)
|
||||||
|
val showSendDialog: androidx.lifecycle.LiveData<Boolean> = _showSendDialog
|
||||||
|
|
||||||
|
private val _showReceiveDialog = MutableLiveData<Boolean>(false)
|
||||||
|
val showReceiveDialog: androidx.lifecycle.LiveData<Boolean> = _showReceiveDialog
|
||||||
|
|
||||||
|
private val _sendType = MutableLiveData<WalletViewModel.SendType>(WalletViewModel.SendType.CASHU)
|
||||||
|
val sendType: androidx.lifecycle.LiveData<WalletViewModel.SendType> = _sendType
|
||||||
|
|
||||||
|
private val _receiveType = MutableLiveData<WalletViewModel.ReceiveType>(WalletViewModel.ReceiveType.CASHU)
|
||||||
|
val receiveType: androidx.lifecycle.LiveData<WalletViewModel.ReceiveType> = _receiveType
|
||||||
|
|
||||||
|
// Mints tab state
|
||||||
|
private val _showAddMintDialog = MutableLiveData<Boolean>(false)
|
||||||
|
val showAddMintDialog: androidx.lifecycle.LiveData<Boolean> = _showAddMintDialog
|
||||||
|
|
||||||
|
// Success animation state
|
||||||
|
private val _showSuccessAnimation = MutableLiveData<Boolean>(false)
|
||||||
|
val showSuccessAnimation: androidx.lifecycle.LiveData<Boolean> = _showSuccessAnimation
|
||||||
|
|
||||||
|
private val _successAnimationData = MutableLiveData<WalletViewModel.SuccessAnimationData?>(null)
|
||||||
|
val successAnimationData: androidx.lifecycle.LiveData<WalletViewModel.SuccessAnimationData?> = _successAnimationData
|
||||||
|
|
||||||
|
// Failure animation state
|
||||||
|
private val _showFailureAnimation = MutableLiveData<Boolean>(false)
|
||||||
|
val showFailureAnimation: androidx.lifecycle.LiveData<Boolean> = _showFailureAnimation
|
||||||
|
|
||||||
|
private val _failureAnimationData = MutableLiveData<WalletViewModel.FailureAnimationData?>(null)
|
||||||
|
val failureAnimationData: androidx.lifecycle.LiveData<WalletViewModel.FailureAnimationData?> = _failureAnimationData
|
||||||
|
|
||||||
|
// Loading and error state
|
||||||
|
private val _isLoading = MutableLiveData<Boolean>(false)
|
||||||
|
val isLoading: androidx.lifecycle.LiveData<Boolean> = _isLoading
|
||||||
|
|
||||||
|
private val _errorMessage = MutableLiveData<String?>(null)
|
||||||
|
val errorMessage: androidx.lifecycle.LiveData<String?> = _errorMessage
|
||||||
|
|
||||||
|
// Back navigation handler
|
||||||
|
private var backHandler: (() -> Boolean)? = null
|
||||||
|
|
||||||
|
// Dialog Management
|
||||||
|
|
||||||
|
fun showSendDialog() {
|
||||||
|
_showSendDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideSendDialog() {
|
||||||
|
_showSendDialog.value = false
|
||||||
|
_sendType.value = WalletViewModel.SendType.CASHU
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showReceiveDialog() {
|
||||||
|
_showReceiveDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideReceiveDialog() {
|
||||||
|
_showReceiveDialog.value = false
|
||||||
|
_receiveType.value = WalletViewModel.ReceiveType.CASHU
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSendType(type: WalletViewModel.SendType) {
|
||||||
|
_sendType.value = type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setReceiveType(type: WalletViewModel.ReceiveType) {
|
||||||
|
_receiveType.value = type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showAddMintDialog() {
|
||||||
|
_showAddMintDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideAddMintDialog() {
|
||||||
|
_showAddMintDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation Management
|
||||||
|
|
||||||
|
fun showSuccessAnimation(animationData: WalletViewModel.SuccessAnimationData) {
|
||||||
|
_successAnimationData.value = animationData
|
||||||
|
_showSuccessAnimation.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideSuccessAnimation() {
|
||||||
|
_showSuccessAnimation.value = false
|
||||||
|
_successAnimationData.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showFailureAnimation(animationData: WalletViewModel.FailureAnimationData) {
|
||||||
|
_failureAnimationData.value = animationData
|
||||||
|
_showFailureAnimation.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideFailureAnimation() {
|
||||||
|
_showFailureAnimation.value = false
|
||||||
|
_failureAnimationData.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading and Error Management
|
||||||
|
|
||||||
|
fun setLoading(loading: Boolean) {
|
||||||
|
_isLoading.value = loading
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearError() {
|
||||||
|
_errorMessage.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setError(message: String) {
|
||||||
|
_errorMessage.value = message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back Navigation
|
||||||
|
|
||||||
|
fun setBackHandler(handler: () -> Boolean) {
|
||||||
|
backHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleBackPress(): Boolean {
|
||||||
|
return backHandler?.invoke() ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// State Getters
|
||||||
|
|
||||||
|
fun getCurrentSendType(): WalletViewModel.SendType = _sendType.value ?: WalletViewModel.SendType.CASHU
|
||||||
|
|
||||||
|
fun getCurrentReceiveType(): WalletViewModel.ReceiveType = _receiveType.value ?: WalletViewModel.ReceiveType.CASHU
|
||||||
|
|
||||||
|
fun isCurrentlyLoading(): Boolean = _isLoading.value ?: false
|
||||||
|
|
||||||
|
fun getCurrentError(): String? = _errorMessage.value
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user