mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-19 18:51:25 +02:00
Performance Improvements for Zaps in Polls.
This commit is contained in:
@@ -595,7 +595,7 @@ object LocalCache {
|
||||
// Already processed this event.
|
||||
if (note.event != null) return
|
||||
|
||||
val zapRequest = event.containedPost()?.id?.let { getOrCreateNote(it) }
|
||||
val zapRequest = event.zapRequest?.id?.let { getOrCreateNote(it) }
|
||||
|
||||
val author = getOrCreateUser(event.pubKey)
|
||||
val mentions = event.zappedAuthor().mapNotNull { checkGetOrCreateUser(it) }
|
||||
|
@@ -76,7 +76,7 @@ abstract class NostrDataSource(val debugName: String) {
|
||||
is DeletionEvent -> LocalCache.consume(event)
|
||||
|
||||
is LnZapEvent -> {
|
||||
event.containedPost()?.let { onEvent(it, subscriptionId, relay) }
|
||||
event.zapRequest?.let { onEvent(it, subscriptionId, relay) }
|
||||
LocalCache.consume(event)
|
||||
}
|
||||
is LnZapRequestEvent -> LocalCache.consume(event)
|
||||
|
@@ -4,7 +4,6 @@ import android.util.Log
|
||||
import com.vitorpamplona.amethyst.model.HexKey
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LnInvoiceUtil
|
||||
import com.vitorpamplona.amethyst.service.relays.Client
|
||||
import java.math.BigDecimal
|
||||
|
||||
class LnZapEvent(
|
||||
id: HexKey,
|
||||
@@ -14,25 +13,37 @@ class LnZapEvent(
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : LnZapEventInterface, Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
// This event is also kept in LocalCache (same object)
|
||||
@Transient val zapRequest: LnZapRequestEvent?
|
||||
|
||||
override fun zappedPost() = tags
|
||||
.filter { it.firstOrNull() == "e" }
|
||||
.mapNotNull { it.getOrNull(1) }
|
||||
|
||||
override fun zappedPollOption(): Int? = containedPost()?.tags
|
||||
?.filter { it.firstOrNull() == POLL_OPTION }
|
||||
?.getOrNull(0)?.getOrNull(1)?.toInt()
|
||||
|
||||
override fun zappedAuthor() = tags
|
||||
.filter { it.firstOrNull() == "p" }
|
||||
.mapNotNull { it.getOrNull(1) }
|
||||
|
||||
override fun zappedRequestAuthor(): String? = containedPost()?.pubKey()
|
||||
|
||||
override fun amount(): BigDecimal? {
|
||||
return amount
|
||||
private fun containedPost(): LnZapRequestEvent? = try {
|
||||
description()?.ifBlank { null }?.let {
|
||||
fromJson(it, Client.lenient)
|
||||
} as? LnZapRequestEvent
|
||||
} catch (e: Exception) {
|
||||
Log.e("LnZapEvent", "Failed to Parse Contained Post ${description()}", e)
|
||||
null
|
||||
}
|
||||
|
||||
init {
|
||||
zapRequest = containedPost()
|
||||
}
|
||||
|
||||
override fun zappedPost() = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
|
||||
|
||||
override fun zappedAuthor() = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }
|
||||
|
||||
override fun zappedPollOption(): Int? = try {
|
||||
zapRequest?.tags?.firstOrNull { it.size > 1 && it[0] == POLL_OPTION }?.get(1)?.toInt()
|
||||
} catch (e: Exception) {
|
||||
Log.e("LnZapEvent", "ZappedPollOption failed to parse", e)
|
||||
null
|
||||
}
|
||||
|
||||
override fun zappedRequestAuthor(): String? = zapRequest?.pubKey()
|
||||
|
||||
override fun amount() = amount
|
||||
|
||||
// Keeps this as a field because it's a heavier function used everywhere.
|
||||
val amount by lazy {
|
||||
try {
|
||||
@@ -42,29 +53,14 @@ class LnZapEvent(
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun message(): String {
|
||||
return message
|
||||
}
|
||||
val message = content
|
||||
|
||||
override fun containedPost(): Event? = try {
|
||||
description()?.ifBlank { null }?.let {
|
||||
fromJson(it, Client.lenient)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("LnZapEvent", "Failed to Parse Contained Post ${description()}", e)
|
||||
null
|
||||
return content
|
||||
}
|
||||
|
||||
private fun lnInvoice(): String? = tags
|
||||
.filter { it.firstOrNull() == "bolt11" }
|
||||
.mapNotNull { it.getOrNull(1) }
|
||||
.firstOrNull()
|
||||
private fun lnInvoice() = tags.firstOrNull { it.size > 1 && it[0] == "bolt11" }?.get(1)
|
||||
|
||||
private fun description(): String? = tags
|
||||
.filter { it.firstOrNull() == "description" }
|
||||
.mapNotNull { it.getOrNull(1) }
|
||||
.firstOrNull()
|
||||
private fun description() = tags.firstOrNull { it.size > 1 && it[0] == "description" }?.get(1)
|
||||
|
||||
companion object {
|
||||
const val kind = 9735
|
||||
|
@@ -16,7 +16,5 @@ interface LnZapEventInterface : EventInterface {
|
||||
|
||||
fun amount(): BigDecimal?
|
||||
|
||||
fun containedPost(): Event?
|
||||
|
||||
fun message(): String
|
||||
}
|
||||
|
@@ -13,8 +13,10 @@ class LnZapRequestEvent(
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun zappedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }
|
||||
fun zappedAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) }
|
||||
|
||||
fun zappedPost() = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
|
||||
|
||||
fun zappedAuthor() = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }
|
||||
|
||||
companion object {
|
||||
const val kind = 9734
|
||||
|
@@ -20,24 +20,17 @@ class PollNoteEvent(
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : BaseTextNoteEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun pollOptions(): Map<Int, String> {
|
||||
val map = mutableMapOf<Int, String>()
|
||||
tags.filter { it.first() == POLL_OPTION }
|
||||
.forEach { map[it[1].toInt()] = it[2] }
|
||||
return map
|
||||
}
|
||||
fun pollOptions() =
|
||||
tags.filter { it.size > 2 && it[0] == POLL_OPTION }
|
||||
.associate { it[1].toInt() to it[2] }
|
||||
|
||||
fun getTagInt(property: String): Int? {
|
||||
val tagList = tags.filter {
|
||||
it.firstOrNull() == property
|
||||
}
|
||||
val tag = tagList.getOrNull(0)
|
||||
val s = tag?.getOrNull(1)
|
||||
val number = tags.firstOrNull() { it.size > 1 && it[0] == property }?.get(1)
|
||||
|
||||
return if (s.isNullOrBlank() || s == "null") {
|
||||
return if (number.isNullOrBlank() || number == "null") {
|
||||
null
|
||||
} else {
|
||||
s.toInt()
|
||||
number.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -48,9 +48,6 @@ fun PollNote(
|
||||
accountViewModel: AccountViewModel,
|
||||
navController: NavController
|
||||
) {
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val account = accountState?.account ?: return
|
||||
|
||||
val zapsState by baseNote.live().zaps.observeAsState()
|
||||
val zappedNote = zapsState?.note ?: return
|
||||
|
||||
@@ -72,7 +69,7 @@ fun PollNote(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(vertical = 3.dp)
|
||||
) {
|
||||
if (zappedNote.author == account.userProfile() || zappedNote.isZappedBy(account.userProfile())) {
|
||||
if (accountViewModel.isLoggedUser(zappedNote.author) || zappedNote.isZappedBy(accountViewModel.userProfile())) {
|
||||
ZapVote(
|
||||
baseNote,
|
||||
accountViewModel,
|
||||
@@ -90,7 +87,7 @@ fun PollNote(
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
color = color,
|
||||
progress = optionTally
|
||||
progress = optionTally.toFloat()
|
||||
)
|
||||
|
||||
Row(
|
||||
@@ -101,7 +98,7 @@ fun PollNote(
|
||||
modifier = Modifier.padding(horizontal = 10.dp).width(40.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "${(optionTally * 100).roundToInt()}%",
|
||||
text = "${(optionTally.toFloat() * 100).roundToInt()}%",
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
@@ -210,7 +207,7 @@ fun ZapVote(
|
||||
)
|
||||
.show()
|
||||
}
|
||||
} else if (zappedNote?.author == account.userProfile()) {
|
||||
} else if (accountViewModel.isLoggedUser(zappedNote?.author)) {
|
||||
scope.launch {
|
||||
Toast
|
||||
.makeText(
|
||||
|
@@ -5,6 +5,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.model.*
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.util.*
|
||||
|
||||
class PollNoteViewModel {
|
||||
@@ -16,7 +17,9 @@ class PollNoteViewModel {
|
||||
var valueMaximum: Int? = null
|
||||
var valueMinimum: Int? = null
|
||||
private var closedAt: Int? = null
|
||||
var consensusThreshold: Float? = null
|
||||
var consensusThreshold: BigDecimal? = null
|
||||
|
||||
var totalZapped: BigDecimal = BigDecimal.ZERO
|
||||
|
||||
fun load(note: Note?) {
|
||||
pollNote = note
|
||||
@@ -24,8 +27,10 @@ class PollNoteViewModel {
|
||||
pollOptions = pollEvent?.pollOptions()
|
||||
valueMaximum = pollEvent?.getTagInt(VALUE_MAXIMUM)
|
||||
valueMinimum = pollEvent?.getTagInt(VALUE_MINIMUM)
|
||||
consensusThreshold = pollEvent?.getTagInt(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)
|
||||
consensusThreshold = pollEvent?.getTagInt(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
|
||||
closedAt = pollEvent?.getTagInt(CLOSED_AT)
|
||||
|
||||
totalZapped = totalZapped()
|
||||
}
|
||||
|
||||
fun isVoteAmountAtomic() = valueMaximum != null && valueMinimum != null && valueMinimum == valueMaximum
|
||||
@@ -73,47 +78,39 @@ class PollNoteViewModel {
|
||||
return false
|
||||
}
|
||||
|
||||
fun optionVoteTally(op: Int): Float {
|
||||
val tally = zappedPollOptionAmount(op).toFloat().div(zappedVoteTotal())
|
||||
return if (tally.isNaN()) { // catch div by 0
|
||||
0f
|
||||
} else { tally }
|
||||
}
|
||||
|
||||
private fun zappedVoteTotal(): Float {
|
||||
var total = 0f
|
||||
pollOptions?.keys?.forEach {
|
||||
total += zappedPollOptionAmount(it).toFloat()
|
||||
fun optionVoteTally(op: Int): BigDecimal {
|
||||
return if (totalZapped.compareTo(BigDecimal.ZERO) > 0) {
|
||||
zappedPollOptionAmount(op).divide(totalZapped, 2, RoundingMode.HALF_UP)
|
||||
} else {
|
||||
BigDecimal.ZERO
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
fun isPollOptionZappedBy(option: Int, user: User): Boolean {
|
||||
if (pollNote?.zaps?.any { it.key.author == user } == true) {
|
||||
pollNote!!.zaps.mapNotNull { it.value?.event }
|
||||
.filterIsInstance<LnZapEvent>()
|
||||
.map {
|
||||
val zappedOption = it.zappedPollOption()
|
||||
if (zappedOption == option && it.zappedRequestAuthor() == user.pubkeyHex) {
|
||||
return true
|
||||
}
|
||||
if (pollNote?.zaps?.any { it.key.author === user } == true) {
|
||||
pollNote!!.zaps
|
||||
.any {
|
||||
val event = it.value?.event as? LnZapEvent
|
||||
event?.zappedPollOption() == option && event.zappedRequestAuthor() == user.pubkeyHex
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun zappedPollOptionAmount(option: Int): BigDecimal {
|
||||
return if (pollNote != null) {
|
||||
pollNote!!.zaps.mapNotNull { it.value?.event }
|
||||
.filterIsInstance<LnZapEvent>()
|
||||
.mapNotNull {
|
||||
val zappedOption = it.zappedPollOption()
|
||||
if (zappedOption == option) {
|
||||
it.amount
|
||||
} else { null }
|
||||
}.sumOf { it }
|
||||
} else {
|
||||
BigDecimal(0)
|
||||
}
|
||||
return pollNote?.zaps?.values?.sumOf {
|
||||
val event = it?.event as? LnZapEvent
|
||||
if (event?.zappedPollOption() == option) {
|
||||
event.amount ?: BigDecimal(0)
|
||||
} else {
|
||||
BigDecimal(0)
|
||||
}
|
||||
} ?: BigDecimal(0)
|
||||
}
|
||||
|
||||
fun totalZapped(): BigDecimal {
|
||||
return pollNote?.zaps?.values?.sumOf {
|
||||
(it?.event as? LnZapEvent)?.amount ?: BigDecimal(0)
|
||||
} ?: BigDecimal(0)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user