From 67c20326f97f2f980d588d4f62828f94473c079f Mon Sep 17 00:00:00 2001 From: Ivan Kupalov Date: Tue, 3 Mar 2020 21:27:26 +0100 Subject: [PATCH] Add options to confirm reblog/unreblog actions, close #460 (#1650) --- app/build.gradle | 2 +- .../tusky/PreferencesActivity.kt | 2 +- .../tusky/adapter/NotificationsAdapter.java | 3 +- .../tusky/adapter/StatusBaseViewHolder.java | 43 ++++++++++++++++--- .../tusky/adapter/TimelineAdapter.java | 3 +- .../conversation/ConversationViewHolder.java | 3 +- .../conversation/ConversationsFragment.kt | 3 +- .../fragments/ReportStatusesFragment.kt | 3 +- .../fragments/SearchStatusesFragment.kt | 5 ++- .../tusky/fragment/NotificationsFragment.java | 3 +- .../tusky/fragment/TimelineFragment.java | 7 ++- .../tusky/fragment/ViewThreadFragment.java | 3 +- .../tusky/util/StatusDisplayOptions.kt | 4 +- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 6 +++ 15 files changed, 71 insertions(+), 20 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 11f4474f6..1defe9f78 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -155,7 +155,7 @@ dependencies { implementation "com.google.dagger:dagger-android-support:$daggerVersion" kapt "com.google.dagger:dagger-android-processor:$daggerVersion" - implementation "com.github.connyduck:sparkbutton:3.0.0" + implementation "com.github.connyduck:sparkbutton:4.0.0" implementation "com.github.chrisbanes:PhotoView:2.3.0" diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt index 157916dbd..4adfc4e98 100644 --- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt @@ -129,7 +129,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference } "statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars", - "useBlurhash", "showCardsInTimelines" -> { + "useBlurhash", "showCardsInTimelines", "confirmReblogs" -> { restartActivitiesOnExit = true } "language" -> { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index 61b6eecc5..b12bab338 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -232,7 +232,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter { statusDisplayOptions.useAbsoluteTime(), statusDisplayOptions.showBotOverlay(), statusDisplayOptions.useBlurhash(), - CardViewMode.NONE + CardViewMode.NONE, + statusDisplayOptions.confirmReblogs() ); } 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 30baa5034..ad0e6573d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -19,6 +19,7 @@ import android.widget.Toast; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -209,7 +210,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { contentWarningDescription.setVisibility(View.VISIBLE); contentWarningButton.setVisibility(View.VISIBLE); setContentWarningButtonText(expanded); - contentWarningButton.setOnClickListener( view -> { + contentWarningButton.setOnClickListener(view -> { contentWarningDescription.invalidate(); if (getAdapterPosition() != RecyclerView.NO_POSITION) { listener.onExpandedChange(!expanded, getAdapterPosition()); @@ -227,7 +228,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { } private void setContentWarningButtonText(boolean expanded) { - if(expanded) { + if (expanded) { contentWarningButton.setText(R.string.status_content_warning_show_less); } else { contentWarningButton.setText(R.string.status_content_warning_show_more); @@ -610,7 +611,9 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { sensitiveMediaShow.setVisibility(View.GONE); } - protected void setupButtons(final StatusActionListener listener, final String accountId) { + protected void setupButtons(final StatusActionListener listener, final String accountId, + final String statusContent, + StatusDisplayOptions statusDisplayOptions) { avatar.setOnClickListener(v -> listener.onViewAccount(accountId)); replyButton.setOnClickListener(v -> { @@ -623,7 +626,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { reblogButton.setEventListener((button, buttonState) -> { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { - listener.onReblog(buttonState, position); + listener.onReblog(!buttonState, position); + } + if (statusDisplayOptions.confirmReblogs()) { + showConfirmReblogDialog(listener, statusContent, buttonState, position); + return false; + } else { + return true; } }); } @@ -631,15 +640,17 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { favouriteButton.setEventListener((button, buttonState) -> { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { - listener.onFavourite(buttonState, position); + listener.onFavourite(!buttonState, position); } + return true; }); bookmarkButton.setEventListener((button, buttonState) -> { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { - listener.onBookmark(buttonState, position); + listener.onBookmark(!buttonState, position); } + return true; }); moreButton.setOnClickListener(v -> { @@ -662,6 +673,23 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { itemView.setOnClickListener(viewThreadListener); } + private void showConfirmReblogDialog(StatusActionListener listener, + String statusContent, + boolean buttonState, + int position) { + int okButtonTextId = buttonState ? R.string.action_unreblog : R.string.action_reblog; + new AlertDialog.Builder(reblogButton.getContext()) + .setMessage(statusContent) + .setPositiveButton(okButtonTextId, (__, ___) -> { + listener.onReblog(!buttonState, position); + if (!buttonState) { + // Play animation only when it's reblog, not unreblog + reblogButton.playAnimation(); + } + }) + .show(); + } + public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener, StatusDisplayOptions statusDisplayOptions) { this.setupWithStatus(status, listener, statusDisplayOptions, null); @@ -706,7 +734,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { setupCard(status, statusDisplayOptions.cardViewMode()); } - setupButtons(listener, status.getSenderId()); + setupButtons(listener, status.getSenderId(), status.getContent().toString(), + statusDisplayOptions); setRebloggingEnabled(status.getRebloggingEnabled(), status.getVisibility()); setSpoilerAndContent(status.isExpanded(), status.getContent(), status.getSpoilerText(), status.getMentions(), status.getStatusEmojis(), status.getPoll(), statusDisplayOptions, listener); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java index 307ddfc50..91ec25e2f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java @@ -64,7 +64,8 @@ public final class TimelineAdapter extends RecyclerView.Adapter { statusDisplayOptions.useAbsoluteTime(), statusDisplayOptions.showBotOverlay(), statusDisplayOptions.useBlurhash(), - statusDisplayOptions.cardViewMode() + statusDisplayOptions.cardViewMode(), + statusDisplayOptions.confirmReblogs() ); } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java index 365575994..6727156d0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java @@ -104,7 +104,8 @@ public class ConversationViewHolder extends StatusBaseViewHolder { hideSensitiveMediaWarning(); } - setupButtons(listener, account.getId()); + setupButtons(listener, account.getId(), status.getContent().toString(), + statusDisplayOptions); setSpoilerAndContent(status.getExpanded(), status.getContent(), status.getSpoilerText(), status.getMentions(), status.getEmojis(), diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 65816c18a..a91ed2f08 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -66,7 +66,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false), showBotOverlay = preferences.getBoolean("showBotOverlay", true), useBlurhash = preferences.getBoolean("useBlurhash", true), - cardViewMode = CardViewMode.NONE + cardViewMode = CardViewMode.NONE, + confirmReblogs = preferences.getBoolean("confirmReblogs", true) ) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt index f30406ce1..dad6fe1f5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt @@ -117,7 +117,8 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler { useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false), showBotOverlay = false, useBlurhash = preferences.getBoolean("useBlurhash", true), - cardViewMode = CardViewMode.NONE + cardViewMode = CardViewMode.NONE, + confirmReblogs = preferences.getBoolean("confirmReblogs", true) ) adapter = StatusesAdapter(statusDisplayOptions, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index 3ef3d5242..ac171b9e7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -71,7 +71,7 @@ class SearchStatusesFragment : SearchFragment, *> { val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context) @@ -81,7 +81,8 @@ class SearchStatusesFragment : SearchFragmentYou don\'t have any scheduled statuses. Mastodon has a minimum scheduling interval of 5 minutes. Show link previews in timelines + Show confirmation dialog before boosting diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d2e2bfae8..b04ccacc7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -78,6 +78,12 @@ android:title="@string/pref_title_show_cards_in_timelines" app:singleLineTitle="false" /> + +