Skip to content

Commit ac9e5b3

Browse files
authored
Merge pull request #373 from synonymdev/fix/ln-balance-check-send
Use cached outbound value as fallback when node is initializing
2 parents 933b619 + d0e1667 commit ac9e5b3

File tree

8 files changed

+47
-17
lines changed

8 files changed

+47
-17
lines changed

app/src/main/java/to/bitkit/repositories/LightningRepo.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ import kotlin.time.Duration
6262
import kotlin.time.Duration.Companion.minutes
6363
import kotlin.time.Duration.Companion.seconds
6464

65-
private const val SYNC_TIMEOUT_MS = 10_000L
65+
private const val SYNC_TIMEOUT_MS = 15_000L
6666

6767
@Singleton
6868
class LightningRepo @Inject constructor(
@@ -684,8 +684,13 @@ class LightningRepo @Inject constructor(
684684
}
685685
}
686686

687-
fun canSend(amountSats: ULong): Boolean =
688-
_lightningState.value.nodeLifecycleState.isRunning() && lightningService.canSend(amountSats)
687+
suspend fun canSend(amountSats: ULong, fallbackToCachedBalance: Boolean = true): Boolean {
688+
return if (!_lightningState.value.nodeLifecycleState.isRunning() && fallbackToCachedBalance) {
689+
amountSats <= (cacheStore.data.first().balance?.maxSendLightningSats ?: 0u)
690+
} else {
691+
lightningService.canSend(amountSats)
692+
}
693+
}
689694

690695
fun getSyncFlow(): Flow<Unit> = lightningService.syncFlow()
691696

app/src/main/java/to/bitkit/services/LightningService.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,11 +445,13 @@ class LightningService @Inject constructor(
445445
.getOrElse { e -> throw LdkError(e as NodeException) }
446446

447447
return ServiceQueue.LDK.background {
448-
when (sats != null) {
449-
true -> node.bolt11Payment().sendUsingAmount(bolt11Invoice, sats * 1000u, null)
450-
else -> node.bolt11Payment().send(bolt11Invoice, null)
448+
runCatching {
449+
when (sats != null) {
450+
true -> node.bolt11Payment().sendUsingAmount(bolt11Invoice, sats * 1000u, null)
451+
else -> node.bolt11Payment().send(bolt11Invoice, null)
452+
}
451453
}
452-
}
454+
}.getOrThrow()
453455
}
454456

455457
suspend fun estimateRoutingFees(bolt11: String): Result<ULong> {

app/src/main/java/to/bitkit/ui/MainActivity.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package to.bitkit.ui
22

3+
import android.app.NotificationManager
34
import android.content.Intent
45
import android.os.Bundle
56
import androidx.activity.compose.setContent
@@ -70,10 +71,11 @@ class MainActivity : FragmentActivity() {
7071

7172
initNotificationChannel()
7273
initNotificationChannel(
73-
// TODO EXTRACT TO Strings
74+
// TODO Transifex
7475
id = CHANNEL_ID_NODE,
7576
name = "Lightning node notification",
7677
desc = "Channel for LightningNodeService",
78+
importance = NotificationManager.IMPORTANCE_LOW
7779
)
7880
startForegroundService(Intent(this, LightningNodeService::class.java))
7981
installSplashScreen()

app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ private fun SendAmountNodeRunning(
294294
PrimaryButton(
295295
text = stringResource(R.string.common__continue),
296296
enabled = uiState.isAmountInputValid,
297+
isLoading = uiState.isLoading,
297298
onClick = { onEvent(SendEvent.AmountContinue(uiState.amountInput)) },
298299
modifier = Modifier.testTag("ContinueAmount")
299300
)

app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ class AppViewModel @Inject constructor(
347347
}
348348
}
349349

350-
private fun onAmountChange(value: String) {
350+
private fun onAmountChange(value: String) = viewModelScope.launch {
351351
_sendUiState.update {
352352
it.copy(
353353
amountInput = value,
@@ -401,7 +401,7 @@ class AppViewModel @Inject constructor(
401401
}
402402
}
403403

404-
private fun onPaymentMethodSwitch() {
404+
private fun onPaymentMethodSwitch() = viewModelScope.launch {
405405
val nextPaymentMethod = when (_sendUiState.value.payMethod) {
406406
SendMethod.ONCHAIN -> SendMethod.LIGHTNING
407407
SendMethod.LIGHTNING -> SendMethod.ONCHAIN
@@ -433,8 +433,10 @@ class AppViewModel @Inject constructor(
433433
return
434434
}
435435

436+
_sendUiState.update { it.copy(isLoading = true) }
436437
refreshOnchainSendIfNeeded()
437438
estimateLightningRoutingFeesIfNeeded()
439+
_sendUiState.update { it.copy(isLoading = false) }
438440

439441
setSendEffect(SendEffect.NavigateToConfirm)
440442
}
@@ -447,7 +449,7 @@ class AppViewModel @Inject constructor(
447449
setSendEffect(SendEffect.NavigateToConfirm)
448450
}
449451

450-
private fun validateAmount(
452+
private suspend fun validateAmount(
451453
value: String,
452454
payMethod: SendMethod = _sendUiState.value.payMethod,
453455
): Boolean {
@@ -822,7 +824,7 @@ class AppViewModel @Inject constructor(
822824
return false
823825
}
824826

825-
private fun resetAmountInput() {
827+
private fun resetAmountInput() = viewModelScope.launch {
826828
_sendUiState.update { state ->
827829
state.copy(
828830
amountInput = state.amount.toString(),

app/src/main/java/to/bitkit/viewmodels/QuickPayViewModel.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ class QuickPayViewModel @Inject constructor(
7373
bolt11: String,
7474
amount: ULong? = null,
7575
): Result<PaymentId> {
76-
val hash = lightningRepo.payInvoice(bolt11 = bolt11, sats = amount).getOrThrow()
76+
val hash = lightningRepo.payInvoice(bolt11 = bolt11, sats = amount)
77+
.onFailure { exception ->
78+
return Result.failure(exception)
79+
}
80+
.getOrDefault("")
7781

7882
// Wait until matching payment event is received
7983
val result = ldkNodeEventBus.events.watchUntil { event ->

app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
77
import androidx.lifecycle.viewModelScope
88
import dagger.hilt.android.lifecycle.HiltViewModel
99
import kotlinx.coroutines.CoroutineDispatcher
10+
import kotlinx.coroutines.TimeoutCancellationException
1011
import kotlinx.coroutines.delay
1112
import kotlinx.coroutines.flow.MutableSharedFlow
1213
import kotlinx.coroutines.flow.MutableStateFlow
@@ -163,7 +164,9 @@ class WalletViewModel @Inject constructor(
163164
walletRepo.syncNodeAndWallet()
164165
.onFailure { error ->
165166
Logger.error("Failed to refresh state: ${error.message}", error)
166-
ToastEventBus.send(error)
167+
if (error !is TimeoutCancellationException) {
168+
ToastEventBus.send(error)
169+
}
167170
}
168171
}
169172
}
@@ -174,7 +177,9 @@ class WalletViewModel @Inject constructor(
174177
walletRepo.syncNodeAndWallet()
175178
.onFailure { error ->
176179
Logger.error("Failed to refresh state: ${error.message}", error)
177-
ToastEventBus.send(error)
180+
if (error !is TimeoutCancellationException) {
181+
ToastEventBus.send(error)
182+
}
178183
}
179184
_uiState.update { it.copy(isRefreshing = false) }
180185
}

app/src/test/java/to/bitkit/repositories/LightningRepoTest.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ import org.mockito.kotlin.verify
2222
import org.mockito.kotlin.verifyBlocking
2323
import org.mockito.kotlin.whenever
2424
import org.mockito.kotlin.wheneverBlocking
25+
import to.bitkit.data.AppCacheData
2526
import to.bitkit.data.CacheStore
2627
import to.bitkit.data.SettingsData
2728
import to.bitkit.data.SettingsStore
2829
import to.bitkit.data.dto.TransactionMetadata
2930
import to.bitkit.data.keychain.Keychain
3031
import to.bitkit.ext.createChannelDetails
32+
import to.bitkit.models.BalanceState
3133
import to.bitkit.models.CoinSelectionPreference
3234
import to.bitkit.models.ElectrumServer
3335
import to.bitkit.models.LnPeer
@@ -286,8 +288,15 @@ class LightningRepoTest : BaseUnitTest() {
286288
}
287289

288290
@Test
289-
fun `canSend should return false when node is not running`() = test {
290-
assertFalse(sut.canSend(1000uL))
291+
fun `canSend should use cached outbound when node is not running`() = test {
292+
val cacheData = AppCacheData(
293+
balance = BalanceState(
294+
maxSendLightningSats = 2000uL
295+
)
296+
)
297+
whenever(cacheStore.data).thenReturn(flowOf(cacheData))
298+
299+
assert(sut.canSend(1000uL, fallbackToCachedBalance = true))
291300
}
292301

293302
@Test

0 commit comments

Comments
 (0)