From c4e23b0fe6c60a5b8e31f1b86e22d9a2974b2057 Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 23 Jan 2023 13:57:17 +0100 Subject: [PATCH] update hashtags/lists in home closes sk22#312 --- .../android/events/HashtagUpdatedEvent.java | 11 ++++ .../android/events/ListDeletedEvent.java | 9 +++ .../events/ListUpdatedCreatedEvent.java | 15 +++++ .../fragments/HashtagTimelineFragment.java | 3 + .../android/fragments/HomeTabFragment.java | 52 ++++++++++++++- .../fragments/ListTimelineFragment.java | 44 ++++++------- .../fragments/ListTimelinesFragment.java | 66 +++++++++++-------- 7 files changed, 150 insertions(+), 50 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/events/HashtagUpdatedEvent.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/events/ListDeletedEvent.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/events/ListUpdatedCreatedEvent.java diff --git a/mastodon/src/main/java/org/joinmastodon/android/events/HashtagUpdatedEvent.java b/mastodon/src/main/java/org/joinmastodon/android/events/HashtagUpdatedEvent.java new file mode 100644 index 000000000..679c65793 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/events/HashtagUpdatedEvent.java @@ -0,0 +1,11 @@ +package org.joinmastodon.android.events; + +public class HashtagUpdatedEvent { + public final String name; + public final boolean following; + + public HashtagUpdatedEvent(String name, boolean following) { + this.name = name; + this.following = following; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/events/ListDeletedEvent.java b/mastodon/src/main/java/org/joinmastodon/android/events/ListDeletedEvent.java new file mode 100644 index 000000000..9824bb233 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/events/ListDeletedEvent.java @@ -0,0 +1,9 @@ +package org.joinmastodon.android.events; + +public class ListDeletedEvent { + public final String id; + + public ListDeletedEvent(String id) { + this.id = id; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/events/ListUpdatedCreatedEvent.java b/mastodon/src/main/java/org/joinmastodon/android/events/ListUpdatedCreatedEvent.java new file mode 100644 index 000000000..89efffda2 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/events/ListUpdatedCreatedEvent.java @@ -0,0 +1,15 @@ +package org.joinmastodon.android.events; + +import org.joinmastodon.android.model.ListTimeline; + +public class ListUpdatedCreatedEvent { + public final String id; + public final String title; + public final ListTimeline.RepliesPolicy repliesPolicy; + + public ListUpdatedCreatedEvent(String id, String title, ListTimeline.RepliesPolicy repliesPolicy) { + this.id = id; + this.title = title; + this.repliesPolicy = repliesPolicy; + } +} 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 08aed61fa..c7b0afd51 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java @@ -11,10 +11,12 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.Toast; +import org.joinmastodon.android.E; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.tags.GetHashtag; import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed; import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline; +import org.joinmastodon.android.events.HashtagUpdatedEvent; import org.joinmastodon.android.model.Hashtag; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.TimelineDefinition; @@ -55,6 +57,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment { this.following = newFollowing; followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag)); followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular); + E.post(new HashtagUpdatedEvent(hashtag, following)); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java index 957b8b534..628c425f9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java @@ -39,6 +39,9 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.announcements.GetAnnouncements; import org.joinmastodon.android.api.requests.lists.GetLists; import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags; +import org.joinmastodon.android.events.HashtagUpdatedEvent; +import org.joinmastodon.android.events.ListDeletedEvent; +import org.joinmastodon.android.events.ListUpdatedCreatedEvent; import org.joinmastodon.android.events.SelfUpdateStateChangedEvent; import org.joinmastodon.android.model.Announcement; import org.joinmastodon.android.model.Hashtag; @@ -52,6 +55,9 @@ import org.joinmastodon.android.updater.GithubSelfUpdater; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.function.Supplier; import me.grishka.appkit.Nav; import me.grishka.appkit.api.Callback; @@ -93,6 +99,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + E.register(this); accountID = getArguments().getString("account"); timelineDefinitions = GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES); assert timelineDefinitions != null; @@ -212,7 +219,6 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab } if(GithubSelfUpdater.needSelfUpdating()){ - E.register(this); updateUpdateState(GithubSelfUpdater.getInstance().getState()); } } @@ -572,6 +578,50 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab outState.putInt("selectedTab", pager.getCurrentItem()); } + @Subscribe + public void onHashtagUpdatedEvent(HashtagUpdatedEvent event) { + handleListEvent(hashtagsItems, h -> h.name.equalsIgnoreCase(event.name), event.following, () -> { + Hashtag hashtag = new Hashtag(); + hashtag.name = event.name; + hashtag.following = true; + return hashtag; + }); + } + + @Subscribe + public void onListDeletedEvent(ListDeletedEvent event) { + handleListEvent(listItems, l -> l.id.equals(event.id), false, null); + } + + @Subscribe + public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) { + handleListEvent(listItems, l -> l.id.equals(event.id), true, () -> { + ListTimeline list = new ListTimeline(); + list.id = event.id; + list.title = event.title; + list.repliesPolicy = event.repliesPolicy; + return list; + }); + } + + private void handleListEvent( + Map existingThings, + Predicate matchExisting, + boolean shouldBeInList, + Supplier makeNewThing + ) { + Optional> existingThing = existingThings.entrySet().stream() + .filter(e -> matchExisting.test(e.getValue())).findFirst(); + if (shouldBeInList) { + existingThings.put(existingThing.isPresent() + ? existingThing.get().getKey() : View.generateViewId(), makeNewThing.get()); + createOptionsMenu(); + } else if (existingThing.isPresent() && !shouldBeInList) { + existingThings.remove(existingThing.get().getKey()); + createOptionsMenu(); + } + } + private class HomePagerAdapter extends RecyclerView.Adapter { @NonNull @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java index 99cfa4021..955571245 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java @@ -11,10 +11,13 @@ import android.widget.ImageButton; import androidx.annotation.Nullable; +import org.joinmastodon.android.E; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.lists.GetList; import org.joinmastodon.android.api.requests.lists.UpdateList; import org.joinmastodon.android.api.requests.timelines.GetListTimeline; +import org.joinmastodon.android.events.ListDeletedEvent; +import org.joinmastodon.android.events.ListUpdatedCreatedEvent; import org.joinmastodon.android.model.ListTimeline; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.TimelineDefinition; @@ -87,33 +90,30 @@ public class ListTimelineFragment extends PinnableStatusListFragment { .setTitle(R.string.sk_edit_list_title) .setIcon(R.drawable.ic_fluent_people_list_28_regular) .setView(editor) - .setPositiveButton(R.string.save, (d, which) -> - new UpdateList(listID, editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() { - @Override - public void onSuccess(ListTimeline list) { - setTitle(list.title); - listTitle = list.title; - repliesPolicy = list.repliesPolicy; - Bundle result = new Bundle(); - result.putString("listID", listID); - result.putString("listTitle", listTitle); - if (repliesPolicy != null) result.putInt("repliesPolicy", repliesPolicy.ordinal()); - setResult(true, result); - } + .setPositiveButton(R.string.save, (d, which) -> { + String newTitle = editor.getTitle().trim(); + setTitle(newTitle); + new UpdateList(listID, newTitle, editor.getRepliesPolicy()).setCallback(new Callback<>() { + @Override + public void onSuccess(ListTimeline list) { + setTitle(list.title); + listTitle = list.title; + repliesPolicy = list.repliesPolicy; + E.post(new ListUpdatedCreatedEvent(listID, listTitle, repliesPolicy)); + } - @Override - public void onError(ErrorResponse error) { - error.showToast(getContext()); - } - }).exec(accountID)) + @Override + public void onError(ErrorResponse error) { + setTitle(listTitle); + error.showToast(getContext()); + } + }).exec(accountID); + }) .setNegativeButton(R.string.cancel, (d, which) -> {}) .show(); } else if (item.getItemId() == R.id.delete) { UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> { - Bundle result = new Bundle(); - result.putBoolean("deleted", true); - result.putString("listID", listID); - setResult(true, result); + E.post(new ListDeletedEvent(listID)); Nav.finish(this); }); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelinesFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelinesFragment.java index 221d46304..929868c1a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelinesFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelinesFragment.java @@ -12,12 +12,17 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; +import com.squareup.otto.Subscribe; + +import org.joinmastodon.android.E; import org.joinmastodon.android.R; import org.joinmastodon.android.api.MastodonAPIRequest; import org.joinmastodon.android.api.requests.lists.AddAccountsToList; import org.joinmastodon.android.api.requests.lists.CreateList; import org.joinmastodon.android.api.requests.lists.GetLists; import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList; +import org.joinmastodon.android.events.ListDeletedEvent; +import org.joinmastodon.android.events.ListUpdatedCreatedEvent; import org.joinmastodon.android.model.ListTimeline; import org.joinmastodon.android.ui.DividerItemDecoration; import org.joinmastodon.android.ui.M3AlertDialogBuilder; @@ -37,8 +42,6 @@ import me.grishka.appkit.utils.BindableViewHolder; import me.grishka.appkit.views.UsableRecyclerView; public class ListTimelinesFragment extends BaseRecyclerFragment implements ScrollableToTop { - private static final int LIST_CHANGED_RESULT = 987; - private String accountId; private String profileAccountId; private final HashMap userInListBefore = new HashMap<>(); @@ -55,6 +58,7 @@ public class ListTimelinesFragment extends BaseRecyclerFragment im Bundle args=getArguments(); accountId=args.getString("account"); setHasOptionsMenu(true); + E.register(this); if(args.containsKey("profileAccount")){ profileAccountId=args.getString("profileAccount"); @@ -98,6 +102,7 @@ public class ListTimelinesFragment extends BaseRecyclerFragment im saveListMembership(list.id, true); data.add(0, list); adapter.notifyItemRangeInserted(0, 1); + E.post(new ListUpdatedCreatedEvent(list.id, list.title, list.repliesPolicy)); } @Override @@ -116,9 +121,14 @@ public class ListTimelinesFragment extends BaseRecyclerFragment im userInList.put(listId, isMember); List accountIdList = Collections.singletonList(profileAccountId); MastodonAPIRequest req = isMember ? new AddAccountsToList(listId, accountIdList) : new RemoveAccountsFromList(listId, accountIdList); - req.setCallback(new SimpleCallback<>(this) { - @Override - public void onSuccess(Object o) {} + req.setCallback(new Callback<>() { + @Override + public void onSuccess(Object o) {} + + @Override + public void onError(ErrorResponse error) { + error.showToast(getContext()); + } }).exec(accountId); } @@ -154,29 +164,31 @@ public class ListTimelinesFragment extends BaseRecyclerFragment im .exec(accountId); } - @Override - public void onFragmentResult(int reqCode, boolean listChanged, Bundle result){ - if (reqCode == LIST_CHANGED_RESULT && listChanged) { - String listID = result.getString("listID"); - for (int i = 0; i < data.size(); i++) { - ListTimeline item = data.get(i); - if (item.id.equals(listID)) { - if (result.getBoolean("deleted")) { - data.remove(i); - adapter.notifyItemRemoved(i); - } else { - item.title = result.getString("listTitle", item.title); - if (result.containsKey("repliesPolicy")) { - item.repliesPolicy = ListTimeline.RepliesPolicy.values()[result.getInt("repliesPolicy")]; - } - adapter.notifyItemChanged(i); - } - break; - } - } - } + @Subscribe + public void onListDeletedEvent(ListDeletedEvent event) { + for (int i = 0; i < data.size(); i++) { + ListTimeline item = data.get(i); + if (item.id.equals(event.id)) { + data.remove(i); + adapter.notifyItemRemoved(i); + break; + } + } } + @Subscribe + public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) { + for (int i = 0; i < data.size(); i++) { + ListTimeline item = data.get(i); + if (item.id.equals(event.id)) { + item.title = event.title; + item.repliesPolicy = event.repliesPolicy; + adapter.notifyItemChanged(i); + break; + } + } + } + @Override protected RecyclerView.Adapter getAdapter() { return adapter = new ListsAdapter(); @@ -240,7 +252,7 @@ public class ListTimelinesFragment extends BaseRecyclerFragment im args.putString("listID", item.id); args.putString("listTitle", item.title); if (item.repliesPolicy != null) args.putInt("repliesPolicy", item.repliesPolicy.ordinal()); - Nav.goForResult(getActivity(), ListTimelineFragment.class, args, LIST_CHANGED_RESULT, ListTimelinesFragment.this); + Nav.go(getActivity(), ListTimelineFragment.class, args); } } }