Turn the Pay zaps via intent dialog into a full screen with route

This commit is contained in:
Vitor Pamplona
2025-11-15 16:41:47 -05:00
parent fef78cd557
commit f7e72c57d3
7 changed files with 140 additions and 140 deletions
@@ -50,12 +50,14 @@ import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.ZapPaymentHandler import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.note.ObserveZapIcon import com.vitorpamplona.amethyst.ui.note.ObserveZapIcon
import com.vitorpamplona.amethyst.ui.note.PayViaIntentDialog
import com.vitorpamplona.amethyst.ui.note.ZapAmountChoicePopup import com.vitorpamplona.amethyst.ui.note.ZapAmountChoicePopup
import com.vitorpamplona.amethyst.ui.note.ZapIcon import com.vitorpamplona.amethyst.ui.note.ZapIcon
import com.vitorpamplona.amethyst.ui.note.ZappedIcon import com.vitorpamplona.amethyst.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.ui.note.payViaIntent
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ModifierWidth3dp import com.vitorpamplona.amethyst.ui.theme.ModifierWidth3dp
@@ -64,11 +66,12 @@ import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
/** /**
* Configuration for zap button behavior and appearance * Configuration for zap button behavior and appearance
@@ -91,6 +94,7 @@ data class ZapButtonCallbacks(
val onZapComplete: ((Boolean) -> Unit)? = null, val onZapComplete: ((Boolean) -> Unit)? = null,
) )
@OptIn(ExperimentalUuidApi::class)
@Composable @Composable
fun ReusableZapButton( fun ReusableZapButton(
baseNote: Note, baseNote: Note,
@@ -100,9 +104,6 @@ fun ReusableZapButton(
callbacks: ZapButtonCallbacks = ZapButtonCallbacks(), callbacks: ZapButtonCallbacks = ZapButtonCallbacks(),
) { ) {
var wantsToZap by remember { mutableStateOf<ImmutableList<Long>?>(null) } var wantsToZap by remember { mutableStateOf<ImmutableList<Long>?>(null) }
var wantsToPay by remember(baseNote) {
mutableStateOf<ImmutableList<ZapPaymentHandler.Payable>>(persistentListOf())
}
// Makes sure the user is loaded to get his ln address ahead of time (for DVM buttons) // Makes sure the user is loaded to get his ln address ahead of time (for DVM buttons)
if (config.showUserFinderSubscription) { if (config.showUserFinderSubscription) {
@@ -138,7 +139,19 @@ fun ReusableZapButton(
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, toUser) accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, toUser)
} }
}, },
onPayViaIntent = { wantsToPay = it }, onPayViaIntent = {
if (it.size == 1) {
val payable = it.first()
payViaIntent(payable.invoice, context, { }) {
zappingProgress = 0f
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, UserBasedErrorMessage(it, payable.info.user))
}
} else {
val uid = Uuid.random().toString()
accountViewModel.tempManualPaymentCache.put(uid, it)
nav.nav(Route.ManualZapSplitPayment(uid))
}
},
) )
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -166,25 +179,17 @@ fun ReusableZapButton(
onProgress = { onProgress = {
scope.launch(Dispatchers.Main) { zappingProgress = it } scope.launch(Dispatchers.Main) { zappingProgress = it }
}, },
onPayViaIntent = { wantsToPay = it }, onPayViaIntent = {
) if (it.size == 1) {
} val payable = it.first()
payViaIntent(payable.invoice, context, { }) {
if (wantsToPay.isNotEmpty()) {
PayViaIntentDialog(
payingInvoices = wantsToPay,
accountViewModel = accountViewModel,
onClose = { wantsToPay = persistentListOf() },
onError = {
wantsToPay = persistentListOf()
scope.launch {
zappingProgress = 0f zappingProgress = 0f
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it) accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, UserBasedErrorMessage(it, payable.info.user))
} }
}, } else {
justShowError = { val uid = Uuid.random().toString()
scope.launch { accountViewModel.tempManualPaymentCache.put(uid, it)
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it) nav.nav(Route.ManualZapSplitPayment(uid))
} }
}, },
) )
@@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.routes.getRouteWithArguments import com.vitorpamplona.amethyst.ui.navigation.routes.getRouteWithArguments
import com.vitorpamplona.amethyst.ui.navigation.routes.isBaseRoute import com.vitorpamplona.amethyst.ui.navigation.routes.isBaseRoute
import com.vitorpamplona.amethyst.ui.navigation.routes.isSameRoute import com.vitorpamplona.amethyst.ui.navigation.routes.isSameRoute
import com.vitorpamplona.amethyst.ui.note.PayViaIntentScreen
import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
@@ -137,6 +138,8 @@ fun AppNavigation(
composableFromBottomArgs<Route.PeopleListMetadataEdit> { PeopleListMetadataScreen(it.dTag, accountViewModel, nav) } composableFromBottomArgs<Route.PeopleListMetadataEdit> { PeopleListMetadataScreen(it.dTag, accountViewModel, nav) }
composableFromBottomArgs<Route.FollowPackMetadataEdit> { FollowPackMetadataScreen(it.dTag, accountViewModel, nav) } composableFromBottomArgs<Route.FollowPackMetadataEdit> { FollowPackMetadataScreen(it.dTag, accountViewModel, nav) }
composableFromBottomArgs<Route.ManualZapSplitPayment> { PayViaIntentScreen(it.paymentId, accountViewModel, nav) }
composableFromBottomArgs<Route.EditProfile> { NewUserMetadataScreen(nav, accountViewModel) } composableFromBottomArgs<Route.EditProfile> { NewUserMetadataScreen(nav, accountViewModel) }
composable<Route.Search> { SearchScreen(accountViewModel, nav) } composable<Route.Search> { SearchScreen(accountViewModel, nav) }
@@ -252,6 +252,11 @@ sealed class Route {
val version: String? = null, val version: String? = null,
val draft: String? = null, val draft: String? = null,
) : Route() ) : Route()
@Serializable
data class ManualZapSplitPayment(
val paymentId: String,
) : Route()
} }
inline fun <reified T : Route> isBaseRoute(navController: NavHostController): Boolean = navController.currentBackStackEntry?.destination?.hasRoute<T>() == true inline fun <reified T : Route> isBaseRoute(navController: NavHostController): Boolean = navController.currentBackStackEntry?.destination?.hasRoute<T>() == true
@@ -310,6 +315,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? {
dest.hasRoute<Route.PeopleListManagement>() -> entry.toRoute<Route.PeopleListManagement>() dest.hasRoute<Route.PeopleListManagement>() -> entry.toRoute<Route.PeopleListManagement>()
dest.hasRoute<Route.NewGroupDM>() -> entry.toRoute<Route.NewGroupDM>() dest.hasRoute<Route.NewGroupDM>() -> entry.toRoute<Route.NewGroupDM>()
dest.hasRoute<Route.UserSettings>() -> entry.toRoute<Route.UserSettings>() dest.hasRoute<Route.UserSettings>() -> entry.toRoute<Route.UserSettings>()
dest.hasRoute<Route.ManualZapSplitPayment>() -> entry.toRoute<Route.ManualZapSplitPayment>()
else -> { else -> {
null null
@@ -52,7 +52,6 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@@ -81,6 +80,7 @@ import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.components.toasts.StringToastMsg import com.vitorpamplona.amethyst.ui.components.toasts.StringToastMsg
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.routes.routeToMessage import com.vitorpamplona.amethyst.ui.navigation.routes.routeToMessage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
@@ -102,14 +102,14 @@ import com.vitorpamplona.quartz.nip01Core.core.ImmutableListOfLists
import com.vitorpamplona.quartz.nip01Core.core.toImmutableListOfLists import com.vitorpamplona.quartz.nip01Core.core.toImmutableListOfLists
import com.vitorpamplona.quartz.nip31Alts.AltTag import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
@Preview @Preview
@Composable @Composable
@@ -499,7 +499,7 @@ private fun RenderOptionBeforeVote(
} }
@Composable @Composable
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class, ExperimentalUuidApi::class)
fun ZapVote( fun ZapVote(
baseNote: Note, baseNote: Note,
poolOption: PollOption, poolOption: PollOption,
@@ -513,14 +513,8 @@ fun ZapVote(
val isLoggedUser by remember { derivedStateOf { accountViewModel.isLoggedUser(baseNote.author) } } val isLoggedUser by remember { derivedStateOf { accountViewModel.isLoggedUser(baseNote.author) } }
var wantsToZap by remember { mutableStateOf(false) } var wantsToZap by remember { mutableStateOf(false) }
var wantsToPay by remember {
mutableStateOf<ImmutableList<ZapPaymentHandler.Payable>>(
persistentListOf(),
)
}
var zappingProgress by remember { mutableFloatStateOf(0f) } var zappingProgress by remember { mutableFloatStateOf(0f) }
var zapStartingTime by remember { mutableLongStateOf(0L) }
var showErrorMessageDialog by remember { mutableStateOf<StringToastMsg?>(null) } var showErrorMessageDialog by remember { mutableStateOf<StringToastMsg?>(null) }
@@ -563,7 +557,6 @@ fun ZapVote(
accountViewModel.zapAmountChoices().size == 1 && accountViewModel.zapAmountChoices().size == 1 &&
pollViewModel.isValidInputVoteAmount(accountViewModel.zapAmountChoices().first()) pollViewModel.isValidInputVoteAmount(accountViewModel.zapAmountChoices().first())
) { ) {
zapStartingTime = TimeUtils.now()
accountViewModel.zap( accountViewModel.zap(
baseNote, baseNote,
accountViewModel.zapAmountChoices().first() * 1000, accountViewModel.zapAmountChoices().first() * 1000,
@@ -584,12 +577,13 @@ fun ZapVote(
), ),
) { ) {
if (wantsToZap) { if (wantsToZap) {
val context = LocalContext.current
FilteredZapAmountChoicePopup( FilteredZapAmountChoicePopup(
baseNote, baseNote,
accountViewModel, accountViewModel,
pollViewModel, pollViewModel,
poolOption.option, poolOption.option,
onZapStarts = { zapStartingTime = TimeUtils.now() }, onZapStarts = { },
onDismiss = { onDismiss = {
wantsToZap = false wantsToZap = false
zappingProgress = 0f zappingProgress = 0f
@@ -600,33 +594,17 @@ fun ZapVote(
zappingProgress = 0f zappingProgress = 0f
}, },
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } }, onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
onPayViaIntent = { wantsToPay = it }, onPayViaIntent = {
) if (it.size == 1) {
} val payable = it.first()
payViaIntent(payable.invoice, context, { }) { error ->
if (wantsToPay.isNotEmpty()) {
PayViaIntentDialog(
payingInvoices = wantsToPay,
accountViewModel = accountViewModel,
onClose = { wantsToPay = persistentListOf() },
onError = {
wantsToPay = persistentListOf()
scope.launch {
zappingProgress = 0f zappingProgress = 0f
showErrorMessageDialog = showErrorMessageDialog = StringToastMsg(stringRes(context, R.string.error_dialog_zap_error), error)
StringToastMsg(
stringRes(context, R.string.error_dialog_zap_error),
it.error,
)
} }
}, } else {
justShowError = { val uid = Uuid.random().toString()
scope.launch { accountViewModel.tempManualPaymentCache.put(uid, it)
showErrorMessageDialog = nav.nav(Route.ManualZapSplitPayment(uid))
StringToastMsg(
stringRes(context, R.string.error_dialog_zap_error),
it.error,
)
} }
}, },
) )
@@ -114,6 +114,7 @@ import com.vitorpamplona.amethyst.ui.components.AnimatedBorderTextCornerRadius
import com.vitorpamplona.amethyst.ui.components.ClickableBox import com.vitorpamplona.amethyst.ui.components.ClickableBox
import com.vitorpamplona.amethyst.ui.components.GenericLoadable import com.vitorpamplona.amethyst.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.routes.routeReplyTo import com.vitorpamplona.amethyst.ui.navigation.routes.routeReplyTo
@@ -169,6 +170,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
@Composable @Composable
fun ReactionsRow( fun ReactionsRow(
@@ -970,7 +973,7 @@ private fun likeClick(
} }
@Composable @Composable
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class, ExperimentalUuidApi::class)
fun ZapReaction( fun ZapReaction(
baseNote: Note, baseNote: Note,
grayTint: Color, grayTint: Color,
@@ -983,12 +986,6 @@ fun ZapReaction(
var wantsToZap by remember { mutableStateOf(false) } var wantsToZap by remember { mutableStateOf(false) }
var wantsToChangeZapAmount by remember { mutableStateOf(false) } var wantsToChangeZapAmount by remember { mutableStateOf(false) }
var wantsToSetCustomZap by remember { mutableStateOf(false) } var wantsToSetCustomZap by remember { mutableStateOf(false) }
var wantsToPay by
remember(baseNote) {
mutableStateOf<ImmutableList<ZapPaymentHandler.Payable>>(
persistentListOf(),
)
}
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -1022,7 +1019,19 @@ fun ZapReaction(
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, user) accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, user)
} }
}, },
onPayViaIntent = { wantsToPay = it }, onPayViaIntent = {
if (it.size == 1) {
val payable = it.first()
payViaIntent(payable.invoice, context, { }) { error ->
zappingProgress = 0f
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, UserBasedErrorMessage(error, payable.info.user))
}
} else {
val uid = Uuid.random().toString()
accountViewModel.tempManualPaymentCache.put(uid, it)
nav.nav(Route.ManualZapSplitPayment(uid))
}
},
) )
} }
}, },
@@ -1053,7 +1062,19 @@ fun ZapReaction(
} }
}, },
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } }, onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
onPayViaIntent = { wantsToPay = it }, onPayViaIntent = {
if (it.size == 1) {
val payable = it.first()
payViaIntent(payable.invoice, context, { }) { error ->
zappingProgress = 0f
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, UserBasedErrorMessage(error, payable.info.user))
}
} else {
val uid = Uuid.random().toString()
accountViewModel.tempManualPaymentCache.put(uid, it)
nav.nav(Route.ManualZapSplitPayment(uid))
}
},
) )
} }
@@ -1064,26 +1085,6 @@ fun ZapReaction(
) )
} }
if (wantsToPay.isNotEmpty()) {
PayViaIntentDialog(
payingInvoices = wantsToPay,
accountViewModel = accountViewModel,
onClose = { wantsToPay = persistentListOf() },
onError = {
wantsToPay = persistentListOf()
scope.launch {
zappingProgress = 0f
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
justShowError = {
scope.launch {
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
)
}
if (wantsToSetCustomZap) { if (wantsToSetCustomZap) {
ZapCustomDialog( ZapCustomDialog(
onZapStarts = { zapStartingTime = TimeUtils.now() }, onZapStarts = { zapStartingTime = TimeUtils.now() },
@@ -1095,7 +1096,19 @@ fun ZapReaction(
} }
}, },
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } }, onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
onPayViaIntent = { wantsToPay = it }, onPayViaIntent = {
if (it.size == 1) {
val payable = it.first()
payViaIntent(payable.invoice, context, { }) { error ->
zappingProgress = 0f
accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, UserBasedErrorMessage(error, payable.info.user))
}
} else {
val uid = Uuid.random().toString()
accountViewModel.tempManualPaymentCache.put(uid, it)
nav.nav(Route.ManualZapSplitPayment(uid))
}
},
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
baseNote = baseNote, baseNote = baseNote,
) )
@@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -67,10 +68,10 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.ZapPaymentHandler import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.components.TextSpinner import com.vitorpamplona.amethyst.ui.components.TextSpinner
import com.vitorpamplona.amethyst.ui.components.TitleExplainer import com.vitorpamplona.amethyst.ui.components.TitleExplainer
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
import com.vitorpamplona.amethyst.ui.note.buttons.CloseButton import com.vitorpamplona.amethyst.ui.note.buttons.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -286,36 +287,27 @@ fun ZapButton(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun PayViaIntentDialog( fun PayViaIntentScreen(
payingInvoices: ImmutableList<ZapPaymentHandler.Payable>, paymentId: String,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
onClose: () -> Unit, nav: INav,
onError: (UserBasedErrorMessage) -> Unit,
justShowError: (UserBasedErrorMessage) -> Unit,
) { ) {
if (payingInvoices.size == 1) {
val context = LocalContext.current
val payable = payingInvoices.first()
payViaIntent(payable.invoice, context, onClose) {
onError(UserBasedErrorMessage(it, payable.info.user))
}
} else {
Dialog(
onDismissRequest = onClose,
properties =
DialogProperties(
dismissOnClickOutside = false,
usePlatformDefaultWidth = false,
decorFitsSystemWindows = false,
),
) {
SetDialogToEdgeToEdge()
Scaffold( Scaffold(
topBar = { topBar = {
TopBarWithBackButton(stringRes(id = R.string.manual_zaps), onClose) TopBarWithBackButton(stringRes(id = R.string.manual_zaps), nav::popBack)
}, },
) { pad -> ) { pad ->
val list = accountViewModel.tempManualPaymentCache.get(paymentId)
if (list == null) {
Column(
Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(stringRes(R.string.feed_is_empty))
}
} else {
LazyColumn( LazyColumn(
Modifier.padding( Modifier.padding(
16.dp, 16.dp,
@@ -325,13 +317,12 @@ fun PayViaIntentDialog(
), ),
) { ) {
itemsIndexed( itemsIndexed(
payingInvoices, accountViewModel.tempManualPaymentCache.get(paymentId) ?: emptyList(),
key = { _: Int, invoice: ZapPaymentHandler.Payable -> key = { _: Int, invoice: ZapPaymentHandler.Payable ->
invoice.invoice invoice.invoice
}, },
) { index, payable -> ) { index, payable ->
DisplayPayable(index, payable, justShowError, accountViewModel) DisplayPayable(index, payable, accountViewModel)
}
} }
} }
} }
@@ -342,7 +333,6 @@ fun PayViaIntentDialog(
fun DisplayPayable( fun DisplayPayable(
index: Int, index: Int,
payable: ZapPaymentHandler.Payable, payable: ZapPaymentHandler.Payable,
justShowError: (UserBasedErrorMessage) -> Unit,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
) { ) {
val paid = rememberSaveable(payable) { mutableStateOf(false) } val paid = rememberSaveable(payable) { mutableStateOf(false) }
@@ -395,7 +385,10 @@ fun DisplayPayable(
PayButton(isActive = !paid.value) { PayButton(isActive = !paid.value) {
payViaIntent(payable.invoice, context, { paid.value = true }) { payViaIntent(payable.invoice, context, { paid.value = true }) {
justShowError(UserBasedErrorMessage(it, null)) accountViewModel.toastManager.toast(
R.string.error_dialog_zap_error,
UserBasedErrorMessage(it, payable.info.user),
)
} }
} }
} }
@@ -175,6 +175,8 @@ class AccountViewModel(
val toastManager = ToastManager() val toastManager = ToastManager()
val feedStates = AccountFeedContentStates(account, viewModelScope) val feedStates = AccountFeedContentStates(account, viewModelScope)
val tempManualPaymentCache = LruCache<String, List<ZapPaymentHandler.Payable>>(5)
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val notificationHasNewItems = val notificationHasNewItems =
combineTransform( combineTransform(