Skip to content

Commit bc1d3f7

Browse files
committed
Merge branch 'master' into feat/transfer-from-spending
# Conflicts: # app/src/main/java/to/bitkit/viewmodels/BlocktankViewModel.kt
2 parents 75145ed + e05b0a3 commit bc1d3f7

File tree

25 files changed

+651
-315
lines changed

25 files changed

+651
-315
lines changed

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

Lines changed: 6 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7,103 +7,36 @@ import io.ktor.client.request.post
77
import io.ktor.client.request.setBody
88
import io.ktor.client.statement.HttpResponse
99
import io.ktor.http.isSuccess
10-
import kotlinx.serialization.Serializable
11-
import kotlinx.serialization.json.JsonObject
1210
import to.bitkit.env.Env
1311
import to.bitkit.models.FxRateResponse
14-
import to.bitkit.models.blocktank.RegtestCloseChannelRequest
15-
import to.bitkit.models.blocktank.RegtestDepositRequest
16-
import to.bitkit.models.blocktank.RegtestMineRequest
17-
import to.bitkit.models.blocktank.RegtestPayRequest
1812
import to.bitkit.utils.AppError
1913
import to.bitkit.utils.Logger
2014
import javax.inject.Inject
2115
import javax.inject.Singleton
2216

23-
private typealias IgnoreResponse = String
24-
2517
@Singleton
2618
class BlocktankHttpClient @Inject constructor(
2719
private val client: HttpClient,
2820
) {
29-
// region notifications
30-
suspend fun registerDevice(payload: RegisterDeviceRequest) {
31-
post<IgnoreResponse>("${Env.blocktankPushNotificationServer}/device", payload)
32-
}
33-
34-
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
35-
post<IgnoreResponse>("${Env.blocktankPushNotificationServer}/device/$deviceToken/test-notification", payload)
36-
}
37-
// endregion
38-
3921
// region rates
4022
suspend fun fetchLatestRates(): FxRateResponse {
4123
return get<FxRateResponse>(Env.btcRatesServer)
4224
}
4325
// endregion
4426

45-
// region regtest
46-
/**
47-
* Mines a number of blocks on the regtest network.
48-
* @param count Number of blocks to mine. Default is 1.
49-
*/
50-
suspend fun regtestMine(count: Int = 1) {
51-
val payload = RegtestMineRequest(count)
52-
post<IgnoreResponse>("${Env.blocktankClientServer}/regtest/chain/mine", payload)
53-
}
54-
55-
/**
56-
* Deposits a number of satoshis to an address on the regtest network.
57-
* @param address Address to deposit to.
58-
* @param amountSat Amount of satoshis to deposit. Default is 10,000,000.
59-
* @return Onchain transaction ID.
60-
*/
61-
suspend fun regtestDeposit(address: String, amountSat: Int = 10_000_000): String {
62-
val payload = RegtestDepositRequest(address = address, amountSat = amountSat)
63-
return post("${Env.blocktankClientServer}/regtest/chain/deposit", payload)
64-
}
65-
66-
/**
67-
* Pays an invoice on the regtest network.
68-
* @param invoice Invoice to pay.
69-
* @param amountSat Amount of satoshis to pay (only for 0-amount invoices).
70-
* @return Blocktank payment ID.
71-
*/
72-
suspend fun regtestPay(invoice: String, amountSat: Int? = null): String {
73-
val payload = RegtestPayRequest(invoice = invoice, amountSat = amountSat)
74-
return post("${Env.blocktankClientServer}/regtest/channel/pay", payload)
75-
}
76-
77-
/**
78-
* Closes a channel on the regtest network.
79-
* @param fundingTxId Funding transaction ID.
80-
* @param vout Funding transaction output index.
81-
* @param forceCloseAfterS Time in seconds to force-close the channel after. Default is 24 hours (86400). Set it to 0 for immediate force close.
82-
* @return Closing transaction ID.
83-
*/
84-
suspend fun regtestCloseChannel(fundingTxId: String, vout: Int, forceCloseAfterS: Int = 86400): String {
85-
val payload = RegtestCloseChannelRequest(
86-
fundingTxId = fundingTxId,
87-
vout = vout,
88-
forceCloseAfterS = forceCloseAfterS,
89-
)
90-
return post("${Env.blocktankClientServer}/regtest/channel/close", payload)
91-
}
92-
// endregion
93-
9427
// region utils
9528
private suspend inline fun <reified T> post(url: String, payload: Any? = null): T {
9629
val response = client.post(url) { payload?.let { setBody(it) } }
9730
Logger.debug("Http call: $response")
9831
return when (response.status.isSuccess()) {
9932
true -> {
10033
val responseBody = runCatching { response.body<T>() }.getOrElse {
101-
throw BlocktankErrorOld.InvalidResponse(it.message.orEmpty())
34+
throw BlocktankHttpError.InvalidResponse(it.message.orEmpty())
10235
}
10336
responseBody
10437
}
10538

106-
else -> throw BlocktankErrorOld.InvalidResponse(response.status.description)
39+
else -> throw BlocktankHttpError.InvalidResponse(response.status.description)
10740
}
10841
}
10942

@@ -117,39 +50,17 @@ class BlocktankHttpClient @Inject constructor(
11750
return when (response.status.isSuccess()) {
11851
true -> {
11952
val responseBody = runCatching { response.body<T>() }.getOrElse {
120-
throw BlocktankErrorOld.InvalidResponse(it.message.orEmpty())
53+
throw BlocktankHttpError.InvalidResponse(it.message.orEmpty())
12154
}
12255
responseBody
12356
}
12457

125-
else -> throw BlocktankErrorOld.InvalidResponse(response.status.description)
58+
else -> throw BlocktankHttpError.InvalidResponse(response.status.description)
12659
}
12760
}
12861
// endregion
12962
}
13063

131-
sealed class BlocktankErrorOld(message: String) : AppError(message) {
132-
data class InvalidResponse(override val message: String) : BlocktankErrorOld(message)
133-
}
134-
135-
@Serializable
136-
data class RegisterDeviceRequest(
137-
val deviceToken: String,
138-
val publicKey: String,
139-
val features: List<String>,
140-
val nodeId: String,
141-
val isoTimestamp: String,
142-
val signature: String,
143-
)
144-
145-
@Serializable
146-
data class TestNotificationRequest(
147-
val data: Data,
148-
) {
149-
@Serializable
150-
data class Data(
151-
val source: String,
152-
val type: String,
153-
val payload: JsonObject,
154-
)
64+
sealed class BlocktankHttpError(message: String) : AppError(message) {
65+
data class InvalidResponse(override val message: String) : BlocktankHttpError(message)
15566
}

app/src/main/java/to/bitkit/ext/String.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ fun String.truncate(length: Int): String {
1717
this
1818
}.trim()
1919
}
20+
21+
fun String.removeSpaces() = this.filterNot { it.isWhitespace() }

app/src/main/java/to/bitkit/models/blocktank/Regtest.kt

Lines changed: 0 additions & 27 deletions
This file was deleted.

app/src/main/java/to/bitkit/services/BlocktankServiceOld.kt renamed to app/src/main/java/to/bitkit/services/BlocktankNotificationsService.kt

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package to.bitkit.services
22

33
import kotlinx.coroutines.CoroutineDispatcher
4-
import kotlinx.serialization.json.JsonObject
5-
import kotlinx.serialization.json.JsonPrimitive
64
import to.bitkit.async.BaseCoroutineScope
75
import to.bitkit.async.ServiceQueue
8-
import to.bitkit.data.BlocktankHttpClient
9-
import to.bitkit.data.RegisterDeviceRequest
10-
import to.bitkit.data.TestNotificationRequest
116
import to.bitkit.data.keychain.Keychain
127
import to.bitkit.data.keychain.Keychain.Key
138
import to.bitkit.di.BgDispatcher
@@ -22,15 +17,13 @@ import javax.inject.Inject
2217
import javax.inject.Singleton
2318

2419
@Singleton
25-
class BlocktankServiceOld @Inject constructor(
20+
class BlocktankNotificationsService @Inject constructor(
2621
@BgDispatcher bgDispatcher: CoroutineDispatcher,
27-
private val blocktankHttpClient: BlocktankHttpClient,
2822
private val lightningService: LightningService,
2923
private val keychain: Keychain,
3024
private val crypto: Crypto,
3125
) : BaseCoroutineScope(bgDispatcher) {
3226

33-
// region notifications
3427
suspend fun registerDevice(deviceToken: String) {
3528
val nodeId = lightningService.nodeId ?: throw ServiceError.NodeNotStarted
3629

@@ -51,17 +44,16 @@ class BlocktankServiceOld @Inject constructor(
5144
}
5245
keychain.save(Key.PUSH_NOTIFICATION_PRIVATE_KEY.name, keypair.privateKey)
5346

54-
val payload = RegisterDeviceRequest(
55-
deviceToken = deviceToken,
56-
publicKey = publicKey,
57-
features = Env.pushNotificationFeatures.map { it.toString() },
58-
nodeId = nodeId,
59-
isoTimestamp = "$timestamp",
60-
signature = signature,
61-
)
62-
6347
ServiceQueue.CORE.background {
64-
blocktankHttpClient.registerDevice(payload)
48+
uniffi.bitkitcore.registerDevice(
49+
deviceToken = deviceToken,
50+
publicKey = publicKey,
51+
features = Env.pushNotificationFeatures.map { it.toString() },
52+
nodeId = nodeId,
53+
isoTimestamp = "$timestamp",
54+
signature = signature,
55+
customUrl = Env.blocktankPushNotificationServer,
56+
)
6557
}
6658

6759
// Cache token so we can avoid re-registering
@@ -76,21 +68,13 @@ class BlocktankServiceOld @Inject constructor(
7668
suspend fun testNotification(deviceToken: String) {
7769
Logger.debug("Sending test notification to self…")
7870

79-
val payload = TestNotificationRequest(
80-
data = TestNotificationRequest.Data(
81-
source = "blocktank",
82-
type = "incomingHtlc",
83-
payload = JsonObject(
84-
mapOf(
85-
"secretMessage" to JsonPrimitive("hello")
86-
)
87-
)
88-
)
89-
)
90-
9171
ServiceQueue.CORE.background {
92-
blocktankHttpClient.testNotification(deviceToken, payload)
72+
uniffi.bitkitcore.testNotification(
73+
deviceToken = deviceToken,
74+
secretMessage = "hello",
75+
notificationType = "incomingHtlc",
76+
customUrl = Env.blocktankPushNotificationServer,
77+
)
9378
}
9479
}
95-
// endregion
9680
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ class CoreService @Inject constructor(
8484
}
8585

8686
try {
87-
updateBlocktankUrl(newUrl = Env.blocktankClientServer)
88-
Logger.info("Blocktank URL updated to ${Env.blocktankClientServer}")
87+
val blocktankUrl = Env.blocktankClientServer
88+
updateBlocktankUrl(newUrl = blocktankUrl)
89+
Logger.info("Blocktank URL updated to $blocktankUrl")
8990
} catch (e: Exception) {
9091
Logger.error("Failed to update Blocktank URL", e)
9192
}
@@ -405,9 +406,7 @@ class BlocktankService(
405406
options: CreateOrderOptions,
406407
): IBtOrder {
407408
return ServiceQueue.CORE.background {
408-
createOrder(
409-
lspBalanceSat = lspBalanceSat, channelExpiryWeeks = channelExpiryWeeks, options = options,
410-
)
409+
createOrder(lspBalanceSat = lspBalanceSat, channelExpiryWeeks = channelExpiryWeeks, options = options)
411410
}
412411
}
413412

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

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,16 @@ import androidx.compose.ui.Modifier
1616
import androidx.compose.ui.focus.FocusRequester
1717
import androidx.compose.ui.focus.focusRequester
1818
import androidx.compose.ui.res.stringResource
19-
import androidx.compose.ui.text.font.FontWeight
19+
import androidx.compose.ui.tooling.preview.Preview
2020
import androidx.compose.ui.unit.dp
2121
import to.bitkit.R
22-
import to.bitkit.viewmodels.SendEvent
23-
import to.bitkit.viewmodels.SendUiState
22+
import to.bitkit.ui.components.Caption13Up
2423
import to.bitkit.ui.components.PrimaryButton
2524
import to.bitkit.ui.scaffold.SheetTopBar
26-
import to.bitkit.ui.shared.util.DarkModePreview
27-
import to.bitkit.ui.shared.util.LightModePreview
2825
import to.bitkit.ui.theme.AppTextFieldDefaults
2926
import to.bitkit.ui.theme.AppThemeSurface
27+
import to.bitkit.viewmodels.SendEvent
28+
import to.bitkit.viewmodels.SendUiState
3029

3130
@Composable
3231
fun SendAddressScreen(
@@ -47,25 +46,21 @@ fun SendAddressScreen(
4746
val focusRequester = remember { FocusRequester() }
4847
LaunchedEffect(Unit) { focusRequester.requestFocus() }
4948

50-
Text(
51-
text = stringResource(R.string.wallet__send_to),
52-
style = MaterialTheme.typography.labelSmall,
53-
fontWeight = FontWeight.Normal,
54-
)
55-
Spacer(modifier = Modifier.height(4.dp))
49+
Caption13Up(text = stringResource(R.string.wallet__send_to))
50+
Spacer(modifier = Modifier.height(16.dp))
5651
TextField(
5752
placeholder = { Text(stringResource(R.string.address_placeholder)) },
5853
value = uiState.addressInput,
5954
onValueChange = { onEvent(SendEvent.AddressChange(it)) },
6055
minLines = 12,
61-
maxLines = 12,
6256
colors = AppTextFieldDefaults.noIndicatorColors,
63-
shape = MaterialTheme.shapes.medium,
57+
shape = MaterialTheme.shapes.small,
6458
modifier = Modifier
6559
.fillMaxWidth()
60+
.weight(1f)
6661
.focusRequester(focusRequester),
6762
)
68-
Spacer(modifier = Modifier.weight(1f))
63+
Spacer(modifier = Modifier.height(16.dp))
6964
PrimaryButton(
7065
text = stringResource(R.string.continue_button),
7166
enabled = uiState.isAddressInputValid,
@@ -77,10 +72,9 @@ fun SendAddressScreen(
7772

7873
}
7974

80-
@LightModePreview
81-
@DarkModePreview
75+
@Preview(showSystemUi = true)
8276
@Composable
83-
private fun SendEnterManuallyScreenPreview() {
77+
private fun Preview() {
8478
AppThemeSurface {
8579
SendAddressScreen(
8680
uiState = SendUiState(),
@@ -89,3 +83,19 @@ private fun SendEnterManuallyScreenPreview() {
8983
)
9084
}
9185
}
86+
87+
@Preview(showSystemUi = true)
88+
@Composable
89+
private fun Preview2() {
90+
AppThemeSurface {
91+
SendAddressScreen(
92+
uiState = SendUiState(
93+
address = "bc1q5f29hzkgp6hqla63m32pa0jyfqq32c20837cz4",
94+
addressInput = "bc1q5f29hzkgp6hqla63m32pa0jyfqq32c20837cz4",
95+
isAddressInputValid = true
96+
),
97+
onBack = {},
98+
onEvent = {},
99+
)
100+
}
101+
}

0 commit comments

Comments
 (0)