Showing progress bars when Zaps are requested

This commit is contained in:
Vitor Pamplona
2023-03-22 14:24:33 -04:00
parent ba40b1815f
commit 1e8bc10ad1
4 changed files with 104 additions and 25 deletions

View File

@@ -130,12 +130,14 @@ class LightningAddressResolver {
)
}
fun lnAddressInvoice(lnaddress: String, milliSats: Long, message: String, nostrRequest: String? = null, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
fun lnAddressInvoice(lnaddress: String, milliSats: Long, message: String, nostrRequest: String? = null, onSuccess: (String) -> Unit, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit) {
val mapper = jacksonObjectMapper()
fetchLightningAddressJson(
lnaddress,
onSuccess = { lnAddressJson ->
onProgress(0.4f)
val lnurlp = try {
mapper.readTree(lnAddressJson)
} catch (t: Throwable) {
@@ -158,6 +160,8 @@ class LightningAddressResolver {
message,
if (allowsNostr) nostrRequest else null,
onSuccess = {
onProgress(0.6f)
val lnInvoice = try {
mapper.readTree(it)
} catch (t: Throwable) {
@@ -169,6 +173,7 @@ class LightningAddressResolver {
// Forces LN Invoice amount to be the requested amount.
val invoiceAmount = LnInvoiceUtil.getAmountInSats(pr)
if (invoiceAmount.multiply(BigDecimal(1000)).toLong() == BigDecimal(milliSats).toLong()) {
onProgress(0.7f)
onSuccess(pr)
} else {
onError("Incorrect invoice amount (${invoiceAmount.toLong()} sats) from server")

View File

@@ -149,6 +149,8 @@ fun InvoiceRequest(lud16: String, toUserPubKeyHex: String, account: Account, onC
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
onClose()
}
},
onProgress = {
}
)
},

View File

@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
@@ -33,6 +34,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
@@ -307,7 +309,10 @@ fun ZapReaction(
val context = LocalContext.current
val scope = rememberCoroutineScope()
var zappingProgress by remember { mutableStateOf(0f) }
Row(
verticalAlignment = CenterVertically,
modifier = Modifier
.then(Modifier.size(20.dp))
.combinedClickable(
@@ -336,17 +341,26 @@ fun ZapReaction(
.show()
}
} else if (account.zapAmountChoices.size == 1) {
accountViewModel.zap(
baseNote,
account.zapAmountChoices.first() * 1000,
"",
context
) {
scope.launch {
Toast
.makeText(context, it, Toast.LENGTH_SHORT)
.show()
}
scope.launch(Dispatchers.IO) {
accountViewModel.zap(
baseNote,
account.zapAmountChoices.first() * 1000,
"",
context,
onError = {
scope.launch {
zappingProgress = 0f
Toast
.makeText(context, it, Toast.LENGTH_SHORT)
.show()
}
},
onProgress = {
scope.launch(Dispatchers.Main) {
zappingProgress = it
}
}
)
}
} else if (account.zapAmountChoices.size > 1) {
wantsToZap = true
@@ -363,6 +377,7 @@ fun ZapReaction(
accountViewModel,
onDismiss = {
wantsToZap = false
zappingProgress = 0f
},
onChangeAmount = {
wantsToZap = false
@@ -370,8 +385,14 @@ fun ZapReaction(
},
onError = {
scope.launch {
zappingProgress = 0f
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}
},
onProgress = {
scope.launch(Dispatchers.Main) {
zappingProgress = it
}
}
)
}
@@ -380,6 +401,7 @@ fun ZapReaction(
}
if (zappedNote?.isZappedBy(account.userProfile()) == true) {
zappingProgress = 1f
Icon(
imageVector = Icons.Default.Bolt,
contentDescription = stringResource(R.string.zaps),
@@ -387,12 +409,19 @@ fun ZapReaction(
tint = BitcoinOrange
)
} else {
Icon(
imageVector = Icons.Outlined.Bolt,
contentDescription = stringResource(id = R.string.zaps),
modifier = Modifier.size(20.dp),
tint = grayTint
)
if (zappingProgress < 0.1 || zappingProgress > 0.99) {
Icon(
imageVector = Icons.Outlined.Bolt,
contentDescription = stringResource(id = R.string.zaps),
modifier = Modifier.size(20.dp),
tint = grayTint
)
} else {
CircularProgressIndicator(
progress = zappingProgress,
modifier = Modifier.size(15.dp)
)
}
}
}
@@ -485,12 +514,21 @@ private fun BoostTypeChoicePopup(baseNote: Note, accountViewModel: AccountViewMo
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
@Composable
fun ZapAmountChoicePopup(baseNote: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit, onChangeAmount: () -> Unit, onError: (text: String) -> Unit) {
fun ZapAmountChoicePopup(
baseNote: Note,
accountViewModel: AccountViewModel,
onDismiss: () -> Unit,
onChangeAmount: () -> Unit,
onError: (text: String) -> Unit,
onProgress: (percent: Float) -> Unit
) {
val context = LocalContext.current
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val scope = rememberCoroutineScope()
Popup(
alignment = Alignment.BottomCenter,
offset = IntOffset(0, -50),
@@ -501,8 +539,17 @@ fun ZapAmountChoicePopup(baseNote: Note, accountViewModel: AccountViewModel, onD
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = {
accountViewModel.zap(baseNote, amountInSats * 1000, "", context, onError)
onDismiss()
scope.launch(Dispatchers.IO) {
accountViewModel.zap(
baseNote,
amountInSats * 1000,
"",
context,
onError,
onProgress
)
onDismiss()
}
},
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults
@@ -516,8 +563,17 @@ fun ZapAmountChoicePopup(baseNote: Note, accountViewModel: AccountViewModel, onD
textAlign = TextAlign.Center,
modifier = Modifier.combinedClickable(
onClick = {
accountViewModel.zap(baseNote, amountInSats * 1000, "", context, onError)
onDismiss()
scope.launch(Dispatchers.IO) {
accountViewModel.zap(
baseNote,
amountInSats * 1000,
"",
context,
onError,
onProgress
)
onDismiss()
}
},
onLongClick = {
onChangeAmount()

View File

@@ -7,6 +7,7 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AccountState
@@ -14,6 +15,9 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
import com.vitorpamplona.amethyst.service.model.ReportEvent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Locale
class AccountViewModel(private val account: Account) : ViewModel() {
@@ -48,7 +52,7 @@ class AccountViewModel(private val account: Account) : ViewModel() {
account.delete(account.boostsTo(note))
}
fun zap(note: Note, amount: Long, message: String, context: Context, onError: (String) -> Unit) {
suspend fun zap(note: Note, amount: Long, message: String, context: Context, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit) {
val lud16 = note.author?.info?.lud16?.trim() ?: note.author?.info?.lud06?.trim()
if (lud16.isNullOrBlank()) {
@@ -58,22 +62,34 @@ class AccountViewModel(private val account: Account) : ViewModel() {
val zapRequest = account.createZapRequestFor(note)
onProgress(0.10f)
LightningAddressResolver().lnAddressInvoice(
lud16,
amount,
message,
zapRequest?.toJson(),
onSuccess = {
onProgress(0.7f)
if (account.hasWalletConnectSetup()) {
account.sendZapPaymentRequestFor(it)
onProgress(0.8f)
// Awaits for the event to come back to LocalCache.
viewModelScope.launch(Dispatchers.IO) {
delay(1000)
onProgress(0f)
}
} else {
runCatching {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("lightning:$it"))
ContextCompat.startActivity(context, intent, null)
}
onProgress(0f)
}
},
onError = onError
onError = onError,
onProgress = onProgress
)
}