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

View File

@ -1432,7 +1432,7 @@ fun RenderPoll(
nav: (String) -> Unit,
) {
val noteEvent = note.event as? PollNoteEvent ?: return
val eventContent = remember(note) { noteEvent.content() }
val eventContent = noteEvent.content()
if (makeItShort && accountViewModel.isLoggedUser(note.author)) {
Text(

View File

@ -68,7 +68,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
@ -101,7 +100,7 @@ fun PollNote(
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val pollViewModel: PollNoteViewModel = viewModel(key = "PollNoteViewModel")
val pollViewModel: PollNoteViewModel = viewModel(key = "PollNoteViewModel${baseNote.idHex}")
pollViewModel.load(accountViewModel.account, baseNote)
@ -126,11 +125,9 @@ fun PollNote(
) {
WatchZapsAndUpdateTallies(baseNote, pollViewModel)
val tallies by pollViewModel.tallies.collectAsStateWithLifecycle()
tallies.forEach { poll_op ->
pollViewModel.tallies.forEach { option ->
OptionNote(
poll_op,
option,
pollViewModel,
baseNote,
accountViewModel,
@ -167,9 +164,9 @@ private fun OptionNote(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 3.dp),
) {
if (!pollViewModel.canZap()) {
if (!pollViewModel.canZap.value) {
val color =
if (poolOption.consensusThreadhold) {
if (poolOption.consensusThreadhold.value) {
Color.Green.copy(alpha = 0.32f)
} else {
MaterialTheme.colorScheme.mediumImportanceLink
@ -181,8 +178,7 @@ private fun OptionNote(
pollViewModel = pollViewModel,
nonClickablePrepend = {
RenderOptionAfterVote(
poolOption.descriptor,
poolOption.tally.toFloat(),
poolOption,
color,
canPreview,
tags,
@ -220,8 +216,7 @@ private fun OptionNote(
@Composable
private fun RenderOptionAfterVote(
description: String,
totalRatio: Float,
poolOption: PollOption,
color: Color,
canPreview: Boolean,
tags: ImmutableListOfLists<String>,
@ -229,10 +224,9 @@ private fun RenderOptionAfterVote(
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val totalPercentage = remember(totalRatio) { "${(totalRatio * 100).roundToInt()}%" }
Box(
Modifier.fillMaxWidth(0.75f)
Modifier
.fillMaxWidth(0.75f)
.clip(shape = QuoteBorder)
.border(
2.dp,
@ -243,7 +237,9 @@ private fun RenderOptionAfterVote(
LinearProgressIndicator(
modifier = Modifier.matchParentSize(),
color = color,
progress = totalRatio,
progress = {
poolOption.tally.value.toFloat()
},
)
Row(
@ -251,21 +247,31 @@ private fun RenderOptionAfterVote(
) {
Column(
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 = totalPercentage,
text = "${(poolOption.tally.value.toFloat() * 100).roundToInt()}%",
fontWeight = FontWeight.Bold,
)
}
Column(
modifier = remember { Modifier.fillMaxWidth().padding(15.dp) },
modifier =
remember {
Modifier
.fillMaxWidth()
.padding(vertical = 15.dp, horizontal = 10.dp)
},
) {
TranslatableRichTextViewer(
description,
poolOption.descriptor,
canPreview,
remember { Modifier },
Modifier,
tags,
backgroundColor,
accountViewModel,
@ -286,7 +292,8 @@ private fun RenderOptionBeforeVote(
nav: (String) -> Unit,
) {
Box(
Modifier.fillMaxWidth(0.75f)
Modifier
.fillMaxWidth(0.75f)
.clip(shape = QuoteBorder)
.border(
2.dp,
@ -358,7 +365,7 @@ fun ZapVote(
R.string.poll_unable_to_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
accountViewModel.toast(
R.string.poll_unable_to_vote,
@ -443,7 +450,7 @@ fun ZapVote(
clickablePrepend()
if (poolOption.zappedByLoggedIn) {
if (poolOption.zappedByLoggedIn.value) {
zappingProgress = 1f
Icon(
imageVector = Icons.Default.Bolt,
@ -471,8 +478,8 @@ fun ZapVote(
}
// only show tallies after a user has zapped note
if (!pollViewModel.canZap()) {
val amountStr = remember(poolOption.zappedValue) { showAmount(poolOption.zappedValue) }
if (!pollViewModel.canZap.value) {
val amountStr = remember(poolOption.zappedValue.value) { showAmount(poolOption.zappedValue.value) }
Text(
text = amountStr,
fontSize = Font14SP,

View File

@ -20,8 +20,9 @@
*/
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.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
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.utils.TimeUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.math.BigDecimal
import java.math.RoundingMode
@Immutable
@Stable
data class PollOption(
val option: Int,
val descriptor: String,
val zappedValue: BigDecimal,
val tally: BigDecimal,
val consensusThreadhold: Boolean,
val zappedByLoggedIn: Boolean,
var zappedValue: MutableState<BigDecimal> = mutableStateOf(BigDecimal.ZERO),
var tally: MutableState<BigDecimal> = mutableStateOf(BigDecimal.ZERO),
var consensusThreadhold: MutableState<Boolean> = mutableStateOf(false),
var zappedByLoggedIn: MutableState<Boolean> = mutableStateOf(false),
)
@Stable
@ -70,8 +69,8 @@ class PollNoteViewModel : ViewModel() {
private var totalZapped: BigDecimal = BigDecimal.ZERO
private var wasZappedByLoggedInAccount: Boolean = false
private val _tallies = MutableStateFlow<List<PollOption>>(emptyList())
val tallies = _tallies.asStateFlow()
var canZap = mutableStateOf(false)
var tallies: List<PollOption> = emptyList()
fun load(
acc: Account,
@ -88,47 +87,44 @@ class PollNoteViewModel : ViewModel() {
consensusThreshold =
pollEvent?.getTagLong(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
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() {
viewModelScope.launch(Dispatchers.Default) {
totalZapped = totalZapped()
wasZappedByLoggedInAccount = false
account?.calculateIfNoteWasZappedByAccount(pollNote) { wasZappedByLoggedInAccount = true }
account?.calculateIfNoteWasZappedByAccount(pollNote) {
wasZappedByLoggedInAccount = true
canZap.value = checkIfCanZap()
}
val newOptions =
pollOptions?.keys?.map { option ->
val zappedInOption = zappedPollOptionAmount(option)
val myTally =
if (totalZapped.compareTo(BigDecimal.ZERO) > 0) {
zappedInOption.divide(totalZapped, 2, RoundingMode.HALF_UP)
} else {
BigDecimal.ZERO
}
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(),
)
tallies.forEach {
it.zappedValue.value = zappedPollOptionAmount(it.option)
it.tally.value =
if (totalZapped.compareTo(BigDecimal.ZERO) > 0) {
it.zappedValue.value.divide(totalZapped, 2, RoundingMode.HALF_UP)
} else {
BigDecimal.ZERO
}
it.consensusThreadhold.value = consensusThreshold != null && it.tally.value >= consensusThreshold!!
it.zappedByLoggedIn.value = account?.userProfile()?.let { it1 -> cachedIsPollOptionZappedBy(it.option, it1) } ?: false
}
}
}
fun canZap(): Boolean {
fun checkIfCanZap(): Boolean {
val account = account ?: return false
val note = pollNote ?: return false
return account.userProfile() != note.author && !wasZappedByLoggedInAccount