Highlight your own votes when displaying poll results (#2242)
* Highlight your own votes when displaying poll results * Unbreak tests * Add a checkmark to the description of self-voted options
This commit is contained in:
parent
45598cf047
commit
d07c1b098e
12 changed files with 47 additions and 15 deletions
|
@ -18,8 +18,10 @@ package com.keylesspalace.tusky.adapter
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.emoji.text.EmojiCompat
|
import androidx.emoji.text.EmojiCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.databinding.ItemPollBinding
|
import com.keylesspalace.tusky.databinding.ItemPollBinding
|
||||||
import com.keylesspalace.tusky.entity.Emoji
|
import com.keylesspalace.tusky.entity.Emoji
|
||||||
import com.keylesspalace.tusky.util.BindingHolder
|
import com.keylesspalace.tusky.util.BindingHolder
|
||||||
|
@ -85,13 +87,19 @@ class PollAdapter : RecyclerView.Adapter<BindingHolder<ItemPollBinding>>() {
|
||||||
when (mode) {
|
when (mode) {
|
||||||
RESULT -> {
|
RESULT -> {
|
||||||
val percent = calculatePercent(option.votesCount, votersCount, voteCount)
|
val percent = calculatePercent(option.votesCount, votersCount, voteCount)
|
||||||
val emojifiedPollOptionText = buildDescription(option.title, percent, resultTextView.context)
|
val emojifiedPollOptionText = buildDescription(option.title, percent, option.voted, resultTextView.context)
|
||||||
.emojify(emojis, resultTextView, animateEmojis)
|
.emojify(emojis, resultTextView, animateEmojis)
|
||||||
resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
|
resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
|
||||||
|
|
||||||
val level = percent * 100
|
val level = percent * 100
|
||||||
|
val optionColor = if (option.voted) {
|
||||||
|
R.color.colorBackgroundHighlight
|
||||||
|
} else {
|
||||||
|
R.color.colorBackgroundAccent
|
||||||
|
}
|
||||||
|
|
||||||
resultTextView.background.level = level
|
resultTextView.background.level = level
|
||||||
|
resultTextView.background.setTint(ContextCompat.getColor(resultTextView.context, optionColor))
|
||||||
resultTextView.setOnClickListener(resultClickListener)
|
resultTextView.setOnClickListener(resultClickListener)
|
||||||
}
|
}
|
||||||
SINGLE -> {
|
SINGLE -> {
|
||||||
|
|
|
@ -889,7 +889,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
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.getVotersCount(), 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, options.get(i).getVoted(), context);
|
||||||
} else {
|
} else {
|
||||||
args[i] = "";
|
args[i] = "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -659,9 +659,12 @@ public class NotificationHelper {
|
||||||
StringBuilder builder = new StringBuilder(notification.getStatus().getContent());
|
StringBuilder builder = new StringBuilder(notification.getStatus().getContent());
|
||||||
builder.append('\n');
|
builder.append('\n');
|
||||||
Poll poll = notification.getStatus().getPoll();
|
Poll poll = notification.getStatus().getPoll();
|
||||||
for(PollOption option: poll.getOptions()) {
|
List<PollOption> options = poll.getOptions();
|
||||||
|
for(int i = 0; i < options.size(); ++i) {
|
||||||
|
PollOption option = options.get(i);
|
||||||
builder.append(buildDescription(option.getTitle(),
|
builder.append(buildDescription(option.getTitle(),
|
||||||
PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotersCount(), poll.getVotesCount()),
|
PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotersCount(), poll.getVotesCount()),
|
||||||
|
poll.getOwnVotes() != null && poll.getOwnVotes().contains(i),
|
||||||
context));
|
context));
|
||||||
builder.append('\n');
|
builder.append('\n');
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ data class Poll(
|
||||||
@SerializedName("votes_count") val votesCount: Int,
|
@SerializedName("votes_count") val votesCount: Int,
|
||||||
@SerializedName("voters_count") val votersCount: Int?, // nullable for compatibility with Pleroma
|
@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,
|
||||||
|
@SerializedName("own_votes") val ownVotes: List<Int>?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun votedCopy(choices: List<Int>): Poll {
|
fun votedCopy(choices: List<Int>): Poll {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
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
|
||||||
|
@ -310,13 +311,19 @@ class StatusViewHelper(private val itemView: View) {
|
||||||
if (i < options.size) {
|
if (i < options.size) {
|
||||||
val percent = calculatePercent(options[i].votesCount, poll.votersCount, 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, options[i].voted, pollResults[i].context)
|
||||||
pollResults[i].text = pollOptionText.emojify(emojis, pollResults[i], animateEmojis)
|
pollResults[i].text = pollOptionText.emojify(emojis, pollResults[i], animateEmojis)
|
||||||
pollResults[i].visibility = View.VISIBLE
|
pollResults[i].visibility = View.VISIBLE
|
||||||
|
|
||||||
val level = percent * 100
|
val level = percent * 100
|
||||||
|
val optionColor = if (options[i].voted) {
|
||||||
|
R.color.colorBackgroundHighlight
|
||||||
|
} else {
|
||||||
|
R.color.colorBackgroundAccent
|
||||||
|
}
|
||||||
|
|
||||||
pollResults[i].background.level = level
|
pollResults[i].background.level = level
|
||||||
|
pollResults[i].background.setTint(ContextCompat.getColor(pollResults[i].context, optionColor))
|
||||||
} else {
|
} else {
|
||||||
pollResults[i].visibility = View.GONE
|
pollResults[i].visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,8 @@ data class PollViewData(
|
||||||
data class PollOptionViewData(
|
data class PollOptionViewData(
|
||||||
val title: String,
|
val title: String,
|
||||||
var votesCount: Int,
|
var votesCount: Int,
|
||||||
var selected: Boolean
|
var selected: Boolean,
|
||||||
|
var voted: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
fun calculatePercent(fraction: Int, totalVoters: Int?, totalVotes: Int): Int {
|
fun calculatePercent(fraction: Int, totalVoters: Int?, totalVotes: Int): Int {
|
||||||
|
@ -51,10 +52,14 @@ fun calculatePercent(fraction: Int, totalVoters: Int?, totalVotes: Int): Int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildDescription(title: String, percent: Int, context: Context): Spanned {
|
fun buildDescription(title: String, percent: Int, voted: Boolean, context: Context): Spanned {
|
||||||
return SpannableStringBuilder(context.getString(R.string.poll_percent_format, percent).parseAsHtml())
|
val builder = SpannableStringBuilder(context.getString(R.string.poll_percent_format, percent).parseAsHtml())
|
||||||
.append(" ")
|
if (voted) {
|
||||||
.append(title)
|
builder.append(" ✓ ")
|
||||||
|
} else {
|
||||||
|
builder.append(" ")
|
||||||
|
}
|
||||||
|
return builder.append(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Poll?.toViewData(): PollViewData? {
|
fun Poll?.toViewData(): PollViewData? {
|
||||||
|
@ -66,15 +71,16 @@ fun Poll?.toViewData(): PollViewData? {
|
||||||
multiple = multiple,
|
multiple = multiple,
|
||||||
votesCount = votesCount,
|
votesCount = votesCount,
|
||||||
votersCount = votersCount,
|
votersCount = votersCount,
|
||||||
options = options.map { it.toViewData() },
|
options = options.mapIndexed { index, option -> option.toViewData(ownVotes?.contains(index) == true) },
|
||||||
voted = voted
|
voted = voted,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PollOption.toViewData(): PollOptionViewData {
|
fun PollOption.toViewData(voted: Boolean): PollOptionViewData {
|
||||||
return PollOptionViewData(
|
return PollOptionViewData(
|
||||||
title = title,
|
title = title,
|
||||||
votesCount = votesCount,
|
votesCount = votesCount,
|
||||||
selected = false
|
selected = false,
|
||||||
|
voted = voted
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<color name="iconColor">@color/tusky_grey_70</color>
|
<color name="iconColor">@color/tusky_grey_70</color>
|
||||||
|
|
||||||
<color name="colorBackgroundAccent">@color/tusky_grey_30</color>
|
<color name="colorBackgroundAccent">@color/tusky_grey_30</color>
|
||||||
|
<color name="colorBackgroundHighlight">@color/tusky_grey_50</color>
|
||||||
<color name="dividerColor">@color/tusky_grey_25</color>
|
<color name="dividerColor">@color/tusky_grey_25</color>
|
||||||
|
|
||||||
<color name="favoriteButtonActiveColor">@color/tusky_orange</color>
|
<color name="favoriteButtonActiveColor">@color/tusky_orange</color>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
<!--Themed Attributes-->
|
<!--Themed Attributes-->
|
||||||
<attr name="colorBackgroundAccent" format="reference|color" />
|
<attr name="colorBackgroundAccent" format="reference|color" />
|
||||||
|
<attr name="colorBackgroundHighlight" format="reference|color" />
|
||||||
<attr name="textColorDisabled" format="reference|color" />
|
<attr name="textColorDisabled" format="reference|color" />
|
||||||
<attr name="iconColor" format="reference|color" />
|
<attr name="iconColor" format="reference|color" />
|
||||||
<attr name="windowBackgroundColor" format="reference|color" />
|
<attr name="windowBackgroundColor" format="reference|color" />
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
|
|
||||||
<item name="android:colorBackground">@color/colorBackground</item>
|
<item name="android:colorBackground">@color/colorBackground</item>
|
||||||
<item name="colorBackgroundAccent">@color/colorBackgroundAccent</item>
|
<item name="colorBackgroundAccent">@color/colorBackgroundAccent</item>
|
||||||
|
<item name="colorBackgroundHighlight">@color/colorBackgroundHighlight</item>
|
||||||
<item name="windowBackgroundColor">@color/windowBackground</item>
|
<item name="windowBackgroundColor">@color/windowBackground</item>
|
||||||
|
|
||||||
<item name="android:textColorPrimary">@color/textColorPrimary</item>
|
<item name="android:textColorPrimary">@color/textColorPrimary</item>
|
||||||
|
@ -142,6 +143,7 @@
|
||||||
<item name="colorSurface">@color/tusky_grey_10</item>
|
<item name="colorSurface">@color/tusky_grey_10</item>
|
||||||
|
|
||||||
<item name="iconColor">@color/tusky_grey_40</item>
|
<item name="iconColor">@color/tusky_grey_40</item>
|
||||||
|
<item name="colorBackgroundHighlight">@color/tusky_grey_40</item>
|
||||||
<item name="colorBackgroundAccent">@color/tusky_grey_20</item>
|
<item name="colorBackgroundAccent">@color/tusky_grey_20</item>
|
||||||
|
|
||||||
<item name="dividerColor">@color/tusky_grey_10</item>
|
<item name="dividerColor">@color/tusky_grey_10</item>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<color name="iconColor">@color/tusky_grey_50</color>
|
<color name="iconColor">@color/tusky_grey_50</color>
|
||||||
|
|
||||||
<color name="colorBackgroundAccent">@color/tusky_grey_70</color>
|
<color name="colorBackgroundAccent">@color/tusky_grey_70</color>
|
||||||
|
<color name="colorBackgroundHighlight">@color/tusky_grey_50</color>
|
||||||
<color name="dividerColor">@color/tusky_grey_80</color>
|
<color name="dividerColor">@color/tusky_grey_80</color>
|
||||||
|
|
||||||
<color name="favoriteButtonActiveColor">@color/tusky_orange_light</color>
|
<color name="favoriteButtonActiveColor">@color/tusky_orange_light</color>
|
||||||
|
|
|
@ -173,7 +173,8 @@ class FilterTest {
|
||||||
options = pollOptions.map {
|
options = pollOptions.map {
|
||||||
PollOption(it, 0)
|
PollOption(it, 0)
|
||||||
},
|
},
|
||||||
voted = false
|
voted = false,
|
||||||
|
ownVotes = null
|
||||||
)
|
)
|
||||||
} else null,
|
} else null,
|
||||||
card = null
|
card = null
|
||||||
|
|
|
@ -697,6 +697,7 @@ class TimelineViewModelTest {
|
||||||
votesCount = 1,
|
votesCount = 1,
|
||||||
voted = false,
|
voted = false,
|
||||||
options = listOf(PollOption("1", 1), PollOption("2", 2)),
|
options = listOf(PollOption("1", 1), PollOption("2", 2)),
|
||||||
|
ownVotes = null
|
||||||
)
|
)
|
||||||
val status4 = makeStatus("4").copy(poll = poll)
|
val status4 = makeStatus("4").copy(poll = poll)
|
||||||
val status3 = makeStatus("3")
|
val status3 = makeStatus("3")
|
||||||
|
|
Loading…
Reference in a new issue