feat: surface on-chain zaps from the reactions zap button
Adds a second row of bitcoin-orange chips to the zap amount popup that opens the existing OnchainZapSendDialog prefilled with the chosen amount and the note's author + zappedEvent. The on-chain stack (kind 8333, OnchainZapSender, Account.sendOnchainZap, OnchainZapSendDialog) already existed end-to-end; only the entry point from the reactions row and the on-chain preset amounts were missing. Settings: - AccountZapPreferencesInternal: new onchainZapAmountChoices (defaults 10k/50k/250k sats); back-compat via kotlinx.serialization defaults - AccountSyncedSettings / AccountSettings / Account.updateZapAmounts: wired through end-to-end alongside the existing Lightning list - UpdateZapAmountDialog: new "Quick On-chain Zap Amounts" section with its own chip list and add-amount field; the existing zap-privacy dropdown stays Lightning-only since on-chain has no zap type Reactions UI: - ZapAmountChoicePopup now collects both choice lists and offers an on-chain row in addition to the Lightning one - New on-chain callback opens OnchainZapSendDialog with the prefilled amount; the existing dialog gains a prefillAmountSats parameter and now reads the on-chain preset list for its own quick-pick chips - Other zap entry points (ReusableZapButton, NestActionBar, FilteredZapAmountChoicePopup) are unchanged: on-chain row defaults off so they keep their existing Lightning-only behavior
This commit is contained in:
@@ -603,12 +603,14 @@ class Account(
|
||||
|
||||
suspend fun updateZapAmounts(
|
||||
amountSet: List<Long>,
|
||||
onchainAmountSet: List<Long>,
|
||||
selectedZapType: LnZapEvent.ZapType,
|
||||
nip47Update: Nip47WalletConnect.Nip47URINorm?,
|
||||
) {
|
||||
var changed = false
|
||||
|
||||
if (settings.changeZapAmounts(amountSet)) changed = true
|
||||
if (settings.changeOnchainZapAmounts(onchainAmountSet)) changed = true
|
||||
if (settings.changeDefaultZapType(selectedZapType)) changed = true
|
||||
if (settings.changeZapPaymentRequest(nip47Update)) changed = true
|
||||
|
||||
|
||||
@@ -267,6 +267,15 @@ class AccountSettings(
|
||||
return false
|
||||
}
|
||||
|
||||
fun changeOnchainZapAmounts(newAmounts: List<Long>): Boolean {
|
||||
if (syncedSettings.zaps.onchainZapAmountChoices.value != newAmounts) {
|
||||
syncedSettings.zaps.onchainZapAmountChoices.tryEmit(newAmounts.toImmutableList())
|
||||
saveAccountSettings()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun changeReactionTypes(newTypes: List<String>): Boolean {
|
||||
if (syncedSettings.reactions.reactionChoices.value != newTypes) {
|
||||
syncedSettings.reactions.reactionChoices.tryEmit(newTypes.toImmutableList())
|
||||
|
||||
@@ -40,6 +40,7 @@ class AccountSyncedSettings(
|
||||
val zaps =
|
||||
AccountZapPreferences(
|
||||
MutableStateFlow(internalSettings.zaps.zapAmountChoices.toImmutableList()),
|
||||
MutableStateFlow(internalSettings.zaps.onchainZapAmountChoices.toImmutableList()),
|
||||
MutableStateFlow(internalSettings.zaps.defaultZapType),
|
||||
)
|
||||
val languages =
|
||||
@@ -69,6 +70,7 @@ class AccountSyncedSettings(
|
||||
zaps =
|
||||
AccountZapPreferencesInternal(
|
||||
zaps.zapAmountChoices.value,
|
||||
zaps.onchainZapAmountChoices.value,
|
||||
zaps.defaultZapType.value,
|
||||
),
|
||||
languages =
|
||||
@@ -107,6 +109,11 @@ class AccountSyncedSettings(
|
||||
zaps.zapAmountChoices.tryEmit(newZapChoices)
|
||||
}
|
||||
|
||||
val newOnchainZapChoices = syncedSettingsInternal.zaps.onchainZapAmountChoices.toImmutableList()
|
||||
if (!equalImmutableLists(zaps.onchainZapAmountChoices.value, newOnchainZapChoices)) {
|
||||
zaps.onchainZapAmountChoices.tryEmit(newOnchainZapChoices)
|
||||
}
|
||||
|
||||
if (zaps.defaultZapType.value != syncedSettingsInternal.zaps.defaultZapType) {
|
||||
zaps.defaultZapType.tryEmit(syncedSettingsInternal.zaps.defaultZapType)
|
||||
}
|
||||
@@ -175,6 +182,7 @@ class AccountVideoPlayerPreferences(
|
||||
@Stable
|
||||
class AccountZapPreferences(
|
||||
var zapAmountChoices: MutableStateFlow<ImmutableList<Long>>,
|
||||
var onchainZapAmountChoices: MutableStateFlow<ImmutableList<Long>>,
|
||||
val defaultZapType: MutableStateFlow<LnZapEvent.ZapType>,
|
||||
)
|
||||
|
||||
|
||||
+2
@@ -38,6 +38,7 @@ val DefaultReactions =
|
||||
)
|
||||
|
||||
val DefaultZapAmounts = listOf(100L, 500L, 1000L)
|
||||
val DefaultOnchainZapAmounts = listOf(10_000L, 50_000L, 250_000L)
|
||||
val DefaultReportWarningThreshold = 5
|
||||
|
||||
@Serializable
|
||||
@@ -153,6 +154,7 @@ class AccountReactionPreferencesInternal(
|
||||
@Serializable
|
||||
class AccountZapPreferencesInternal(
|
||||
var zapAmountChoices: List<Long> = DefaultZapAmounts,
|
||||
var onchainZapAmountChoices: List<Long> = DefaultOnchainZapAmounts,
|
||||
val defaultZapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PUBLIC,
|
||||
)
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.routeReplyTo
|
||||
import com.vitorpamplona.amethyst.ui.note.types.EditState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.PaymentTargetsDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.OnchainZapSendDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
@@ -172,6 +173,7 @@ import com.vitorpamplona.amethyst.ui.theme.reactionBox
|
||||
import com.vitorpamplona.amethyst.ui.theme.ripple24dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.selectedReactionBoxModifier
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||
@@ -1121,6 +1123,11 @@ private fun likeClick(
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private data class OnchainZapRequest(
|
||||
val amountSats: Long?,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalUuidApi::class)
|
||||
fun ZapReaction(
|
||||
@@ -1135,6 +1142,9 @@ fun ZapReaction(
|
||||
) {
|
||||
var wantsToZap by remember { mutableStateOf(false) }
|
||||
var wantsToSetCustomZap by remember { mutableStateOf(false) }
|
||||
// null = closed; OnchainZapRequest(amount=null) = open with no prefill;
|
||||
// OnchainZapRequest(amount=N) = open prefilled to N sats.
|
||||
var onchainZapRequest by remember { mutableStateOf<OnchainZapRequest?>(null) }
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -1207,6 +1217,10 @@ fun ZapReaction(
|
||||
nav.nav(Route.UpdateZapAmount())
|
||||
}
|
||||
},
|
||||
onOnchainAmount = { amount ->
|
||||
wantsToZap = false
|
||||
onchainZapRequest = OnchainZapRequest(amount)
|
||||
},
|
||||
onError = { _, message, user ->
|
||||
scope.launch {
|
||||
zappingProgress = 0f
|
||||
@@ -1230,6 +1244,16 @@ fun ZapReaction(
|
||||
)
|
||||
}
|
||||
|
||||
onchainZapRequest?.let { request ->
|
||||
OnchainZapSendDialog(
|
||||
accountViewModel = accountViewModel,
|
||||
onDismiss = { onchainZapRequest = null },
|
||||
recipientPubKey = baseNote.author?.pubkeyHex,
|
||||
zappedEvent = baseNote.toEventHint<Event>(),
|
||||
prefillAmountSats = request.amountSats,
|
||||
)
|
||||
}
|
||||
|
||||
if (wantsToSetCustomZap) {
|
||||
ZapCustomDialog(
|
||||
onZapStarts = { zapStartingTime = TimeUtils.now() },
|
||||
@@ -1834,12 +1858,29 @@ fun ZapAmountChoicePopup(
|
||||
onError: (title: String, text: String, user: User?) -> Unit,
|
||||
onProgress: (percent: Float) -> Unit,
|
||||
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
|
||||
onOnchainAmount: ((Long?) -> Unit)? = null,
|
||||
) {
|
||||
val zapAmountChoices by
|
||||
accountViewModel.account.settings.syncedSettings.zaps.zapAmountChoices
|
||||
.collectAsStateWithLifecycle()
|
||||
val onchainZapAmountChoices by
|
||||
accountViewModel.account.settings.syncedSettings.zaps.onchainZapAmountChoices
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
ZapAmountChoicePopup(baseNote, zapAmountChoices, accountViewModel, popupYOffset, onZapStarts, onDismiss, onChangeAmount, onError, onProgress, onPayViaIntent)
|
||||
ZapAmountChoicePopup(
|
||||
baseNote = baseNote,
|
||||
zapAmountChoices = zapAmountChoices,
|
||||
accountViewModel = accountViewModel,
|
||||
popupYOffset = popupYOffset,
|
||||
onZapStarts = onZapStarts,
|
||||
onDismiss = onDismiss,
|
||||
onChangeAmount = onChangeAmount,
|
||||
onError = onError,
|
||||
onProgress = onProgress,
|
||||
onPayViaIntent = onPayViaIntent,
|
||||
onchainZapAmountChoices = if (onOnchainAmount != null) onchainZapAmountChoices else persistentListOf(),
|
||||
onOnchainAmount = onOnchainAmount ?: {},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1854,9 +1895,11 @@ fun ZapAmountChoicePopup(
|
||||
onError: (title: String, text: String, user: User?) -> Unit,
|
||||
onProgress: (percent: Float) -> Unit,
|
||||
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
|
||||
onchainZapAmountChoices: ImmutableList<Long> = persistentListOf(),
|
||||
onOnchainAmount: (Long?) -> Unit = {},
|
||||
) {
|
||||
val visibilityState = rememberVisibilityState(onDismiss)
|
||||
ZapAmountChoicePopup(baseNote, zapAmountChoices, accountViewModel, popupYOffset, visibilityState, onZapStarts, onChangeAmount, onError, onProgress, onPayViaIntent)
|
||||
ZapAmountChoicePopup(baseNote, zapAmountChoices, onchainZapAmountChoices, accountViewModel, popupYOffset, visibilityState, onZapStarts, onChangeAmount, onOnchainAmount, onError, onProgress, onPayViaIntent)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
|
||||
@@ -1864,11 +1907,13 @@ fun ZapAmountChoicePopup(
|
||||
fun ZapAmountChoicePopup(
|
||||
baseNote: Note,
|
||||
zapAmountChoices: ImmutableList<Long>,
|
||||
onchainZapAmountChoices: ImmutableList<Long>,
|
||||
accountViewModel: AccountViewModel,
|
||||
popupYOffset: Dp,
|
||||
visibilityState: MutableTransitionState<Boolean>,
|
||||
onZapStarts: () -> Unit,
|
||||
onChangeAmount: () -> Unit,
|
||||
onOnchainAmount: (Long?) -> Unit,
|
||||
onError: (title: String, text: String, user: User?) -> Unit,
|
||||
onProgress: (percent: Float) -> Unit,
|
||||
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
|
||||
@@ -1889,6 +1934,7 @@ fun ZapAmountChoicePopup(
|
||||
) {
|
||||
ZapAmountChoicePopupContent(
|
||||
zapAmountChoices = zapAmountChoices,
|
||||
onchainZapAmountChoices = onchainZapAmountChoices,
|
||||
onZap = { amountInSats ->
|
||||
onZapStarts()
|
||||
accountViewModel.zap(
|
||||
@@ -1905,6 +1951,10 @@ fun ZapAmountChoicePopup(
|
||||
visibilityState.targetState = false
|
||||
},
|
||||
onChangeAmount = onChangeAmount,
|
||||
onOnchainAmount = { amount ->
|
||||
onOnchainAmount(amount)
|
||||
visibilityState.targetState = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1916,6 +1966,8 @@ fun ZapAmountChoicePopupContent(
|
||||
zapAmountChoices: ImmutableList<Long>,
|
||||
onZap: (Long) -> Unit,
|
||||
onChangeAmount: () -> Unit,
|
||||
onchainZapAmountChoices: ImmutableList<Long> = persistentListOf(),
|
||||
onOnchainAmount: (Long?) -> Unit = {},
|
||||
) {
|
||||
Box(HalfPadding, contentAlignment = Center) {
|
||||
ElevatedCard(
|
||||
@@ -1923,33 +1975,66 @@ fun ZapAmountChoicePopupContent(
|
||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 8.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
|
||||
) {
|
||||
FlowRow(
|
||||
modifier = Modifier.padding(horizontal = 5.dp, vertical = 5.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
itemVerticalAlignment = CenterVertically,
|
||||
) {
|
||||
zapAmountChoices.forEach { amountInSats ->
|
||||
ZapAmountChip(
|
||||
amountInSats = amountInSats,
|
||||
onClick = { onZap(amountInSats) },
|
||||
onLongClick = onChangeAmount,
|
||||
)
|
||||
}
|
||||
ClickableBox(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 4.dp, vertical = 6.dp)
|
||||
.size(32.dp)
|
||||
.padding(7.dp),
|
||||
onClick = onChangeAmount,
|
||||
Column {
|
||||
FlowRow(
|
||||
modifier = Modifier.padding(horizontal = 5.dp, vertical = 5.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
itemVerticalAlignment = CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Tune,
|
||||
contentDescription = stringRes(R.string.quick_zap_amounts),
|
||||
modifier = Size18Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
zapAmountChoices.forEach { amountInSats ->
|
||||
ZapAmountChip(
|
||||
amountInSats = amountInSats,
|
||||
onClick = { onZap(amountInSats) },
|
||||
onLongClick = onChangeAmount,
|
||||
)
|
||||
}
|
||||
ClickableBox(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 4.dp, vertical = 6.dp)
|
||||
.size(32.dp)
|
||||
.padding(7.dp),
|
||||
onClick = onChangeAmount,
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Tune,
|
||||
contentDescription = stringRes(R.string.quick_zap_amounts),
|
||||
modifier = Size18Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (onchainZapAmountChoices.isNotEmpty()) {
|
||||
FlowRow(
|
||||
modifier = Modifier.padding(horizontal = 5.dp, vertical = 5.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
itemVerticalAlignment = CenterVertically,
|
||||
) {
|
||||
onchainZapAmountChoices.forEach { amountInSats ->
|
||||
OnchainZapAmountChip(
|
||||
amountInSats = amountInSats,
|
||||
onClick = { onOnchainAmount(amountInSats) },
|
||||
onLongClick = onChangeAmount,
|
||||
)
|
||||
}
|
||||
ClickableBox(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 4.dp, vertical = 6.dp)
|
||||
.size(32.dp)
|
||||
.padding(7.dp),
|
||||
onClick = { onOnchainAmount(null) },
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Tune,
|
||||
contentDescription = stringRes(R.string.quick_zap_amounts_onchain),
|
||||
modifier = Size18Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1992,14 +2077,52 @@ private fun ZapAmountChip(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun OnchainZapAmountChip(
|
||||
amountInSats: Long,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
shape = ButtonBorder,
|
||||
color = BitcoinOrange,
|
||||
modifier = Modifier.padding(horizontal = 4.dp, vertical = 6.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||
.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||
verticalAlignment = CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.CurrencyBitcoin,
|
||||
contentDescription = null,
|
||||
modifier = Size18Modifier,
|
||||
tint = Color.White,
|
||||
)
|
||||
Spacer(Modifier.width(2.dp))
|
||||
Text(
|
||||
text = showAmount(amountInSats.toBigDecimal().setScale(1)),
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ZapAmountChoicePopupPreview() {
|
||||
ThemeComparisonColumn {
|
||||
ZapAmountChoicePopupContent(
|
||||
zapAmountChoices = persistentListOf(50L, 100L, 500L, 1_000L, 5_000L, 10_000L, 100_000L),
|
||||
onchainZapAmountChoices = persistentListOf(10_000L, 50_000L, 250_000L),
|
||||
onZap = {},
|
||||
onChangeAmount = {},
|
||||
onOnchainAmount = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +306,96 @@ fun UpdateZapAmountContent(
|
||||
)
|
||||
}
|
||||
|
||||
// ── Section 2: Zap Privacy ────────────────────────────────────────────
|
||||
// ── Section 2: Quick On-chain Zap Amounts ─────────────────────────────
|
||||
|
||||
Text(
|
||||
text = stringRes(R.string.quick_zap_amounts_onchain),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = SettingsCategorySpacingModifier,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.quick_zap_amounts_onchain_explainer),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(bottom = 6.dp),
|
||||
)
|
||||
|
||||
FlowRow(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.animateContentSize(animationSpec = spring(stiffness = Spring.StiffnessMediumLow)),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
postViewModel.onchainAmountSet.forEach { amountInSats ->
|
||||
InputChip(
|
||||
selected = false,
|
||||
onClick = { postViewModel.removeOnchainAmount(amountInSats) },
|
||||
label = {
|
||||
Text(
|
||||
text = "₿ ${showAmount(amountInSats.toBigDecimal().setScale(1))}",
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Close,
|
||||
contentDescription = stringRes(R.string.remove),
|
||||
modifier = Modifier.size(InputChipDefaults.AvatarSize),
|
||||
)
|
||||
},
|
||||
colors =
|
||||
InputChipDefaults.inputChipColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f),
|
||||
labelColor = MaterialTheme.colorScheme.primary,
|
||||
trailingIconColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.new_amount_in_sats_onchain)) },
|
||||
value = postViewModel.nextOnchainAmount,
|
||||
onValueChange = { postViewModel.nextOnchainAmount = it },
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(
|
||||
capitalization = KeyboardCapitalization.None,
|
||||
keyboardType = KeyboardType.Number,
|
||||
),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "10000, 50000, 250000",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
IconButton(
|
||||
onClick = postViewModel::addOnchainAmount,
|
||||
shape = ButtonBorder,
|
||||
enabled = postViewModel.nextOnchainAmount.text.isNotBlank(),
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AddCircle,
|
||||
contentDescription = stringRes(R.string.add),
|
||||
modifier = Size20Modifier,
|
||||
)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
||||
// ── Section 3: Zap Privacy ────────────────────────────────────────────
|
||||
|
||||
Text(
|
||||
text = stringRes(R.string.zap_privacy_section),
|
||||
|
||||
+20
-1
@@ -41,6 +41,8 @@ class UpdateZapAmountViewModel : ViewModel() {
|
||||
|
||||
var nextAmount by mutableStateOf(TextFieldValue(""))
|
||||
var amountSet by mutableStateOf(listOf<Long>())
|
||||
var nextOnchainAmount by mutableStateOf(TextFieldValue(""))
|
||||
var onchainAmountSet by mutableStateOf(listOf<Long>())
|
||||
var walletConnectRelay by mutableStateOf(TextFieldValue(""))
|
||||
var walletConnectPubkey by mutableStateOf(TextFieldValue(""))
|
||||
var walletConnectSecret by mutableStateOf(TextFieldValue(""))
|
||||
@@ -59,6 +61,7 @@ class UpdateZapAmountViewModel : ViewModel() {
|
||||
|
||||
fun load() {
|
||||
this.amountSet = accountViewModel.account.settings.syncedSettings.zaps.zapAmountChoices.value
|
||||
this.onchainAmountSet = accountViewModel.account.settings.syncedSettings.zaps.onchainZapAmountChoices.value
|
||||
this.selectedZapType = accountViewModel.account.settings.syncedSettings.zaps.defaultZapType.value
|
||||
|
||||
val nip47 = accountViewModel.account.settings.defaultZapPaymentRequest()
|
||||
@@ -83,6 +86,19 @@ class UpdateZapAmountViewModel : ViewModel() {
|
||||
amountSet = amountSet - amount
|
||||
}
|
||||
|
||||
fun addOnchainAmount() {
|
||||
val newValue = nextOnchainAmount.text.trim().toLongOrNull()
|
||||
if (newValue != null) {
|
||||
onchainAmountSet = onchainAmountSet + newValue
|
||||
}
|
||||
|
||||
nextOnchainAmount = TextFieldValue("")
|
||||
}
|
||||
|
||||
fun removeOnchainAmount(amount: Long) {
|
||||
onchainAmountSet = onchainAmountSet - amount
|
||||
}
|
||||
|
||||
fun sendPost() {
|
||||
accountViewModel.launchSigner {
|
||||
sendPostSuspend()
|
||||
@@ -116,13 +132,15 @@ class UpdateZapAmountViewModel : ViewModel() {
|
||||
null
|
||||
}
|
||||
|
||||
accountViewModel.account.updateZapAmounts(amountSet, selectedZapType, nip47Update)
|
||||
accountViewModel.account.updateZapAmounts(amountSet, onchainAmountSet, selectedZapType, nip47Update)
|
||||
|
||||
nextAmount = TextFieldValue("")
|
||||
nextOnchainAmount = TextFieldValue("")
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
nextAmount = TextFieldValue("")
|
||||
nextOnchainAmount = TextFieldValue("")
|
||||
}
|
||||
|
||||
fun hasChanged(): Boolean {
|
||||
@@ -130,6 +148,7 @@ class UpdateZapAmountViewModel : ViewModel() {
|
||||
return (
|
||||
selectedZapType != accountViewModel.account.settings.syncedSettings.zaps.defaultZapType.value ||
|
||||
amountSet != accountViewModel.account.settings.syncedSettings.zaps.zapAmountChoices.value ||
|
||||
onchainAmountSet != accountViewModel.account.settings.syncedSettings.zaps.onchainZapAmountChoices.value ||
|
||||
walletConnectPubkey.text != (defaultUri?.pubKeyHex ?: "") ||
|
||||
walletConnectRelay.text != (defaultUri?.relayUri?.url ?: "") ||
|
||||
walletConnectSecret.text != (defaultUri?.secret ?: "")
|
||||
|
||||
+2
-1
@@ -1236,9 +1236,10 @@ class AccountViewModel(
|
||||
|
||||
fun updateZapAmounts(
|
||||
amountSet: List<Long>,
|
||||
onchainAmountSet: List<Long>,
|
||||
selectedZapType: LnZapEvent.ZapType,
|
||||
nip47Update: Nip47WalletConnect.Nip47URINorm?,
|
||||
) = launchSigner { account.updateZapAmounts(amountSet, selectedZapType, nip47Update) }
|
||||
) = launchSigner { account.updateZapAmounts(amountSet, onchainAmountSet, selectedZapType, nip47Update) }
|
||||
|
||||
fun toggleDontTranslateFrom(languageCode: String) = launchSigner { account.toggleDontTranslateFrom(languageCode) }
|
||||
|
||||
|
||||
+4
-5
@@ -127,6 +127,7 @@ fun OnchainZapSendDialog(
|
||||
onDismiss: () -> Unit,
|
||||
recipientPubKey: HexKey? = null,
|
||||
zappedEvent: EventHintBundle<out Event>? = null,
|
||||
prefillAmountSats: Long? = null,
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -141,7 +142,7 @@ fun OnchainZapSendDialog(
|
||||
|
||||
var searchInput by remember { mutableStateOf("") }
|
||||
var selectedUser by remember { mutableStateOf<User?>(null) }
|
||||
var amountInput by remember { mutableStateOf("") }
|
||||
var amountInput by remember { mutableStateOf(prefillAmountSats?.toString().orEmpty()) }
|
||||
var comment by remember { mutableStateOf("") }
|
||||
var feeTier by remember { mutableStateOf(FeeTier.NORMAL) }
|
||||
var fees by remember { mutableStateOf<FeeEstimates?>(null) }
|
||||
@@ -155,10 +156,8 @@ fun OnchainZapSendDialog(
|
||||
runCatching { withContext(Dispatchers.IO) { backend.feeEstimates() } }.getOrNull()
|
||||
}
|
||||
|
||||
val presetAmounts =
|
||||
remember(accountViewModel) {
|
||||
accountViewModel.zapAmountChoices()
|
||||
}
|
||||
val presetAmounts by accountViewModel.account.settings.syncedSettings.zaps.onchainZapAmountChoices
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
// Mirror the dropdown's NIP-05 / Namecoin (.bit) resolution so Send can
|
||||
// enable as soon as the typed name resolves, without forcing the user to
|
||||
|
||||
@@ -821,6 +821,9 @@
|
||||
<string name="wallet_connect_manual_config">Advanced: enter connection details manually</string>
|
||||
<string name="quick_zap_amounts">Quick Zap Amounts</string>
|
||||
<string name="quick_zap_amounts_explainer">Shown when pressing the zap button. Tap an amount to remove it. If you leave it empty, it will open the dialog to insert an amount every time.</string>
|
||||
<string name="quick_zap_amounts_onchain">Quick On-chain Zap Amounts</string>
|
||||
<string name="quick_zap_amounts_onchain_explainer">On-chain zaps pay miner fees, so amounts are usually larger than Lightning. Tap an amount to remove it.</string>
|
||||
<string name="new_amount_in_sats_onchain">New on-chain amount in sats</string>
|
||||
<string name="zap_privacy_section">Zap Privacy</string>
|
||||
<string name="zap_type_section_explainer">Controls how your identity is shown when you send a zap.</string>
|
||||
<string name="wallet_connect_connect_app">Connect Wallet</string>
|
||||
|
||||
Reference in New Issue
Block a user