Follow requests list is available. Closes #222
This commit is contained in:
parent
41088de6be
commit
7d83a9aaba
18 changed files with 403 additions and 77 deletions
|
@ -19,4 +19,5 @@ interface AccountActionListener {
|
|||
void onViewAccount(String id);
|
||||
void onMute(final boolean mute, final String id, final int position);
|
||||
void onBlock(final boolean block, final String id, final int position);
|
||||
void onRespondToFollowRequest(final boolean accept, final String id, final int position);
|
||||
}
|
||||
|
|
|
@ -24,16 +24,17 @@ import android.support.v7.app.ActionBar;
|
|||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
|
||||
public class BlocksActivity extends BaseActivity {
|
||||
public class AccountListActivity extends BaseActivity {
|
||||
enum Type {
|
||||
BLOCKS,
|
||||
MUTES
|
||||
MUTES,
|
||||
FOLLOW_REQUESTS,
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_blocks);
|
||||
setContentView(R.layout.activity_account_list);
|
||||
|
||||
Type type;
|
||||
Intent intent = getIntent();
|
||||
|
@ -48,21 +49,29 @@ public class BlocksActivity extends BaseActivity {
|
|||
ActionBar bar = getSupportActionBar();
|
||||
if (bar != null) {
|
||||
switch (type) {
|
||||
case MUTES: { bar.setTitle(getString(R.string.title_mutes)); break; }
|
||||
case BLOCKS: { bar.setTitle(getString(R.string.title_blocks)); break; }
|
||||
case MUTES: { bar.setTitle(getString(R.string.title_mutes)); break; }
|
||||
case FOLLOW_REQUESTS: {
|
||||
bar.setTitle(getString(R.string.title_follow_requests));
|
||||
break;
|
||||
}
|
||||
}
|
||||
bar.setDisplayHomeAsUpEnabled(true);
|
||||
bar.setDisplayShowHomeEnabled(true);
|
||||
}
|
||||
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
AccountFragment.Type fragmentType;
|
||||
AccountListFragment.Type fragmentType;
|
||||
switch (type) {
|
||||
case MUTES: { fragmentType = AccountFragment.Type.MUTES; break; }
|
||||
default:
|
||||
case BLOCKS: { fragmentType = AccountFragment.Type.BLOCKS; break; }
|
||||
case BLOCKS: { fragmentType = AccountListFragment.Type.BLOCKS; break; }
|
||||
case MUTES: { fragmentType = AccountListFragment.Type.MUTES; break; }
|
||||
case FOLLOW_REQUESTS: {
|
||||
fragmentType = AccountListFragment.Type.FOLLOW_REQUESTS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Fragment fragment = AccountFragment.newInstance(fragmentType);
|
||||
Fragment fragment = AccountListFragment.newInstance(fragmentType);
|
||||
fragmentTransaction.add(R.id.fragment_container, fragment);
|
||||
fragmentTransaction.commit();
|
||||
}
|
|
@ -38,16 +38,15 @@ import retrofit2.Call;
|
|||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class AccountFragment extends BaseFragment implements AccountActionListener {
|
||||
private static final String TAG = "Account"; // logging tag
|
||||
|
||||
private Call<List<Account>> listCall;
|
||||
public class AccountListFragment extends BaseFragment implements AccountActionListener {
|
||||
private static final String TAG = "AccountList"; // logging tag
|
||||
|
||||
public enum Type {
|
||||
FOLLOWS,
|
||||
FOLLOWERS,
|
||||
BLOCKS,
|
||||
MUTES,
|
||||
FOLLOW_REQUESTS,
|
||||
}
|
||||
|
||||
private Type type;
|
||||
|
@ -59,17 +58,17 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
private TabLayout.OnTabSelectedListener onTabSelectedListener;
|
||||
private MastodonAPI api;
|
||||
|
||||
public static AccountFragment newInstance(Type type) {
|
||||
public static AccountListFragment newInstance(Type type) {
|
||||
Bundle arguments = new Bundle();
|
||||
AccountFragment fragment = new AccountFragment();
|
||||
AccountListFragment fragment = new AccountListFragment();
|
||||
arguments.putSerializable("type", type);
|
||||
fragment.setArguments(arguments);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public static AccountFragment newInstance(Type type, String accountId) {
|
||||
public static AccountListFragment newInstance(Type type, String accountId) {
|
||||
Bundle arguments = new Bundle();
|
||||
AccountFragment fragment = new AccountFragment();
|
||||
AccountListFragment fragment = new AccountListFragment();
|
||||
arguments.putSerializable("type", type);
|
||||
arguments.putString("accountId", accountId);
|
||||
fragment.setArguments(arguments);
|
||||
|
@ -90,7 +89,7 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
View rootView = inflater.inflate(R.layout.fragment_account, container, false);
|
||||
View rootView = inflater.inflate(R.layout.fragment_account_list, container, false);
|
||||
|
||||
Context context = getContext();
|
||||
recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
|
||||
|
@ -108,6 +107,8 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
adapter = new BlocksAdapter(this);
|
||||
} else if (type == Type.MUTES) {
|
||||
adapter = new MutesAdapter(this);
|
||||
} else if (type == Type.FOLLOW_REQUESTS) {
|
||||
adapter = new FollowRequestsAdapter(this);
|
||||
} else {
|
||||
adapter = new FollowAdapter(this);
|
||||
}
|
||||
|
@ -157,12 +158,6 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
recyclerView.addOnScrollListener(scrollListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (listCall != null) listCall.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (jumpToTopAllowed()) {
|
||||
|
@ -189,6 +184,7 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
}
|
||||
};
|
||||
|
||||
Call<List<Account>> listCall;
|
||||
switch (type) {
|
||||
default:
|
||||
case FOLLOWS: {
|
||||
|
@ -207,6 +203,10 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
listCall = api.mutes(fromId, uptoId, null);
|
||||
break;
|
||||
}
|
||||
case FOLLOW_REQUESTS: {
|
||||
listCall = api.followRequests(fromId, uptoId, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
callList.add(listCall);
|
||||
listCall.enqueue(cb);
|
||||
|
@ -239,12 +239,14 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
Log.e(TAG, "Fetch failure: " + exception.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAccount(String id) {
|
||||
Intent intent = new Intent(getContext(), AccountActivity.class);
|
||||
intent.putExtra("id", id);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMute(final boolean mute, final String id, final int position) {
|
||||
if (api == null) {
|
||||
/* If somehow an unmute button is clicked after onCreateView but before
|
||||
|
@ -308,6 +310,7 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
Log.e(TAG, String.format("Failed to %s account id %s", verb, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlock(final boolean block, final String id, final int position) {
|
||||
if (api == null) {
|
||||
/* If somehow an unblock button is clicked after onCreateView but before
|
||||
|
@ -371,6 +374,54 @@ public class AccountFragment extends BaseFragment implements AccountActionListen
|
|||
Log.e(TAG, String.format("Failed to %s account id %s", verb, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRespondToFollowRequest(final boolean accept, final String accountId,
|
||||
final int position) {
|
||||
if (api == null) {
|
||||
/* If somehow an response button is clicked after onCreateView but before
|
||||
* onActivityCreated, then this would get called with a null api object, so this eats
|
||||
* that input. */
|
||||
Log.d(TAG, "MastodonAPI isn't initialised, so follow requests can't be responded to.");
|
||||
return;
|
||||
}
|
||||
|
||||
Callback<Relationship> callback = new Callback<Relationship>() {
|
||||
@Override
|
||||
public void onResponse(Call<Relationship> call, Response<Relationship> response) {
|
||||
if (response.isSuccessful()) {
|
||||
onRespondToFollowRequestSuccess(position);
|
||||
} else {
|
||||
onRespondToFollowRequestFailure(accept, accountId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<Relationship> call, Throwable t) {
|
||||
onRespondToFollowRequestFailure(accept, accountId);
|
||||
}
|
||||
};
|
||||
|
||||
Call<Relationship> call;
|
||||
if (accept) {
|
||||
call = api.authorizeFollowRequest(accountId);
|
||||
} else {
|
||||
call = api.rejectFollowRequest(accountId);
|
||||
}
|
||||
callList.add(call);
|
||||
call.enqueue(callback);
|
||||
}
|
||||
|
||||
private void onRespondToFollowRequestSuccess(int position) {
|
||||
FollowRequestsAdapter followRequestsAdapter = (FollowRequestsAdapter) adapter;
|
||||
followRequestsAdapter.removeItem(position);
|
||||
}
|
||||
|
||||
private void onRespondToFollowRequestFailure(boolean accept, String accountId) {
|
||||
String verb = (accept) ? "accept" : "reject";
|
||||
String message = String.format("Failed to %s account id %s.", verb, accountId);
|
||||
Log.e(TAG, message);
|
||||
}
|
||||
|
||||
private boolean jumpToTopAllowed() {
|
||||
return type == Type.FOLLOWS || type == Type.FOLLOWERS;
|
||||
}
|
|
@ -51,10 +51,10 @@ class AccountPagerAdapter extends FragmentPagerAdapter {
|
|||
return TimelineFragment.newInstance(TimelineFragment.Kind.USER, accountId);
|
||||
}
|
||||
case 1: {
|
||||
return AccountFragment.newInstance(AccountFragment.Type.FOLLOWS, accountId);
|
||||
return AccountListFragment.newInstance(AccountListFragment.Type.FOLLOWS, accountId);
|
||||
}
|
||||
case 2: {
|
||||
return AccountFragment.newInstance(AccountFragment.Type.FOLLOWERS, accountId);
|
||||
return AccountListFragment.newInstance(AccountListFragment.Type.FOLLOWERS, accountId);
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.pkmmte.view.CircularImageView;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
class FollowRequestsAdapter extends AccountAdapter {
|
||||
private static final int VIEW_TYPE_FOLLOW_REQUEST = 0;
|
||||
private static final int VIEW_TYPE_FOOTER = 1;
|
||||
|
||||
FollowRequestsAdapter(AccountActionListener accountActionListener) {
|
||||
super(accountActionListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
default:
|
||||
case VIEW_TYPE_FOLLOW_REQUEST: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_follow_request, parent, false);
|
||||
return new FollowRequestViewHolder(view);
|
||||
}
|
||||
case VIEW_TYPE_FOOTER: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_footer, parent, false);
|
||||
return new FooterViewHolder(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (position < accountList.size()) {
|
||||
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupActionListener(accountActionListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == accountList.size()) {
|
||||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
return VIEW_TYPE_FOLLOW_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
static class FollowRequestViewHolder extends RecyclerView.ViewHolder {
|
||||
@BindView(R.id.follow_request_avatar) CircularImageView avatar;
|
||||
@BindView(R.id.follow_request_username) TextView username;
|
||||
@BindView(R.id.follow_request_display_name) TextView displayName;
|
||||
@BindView(R.id.follow_request_accept) ImageButton accept;
|
||||
@BindView(R.id.follow_request_reject) ImageButton reject;
|
||||
|
||||
private String id;
|
||||
|
||||
FollowRequestViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
|
||||
void setupWithAccount(Account account) {
|
||||
id = account.id;
|
||||
displayName.setText(account.getDisplayName());
|
||||
String format = username.getContext().getString(R.string.status_username_format);
|
||||
String formattedUsername = String.format(format, account.username);
|
||||
username.setText(formattedUsername);
|
||||
Picasso.with(avatar.getContext())
|
||||
.load(account.avatar)
|
||||
.error(R.drawable.avatar_error)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar);
|
||||
}
|
||||
|
||||
void setupActionListener(final AccountActionListener listener) {
|
||||
accept.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onRespondToFollowRequest(true, id, position);
|
||||
}
|
||||
}
|
||||
});
|
||||
reject.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onRespondToFollowRequest(false, id, position);
|
||||
}
|
||||
}
|
||||
});
|
||||
avatar.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onViewAccount(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -304,18 +304,22 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove
|
|||
Intent intent = new Intent(MainActivity.this, FavouritesActivity.class);
|
||||
startActivity(intent);
|
||||
} else if (drawerItemIdentifier == 2) {
|
||||
Intent intent = new Intent(MainActivity.this, BlocksActivity.class);
|
||||
intent.putExtra("type", BlocksActivity.Type.MUTES);
|
||||
Intent intent = new Intent(MainActivity.this, AccountListActivity.class);
|
||||
intent.putExtra("type", AccountListActivity.Type.MUTES);
|
||||
startActivity(intent);
|
||||
} else if (drawerItemIdentifier == 3) {
|
||||
Intent intent = new Intent(MainActivity.this, BlocksActivity.class);
|
||||
intent.putExtra("type", BlocksActivity.Type.BLOCKS);
|
||||
Intent intent = new Intent(MainActivity.this, AccountListActivity.class);
|
||||
intent.putExtra("type", AccountListActivity.Type.BLOCKS);
|
||||
startActivity(intent);
|
||||
} else if (drawerItemIdentifier == 4) {
|
||||
Intent intent = new Intent(MainActivity.this, PreferencesActivity.class);
|
||||
startActivity(intent);
|
||||
} else if (drawerItemIdentifier == 5) {
|
||||
logout();
|
||||
} else if (drawerItemIdentifier == 6) {
|
||||
Intent intent = new Intent(MainActivity.this, AccountListActivity.class);
|
||||
intent.putExtra("type", AccountListActivity.Type.FOLLOW_REQUESTS);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,36 +447,7 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove
|
|||
onFetchUserInfoFailure(new Exception(response.message()));
|
||||
return;
|
||||
}
|
||||
|
||||
headerResult.clear();
|
||||
|
||||
Account me = response.body();
|
||||
ImageView background = headerResult.getHeaderBackgroundView();
|
||||
int backgroundWidth = background.getWidth();
|
||||
int backgroundHeight = background.getHeight();
|
||||
if (backgroundWidth == 0 || backgroundHeight == 0) {
|
||||
/* The header ImageView may not be layed out when the verify credentials call
|
||||
* returns so measure the dimensions and use those. */
|
||||
background.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY);
|
||||
backgroundWidth = background.getMeasuredWidth();
|
||||
backgroundHeight = background.getMeasuredHeight();
|
||||
}
|
||||
|
||||
Picasso.with(MainActivity.this)
|
||||
.load(me.header)
|
||||
.placeholder(R.drawable.account_header_missing)
|
||||
.resize(backgroundWidth, backgroundHeight)
|
||||
.centerCrop()
|
||||
.into(background);
|
||||
|
||||
headerResult.addProfiles(
|
||||
new ProfileDrawerItem()
|
||||
.withName(me.getDisplayName())
|
||||
.withEmail(String.format("%s@%s", me.username, domain))
|
||||
.withIcon(me.avatar)
|
||||
);
|
||||
|
||||
onFetchUserInfoSuccess(me.id, me.username);
|
||||
onFetchUserInfoSuccess(response.body(), domain);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -482,9 +457,48 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove
|
|||
});
|
||||
}
|
||||
|
||||
private void onFetchUserInfoSuccess(String id, String username) {
|
||||
loggedInAccountId = id;
|
||||
loggedInAccountUsername = username;
|
||||
private void onFetchUserInfoSuccess(Account me, String domain) {
|
||||
// Add the header image and avatar from the account, into the navigation drawer header.
|
||||
headerResult.clear();
|
||||
|
||||
ImageView background = headerResult.getHeaderBackgroundView();
|
||||
int backgroundWidth = background.getWidth();
|
||||
int backgroundHeight = background.getHeight();
|
||||
if (backgroundWidth == 0 || backgroundHeight == 0) {
|
||||
/* The header ImageView may not be layed out when the verify credentials call returns so
|
||||
* measure the dimensions and use those. */
|
||||
background.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY);
|
||||
backgroundWidth = background.getMeasuredWidth();
|
||||
backgroundHeight = background.getMeasuredHeight();
|
||||
}
|
||||
|
||||
Picasso.with(MainActivity.this)
|
||||
.load(me.header)
|
||||
.placeholder(R.drawable.account_header_missing)
|
||||
.resize(backgroundWidth, backgroundHeight)
|
||||
.centerCrop()
|
||||
.into(background);
|
||||
|
||||
headerResult.addProfiles(
|
||||
new ProfileDrawerItem()
|
||||
.withName(me.getDisplayName())
|
||||
.withEmail(String.format("%s@%s", me.username, domain))
|
||||
.withIcon(me.avatar)
|
||||
);
|
||||
|
||||
// Show follow requests in the menu, if this is a locked account.
|
||||
if (me.locked) {
|
||||
PrimaryDrawerItem followRequestsItem = new PrimaryDrawerItem()
|
||||
.withIdentifier(6)
|
||||
.withName(R.string.action_view_follow_requests)
|
||||
.withSelectable(false)
|
||||
.withIcon(GoogleMaterial.Icon.gmd_person_add);
|
||||
drawer.addItemAtPosition(followRequestsItem, 3);
|
||||
}
|
||||
|
||||
// Update the current login information.
|
||||
loggedInAccountId = me.id;
|
||||
loggedInAccountUsername = me.username;
|
||||
getPrivatePreferences().edit()
|
||||
.putString("loggedInAccountId", loggedInAccountId)
|
||||
.putString("loggedInAccountUsername", loggedInAccountUsername)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue