Skip to content

Commit eb4e54c

Browse files
authored
Merge pull request #167 from synonymdev/feat/settings-general-quickpay
feat: QuickPay & QuickPay Settings
2 parents 11a4e02 + f660e75 commit eb4e54c

File tree

19 files changed

+911
-78
lines changed

19 files changed

+911
-78
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package to.bitkit.ui.settings.quickPay
2+
3+
import androidx.compose.ui.test.assertIsDisplayed
4+
import androidx.compose.ui.test.junit4.createComposeRule
5+
import androidx.compose.ui.test.onNodeWithTag
6+
import androidx.compose.ui.test.performClick
7+
import dagger.hilt.android.testing.HiltAndroidRule
8+
import dagger.hilt.android.testing.HiltAndroidTest
9+
import org.junit.Before
10+
import org.junit.Rule
11+
import org.junit.Test
12+
import to.bitkit.ui.theme.AppThemeSurface
13+
14+
@HiltAndroidTest
15+
class QuickPaySettingsScreenTest {
16+
17+
@get:Rule
18+
val composeTestRule = createComposeRule()
19+
20+
@get:Rule
21+
val hiltRule = HiltAndroidRule(this)
22+
23+
@Before
24+
fun setup() {
25+
hiltRule.inject()
26+
}
27+
28+
@Test
29+
fun whenScreenLoaded_shouldShowAllComponents() {
30+
composeTestRule.setContent {
31+
AppThemeSurface {
32+
QuickPaySettingsScreenContent(
33+
isQuickPayEnabled = true,
34+
quickPayAmount = 5,
35+
)
36+
}
37+
}
38+
39+
composeTestRule.onNodeWithTag("quickpay_toggle_switch").assertIsDisplayed()
40+
composeTestRule.onNodeWithTag("quickpay_amount_slider").assertIsDisplayed()
41+
}
42+
43+
@Test
44+
fun whenToggleSwitchClicked_shouldTriggerCallback() {
45+
var toggleCalled = false
46+
var toggleValue = false
47+
48+
composeTestRule.setContent {
49+
AppThemeSurface {
50+
QuickPaySettingsScreenContent(
51+
isQuickPayEnabled = false,
52+
quickPayAmount = 5,
53+
onToggleQuickPay = { enabled ->
54+
toggleCalled = true
55+
toggleValue = enabled
56+
},
57+
)
58+
}
59+
}
60+
61+
composeTestRule.onNodeWithTag("quickpay_toggle_switch")
62+
.performClick()
63+
64+
assert(toggleCalled)
65+
assert(toggleValue) // Should be true since we're toggling from false
66+
}
67+
}

app/src/main/java/to/bitkit/data/SettingsStore.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ data class SettingsData(
6363
val hasSeenShopIntro: Boolean = false,
6464
val hasSeenProfileIntro: Boolean = false,
6565
val quickPayIntroSeen: Boolean = false,
66+
val isQuickPayEnabled: Boolean = false,
67+
val quickPayAmount: Int = 5,
6668
val lightningSetupStep: Int = 0,
6769
val isPinEnabled: Boolean = false,
6870
val isPinOnLaunchEnabled: Boolean = false,

app/src/main/java/to/bitkit/models/Currency.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import java.text.DecimalFormatSymbols
88
import java.util.Locale
99

1010
const val BITCOIN_SYMBOL = ""
11+
const val SATS_IN_BTC = 100_000_000
1112

1213
@Serializable
1314
data class FxRateResponse(
@@ -51,7 +52,7 @@ data class ConvertedAmount(
5152
val flag: String,
5253
val sats: Long,
5354
) {
54-
val btcValue: BigDecimal = BigDecimal(sats).divide(BigDecimal(100_000_000))
55+
val btcValue: BigDecimal = BigDecimal(sats).divide(BigDecimal(SATS_IN_BTC))
5556

5657
data class BitcoinDisplayComponents(
5758
val symbol: String,

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import to.bitkit.async.ServiceQueue
55
import to.bitkit.data.BlocktankHttpClient
66
import to.bitkit.models.ConvertedAmount
77
import to.bitkit.models.FxRate
8+
import to.bitkit.models.SATS_IN_BTC
89
import to.bitkit.ui.utils.formatCurrency
910
import to.bitkit.utils.AppError
1011
import java.math.BigDecimal
1112
import java.math.RoundingMode
1213
import javax.inject.Inject
1314
import javax.inject.Singleton
1415
import kotlin.math.pow
16+
import kotlin.math.roundToLong
1517

1618
@Singleton
1719
class CurrencyService @Inject constructor(
@@ -52,7 +54,7 @@ class CurrencyService @Inject constructor(
5254
}
5355

5456
fun convert(sats: Long, rate: FxRate): ConvertedAmount? {
55-
val btcAmount = BigDecimal(sats).divide(BigDecimal(100_000_000))
57+
val btcAmount = BigDecimal(sats).divide(BigDecimal(SATS_IN_BTC))
5658
val value: BigDecimal = btcAmount.multiply(BigDecimal.valueOf(rate.rate))
5759

5860
val formatted = value.formatCurrency() ?: return null
@@ -69,15 +71,26 @@ class CurrencyService @Inject constructor(
6971

7072
fun convertFiatToSats(fiatValue: BigDecimal, rate: FxRate): ULong {
7173
val btcAmount = fiatValue.divide(BigDecimal.valueOf(rate.rate), 8, RoundingMode.HALF_UP)
72-
val satsDecimal = btcAmount.multiply(BigDecimal(100_000_000))
74+
val satsDecimal = btcAmount.multiply(BigDecimal(SATS_IN_BTC))
7375

7476
val roundedNumber = satsDecimal.setScale(0, RoundingMode.HALF_UP)
7577

7678
return roundedNumber.toLong().toULong()
7779
}
7880

79-
fun getAvailableCurrencies(rates: List<FxRate>): List<String> {
80-
return rates.map { it.quote }
81+
fun convertFiatToSats(fiatAmount: Double, currency: String, rates: List<FxRate>): Long {
82+
val rate = getCurrentRate(currency, rates) ?: return 0
83+
84+
// Convert the fiat amount to BTC, then to sats
85+
val btc = fiatAmount / rate.rate
86+
val sats = (btc * SATS_IN_BTC).roundToLong()
87+
88+
return sats
89+
}
90+
91+
suspend fun convertFiatToSats(fiatAmount: Double, currency: String): Long {
92+
val rates = cachedRates ?: fetchLatestRates()
93+
return convertFiatToSats(fiatAmount, currency, rates)
8194
}
8295

8396
fun getCurrentRate(currency: String, rates: List<FxRate>): FxRate? {

app/src/main/java/to/bitkit/ui/components/AmountInput.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ fun AmountInput(
168168
fiatAmount = TextFieldValue(newFiatAmount, TextRange(newFiatAmount.length))
169169

170170
newFiatAmount.toDoubleOrNull()?.let { fiatDouble ->
171-
currency.convertFiatToSats(fiatAmount = fiatDouble)?.let { convertedSats ->
171+
currency.convertFiatToSats(fiatAmount = fiatDouble).let { convertedSats ->
172172
val satsString = convertedSats.toString()
173173
satsAmount = TextFieldValue(satsString, TextRange(satsString.length))
174174
onSatsChange(convertedSats)

app/src/main/java/to/bitkit/ui/components/NumberPadTextField.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import to.bitkit.ext.removeSpaces
2424
import to.bitkit.models.BITCOIN_SYMBOL
2525
import to.bitkit.models.BitcoinDisplayUnit
2626
import to.bitkit.models.PrimaryDisplay
27+
import to.bitkit.models.SATS_IN_BTC
2728
import to.bitkit.models.formatToModernDisplay
2829
import to.bitkit.ui.currencyViewModel
2930
import to.bitkit.ui.theme.AppThemeSurface
@@ -56,8 +57,7 @@ fun NumberPadTextField(
5657
val currency = currencyViewModel ?: return
5758

5859
val satoshis = if (primaryDisplay == PrimaryDisplay.FIAT) {
59-
currency.convertFiatToSats(fiatAmount = input.replace(",", "").toDoubleOrNull() ?: 0.0)
60-
.toString()
60+
currency.convertFiatToSats(fiatAmount = input.replace(",", "").toDoubleOrNull() ?: 0.0).toString()
6161
} else {
6262
input.removeSpaces()
6363
}
@@ -152,7 +152,7 @@ fun AmountInputHandler(
152152
lastDisplay = primaryDisplay
153153
val newInput = when (primaryDisplay) {
154154
PrimaryDisplay.BITCOIN -> { //Convert fiat to sats
155-
val amountLong = currencyVM.convertFiatToSats(input.replace(",", "").toDoubleOrNull() ?: 0.0) ?: 0
155+
val amountLong = currencyVM.convertFiatToSats(input.replace(",", "").toDoubleOrNull() ?: 0.0)
156156
if (amountLong > 0.0) amountLong.toString() else ""
157157
}
158158

@@ -169,11 +169,11 @@ fun AmountInputHandler(
169169
LaunchedEffect(input) {
170170
val sats = when (primaryDisplay) {
171171
PrimaryDisplay.BITCOIN -> {
172-
if (displayUnit == BitcoinDisplayUnit.MODERN) input else (input.toLongOrDefault(0L) * 100_000_000).toString()
172+
if (displayUnit == BitcoinDisplayUnit.MODERN) input else (input.toLongOrDefault(0L) * SATS_IN_BTC).toString()
173173
}
174174

175175
PrimaryDisplay.FIAT -> {
176-
val convertedAmount = currencyVM.convertFiatToSats(input.replace(",", "").toDoubleOrNull() ?: 0.0) ?: 0L
176+
val convertedAmount = currencyVM.convertFiatToSats(input.replace(",", "").toDoubleOrNull() ?: 0.0)
177177
convertedAmount.toString()
178178
}
179179
}

0 commit comments

Comments
 (0)