@@ -98,39 +98,15 @@ class WalletRepo @Inject constructor(
9898 }
9999 }
100100
101- suspend fun refreshBip21 (force : Boolean = false ): Result <Unit > = withContext(bgDispatcher) {
102- Logger .debug(" Refreshing bip21 (force: $force ) " , context = TAG )
101+ suspend fun refreshBip21 (): Result <Unit > = withContext(bgDispatcher) {
102+ Logger .debug(" Refreshing bip21" , context = TAG )
103103
104- val shouldBlockLightningReceive = coreService.checkGeoBlock().second
104+ val (_, shouldBlockLightningReceive) = coreService.checkGeoBlock()
105105 _walletState .update {
106106 it.copy(receiveOnSpendingBalance = ! shouldBlockLightningReceive)
107107 }
108-
109- // Reset invoice state
110- _walletState .update {
111- it.copy(
112- selectedTags = emptyList(),
113- bip21Description = " " ,
114- bip21 = " " ,
115- bip21AmountSats = null ,
116- )
117- }
118-
119- // Check current address or generate new one
120- val currentAddress = getOnchainAddress()
121- if (force || currentAddress.isEmpty()) {
122- newAddress()
123- } else {
124- // Check if current address has been used
125- checkAddressUsage(currentAddress)
126- .onSuccess { hasTransactions ->
127- if (hasTransactions) {
128- // Address has been used, generate a new one
129- newAddress()
130- }
131- }
132- }
133-
108+ clearBip21State()
109+ refreshAddressIfNeeded()
134110 updateBip21Invoice()
135111 return @withContext Result .success(Unit )
136112 }
@@ -172,11 +148,66 @@ class WalletRepo @Inject constructor(
172148
173149 suspend fun refreshBip21ForEvent (event : Event ) {
174150 when (event) {
175- is Event .PaymentReceived , is Event .ChannelReady , is Event .ChannelClosed -> refreshBip21()
151+ is Event .ChannelReady -> {
152+ // Only refresh bolt11 if we can now receive on lightning
153+ if (lightningRepo.canReceive()) {
154+ lightningRepo.createInvoice(
155+ amountSats = _walletState .value.bip21AmountSats,
156+ description = _walletState .value.bip21Description,
157+ ).onSuccess { bolt11 ->
158+ setBolt11(bolt11)
159+ updateBip21Url()
160+ }
161+ }
162+ }
163+
164+ is Event .ChannelClosed -> {
165+ // Clear bolt11 if we can no longer receive on lightning
166+ if (! lightningRepo.canReceive()) {
167+ setBolt11(" " )
168+ updateBip21Url()
169+ }
170+ }
171+
172+ is Event .PaymentReceived -> {
173+ // Check if onchain address was used, generate new one if needed
174+ refreshAddressIfNeeded()
175+ updateBip21Url()
176+ }
177+
176178 else -> Unit
177179 }
178180 }
179181
182+ private suspend fun refreshAddressIfNeeded () = withContext(bgDispatcher) {
183+ val address = getOnchainAddress()
184+ if (address.isEmpty()) {
185+ newAddress()
186+ } else {
187+ checkAddressUsage(address).onSuccess { wasUsed ->
188+ if (wasUsed) {
189+ newAddress()
190+ }
191+ }
192+ }
193+ }
194+
195+ private suspend fun updateBip21Url (
196+ amountSats : ULong? = _walletState .value.bip21AmountSats,
197+ message : String = _walletState .value.bip21Description,
198+ ): String {
199+ val address = getOnchainAddress()
200+ val newBip21 = buildBip21Url(
201+ bitcoinAddress = address,
202+ amountSats = amountSats,
203+ message = message.ifBlank { Env .DEFAULT_INVOICE_MESSAGE },
204+ lightningInvoice = getBolt11(),
205+ )
206+ setBip21(newBip21)
207+
208+ return newBip21
209+ }
210+
180211 suspend fun createWallet (bip39Passphrase : String? ): Result <Unit > = withContext(bgDispatcher) {
181212 lightningRepo.setRecoveryMode(enabled = false )
182213 try {
@@ -310,12 +341,19 @@ class WalletRepo @Inject constructor(
310341 }
311342
312343 // BIP21 state management
313- fun updateBip21AmountSats (amount : ULong? ) {
314- _walletState .update { it.copy(bip21AmountSats = amount) }
315- }
344+ fun setBip21AmountSats (amount : ULong? ) = _walletState .update { it.copy(bip21AmountSats = amount) }
345+
346+ fun setBip21Description ( description : String ) = _walletState .update { it.copy(bip21Description = description) }
316347
317- fun updateBip21Description (description : String ) {
318- _walletState .update { it.copy(bip21Description = description) }
348+ fun clearBip21State () {
349+ _walletState .update {
350+ it.copy(
351+ bip21 = " " ,
352+ selectedTags = emptyList(),
353+ bip21AmountSats = null ,
354+ bip21Description = " " ,
355+ )
356+ }
319357 }
320358
321359 suspend fun toggleReceiveOnSpendingBalance (): Result <Unit > = withContext(bgDispatcher) {
@@ -348,37 +386,23 @@ class WalletRepo @Inject constructor(
348386
349387 // BIP21 invoice creation
350388 suspend fun updateBip21Invoice (
351- amountSats : ULong? = null ,
352- description : String = "" ,
389+ amountSats : ULong? = walletState.value.bip21AmountSats ,
390+ description : String = walletState.value.bip21Description ,
353391 ): Result <Unit > = withContext(bgDispatcher) {
354392 try {
355- updateBip21AmountSats (amountSats)
356- updateBip21Description (description)
393+ setBip21AmountSats (amountSats)
394+ setBip21Description (description)
357395
358396 val canReceive = lightningRepo.canReceive()
359397 if (canReceive && _walletState .value.receiveOnSpendingBalance) {
360- lightningRepo.createInvoice(
361- amountSats = _walletState .value.bip21AmountSats,
362- description = _walletState .value.bip21Description,
363- ).onSuccess { bolt11 ->
364- setBolt11(bolt11)
398+ lightningRepo.createInvoice(amountSats, description).onSuccess {
399+ setBolt11(it)
365400 }
366401 } else {
367402 setBolt11(" " )
368403 }
369- val address = getOnchainAddress()
370- val newBip21 = buildBip21Url(
371- bitcoinAddress = address,
372- amountSats = _walletState .value.bip21AmountSats,
373- message = description.ifBlank { Env .DEFAULT_INVOICE_MESSAGE },
374- lightningInvoice = getBolt11()
375- )
376- setBip21(newBip21)
377- saveInvoiceWithTags(
378- bip21Invoice = newBip21,
379- onChainAddress = address,
380- tags = _walletState .value.selectedTags
381- )
404+ val newBip21Url = updateBip21Url(amountSats, description)
405+ persistTagsMetadata(newBip21Url)
382406 Result .success(Unit )
383407 } catch (e: Throwable ) {
384408 Logger .error(" Update BIP21 invoice error" , e, context = TAG )
@@ -404,14 +428,16 @@ class WalletRepo @Inject constructor(
404428 }
405429 }
406430
407- suspend fun saveInvoiceWithTags ( bip21Invoice : String , onChainAddress : String , tags : List < String > ) =
431+ private suspend fun persistTagsMetadata ( bip21Url : String ) =
408432 withContext(bgDispatcher) {
433+ val tags = _walletState .value.selectedTags
409434 if (tags.isEmpty()) return @withContext
410435
436+ val onChainAddress = getOnchainAddress()
437+
411438 try {
412- deleteExpiredInvoices()
413- val decoded = decode(bip21Invoice)
414- val paymentHash = when (decoded) {
439+ deleteExpiredTagMetadata()
440+ val paymentHash = when (val decoded = decode(bip21Url)) {
415441 is Scanner .Lightning -> decoded.invoice.paymentHash.toHex()
416442 is Scanner .OnChain -> decoded.extractLightningHash()
417443 else -> null
@@ -432,15 +458,7 @@ class WalletRepo @Inject constructor(
432458 }
433459 }
434460
435- suspend fun deleteAllInvoices () = withContext(bgDispatcher) {
436- try {
437- db.tagMetadataDao().deleteAll()
438- } catch (e: Throwable ) {
439- Logger .error(" deleteAllInvoices error" , e, context = TAG )
440- }
441- }
442-
443- suspend fun deleteExpiredInvoices () = withContext(bgDispatcher) {
461+ private suspend fun deleteExpiredTagMetadata () = withContext(bgDispatcher) {
444462 try {
445463 val twoDaysAgoMillis = Clock .System .now().minus(2 .days).toEpochMilliseconds()
446464 db.tagMetadataDao().deleteExpired(expirationTimeStamp = twoDaysAgoMillis)
@@ -451,9 +469,8 @@ class WalletRepo @Inject constructor(
451469
452470 private suspend fun Scanner.OnChain.extractLightningHash (): String? {
453471 val lightningInvoice: String = this .invoice.params?.get(" lightning" ) ? : return null
454- val decoded = decode(lightningInvoice)
455472
456- return when (decoded) {
473+ return when (val decoded = decode(lightningInvoice) ) {
457474 is Scanner .Lightning -> decoded.invoice.paymentHash.toHex()
458475 else -> null
459476 }
0 commit comments