Adds recommended amounts for the Zap the Devs

Block error messages from closing the Zap split payment screen.
This commit is contained in:
Vitor Pamplona
2024-05-13 13:27:03 -04:00
parent 065ba1c165
commit 58ed27dc75
8 changed files with 119 additions and 31 deletions
@@ -103,7 +103,7 @@ class ZapPaymentHandler(val account: Account) {
} }
if (account.hasWalletConnectSetup()) { if (account.hasWalletConnectSetup()) {
payViaNWC(it.values.map { it.second }, note, onError, onProgress = { payViaNWC(it.values.map { it.invoice }, note, onError, onProgress = {
onProgress(it * 0.25f + 0.75f) // keeps within range. onProgress(it * 0.25f + 0.75f) // keeps within range.
}, context) { }, context) {
// onProgress(1f) // onProgress(1f)
@@ -113,9 +113,9 @@ class ZapPaymentHandler(val account: Account) {
it.map { it.map {
Payable( Payable(
info = it.key.first, info = it.key.first,
user = null, user = it.key.second.user,
amountMilliSats = it.value.first, amountMilliSats = it.value.zapValue,
invoice = it.value.second, invoice = it.value.invoice,
) )
}.toImmutableList(), }.toImmutableList(),
) )
@@ -136,28 +136,33 @@ class ZapPaymentHandler(val account: Account) {
return roundedZapValue return roundedZapValue
} }
class SignAllZapRequestsReturn(
val zapRequestJson: String,
val user: User? = null,
)
suspend fun signAllZapRequests( suspend fun signAllZapRequests(
note: Note, note: Note,
pollOption: Int?, pollOption: Int?,
message: String, message: String,
zapType: LnZapEvent.ZapType, zapType: LnZapEvent.ZapType,
zapsToSend: List<ZapSplitSetup>, zapsToSend: List<ZapSplitSetup>,
onAllDone: suspend (MutableMap<ZapSplitSetup, String>) -> Unit, onAllDone: suspend (MutableMap<ZapSplitSetup, SignAllZapRequestsReturn>) -> Unit,
) { ) {
collectSuccessfulSigningOperations<ZapSplitSetup, String>( collectSuccessfulSigningOperations<ZapSplitSetup, SignAllZapRequestsReturn>(
operationsInput = zapsToSend, operationsInput = zapsToSend,
runRequestFor = { next: ZapSplitSetup, onReady -> runRequestFor = { next: ZapSplitSetup, onReady ->
if (next.isLnAddress) { if (next.isLnAddress) {
prepareZapRequestIfNeeded(note, pollOption, message, zapType) { zapRequestJson -> prepareZapRequestIfNeeded(note, pollOption, message, zapType) { zapRequestJson ->
if (zapRequestJson != null) { if (zapRequestJson != null) {
onReady(zapRequestJson) onReady(SignAllZapRequestsReturn(zapRequestJson))
} }
} }
} else { } else {
val user = LocalCache.getUserIfExists(next.lnAddressOrPubKeyHex) val user = LocalCache.getUserIfExists(next.lnAddressOrPubKeyHex)
prepareZapRequestIfNeeded(note, pollOption, message, zapType, user) { zapRequestJson -> prepareZapRequestIfNeeded(note, pollOption, message, zapType, user) { zapRequestJson ->
if (zapRequestJson != null) { if (zapRequestJson != null) {
onReady(zapRequestJson) onReady(SignAllZapRequestsReturn(zapRequestJson, user))
} }
} }
} }
@@ -167,23 +172,23 @@ class ZapPaymentHandler(val account: Account) {
} }
suspend fun assembleAllInvoices( suspend fun assembleAllInvoices(
invoices: List<Pair<ZapSplitSetup, String>>, invoices: List<Pair<ZapSplitSetup, SignAllZapRequestsReturn>>,
totalAmountMilliSats: Long, totalAmountMilliSats: Long,
message: String, message: String,
onError: (String, String) -> Unit, onError: (String, String) -> Unit,
onProgress: (percent: Float) -> Unit, onProgress: (percent: Float) -> Unit,
context: Context, context: Context,
onAllDone: suspend (MutableMap<Pair<ZapSplitSetup, String>, Pair<Long, String>>) -> Unit, onAllDone: suspend (MutableMap<Pair<ZapSplitSetup, SignAllZapRequestsReturn>, AssembleInvoiceReturn>) -> Unit,
) { ) {
var progressAllPayments = 0.00f var progressAllPayments = 0.00f
val totalWeight = invoices.sumOf { it.first.weight } val totalWeight = invoices.sumOf { it.first.weight }
collectSuccessfulSigningOperations<Pair<ZapSplitSetup, String>, Pair<Long, String>>( collectSuccessfulSigningOperations<Pair<ZapSplitSetup, SignAllZapRequestsReturn>, AssembleInvoiceReturn>(
operationsInput = invoices, operationsInput = invoices,
runRequestFor = { splitZapRequestPair: Pair<ZapSplitSetup, String>, onReady -> runRequestFor = { splitZapRequestPair: Pair<ZapSplitSetup, SignAllZapRequestsReturn>, onReady ->
assembleInvoice( assembleInvoice(
splitSetup = splitZapRequestPair.first, splitSetup = splitZapRequestPair.first,
nostrZapRequest = splitZapRequestPair.second, nostrZapRequest = splitZapRequestPair.second.zapRequestJson,
zapValue = calculateZapValue(totalAmountMilliSats, splitZapRequestPair.first.weight, totalWeight), zapValue = calculateZapValue(totalAmountMilliSats, splitZapRequestPair.first.weight, totalWeight),
message = message, message = message,
onError = onError, onError = onError,
@@ -243,6 +248,11 @@ class ZapPaymentHandler(val account: Account) {
) )
} }
class AssembleInvoiceReturn(
val zapValue: Long,
val invoice: String,
)
private fun assembleInvoice( private fun assembleInvoice(
splitSetup: ZapSplitSetup, splitSetup: ZapSplitSetup,
nostrZapRequest: String, nostrZapRequest: String,
@@ -251,7 +261,7 @@ class ZapPaymentHandler(val account: Account) {
onError: (String, String) -> Unit, onError: (String, String) -> Unit,
onProgressStep: (percent: Float) -> Unit, onProgressStep: (percent: Float) -> Unit,
context: Context, context: Context,
onReady: (Pair<Long, String>) -> Unit, onReady: (AssembleInvoiceReturn) -> Unit,
) { ) {
var progressThisPayment = 0.00f var progressThisPayment = 0.00f
@@ -280,7 +290,7 @@ class ZapPaymentHandler(val account: Account) {
context = context, context = context,
onSuccess = { onSuccess = {
onProgressStep(1 - progressThisPayment) onProgressStep(1 - progressThisPayment)
onReady(Pair(zapValue, it)) onReady(AssembleInvoiceReturn(zapValue, it))
}, },
) )
} else { } else {
@@ -79,7 +79,7 @@ fun ClickableWithdrawal(withdrawalString: String) {
ClickableText( ClickableText(
text = withdraw, text = withdraw,
onClick = { payViaIntent(withdrawalString, context) { showErrorMessageDialog = it } }, onClick = { payViaIntent(withdrawalString, context, { }) { showErrorMessageDialog = it } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary),
) )
} }
@@ -169,7 +169,7 @@ fun InvoicePreview(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 10.dp), .padding(vertical = 10.dp),
onClick = { payViaIntent(lnInvoice, context) { showErrorMessageDialog = it } }, onClick = { payViaIntent(lnInvoice, context, { }) { showErrorMessageDialog = it } },
shape = QuoteBorder, shape = QuoteBorder,
colors = colors =
ButtonDefaults.buttonColors( ButtonDefaults.buttonColors(
@@ -439,6 +439,15 @@ fun ZapVote(
) )
} }
}, },
justShowError = {
scope.launch {
showErrorMessageDialog =
StringToastMsg(
context.getString(R.string.error_dialog_zap_error),
it,
)
}
},
) )
} }
@@ -985,7 +985,8 @@ fun ZapReaction(
if (wantsToZap) { if (wantsToZap) {
ZapAmountChoicePopup( ZapAmountChoicePopup(
baseNote = baseNote, baseNote = baseNote,
iconSize = iconSize, zapAmountChoices = accountViewModel.account.zapAmountChoices,
popupYOffset = iconSize,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onDismiss = { onDismiss = {
wantsToZap = false wantsToZap = false
@@ -1042,6 +1043,11 @@ fun ZapReaction(
showErrorMessageDialog = showErrorMessageDialog + it showErrorMessageDialog = showErrorMessageDialog + it
} }
}, },
justShowError = {
scope.launch {
showErrorMessageDialog = showErrorMessageDialog + it
}
},
) )
} }
@@ -1430,6 +1436,7 @@ private fun ActionableReactionButton(
@Composable @Composable
fun ZapAmountChoicePopup( fun ZapAmountChoicePopup(
baseNote: Note, baseNote: Note,
zapAmountChoices: List<Long>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
popupYOffset: Dp, popupYOffset: Dp,
onDismiss: () -> Unit, onDismiss: () -> Unit,
@@ -1449,7 +1456,7 @@ fun ZapAmountChoicePopup(
onDismissRequest = { onDismiss() }, onDismissRequest = { onDismiss() },
) { ) {
FlowRow(horizontalArrangement = Arrangement.Center) { FlowRow(horizontalArrangement = Arrangement.Center) {
accountViewModel.account.zapAmountChoices.forEach { amountInSats -> zapAmountChoices.forEach { amountInSats ->
Button( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
@@ -347,12 +347,12 @@ fun PayViaIntentDialog(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
onClose: () -> Unit, onClose: () -> Unit,
onError: (String) -> Unit, onError: (String) -> Unit,
justShowError: (String) -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current
if (payingInvoices.size == 1) { if (payingInvoices.size == 1) {
payViaIntent(payingInvoices.first().invoice, context, onError) payViaIntent(payingInvoices.first().invoice, context, onClose, onError)
onClose()
} else { } else {
Dialog( Dialog(
onDismissRequest = onClose, onDismissRequest = onClose,
@@ -422,9 +422,7 @@ fun PayViaIntentDialog(
Spacer(modifier = DoubleHorzSpacer) Spacer(modifier = DoubleHorzSpacer)
PayButton(isActive = !paid.value) { PayButton(isActive = !paid.value) {
paid.value = true payViaIntent(it.invoice, context, { paid.value = true }, justShowError)
payViaIntent(it.invoice, context, onError)
} }
} }
} }
@@ -437,6 +435,7 @@ fun PayViaIntentDialog(
fun payViaIntent( fun payViaIntent(
invoice: String, invoice: String,
context: Context, context: Context,
onPaid: () -> Unit,
onError: (String) -> Unit, onError: (String) -> Unit,
) { ) {
try { try {
@@ -444,6 +443,7 @@ fun payViaIntent(
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
ContextCompat.startActivity(context, intent, null) ContextCompat.startActivity(context, intent, null)
onPaid()
} catch (e: Exception) { } catch (e: Exception) {
if (e is CancellationException) throw e if (e is CancellationException) throw e
// don't display ugly error messages // don't display ugly error messages
@@ -20,6 +20,7 @@
*/ */
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import android.content.Context
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -306,7 +307,7 @@ fun ZapDonationButton(
animationSize: Dp = 14.dp, animationSize: Dp = 14.dp,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
var wantsToZap by remember { mutableStateOf(false) } var wantsToZap by remember { mutableStateOf<List<Long>?>(null) }
var showErrorMessageDialog by remember { mutableStateOf<String?>(null) } var showErrorMessageDialog by remember { mutableStateOf<String?>(null) }
var wantsToPay by var wantsToPay by
remember(baseNote) { remember(baseNote) {
@@ -323,14 +324,14 @@ fun ZapDonationButton(
Button( Button(
onClick = { onClick = {
zapClick( customZapClick(
baseNote, baseNote,
accountViewModel, accountViewModel,
context, context,
onZappingProgress = { progress: Float -> onZappingProgress = { progress: Float ->
scope.launch { zappingProgress = progress } scope.launch { zappingProgress = progress }
}, },
onMultipleChoices = { wantsToZap = true }, onMultipleChoices = { options -> wantsToZap = options },
onError = { _, message -> onError = { _, message ->
scope.launch { scope.launch {
zappingProgress = 0f zappingProgress = 0f
@@ -342,17 +343,18 @@ fun ZapDonationButton(
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
if (wantsToZap) { if (wantsToZap != null) {
ZapAmountChoicePopup( ZapAmountChoicePopup(
baseNote = baseNote, baseNote = baseNote,
zapAmountChoices = wantsToZap ?: accountViewModel.account.zapAmountChoices,
popupYOffset = iconSize, popupYOffset = iconSize,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onDismiss = { onDismiss = {
wantsToZap = false wantsToZap = null
zappingProgress = 0f zappingProgress = 0f
}, },
onChangeAmount = { onChangeAmount = {
wantsToZap = false wantsToZap = null
}, },
onError = { _, message -> onError = { _, message ->
scope.launch { scope.launch {
@@ -395,6 +397,11 @@ fun ZapDonationButton(
showErrorMessageDialog = it showErrorMessageDialog = it
} }
}, },
justShowError = {
scope.launch {
showErrorMessageDialog = it
}
},
) )
} }
@@ -448,3 +455,58 @@ fun ZapDonationButton(
} }
} }
} }
fun customZapClick(
baseNote: Note,
accountViewModel: AccountViewModel,
context: Context,
onZappingProgress: (Float) -> Unit,
onMultipleChoices: (List<Long>) -> Unit,
onError: (String, String) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
) {
if (baseNote.isDraft()) {
accountViewModel.toast(
R.string.draft_note,
R.string.it_s_not_possible_to_zap_to_a_draft_note,
)
return
}
if (accountViewModel.account.zapAmountChoices.isEmpty()) {
accountViewModel.toast(
context.getString(R.string.error_dialog_zap_error),
context.getString(R.string.no_zap_amount_setup_long_press_to_change),
)
} else if (!accountViewModel.isWriteable()) {
accountViewModel.toast(
context.getString(R.string.error_dialog_zap_error),
context.getString(R.string.login_with_a_private_key_to_be_able_to_send_zaps),
)
} else if (accountViewModel.account.zapAmountChoices.size == 1) {
val amount = accountViewModel.account.zapAmountChoices.first()
if (amount > 600) {
accountViewModel.zap(
baseNote,
amount * 1000,
null,
"",
context,
onError = onError,
onProgress = { onZappingProgress(it) },
zapType = accountViewModel.account.defaultZapType,
onPayViaIntent = onPayViaIntent,
)
} else {
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
// recommends amounts for a monthly release.
}
} else if (accountViewModel.account.zapAmountChoices.size > 1) {
if (accountViewModel.account.zapAmountChoices.any { it > 600 }) {
onMultipleChoices(accountViewModel.account.zapAmountChoices)
} else {
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
}
}
}
@@ -1193,7 +1193,7 @@ fun DisplayLNAddress(
} }
} }
} else { } else {
payViaIntent(it, context) { showErrorMessageDialog = it } payViaIntent(it, context, { zapExpanded = false }, { showErrorMessageDialog = it })
} }
}, },
onClose = { zapExpanded = false }, onClose = { zapExpanded = false },