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:
parent
8cb83050ac
commit
cf782f039f
8 changed files with 60 additions and 30 deletions
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
|
@ -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])
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue