From b0f8cbb2e3965e24af060012cae429c04357e8e0 Mon Sep 17 00:00:00 2001 From: FineFindus Date: Sun, 25 Aug 2024 13:35:32 +0200 Subject: [PATCH 1/2] 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. --- .../fragments/HashtagTimelineFragment.java | 63 +++++++++---------- .../AccountRestrictionConfirmationSheet.java | 52 +++++++++++++++ .../sheets/MuteAccountConfirmationSheet.java | 47 +------------- .../sheets/MuteHashtagConfirmationSheet.java | 30 +++++++++ mastodon/src/main/res/values/strings_mo.xml | 3 + 5 files changed, 116 insertions(+), 79 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteHashtagConfirmationSheet.java diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java index a982737dc..9cb6f75fb 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java @@ -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 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); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/AccountRestrictionConfirmationSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/AccountRestrictionConfirmationSheet.java index 7f293f734..93cdfa28b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/AccountRestrictionConfirmationSheet.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/AccountRestrictionConfirmationSheet.java @@ -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 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 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 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); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteAccountConfirmationSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteAccountConfirmationSheet.java index 6a1172e13..100ce9222 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteAccountConfirmationSheet.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteAccountConfirmationSheet.java @@ -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 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 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; - } - - } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteHashtagConfirmationSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteHashtagConfirmationSheet.java new file mode 100644 index 000000000..33955572c --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/sheets/MuteHashtagConfirmationSheet.java @@ -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 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); + } +} diff --git a/mastodon/src/main/res/values/strings_mo.xml b/mastodon/src/main/res/values/strings_mo.xml index 4881f8d06..e9c2f5295 100644 --- a/mastodon/src/main/res/values/strings_mo.xml +++ b/mastodon/src/main/res/values/strings_mo.xml @@ -45,6 +45,9 @@ Are you sure you want to mute this conversation? Are you sure you want to unmute this conversation? Mute hashtag + You won’t see posts mentioning this hashtag in your Home timeline. + Others won’t know that you’ve muted this hashtag. + You can still find posts with this hashtag on other timelines or through search. Unmute hashtag Are you sure you want to mute this hashtag? Are you sure you want to unmute this hashtag? From 1f06e4e8f3288dd1b327612f1243076f94ecb8bb Mon Sep 17 00:00:00 2001 From: FineFindus Date: Sun, 25 Aug 2024 13:43:26 +0200 Subject: [PATCH 2/2] feat(Timeline/Hashtag): show snackbar when unmuting Gives the user feedback in the form of a snackbar that the unmuting was sucessful. This is already done for unmuting accounts. --- .../android/fragments/HashtagTimelineFragment.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java index 9cb6f75fb..b2392c0fa 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java @@ -34,6 +34,7 @@ import org.joinmastodon.android.model.FilterKeyword; import org.joinmastodon.android.model.Hashtag; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.TimelineDefinition; +import org.joinmastodon.android.ui.Snackbar; import org.joinmastodon.android.ui.sheets.MuteHashtagConfirmationSheet; import org.joinmastodon.android.ui.text.SpacerSpan; import org.joinmastodon.android.ui.utils.UiUtils; @@ -131,6 +132,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{ }).exec(accountID); }).show(); } + private void unmuteHashtag() { //safe to get, this only called if filter is present new DeleteFilter(filter.get().id).setCallback(new Callback<>(){ @@ -138,6 +140,9 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{ public void onSuccess(Void result){ filter=Optional.empty(); updateMuteState(false); + new Snackbar.Builder(getContext()) + .setText(getContext().getString(R.string.unmuted_user_x, '#'+hashtagName)) + .show(); } @Override