refactor: WalletViewModel extract ClipboardViewModel

This commit is contained in:
2026-06-01 22:10:52 +02:00
parent d84b6711ff
commit 2e0530073d
3 changed files with 81 additions and 57 deletions
@@ -4,12 +4,9 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bitcointxoko.gudariwallet.api.LnurlScanResponse
import com.bitcointxoko.gudariwallet.data.LnurlCacheRepository
import com.bitcointxoko.gudariwallet.data.NodeAliasRepository
import com.bitcointxoko.gudariwallet.data.PaymentCacheRepository
import com.bitcointxoko.gudariwallet.data.WalletRepository
import com.bitcointxoko.gudariwallet.util.SendInputDetector
import com.bitcointxoko.gudariwallet.util.SendInputType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
@@ -17,6 +14,7 @@ import kotlinx.coroutines.launch
import androidx.fragment.app.FragmentActivity
import com.bitcointxoko.gudariwallet.data.BalancePrefsStore
import com.bitcointxoko.gudariwallet.ui.balance.BalanceViewModel
import com.bitcointxoko.gudariwallet.ui.clipboard.ClipboardViewModel
import com.bitcointxoko.gudariwallet.ui.fiat.FiatViewModel
import com.bitcointxoko.gudariwallet.ui.receive.ReceiveViewModel
import com.bitcointxoko.gudariwallet.ui.send.SendViewModel
@@ -26,16 +24,15 @@ private const val TAG = "WalletViewModel"
class WalletViewModel(
val repo : WalletRepository,
val aliasRepo : NodeAliasRepository,
val lnurlCache: LnurlCacheRepository,
val paymentCache: PaymentCacheRepository,
private val balancePrefs: BalancePrefsStore,
val balanceVm : BalanceViewModel,
val fiatVm : FiatViewModel,
val receiveVm : ReceiveViewModel,
val sendVm : SendViewModel,
val clipboardVm : ClipboardViewModel,
val lightningAddress: MutableStateFlow<String?>,
) : ViewModel() {
// ── Balance state ─────────────────────────────────────────────────────────
val balanceState: StateFlow<BalanceState> get() = balanceVm.balanceState
val balanceHidden: StateFlow<Boolean> get() = fiatVm.balanceHidden
val selectedCurrency: StateFlow<String?> get() = fiatVm.selectedCurrency
@@ -52,6 +49,7 @@ class WalletViewModel(
fun executeWithdraw(k1: String, callback: String, amountSats: Long, memo: String) =
receiveVm.executeWithdraw(k1, callback, amountSats, memo)
val sendState: StateFlow<SendState> get() = sendVm.sendState
fun refreshBalance() = balanceVm.refreshBalance()
fun scan(rawInput: String) = sendVm.scan(rawInput)
fun payBolt11(bolt11: String) = sendVm.payBolt11(bolt11)
fun payLnurl(rawRes: LnurlScanResponse, lnurl: String, amountMsat: Long, comment: String?) =
@@ -62,12 +60,9 @@ class WalletViewModel(
fun requestPayment(activity: FragmentActivity, subtitle: String, pay: () -> Unit) =
sendVm.requestPayment(activity, subtitle, pay)
fun resetSendState() = sendVm.resetSendState()
// ── Other states ──────────────────────────────────────────────────────────
private val _lightningAddress = MutableStateFlow<String?>(balancePrefs.lightningAddress)
val lightningAddress: StateFlow<String?> = _lightningAddress
// ── Init ──────────────────────────────────────────────────────────────────
val clipboardOfferState: StateFlow<ClipboardOfferState> get() = clipboardVm.clipboardOfferState
fun checkClipboard(text: String?) = clipboardVm.checkClipboard(text)
fun dismissClipboardOffer() = clipboardVm.dismissClipboardOffer()
init {
observePaymentEvents()
@@ -91,7 +86,7 @@ class WalletViewModel(
.onSuccess { address ->
if (address != null) {
Log.d(TAG, "LNADDR [FETCH OK ] $address")
_lightningAddress.value = address
lightningAddress.value = address
balancePrefs.setLightningAddress(address)
} else {
Log.d(TAG, "LNADDR [FETCH OK ] no pay link / username found")
@@ -103,47 +98,4 @@ class WalletViewModel(
}
}
}
// ── Clipboard offer ───────────────────────────────────────────────────────
private var lastOfferedClipboard: String? = null
private val _clipboardOfferState = MutableStateFlow<ClipboardOfferState>(ClipboardOfferState.None)
val clipboardOfferState: StateFlow<ClipboardOfferState> = _clipboardOfferState
fun checkClipboard(text: String?) {
Log.d("Clipboard", "checkClipboard called with: $text")
val trimmed = text?.trim().takeIf { !it.isNullOrBlank() } ?: return
// ── Deduplicate: don't re-offer the same string twice ─────────────────
if (trimmed == lastOfferedClipboard) return
// ── Guard: ignore our own invoice ─────────────────────────────────────
val ownInvoice = (receiveState.value as? ReceiveState.InvoiceReady)?.bolt11
if (ownInvoice != null && trimmed.equals(ownInvoice, ignoreCase = true)) {
Log.d("Clipboard", "Skipping own invoice")
lastOfferedClipboard = trimmed // still mark as seen so focus re-checks don't re-trigger
return
}
// ── Guard: ignore our own lightning address ────────────────────────────
val ownAddress = _lightningAddress.value
if (ownAddress != null && trimmed.equals(ownAddress, ignoreCase = true)) {
Log.d("Clipboard", "Skipping own lightning address")
lastOfferedClipboard = trimmed
return
}
val type = SendInputDetector.detect(trimmed)
if (type == SendInputType.Unknown) return
lastOfferedClipboard = trimmed
_clipboardOfferState.value = ClipboardOfferState.Detected(raw = trimmed, inputType = type)
}
fun dismissClipboardOffer() {
_clipboardOfferState.value = ClipboardOfferState.None
}
fun refreshBalance() = balanceVm.refreshBalance()
}