enable remote-following accounts
closes sk22#431
This commit is contained in:
@@ -24,6 +24,7 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.SubMenu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewOutlineProvider;
|
import android.view.ViewOutlineProvider;
|
||||||
@@ -37,6 +38,7 @@ import android.widget.ImageView;
|
|||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
@@ -298,6 +300,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
});
|
});
|
||||||
|
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
|
actionButton.setOnLongClickListener(this::onActionButtonLongClick);
|
||||||
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
||||||
avatar.setOnClickListener(this::onAvatarClick);
|
avatar.setOnClickListener(this::onAvatarClick);
|
||||||
cover.setOnClickListener(this::onCoverClick);
|
cover.setOnClickListener(this::onCoverClick);
|
||||||
@@ -601,6 +604,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
return;
|
return;
|
||||||
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
|
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
|
||||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags);
|
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()));
|
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
||||||
if(isOwnProfile)
|
if(isOwnProfile)
|
||||||
return;
|
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){
|
private void setActionProgressVisible(boolean visible){
|
||||||
actionButton.setTextVisible(!visible);
|
actionButton.setTextVisible(!visible);
|
||||||
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
* Represents a user of Mastodon and their associated profile.
|
* Represents a user of Mastodon and their associated profile.
|
||||||
*/
|
*/
|
||||||
@Parcel
|
@Parcel
|
||||||
public class Account extends BaseModel{
|
public class Account extends BaseModel implements Searchable{
|
||||||
// Base attributes
|
// Base attributes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,6 +135,11 @@ public class Account extends BaseModel{
|
|||||||
|
|
||||||
public List<Role> roles;
|
public List<Role> roles;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuery() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public static class Role {
|
public static class Role {
|
||||||
public String name;
|
public String name;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
public interface Searchable {
|
||||||
|
String getQuery();
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import java.time.Instant;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public class Status extends BaseModel implements DisplayItemsParent{
|
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||||
@RequiredField
|
@RequiredField
|
||||||
public String id;
|
public String id;
|
||||||
@RequiredField
|
@RequiredField
|
||||||
@@ -163,4 +163,9 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
|||||||
s.filtered = List.of();
|
s.filtered = List.of();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuery() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
String accountID = session.getID();
|
String accountID = session.getID();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
UiUtils.lookupStatus(v.getContext(), item.status, accountID, item.accountID, status -> {
|
UiUtils.lookupStatus(v.getContext(), item.status, accountID, item.accountID, status -> {
|
||||||
|
if (status == null) return;
|
||||||
args.putParcelable("replyTo", Parcels.wrap(status));
|
args.putParcelable("replyTo", Parcels.wrap(status));
|
||||||
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -455,7 +455,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
if (hasMultipleAccounts && accountsMenu != null) {
|
if (hasMultipleAccounts && accountsMenu != null) {
|
||||||
openWithAccounts.setVisible(true);
|
openWithAccounts.setVisible(true);
|
||||||
accountsMenu.clear();
|
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) {
|
} else if (openWithAccounts != null) {
|
||||||
openWithAccounts.setVisible(false);
|
openWithAccounts.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ import org.joinmastodon.android.model.Notification;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.SearchResults;
|
import org.joinmastodon.android.model.SearchResults;
|
||||||
|
import org.joinmastodon.android.model.Searchable;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
@@ -107,8 +108,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
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) {
|
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 -> {
|
pickAccount(context, accountID, interactAsRes, iconRes, session -> {
|
||||||
lookupStatus(context, sourceStatus, session.getID(), accountID, status -> {
|
lookupStatus(context, sourceStatus, session.getID(), accountID, status -> {
|
||||||
|
if (status == null) return;
|
||||||
|
|
||||||
if (checkInteracted.test(status)) {
|
if (checkInteracted.test(status)) {
|
||||||
Toast.makeText(context, alreadyInteractedRes, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, alreadyInteractedRes, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
@@ -997,18 +1002,33 @@ public class UiUtils {
|
|||||||
}, null);
|
}, 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('_')))) {
|
if (sourceAccountID != null && targetAccountID.startsWith(sourceAccountID.substring(0, sourceAccountID.indexOf('_')))) {
|
||||||
statusConsumer.accept(queryStatus);
|
resultConsumer.accept(query);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new GetSearchResults(queryStatus.url, GetSearchResults.Type.STATUSES, true).setCallback(new Callback<>() {
|
new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults results) {
|
public void onSuccess(SearchResults results) {
|
||||||
if (!results.statuses.isEmpty()) statusConsumer.accept(results.statuses.get(0));
|
Optional<T> result = extractResult.apply(results);
|
||||||
else
|
if (result.isPresent()) resultConsumer.accept(result.get());
|
||||||
|
else {
|
||||||
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
||||||
|
resultConsumer.accept(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1245,6 +1265,17 @@ public class UiUtils {
|
|||||||
return intent;
|
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.
|
* Wraps a View.OnClickListener to filter multiple clicks in succession.
|
||||||
* Useful for buttons that perform some action that changes their state asynchronously.
|
* Useful for buttons that perform some action that changes their state asynchronously.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||||
|
<path android:pathData="M15.114 25.719c-0.396-0.408-0.746-0.861-1.04-1.35C13.418 24.453 12.725 24.5 12 24.5c-5.111 0-8.5-2.111-8.5-4.785V19l0.007-0.145C3.58 18.095 4.22 17.5 5 17.5h8.624c0.234-0.535 0.529-1.038 0.875-1.5H5c-1.657 0-3 1.343-3 3v0.715C2 23.433 6.21 26 12 26c1.101 0 2.145-0.098 3.114-0.281zM18 8c0-3.314-2.686-6-6-6S6 4.686 6 8s2.686 6 6 6 6-2.686 6-6zM7.5 8c0-2.485 2.015-4.5 4.5-4.5s4.5 2.015 4.5 4.5-2.015 4.5-4.5 4.5S7.5 10.485 7.5 8zm13 19c3.59 0 6.5-2.91 6.5-6.5S24.09 14 20.5 14 14 16.91 14 20.5s2.91 6.5 6.5 6.5zm0-11c0.276 0 0.5 0.224 0.5 0.5V20h3.5c0.276 0 0.5 0.224 0.5 0.5S24.776 21 24.5 21H21v3.5c0 0.276-0.224 0.5-0.5 0.5S20 24.776 20 24.5V21h-3.5c-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5H20v-3.5c0-0.276 0.224-0.5 0.5-0.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/open_with_account" android:title="@string/sk_open_with_account" android:icon="@drawable/ic_fluent_person_swap_24_regular">
|
||||||
|
<menu android:id="@+id/accounts" />
|
||||||
|
</item>
|
||||||
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
|
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
|
||||||
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_off_24_regular"/>
|
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_off_24_regular"/>
|
||||||
<item android:id="@+id/block" android:title="@string/block_user" android:icon="@drawable/ic_fluent_person_prohibited_24_regular"/>
|
<item android:id="@+id/block" android:title="@string/block_user" android:icon="@drawable/ic_fluent_person_prohibited_24_regular"/>
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:id="@+id/followed_hashtags" android:title="@string/sk_hashtags_you_follow" android:icon="@drawable/ic_fluent_number_symbol_24_regular" android:showAsAction="always"/>
|
<item android:id="@+id/followed_hashtags" android:title="@string/sk_hashtags_you_follow" android:icon="@drawable/ic_fluent_number_symbol_24_regular" android:showAsAction="always"/>
|
||||||
<item android:id="@+id/bookmarks" android:title="@string/bookmarks" android:icon="@drawable/ic_fluent_bookmark_multiple_24_regular" android:showAsAction="always"/>
|
<item android:id="@+id/bookmarks" android:title="@string/bookmarks" android:icon="@drawable/ic_fluent_bookmark_multiple_24_regular" android:showAsAction="always"/>
|
||||||
|
<item android:id="@+id/open_with_account" android:title="@string/sk_open_with_account" android:icon="@drawable/ic_fluent_person_swap_24_regular">
|
||||||
|
<menu android:id="@+id/accounts" />
|
||||||
|
</item>
|
||||||
<item android:id="@+id/manage_user_lists" android:title="@string/sk_your_lists" android:icon="@drawable/ic_fluent_people_24_regular"/>
|
<item android:id="@+id/manage_user_lists" android:title="@string/sk_your_lists" android:icon="@drawable/ic_fluent_people_24_regular"/>
|
||||||
<item android:id="@+id/favorites" android:title="@string/your_favorites" android:icon="@drawable/ic_fluent_star_24_regular"/>
|
<item android:id="@+id/favorites" android:title="@string/your_favorites" android:icon="@drawable/ic_fluent_star_24_regular"/>
|
||||||
<item android:id="@+id/scheduled" android:title="@string/sk_unsent_posts" android:icon="@drawable/ic_fluent_folder_open_24_regular"/>
|
<item android:id="@+id/scheduled" android:title="@string/sk_unsent_posts" android:icon="@drawable/ic_fluent_folder_open_24_regular"/>
|
||||||
|
|||||||
@@ -259,4 +259,6 @@
|
|||||||
<string name="sk_unfinished_attachments">Fix attachments?</string>
|
<string name="sk_unfinished_attachments">Fix attachments?</string>
|
||||||
<string name="sk_unfinished_attachments_message">Some attachments haven’t finished uploading.</string>
|
<string name="sk_unfinished_attachments_message">Some attachments haven’t finished uploading.</string>
|
||||||
<string name="sk_settings_hide_interaction">Hide interaction buttons</string>
|
<string name="sk_settings_hide_interaction">Hide interaction buttons</string>
|
||||||
|
<string name="sk_follow_as">Follow from other account</string>
|
||||||
|
<string name="sk_followed_as">Followed from %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user