mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-26 16:47:30 +00:00
Approve/Deny new users
This commit is contained in:
parent
6aacf1945f
commit
7addb16e5c
@ -700,4 +700,8 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
});
|
});
|
||||||
EmojiCompat.init(config);
|
EmojiCompat.init(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Toolbar getToolbar() {
|
||||||
|
return binding.toolbar;
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
package awais.instagrabber.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.viewholder.directmessages.DirectPendingUserViewHolder;
|
||||||
|
import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
|
||||||
|
import awais.instagrabber.repositories.responses.User;
|
||||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||||
|
|
||||||
|
public final class DirectPendingUsersAdapter extends ListAdapter<DirectPendingUsersAdapter.PendingUser, DirectPendingUserViewHolder> {
|
||||||
|
|
||||||
|
private static final DiffUtil.ItemCallback<PendingUser> DIFF_CALLBACK = new DiffUtil.ItemCallback<PendingUser>() {
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
|
||||||
|
return oldItem.user.getPk() == newItem.user.getPk();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
|
||||||
|
return Objects.equals(oldItem.user.getUsername(), newItem.user.getUsername()) &&
|
||||||
|
Objects.equals(oldItem.user.getFullName(), newItem.user.getFullName()) &&
|
||||||
|
Objects.equals(oldItem.requester, newItem.requester);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final PendingUserCallback callback;
|
||||||
|
|
||||||
|
public DirectPendingUsersAdapter(final PendingUserCallback callback) {
|
||||||
|
super(DIFF_CALLBACK);
|
||||||
|
this.callback = callback;
|
||||||
|
setHasStableIds(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void submitPendingRequests(final DirectThreadParticipantRequestsResponse requests) {
|
||||||
|
if (requests == null || requests.getUsers() == null) {
|
||||||
|
submitList(Collections.emptyList());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitList(parse(requests));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PendingUser> parse(final DirectThreadParticipantRequestsResponse requests) {
|
||||||
|
final List<User> users = requests.getUsers();
|
||||||
|
final Map<Long, String> requesterUsernames = requests.getRequesterUsernames();
|
||||||
|
return users.stream()
|
||||||
|
.map(user -> new PendingUser(user, requesterUsernames.get(user.getPk())))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public DirectPendingUserViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||||
|
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||||
|
final LayoutDmPendingUserItemBinding binding = LayoutDmPendingUserItemBinding.inflate(layoutInflater, parent, false);
|
||||||
|
return new DirectPendingUserViewHolder(binding, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull final DirectPendingUserViewHolder holder, final int position) {
|
||||||
|
final PendingUser pendingUser = getItem(position);
|
||||||
|
holder.bind(position, pendingUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(final int position) {
|
||||||
|
final PendingUser item = getItem(position);
|
||||||
|
return item.user.getPk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PendingUser {
|
||||||
|
private final User user;
|
||||||
|
private final String requester;
|
||||||
|
|
||||||
|
private boolean inProgress;
|
||||||
|
|
||||||
|
public PendingUser(final User user, final String requester) {
|
||||||
|
this.user = user;
|
||||||
|
this.requester = requester;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequester() {
|
||||||
|
return requester;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInProgress() {
|
||||||
|
return inProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PendingUser setInProgress(final boolean inProgress) {
|
||||||
|
this.inProgress = inProgress;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface PendingUserCallback {
|
||||||
|
void onClick(int position, PendingUser pendingUser);
|
||||||
|
|
||||||
|
void onApprove(int position, PendingUser pendingUser);
|
||||||
|
|
||||||
|
void onDeny(int position, PendingUser pendingUser);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder.directmessages;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;
|
||||||
|
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;
|
||||||
|
import awais.instagrabber.customviews.VerticalImageSpan;
|
||||||
|
import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
|
||||||
|
import awais.instagrabber.repositories.responses.User;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
|
public class DirectPendingUserViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private static final String TAG = DirectPendingUserViewHolder.class.getSimpleName();
|
||||||
|
|
||||||
|
private final LayoutDmPendingUserItemBinding binding;
|
||||||
|
private final PendingUserCallback callback;
|
||||||
|
private final int drawableSize;
|
||||||
|
|
||||||
|
private VerticalImageSpan verifiedSpan;
|
||||||
|
|
||||||
|
public DirectPendingUserViewHolder(@NonNull final LayoutDmPendingUserItemBinding binding,
|
||||||
|
final PendingUserCallback callback) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
this.callback = callback;
|
||||||
|
drawableSize = Utils.convertDpToPx(24);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(final int position, final PendingUser pendingUser) {
|
||||||
|
if (pendingUser == null) return;
|
||||||
|
binding.getRoot().setOnClickListener(v -> {
|
||||||
|
if (callback == null) return;
|
||||||
|
callback.onClick(position, pendingUser);
|
||||||
|
});
|
||||||
|
setUsername(pendingUser);
|
||||||
|
binding.requester.setText(itemView.getResources().getString(R.string.added_by, pendingUser.getRequester()));
|
||||||
|
binding.profilePic.setImageURI(pendingUser.getUser().getProfilePicUrl());
|
||||||
|
if (pendingUser.isInProgress()) {
|
||||||
|
binding.approve.setVisibility(View.GONE);
|
||||||
|
binding.deny.setVisibility(View.GONE);
|
||||||
|
binding.progress.setVisibility(View.VISIBLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.approve.setVisibility(View.VISIBLE);
|
||||||
|
binding.deny.setVisibility(View.VISIBLE);
|
||||||
|
binding.progress.setVisibility(View.GONE);
|
||||||
|
binding.approve.setOnClickListener(v -> {
|
||||||
|
if (callback == null) return;
|
||||||
|
callback.onApprove(position, pendingUser);
|
||||||
|
});
|
||||||
|
binding.deny.setOnClickListener(v -> {
|
||||||
|
if (callback == null) return;
|
||||||
|
callback.onDeny(position, pendingUser);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUsername(final PendingUser pendingUser) {
|
||||||
|
final User user = pendingUser.getUser();
|
||||||
|
final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());
|
||||||
|
if (user.isVerified()) {
|
||||||
|
if (verifiedSpan == null) {
|
||||||
|
final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);
|
||||||
|
if (verifiedDrawable != null) {
|
||||||
|
final Drawable drawable = verifiedDrawable.mutate();
|
||||||
|
drawable.setBounds(0, 0, drawableSize, drawableSize);
|
||||||
|
verifiedSpan = new VerticalImageSpan(drawable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (verifiedSpan != null) {
|
||||||
|
sb.append(" ");
|
||||||
|
sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "bind: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.username.setText(sb);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package awais.instagrabber.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
|
||||||
|
public class ConfirmDialogFragment extends DialogFragment {
|
||||||
|
private Context context;
|
||||||
|
private ConfirmDialogFragmentCallback callback;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ConfirmDialogFragment newInstance(final int requestCode,
|
||||||
|
@StringRes final int title,
|
||||||
|
@StringRes final int message,
|
||||||
|
@StringRes final int positiveText,
|
||||||
|
@StringRes final int negativeText,
|
||||||
|
@StringRes final int neutralText) {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("requestCode", requestCode);
|
||||||
|
args.putInt("title", title);
|
||||||
|
args.putInt("message", message);
|
||||||
|
args.putInt("positive", positiveText);
|
||||||
|
args.putInt("negative", negativeText);
|
||||||
|
args.putInt("neutral", neutralText);
|
||||||
|
ConfirmDialogFragment fragment = new ConfirmDialogFragment();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfirmDialogFragment() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull final Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
try {
|
||||||
|
callback = (ConfirmDialogFragmentCallback) getParentFragment();
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException("Calling fragment must implement ConfirmDialogFragmentCallback interface");
|
||||||
|
}
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
int title = -1;
|
||||||
|
int message = -1;
|
||||||
|
int positiveButtonText = R.string.ok;
|
||||||
|
int negativeButtonText = R.string.cancel;
|
||||||
|
int neutralButtonText = -1;
|
||||||
|
final int requestCode;
|
||||||
|
if (arguments != null) {
|
||||||
|
title = arguments.getInt("title", -1);
|
||||||
|
message = arguments.getInt("message", -1);
|
||||||
|
positiveButtonText = arguments.getInt("positive", R.string.ok);
|
||||||
|
negativeButtonText = arguments.getInt("negative", R.string.cancel);
|
||||||
|
neutralButtonText = arguments.getInt("neutral", -1);
|
||||||
|
requestCode = arguments.getInt("requestCode", 0);
|
||||||
|
} else {
|
||||||
|
requestCode = 0;
|
||||||
|
}
|
||||||
|
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
|
||||||
|
.setPositiveButton(positiveButtonText, (d, w) -> {
|
||||||
|
if (callback == null) return;
|
||||||
|
callback.onPositiveButtonClicked(requestCode);
|
||||||
|
})
|
||||||
|
.setNegativeButton(negativeButtonText, (dialog, which) -> {
|
||||||
|
if (callback == null) return;
|
||||||
|
callback.onNegativeButtonClicked(requestCode);
|
||||||
|
});
|
||||||
|
if (title > 0) {
|
||||||
|
builder.setTitle(title);
|
||||||
|
}
|
||||||
|
if (message > 0) {
|
||||||
|
builder.setMessage(message);
|
||||||
|
}
|
||||||
|
if (neutralButtonText > 0) {
|
||||||
|
builder.setNeutralButton(neutralButtonText, (dialog, which) -> {
|
||||||
|
if (callback == null) return;
|
||||||
|
callback.onNeutralButtonClicked(requestCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ConfirmDialogFragmentCallback {
|
||||||
|
void onPositiveButtonClicked(int requestCode);
|
||||||
|
|
||||||
|
void onNegativeButtonClicked(int requestCode);
|
||||||
|
|
||||||
|
void onNeutralButtonClicked(int requestCode);
|
||||||
|
}
|
||||||
|
}
|
@ -34,11 +34,17 @@ import java.util.Optional;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import awais.instagrabber.ProfileNavGraphDirections;
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.UserSearchNavGraphDirections;
|
import awais.instagrabber.UserSearchNavGraphDirections;
|
||||||
|
import awais.instagrabber.adapters.DirectPendingUsersAdapter;
|
||||||
|
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;
|
||||||
|
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;
|
||||||
import awais.instagrabber.adapters.DirectUsersAdapter;
|
import awais.instagrabber.adapters.DirectUsersAdapter;
|
||||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
||||||
import awais.instagrabber.databinding.FragmentDirectMessagesSettingsBinding;
|
import awais.instagrabber.databinding.FragmentDirectMessagesSettingsBinding;
|
||||||
|
import awais.instagrabber.dialogs.ConfirmDialogFragment;
|
||||||
|
import awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback;
|
||||||
import awais.instagrabber.dialogs.MultiOptionDialogFragment;
|
import awais.instagrabber.dialogs.MultiOptionDialogFragment;
|
||||||
import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option;
|
import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option;
|
||||||
import awais.instagrabber.fragments.UserSearchFragment;
|
import awais.instagrabber.fragments.UserSearchFragment;
|
||||||
@ -46,16 +52,21 @@ import awais.instagrabber.fragments.UserSearchFragmentDirections;
|
|||||||
import awais.instagrabber.models.Resource;
|
import awais.instagrabber.models.Resource;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
||||||
import awais.instagrabber.viewmodels.DirectInboxViewModel;
|
import awais.instagrabber.viewmodels.DirectInboxViewModel;
|
||||||
import awais.instagrabber.viewmodels.DirectSettingsViewModel;
|
import awais.instagrabber.viewmodels.DirectSettingsViewModel;
|
||||||
|
|
||||||
public class DirectMessageSettingsFragment extends Fragment {
|
public class DirectMessageSettingsFragment extends Fragment implements ConfirmDialogFragmentCallback {
|
||||||
private static final String TAG = DirectMessageSettingsFragment.class.getSimpleName();
|
private static final String TAG = DirectMessageSettingsFragment.class.getSimpleName();
|
||||||
|
public static final int APPROVAL_REQUIRED_REQUEST_CODE = 200;
|
||||||
|
|
||||||
private FragmentDirectMessagesSettingsBinding binding;
|
private FragmentDirectMessagesSettingsBinding binding;
|
||||||
private DirectSettingsViewModel viewModel;
|
private DirectSettingsViewModel viewModel;
|
||||||
private DirectUsersAdapter usersAdapter;
|
private DirectUsersAdapter usersAdapter;
|
||||||
|
private boolean isPendingRequestsSetupDone = false;
|
||||||
|
private DirectPendingUsersAdapter pendingUsersAdapter;
|
||||||
|
private Set<User> approvalRequiredUsers;
|
||||||
// private List<Option<String>> options;
|
// private List<Option<String>> options;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -165,6 +176,7 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
binding = null;
|
binding = null;
|
||||||
|
isPendingRequestsSetupDone = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupObservers() {
|
private void setupObservers() {
|
||||||
@ -178,18 +190,21 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
usersAdapter.setAdminUserIds(adminUserIds);
|
usersAdapter.setAdminUserIds(adminUserIds);
|
||||||
});
|
});
|
||||||
viewModel.getMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
|
viewModel.getMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
|
||||||
|
if (viewModel.isViewerAdmin()) {
|
||||||
|
viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required));
|
||||||
|
viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests);
|
||||||
|
}
|
||||||
final NavController navController = NavHostFragment.findNavController(this);
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
|
final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
|
||||||
if (backStackEntry != null) {
|
if (backStackEntry != null) {
|
||||||
final MutableLiveData<Object> resultLiveData = backStackEntry.getSavedStateHandle().getLiveData("result");
|
final MutableLiveData<Object> resultLiveData = backStackEntry.getSavedStateHandle().getLiveData("result");
|
||||||
resultLiveData.observe(getViewLifecycleOwner(), result -> {
|
resultLiveData.observe(getViewLifecycleOwner(), result -> {
|
||||||
LiveData<Resource<Object>> detailsChangeResourceLiveData = null;
|
|
||||||
if ((result instanceof RankedRecipient)) {
|
if ((result instanceof RankedRecipient)) {
|
||||||
final RankedRecipient recipient = (RankedRecipient) result;
|
final RankedRecipient recipient = (RankedRecipient) result;
|
||||||
final User user = getUser(recipient);
|
final User user = getUser(recipient);
|
||||||
// Log.d(TAG, "result: " + user);
|
// Log.d(TAG, "result: " + user);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
detailsChangeResourceLiveData = viewModel.addMembers(Collections.singleton(recipient.getUser()));
|
addMembers(Collections.singleton(recipient.getUser()));
|
||||||
}
|
}
|
||||||
} else if ((result instanceof Set)) {
|
} else if ((result instanceof Set)) {
|
||||||
try {
|
try {
|
||||||
@ -201,19 +216,35 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
// Log.d(TAG, "result: " + users);
|
// Log.d(TAG, "result: " + users);
|
||||||
detailsChangeResourceLiveData = viewModel.addMembers(users);
|
addMembers(users);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "search users result: ", e);
|
Log.e(TAG, "search users result: ", e);
|
||||||
Snackbar.make(binding.getRoot(), e.getMessage() != null ? e.getMessage() : "", Snackbar.LENGTH_LONG).show();
|
Snackbar.make(binding.getRoot(), e.getMessage() != null ? e.getMessage() : "", Snackbar.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (detailsChangeResourceLiveData != null) {
|
|
||||||
observeDetailsChange(detailsChangeResourceLiveData);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addMembers(final Set<User> users) {
|
||||||
|
final Boolean approvalRequired = viewModel.getApprovalRequiredToJoin().getValue();
|
||||||
|
if (!viewModel.isViewerAdmin() && approvalRequired != null && approvalRequired) {
|
||||||
|
approvalRequiredUsers = users;
|
||||||
|
final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance(
|
||||||
|
APPROVAL_REQUIRED_REQUEST_CODE,
|
||||||
|
R.string.admin_approval_required,
|
||||||
|
R.string.admin_approval_required_description,
|
||||||
|
R.string.ok,
|
||||||
|
R.string.cancel,
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final LiveData<Resource<Object>> detailsChangeResourceLiveData = viewModel.addMembers(users);
|
||||||
|
observeDetailsChange(detailsChangeResourceLiveData);
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private User getUser(@NonNull final RankedRecipient recipient) {
|
private User getUser(@NonNull final RankedRecipient recipient) {
|
||||||
User user = null;
|
User user = null;
|
||||||
@ -279,16 +310,28 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle());
|
binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle());
|
||||||
binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.mute() : viewModel.unmute();
|
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.mute() : viewModel.unmute();
|
||||||
handleMuteChangeResource(resourceLiveData, buttonView);
|
handleSwitchChangeResource(resourceLiveData, buttonView);
|
||||||
});
|
});
|
||||||
binding.muteMentionsLabel.setOnClickListener(v -> binding.muteMentions.toggle());
|
binding.muteMentionsLabel.setOnClickListener(v -> binding.muteMentions.toggle());
|
||||||
binding.muteMentions.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
binding.muteMentions.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.muteMentions() : viewModel.unmuteMentions();
|
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.muteMentions() : viewModel.unmuteMentions();
|
||||||
handleMuteChangeResource(resourceLiveData, buttonView);
|
handleSwitchChangeResource(resourceLiveData, buttonView);
|
||||||
|
});
|
||||||
|
if (!viewModel.isViewerAdmin()) {
|
||||||
|
binding.pendingMembersGroup.setVisibility(View.GONE);
|
||||||
|
binding.approvalRequired.setVisibility(View.GONE);
|
||||||
|
binding.approvalRequiredLabel.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.approvalRequired.setVisibility(View.VISIBLE);
|
||||||
|
binding.approvalRequiredLabel.setVisibility(View.VISIBLE);
|
||||||
|
binding.approvalRequiredLabel.setOnClickListener(v -> binding.approvalRequired.toggle());
|
||||||
|
binding.approvalRequired.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMuteChangeResource(final LiveData<Resource<Object>> resourceLiveData, final CompoundButton buttonView) {
|
private void handleSwitchChangeResource(final LiveData<Resource<Object>> resourceLiveData, final CompoundButton buttonView) {
|
||||||
resourceLiveData.observe(getViewLifecycleOwner(), resource -> {
|
resourceLiveData.observe(getViewLifecycleOwner(), resource -> {
|
||||||
if (resource == null) return;
|
if (resource == null) return;
|
||||||
switch (resource.status) {
|
switch (resource.status) {
|
||||||
@ -297,6 +340,7 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
break;
|
break;
|
||||||
case ERROR:
|
case ERROR:
|
||||||
buttonView.setEnabled(true);
|
buttonView.setEnabled(true);
|
||||||
|
buttonView.setChecked(!buttonView.isChecked());
|
||||||
if (resource.message != null) {
|
if (resource.message != null) {
|
||||||
Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
|
Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
@ -316,7 +360,10 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
usersAdapter = new DirectUsersAdapter(
|
usersAdapter = new DirectUsersAdapter(
|
||||||
inviter != null ? inviter.getPk() : -1,
|
inviter != null ? inviter.getPk() : -1,
|
||||||
(position, user, selected) -> {
|
(position, user, selected) -> {
|
||||||
// navigate to profile
|
final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections
|
||||||
|
.actionGlobalProfileFragment()
|
||||||
|
.setUsername("@" + user.getUsername());
|
||||||
|
NavHostFragment.findNavController(this).navigate(directions);
|
||||||
},
|
},
|
||||||
(position, user) -> {
|
(position, user) -> {
|
||||||
final ArrayList<Option<String>> options = viewModel.createUserOptions(user);
|
final ArrayList<Option<String>> options = viewModel.createUserOptions(user);
|
||||||
@ -340,6 +387,45 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
binding.users.setAdapter(usersAdapter);
|
binding.users.setAdapter(usersAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setPendingRequests(final DirectThreadParticipantRequestsResponse requests) {
|
||||||
|
if (requests == null || requests.getUsers() == null || requests.getUsers().isEmpty()) {
|
||||||
|
binding.pendingMembersGroup.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isPendingRequestsSetupDone) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
binding.pendingMembers.setLayoutManager(new LinearLayoutManager(context));
|
||||||
|
pendingUsersAdapter = new DirectPendingUsersAdapter(new PendingUserCallback() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final int position, final PendingUser pendingUser) {
|
||||||
|
final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections
|
||||||
|
.actionGlobalProfileFragment()
|
||||||
|
.setUsername("@" + pendingUser.getUser().getUsername());
|
||||||
|
NavHostFragment.findNavController(DirectMessageSettingsFragment.this).navigate(directions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApprove(final int position, final PendingUser pendingUser) {
|
||||||
|
final LiveData<Resource<Object>> resourceLiveData = viewModel.approveUsers(Collections.singletonList(pendingUser.getUser()));
|
||||||
|
observeApprovalChange(resourceLiveData, position, pendingUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeny(final int position, final PendingUser pendingUser) {
|
||||||
|
final LiveData<Resource<Object>> resourceLiveData = viewModel.denyUsers(Collections.singletonList(pendingUser.getUser()));
|
||||||
|
observeApprovalChange(resourceLiveData, position, pendingUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
binding.pendingMembers.setAdapter(pendingUsersAdapter);
|
||||||
|
binding.pendingMembersGroup.setVisibility(View.VISIBLE);
|
||||||
|
isPendingRequestsSetupDone = true;
|
||||||
|
}
|
||||||
|
if (pendingUsersAdapter != null) {
|
||||||
|
pendingUsersAdapter.submitPendingRequests(requests);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void observeDetailsChange(@NonNull final LiveData<Resource<Object>> detailsChangeResourceLiveData) {
|
private void observeDetailsChange(@NonNull final LiveData<Resource<Object>> detailsChangeResourceLiveData) {
|
||||||
detailsChangeResourceLiveData.observe(getViewLifecycleOwner(), resource -> {
|
detailsChangeResourceLiveData.observe(getViewLifecycleOwner(), resource -> {
|
||||||
if (resource == null) return;
|
if (resource == null) return;
|
||||||
@ -356,6 +442,48 @@ public class DirectMessageSettingsFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void observeApprovalChange(@NonNull final LiveData<Resource<Object>> detailsChangeResourceLiveData,
|
||||||
|
final int position,
|
||||||
|
@NonNull final PendingUser pendingUser) {
|
||||||
|
detailsChangeResourceLiveData.observe(getViewLifecycleOwner(), resource -> {
|
||||||
|
if (resource == null) return;
|
||||||
|
switch (resource.status) {
|
||||||
|
case SUCCESS:
|
||||||
|
// pending user will be removed from the list, so no need to set the progress to false
|
||||||
|
// pendingUser.setInProgress(false);
|
||||||
|
break;
|
||||||
|
case LOADING:
|
||||||
|
pendingUser.setInProgress(true);
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
pendingUser.setInProgress(false);
|
||||||
|
if (resource.message != null) {
|
||||||
|
Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pendingUsersAdapter.notifyItemChanged(position);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPositiveButtonClicked(final int requestCode) {
|
||||||
|
if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE && approvalRequiredUsers != null) {
|
||||||
|
final LiveData<Resource<Object>> detailsChangeResourceLiveData = viewModel.addMembers(approvalRequiredUsers);
|
||||||
|
observeDetailsChange(detailsChangeResourceLiveData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNegativeButtonClicked(final int requestCode) {
|
||||||
|
if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE) {
|
||||||
|
approvalRequiredUsers = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNeutralButtonClicked(final int requestCode) {}
|
||||||
|
|
||||||
// class ChangeSettings extends AsyncTask<String, Void, Void> {
|
// class ChangeSettings extends AsyncTask<String, Void, Void> {
|
||||||
// String action, argument;
|
// String action, argument;
|
||||||
// boolean ok = false;
|
// boolean ok = false;
|
||||||
|
@ -4,6 +4,7 @@ import android.animation.Animator;
|
|||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -49,6 +50,8 @@ import androidx.transition.TransitionManager;
|
|||||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
|
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
|
||||||
|
|
||||||
|
import com.google.android.material.badge.BadgeDrawable;
|
||||||
|
import com.google.android.material.badge.BadgeUtils;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
@ -58,6 +61,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import awais.instagrabber.ProfileNavGraphDirections;
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.UserSearchNavGraphDirections;
|
import awais.instagrabber.UserSearchNavGraphDirections;
|
||||||
import awais.instagrabber.activities.CameraActivity;
|
import awais.instagrabber.activities.CameraActivity;
|
||||||
@ -140,6 +144,8 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
private DirectItem itemToForward;
|
private DirectItem itemToForward;
|
||||||
private MutableLiveData<Object> backStackSavedStateResultLiveData;
|
private MutableLiveData<Object> backStackSavedStateResultLiveData;
|
||||||
private int prevLength;
|
private int prevLength;
|
||||||
|
private BadgeDrawable pendingRequestCountBadgeDrawable;
|
||||||
|
private boolean isPendingRequestCountBadgeAttached = false;
|
||||||
|
|
||||||
private final AppExecutors appExecutors = AppExecutors.getInstance();
|
private final AppExecutors appExecutors = AppExecutors.getInstance();
|
||||||
private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
|
private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
|
||||||
@ -413,6 +419,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
}
|
}
|
||||||
binding.send.stopScale();
|
binding.send.stopScale();
|
||||||
setupBackStackResultObserver();
|
setupBackStackResultObserver();
|
||||||
|
attachPendingRequestsBadge(viewModel.getPendingRequestsCount().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -421,6 +428,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeExperimentalUsageError")
|
||||||
private void cleanup() {
|
private void cleanup() {
|
||||||
if (prevTitleRunnable != null) {
|
if (prevTitleRunnable != null) {
|
||||||
appExecutors.mainThread().cancel(prevTitleRunnable);
|
appExecutors.mainThread().cancel(prevTitleRunnable);
|
||||||
@ -439,6 +447,11 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
((DirectItemViewHolder) holder).cleanup();
|
((DirectItemViewHolder) holder).cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isPendingRequestCountBadgeAttached = false;
|
||||||
|
if (pendingRequestCountBadgeDrawable != null) {
|
||||||
|
BadgeUtils.detachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);
|
||||||
|
pendingRequestCountBadgeDrawable = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
@ -705,6 +718,28 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
}
|
}
|
||||||
prevLength = length;
|
prevLength = length;
|
||||||
});
|
});
|
||||||
|
viewModel.getPendingRequestsCount().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeExperimentalUsageError")
|
||||||
|
private void attachPendingRequestsBadge(@Nullable final Integer count) {
|
||||||
|
if (pendingRequestCountBadgeDrawable == null) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
pendingRequestCountBadgeDrawable = BadgeDrawable.create(context);
|
||||||
|
}
|
||||||
|
if (count == null || count == 0) {
|
||||||
|
BadgeUtils.detachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);
|
||||||
|
isPendingRequestCountBadgeAttached = false;
|
||||||
|
pendingRequestCountBadgeDrawable.setNumber(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pendingRequestCountBadgeDrawable.getNumber() == count) return;
|
||||||
|
pendingRequestCountBadgeDrawable.setNumber(count);
|
||||||
|
if (!isPendingRequestCountBadgeAttached) {
|
||||||
|
BadgeUtils.attachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);
|
||||||
|
isPendingRequestCountBadgeAttached = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showExtraInputOption(final boolean show) {
|
private void showExtraInputOption(final boolean show) {
|
||||||
@ -1396,9 +1431,10 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToUser(@NonNull final String username) {
|
private void navigateToUser(@NonNull final String username) {
|
||||||
final Bundle bundle = new Bundle();
|
final ProfileNavGraphDirections.ActionGlobalProfileFragment direction = ProfileNavGraphDirections
|
||||||
bundle.putString("username", "@" + username);
|
.actionGlobalProfileFragment()
|
||||||
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(R.id.action_global_profileFragment, bundle);
|
.setUsername("@" + username);
|
||||||
|
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ItemsAdapterDataMerger extends MediatorLiveData<Pair<User, DirectThread>> {
|
public static class ItemsAdapterDataMerger extends MediatorLiveData<Pair<User, DirectThread>> {
|
||||||
|
@ -8,6 +8,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
|||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
|
||||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
|
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.http.FieldMap;
|
import retrofit2.http.FieldMap;
|
||||||
@ -15,6 +16,7 @@ import retrofit2.http.FormUrlEncoded;
|
|||||||
import retrofit2.http.GET;
|
import retrofit2.http.GET;
|
||||||
import retrofit2.http.POST;
|
import retrofit2.http.POST;
|
||||||
import retrofit2.http.Path;
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
import retrofit2.http.QueryMap;
|
import retrofit2.http.QueryMap;
|
||||||
|
|
||||||
public interface DirectMessagesRepository {
|
public interface DirectMessagesRepository {
|
||||||
@ -95,4 +97,19 @@ public interface DirectMessagesRepository {
|
|||||||
@POST("/api/v1/direct_v2/threads/{threadId}/unmute_mentions/")
|
@POST("/api/v1/direct_v2/threads/{threadId}/unmute_mentions/")
|
||||||
Call<String> unmuteMentions(@Path("threadId") String threadId,
|
Call<String> unmuteMentions(@Path("threadId") String threadId,
|
||||||
@FieldMap final Map<String, String> form);
|
@FieldMap final Map<String, String> form);
|
||||||
|
|
||||||
|
@GET("/api/v1/direct_v2/threads/{threadId}/participant_requests/")
|
||||||
|
Call<DirectThreadParticipantRequestsResponse> participantRequests(@Path("threadId") String threadId,
|
||||||
|
@Query("page_size") int pageSize,
|
||||||
|
@Query("cursor") String cursor);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/api/v1/direct_v2/threads/{threadId}/approve_participant_requests/")
|
||||||
|
Call<DirectThreadDetailsChangeResponse> approveParticipantRequests(@Path("threadId") String threadId,
|
||||||
|
@FieldMap final Map<String, String> form);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/api/v1/direct_v2/threads/{threadId}/deny_participant_requests/")
|
||||||
|
Call<DirectThreadDetailsChangeResponse> declineParticipantRequests(@Path("threadId") String threadId,
|
||||||
|
@FieldMap final Map<String, String> form);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ public class DirectThread implements Serializable {
|
|||||||
private final boolean isSpam;
|
private final boolean isSpam;
|
||||||
private final DirectItem lastPermanentItem;
|
private final DirectItem lastPermanentItem;
|
||||||
private final DirectThreadDirectStory directStory;
|
private final DirectThreadDirectStory directStory;
|
||||||
|
private final boolean approvalRequiredForNewMembers;
|
||||||
|
|
||||||
public DirectThread(final String threadId,
|
public DirectThread(final String threadId,
|
||||||
final String threadV2Id,
|
final String threadV2Id,
|
||||||
@ -72,7 +73,8 @@ public class DirectThread implements Serializable {
|
|||||||
final String oldestCursor,
|
final String oldestCursor,
|
||||||
final boolean isSpam,
|
final boolean isSpam,
|
||||||
final DirectItem lastPermanentItem,
|
final DirectItem lastPermanentItem,
|
||||||
final DirectThreadDirectStory directStory) {
|
final DirectThreadDirectStory directStory,
|
||||||
|
final boolean approvalRequiredForNewMembers) {
|
||||||
this.threadId = threadId;
|
this.threadId = threadId;
|
||||||
this.threadV2Id = threadV2Id;
|
this.threadV2Id = threadV2Id;
|
||||||
this.users = users;
|
this.users = users;
|
||||||
@ -104,6 +106,7 @@ public class DirectThread implements Serializable {
|
|||||||
this.isSpam = isSpam;
|
this.isSpam = isSpam;
|
||||||
this.lastPermanentItem = lastPermanentItem;
|
this.lastPermanentItem = lastPermanentItem;
|
||||||
this.directStory = directStory;
|
this.directStory = directStory;
|
||||||
|
this.approvalRequiredForNewMembers = approvalRequiredForNewMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getThreadId() {
|
public String getThreadId() {
|
||||||
@ -238,6 +241,10 @@ public class DirectThread implements Serializable {
|
|||||||
return directStory;
|
return directStory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isApprovalRequiredForNewMembers() {
|
||||||
|
return approvalRequiredForNewMembers;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public DirectItem getFirstDirectItem() {
|
public DirectItem getFirstDirectItem() {
|
||||||
DirectItem firstItem = null;
|
DirectItem firstItem = null;
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
package awais.instagrabber.repositories.responses.directmessages;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import awais.instagrabber.repositories.responses.User;
|
||||||
|
|
||||||
|
public class DirectThreadParticipantRequestsResponse implements Serializable, Cloneable {
|
||||||
|
private List<User> users;
|
||||||
|
private final Map<Long, String> requesterUsernames;
|
||||||
|
private final String cursor;
|
||||||
|
private final int totalThreadParticipants;
|
||||||
|
private final int totalParticipantRequests;
|
||||||
|
private final String status;
|
||||||
|
|
||||||
|
public DirectThreadParticipantRequestsResponse(final List<User> users,
|
||||||
|
final Map<Long, String> requesterUsernames,
|
||||||
|
final String cursor,
|
||||||
|
final int totalThreadParticipants,
|
||||||
|
final int totalParticipantRequests,
|
||||||
|
final String status) {
|
||||||
|
this.users = users;
|
||||||
|
this.requesterUsernames = requesterUsernames;
|
||||||
|
this.cursor = cursor;
|
||||||
|
this.totalThreadParticipants = totalThreadParticipants;
|
||||||
|
this.totalParticipantRequests = totalParticipantRequests;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> getUsers() {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsers(final List<User> users) {
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Long, String> getRequesterUsernames() {
|
||||||
|
return requesterUsernames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCursor() {
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalThreadParticipants() {
|
||||||
|
return totalThreadParticipants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalParticipantRequests() {
|
||||||
|
return totalParticipantRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Object clone() throws CloneNotSupportedException {
|
||||||
|
return super.clone();
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import android.content.res.Resources;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
@ -20,6 +21,7 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ import awais.instagrabber.repositories.responses.FriendshipRestrictResponse;
|
|||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
|
||||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
@ -61,6 +64,8 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
|||||||
private final MutableLiveData<List<Long>> adminUserIds = new MutableLiveData<>(Collections.emptyList());
|
private final MutableLiveData<List<Long>> adminUserIds = new MutableLiveData<>(Collections.emptyList());
|
||||||
private final MutableLiveData<Boolean> muted = new MutableLiveData<>(false);
|
private final MutableLiveData<Boolean> muted = new MutableLiveData<>(false);
|
||||||
private final MutableLiveData<Boolean> mentionsMuted = new MutableLiveData<>(false);
|
private final MutableLiveData<Boolean> mentionsMuted = new MutableLiveData<>(false);
|
||||||
|
private final MutableLiveData<Boolean> approvalRequiredToJoin = new MutableLiveData<>(false);
|
||||||
|
private final MutableLiveData<DirectThreadParticipantRequestsResponse> pendingRequests = new MutableLiveData<>(null);
|
||||||
private final DirectMessagesService directMessagesService;
|
private final DirectMessagesService directMessagesService;
|
||||||
private final long userId;
|
private final long userId;
|
||||||
private final Resources resources;
|
private final Resources resources;
|
||||||
@ -107,6 +112,10 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
|||||||
viewerIsAdmin = adminUserIds.contains(userId);
|
viewerIsAdmin = adminUserIds.contains(userId);
|
||||||
muted.postValue(thread.isMuted());
|
muted.postValue(thread.isMuted());
|
||||||
mentionsMuted.postValue(thread.isMentionsMuted());
|
mentionsMuted.postValue(thread.isMentionsMuted());
|
||||||
|
approvalRequiredToJoin.postValue(thread.isApprovalRequiredForNewMembers());
|
||||||
|
if (thread.isGroup() && viewerIsAdmin) {
|
||||||
|
fetchPendingRequests();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroup() {
|
public boolean isGroup() {
|
||||||
@ -140,6 +149,18 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
|||||||
return muted;
|
return muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<Boolean> getApprovalRequiredToJoin() {
|
||||||
|
return approvalRequiredToJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<DirectThreadParticipantRequestsResponse> getPendingRequests() {
|
||||||
|
return pendingRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isViewerAdmin() {
|
||||||
|
return viewerIsAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
public LiveData<Resource<Object>> updateTitle(final String newTitle) {
|
public LiveData<Resource<Object>> updateTitle(final String newTitle) {
|
||||||
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
||||||
final Call<DirectThreadDetailsChangeResponse> addUsersRequest = directMessagesService
|
final Call<DirectThreadDetailsChangeResponse> addUsersRequest = directMessagesService
|
||||||
@ -454,9 +475,52 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<Resource<Object>> approveUsers(final List<User> users) {
|
||||||
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
||||||
|
data.postValue(Resource.loading(null));
|
||||||
|
final Call<DirectThreadDetailsChangeResponse> approveUsersRequest = directMessagesService
|
||||||
|
.approveParticipantRequests(thread.getThreadId(), users.stream().map(User::getPk).collect(Collectors.toList()));
|
||||||
|
handleDetailsChangeRequest(data, approveUsersRequest);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Resource<Object>> denyUsers(final List<User> users) {
|
||||||
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
||||||
|
data.postValue(Resource.loading(null));
|
||||||
|
final Call<DirectThreadDetailsChangeResponse> approveUsersRequest = directMessagesService
|
||||||
|
.declineParticipantRequests(thread.getThreadId(), users.stream().map(User::getPk).collect(Collectors.toList()));
|
||||||
|
handleDetailsChangeRequest(data, approveUsersRequest, () -> {
|
||||||
|
final DirectThreadParticipantRequestsResponse pendingRequestsValue = pendingRequests.getValue();
|
||||||
|
if (pendingRequestsValue == null) return;
|
||||||
|
final List<User> pendingUsers = pendingRequestsValue.getUsers();
|
||||||
|
if (pendingUsers == null || pendingUsers.isEmpty()) return;
|
||||||
|
final List<User> filtered = pendingUsers.stream()
|
||||||
|
.filter(o -> !users.contains(o))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
try {
|
||||||
|
final DirectThreadParticipantRequestsResponse clone = (DirectThreadParticipantRequestsResponse) pendingRequestsValue.clone();
|
||||||
|
clone.setUsers(filtered);
|
||||||
|
pendingRequests.postValue(clone);
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
Log.e(TAG, "denyUsers: ", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface OnSuccessAction {
|
||||||
|
void onSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
|
private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
|
||||||
final Call<DirectThreadDetailsChangeResponse> addUsersRequest) {
|
final Call<DirectThreadDetailsChangeResponse> request) {
|
||||||
addUsersRequest.enqueue(new Callback<DirectThreadDetailsChangeResponse>() {
|
handleDetailsChangeRequest(data, request, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
|
||||||
|
final Call<DirectThreadDetailsChangeResponse> request,
|
||||||
|
@Nullable final OnSuccessAction action) {
|
||||||
|
request.enqueue(new Callback<DirectThreadDetailsChangeResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull final Call<DirectThreadDetailsChangeResponse> call,
|
public void onResponse(@NonNull final Call<DirectThreadDetailsChangeResponse> call,
|
||||||
@NonNull final Response<DirectThreadDetailsChangeResponse> response) {
|
@NonNull final Response<DirectThreadDetailsChangeResponse> response) {
|
||||||
@ -464,16 +528,19 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
|||||||
handleErrorResponse(response, data);
|
handleErrorResponse(response, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final DirectThreadDetailsChangeResponse addUserResponse = response.body();
|
final DirectThreadDetailsChangeResponse changeResponse = response.body();
|
||||||
if (addUserResponse == null) {
|
if (changeResponse == null) {
|
||||||
data.postValue(Resource.error("Response is null", null));
|
data.postValue(Resource.error("Response is null", null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.postValue(Resource.success(new Object()));
|
data.postValue(Resource.success(new Object()));
|
||||||
final DirectThread thread = addUserResponse.getThread();
|
final DirectThread thread = changeResponse.getThread();
|
||||||
if (thread != null) {
|
if (thread != null) {
|
||||||
setThread(thread);
|
setThread(thread);
|
||||||
}
|
}
|
||||||
|
if (action != null) {
|
||||||
|
action.onSuccess();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -578,4 +645,44 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
|||||||
public void setViewer(final User viewer) {
|
public void setViewer(final User viewer) {
|
||||||
this.viewer = viewer;
|
this.viewer = viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fetchPendingRequests() {
|
||||||
|
final Call<DirectThreadParticipantRequestsResponse> request = directMessagesService.participantRequests(thread.getThreadId(), 5, null);
|
||||||
|
request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull final Call<DirectThreadParticipantRequestsResponse> call,
|
||||||
|
@NonNull final Response<DirectThreadParticipantRequestsResponse> response) {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
if (response.errorBody() != null) {
|
||||||
|
try {
|
||||||
|
final String string = response.errorBody().string();
|
||||||
|
final String msg = String.format(Locale.US,
|
||||||
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
||||||
|
call.request().url().toString(),
|
||||||
|
response.code(),
|
||||||
|
string);
|
||||||
|
Log.e(TAG, msg);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "onResponse: ", e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.e(TAG, "onResponse: request was not successful and response error body was null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final DirectThreadParticipantRequestsResponse body = response.body();
|
||||||
|
if (body == null) {
|
||||||
|
Log.e(TAG, "onResponse: response body was null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingRequests.postValue(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Call<DirectThreadParticipantRequestsResponse> call, @NonNull final Throwable t) {
|
||||||
|
Log.e(TAG, "onFailure: ", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroa
|
|||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponseMessageMetadata;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponseMessageMetadata;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponsePayload;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponsePayload;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
|
||||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
||||||
import awais.instagrabber.utils.BitmapUtils;
|
import awais.instagrabber.utils.BitmapUtils;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
@ -82,6 +83,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
private final MutableLiveData<List<User>> users = new MutableLiveData<>(new ArrayList<>());
|
private final MutableLiveData<List<User>> users = new MutableLiveData<>(new ArrayList<>());
|
||||||
private final MutableLiveData<List<User>> leftUsers = new MutableLiveData<>(new ArrayList<>());
|
private final MutableLiveData<List<User>> leftUsers = new MutableLiveData<>(new ArrayList<>());
|
||||||
private final MutableLiveData<DirectItem> replyToItem = new MutableLiveData<>();
|
private final MutableLiveData<DirectItem> replyToItem = new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Integer> pendingRequestsCount = new MutableLiveData<>(null);
|
||||||
|
|
||||||
private final DirectMessagesService service;
|
private final DirectMessagesService service;
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
@ -89,6 +91,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
private final String csrfToken;
|
private final String csrfToken;
|
||||||
private final File recordingsDir;
|
private final File recordingsDir;
|
||||||
private final Application application;
|
private final Application application;
|
||||||
|
private final long viewerId;
|
||||||
|
|
||||||
private String cursor;
|
private String cursor;
|
||||||
private String threadId;
|
private String threadId;
|
||||||
@ -97,7 +100,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
private User currentUser;
|
private User currentUser;
|
||||||
private Call<DirectThreadFeedResponse> chatsRequest;
|
private Call<DirectThreadFeedResponse> chatsRequest;
|
||||||
private VoiceRecorder voiceRecorder;
|
private VoiceRecorder voiceRecorder;
|
||||||
private final long viewerId;
|
private boolean viewerIsAdmin;
|
||||||
|
|
||||||
public DirectThreadViewModel(@NonNull final Application application) {
|
public DirectThreadViewModel(@NonNull final Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
@ -328,6 +331,10 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
return replyToItem;
|
return replyToItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<Integer> getPendingRequestsCount() {
|
||||||
|
return pendingRequestsCount;
|
||||||
|
}
|
||||||
|
|
||||||
public void fetchChats() {
|
public void fetchChats() {
|
||||||
final Boolean isFetching = fetching.getValue();
|
final Boolean isFetching = fetching.getValue();
|
||||||
if ((isFetching != null && isFetching) || !hasOlder) return;
|
if ((isFetching != null && isFetching) || !hasOlder) return;
|
||||||
@ -391,6 +398,11 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
users.postValue(thread.getUsers());
|
users.postValue(thread.getUsers());
|
||||||
leftUsers.postValue(thread.getLeftUsers());
|
leftUsers.postValue(thread.getLeftUsers());
|
||||||
fetching.postValue(false);
|
fetching.postValue(false);
|
||||||
|
final List<Long> adminUserIds = thread.getAdminUserIds();
|
||||||
|
viewerIsAdmin = adminUserIds.contains(viewerId);
|
||||||
|
if (thread.isGroup() && viewerIsAdmin) {
|
||||||
|
fetchPendingRequests();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Resource<DirectItem>> sendText(final String text) {
|
public LiveData<Resource<DirectItem>> sendText(final String text) {
|
||||||
@ -934,7 +946,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
@NonNull final MutableLiveData<Resource<DirectItem>> data,
|
@NonNull final MutableLiveData<Resource<DirectItem>> data,
|
||||||
@NonNull final DirectItem directItem) {
|
@NonNull final DirectItem directItem) {
|
||||||
try {
|
try {
|
||||||
final String string = response.errorBody().string();
|
final String string = response.errorBody() != null ? response.errorBody().string() : "";
|
||||||
final String msg = String.format(Locale.US,
|
final String msg = String.format(Locale.US,
|
||||||
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
||||||
call.request().url().toString(),
|
call.request().url().toString(),
|
||||||
@ -1047,4 +1059,44 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
// Log.d(TAG, "setReplyToItem: " + item);
|
// Log.d(TAG, "setReplyToItem: " + item);
|
||||||
replyToItem.postValue(item);
|
replyToItem.postValue(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fetchPendingRequests() {
|
||||||
|
final Call<DirectThreadParticipantRequestsResponse> request = service.participantRequests(threadId, 1, null);
|
||||||
|
request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull final Call<DirectThreadParticipantRequestsResponse> call,
|
||||||
|
@NonNull final Response<DirectThreadParticipantRequestsResponse> response) {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
if (response.errorBody() != null) {
|
||||||
|
try {
|
||||||
|
final String string = response.errorBody().string();
|
||||||
|
final String msg = String.format(Locale.US,
|
||||||
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
||||||
|
call.request().url().toString(),
|
||||||
|
response.code(),
|
||||||
|
string);
|
||||||
|
Log.e(TAG, msg);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "onResponse: ", e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.e(TAG, "onResponse: request was not successful and response error body was null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final DirectThreadParticipantRequestsResponse body = response.body();
|
||||||
|
if (body == null) {
|
||||||
|
Log.e(TAG, "onResponse: response body was null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingRequestsCount.postValue(body.getTotalParticipantRequests());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Call<DirectThreadParticipantRequestsResponse> call, @NonNull final Throwable t) {
|
||||||
|
Log.e(TAG, "onFailure: ", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
|||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
|
||||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
|
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
@ -349,4 +350,31 @@ public class DirectMessagesService extends BaseService {
|
|||||||
);
|
);
|
||||||
return repository.unmuteMentions(threadId, form);
|
return repository.unmuteMentions(threadId, form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Call<DirectThreadParticipantRequestsResponse> participantRequests(@NonNull final String threadId,
|
||||||
|
final int pageSize,
|
||||||
|
@Nullable final String cursor) {
|
||||||
|
return repository.participantRequests(threadId, pageSize, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Call<DirectThreadDetailsChangeResponse> approveParticipantRequests(@NonNull final String threadId,
|
||||||
|
@NonNull final List<Long> userIds) {
|
||||||
|
final ImmutableMap<String, String> form = ImmutableMap.of(
|
||||||
|
"_csrftoken", csrfToken,
|
||||||
|
"_uuid", deviceUuid,
|
||||||
|
"user_ids", new JSONArray(userIds).toString()
|
||||||
|
// , "share_join_chat_story", String.valueOf(true)
|
||||||
|
);
|
||||||
|
return repository.approveParticipantRequests(threadId, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Call<DirectThreadDetailsChangeResponse> declineParticipantRequests(@NonNull final String threadId,
|
||||||
|
@NonNull final List<Long> userIds) {
|
||||||
|
final ImmutableMap<String, String> form = ImmutableMap.of(
|
||||||
|
"_csrftoken", csrfToken,
|
||||||
|
"_uuid", deviceUuid,
|
||||||
|
"user_ids", new JSONArray(userIds).toString()
|
||||||
|
);
|
||||||
|
return repository.declineParticipantRequests(threadId, form);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,10 +101,37 @@
|
|||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:paddingStart="0dp"
|
android:paddingStart="0dp"
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
app:layout_constraintBottom_toTopOf="@id/leave"
|
app:layout_constraintBottom_toTopOf="@id/approval_required"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/mute_messages" />
|
app:layout_constraintTop_toBottomOf="@id/mute_messages" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/approval_required_label"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:text="@string/approval_required_for_new_members"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/approval_required"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/approval_required" />
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/approval_required"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/leave"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/mute_mentions" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/leave"
|
android:id="@+id/leave"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -119,8 +146,44 @@
|
|||||||
android:text="@string/dms_action_leave"
|
android:text="@string/dms_action_leave"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
android:textColor="@color/red_600"
|
android:textColor="@color/red_600"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/pending_members_header"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/approval_required" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/pending_members_header"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:text="@string/requests"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||||
|
android:textColor="?colorAccent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/pending_members"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/leave" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/pending_members_admin_only"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:text="@string/admins_only"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
|
||||||
|
app:layout_constraintBaseline_toBaselineOf="@id/pending_members_header"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/pending_members"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toTopOf="@id/add_members"
|
app:layout_constraintBottom_toTopOf="@id/add_members"
|
||||||
app:layout_constraintTop_toBottomOf="@id/mute_mentions" />
|
app:layout_constraintTop_toBottomOf="@id/pending_members_header"
|
||||||
|
tools:itemCount="2"
|
||||||
|
tools:listitem="@layout/layout_dm_user_item" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/add_members"
|
android:id="@+id/add_members"
|
||||||
@ -139,13 +202,21 @@
|
|||||||
app:drawableStartCompat="@drawable/ic_add"
|
app:drawableStartCompat="@drawable/ic_add"
|
||||||
app:drawableTint="?android:textColorPrimary"
|
app:drawableTint="?android:textColorPrimary"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/leave" />
|
app:layout_constraintTop_toBottomOf="@id/pending_members" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/pending_members_group"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="pending_members,pending_members_admin_only,pending_members_header"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
<androidx.constraintlayout.widget.Group
|
||||||
android:id="@+id/group_settings"
|
android:id="@+id/group_settings"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:constraint_referenced_ids="title_edit_input_layout, mute_mentions_label, mute_mentions, leave, add_members" />
|
app:constraint_referenced_ids="title_edit_input_layout, mute_mentions_label, mute_mentions, leave, add_members, approval_required, approval_required_label" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
@ -155,5 +226,6 @@
|
|||||||
android:id="@+id/users"
|
android:id="@+id/users"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:listitem="@layout/layout_dm_user_item" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
118
app/src/main/res/layout/layout_dm_pending_user_item.xml
Normal file
118
app/src/main/res/layout/layout_dm_pending_user_item.xml
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="72dp"
|
||||||
|
android:background="?android:selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp">
|
||||||
|
|
||||||
|
<com.facebook.drawee.view.SimpleDraweeView
|
||||||
|
android:id="@+id/profile_pic"
|
||||||
|
android:layout_width="@dimen/dm_inbox_avatar_size"
|
||||||
|
android:layout_height="@dimen/dm_inbox_avatar_size"
|
||||||
|
app:actualImageScaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/username"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:roundAsCircle="true"
|
||||||
|
tools:background="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/username"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/requester"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/controls_barrier"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/profile_pic"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="username......................." />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/requester"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/username"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/username"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/username"
|
||||||
|
tools:text="Added by someone" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/controls_barrier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierAllowsGoneWidgets="true"
|
||||||
|
app:barrierDirection="start"
|
||||||
|
app:constraint_referenced_ids="progress,approve,deny" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/approve"
|
||||||
|
android:layout_width="42dp"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/deny"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/controls_barrier"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_check_24"
|
||||||
|
app:tint="@color/green_500"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/deny"
|
||||||
|
android:layout_width="42dp"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/profile_pic"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/approve"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/profile_pic"
|
||||||
|
app:srcCompat="@drawable/ic_close_24"
|
||||||
|
app:tint="@color/red_500"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:indicatorColor="?colorSurface"
|
||||||
|
app:indicatorSize="30dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/profile_pic"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/controls_barrier"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/profile_pic"
|
||||||
|
app:trackColor="@color/blue_900"
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/item_pref_divider"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/thread_title" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -9,12 +9,7 @@
|
|||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_profileFragment"
|
android:id="@+id/action_global_profileFragment"
|
||||||
app:destination="@id/profile_nav_graph">
|
app:destination="@id/profile_nav_graph" />
|
||||||
<argument
|
|
||||||
android:name="username"
|
|
||||||
app:argType="string"
|
|
||||||
app:nullable="true" />
|
|
||||||
</action>
|
|
||||||
|
|
||||||
<include app:graph="@navigation/location_nav_graph" />
|
<include app:graph="@navigation/location_nav_graph" />
|
||||||
|
|
||||||
|
@ -407,4 +407,10 @@
|
|||||||
<string name="video">Video</string>
|
<string name="video">Video</string>
|
||||||
<string name="voice_message">Voice message</string>
|
<string name="voice_message">Voice message</string>
|
||||||
<string name="post">Post</string>
|
<string name="post">Post</string>
|
||||||
|
<string name="approval_required_for_new_members">Approval required to join</string>
|
||||||
|
<string name="requests">Requests</string>
|
||||||
|
<string name="admins_only">Admins only</string>
|
||||||
|
<string name="added_by">Added by %s</string>
|
||||||
|
<string name="admin_approval_required">Admin approval required</string>
|
||||||
|
<string name="admin_approval_required_description">An admin approval will be required to add new members to the group</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user