diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/AddAccountsToList.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/AddAccountsToList.java new file mode 100644 index 000000000..f3db76322 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/AddAccountsToList.java @@ -0,0 +1,17 @@ +package org.joinmastodon.android.api.requests.lists; + +import org.joinmastodon.android.api.MastodonAPIRequest; +import java.util.List; + +public class AddAccountsToList extends MastodonAPIRequest { + public AddAccountsToList(String listId, List accountIds){ + super(HttpMethod.POST, "/lists/"+listId+"/accounts", Object.class); + Request req = new Request(); + req.accountIds = accountIds; + setRequestBody(req); + } + + public static class Request{ + public List accountIds; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/GetLists.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/GetLists.java index a4f1752e7..61abeb03a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/GetLists.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/GetLists.java @@ -11,4 +11,7 @@ public class GetLists extends MastodonAPIRequest>{ public GetLists() { super(HttpMethod.GET, "/lists", new TypeToken<>(){}); } + public GetLists(String accountID) { + super(HttpMethod.GET, "/accounts/"+accountID+"/lists", new TypeToken<>(){}); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/RemoveAccountsFromList.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/RemoveAccountsFromList.java new file mode 100644 index 000000000..f285d54f6 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/lists/RemoveAccountsFromList.java @@ -0,0 +1,17 @@ +package org.joinmastodon.android.api.requests.lists; + +import org.joinmastodon.android.api.MastodonAPIRequest; +import java.util.List; + +public class RemoveAccountsFromList extends MastodonAPIRequest { + public RemoveAccountsFromList(String listId, List accountIds){ + super(HttpMethod.DELETE, "/lists/"+listId+"/accounts", Object.class); + Request req = new Request(); + req.accountIds = accountIds; + setRequestBody(req); + } + + public static class Request{ + public List accountIds; + } +} 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 465170bb9..275a74f5a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java @@ -3,6 +3,8 @@ package org.joinmastodon.android.fragments; import android.app.Activity; import android.media.MediaRouter; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; @@ -33,6 +35,13 @@ public class ListTimelineFragment extends StatusListFragment { listID=getArguments().getString("listID"); listTitle=getArguments().getString("listTitle"); setTitle(listTitle); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + // inflater.inflate(R.menu.list, menu); } @Override 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 70da67991..a78f256c5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelinesFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelinesFragment.java @@ -1,29 +1,48 @@ package org.joinmastodon.android.fragments; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.RecyclerView; 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.GetLists; -import org.joinmastodon.android.fragments.ScrollableToTop; +import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList; import org.joinmastodon.android.model.ListTimeline; import org.joinmastodon.android.ui.utils.UiUtils; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import me.grishka.appkit.api.ErrorResponse; import me.grishka.appkit.api.SimpleCallback; import me.grishka.appkit.fragments.BaseRecyclerFragment; import me.grishka.appkit.utils.BindableViewHolder; +import me.grishka.appkit.utils.V; import me.grishka.appkit.views.UsableRecyclerView; public class ListTimelinesFragment extends BaseRecyclerFragment implements ScrollableToTop { private String accountId; + private String profileAccountId; + private String profileDisplayUsername; + private HashMap userInListBefore = new HashMap<>(); + private HashMap userInList = new HashMap<>(); + private int inProgress = 0; public ListTimelinesFragment() { super(10); @@ -32,16 +51,76 @@ public class ListTimelinesFragment extends BaseRecyclerFragment im @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - accountId=getArguments().getString("account"); + Bundle args=getArguments(); + accountId=args.getString("account"); + + if(args.containsKey("profileAccount")){ + profileAccountId=args.getString("profileAccount"); + profileDisplayUsername=args.getString("profileDisplayUsername"); + setTitle(getString(R.string.lists_with_user, profileDisplayUsername)); +// setHasOptionsMenu(true); + } + } + + @Override + protected void onShown(){ + super.onShown(); + if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading) + loadData(); + } + +// @Override +// public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { +// Button saveButton=new Button(getActivity()); +// saveButton.setText(R.string.save); +// saveButton.setOnClickListener(this::onSaveClick); +// LinearLayout wrap=new LinearLayout(getActivity()); +// wrap.setOrientation(LinearLayout.HORIZONTAL); +// wrap.addView(saveButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); +// wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8)); +// wrap.setClipToPadding(false); +// MenuItem item=menu.add(R.string.save); +// item.setActionView(wrap); +// item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); +// } + + private void saveListMembership(String listId, boolean isMember) { + 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) {} + }).exec(accountId); } @Override protected void doLoadData(int offset, int count){ - currentRequest=new GetLists() + userInListBefore.clear(); + userInList.clear(); + currentRequest=(profileAccountId != null ? new GetLists(profileAccountId) : new GetLists()) .setCallback(new SimpleCallback<>(this) { @Override - public void onSuccess(List result) { - onDataLoaded(result, false); + public void onSuccess(List lists) { + for (ListTimeline l : lists) userInListBefore.put(l.id, true); + userInList.putAll(userInListBefore); + if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false); + if (profileAccountId == null) return; + + currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListTimelinesFragment.this) { + @Override + public void onSuccess(List allLists) { + List newLists = new ArrayList<>(); + for (ListTimeline l : allLists) { + if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l); + if (!userInListBefore.containsKey(l.id)) { + userInListBefore.put(l.id, false); + } + } + userInList.putAll(userInListBefore); + onDataLoaded(newLists, false); + } + }).exec(accountId); } }) .exec(accountId); @@ -77,15 +156,28 @@ public class ListTimelinesFragment extends BaseRecyclerFragment im private class ListViewHolder extends BindableViewHolder implements UsableRecyclerView.Clickable{ private final TextView title; + private final CheckBox listToggle; public ListViewHolder(){ super(getActivity(), R.layout.item_list_timeline, list); title=findViewById(R.id.title); + listToggle=findViewById(R.id.list_toggle); } @Override public void onBind(ListTimeline item) { title.setText(item.title); + if (profileAccountId != null) { + Boolean checked = userInList.get(item.id); + listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked); + listToggle.setOnClickListener(this::onClickToggle); + } else { + listToggle.setVisibility(View.GONE); + } + } + + private void onClickToggle(View view) { + saveListMembership(item.id, listToggle.isChecked()); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index cef45e805..57915e588 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -531,10 +531,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList return; inflater.inflate(R.menu.profile, menu); menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getDisplayUsername())); + menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.lists_with_user, account.getDisplayUsername())); if(isOwnProfile){ for(int i=0;i + + diff --git a/mastodon/src/main/res/layout/item_list_timeline.xml b/mastodon/src/main/res/layout/item_list_timeline.xml index ecaf7eb17..fa0e399f9 100644 --- a/mastodon/src/main/res/layout/item_list_timeline.xml +++ b/mastodon/src/main/res/layout/item_list_timeline.xml @@ -3,26 +3,29 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="56dp" - android:paddingHorizontal="16dp" android:orientation="horizontal" android:gravity="center"> - - + + diff --git a/mastodon/src/main/res/menu/list.xml b/mastodon/src/main/res/menu/list.xml new file mode 100644 index 000000000..7693ee429 --- /dev/null +++ b/mastodon/src/main/res/menu/list.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/menu/profile.xml b/mastodon/src/main/res/menu/profile.xml index d4bb68701..b2a34198a 100644 --- a/mastodon/src/main/res/menu/profile.xml +++ b/mastodon/src/main/res/menu/profile.xml @@ -6,6 +6,7 @@ + Favorisierte Beiträge Folgeanfrage akzeptieren Folgeanfrage ablehnen + Listen mit %s diff --git a/mastodon/src/main/res/values/strings.xml b/mastodon/src/main/res/values/strings.xml index ec409b211..1a84c4772 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -418,4 +418,5 @@ Favorited posts Accept follow request Reject follow request + Lists with %s