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.
This commit is contained in:
parent
61720c3472
commit
c36b243745
5 changed files with 68 additions and 36 deletions
|
@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
|
import android.view.Menu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
@ -21,7 +22,7 @@ import android.widget.Toast;
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.widget.PopupMenu;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.text.HtmlCompat;
|
import androidx.core.text.HtmlCompat;
|
||||||
|
@ -78,6 +79,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
public static final String KEY_CREATED = "created";
|
public static final String KEY_CREATED = "created";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final String TAG = "StatusBaseViewHolder";
|
||||||
|
|
||||||
private final TextView displayName;
|
private final TextView displayName;
|
||||||
private final TextView username;
|
private final TextView username;
|
||||||
private final ImageButton replyButton;
|
private final ImageButton replyButton;
|
||||||
|
@ -645,7 +648,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
int position = getBindingAdapterPosition();
|
int position = getBindingAdapterPosition();
|
||||||
if (position != RecyclerView.NO_POSITION) {
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
if (statusDisplayOptions.confirmReblogs()) {
|
if (statusDisplayOptions.confirmReblogs()) {
|
||||||
showConfirmReblogDialog(listener, statusContent, buttonState, position);
|
showConfirmReblog(listener, buttonState, position);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
listener.onReblog(!buttonState, position);
|
listener.onReblog(!buttonState, position);
|
||||||
|
@ -663,7 +666,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
int position = getBindingAdapterPosition();
|
int position = getBindingAdapterPosition();
|
||||||
if (position != RecyclerView.NO_POSITION) {
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
if (statusDisplayOptions.confirmFavourites()) {
|
if (statusDisplayOptions.confirmFavourites()) {
|
||||||
showConfirmFavouriteDialog(listener, statusContent, buttonState, position);
|
showConfirmFavourite(listener, buttonState, position);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
listener.onFavourite(!buttonState, position);
|
listener.onFavourite(!buttonState, position);
|
||||||
|
@ -702,38 +705,46 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
itemView.setOnClickListener(viewThreadListener);
|
itemView.setOnClickListener(viewThreadListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showConfirmReblogDialog(StatusActionListener listener,
|
private void showConfirmReblog(StatusActionListener listener,
|
||||||
String statusContent,
|
boolean buttonState,
|
||||||
boolean buttonState,
|
int position) {
|
||||||
int position) {
|
PopupMenu popup = new PopupMenu(itemView.getContext(), reblogButton);
|
||||||
int okButtonTextId = buttonState ? R.string.action_unreblog : R.string.action_reblog;
|
popup.inflate(R.menu.status_reblog);
|
||||||
new AlertDialog.Builder(reblogButton.getContext())
|
Menu menu = popup.getMenu();
|
||||||
.setMessage(statusContent)
|
if (buttonState) {
|
||||||
.setPositiveButton(okButtonTextId, (__, ___) -> {
|
menu.findItem(R.id.menu_action_reblog).setVisible(false);
|
||||||
listener.onReblog(!buttonState, position);
|
} else {
|
||||||
if (!buttonState) {
|
menu.findItem(R.id.menu_action_unreblog).setVisible(false);
|
||||||
// Play animation only when it's reblog, not unreblog
|
}
|
||||||
reblogButton.playAnimation();
|
popup.setOnMenuItemClickListener(item -> {
|
||||||
}
|
listener.onReblog(!buttonState, position);
|
||||||
})
|
if(!buttonState) {
|
||||||
.show();
|
reblogButton.playAnimation();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showConfirmFavouriteDialog(StatusActionListener listener,
|
private void showConfirmFavourite(StatusActionListener listener,
|
||||||
String statusContent,
|
boolean buttonState,
|
||||||
boolean buttonState,
|
int position) {
|
||||||
int position) {
|
PopupMenu popup = new PopupMenu(itemView.getContext(), favouriteButton);
|
||||||
int okButtonTextId = buttonState ? R.string.action_unfavourite : R.string.action_favourite;
|
popup.inflate(R.menu.status_favourite);
|
||||||
new AlertDialog.Builder(favouriteButton.getContext())
|
Menu menu = popup.getMenu();
|
||||||
.setMessage(statusContent)
|
if (buttonState) {
|
||||||
.setPositiveButton(okButtonTextId, (__, ___) -> {
|
menu.findItem(R.id.menu_action_favourite).setVisible(false);
|
||||||
listener.onFavourite(!buttonState, position);
|
} else {
|
||||||
if (!buttonState) {
|
menu.findItem(R.id.menu_action_unfavourite).setVisible(false);
|
||||||
// Play animation only when it's favourite, not unfavourite
|
}
|
||||||
favouriteButton.playAnimation();
|
popup.setOnMenuItemClickListener(item -> {
|
||||||
}
|
listener.onFavourite(!buttonState, position);
|
||||||
})
|
if(!buttonState) {
|
||||||
.show();
|
favouriteButton.playAnimation();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
|
public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
|
||||||
|
|
11
app/src/main/res/menu/status_favourite.xml
Normal file
11
app/src/main/res/menu/status_favourite.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_favourite"
|
||||||
|
android:icon="@drawable/ic_favourite_24dp"
|
||||||
|
android:title="@string/action_favourite" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_unfavourite"
|
||||||
|
android:icon="@drawable/ic_favourite_24dp"
|
||||||
|
android:title="@string/action_unfavourite" />
|
||||||
|
</menu>
|
11
app/src/main/res/menu/status_reblog.xml
Normal file
11
app/src/main/res/menu/status_reblog.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_reblog"
|
||||||
|
android:icon="@drawable/ic_reblog_24dp"
|
||||||
|
android:title="@string/action_reblog" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_unreblog"
|
||||||
|
android:icon="@drawable/ic_reblog_24dp"
|
||||||
|
android:title="@string/action_unreblog" />
|
||||||
|
</menu>
|
|
@ -697,8 +697,8 @@
|
||||||
<string name="warning_scheduling_interval">Mastodon has a minimum scheduling interval of 5 minutes.</string>
|
<string name="warning_scheduling_interval">Mastodon has a minimum scheduling interval of 5 minutes.</string>
|
||||||
<string name="pref_title_show_self_username">Show username in toolbars</string>
|
<string name="pref_title_show_self_username">Show username in toolbars</string>
|
||||||
<string name="pref_title_show_cards_in_timelines">Show link previews in timelines</string>
|
<string name="pref_title_show_cards_in_timelines">Show link previews in timelines</string>
|
||||||
<string name="pref_title_confirm_reblogs">Show confirmation dialog before boosting</string>
|
<string name="pref_title_confirm_reblogs">Show confirmation before boosting</string>
|
||||||
<string name="pref_title_confirm_favourites">Show confirmation dialog before favoriting</string>
|
<string name="pref_title_confirm_favourites">Show confirmation before favoriting</string>
|
||||||
<string name="pref_title_hide_top_toolbar">Hide the title of the top toolbar</string>
|
<string name="pref_title_hide_top_toolbar">Hide the title of the top toolbar</string>
|
||||||
<string name="pref_title_wellbeing_mode">Wellbeing</string>
|
<string name="pref_title_wellbeing_mode">Wellbeing</string>
|
||||||
<string name="account_note_hint">Your private note about this account</string>
|
<string name="account_note_hint">Your private note about this account</string>
|
||||||
|
|
|
@ -184,5 +184,4 @@
|
||||||
<item name="cornerFamily">rounded</item>
|
<item name="cornerFamily">rounded</item>
|
||||||
<item name="cornerSize">12.5%</item>
|
<item name="cornerSize">12.5%</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue