From c36b2437451a894dc3dff294ff47ff976a1f87a9 Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Sat, 18 Mar 2023 09:37:55 +0100 Subject: [PATCH] Show reblog/favourite confirmations as menus not dialogs (#3418) * Show reblog/favourite confirmations as menus not dialogs The previous code used dialogs and displayed the text of the status when reblogging or favouriting. This didn't work when the post just contained images, and other material from the status (content warning, polls) was not shown either. Fix this by displaying a popup menu instead. The status remains visible so the user can clearly see what they're acting on. In addition, this lays the groundwork for supporting a long-press menu in the future to allow the user to reblog/favourite from a different account. Fixes https://github.com/tuskyapp/Tusky/issues/3308 * Revert the change that puts the menu immediately over the icon Although this behavious is consistent with how the option menu works, I decided that the risk of someone inadvertently double-tapping in the same location, and the first tap opens the menu and the second tap confirms the action was too great. So now the menu appears either above or below the icon depending on space, and the user has to tap in two slightly different spaces. This is also consistent with the previous behaviour, where it's highly unlikely that the confirm button on the dialog would have been directly under the user's finger if they double-tapped. --- .../tusky/adapter/StatusBaseViewHolder.java | 77 +++++++++++-------- app/src/main/res/menu/status_favourite.xml | 11 +++ app/src/main/res/menu/status_reblog.xml | 11 +++ app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/styles.xml | 1 - 5 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 app/src/main/res/menu/status_favourite.xml create mode 100644 app/src/main/res/menu/status_reblog.xml 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 676cdf0dc..dfceb3093 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable; import android.text.Spanned; import android.text.TextUtils; import android.text.format.DateUtils; +import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -21,7 +22,7 @@ import android.widget.Toast; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.PopupMenu; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.ContextCompat; import androidx.core.text.HtmlCompat; @@ -78,6 +79,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { public static final String KEY_CREATED = "created"; } + private final String TAG = "StatusBaseViewHolder"; + private final TextView displayName; private final TextView username; private final ImageButton replyButton; @@ -645,7 +648,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { int position = getBindingAdapterPosition(); if (position != RecyclerView.NO_POSITION) { if (statusDisplayOptions.confirmReblogs()) { - showConfirmReblogDialog(listener, statusContent, buttonState, position); + showConfirmReblog(listener, buttonState, position); return false; } else { listener.onReblog(!buttonState, position); @@ -663,7 +666,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { int position = getBindingAdapterPosition(); if (position != RecyclerView.NO_POSITION) { if (statusDisplayOptions.confirmFavourites()) { - showConfirmFavouriteDialog(listener, statusContent, buttonState, position); + showConfirmFavourite(listener, buttonState, position); return false; } else { listener.onFavourite(!buttonState, position); @@ -702,38 +705,46 @@ 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(); + private void showConfirmReblog(StatusActionListener listener, + boolean buttonState, + int position) { + PopupMenu popup = new PopupMenu(itemView.getContext(), reblogButton); + popup.inflate(R.menu.status_reblog); + Menu menu = popup.getMenu(); + if (buttonState) { + menu.findItem(R.id.menu_action_reblog).setVisible(false); + } else { + menu.findItem(R.id.menu_action_unreblog).setVisible(false); + } + popup.setOnMenuItemClickListener(item -> { + listener.onReblog(!buttonState, position); + if(!buttonState) { + reblogButton.playAnimation(); + } + return true; + }); + popup.show(); } - private void showConfirmFavouriteDialog(StatusActionListener listener, - String statusContent, - boolean buttonState, - int position) { - int okButtonTextId = buttonState ? R.string.action_unfavourite : R.string.action_favourite; - new AlertDialog.Builder(favouriteButton.getContext()) - .setMessage(statusContent) - .setPositiveButton(okButtonTextId, (__, ___) -> { - listener.onFavourite(!buttonState, position); - if (!buttonState) { - // Play animation only when it's favourite, not unfavourite - favouriteButton.playAnimation(); - } - }) - .show(); + private void showConfirmFavourite(StatusActionListener listener, + boolean buttonState, + int position) { + PopupMenu popup = new PopupMenu(itemView.getContext(), favouriteButton); + popup.inflate(R.menu.status_favourite); + Menu menu = popup.getMenu(); + if (buttonState) { + menu.findItem(R.id.menu_action_favourite).setVisible(false); + } else { + menu.findItem(R.id.menu_action_unfavourite).setVisible(false); + } + popup.setOnMenuItemClickListener(item -> { + listener.onFavourite(!buttonState, position); + if(!buttonState) { + favouriteButton.playAnimation(); + } + return true; + }); + popup.show(); } public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener, diff --git a/app/src/main/res/menu/status_favourite.xml b/app/src/main/res/menu/status_favourite.xml new file mode 100644 index 000000000..66188c270 --- /dev/null +++ b/app/src/main/res/menu/status_favourite.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/menu/status_reblog.xml b/app/src/main/res/menu/status_reblog.xml new file mode 100644 index 000000000..4d79e6b30 --- /dev/null +++ b/app/src/main/res/menu/status_reblog.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8cea1a643..91ea0e9d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -697,8 +697,8 @@ Mastodon has a minimum scheduling interval of 5 minutes. Show username in toolbars Show link previews in timelines - Show confirmation dialog before boosting - Show confirmation dialog before favoriting + Show confirmation before boosting + Show confirmation before favoriting Hide the title of the top toolbar Wellbeing Your private note about this account diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e465e2d18..f307f2d4a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -184,5 +184,4 @@ rounded 12.5% -