feat(Timeline/Hashtag): show confirmation sheet when muting

Shows a MuteHashtagConfirmationSheet when muting a hashtag, similar to
the MuteAccountConfirmationSheet. This also adds the option for the mute
to expire.
This commit is contained in:
FineFindus 2024-08-25 13:35:32 +02:00
parent 5427b21365
commit b0f8cbb2e3
No known key found for this signature in database
GPG key ID: 64873EE210FF8E6B
5 changed files with 116 additions and 79 deletions

View file

@ -11,7 +11,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
@ -32,21 +31,20 @@ import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.FilterKeyword;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.TimelineDefinition;
import org.joinmastodon.android.ui.sheets.MuteHashtagConfirmationSheet;
import org.joinmastodon.android.ui.text.SpacerSpan;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ProgressBarButton;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.time.Duration;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicReference;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
@ -105,14 +103,33 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
muteMenuItem.setIcon(newMute ? R.drawable.ic_fluent_speaker_2_24_regular : R.drawable.ic_fluent_speaker_off_24_regular);
}
private void showMuteDialog(boolean mute) {
UiUtils.showConfirmationAlert(getContext(),
mute ? R.string.mo_unmute_hashtag : R.string.mo_mute_hashtag,
mute ? R.string.mo_confirm_to_unmute_hashtag : R.string.mo_confirm_to_mute_hashtag,
mute ? R.string.do_unmute : R.string.do_mute,
mute ? R.drawable.ic_fluent_speaker_2_28_regular : R.drawable.ic_fluent_speaker_off_28_regular,
mute ? this::unmuteHashtag : this::muteHashtag
);
private void showMuteDialog(boolean currentlyMuted) {
if (currentlyMuted) {
unmuteHashtag();
return;
}
//pass a references, so they can be changed inside the confirmation sheet
AtomicReference<Duration> muteDuration=new AtomicReference<>(Duration.ZERO);
new MuteHashtagConfirmationSheet(getContext(), null, muteDuration, hashtag, (onSuccess, onError)->{
FilterKeyword hashtagFilter=new FilterKeyword();
hashtagFilter.wholeWord=true;
hashtagFilter.keyword="#"+hashtagName;
new CreateFilter("#"+hashtagName, EnumSet.of(FilterContext.HOME), FilterAction.HIDE, (int) muteDuration.get().getSeconds(), List.of(hashtagFilter)).setCallback(new Callback<>(){
@Override
public void onSuccess(Filter result){
filter=Optional.of(result);
updateMuteState(true);
onSuccess.run();
}
@Override
public void onError(ErrorResponse error){
error.showToast(getContext());
onError.run();
}
}).exec(accountID);
}).show();
}
private void unmuteHashtag() {
//safe to get, this only called if filter is present
@ -130,26 +147,6 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
}).exec(accountID);
}
private void muteHashtag() {
FilterKeyword hashtagFilter=new FilterKeyword();
hashtagFilter.wholeWord=true;
hashtagFilter.keyword="#"+hashtagName;
new CreateFilter("#"+hashtagName, EnumSet.of(FilterContext.HOME), FilterAction.HIDE, 0 , List.of(hashtagFilter)).setCallback(new Callback<>(){
@Override
public void onSuccess(Filter result){
filter=Optional.of(result);
updateMuteState(true);
}
@Override
public void onError(ErrorResponse error){
error.showToast(getContext());
}
}).exec(accountID);
}
@Override
protected TimelineDefinition makeTimelineDefinition() {
return TimelineDefinition.ofHashtag(hashtagName);

View file

@ -1,5 +1,6 @@
package org.joinmastodon.android.ui.sheets;
import android.app.AlertDialog;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.ColorDrawable;
@ -15,6 +16,7 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.drawables.EmptyDrawable;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.AutoOrientationLinearLayout;
@ -23,6 +25,11 @@ import org.joinmastodon.android.ui.views.ProgressBarButton;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.BottomSheet;
@ -108,6 +115,51 @@ public abstract class AccountRestrictionConfirmationSheet extends BottomSheet{
addRow(icon, getContext().getString(text));
}
public void addDurationRow(@NonNull Context context, AtomicReference<Duration> muteDuration) {
//Moshidon: add row to choose a duration, e.g. for muting accounts
Button muteDurationBtn=new Button(getContext());
muteDurationBtn.setOnClickListener(v->getMuteDurationDialog(context, muteDuration, muteDurationBtn).show());
muteDurationBtn.setText(R.string.sk_duration_indefinite);
addRow(R.drawable.ic_fluent_clock_20_regular, R.string.sk_mute_label, muteDurationBtn);
}
@NonNull
private M3AlertDialogBuilder getMuteDurationDialog(@NonNull Context context, AtomicReference<Duration> muteDuration, Button button){
M3AlertDialogBuilder builder=new M3AlertDialogBuilder(context);
builder.setTitle(R.string.sk_mute_label);
builder.setIcon(R.drawable.ic_fluent_clock_20_regular);
List<Duration> durations =List.of(Duration.ZERO,
Duration.ofMinutes(5),
Duration.ofMinutes(30),
Duration.ofHours(1),
Duration.ofHours(6),
Duration.ofDays(1),
Duration.ofDays(3),
Duration.ofDays(7),
Duration.ofDays(7));
String[] choices = {context.getString(R.string.sk_duration_indefinite),
context.getString(R.string.sk_duration_minutes_5),
context.getString(R.string.sk_duration_minutes_30),
context.getString(R.string.sk_duration_hours_1),
context.getString(R.string.sk_duration_hours_6),
context.getString(R.string.sk_duration_days_1),
context.getString(R.string.sk_duration_days_3),
context.getString(R.string.sk_duration_days_7)};
builder.setSingleChoiceItems(choices, durations.indexOf(muteDuration.get()), (dialog, which) -> {});
builder.setPositiveButton(R.string.ok, (dialog, which)->{
int selected = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
muteDuration.set(durations.get(selected));
button.setText(choices[selected]);
});
builder.setNegativeButton(R.string.cancel, null);
return builder;
}
public interface ConfirmCallback{
void onConfirmed(Runnable onSuccess, Runnable onError);
}

View file

@ -1,17 +1,13 @@
package org.joinmastodon.android.ui.sheets;
import android.app.AlertDialog;
import android.content.Context;
import android.view.View;
import android.widget.Button;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.views.M3Switch;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@ -39,47 +35,6 @@ public class MuteAccountConfirmationSheet extends AccountRestrictionConfirmation
addRow(R.drawable.ic_fluent_alert_off_24_regular, R.string.mo_mute_notifications, m3Switch);
// add mute duration (Moshidon)
Button muteDurationBtn=new Button(getContext());
muteDurationBtn.setOnClickListener(v->getMuteDurationDialog(context, muteDuration, muteDurationBtn).show());
muteDurationBtn.setText(R.string.sk_duration_indefinite);
addRow(R.drawable.ic_fluent_clock_20_regular, R.string.sk_mute_label, muteDurationBtn);
addDurationRow(context, muteDuration);
}
@NonNull
private M3AlertDialogBuilder getMuteDurationDialog(@NonNull Context context, AtomicReference<Duration> muteDuration, Button button){
M3AlertDialogBuilder builder=new M3AlertDialogBuilder(context);
builder.setTitle(R.string.sk_mute_label);
builder.setIcon(R.drawable.ic_fluent_clock_20_regular);
List<Duration> durations =List.of(Duration.ZERO,
Duration.ofMinutes(5),
Duration.ofMinutes(30),
Duration.ofHours(1),
Duration.ofHours(6),
Duration.ofDays(1),
Duration.ofDays(3),
Duration.ofDays(7),
Duration.ofDays(7));
String[] choices = {context.getString(R.string.sk_duration_indefinite),
context.getString(R.string.sk_duration_minutes_5),
context.getString(R.string.sk_duration_minutes_30),
context.getString(R.string.sk_duration_hours_1),
context.getString(R.string.sk_duration_hours_6),
context.getString(R.string.sk_duration_days_1),
context.getString(R.string.sk_duration_days_3),
context.getString(R.string.sk_duration_days_7)};
builder.setSingleChoiceItems(choices, durations.indexOf(muteDuration.get()), (dialog, which) -> {});
builder.setPositiveButton(R.string.ok, (dialog, which)->{
int selected = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
muteDuration.set(durations.get(selected));
button.setText(choices[selected]);
});
builder.setNegativeButton(R.string.cancel, null);
return builder;
}
}

View file

@ -0,0 +1,30 @@
package org.joinmastodon.android.ui.sheets;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Hashtag;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;
// MOSHIDON
public class MuteHashtagConfirmationSheet extends AccountRestrictionConfirmationSheet{
public MuteHashtagConfirmationSheet(@NonNull Context context, Account user, AtomicReference<Duration> muteDuration, Hashtag hashtag, ConfirmCallback confirmCallback){
super(context, user, confirmCallback);
titleView.setText(R.string.mo_mute_hashtag);
confirmBtn.setText(R.string.do_mute);
secondaryBtn.setVisibility(View.GONE);
icon.setImageResource(R.drawable.ic_fluent_speaker_off_24_regular);
subtitleView.setText("#"+hashtag.name);
addRow(R.drawable.ic_fluent_number_symbol_24_regular, R.string.mo_mute_hashtag_explanation_muted_home);
addRow(R.drawable.ic_fluent_eye_off_24_regular, R.string.mo_mute_hashtag_explanation_discreet);
addRow(R.drawable.ic_fluent_search_24_regular, R.string.mo_mute_hashtag_explanation_search);
addDurationRow(context, muteDuration);
}
}

View file

@ -45,6 +45,9 @@
<string name="mo_confirm_to_mute_conversation">Are you sure you want to mute this conversation?</string>
<string name="mo_confirm_to_unmute_conversation">Are you sure you want to unmute this conversation?</string>
<string name="mo_mute_hashtag">Mute hashtag</string>
<string name="mo_mute_hashtag_explanation_muted_home">You wont see posts mentioning this hashtag in your Home timeline.</string>
<string name="mo_mute_hashtag_explanation_discreet">Others wont know that youve muted this hashtag.</string>
<string name="mo_mute_hashtag_explanation_search">You can still find posts with this hashtag on other timelines or through search.</string>
<string name="mo_unmute_hashtag">Unmute hashtag</string>
<string name="mo_confirm_to_mute_hashtag">Are you sure you want to mute this hashtag?</string>
<string name="mo_confirm_to_unmute_hashtag">Are you sure you want to unmute this hashtag?</string>