diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml
index 9ece0cf15..1b66e4f1d 100644
--- a/app/detekt-baseline.xml
+++ b/app/detekt-baseline.xml
@@ -191,7 +191,7 @@
LongParameterList:CoreService.kt$OnchainService$( mnemonicPhrase: String, derivationPathStr: String?, network: Network?, bip39Passphrase: String?, isChange: Boolean?, startIndex: UInt?, count: UInt?, )
LongParameterList:DevSettingsViewModel.kt$DevSettingsViewModel$( @ApplicationContext private val context: Context, @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val firebaseMessaging: FirebaseMessaging, private val lightningRepo: LightningRepo, private val walletRepo: WalletRepo, private val widgetsStore: WidgetsStore, private val currencyRepo: CurrencyRepo, private val logsRepo: LogsRepo, private val cacheStore: CacheStore, private val blocktankRepo: BlocktankRepo, )
LongParameterList:LightningConnectionsViewModel.kt$LightningConnectionsViewModel$( @ApplicationContext private val context: Context, @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val lightningRepo: LightningRepo, internal val blocktankRepo: BlocktankRepo, private val logsRepo: LogsRepo, private val addressChecker: AddressChecker, private val ldkNodeEventBus: LdkNodeEventBus, private val walletRepo: WalletRepo, )
- LongParameterList:LightningRepo.kt$LightningRepo$( @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val lightningService: LightningService, private val ldkNodeEventBus: LdkNodeEventBus, private val settingsStore: SettingsStore, private val coreService: CoreService, private val blocktankNotificationsService: BlocktankNotificationsService, private val firebaseMessaging: FirebaseMessaging, private val keychain: Keychain, private val lnurlService: LnurlService, private val cacheStore: CacheStore, )
+ LongParameterList:LightningRepo.kt$LightningRepo$( @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val lightningService: LightningService, private val ldkNodeEventBus: LdkNodeEventBus, private val settingsStore: SettingsStore, private val coreService: CoreService, private val lspNotificationsService: LspNotificationsService, private val firebaseMessaging: FirebaseMessaging, private val keychain: Keychain, private val lnurlService: LnurlService, private val cacheStore: CacheStore, )
LongParameterList:LightningRepo.kt$LightningRepo$( address: Address, sats: ULong, speed: TransactionSpeed? = null, utxosToSpend: List<SpendableUtxo>? = null, feeRates: FeeRates? = null, isTransfer: Boolean = false, channelId: String? = null, )
LongParameterList:LightningRepo.kt$LightningRepo$( walletIndex: Int = 0, timeout: Duration? = null, shouldRetry: Boolean = true, eventHandler: NodeEventHandler? = null, customServer: ElectrumServer? = null, customRgsServerUrl: String? = null, )
LongParameterList:Notifications.kt$( title: String?, text: String?, extras: Bundle? = null, bigText: String? = null, id: Int = Random.nextInt(), context: Context, )
@@ -330,7 +330,6 @@
MagicNumber:QuickPaySettingsScreen.kt$20
MagicNumber:QuickPaySettingsScreen.kt$5
MagicNumber:QuickPaySettingsScreen.kt$50
- MagicNumber:ReceiveAmountScreen.kt$500
MagicNumber:ReceiveLiquidityScreen.kt$100.0
MagicNumber:ReceiveQrScreen.kt$17.33f
MagicNumber:ReceiveQrScreen.kt$32
@@ -481,7 +480,6 @@
ModifierMissing:HighBalanceWarningSheet.kt$HighBalanceWarningContent
ModifierMissing:HomeNav.kt$HomeNav
ModifierMissing:InfoScreenContent.kt$InfoScreenContent
- ModifierMissing:InfoTextField.kt$InfoTextField
ModifierMissing:InitializingWalletView.kt$InitializingWalletView
ModifierMissing:IntroScreen.kt$IntroScreen
ModifierMissing:LocalCurrencySettingsScreen.kt$LocalCurrencySettingsContent
@@ -498,7 +496,6 @@
ModifierMissing:QrScanningScreen.kt$QrScanningScreen
ModifierMissing:QuickPayIntroScreen.kt$QuickPayIntroScreen
ModifierMissing:QuickPaySettingsScreen.kt$QuickPaySettingsScreenContent
- ModifierMissing:ReceiveAmountScreen.kt$ReceiveAmountScreen
ModifierMissing:ReceiveSheet.kt$ReceiveSheet
ModifierMissing:ReportIssueResultScreen.kt$ReportIssueResultScreen
ModifierMissing:ReportIssueScreen.kt$ReportIssueContent
@@ -596,7 +593,6 @@
ReturnCount:LightningConnectionsViewModel.kt$LightningConnectionsViewModel$private fun findUpdatedChannel( currentChannel: ChannelDetails, allChannels: List<ChannelDetails>, ): ChannelDetails?
SpreadOperator:RestoreWalletScreen.kt$(*Array(24) { "" })
SwallowedException:Crypto.kt$Crypto$e: Exception
- SwallowedException:FcmService.kt$FcmService$e: SerializationException
TooGenericExceptionCaught:ActivityDetailViewModel.kt$ActivityDetailViewModel$e: Exception
TooGenericExceptionCaught:ActivityDetailViewModel.kt$ActivityDetailViewModel$e: Throwable
TooGenericExceptionCaught:ActivityListViewModel.kt$ActivityListViewModel$e: Exception
@@ -628,7 +624,6 @@
TooGenericExceptionCaught:NewTransactionSheetDetails.kt$NewTransactionSheetDetails.Companion$e: Exception
TooGenericExceptionCaught:PriceService.kt$PriceService$e: Exception
TooGenericExceptionCaught:QrScanningScreen.kt$e: Exception
- TooGenericExceptionCaught:ReceiveAmountScreen.kt$e: Exception
TooGenericExceptionCaught:SendCoinSelectionViewModel.kt$SendCoinSelectionViewModel$e: Throwable
TooGenericExceptionCaught:ServiceQueue.kt$ServiceQueue$e: Exception
TooGenericExceptionCaught:SettingUpScreen.kt$e: Throwable
diff --git a/app/src/androidTest/java/to/bitkit/ui/screens/wallets/send/SendAmountContentTest.kt b/app/src/androidTest/java/to/bitkit/ui/screens/wallets/send/SendAmountContentTest.kt
index 759ce22e5..840632ef6 100644
--- a/app/src/androidTest/java/to/bitkit/ui/screens/wallets/send/SendAmountContentTest.kt
+++ b/app/src/androidTest/java/to/bitkit/ui/screens/wallets/send/SendAmountContentTest.kt
@@ -51,8 +51,8 @@ class SendAmountContentTest {
// composeTestRule.onNodeWithTag("amount_input_field").assertExists() doesn't displayed because of viewmodel injection
composeTestRule.onNodeWithTag("available_balance").assertExists()
composeTestRule.onNodeWithTag("AssetButton-switch").assertExists()
- composeTestRule.onNodeWithTag("continue_button").assertExists()
- composeTestRule.onNodeWithTag("amount_keyboard").assertExists()
+ composeTestRule.onNodeWithTag("ContinueAmount").assertExists()
+ composeTestRule.onNodeWithTag("SendAmountNumberPad").assertExists()
}
@Test
diff --git a/app/src/main/java/to/bitkit/repositories/LightningRepo.kt b/app/src/main/java/to/bitkit/repositories/LightningRepo.kt
index f4ffa19b5..a786fee65 100644
--- a/app/src/main/java/to/bitkit/repositories/LightningRepo.kt
+++ b/app/src/main/java/to/bitkit/repositories/LightningRepo.kt
@@ -24,6 +24,7 @@ import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.withTimeoutOrNull
import org.lightningdevkit.ldknode.Address
import org.lightningdevkit.ldknode.BalanceDetails
+import org.lightningdevkit.ldknode.ChannelConfig
import org.lightningdevkit.ldknode.ChannelDetails
import org.lightningdevkit.ldknode.NodeStatus
import org.lightningdevkit.ldknode.PaymentDetails
@@ -646,8 +647,9 @@ class LightningRepo @Inject constructor(
peer: LnPeer,
channelAmountSats: ULong,
pushToCounterpartySats: ULong? = null,
+ channelConfig: ChannelConfig? = null,
): Result = executeWhenNodeRunning("Open channel") {
- val result = lightningService.openChannel(peer, channelAmountSats, pushToCounterpartySats)
+ val result = lightningService.openChannel(peer, channelAmountSats, pushToCounterpartySats, channelConfig)
syncState()
result
}
diff --git a/app/src/main/java/to/bitkit/services/LightningService.kt b/app/src/main/java/to/bitkit/services/LightningService.kt
index d578a9633..b0b87dabf 100644
--- a/app/src/main/java/to/bitkit/services/LightningService.kt
+++ b/app/src/main/java/to/bitkit/services/LightningService.kt
@@ -18,6 +18,7 @@ import org.lightningdevkit.ldknode.Bolt11Invoice
import org.lightningdevkit.ldknode.Bolt11InvoiceDescription
import org.lightningdevkit.ldknode.BuildException
import org.lightningdevkit.ldknode.Builder
+import org.lightningdevkit.ldknode.ChannelConfig
import org.lightningdevkit.ldknode.ChannelDetails
import org.lightningdevkit.ldknode.CoinSelectionAlgorithm
import org.lightningdevkit.ldknode.Config
@@ -324,6 +325,7 @@ class LightningService @Inject constructor(
peer: LnPeer,
channelAmountSats: ULong,
pushToCounterpartySats: ULong? = null,
+ channelConfig: ChannelConfig? = null,
): Result {
val node = this.node ?: throw ServiceError.NodeNotSetup
@@ -336,7 +338,7 @@ class LightningService @Inject constructor(
address = peer.address,
channelAmountSats = channelAmountSats,
pushToCounterpartyMsat = pushToCounterpartySats?.let { it * 1000u },
- channelConfig = null,
+ channelConfig = channelConfig,
)
Logger.info("Channel open initiated, userChannelId: $userChannelId")
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/EditInvoiceScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/EditInvoiceScreen.kt
index 445b12c78..3aa382112 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/EditInvoiceScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/EditInvoiceScreen.kt
@@ -12,13 +12,11 @@ import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@@ -38,7 +36,7 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.tooling.preview.Devices.PIXEL_5
+import androidx.compose.ui.tooling.preview.Devices.NEXUS_5
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@@ -53,11 +51,13 @@ import to.bitkit.ui.components.BodySSB
import to.bitkit.ui.components.BottomSheetPreview
import to.bitkit.ui.components.ButtonSize
import to.bitkit.ui.components.Caption13Up
+import to.bitkit.ui.components.FillHeight
import to.bitkit.ui.components.Keyboard
import to.bitkit.ui.components.NumberPadTextField
import to.bitkit.ui.components.PrimaryButton
import to.bitkit.ui.components.TagButton
import to.bitkit.ui.components.UnitButton
+import to.bitkit.ui.components.VerticalSpacer
import to.bitkit.ui.currencyViewModel
import to.bitkit.ui.scaffold.SheetTopBar
import to.bitkit.ui.shared.modifiers.sheetHeight
@@ -145,8 +145,14 @@ fun EditInvoiceScreen(
tags = walletUiState.selectedTags,
onBack = onBack,
onTextChanged = onDescriptionUpdate,
- numericKeyboardVisible = keyboardVisible,
- onClickBalance = { keyboardVisible = true },
+ keyboardVisible = keyboardVisible,
+ onClickBalance = {
+ if (keyboardVisible) {
+ currencyVM.togglePrimaryDisplay()
+ } else {
+ keyboardVisible = true
+ }
+ },
onInputChanged = onInputUpdated,
onContinueKeyboard = { keyboardVisible = false },
onContinueGeneral = {
@@ -164,7 +170,7 @@ fun EditInvoiceContent(
input: String,
noteText: String,
isSoftKeyboardVisible: Boolean,
- numericKeyboardVisible: Boolean,
+ keyboardVisible: Boolean,
primaryDisplay: PrimaryDisplay,
displayUnit: BitcoinDisplayUnit,
tags: List,
@@ -187,7 +193,7 @@ fun EditInvoiceContent(
val maxHeight = this.maxHeight
AnimatedVisibility(
- visible = !numericKeyboardVisible && !isSoftKeyboardVisible,
+ visible = !keyboardVisible && !isSoftKeyboardVisible,
enter = fadeIn(),
exit = fadeOut(),
modifier = Modifier
@@ -210,16 +216,17 @@ fun EditInvoiceContent(
.navigationBarsPadding()
.testTag("edit_invoice_screen")
) {
- SheetTopBar(stringResource(R.string.wallet__receive_specify)) {
- onBack()
- }
+ SheetTopBar(
+ titleText = stringResource(R.string.wallet__receive_specify),
+ onBack = onBack,
+ )
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
.testTag("ReceiveAmount")
) {
- Spacer(Modifier.height(32.dp))
+ VerticalSpacer(16.dp)
NumberPadTextField(
input = input,
@@ -233,7 +240,7 @@ fun EditInvoiceContent(
// Animated visibility for keyboard section
AnimatedVisibility(
- visible = numericKeyboardVisible,
+ visible = keyboardVisible,
enter = slideInVertically(
initialOffsetY = { fullHeight -> fullHeight },
animationSpec = tween(durationMillis = 300)
@@ -246,7 +253,7 @@ fun EditInvoiceContent(
Column(
modifier = Modifier.testTag("ReceiveNumberPad")
) {
- Spacer(modifier = Modifier.weight(1f))
+ FillHeight(min = 12.dp)
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -276,34 +283,26 @@ fun EditInvoiceContent(
.testTag("amount_keyboard")
)
- Spacer(
- modifier = Modifier
- .weight(1f)
- .sizeIn(minHeight = 16.dp, maxHeight = 41.dp)
- )
-
PrimaryButton(
text = stringResource(R.string.common__continue),
onClick = onContinueKeyboard,
modifier = Modifier.testTag("ReceiveNumberPadSubmit")
)
- Spacer(modifier = Modifier.height(16.dp))
+ VerticalSpacer(16.dp)
}
}
// Animated visibility for note section
AnimatedVisibility(
- visible = !numericKeyboardVisible,
+ visible = !keyboardVisible,
enter = fadeIn(animationSpec = tween(durationMillis = 300)),
exit = fadeOut(animationSpec = tween(durationMillis = 300))
) {
Column {
- Spacer(modifier = Modifier.height(44.dp))
-
+ VerticalSpacer(44.dp)
Caption13Up(text = stringResource(R.string.wallet__note), color = Colors.White64)
-
- Spacer(modifier = Modifier.height(16.dp))
+ VerticalSpacer(16.dp)
TextField(
placeholder = {
@@ -325,10 +324,10 @@ fun EditInvoiceContent(
.testTag("ReceiveNote")
)
- Spacer(modifier = Modifier.height(16.dp))
-
+ VerticalSpacer(16.dp)
Caption13Up(text = stringResource(R.string.wallet__tags), color = Colors.White64)
- Spacer(modifier = Modifier.height(8.dp))
+ VerticalSpacer(8.dp)
+
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
@@ -359,7 +358,7 @@ fun EditInvoiceContent(
modifier = Modifier.testTag("TagsAdd")
)
- Spacer(modifier = Modifier.weight(1f))
+ FillHeight()
PrimaryButton(
text = stringResource(R.string.wallet__receive_show_qr),
@@ -367,7 +366,7 @@ fun EditInvoiceContent(
modifier = Modifier.testTag("ShowQrReceive")
)
- Spacer(modifier = Modifier.height(16.dp))
+ VerticalSpacer(16.dp)
}
}
}
@@ -387,7 +386,7 @@ private fun Preview() {
displayUnit = BitcoinDisplayUnit.MODERN,
onBack = {},
onTextChanged = {},
- numericKeyboardVisible = false,
+ keyboardVisible = false,
onClickBalance = {},
onInputChanged = {},
onContinueGeneral = {},
@@ -404,7 +403,7 @@ private fun Preview() {
@Preview(showSystemUi = true)
@Composable
-private fun Preview2() {
+private fun PreviewWithTags() {
AppThemeSurface {
BottomSheetPreview {
EditInvoiceContent(
@@ -414,7 +413,7 @@ private fun Preview2() {
displayUnit = BitcoinDisplayUnit.MODERN,
onBack = {},
onTextChanged = {},
- numericKeyboardVisible = false,
+ keyboardVisible = false,
onClickBalance = {},
onInputChanged = {},
onContinueGeneral = {},
@@ -431,7 +430,7 @@ private fun Preview2() {
@Preview(showSystemUi = true)
@Composable
-private fun Preview3() {
+private fun PreviewWithKeyboard() {
AppThemeSurface {
BottomSheetPreview {
EditInvoiceContent(
@@ -441,7 +440,7 @@ private fun Preview3() {
displayUnit = BitcoinDisplayUnit.MODERN,
onBack = {},
onTextChanged = {},
- numericKeyboardVisible = true,
+ keyboardVisible = true,
onClickBalance = {},
onInputChanged = {},
onContinueGeneral = {},
@@ -456,9 +455,9 @@ private fun Preview3() {
}
}
-@Preview(showSystemUi = true, device = PIXEL_5)
+@Preview(showSystemUi = true, device = NEXUS_5)
@Composable
-private fun Preview4() {
+private fun PreviewSmallScreen() {
AppThemeSurface {
BottomSheetPreview {
EditInvoiceContent(
@@ -468,7 +467,7 @@ private fun Preview4() {
displayUnit = BitcoinDisplayUnit.MODERN,
onBack = {},
onTextChanged = {},
- numericKeyboardVisible = true,
+ keyboardVisible = true,
onClickBalance = {},
onInputChanged = {},
onContinueGeneral = {},
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveAmountScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveAmountScreen.kt
index d17e6b1ec..c0cefed4d 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveAmountScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveAmountScreen.kt
@@ -1,11 +1,10 @@
package to.bitkit.ui.screens.wallets.receive
+import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -18,12 +17,13 @@ import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Devices.NEXUS_5
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.delay
@@ -31,21 +31,33 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import to.bitkit.R
import to.bitkit.models.NodeLifecycleState
+import to.bitkit.models.PrimaryDisplay
import to.bitkit.models.Toast
import to.bitkit.ui.LocalCurrencies
import to.bitkit.ui.appViewModel
import to.bitkit.ui.blocktankViewModel
-import to.bitkit.ui.components.AmountInput
+import to.bitkit.ui.components.AmountInputHandler
+import to.bitkit.ui.components.BottomSheetPreview
import to.bitkit.ui.components.Caption13Up
+import to.bitkit.ui.components.FillHeight
+import to.bitkit.ui.components.FillWidth
+import to.bitkit.ui.components.Keyboard
import to.bitkit.ui.components.MoneySSB
+import to.bitkit.ui.components.NumberPadTextField
import to.bitkit.ui.components.PrimaryButton
import to.bitkit.ui.components.UnitButton
+import to.bitkit.ui.components.VerticalSpacer
+import to.bitkit.ui.currencyViewModel
import to.bitkit.ui.scaffold.SheetTopBar
+import to.bitkit.ui.shared.modifiers.sheetHeight
import to.bitkit.ui.shared.util.clickableAlpha
import to.bitkit.ui.shared.util.gradientBackground
+import to.bitkit.ui.theme.AppThemeSurface
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.walletViewModel
import to.bitkit.utils.Logger
+import to.bitkit.viewmodels.CurrencyUiState
+import kotlin.time.Duration.Companion.milliseconds
@Composable
fun ReceiveAmountScreen(
@@ -56,11 +68,12 @@ fun ReceiveAmountScreen(
val wallet = walletViewModel ?: return
val blocktank = blocktankViewModel ?: return
val walletState by wallet.uiState.collectAsStateWithLifecycle()
+ val currencyVM = currencyViewModel ?: return
val currencies = LocalCurrencies.current
- var satsAmount by rememberSaveable { mutableLongStateOf(0) }
+ var input: String by remember { mutableStateOf("0") }
var overrideSats: Long? by remember { mutableStateOf(null) }
-
+ var satsAmount by remember { mutableLongStateOf(0L) }
var isCreatingInvoice by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
@@ -68,105 +81,205 @@ fun ReceiveAmountScreen(
blocktank.refreshMinCjitSats()
}
+ AmountInputHandler(
+ input = input,
+ overrideSats = overrideSats,
+ primaryDisplay = currencies.primaryDisplay,
+ displayUnit = currencies.displayUnit,
+ onInputChanged = { newInput -> input = newInput },
+ onAmountCalculated = { sats ->
+ satsAmount = sats.toLongOrNull() ?: 0L
+ overrideSats = null
+ },
+ currencyVM = currencyVM,
+ )
+
+ val minCjitSats by blocktank.minCjitSats.collectAsStateWithLifecycle()
+
+ ReceiveAmountContent(
+ input = input,
+ satsAmount = satsAmount,
+ minCjitSats = minCjitSats,
+ currencyUiState = currencies,
+ isCreatingInvoice = isCreatingInvoice,
+ onInputChange = { input = it },
+ onClickMin = { overrideSats = it },
+ onClickAmount = { currencyVM.togglePrimaryDisplay() },
+ onBack = onBack,
+ onContinue = {
+ val sats = satsAmount.toULong()
+ scope.launch {
+ isCreatingInvoice = true
+
+ if (walletState.nodeLifecycleState == NodeLifecycleState.Starting) {
+ while (walletState.nodeLifecycleState == NodeLifecycleState.Starting && isActive) {
+ delay(5.milliseconds)
+ }
+ }
+
+ if (walletState.nodeLifecycleState == NodeLifecycleState.Running) {
+ runCatching {
+ val entry = blocktank.createCjit(amountSats = sats)
+ onCjitCreated(
+ CjitEntryDetails(
+ networkFeeSat = entry.networkFeeSat.toLong(),
+ serviceFeeSat = entry.serviceFeeSat.toLong(),
+ channelSizeSat = entry.channelSizeSat.toLong(),
+ feeSat = entry.feeSat.toLong(),
+ receiveAmountSats = satsAmount,
+ invoice = entry.invoice.request,
+ )
+ )
+ }.onFailure { e ->
+ app.toast(e)
+ Logger.error("Failed to create CJIT", e)
+ }
+
+ isCreatingInvoice = false
+ } else {
+ // TODO add missing localized texts
+ app.toast(
+ type = Toast.ToastType.WARNING,
+ title = "Lightning not ready",
+ description = "Lightning node must be running to create an invoice",
+ )
+ isCreatingInvoice = false
+ }
+ }
+ }
+ )
+}
+
+@Composable
+private fun ReceiveAmountContent(
+ input: String,
+ satsAmount: Long,
+ minCjitSats: Int?,
+ currencyUiState: CurrencyUiState,
+ isCreatingInvoice: Boolean,
+ modifier: Modifier = Modifier,
+ onInputChange: (String) -> Unit = {},
+ onClickMin: (Long) -> Unit = {},
+ onClickAmount: () -> Unit = {},
+ onBack: () -> Unit = {},
+ onContinue: () -> Unit = {},
+) {
Column(
- modifier = Modifier
+ modifier = modifier
.fillMaxSize()
.gradientBackground()
.navigationBarsPadding()
+ .testTag("ReceiveAmount")
) {
- SheetTopBar(stringResource(R.string.wallet__receive_bitcoin), onBack = onBack)
- Spacer(Modifier.height(24.dp))
-
- Column(
- modifier = Modifier.padding(horizontal = 16.dp)
- ) {
- AmountInput(
- primaryDisplay = currencies.primaryDisplay,
- showConversion = true,
- overrideSats = overrideSats,
- onSatsChange = { sats ->
- satsAmount = sats
- overrideSats = null
- },
- )
+ SheetTopBar(
+ titleText = stringResource(R.string.wallet__receive_bitcoin),
+ onBack = onBack,
+ )
- Spacer(modifier = Modifier.weight(1f))
+ BoxWithConstraints {
+ val maxHeight = this.maxHeight
- // Actions
- Row(
- verticalAlignment = Alignment.Bottom,
- modifier = Modifier.fillMaxWidth()
+ Column(
+ modifier = Modifier.padding(horizontal = 16.dp)
) {
- // Min amount view
- val minCjitSats by blocktank.minCjitSats.collectAsStateWithLifecycle()
- minCjitSats?.let { minCjitSats ->
- Column(
- modifier = Modifier.clickableAlpha { overrideSats = minCjitSats.toLong() }
- ) {
- Caption13Up(
- text = stringResource(R.string.wallet__minimum),
- color = Colors.White64,
- modifier = Modifier.testTag("ReceiveAmountMinimum")
- )
- Spacer(modifier = Modifier.height(8.dp))
- MoneySSB(sats = minCjitSats.toLong())
- }
- } ?: CircularProgressIndicator(modifier = Modifier.size(18.dp))
- Spacer(modifier = Modifier.weight(1f))
- UnitButton(modifier = Modifier.testTag("ReceiveNumberPadUnit"))
- }
+ VerticalSpacer(16.dp)
+ NumberPadTextField(
+ input = input,
+ displayUnit = currencyUiState.displayUnit,
+ primaryDisplay = currencyUiState.primaryDisplay,
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickableAlpha(onClick = onClickAmount)
+ .testTag("ReceiveNumberPadTextField")
+ )
- Spacer(modifier = Modifier.height(16.dp))
- HorizontalDivider()
+ FillHeight(min = 12.dp)
- Spacer(modifier = Modifier.height(16.dp))
- PrimaryButton(
- text = stringResource(R.string.common__continue),
- onClick = {
- val sats = satsAmount.toULong()
+ // Min amount row
+ Row(
+ verticalAlignment = Alignment.Bottom,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ minCjitSats?.let { minCjitSats ->
+ Column(
+ modifier = Modifier
+ .clickableAlpha { onClickMin(minCjitSats.toLong()) }
+ .testTag("ReceiveAmountMin")
+ ) {
+ Caption13Up(
+ text = stringResource(R.string.wallet__minimum),
+ color = Colors.White64,
+ )
+ VerticalSpacer(8.dp)
+ MoneySSB(sats = minCjitSats.toLong())
+ }
+ } ?: CircularProgressIndicator(modifier = Modifier.size(18.dp))
- scope.launch {
- isCreatingInvoice = true
+ FillWidth()
+ UnitButton(modifier = Modifier.testTag("ReceiveNumberPadUnit"))
+ }
- if (walletState.nodeLifecycleState == NodeLifecycleState.Starting) {
- while (walletState.nodeLifecycleState == NodeLifecycleState.Starting && isActive) {
- delay(500) // 0.5 second delay
- }
- }
+ VerticalSpacer(16.dp)
+ HorizontalDivider()
- if (walletState.nodeLifecycleState == NodeLifecycleState.Running) {
- try {
- val entry = blocktank.createCjit(amountSats = sats)
- onCjitCreated(
- CjitEntryDetails(
- networkFeeSat = entry.networkFeeSat.toLong(),
- serviceFeeSat = entry.serviceFeeSat.toLong(),
- channelSizeSat = entry.channelSizeSat.toLong(),
- feeSat = entry.feeSat.toLong(),
- receiveAmountSats = satsAmount,
- invoice = entry.invoice.request,
- )
- )
- } catch (e: Exception) {
- app.toast(e)
- Logger.error("Failed to create cjit", e)
- } finally {
- isCreatingInvoice = false
- }
- } else {
- app.toast(
- type = Toast.ToastType.WARNING,
- title = "Lightning not ready",
- description = "Lightning node must be running to create an invoice",
- )
- isCreatingInvoice = false
- }
- }
- },
- enabled = !isCreatingInvoice && satsAmount != 0L, // TODO if amount is valid
- isLoading = isCreatingInvoice,
+ Keyboard(
+ onClick = { number ->
+ onInputChange(if (input == "0") number else input + number)
+ },
+ onClickBackspace = {
+ onInputChange(if (input.length > 1) input.dropLast(1) else "0")
+ },
+ isDecimal = currencyUiState.primaryDisplay == PrimaryDisplay.FIAT,
+ availableHeight = maxHeight,
+ modifier = Modifier
+ .fillMaxWidth()
+ .testTag("ReceiveNumberPad")
+ )
+
+ PrimaryButton(
+ text = stringResource(R.string.common__continue),
+ enabled = !isCreatingInvoice && satsAmount != 0L,
+ isLoading = isCreatingInvoice,
+ onClick = onContinue,
+ modifier = Modifier.testTag("ContinueAmount")
+ )
+
+ VerticalSpacer(16.dp)
+ }
+ }
+ }
+}
+
+@Preview(showSystemUi = true)
+@Composable
+private fun Preview() {
+ AppThemeSurface {
+ BottomSheetPreview {
+ ReceiveAmountContent(
+ input = "100",
+ satsAmount = 10000L,
+ minCjitSats = 5000,
+ currencyUiState = CurrencyUiState(),
+ isCreatingInvoice = false,
+ modifier = Modifier.sheetHeight(),
+ )
+ }
+ }
+}
+
+@Preview(showSystemUi = true, device = NEXUS_5)
+@Composable
+private fun PreviewSmallScreen() {
+ AppThemeSurface {
+ BottomSheetPreview {
+ ReceiveAmountContent(
+ input = "100",
+ satsAmount = 10000L,
+ minCjitSats = 5000,
+ currencyUiState = CurrencyUiState(),
+ isCreatingInvoice = false,
+ modifier = Modifier.sheetHeight(),
)
- Spacer(modifier = Modifier.height(16.dp))
}
}
}
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt
index 61dd21de0..1bb53ae38 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt
@@ -264,7 +264,7 @@ private fun SendAmountNodeRunning(
onClick = { onClickMax(max) },
modifier = Modifier
.height(28.dp)
- .testTag("max_amount_button")
+ .testTag("SendAmountMax")
)
}
HorizontalSpacer(8.dp)
diff --git a/app/src/test/java/to/bitkit/repositories/LightningRepoTest.kt b/app/src/test/java/to/bitkit/repositories/LightningRepoTest.kt
index 432632472..f5c891933 100644
--- a/app/src/test/java/to/bitkit/repositories/LightningRepoTest.kt
+++ b/app/src/test/java/to/bitkit/repositories/LightningRepoTest.kt
@@ -33,12 +33,12 @@ import to.bitkit.models.ElectrumServer
import to.bitkit.models.LnPeer
import to.bitkit.models.NodeLifecycleState
import to.bitkit.models.TransactionSpeed
-import to.bitkit.services.LspNotificationsService
import to.bitkit.services.BlocktankService
import to.bitkit.services.CoreService
import to.bitkit.services.LdkNodeEventBus
import to.bitkit.services.LightningService
import to.bitkit.services.LnurlService
+import to.bitkit.services.LspNotificationsService
import to.bitkit.test.BaseUnitTest
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -209,11 +209,8 @@ class LightningRepoTest : BaseUnitTest() {
val testPeer = LnPeer("nodeId", "host", "9735")
val testChannelId = "testChannelId"
val channelAmountSats = 100000uL
- whenever(lightningService.openChannel(peer = testPeer, channelAmountSats, null)).thenReturn(
- Result.success(
- testChannelId
- )
- )
+ whenever(lightningService.openChannel(testPeer, channelAmountSats, null, null))
+ .thenReturn(Result.success(testChannelId))
val result = sut.openChannel(testPeer, channelAmountSats, null)
assertTrue(result.isSuccess)