use number of voters instead of votes to calculate poll results (#1733)

* adjust poll vote text, votes -> people

* use number of voters instead of votes to calculate poll results

* fix tests
This commit is contained in:
Konrad Pozniak 2020-03-24 21:06:58 +01:00 committed by GitHub
parent 8cb83050ac
commit cf782f039f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 60 additions and 30 deletions

View file

@ -26,7 +26,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.util.CustomEmojiHelper import com.keylesspalace.tusky.util.CustomEmojiHelper
import com.keylesspalace.tusky.util.HtmlUtils
import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.visible
import com.keylesspalace.tusky.viewdata.PollOptionViewData import com.keylesspalace.tusky.viewdata.PollOptionViewData
import com.keylesspalace.tusky.viewdata.buildDescription import com.keylesspalace.tusky.viewdata.buildDescription
@ -36,12 +35,19 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
private var pollOptions: List<PollOptionViewData> = emptyList() private var pollOptions: List<PollOptionViewData> = emptyList()
private var voteCount: Int = 0 private var voteCount: Int = 0
private var votersCount: Int? = null
private var mode = RESULT private var mode = RESULT
private var emojis: List<Emoji> = emptyList() private var emojis: List<Emoji> = emptyList()
fun setup(options: List<PollOptionViewData>, voteCount: Int, emojis: List<Emoji>, mode: Int) { fun setup(
options: List<PollOptionViewData>,
voteCount: Int,
votersCount: Int?,
emojis: List<Emoji>,
mode: Int) {
this.pollOptions = options this.pollOptions = options
this.voteCount = voteCount this.voteCount = voteCount
this.votersCount = votersCount
this.emojis = emojis this.emojis = emojis
this.mode = mode this.mode = mode
notifyDataSetChanged() notifyDataSetChanged()
@ -71,7 +77,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
when(mode) { when(mode) {
RESULT -> { RESULT -> {
val percent = calculatePercent(option.votesCount, voteCount) val percent = calculatePercent(option.votesCount, votersCount, voteCount)
val emojifiedPollOptionText = CustomEmojiHelper.emojifyText(buildDescription(option.title, percent, holder.resultTextView.context), emojis, holder.resultTextView) val emojifiedPollOptionText = CustomEmojiHelper.emojifyText(buildDescription(option.title, percent, holder.resultTextView.context), emojis, holder.resultTextView)
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText) holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)

View file

@ -869,7 +869,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
List<PollOptionViewData> options = poll.getOptions(); List<PollOptionViewData> options = poll.getOptions();
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if (i < options.size()) { if (i < options.size()) {
int percent = PollViewDataKt.calculatePercent(options.get(i).getVotesCount(), poll.getVotesCount()); int percent = PollViewDataKt.calculatePercent(options.get(i).getVotesCount(), poll.getVotersCount(), poll.getVotesCount());
args[i] = buildDescription(options.get(i).getTitle(), percent, context); args[i] = buildDescription(options.get(i).getTitle(), percent, context);
} else { } else {
args[i] = ""; args[i] = "";
@ -912,12 +912,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
if (expired || poll.getVoted()) { if (expired || poll.getVoted()) {
// no voting possible // no voting possible
pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), emojis, PollAdapter.RESULT); pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), poll.getVotersCount(), emojis, PollAdapter.RESULT);
pollButton.setVisibility(View.GONE); pollButton.setVisibility(View.GONE);
} else { } else {
// voting possible // voting possible
pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), emojis, poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE); pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), poll.getVotersCount(), emojis, poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE);
pollButton.setVisibility(View.VISIBLE); pollButton.setVisibility(View.VISIBLE);
@ -944,8 +944,14 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private CharSequence getPollInfoText(long timestamp, PollViewData poll, private CharSequence getPollInfoText(long timestamp, PollViewData poll,
StatusDisplayOptions statusDisplayOptions, StatusDisplayOptions statusDisplayOptions,
Context context) { Context context) {
String votes = numberFormat.format(poll.getVotesCount()); String votesText;
String votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), votes); if(poll.getVotersCount() == null) {
String voters = numberFormat.format(poll.getVotesCount());
votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), voters);
} else {
String voters = numberFormat.format(poll.getVotersCount());
votesText = context.getResources().getQuantityString(R.plurals.poll_info_people, poll.getVotersCount(), voters);
}
CharSequence pollDurationInfo; CharSequence pollDurationInfo;
if (poll.getExpired()) { if (poll.getExpired()) {
pollDurationInfo = context.getString(R.string.poll_info_closed); pollDurationInfo = context.getString(R.string.poll_info_closed);

View file

@ -9,6 +9,7 @@ data class Poll(
val expired: Boolean, val expired: Boolean,
val multiple: Boolean, val multiple: Boolean,
@SerializedName("votes_count") val votesCount: Int, @SerializedName("votes_count") val votesCount: Int,
@SerializedName("voters_count") val votersCount: Int?, // nullable for compatibility with Pleroma
val options: List<PollOption>, val options: List<PollOption>,
val voted: Boolean val voted: Boolean
) { ) {
@ -22,7 +23,12 @@ data class Poll(
} }
} }
return copy(options = newOptions, votesCount = votesCount + choices.size, voted = true) return copy(
options = newOptions,
votesCount = votesCount + choices.size,
votersCount = votersCount?.plus(1),
voted = true
)
} }
fun toNewPoll(creationDate: Date) = NewPoll( fun toNewPoll(creationDate: Date) = NewPoll(

View file

@ -643,7 +643,7 @@ public class NotificationHelper {
Poll poll = notification.getStatus().getPoll(); Poll poll = notification.getStatus().getPoll();
for(PollOption option: poll.getOptions()) { for(PollOption option: poll.getOptions()) {
builder.append(buildDescription(option.getTitle(), builder.append(buildDescription(option.getTitle(),
PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotesCount()), PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotersCount(), poll.getVotesCount()),
context)); context));
builder.append('\n'); builder.append('\n');
} }

View file

@ -24,7 +24,6 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
@ -275,17 +274,22 @@ class StatusViewHelper(private val itemView: View) {
private fun getPollInfoText(timestamp: Long, poll: PollViewData, pollDescription: TextView, useAbsoluteTime: Boolean): CharSequence { private fun getPollInfoText(timestamp: Long, poll: PollViewData, pollDescription: TextView, useAbsoluteTime: Boolean): CharSequence {
val context = pollDescription.context val context = pollDescription.context
val votes = NumberFormat.getNumberInstance().format(poll.votesCount.toLong())
val votesText = context.resources.getQuantityString(R.plurals.poll_info_votes, poll.votesCount, votes) val votesText = if(poll.votersCount == null) {
val pollDurationInfo: CharSequence val votes = NumberFormat.getNumberInstance().format(poll.votesCount.toLong())
if (poll.expired) { context.resources.getQuantityString(R.plurals.poll_info_votes, poll.votesCount, votes)
pollDurationInfo = context.getString(R.string.poll_info_closed) } else {
val votes = NumberFormat.getNumberInstance().format(poll.votersCount.toLong())
context.resources.getQuantityString(R.plurals.poll_info_people, poll.votersCount, votes)
}
val pollDurationInfo = if (poll.expired) {
context.getString(R.string.poll_info_closed)
} else { } else {
if (useAbsoluteTime) { if (useAbsoluteTime) {
pollDurationInfo = context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.expiresAt)) context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.expiresAt))
} else { } else {
val pollDuration = TimestampUtils.formatPollDuration(context, poll.expiresAt!!.time, timestamp) val pollDuration = TimestampUtils.formatPollDuration(context, poll.expiresAt!!.time, timestamp)
pollDurationInfo = context.getString(R.string.poll_info_time_relative, pollDuration) context.getString(R.string.poll_info_time_relative, pollDuration)
} }
} }
@ -298,7 +302,7 @@ class StatusViewHelper(private val itemView: View) {
for (i in 0 until Status.MAX_POLL_OPTIONS) { for (i in 0 until Status.MAX_POLL_OPTIONS) {
if (i < options.size) { if (i < options.size) {
val percent = calculatePercent(options[i].votesCount, poll.votesCount) val percent = calculatePercent(options[i].votesCount, poll.votersCount, poll.votesCount)
val pollOptionText = buildDescription(options[i].title, percent, pollResults[i].context) val pollOptionText = buildDescription(options[i].title, percent, pollResults[i].context)
pollResults[i].text = CustomEmojiHelper.emojifyText(pollOptionText, emojis, pollResults[i]) pollResults[i].text = CustomEmojiHelper.emojifyText(pollOptionText, emojis, pollResults[i])

View file

@ -31,6 +31,7 @@ data class PollViewData(
val expired: Boolean, val expired: Boolean,
val multiple: Boolean, val multiple: Boolean,
val votesCount: Int, val votesCount: Int,
val votersCount: Int?,
val options: List<PollOptionViewData>, val options: List<PollOptionViewData>,
var voted: Boolean var voted: Boolean
) )
@ -41,10 +42,11 @@ data class PollOptionViewData(
var selected: Boolean var selected: Boolean
) )
fun calculatePercent(fraction: Int, total: Int): Int { fun calculatePercent(fraction: Int, totalVoters: Int?, totalVotes: Int): Int {
return if (fraction == 0) { return if (fraction == 0) {
0 0
} else { } else {
val total = totalVoters ?: totalVotes
(fraction / total.toDouble() * 100).roundToInt() (fraction / total.toDouble() * 100).roundToInt()
} }
} }
@ -58,20 +60,21 @@ fun buildDescription(title: String, percent: Int, context: Context): Spanned {
fun Poll?.toViewData(): PollViewData? { fun Poll?.toViewData(): PollViewData? {
if (this == null) return null if (this == null) return null
return PollViewData( return PollViewData(
id, id = id,
expiresAt, expiresAt = expiresAt,
expired, expired = expired,
multiple, multiple = multiple,
votesCount, votesCount = votesCount,
options.map { it.toViewData() }, votersCount = votersCount,
voted options = options.map { it.toViewData() },
voted = voted
) )
} }
fun PollOption.toViewData(): PollOptionViewData { fun PollOption.toViewData(): PollOptionViewData {
return PollOptionViewData( return PollOptionViewData(
title, title = title,
votesCount, votesCount = votesCount,
false selected = false
) )
} }

View file

@ -494,6 +494,10 @@
<item quantity="one">%s vote</item> <item quantity="one">%s vote</item>
<item quantity="other">%s votes</item> <item quantity="other">%s votes</item>
</plurals> </plurals>
<plurals name="poll_info_people">
<item quantity="one">%s person</item>
<item quantity="other">%s people</item>
</plurals>
<string name="poll_info_time_relative">%s left</string> <string name="poll_info_time_relative">%s left</string>
<string name="poll_info_time_absolute">ends at %s</string> <string name="poll_info_time_absolute">ends at %s</string>
<string name="poll_info_closed">closed</string> <string name="poll_info_closed">closed</string>

View file

@ -222,6 +222,7 @@ class FilterTest {
expired = false, expired = false,
multiple = false, multiple = false,
votesCount = 0, votesCount = 0,
votersCount = 0,
options = pollOptions.map { options = pollOptions.map {
PollOption(it, 0) PollOption(it, 0)
}, },