Show toot stat inline (#3413)
* Show toot stat inline * Correct elements position * Format stats and show it according to setting * inline toot statistics setting * Code formatting * Use kotlin functions * Change the statistics setting description * Use capital letters for all variants * increase the statistics margin * Merge fixes * Code review fixes * move setReblogsCount and setFavouritedCount to StatusViewHolder * code cleaning * code cleaning * import lexicographical order --------- Co-authored-by: Grigorii Ioffe <zikasaks@gmail.com> Co-authored-by: grigoriiioffe <zikasaks@icloud.com>
This commit is contained in:
parent
9087d0ecdd
commit
75e7b9f1a5
|
@ -54,6 +54,7 @@ import com.keylesspalace.tusky.util.CardViewMode;
|
|||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.NumberUtils;
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||
import com.keylesspalace.tusky.util.TimestampUtils;
|
||||
import com.keylesspalace.tusky.util.TouchDelegateHelper;
|
||||
|
@ -388,10 +389,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
}
|
||||
|
||||
private void setReplyCount(int repliesCount) {
|
||||
protected void setReplyCount(int repliesCount) {
|
||||
// This label only exists in the non-detailed view (to match the web ui)
|
||||
if (replyCountLabel != null) {
|
||||
replyCountLabel.setText((repliesCount > 1 ? replyCountLabel.getContext().getString(R.string.status_count_one_plus) : Integer.toString(repliesCount)));
|
||||
replyCountLabel.setText(NumberUtils.shortNumber(repliesCount));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,12 +627,18 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
avatar.setOnClickListener(profileButtonClickListener);
|
||||
displayName.setOnClickListener(profileButtonClickListener);
|
||||
|
||||
if (replyCountLabel != null) {
|
||||
replyCountLabel.setVisibility(statusDisplayOptions.showStatsInline() ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
replyButton.setOnClickListener(v -> {
|
||||
int position = getBindingAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onReply(position);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (reblogButton != null) {
|
||||
reblogButton.setEventListener((button, buttonState) -> {
|
||||
// return true to play animation
|
||||
|
@ -650,6 +657,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
favouriteButton.setEventListener((button, buttonState) -> {
|
||||
// return true to play animation
|
||||
int position = getBindingAdapterPosition();
|
||||
|
|
|
@ -32,6 +32,7 @@ import com.keylesspalace.tusky.entity.Filter;
|
|||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.NumberUtils;
|
||||
import com.keylesspalace.tusky.util.SmartLengthInputFilter;
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||
import com.keylesspalace.tusky.util.StringUtils;
|
||||
|
@ -47,11 +48,15 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
|||
|
||||
private final TextView statusInfo;
|
||||
private final Button contentCollapseButton;
|
||||
private final TextView favouritedCountLabel;
|
||||
private final TextView reblogsCountLabel;
|
||||
|
||||
public StatusViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
statusInfo = itemView.findViewById(R.id.status_info);
|
||||
contentCollapseButton = itemView.findViewById(R.id.button_toggle_content);
|
||||
favouritedCountLabel = itemView.findViewById(R.id.status_favourites_count);
|
||||
reblogsCountLabel = itemView.findViewById(R.id.status_insets);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,6 +82,12 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
reblogsCountLabel.setVisibility(statusDisplayOptions.showStatsInline() ? View.VISIBLE : View.INVISIBLE);
|
||||
favouritedCountLabel.setVisibility(statusDisplayOptions.showStatsInline() ? View.VISIBLE : View.INVISIBLE);
|
||||
setFavouritedCount(status.getActionable().getFavouritesCount());
|
||||
setReblogsCount(status.getActionable().getReblogsCount());
|
||||
|
||||
super.setupWithStatus(status, listener, statusDisplayOptions, payloads);
|
||||
}
|
||||
|
||||
|
@ -102,6 +113,14 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
|||
statusInfo.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
protected void setReblogsCount(int reblogsCount) {
|
||||
reblogsCountLabel.setText(NumberUtils.shortNumber(reblogsCount));
|
||||
}
|
||||
|
||||
protected void setFavouritedCount(int favouritedCount) {
|
||||
favouritedCountLabel.setText(NumberUtils.shortNumber(favouritedCount));
|
||||
}
|
||||
|
||||
protected void hideStatusInfo() {
|
||||
statusInfo.setVisibility(View.GONE);
|
||||
}
|
||||
|
|
|
@ -112,6 +112,7 @@ class ConversationsFragment :
|
|||
confirmFavourites = preferences.getBoolean("confirmFavourites", false),
|
||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
showStatsInline = preferences.getBoolean(PrefKeys.SHOW_STATS_INLINE, false),
|
||||
showSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia,
|
||||
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler
|
||||
)
|
||||
|
|
|
@ -151,7 +151,7 @@ class PreferencesActivity :
|
|||
}
|
||||
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars", "useBlurhash",
|
||||
"showSelfUsername", "showCardsInTimelines", "confirmReblogs", "confirmFavourites",
|
||||
"enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR -> {
|
||||
"enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR, PrefKeys.SHOW_STATS_INLINE -> {
|
||||
restartActivitiesOnBackPressedCallback.isEnabled = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,6 +221,13 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
setTitle(R.string.pref_title_enable_swipe_for_tabs)
|
||||
isSingleLineTitle = false
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setDefaultValue(false)
|
||||
key = PrefKeys.SHOW_STATS_INLINE
|
||||
setTitle(R.string.pref_title_show_stat_inline)
|
||||
isSingleLineTitle = false
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory(R.string.pref_title_browser_settings) {
|
||||
|
|
|
@ -157,6 +157,7 @@ class ReportStatusesFragment :
|
|||
confirmFavourites = preferences.getBoolean("confirmFavourites", false),
|
||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
showStatsInline = preferences.getBoolean(PrefKeys.SHOW_STATS_INLINE, false),
|
||||
showSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia,
|
||||
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler
|
||||
)
|
||||
|
|
|
@ -88,6 +88,7 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
confirmFavourites = preferences.getBoolean("confirmFavourites", false),
|
||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
showStatsInline = preferences.getBoolean(PrefKeys.SHOW_STATS_INLINE, false),
|
||||
showSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia,
|
||||
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler
|
||||
)
|
||||
|
|
|
@ -197,6 +197,7 @@ class TimelineFragment :
|
|||
confirmFavourites = preferences.getBoolean(PrefKeys.CONFIRM_FAVOURITES, false),
|
||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
showStatsInline = preferences.getBoolean(PrefKeys.SHOW_STATS_INLINE, false),
|
||||
showSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia,
|
||||
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler
|
||||
)
|
||||
|
|
|
@ -113,6 +113,7 @@ class ViewThreadFragment :
|
|||
confirmFavourites = preferences.getBoolean("confirmFavourites", false),
|
||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
showStatsInline = preferences.getBoolean(PrefKeys.SHOW_STATS_INLINE, false),
|
||||
showSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia,
|
||||
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler
|
||||
)
|
||||
|
|
|
@ -67,6 +67,7 @@ object PrefKeys {
|
|||
const val CONFIRM_FAVOURITES = "confirmFavourites"
|
||||
const val ENABLE_SWIPE_FOR_TABS = "enableSwipeForTabs"
|
||||
const val ANIMATE_CUSTOM_EMOJIS = "animateCustomEmojis"
|
||||
const val SHOW_STATS_INLINE = "showStatsInline"
|
||||
|
||||
const val CUSTOM_TABS = "customTabs"
|
||||
const val WELLBEING_LIMITED_NOTIFICATIONS = "wellbeingModeLimitedNotifications"
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
@file:JvmName("NumberUtils")
|
||||
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.log10
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
|
||||
val shortLetters = arrayOf(' ', 'K', 'M', 'B', 'T', 'P', 'E')
|
||||
|
||||
fun shortNumber(number: Number): String {
|
||||
val numberAsDouble = number.toDouble()
|
||||
val nonNegativeValue = abs(numberAsDouble)
|
||||
var sign = ""
|
||||
if (numberAsDouble.sign < 0) { sign = "-" }
|
||||
val value = floor(log10(nonNegativeValue)).toInt()
|
||||
val base = value / 3
|
||||
if (value >= 3 && base < shortLetters.size) {
|
||||
return DecimalFormat("$sign#0.0").format(nonNegativeValue / 10.0.pow((base * 3).toDouble())) + shortLetters[base]
|
||||
} else {
|
||||
return DecimalFormat("$sign#,##0").format(nonNegativeValue)
|
||||
}
|
||||
}
|
|
@ -42,6 +42,8 @@ data class StatusDisplayOptions(
|
|||
val hideStats: Boolean,
|
||||
@get:JvmName("animateEmojis")
|
||||
val animateEmojis: Boolean,
|
||||
@get:JvmName("showStatsInline")
|
||||
val showStatsInline: Boolean,
|
||||
@get:JvmName("showSensitiveMedia")
|
||||
val showSensitiveMedia: Boolean,
|
||||
@get:JvmName("openSpoiler")
|
||||
|
@ -119,6 +121,7 @@ data class StatusDisplayOptions(
|
|||
confirmReblogs = preferences.getBoolean(PrefKeys.CONFIRM_REBLOGS, true),
|
||||
confirmFavourites = preferences.getBoolean(PrefKeys.CONFIRM_FAVOURITES, false),
|
||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||
showStatsInline = preferences.getBoolean(PrefKeys.SHOW_STATS_INLINE, false),
|
||||
showSensitiveMedia = account.alwaysShowSensitiveMedia,
|
||||
openSpoiler = account.alwaysOpenSpoiler
|
||||
)
|
||||
|
|
|
@ -329,7 +329,7 @@
|
|||
android:id="@+id/status_replies"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="40dp"
|
||||
android:layout_marginStart="45dp"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintBottom_toBottomOf="@id/status_reply"
|
||||
app:layout_constraintStart_toStartOf="@id/status_reply"
|
||||
|
@ -353,6 +353,17 @@
|
|||
sparkbutton:primaryColor="@color/tusky_blue"
|
||||
sparkbutton:secondaryColor="@color/tusky_blue_light" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_insets"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="45dp"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintBottom_toBottomOf="@id/status_inset"
|
||||
app:layout_constraintStart_toStartOf="@id/status_inset"
|
||||
app:layout_constraintTop_toTopOf="@id/status_inset"
|
||||
tools:text="1+" />
|
||||
|
||||
<at.connyduck.sparkbutton.SparkButton
|
||||
android:id="@+id/status_favourite"
|
||||
android:layout_width="52dp"
|
||||
|
@ -370,6 +381,17 @@
|
|||
sparkbutton:primaryColor="@color/tusky_orange"
|
||||
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_favourites_count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="45dp"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintBottom_toBottomOf="@id/status_inset"
|
||||
app:layout_constraintStart_toStartOf="@id/status_favourite"
|
||||
app:layout_constraintTop_toTopOf="@id/status_inset"
|
||||
tools:text="" />
|
||||
|
||||
<at.connyduck.sparkbutton.SparkButton
|
||||
android:id="@+id/status_bookmark"
|
||||
android:layout_width="52dp"
|
||||
|
|
|
@ -664,6 +664,7 @@
|
|||
|
||||
<string name="pref_title_show_notifications_filter">Show Notifications filter</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Enable swipe gesture to switch between tabs</string>
|
||||
<string name="pref_title_show_stat_inline">Show post statistics in timeline</string>
|
||||
|
||||
|
||||
<string name="create_poll_title">Poll</string>
|
||||
|
|
|
@ -48,6 +48,7 @@ class NotificationsViewModelTestStatusDisplayOptions : NotificationsViewModelTes
|
|||
confirmFavourites = false,
|
||||
hideStats = false,
|
||||
animateEmojis = false,
|
||||
showStatsInline = false,
|
||||
showSensitiveMedia = true, // setting in NotificationsViewModelTestBase
|
||||
openSpoiler = true // setting in NotificationsViewModelTestBase
|
||||
)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package com.keylesspalace.tusky.util
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import kotlin.math.pow
|
||||
|
||||
class NumberUtilsTest {
|
||||
|
||||
@Test
|
||||
fun zeroShouldBeFormattedAsZero() {
|
||||
val shortNumber = shortNumber(0)
|
||||
Assert.assertEquals("0", shortNumber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun negativeValueShouldBeFormattedToNegativeValue() {
|
||||
val shortNumber = shortNumber(-1)
|
||||
Assert.assertEquals("-1", shortNumber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun positiveValueShouldBeFormattedToPositiveValue() {
|
||||
val shortNumber = shortNumber(1)
|
||||
Assert.assertEquals("1", shortNumber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bigNumbersShouldBeShortened() {
|
||||
var shortNumber = 1L
|
||||
Assert.assertEquals("1", shortNumber(shortNumber))
|
||||
for (i in shortLetters.indices) {
|
||||
if (i == 0) {
|
||||
continue
|
||||
}
|
||||
shortNumber = 1000.0.pow(i.toDouble()).toLong()
|
||||
Assert.assertEquals("1.0" + shortLetters[i], shortNumber(shortNumber))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun roundingForNegativeAndPositiveValuesShouldBeTheSame() {
|
||||
var value = 3492
|
||||
Assert.assertEquals("-3.5K", shortNumber(-value))
|
||||
Assert.assertEquals("3.5K", shortNumber(value))
|
||||
value = 1501
|
||||
Assert.assertEquals("-1.5K", shortNumber(-value))
|
||||
Assert.assertEquals("1.5K", shortNumber(value))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue