enable remote-following accounts

closes sk22#431
This commit is contained in:
sk
2023-02-16 19:44:39 +01:00
parent b07858a66d
commit 6b9b6710cf
11 changed files with 106 additions and 8 deletions

View File

@@ -24,6 +24,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -37,6 +38,7 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
import org.joinmastodon.android.GlobalUserPreferences;
@@ -298,6 +300,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
});
actionButton.setOnClickListener(this::onActionButtonClick);
actionButton.setOnLongClickListener(this::onActionButtonLongClick);
notifyButton.setOnClickListener(this::onNotifyButtonClick);
avatar.setOnClickListener(this::onAvatarClick);
cover.setOnClickListener(this::onCoverClick);
@@ -601,6 +604,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
return;
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags);
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
MenuItem openWithAccounts = menu.findItem(R.id.open_with_account);
openWithAccounts.setVisible(hasMultipleAccounts);
SubMenu accountsMenu = openWithAccounts.getSubMenu();
if (hasMultipleAccounts) {
accountsMenu.clear();
UiUtils.populateAccountsMenu(accountID, accountsMenu, s-> UiUtils.openURL(
getActivity(), s.getID(), account.url, false
));
}
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
if(isOwnProfile)
return;
@@ -794,6 +807,31 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
}
private boolean onActionButtonLongClick(View v) {
if (isOwnProfile || AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
UiUtils.lookupAccount(getActivity(), account, session.getID(), accountID, acc -> {
if (acc == null) return;
new SetAccountFollowed(acc.id, true, true).setCallback(new Callback<>() {
@Override
public void onSuccess(Relationship relationship) {
Toast.makeText(
getActivity(),
getString(R.string.sk_followed_as, session.self.getShortUsername()),
Toast.LENGTH_SHORT
).show();
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getActivity());
}
}).exec(session.getID());
});
}, null);
return true;
}
private void setActionProgressVisible(boolean visible){
actionButton.setTextVisible(!visible);
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);

View File

@@ -14,7 +14,7 @@ import java.util.List;
* Represents a user of Mastodon and their associated profile.
*/
@Parcel
public class Account extends BaseModel{
public class Account extends BaseModel implements Searchable{
// Base attributes
/**
@@ -135,6 +135,11 @@ public class Account extends BaseModel{
public List<Role> roles;
@Override
public String getQuery() {
return url;
}
@Parcel
public static class Role {
public String name;

View File

@@ -0,0 +1,5 @@
package org.joinmastodon.android.model;
public interface Searchable {
String getQuery();
}

View File

@@ -11,7 +11,7 @@ import java.time.Instant;
import java.util.List;
@Parcel
public class Status extends BaseModel implements DisplayItemsParent{
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
@RequiredField
public String id;
@RequiredField
@@ -163,4 +163,9 @@ public class Status extends BaseModel implements DisplayItemsParent{
s.filtered = List.of();
return s;
}
@Override
public String getQuery() {
return url;
}
}

View File

@@ -190,6 +190,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
String accountID = session.getID();
args.putString("account", accountID);
UiUtils.lookupStatus(v.getContext(), item.status, accountID, item.accountID, status -> {
if (status == null) return;
args.putParcelable("replyTo", Parcels.wrap(status));
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
});

View File

@@ -455,7 +455,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
if (hasMultipleAccounts && accountsMenu != null) {
openWithAccounts.setVisible(true);
accountsMenu.clear();
populateAccountsMenu(accountsMenu);
UiUtils.populateAccountsMenu(item.accountID, accountsMenu, s-> UiUtils.openURL(
item.parentFragment.getActivity(), s.getID(), item.status.url, false
));
} else if (openWithAccounts != null) {
openWithAccounts.setVisible(false);
}

View File

@@ -87,6 +87,7 @@ import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.SearchResults;
import org.joinmastodon.android.model.Searchable;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
@@ -107,8 +108,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -982,6 +985,8 @@ public class UiUtils {
public static void pickInteractAs(Context context, String accountID, Status sourceStatus, Predicate<Status> checkInteracted, InteractionPerformer interactionPerformer, @StringRes int interactAsRes, @StringRes int interactedAsAccountRes, @StringRes int alreadyInteractedRes, @DrawableRes int iconRes) {
pickAccount(context, accountID, interactAsRes, iconRes, session -> {
lookupStatus(context, sourceStatus, session.getID(), accountID, status -> {
if (status == null) return;
if (checkInteracted.test(status)) {
Toast.makeText(context, alreadyInteractedRes, Toast.LENGTH_SHORT).show();
return;
@@ -997,18 +1002,33 @@ public class UiUtils {
}, null);
}
public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer<Status> statusConsumer) {
public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer<Status> resultConsumer) {
lookup(context, queryStatus, targetAccountID, sourceAccountID, GetSearchResults.Type.STATUSES, resultConsumer, results ->
!results.statuses.isEmpty() ? Optional.of(results.statuses.get(0)) : Optional.empty()
);
}
public static void lookupAccount(Context context, Account queryAccount, String targetAccountID, @Nullable String sourceAccountID, Consumer<Account> resultConsumer) {
lookup(context, queryAccount, targetAccountID, sourceAccountID, GetSearchResults.Type.ACCOUNTS, resultConsumer, results ->
!results.accounts.isEmpty() ? Optional.of(results.accounts.get(0)) : Optional.empty()
);
}
public static <T extends Searchable> void lookup(Context context, T query, String targetAccountID, @Nullable String sourceAccountID, @Nullable GetSearchResults.Type type, Consumer<T> resultConsumer, Function<SearchResults, Optional<T>> extractResult) {
if (sourceAccountID != null && targetAccountID.startsWith(sourceAccountID.substring(0, sourceAccountID.indexOf('_')))) {
statusConsumer.accept(queryStatus);
resultConsumer.accept(query);
return;
}
new GetSearchResults(queryStatus.url, GetSearchResults.Type.STATUSES, true).setCallback(new Callback<>() {
new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() {
@Override
public void onSuccess(SearchResults results) {
if (!results.statuses.isEmpty()) statusConsumer.accept(results.statuses.get(0));
else
Optional<T> result = extractResult.apply(results);
if (result.isPresent()) resultConsumer.accept(result.get());
else {
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
resultConsumer.accept(null);
}
}
@Override
@@ -1245,6 +1265,17 @@ public class UiUtils {
return intent;
}
public static void populateAccountsMenu(String excludeAccountID, Menu menu, Consumer<AccountSession> onClick) {
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
sessions.stream().filter(s -> !s.getID().equals(excludeAccountID)).forEach(s -> {
String username = "@"+s.self.username+"@"+s.domain;
menu.add(username).setOnMenuItemClickListener((c) -> {
onClick.accept(s);
return true;
});
});
}
/**
* Wraps a View.OnClickListener to filter multiple clicks in succession.
* Useful for buttons that perform some action that changes their state asynchronously.