Fixes Poll Caching problems.

This commit is contained in:
Vitor Pamplona
2024-02-29 18:51:39 -05:00
parent 600aab5e8d
commit 4d2a7bbc32
3 changed files with 71 additions and 68 deletions
@@ -1432,7 +1432,7 @@ fun RenderPoll(
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
val noteEvent = note.event as? PollNoteEvent ?: return val noteEvent = note.event as? PollNoteEvent ?: return
val eventContent = remember(note) { noteEvent.content() } val eventContent = noteEvent.content()
if (makeItShort && accountViewModel.isLoggedUser(note.author)) { if (makeItShort && accountViewModel.isLoggedUser(note.author)) {
Text( Text(
@@ -68,7 +68,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup import androidx.compose.ui.window.Popup
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
@@ -101,7 +100,7 @@ fun PollNote(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
val pollViewModel: PollNoteViewModel = viewModel(key = "PollNoteViewModel") val pollViewModel: PollNoteViewModel = viewModel(key = "PollNoteViewModel${baseNote.idHex}")
pollViewModel.load(accountViewModel.account, baseNote) pollViewModel.load(accountViewModel.account, baseNote)
@@ -126,11 +125,9 @@ fun PollNote(
) { ) {
WatchZapsAndUpdateTallies(baseNote, pollViewModel) WatchZapsAndUpdateTallies(baseNote, pollViewModel)
val tallies by pollViewModel.tallies.collectAsStateWithLifecycle() pollViewModel.tallies.forEach { option ->
tallies.forEach { poll_op ->
OptionNote( OptionNote(
poll_op, option,
pollViewModel, pollViewModel,
baseNote, baseNote,
accountViewModel, accountViewModel,
@@ -167,9 +164,9 @@ private fun OptionNote(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 3.dp), modifier = Modifier.padding(vertical = 3.dp),
) { ) {
if (!pollViewModel.canZap()) { if (!pollViewModel.canZap.value) {
val color = val color =
if (poolOption.consensusThreadhold) { if (poolOption.consensusThreadhold.value) {
Color.Green.copy(alpha = 0.32f) Color.Green.copy(alpha = 0.32f)
} else { } else {
MaterialTheme.colorScheme.mediumImportanceLink MaterialTheme.colorScheme.mediumImportanceLink
@@ -181,8 +178,7 @@ private fun OptionNote(
pollViewModel = pollViewModel, pollViewModel = pollViewModel,
nonClickablePrepend = { nonClickablePrepend = {
RenderOptionAfterVote( RenderOptionAfterVote(
poolOption.descriptor, poolOption,
poolOption.tally.toFloat(),
color, color,
canPreview, canPreview,
tags, tags,
@@ -220,8 +216,7 @@ private fun OptionNote(
@Composable @Composable
private fun RenderOptionAfterVote( private fun RenderOptionAfterVote(
description: String, poolOption: PollOption,
totalRatio: Float,
color: Color, color: Color,
canPreview: Boolean, canPreview: Boolean,
tags: ImmutableListOfLists<String>, tags: ImmutableListOfLists<String>,
@@ -229,10 +224,9 @@ private fun RenderOptionAfterVote(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
val totalPercentage = remember(totalRatio) { "${(totalRatio * 100).roundToInt()}%" }
Box( Box(
Modifier.fillMaxWidth(0.75f) Modifier
.fillMaxWidth(0.75f)
.clip(shape = QuoteBorder) .clip(shape = QuoteBorder)
.border( .border(
2.dp, 2.dp,
@@ -243,7 +237,9 @@ private fun RenderOptionAfterVote(
LinearProgressIndicator( LinearProgressIndicator(
modifier = Modifier.matchParentSize(), modifier = Modifier.matchParentSize(),
color = color, color = color,
progress = totalRatio, progress = {
poolOption.tally.value.toFloat()
},
) )
Row( Row(
@@ -251,21 +247,31 @@ private fun RenderOptionAfterVote(
) { ) {
Column( Column(
horizontalAlignment = Alignment.End, horizontalAlignment = Alignment.End,
modifier = remember { Modifier.padding(horizontal = 10.dp).width(40.dp) }, modifier =
remember {
Modifier
.padding(horizontal = 10.dp)
.width(45.dp)
},
) { ) {
Text( Text(
text = totalPercentage, text = "${(poolOption.tally.value.toFloat() * 100).roundToInt()}%",
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
) )
} }
Column( Column(
modifier = remember { Modifier.fillMaxWidth().padding(15.dp) }, modifier =
remember {
Modifier
.fillMaxWidth()
.padding(vertical = 15.dp, horizontal = 10.dp)
},
) { ) {
TranslatableRichTextViewer( TranslatableRichTextViewer(
description, poolOption.descriptor,
canPreview, canPreview,
remember { Modifier }, Modifier,
tags, tags,
backgroundColor, backgroundColor,
accountViewModel, accountViewModel,
@@ -286,7 +292,8 @@ private fun RenderOptionBeforeVote(
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
Box( Box(
Modifier.fillMaxWidth(0.75f) Modifier
.fillMaxWidth(0.75f)
.clip(shape = QuoteBorder) .clip(shape = QuoteBorder)
.border( .border(
2.dp, 2.dp,
@@ -358,7 +365,7 @@ fun ZapVote(
R.string.poll_unable_to_vote, R.string.poll_unable_to_vote,
R.string.poll_author_no_vote, R.string.poll_author_no_vote,
) )
} else if (pollViewModel.isVoteAmountAtomic() && poolOption.zappedByLoggedIn) { } else if (pollViewModel.isVoteAmountAtomic() && poolOption.zappedByLoggedIn.value) {
// only allow one vote per option when min==max, i.e. atomic vote amount specified // only allow one vote per option when min==max, i.e. atomic vote amount specified
accountViewModel.toast( accountViewModel.toast(
R.string.poll_unable_to_vote, R.string.poll_unable_to_vote,
@@ -443,7 +450,7 @@ fun ZapVote(
clickablePrepend() clickablePrepend()
if (poolOption.zappedByLoggedIn) { if (poolOption.zappedByLoggedIn.value) {
zappingProgress = 1f zappingProgress = 1f
Icon( Icon(
imageVector = Icons.Default.Bolt, imageVector = Icons.Default.Bolt,
@@ -471,8 +478,8 @@ fun ZapVote(
} }
// only show tallies after a user has zapped note // only show tallies after a user has zapped note
if (!pollViewModel.canZap()) { if (!pollViewModel.canZap.value) {
val amountStr = remember(poolOption.zappedValue) { showAmount(poolOption.zappedValue) } val amountStr = remember(poolOption.zappedValue.value) { showAmount(poolOption.zappedValue.value) }
Text( Text(
text = amountStr, text = amountStr,
fontSize = Font14SP, fontSize = Font14SP,
@@ -20,8 +20,9 @@
*/ */
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import androidx.compose.runtime.Immutable import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
@@ -36,20 +37,18 @@ import com.vitorpamplona.quartz.events.VALUE_MAXIMUM
import com.vitorpamplona.quartz.events.VALUE_MINIMUM import com.vitorpamplona.quartz.events.VALUE_MINIMUM
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
@Immutable @Stable
data class PollOption( data class PollOption(
val option: Int, val option: Int,
val descriptor: String, val descriptor: String,
val zappedValue: BigDecimal, var zappedValue: MutableState<BigDecimal> = mutableStateOf(BigDecimal.ZERO),
val tally: BigDecimal, var tally: MutableState<BigDecimal> = mutableStateOf(BigDecimal.ZERO),
val consensusThreadhold: Boolean, var consensusThreadhold: MutableState<Boolean> = mutableStateOf(false),
val zappedByLoggedIn: Boolean, var zappedByLoggedIn: MutableState<Boolean> = mutableStateOf(false),
) )
@Stable @Stable
@@ -70,8 +69,8 @@ class PollNoteViewModel : ViewModel() {
private var totalZapped: BigDecimal = BigDecimal.ZERO private var totalZapped: BigDecimal = BigDecimal.ZERO
private var wasZappedByLoggedInAccount: Boolean = false private var wasZappedByLoggedInAccount: Boolean = false
private val _tallies = MutableStateFlow<List<PollOption>>(emptyList()) var canZap = mutableStateOf(false)
val tallies = _tallies.asStateFlow() var tallies: List<PollOption> = emptyList()
fun load( fun load(
acc: Account, acc: Account,
@@ -88,47 +87,44 @@ class PollNoteViewModel : ViewModel() {
consensusThreshold = consensusThreshold =
pollEvent?.getTagLong(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal() pollEvent?.getTagLong(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
closedAt = pollEvent?.getTagLong(CLOSED_AT) closedAt = pollEvent?.getTagLong(CLOSED_AT)
totalZapped = BigDecimal.ZERO
wasZappedByLoggedInAccount = false
canZap.value = checkIfCanZap()
tallies = pollOptions?.keys?.map { option ->
PollOption(
option,
pollOptions?.get(option) ?: "",
)
} ?: emptyList()
} }
fun refreshTallies() { fun refreshTallies() {
viewModelScope.launch(Dispatchers.Default) { viewModelScope.launch(Dispatchers.Default) {
totalZapped = totalZapped() totalZapped = totalZapped()
wasZappedByLoggedInAccount = false wasZappedByLoggedInAccount = false
account?.calculateIfNoteWasZappedByAccount(pollNote) { wasZappedByLoggedInAccount = true } account?.calculateIfNoteWasZappedByAccount(pollNote) {
wasZappedByLoggedInAccount = true
canZap.value = checkIfCanZap()
}
val newOptions = tallies.forEach {
pollOptions?.keys?.map { option -> it.zappedValue.value = zappedPollOptionAmount(it.option)
val zappedInOption = zappedPollOptionAmount(option) it.tally.value =
if (totalZapped.compareTo(BigDecimal.ZERO) > 0) {
val myTally = it.zappedValue.value.divide(totalZapped, 2, RoundingMode.HALF_UP)
if (totalZapped.compareTo(BigDecimal.ZERO) > 0) { } else {
zappedInOption.divide(totalZapped, 2, RoundingMode.HALF_UP) BigDecimal.ZERO
} else { }
BigDecimal.ZERO it.consensusThreadhold.value = consensusThreshold != null && it.tally.value >= consensusThreshold!!
} it.zappedByLoggedIn.value = account?.userProfile()?.let { it1 -> cachedIsPollOptionZappedBy(it.option, it1) } ?: false
}
val cachedZappedByLoggedIn =
account?.userProfile()?.let { it1 -> cachedIsPollOptionZappedBy(option, it1) } ?: false
val consensus = consensusThreshold != null && myTally >= consensusThreshold!!
PollOption(
option,
pollOptions?.get(option) ?: "",
zappedInOption,
myTally,
consensus,
cachedZappedByLoggedIn,
)
}
_tallies.emit(
newOptions ?: emptyList(),
)
} }
} }
fun canZap(): Boolean { fun checkIfCanZap(): Boolean {
val account = account ?: return false val account = account ?: return false
val note = pollNote ?: return false val note = pollNote ?: return false
return account.userProfile() != note.author && !wasZappedByLoggedInAccount return account.userProfile() != note.author && !wasZappedByLoggedInAccount