Caching total Zaps
This commit is contained in:
@@ -581,7 +581,7 @@ class Account(
|
|||||||
fun calculateZappedAmount(zappedNote: Note?): BigDecimal {
|
fun calculateZappedAmount(zappedNote: Note?): BigDecimal {
|
||||||
val privKey = zapPaymentRequest?.secret?.hexToByteArray() ?: keyPair.privKey
|
val privKey = zapPaymentRequest?.secret?.hexToByteArray() ?: keyPair.privKey
|
||||||
val pubKey = zapPaymentRequest?.pubKeyHex?.hexToByteArray()
|
val pubKey = zapPaymentRequest?.pubKeyHex?.hexToByteArray()
|
||||||
return zappedNote?.zappedAmount(privKey, pubKey) ?: BigDecimal.ZERO
|
return zappedNote?.zappedAmountWithNWCPayments(privKey, pubKey) ?: BigDecimal.ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendZapPaymentRequestFor(bolt11: String, zappedNote: Note?, onResponse: (Response?) -> Unit) {
|
fun sendZapPaymentRequestFor(bolt11: String, zappedNote: Note?, onResponse: (Response?) -> Unit) {
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ open class Note(val idHex: String) {
|
|||||||
private set
|
private set
|
||||||
var zaps = mapOf<Note, Note?>()
|
var zaps = mapOf<Note, Note?>()
|
||||||
private set
|
private set
|
||||||
|
var zapsAmount: BigDecimal = BigDecimal.ZERO
|
||||||
|
|
||||||
var zapPayments = mapOf<Note, Note?>()
|
var zapPayments = mapOf<Note, Note?>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@@ -265,6 +267,7 @@ open class Note(val idHex: String) {
|
|||||||
reports = mapOf<User, List<Note>>()
|
reports = mapOf<User, List<Note>>()
|
||||||
zaps = mapOf<Note, Note?>()
|
zaps = mapOf<Note, Note?>()
|
||||||
zapPayments = mapOf<Note, Note?>()
|
zapPayments = mapOf<Note, Note?>()
|
||||||
|
zapsAmount = BigDecimal.ZERO
|
||||||
relays = listOf<String>()
|
relays = listOf<String>()
|
||||||
lastReactionsDownloadTime = emptyMap()
|
lastReactionsDownloadTime = emptyMap()
|
||||||
|
|
||||||
@@ -311,9 +314,11 @@ open class Note(val idHex: String) {
|
|||||||
fun removeZap(note: Note) {
|
fun removeZap(note: Note) {
|
||||||
if (zaps[note] != null) {
|
if (zaps[note] != null) {
|
||||||
zaps = zaps.minus(note)
|
zaps = zaps.minus(note)
|
||||||
|
updateZapTotal()
|
||||||
liveSet?.innerZaps?.invalidateData()
|
liveSet?.innerZaps?.invalidateData()
|
||||||
} else if (zaps.containsValue(note)) {
|
} else if (zaps.containsValue(note)) {
|
||||||
zaps = zaps.filterValues { it != note }
|
zaps = zaps.filterValues { it != note }
|
||||||
|
updateZapTotal()
|
||||||
liveSet?.innerZaps?.invalidateData()
|
liveSet?.innerZaps?.invalidateData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,11 +359,13 @@ open class Note(val idHex: String) {
|
|||||||
if (zapRequest !in zaps.keys) {
|
if (zapRequest !in zaps.keys) {
|
||||||
val inserted = innerAddZap(zapRequest, zap)
|
val inserted = innerAddZap(zapRequest, zap)
|
||||||
if (inserted) {
|
if (inserted) {
|
||||||
|
updateZapTotal()
|
||||||
liveSet?.innerZaps?.invalidateData()
|
liveSet?.innerZaps?.invalidateData()
|
||||||
}
|
}
|
||||||
} else if (zaps[zapRequest] == null) {
|
} else if (zaps[zapRequest] == null) {
|
||||||
val inserted = innerAddZap(zapRequest, zap)
|
val inserted = innerAddZap(zapRequest, zap)
|
||||||
if (inserted) {
|
if (inserted) {
|
||||||
|
updateZapTotal()
|
||||||
liveSet?.innerZaps?.invalidateData()
|
liveSet?.innerZaps?.invalidateData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -478,44 +485,63 @@ open class Note(val idHex: String) {
|
|||||||
}.flatten()
|
}.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun zappedAmount(privKey: ByteArray?, walletServicePubkey: ByteArray?): BigDecimal {
|
private fun updateZapTotal() {
|
||||||
// Regular Zap Receipts
|
var sumOfAmounts = BigDecimal.ZERO
|
||||||
val completedZaps = zaps.asSequence()
|
|
||||||
.mapNotNull { it.value?.event }
|
// Regular Zap Receipts
|
||||||
.filterIsInstance<LnZapEvent>()
|
zaps.values.forEach {
|
||||||
.filter { it.amount != null }
|
val noteEvent = it?.event
|
||||||
.associate {
|
if (noteEvent is LnZapEvent) {
|
||||||
it.lnInvoice() to it.amount
|
sumOfAmounts += noteEvent.amount ?: BigDecimal.ZERO
|
||||||
}
|
}
|
||||||
.toMap()
|
}
|
||||||
|
|
||||||
|
zapsAmount = sumOfAmounts
|
||||||
|
}
|
||||||
|
|
||||||
|
fun zappedAmountWithNWCPayments(privKey: ByteArray?, walletServicePubkey: ByteArray?): BigDecimal {
|
||||||
|
if (zapPayments.isEmpty()) return zapsAmount
|
||||||
|
|
||||||
|
var sumOfAmounts = zapsAmount
|
||||||
|
|
||||||
|
val invoiceSet = LinkedHashSet<String>(zaps.size + zapPayments.size)
|
||||||
|
zaps.forEach {
|
||||||
|
(it.value as? LnZapEvent)?.lnInvoice()?.let {
|
||||||
|
invoiceSet.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (privKey != null && walletServicePubkey != null) {
|
||||||
|
zapPayments.forEach {
|
||||||
|
val noteEvent = (it.value?.event as? LnZapPaymentResponseEvent)?.response(
|
||||||
|
privKey,
|
||||||
|
walletServicePubkey
|
||||||
|
)
|
||||||
|
if (noteEvent is PayInvoiceSuccessResponse) {
|
||||||
|
val invoice = (it.key.event as? LnZapPaymentRequestEvent)?.lnInvoice(
|
||||||
|
privKey,
|
||||||
|
walletServicePubkey
|
||||||
|
)
|
||||||
|
|
||||||
val completedPayments = if (privKey != null && walletServicePubkey != null) {
|
|
||||||
// Payments confirmed by the User's Wallet
|
|
||||||
zapPayments
|
|
||||||
.asSequence()
|
|
||||||
.filter {
|
|
||||||
val response = (it.value?.event as? LnZapPaymentResponseEvent)?.response(privKey, walletServicePubkey)
|
|
||||||
response is PayInvoiceSuccessResponse
|
|
||||||
}
|
|
||||||
.associate {
|
|
||||||
val lnInvoice = (it.key.event as? LnZapPaymentRequestEvent)?.lnInvoice(privKey, walletServicePubkey)
|
|
||||||
val amount = try {
|
val amount = try {
|
||||||
if (lnInvoice == null) {
|
if (invoice == null) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
LnInvoiceUtil.getAmountInSats(lnInvoice)
|
LnInvoiceUtil.getAmountInSats(invoice)
|
||||||
}
|
}
|
||||||
} catch (e: java.lang.Exception) {
|
} catch (e: java.lang.Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
lnInvoice to amount
|
|
||||||
|
if (invoice != null && amount != null && !invoiceSet.contains(invoice)) {
|
||||||
|
invoiceSet.add(invoice)
|
||||||
|
sumOfAmounts += amount
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.toMap()
|
}
|
||||||
} else {
|
|
||||||
emptyMap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (completedZaps + completedPayments).values.filterNotNull().sumOf { it }
|
return sumOfAmounts
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasPledgeBy(user: User): Boolean {
|
fun hasPledgeBy(user: User): Boolean {
|
||||||
@@ -626,6 +652,7 @@ open class Note(val idHex: String) {
|
|||||||
boosts = emptyList()
|
boosts = emptyList()
|
||||||
reports = emptyMap()
|
reports = emptyMap()
|
||||||
zaps = emptyMap()
|
zaps = emptyMap()
|
||||||
|
zapsAmount = BigDecimal.ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearEOSE() {
|
fun clearEOSE() {
|
||||||
|
|||||||
@@ -205,11 +205,6 @@ fun CommentIcon(iconSize: Dp, tint: Color) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ViewCountIcon(iconSize: Dp, tint: Color = Color.Unspecified) {
|
|
||||||
ViewCountIcon(remember { Modifier.size(iconSize) }, tint)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ViewCountIcon(modifier: Modifier, tint: Color = Color.Unspecified) {
|
fun ViewCountIcon(modifier: Modifier, tint: Color = Color.Unspecified) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.animation.with
|
import androidx.compose.animation.with
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
@@ -44,6 +45,7 @@ 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.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
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
|
||||||
@@ -112,6 +114,7 @@ import kotlinx.collections.immutable.toImmutableMap
|
|||||||
import kotlinx.collections.immutable.toImmutableSet
|
import kotlinx.collections.immutable.toImmutableSet
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
@@ -195,60 +198,35 @@ private fun GenericInnerReactionRow(
|
|||||||
six: @Composable () -> Unit
|
six: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
Row(verticalAlignment = CenterVertically, modifier = ReactionRowHeight) {
|
Row(verticalAlignment = CenterVertically, modifier = ReactionRowHeight) {
|
||||||
|
val fullWeight = remember { Modifier.weight(1f) }
|
||||||
|
|
||||||
if (showReactionDetail) {
|
if (showReactionDetail) {
|
||||||
Column(
|
Row(
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalAlignment = CenterVertically,
|
||||||
modifier = ReactionRowExpandButton
|
modifier = remember { ReactionRowExpandButton.then(fullWeight) }
|
||||||
) {
|
) {
|
||||||
Row(verticalAlignment = CenterVertically) {
|
one()
|
||||||
one()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Row(verticalAlignment = CenterVertically, modifier = fullWeight) {
|
||||||
verticalArrangement = Arrangement.Center,
|
two()
|
||||||
modifier = remember { Modifier.weight(1f) }
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = CenterVertically) {
|
|
||||||
two()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Row(verticalAlignment = CenterVertically, modifier = fullWeight) {
|
||||||
verticalArrangement = Arrangement.Center,
|
three()
|
||||||
modifier = remember { Modifier.weight(1f) }
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = CenterVertically) {
|
|
||||||
three()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Row(verticalAlignment = CenterVertically, modifier = fullWeight) {
|
||||||
verticalArrangement = Arrangement.Center,
|
four()
|
||||||
modifier = remember { Modifier.weight(1f) }
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = CenterVertically) {
|
|
||||||
four()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Row(verticalAlignment = CenterVertically, modifier = fullWeight) {
|
||||||
verticalArrangement = Arrangement.Center,
|
five()
|
||||||
modifier = remember { Modifier.weight(1f) }
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = CenterVertically) {
|
|
||||||
five()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Row(verticalAlignment = CenterVertically, modifier = fullWeight) {
|
||||||
verticalArrangement = Arrangement.Center,
|
six()
|
||||||
modifier = remember { Modifier.weight(1f) }
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = CenterVertically) {
|
|
||||||
six()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -590,17 +568,12 @@ fun ReplyCounter(baseNote: Note, textColor: Color) {
|
|||||||
SlidingAnimationCount(repliesState, textColor)
|
SlidingAnimationCount(repliesState, textColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SlidingAnimationCount(baseCount: MutableState<Int>, textColor: Color) {
|
|
||||||
SlidingAnimationCount(baseCount.value, textColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SlidingAnimationCount(baseCount: Int, textColor: Color) {
|
private fun SlidingAnimationCount(baseCount: Int, textColor: Color) {
|
||||||
AnimatedContent<Int>(
|
AnimatedContent<Int>(
|
||||||
targetState = baseCount,
|
targetState = baseCount,
|
||||||
transitionSpec = AnimatedContentTransitionScope<Int>::transitionSpec
|
transitionSpec = AnimatedContentTransitionScope<Int>::transitionSpec,
|
||||||
|
label = "SlidingAnimationCount"
|
||||||
) { count ->
|
) { count ->
|
||||||
TextCount(count, textColor)
|
TextCount(count, textColor)
|
||||||
}
|
}
|
||||||
@@ -612,11 +585,16 @@ private fun <S> AnimatedContentTransitionScope<S>.transitionSpec(): ContentTrans
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
val slideAnimation: ContentTransform = slideInVertically(animationSpec = tween(durationMillis = 100)) { height -> height } + fadeIn(
|
val slideAnimation: ContentTransform =
|
||||||
animationSpec = tween(durationMillis = 100)
|
(
|
||||||
) with slideOutVertically(animationSpec = tween(durationMillis = 100)) { height -> -height } + fadeOut(
|
slideInVertically(animationSpec = tween(durationMillis = 100)) { height -> height } + fadeIn(
|
||||||
animationSpec = tween(durationMillis = 100)
|
animationSpec = tween(durationMillis = 100)
|
||||||
)
|
)
|
||||||
|
).togetherWith(
|
||||||
|
slideOutVertically(animationSpec = tween(durationMillis = 100)) { height -> -height } + fadeOut(
|
||||||
|
animationSpec = tween(durationMillis = 100)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TextCount(count: Int, textColor: Color) {
|
private fun TextCount(count: Int, textColor: Color) {
|
||||||
@@ -630,11 +608,11 @@ private fun TextCount(count: Int, textColor: Color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
|
||||||
private fun SlidingAnimationAmount(amount: MutableState<String>, textColor: Color) {
|
private fun SlidingAnimationAmount(amount: MutableState<String>, textColor: Color) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = amount.value,
|
targetState = amount.value,
|
||||||
transitionSpec = AnimatedContentTransitionScope<String>::transitionSpec
|
transitionSpec = AnimatedContentTransitionScope<String>::transitionSpec,
|
||||||
|
label = "SlidingAnimationAmount"
|
||||||
) { count ->
|
) { count ->
|
||||||
Text(
|
Text(
|
||||||
text = count,
|
text = count,
|
||||||
@@ -667,7 +645,9 @@ fun BoostReaction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
BoostIcon(baseNote, iconSize, grayTint, accountViewModel)
|
ObserveBoostIcon(baseNote, accountViewModel) { hasBoosted ->
|
||||||
|
RepostedIcon(iconButtonModifier, if (hasBoosted) Color.Unspecified else grayTint)
|
||||||
|
}
|
||||||
|
|
||||||
if (wantsToBoost) {
|
if (wantsToBoost) {
|
||||||
BoostTypeChoicePopup(
|
BoostTypeChoicePopup(
|
||||||
@@ -692,20 +672,16 @@ fun BoostReaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BoostIcon(baseNote: Note, iconSize: Dp = Size20dp, grayTint: Color, accountViewModel: AccountViewModel) {
|
fun ObserveBoostIcon(baseNote: Note, accountViewModel: AccountViewModel, inner: @Composable (Boolean) -> Unit) {
|
||||||
val iconTint by remember(baseNote) {
|
val hasBoosted by remember(baseNote) {
|
||||||
baseNote.live().boosts.map {
|
baseNote.live().boosts.map {
|
||||||
if (it.note.isBoostedBy(accountViewModel.userProfile())) Color.Unspecified else grayTint
|
it.note.isBoostedBy(accountViewModel.userProfile())
|
||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
}.observeAsState(
|
}.observeAsState(
|
||||||
if (baseNote.isBoostedBy(accountViewModel.userProfile())) Color.Unspecified else grayTint
|
baseNote.isBoostedBy(accountViewModel.userProfile())
|
||||||
)
|
)
|
||||||
|
|
||||||
val iconModifier = remember {
|
inner(hasBoosted)
|
||||||
Modifier.size(iconSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
RepostedIcon(iconModifier, iconTint)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -760,10 +736,20 @@ fun LikeReaction(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
LikeIcon(baseNote, iconFontSize, heartSize, grayTint, accountViewModel)
|
ObserveLikeIcon(baseNote, accountViewModel) { reactionType ->
|
||||||
|
Crossfade(targetState = reactionType.value, label = "LikeIcon") {
|
||||||
|
if (it != null) {
|
||||||
|
RenderReactionType(it, heartSize, iconFontSize)
|
||||||
|
} else {
|
||||||
|
LikeIcon(heartSize, grayTint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LikeText(baseNote, grayTint)
|
ObserveLikeText(baseNote) { reactionCount ->
|
||||||
|
SlidingAnimationCount(reactionCount, grayTint)
|
||||||
|
}
|
||||||
|
|
||||||
if (wantsToChangeReactionSymbol) {
|
if (wantsToChangeReactionSymbol) {
|
||||||
UpdateReactionTypeDialog(
|
UpdateReactionTypeDialog(
|
||||||
@@ -790,44 +776,26 @@ fun LikeReaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LikeIcon(
|
fun ObserveLikeIcon(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
iconFontSize: TextUnit = Font14SP,
|
accountViewModel: AccountViewModel,
|
||||||
iconSize: Dp = Size20dp,
|
inner: @Composable (MutableState<String?>) -> Unit
|
||||||
grayTint: Color,
|
|
||||||
accountViewModel: AccountViewModel
|
|
||||||
) {
|
) {
|
||||||
val reactionType = remember(baseNote) {
|
val reactionType = remember(baseNote) {
|
||||||
mutableStateOf<String?>(null)
|
mutableStateOf<String?>(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val reactionsState by baseNote.live().reactions.observeAsState()
|
||||||
|
|
||||||
WatchReactionTypeForNote(baseNote, accountViewModel) { newReactionType ->
|
LaunchedEffect(key1 = reactionsState) {
|
||||||
if (reactionType.value != newReactionType) {
|
accountViewModel.loadReactionTo(reactionsState?.note) { newReactionType ->
|
||||||
scope.launch(Dispatchers.Main) {
|
if (reactionType.value != newReactionType) {
|
||||||
reactionType.value = newReactionType
|
reactionType.value = newReactionType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Crossfade(targetState = reactionType) {
|
inner(reactionType)
|
||||||
val value = it.value
|
|
||||||
if (value != null) {
|
|
||||||
RenderReactionType(value, iconSize, iconFontSize)
|
|
||||||
} else {
|
|
||||||
LikeIcon(iconSize, grayTint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun WatchReactionTypeForNote(baseNote: Note, accountViewModel: AccountViewModel, onNewReactionType: (String?) -> Unit) {
|
|
||||||
val reactionsState by baseNote.live().reactions.observeAsState()
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = reactionsState) {
|
|
||||||
accountViewModel.loadReactionTo(reactionsState?.note, onNewReactionType)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -863,10 +831,10 @@ private fun RenderReactionType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LikeText(baseNote: Note, grayTint: Color) {
|
fun ObserveLikeText(baseNote: Note, inner: @Composable (Int) -> Unit) {
|
||||||
val reactionCount by baseNote.live().reactionCount.observeAsState(0)
|
val reactionCount by baseNote.live().reactionCount.observeAsState(0)
|
||||||
|
|
||||||
SlidingAnimationCount(reactionCount, grayTint)
|
inner(reactionCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun likeClick(
|
private fun likeClick(
|
||||||
@@ -919,7 +887,7 @@ fun ZapReaction(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
var zappingProgress by remember { mutableStateOf(0f) }
|
var zappingProgress by remember { mutableFloatStateOf(0f) }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = CenterVertically,
|
verticalAlignment = CenterVertically,
|
||||||
@@ -942,7 +910,7 @@ fun ZapReaction(
|
|||||||
onMultipleChoices = {
|
onMultipleChoices = {
|
||||||
wantsToZap = true
|
wantsToZap = true
|
||||||
},
|
},
|
||||||
onError = { title, message ->
|
onError = { _, message ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
zappingProgress = 0f
|
zappingProgress = 0f
|
||||||
showErrorMessageDialog = message
|
showErrorMessageDialog = message
|
||||||
@@ -973,7 +941,7 @@ fun ZapReaction(
|
|||||||
wantsToZap = false
|
wantsToZap = false
|
||||||
wantsToChangeZapAmount = true
|
wantsToChangeZapAmount = true
|
||||||
},
|
},
|
||||||
onError = { title, message ->
|
onError = { _, message ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
zappingProgress = 0f
|
zappingProgress = 0f
|
||||||
showErrorMessageDialog = message
|
showErrorMessageDialog = message
|
||||||
@@ -1033,7 +1001,7 @@ fun ZapReaction(
|
|||||||
if (wantsToSetCustomZap) {
|
if (wantsToSetCustomZap) {
|
||||||
ZapCustomDialog(
|
ZapCustomDialog(
|
||||||
onClose = { wantsToSetCustomZap = false },
|
onClose = { wantsToSetCustomZap = false },
|
||||||
onError = { title, message ->
|
onError = { _, message ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
zappingProgress = 0f
|
zappingProgress = 0f
|
||||||
showErrorMessageDialog = message
|
showErrorMessageDialog = message
|
||||||
@@ -1058,22 +1026,31 @@ fun ZapReaction(
|
|||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
progress = animateFloatAsState(
|
progress = animateFloatAsState(
|
||||||
targetValue = zappingProgress,
|
targetValue = zappingProgress,
|
||||||
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
|
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
|
||||||
|
label = "ZapIconIndicator"
|
||||||
).value,
|
).value,
|
||||||
modifier = remember { Modifier.size(animationSize) },
|
modifier = remember { Modifier.size(animationSize) },
|
||||||
strokeWidth = 2.dp
|
strokeWidth = 2.dp
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ZapIcon(
|
ObserveZapIcon(
|
||||||
baseNote,
|
baseNote,
|
||||||
iconSize,
|
|
||||||
grayTint,
|
|
||||||
accountViewModel
|
accountViewModel
|
||||||
)
|
) { wasZappedByLoggedInUser ->
|
||||||
|
Crossfade(targetState = wasZappedByLoggedInUser.value, label = "ZapIcon") {
|
||||||
|
if (it) {
|
||||||
|
ZappedIcon(iconSize)
|
||||||
|
} else {
|
||||||
|
ZapIcon(iconSize, grayTint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ZapAmountText(baseNote, grayTint, accountViewModel)
|
ObserveZapAmountText(baseNote, accountViewModel) { zapAmountTxt ->
|
||||||
|
SlidingAnimationAmount(zapAmountTxt, grayTint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun zapClick(
|
private fun zapClick(
|
||||||
@@ -1115,98 +1092,72 @@ private fun zapClick(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ZapIcon(
|
private fun ObserveZapIcon(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
iconSize: Dp,
|
accountViewModel: AccountViewModel,
|
||||||
grayTint: Color,
|
inner: @Composable (MutableState<Boolean>) -> Unit
|
||||||
accountViewModel: AccountViewModel
|
|
||||||
) {
|
) {
|
||||||
val wasZappedByLoggedInUser = remember { mutableStateOf(false) }
|
val wasZappedByLoggedInUser = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
if (!wasZappedByLoggedInUser.value) {
|
if (!wasZappedByLoggedInUser.value) {
|
||||||
WatchZapsForNote(baseNote, accountViewModel) { newWasZapped ->
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
if (wasZappedByLoggedInUser.value != newWasZapped) {
|
|
||||||
scope.launch(Dispatchers.Main) {
|
LaunchedEffect(key1 = zapsState) {
|
||||||
|
accountViewModel.calculateIfNoteWasZappedByAccount(baseNote) { newWasZapped ->
|
||||||
|
if (wasZappedByLoggedInUser.value != newWasZapped) {
|
||||||
wasZappedByLoggedInUser.value = newWasZapped
|
wasZappedByLoggedInUser.value = newWasZapped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Crossfade(targetState = wasZappedByLoggedInUser) {
|
inner(wasZappedByLoggedInUser)
|
||||||
if (it.value) {
|
|
||||||
ZappedIcon(iconSize)
|
|
||||||
} else {
|
|
||||||
ZapIcon(iconSize, grayTint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun WatchZapsForNote(baseNote: Note, accountViewModel: AccountViewModel, onWasZapped: (Boolean) -> Unit) {
|
private fun ObserveZapAmountText(
|
||||||
|
baseNote: Note,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
inner: @Composable (MutableState<String>) -> Unit
|
||||||
|
) {
|
||||||
|
val zapAmountTxt = remember(baseNote) {
|
||||||
|
mutableStateOf(showAmount(baseNote.zapsAmount))
|
||||||
|
}
|
||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
|
|
||||||
LaunchedEffect(key1 = zapsState) {
|
LaunchedEffect(key1 = zapsState) {
|
||||||
accountViewModel.calculateIfNoteWasZappedByAccount(baseNote, onWasZapped)
|
accountViewModel.calculateZapAmount(baseNote) { newZapAmount ->
|
||||||
}
|
if (zapAmountTxt.value != newZapAmount) {
|
||||||
}
|
withContext(Dispatchers.Main) {
|
||||||
|
zapAmountTxt.value = newZapAmount
|
||||||
@Composable
|
}
|
||||||
private fun ZapAmountText(
|
|
||||||
baseNote: Note,
|
|
||||||
grayTint: Color,
|
|
||||||
accountViewModel: AccountViewModel
|
|
||||||
) {
|
|
||||||
val zapAmountTxt = remember(baseNote) { mutableStateOf("") }
|
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
WatchZapAmountsForNote(baseNote, accountViewModel) { newZapAmount ->
|
|
||||||
if (zapAmountTxt.value != newZapAmount) {
|
|
||||||
scope.launch(Dispatchers.Main) {
|
|
||||||
zapAmountTxt.value = newZapAmount
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SlidingAnimationAmount(zapAmountTxt, grayTint)
|
inner(zapAmountTxt)
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun WatchZapAmountsForNote(baseNote: Note, accountViewModel: AccountViewModel, onZapAmount: (String) -> Unit) {
|
|
||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = zapsState) {
|
|
||||||
accountViewModel.calculateZapAmount(baseNote, onZapAmount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ViewCountReaction(
|
fun ViewCountReaction(
|
||||||
note: Note,
|
note: Note,
|
||||||
grayTint: Color,
|
grayTint: Color,
|
||||||
barChartSize: Dp = Size19dp,
|
barChartModifier: Modifier = Modifier.size(Size19dp),
|
||||||
numberSize: Dp = Size24dp,
|
numberSizeModifier: Modifier = Modifier.height(Size24dp),
|
||||||
viewCountColorFilter: ColorFilter
|
viewCountColorFilter: ColorFilter
|
||||||
) {
|
) {
|
||||||
ViewCountIcon(barChartSize, grayTint)
|
ViewCountIcon(barChartModifier, grayTint)
|
||||||
DrawViewCount(note, numberSize, viewCountColorFilter)
|
DrawViewCount(note, numberSizeModifier, viewCountColorFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DrawViewCount(
|
private fun DrawViewCount(
|
||||||
note: Note,
|
note: Note,
|
||||||
numberSize: Dp = Size24dp,
|
iconModifier: Modifier = Modifier,
|
||||||
viewCountColorFilter: ColorFilter
|
viewCountColorFilter: ColorFilter
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val iconModifier = remember {
|
|
||||||
Modifier.height(numberSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = remember(note) {
|
model = remember(note) {
|
||||||
ImageRequest.Builder(context)
|
ImageRequest.Builder(context)
|
||||||
|
|||||||
@@ -201,9 +201,13 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
|
|||||||
return account.calculateZappedAmount(zappedNote)
|
return account.calculateZappedAmount(zappedNote)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateZapAmount(zappedNote: Note, onZapAmount: (String) -> Unit) {
|
suspend fun calculateZapAmount(zappedNote: Note, onZapAmount: suspend (String) -> Unit) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
if (zappedNote.zapPayments.isNotEmpty()) {
|
||||||
onZapAmount(showAmount(account.calculateZappedAmount(zappedNote)))
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
onZapAmount(showAmount(account.calculateZappedAmount(zappedNote)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onZapAmount(showAmount(zappedNote.zapsAmount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -437,6 +437,6 @@ fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, nav: (St
|
|||||||
}
|
}
|
||||||
LikeReaction(baseNote, grayTint = MaterialTheme.colorScheme.onBackground, accountViewModel, nav, iconSize = 40.dp, heartSize = Size35dp, 28.sp)
|
LikeReaction(baseNote, grayTint = MaterialTheme.colorScheme.onBackground, accountViewModel, nav, iconSize = 40.dp, heartSize = Size35dp, 28.sp)
|
||||||
ZapReaction(baseNote, grayTint = MaterialTheme.colorScheme.onBackground, accountViewModel, iconSize = 40.dp, animationSize = Size35dp, nav = nav)
|
ZapReaction(baseNote, grayTint = MaterialTheme.colorScheme.onBackground, accountViewModel, iconSize = 40.dp, animationSize = Size35dp, nav = nav)
|
||||||
ViewCountReaction(baseNote, grayTint = MaterialTheme.colorScheme.onBackground, barChartSize = 39.dp, viewCountColorFilter = MaterialTheme.colorScheme.onBackgroundColorFilter)
|
ViewCountReaction(baseNote, grayTint = MaterialTheme.colorScheme.onBackground, barChartModifier = Modifier.size(39.dp), viewCountColorFilter = MaterialTheme.colorScheme.onBackgroundColorFilter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user