Skip to content

Commit c14acad

Browse files
committed
Merge branch 'master' into feat/external-node
# Conflicts: # app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt
2 parents 14f40d6 + 0e914e7 commit c14acad

File tree

6 files changed

+144
-13
lines changed

6 files changed

+144
-13
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,14 @@ class ActivityService(
240240

241241
// MARK: - Tag Methods
242242

243-
suspend fun appendTags(toActivityId: String, tags: List<String>) {
244-
ServiceQueue.CORE.background {
245-
addTags(toActivityId, tags)
243+
suspend fun appendTags(toActivityId: String, tags: List<String>) : Result<Unit>{
244+
return try {
245+
ServiceQueue.CORE.background {
246+
addTags(toActivityId, tags)
247+
}
248+
Result.success(Unit)
249+
} catch (e: Exception) {
250+
Result.failure(e)
246251
}
247252
}
248253

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

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,41 @@ package to.bitkit.ui.components
22

33
import androidx.compose.foundation.border
44
import androidx.compose.foundation.clickable
5+
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.ExperimentalLayoutApi
8+
import androidx.compose.foundation.layout.Row
59
import androidx.compose.foundation.layout.padding
610
import androidx.compose.foundation.layout.wrapContentWidth
711
import androidx.compose.foundation.shape.RoundedCornerShape
12+
import androidx.compose.material3.Icon
813
import androidx.compose.material3.MaterialTheme
914
import androidx.compose.material3.Text
1015
import androidx.compose.runtime.Composable
16+
import androidx.compose.ui.Alignment
1117
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.res.painterResource
1219
import androidx.compose.ui.text.font.FontWeight
20+
import androidx.compose.ui.tooling.preview.Preview
1321
import androidx.compose.ui.unit.dp
22+
import to.bitkit.R
23+
import to.bitkit.ui.theme.AppThemeSurface
1424
import to.bitkit.ui.theme.Colors
1525

1626
@Composable
1727
fun TagButton(
1828
text: String,
1929
isSelected: Boolean,
30+
displayIconClose: Boolean = false,
2031
onClick: () -> Unit,
2132
modifier: Modifier = Modifier,
2233
) {
2334
val borderColor = if (isSelected) Colors.Brand else Colors.White16
2435
val textColor = if (isSelected) Colors.Brand else MaterialTheme.colorScheme.onSurface
2536

26-
Text(
27-
text = text,
28-
color = textColor,
29-
fontWeight = FontWeight.Medium,
37+
Row(
38+
verticalAlignment = Alignment.CenterVertically,
39+
horizontalArrangement = Arrangement.spacedBy(8.dp),
3040
modifier = modifier
3141
.wrapContentWidth()
3242
.border(
@@ -36,5 +46,34 @@ fun TagButton(
3646
)
3747
.clickable { onClick() }
3848
.padding(horizontal = 12.dp, vertical = 8.dp)
39-
)
49+
) {
50+
Text(
51+
text = text,
52+
color = textColor,
53+
fontWeight = FontWeight.Medium,
54+
)
55+
56+
if (displayIconClose) {
57+
Icon(
58+
painter = painterResource(R.drawable.ic_x),
59+
contentDescription = null,
60+
tint = Colors.White
61+
)
62+
}
63+
}
64+
}
65+
66+
67+
@OptIn(ExperimentalLayoutApi::class)
68+
@Preview
69+
@Composable
70+
private fun Preview() {
71+
AppThemeSurface {
72+
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
73+
TagButton("Selected", isSelected = true, onClick = {})
74+
TagButton("Not Selected", isSelected = false, onClick = {})
75+
TagButton("Selected With icon close", displayIconClose = true, isSelected = true, onClick = {})
76+
TagButton("Not Selected With icon close", displayIconClose = true, isSelected = false, onClick = {})
77+
}
78+
}
4079
}

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fun SendAndReviewScreen(
5858
onBack: () -> Unit,
5959
onEvent: (SendEvent) -> Unit,
6060
onClickAddTag: () -> Unit,
61+
onClickTag: (String) -> Unit,
6162
) {
6263
Column(
6364
modifier = Modifier.fillMaxSize()
@@ -97,7 +98,8 @@ fun SendAndReviewScreen(
9798
TagButton(
9899
text = tagText,
99100
isSelected = false,
100-
onClick = { }, //TODO IMPLEMENT IN OTHER PR
101+
displayIconClose = true,
102+
onClick = { onClickTag(tagText) },
101103
)
102104
}
103105
}
@@ -180,7 +182,7 @@ private fun OnChainDescription(
180182
)
181183
}
182184
Spacer(modifier = Modifier.weight(1f))
183-
HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
185+
HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
184186
}
185187
Column(
186188
modifier = Modifier
@@ -204,7 +206,7 @@ private fun OnChainDescription(
204206
BodySSB(text = "± 20-60 minutes") //TODO GET FROM STATE
205207
}
206208
Spacer(modifier = Modifier.weight(1f))
207-
HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
209+
HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
208210
}
209211

210212
}
@@ -323,6 +325,7 @@ private fun SendAndReviewPreview() {
323325
onBack = {},
324326
onEvent = {},
325327
onClickAddTag = {},
328+
onClickTag = {},
326329
)
327330
}
328331
}
@@ -338,6 +341,7 @@ private fun SendAndReviewPreview2() {
338341
address = "bcrt1qkgfgyxyqhvkdqh04sklnzxphmcds6vft6y7h0r",
339342
bolt11 = "lnbcrt1…",
340343
payMethod = SendMethod.ONCHAIN,
344+
selectedTags = listOf("car", "house", "uber"),
341345
decodedInvoice = LightningInvoice(
342346
bolt11 = "bcrt123",
343347
paymentHash = ByteArray(0),
@@ -353,6 +357,7 @@ private fun SendAndReviewPreview2() {
353357
onBack = {},
354358
onEvent = {},
355359
onClickAddTag = {},
360+
onClickTag = {},
356361
)
357362
}
358363
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ fun SendOptionsView(
106106
uiState = uiState,
107107
onBack = { navController.popBackStack() },
108108
onEvent = { appViewModel.setSendEvent(it) },
109-
onClickAddTag = { navController.navigate(SendRoute.AddTag) }
109+
onClickAddTag = { navController.navigate(SendRoute.AddTag) },
110+
onClickTag = { tag -> appViewModel.removeTag(tag) }
110111
)
111112
}
112113
composable<SendRoute.AddTag> {

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

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import androidx.compose.runtime.setValue
66
import androidx.lifecycle.ViewModel
77
import androidx.lifecycle.viewModelScope
88
import dagger.hilt.android.lifecycle.HiltViewModel
9+
import kotlinx.coroutines.Dispatchers
910
import kotlinx.coroutines.delay
1011
import kotlinx.coroutines.flow.MutableSharedFlow
1112
import kotlinx.coroutines.flow.MutableStateFlow
@@ -38,8 +39,11 @@ import to.bitkit.ui.components.BottomSheetType
3839
import to.bitkit.ui.screens.wallets.send.SendRoute
3940
import to.bitkit.ui.shared.toast.ToastEventBus
4041
import to.bitkit.utils.Logger
42+
import uniffi.bitkitcore.Activity
43+
import uniffi.bitkitcore.ActivityFilter
4144
import uniffi.bitkitcore.LightningInvoice
4245
import uniffi.bitkitcore.OnChainInvoice
46+
import uniffi.bitkitcore.PaymentType
4347
import uniffi.bitkitcore.Scanner
4448
import javax.inject.Inject
4549

@@ -103,6 +107,14 @@ class AppViewModel @Inject constructor(
103107
}
104108
}
105109

110+
fun removeTag(tag: String) {
111+
_sendUiState.update {
112+
it.copy(
113+
selectedTags = it.selectedTags.filterNot { tagItem -> tagItem == tag }
114+
)
115+
}
116+
}
117+
106118
private var scan: Scanner? = null
107119

108120
init {
@@ -454,6 +466,7 @@ class AppViewModel @Inject constructor(
454466
val result = sendOnchain(validatedAddress.address, amount)
455467
if (result.isSuccess) {
456468
val txId = result.getOrNull()
469+
attachTagsToActivity(paymentHashOrTxId = txId, type = ActivityFilter.ONCHAIN)
457470
Logger.info("Onchain send result txid: $txId")
458471
setSendEffect(
459472
SendEffect.PaymentSuccess(
@@ -481,6 +494,7 @@ class AppViewModel @Inject constructor(
481494
if (result.isSuccess) {
482495
val paymentHash = result.getOrNull()
483496
Logger.info("Lightning send result payment hash: $paymentHash")
497+
attachTagsToActivity(paymentHashOrTxId = paymentHash, type = ActivityFilter.LIGHTNING)
484498
setSendEffect(SendEffect.PaymentSuccess())
485499
resetSendState()
486500
} else {
@@ -492,6 +506,55 @@ class AppViewModel @Inject constructor(
492506
}
493507
}
494508

509+
private fun attachTagsToActivity(paymentHashOrTxId: String?, type: ActivityFilter) {
510+
val tags = _sendUiState.value.selectedTags
511+
Logger.debug("attachTagsToActivity $tags")
512+
if (tags.isEmpty()) {
513+
Logger.debug("selectedTags empty")
514+
return
515+
}
516+
517+
if (paymentHashOrTxId == null) {
518+
Logger.error(msg = "null paymentHashOrTxId")
519+
return
520+
}
521+
522+
viewModelScope.launch(Dispatchers.IO) {
523+
val activity = coreService.activity.get(filter = type, txType = PaymentType.SENT, limit = 1u).firstOrNull()
524+
525+
if (activity == null) {
526+
Logger.error(msg = "Activity not found")
527+
return@launch
528+
}
529+
530+
when (activity) {
531+
is Activity.Lightning -> {
532+
if (paymentHashOrTxId == activity.v1.id) {
533+
coreService.activity.appendTags(
534+
toActivityId = activity.v1.id,
535+
tags = tags
536+
).onFailure {
537+
Logger.error("Error attaching tags $tags")
538+
}
539+
} else {
540+
Logger.error("Different activity id. Expected: $paymentHashOrTxId found: ${activity.v1.id}")
541+
}
542+
}
543+
544+
is Activity.Onchain -> {
545+
if (paymentHashOrTxId == activity.v1.txId) {
546+
coreService.activity.appendTags(
547+
toActivityId = activity.v1.id,
548+
tags = tags
549+
)
550+
} else {
551+
Logger.error("Different txId. Expected: $paymentHashOrTxId found: ${activity.v1.txId}")
552+
}
553+
}
554+
}
555+
}
556+
}
557+
495558
private suspend fun sendOnchain(address: String, amount: ULong): Result<Txid> {
496559
return runCatching { lightningService.send(address = address, amount) }
497560
.onFailure {
@@ -520,6 +583,7 @@ class AppViewModel @Inject constructor(
520583
WatchResult.Continue()
521584
}
522585
}
586+
523587
is Event.PaymentFailed -> {
524588
if (event.paymentHash == hash) {
525589
val error = Exception(event.reason?.name ?: "Unknown payment failure reason")
@@ -528,6 +592,7 @@ class AppViewModel @Inject constructor(
528592
WatchResult.Continue()
529593
}
530594
}
595+
531596
else -> WatchResult.Continue()
532597
}
533598
}
@@ -640,7 +705,7 @@ data class SendUiState(
640705
val description: String = "",
641706
val isUnified: Boolean = false,
642707
val payMethod: SendMethod = SendMethod.ONCHAIN,
643-
val selectedTags: List<String> = listOf(), //TODO save tags in other PR
708+
val selectedTags: List<String> = listOf(), // TODO save tags in other PR
644709
val decodedInvoice: LightningInvoice? = null,
645710
)
646711

app/src/main/res/drawable/ic_x.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="16dp"
3+
android:height="16dp"
4+
android:viewportWidth="16"
5+
android:viewportHeight="16">
6+
<path
7+
android:pathData="M12.854,3.146C13.049,3.342 13.049,3.658 12.854,3.854L3.854,12.854C3.658,13.049 3.342,13.049 3.146,12.854C2.951,12.658 2.951,12.342 3.146,12.146L12.146,3.146C12.342,2.951 12.658,2.951 12.854,3.146Z"
8+
android:fillColor="#ffffff"
9+
android:fillAlpha="0.64"
10+
android:fillType="evenOdd"/>
11+
<path
12+
android:pathData="M3.146,3.146C3.342,2.951 3.658,2.951 3.854,3.146L12.854,12.146C13.049,12.342 13.049,12.658 12.854,12.854C12.658,13.049 12.342,13.049 12.146,12.854L3.146,3.854C2.951,3.658 2.951,3.342 3.146,3.146Z"
13+
android:fillColor="#ffffff"
14+
android:fillAlpha="0.64"
15+
android:fillType="evenOdd"/>
16+
</vector>

0 commit comments

Comments
 (0)