From d07c1b098ed0b5c2d9e49512146da4bfb5590b1f Mon Sep 17 00:00:00 2001 From: Levi Bard Date: Fri, 17 Sep 2021 22:12:17 +0200 Subject: [PATCH] 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 --- .../tusky/adapter/PollAdapter.kt | 10 +++++++- .../tusky/adapter/StatusBaseViewHolder.java | 2 +- .../notifications/NotificationHelper.java | 5 +++- .../com/keylesspalace/tusky/entity/Poll.kt | 3 ++- .../tusky/util/StatusViewHelper.kt | 9 ++++++- .../tusky/viewdata/PollViewData.kt | 24 ++++++++++++------- .../main/res/values-night/theme_colors.xml | 1 + app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/styles.xml | 2 ++ app/src/main/res/values/theme_colors.xml | 1 + .../com/keylesspalace/tusky/FilterTest.kt | 3 ++- .../timeline/TimelineViewModelTest.kt | 1 + 12 files changed, 47 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt index 89b3915e..1a60d860 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt @@ -18,8 +18,10 @@ package com.keylesspalace.tusky.adapter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.emoji.text.EmojiCompat import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R import com.keylesspalace.tusky.databinding.ItemPollBinding import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.util.BindingHolder @@ -85,13 +87,19 @@ class PollAdapter : RecyclerView.Adapter>() { when (mode) { RESULT -> { 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) resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText) val level = percent * 100 + val optionColor = if (option.voted) { + R.color.colorBackgroundHighlight + } else { + R.color.colorBackgroundAccent + } resultTextView.background.level = level + resultTextView.background.setTint(ContextCompat.getColor(resultTextView.context, optionColor)) resultTextView.setOnClickListener(resultClickListener) } SINGLE -> { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 36198d28..435f3ac1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -889,7 +889,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { for (int i = 0; i < args.length; i++) { if (i < options.size()) { 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 { args[i] = ""; } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java index 2218e0b6..c0bf149f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java @@ -659,9 +659,12 @@ public class NotificationHelper { StringBuilder builder = new StringBuilder(notification.getStatus().getContent()); builder.append('\n'); Poll poll = notification.getStatus().getPoll(); - for(PollOption option: poll.getOptions()) { + List options = poll.getOptions(); + for(int i = 0; i < options.size(); ++i) { + PollOption option = options.get(i); builder.append(buildDescription(option.getTitle(), PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotersCount(), poll.getVotesCount()), + poll.getOwnVotes() != null && poll.getOwnVotes().contains(i), context)); builder.append('\n'); } diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt index 1a4c2354..584b76f8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt @@ -11,7 +11,8 @@ data class Poll( @SerializedName("votes_count") val votesCount: Int, @SerializedName("voters_count") val votersCount: Int?, // nullable for compatibility with Pleroma val options: List, - val voted: Boolean + val voted: Boolean, + @SerializedName("own_votes") val ownVotes: List? ) { fun votedCopy(choices: List): Poll { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt index 00f6699d..f2e48f4a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt @@ -23,6 +23,7 @@ import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.annotation.DrawableRes +import androidx.core.content.ContextCompat import com.bumptech.glide.Glide import com.keylesspalace.tusky.R import com.keylesspalace.tusky.entity.Attachment @@ -310,13 +311,19 @@ class StatusViewHelper(private val itemView: View) { if (i < options.size) { 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].visibility = View.VISIBLE 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.setTint(ContextCompat.getColor(pollResults[i].context, optionColor)) } else { pollResults[i].visibility = View.GONE } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt index 0cd73bc9..3dc5ca10 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt @@ -39,7 +39,8 @@ data class PollViewData( data class PollOptionViewData( val title: String, var votesCount: Int, - var selected: Boolean + var selected: Boolean, + var voted: Boolean ) 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 { - return SpannableStringBuilder(context.getString(R.string.poll_percent_format, percent).parseAsHtml()) - .append(" ") - .append(title) +fun buildDescription(title: String, percent: Int, voted: Boolean, context: Context): Spanned { + val builder = SpannableStringBuilder(context.getString(R.string.poll_percent_format, percent).parseAsHtml()) + if (voted) { + builder.append(" ✓ ") + } else { + builder.append(" ") + } + return builder.append(title) } fun Poll?.toViewData(): PollViewData? { @@ -66,15 +71,16 @@ fun Poll?.toViewData(): PollViewData? { multiple = multiple, votesCount = votesCount, votersCount = votersCount, - options = options.map { it.toViewData() }, - voted = voted + options = options.mapIndexed { index, option -> option.toViewData(ownVotes?.contains(index) == true) }, + voted = voted, ) } -fun PollOption.toViewData(): PollOptionViewData { +fun PollOption.toViewData(voted: Boolean): PollOptionViewData { return PollOptionViewData( title = title, votesCount = votesCount, - selected = false + selected = false, + voted = voted ) } diff --git a/app/src/main/res/values-night/theme_colors.xml b/app/src/main/res/values-night/theme_colors.xml index 3e4d032c..e45c45fb 100644 --- a/app/src/main/res/values-night/theme_colors.xml +++ b/app/src/main/res/values-night/theme_colors.xml @@ -15,6 +15,7 @@ @color/tusky_grey_70 @color/tusky_grey_30 + @color/tusky_grey_50 @color/tusky_grey_25 @color/tusky_orange diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 3dd3e30f..66109928 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -10,6 +10,7 @@ + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 693c8427..c9f26e4e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -54,6 +54,7 @@ @color/colorBackground @color/colorBackgroundAccent + @color/colorBackgroundHighlight @color/windowBackground @color/textColorPrimary @@ -142,6 +143,7 @@ @color/tusky_grey_10 @color/tusky_grey_40 + @color/tusky_grey_40 @color/tusky_grey_20 @color/tusky_grey_10 diff --git a/app/src/main/res/values/theme_colors.xml b/app/src/main/res/values/theme_colors.xml index c1f14f38..bf5b79fc 100644 --- a/app/src/main/res/values/theme_colors.xml +++ b/app/src/main/res/values/theme_colors.xml @@ -15,6 +15,7 @@ @color/tusky_grey_50 @color/tusky_grey_70 + @color/tusky_grey_50 @color/tusky_grey_80 @color/tusky_orange_light diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt index 1c6a19c6..a8a5aa68 100644 --- a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt @@ -173,7 +173,8 @@ class FilterTest { options = pollOptions.map { PollOption(it, 0) }, - voted = false + voted = false, + ownVotes = null ) } else null, card = null diff --git a/app/src/test/java/com/keylesspalace/tusky/components/timeline/TimelineViewModelTest.kt b/app/src/test/java/com/keylesspalace/tusky/components/timeline/TimelineViewModelTest.kt index a5665de9..9116f29f 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/timeline/TimelineViewModelTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/timeline/TimelineViewModelTest.kt @@ -697,6 +697,7 @@ class TimelineViewModelTest { votesCount = 1, voted = false, options = listOf(PollOption("1", 1), PollOption("2", 2)), + ownVotes = null ) val status4 = makeStatus("4").copy(poll = poll) val status3 = makeStatus("3")