mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 14:47:29 +00:00
Add pending inbox
This commit is contained in:
parent
6aee7ea863
commit
5e3aed38b9
@ -1,11 +1,15 @@
|
||||
package awais.instagrabber.fragments.directmessages;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@ -17,11 +21,13 @@ import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelStoreOwner;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.badge.BadgeDrawable;
|
||||
import com.google.android.material.badge.BadgeUtils;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.List;
|
||||
@ -41,7 +47,6 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
private CoordinatorLayout root;
|
||||
private RecyclerLazyLoaderAtEdge lazyLoader;
|
||||
private DirectInboxViewModel viewModel;
|
||||
// private boolean refreshInbox = false;
|
||||
private boolean shouldRefresh = true;
|
||||
private FragmentDirectMessagesInboxBinding binding;
|
||||
private DMRefreshBroadcastReceiver receiver;
|
||||
@ -50,6 +55,9 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
private boolean scrollToTop = false;
|
||||
private boolean navigating;
|
||||
private Observer<List<DirectThread>> threadsObserver;
|
||||
private MenuItem pendingRequestsMenuItem;
|
||||
private BadgeDrawable pendingRequestTotalBadgeDrawable;
|
||||
private boolean isPendingRequestTotalBadgeAttached;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
@ -60,6 +68,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
|
||||
viewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
}
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,7 +108,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
observeViewModel();
|
||||
setupObservers();
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
receiver = new DMRefreshBroadcastReceiver(() -> {
|
||||
@ -109,10 +118,36 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
context.registerReceiver(receiver, new IntentFilter(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM));
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeExperimentalUsageError")
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
unregisterReceiver();
|
||||
isPendingRequestTotalBadgeAttached = false;
|
||||
if (pendingRequestTotalBadgeDrawable != null) {
|
||||
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
|
||||
pendingRequestTotalBadgeDrawable = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
pendingRequestsMenuItem = menu.add(Menu.NONE, R.id.pending_requests, Menu.NONE, "Pending requests");
|
||||
pendingRequestsMenuItem.setIcon(R.drawable.ic_account_clock_24)
|
||||
.setVisible(false)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
attachPendingRequestsBadge(viewModel.getPendingRequestsTotal().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
if (item.getItemId() == R.id.pending_requests) {
|
||||
final NavDirections directions = DirectMessageInboxFragmentDirections.actionInboxToPendingInbox();
|
||||
NavHostFragment.findNavController(this).navigate(directions);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void unregisterReceiver() {
|
||||
@ -136,7 +171,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
viewModel.onDestroy();
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
private void setupObservers() {
|
||||
threadsObserver = list -> {
|
||||
if (inboxAdapter == null) return;
|
||||
inboxAdapter.submitList(list, () -> {
|
||||
@ -148,6 +183,31 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
viewModel.getThreads().observe(fragmentActivity, threadsObserver);
|
||||
viewModel.getFetchingInbox().observe(getViewLifecycleOwner(), fetching -> binding.swipeRefreshLayout.setRefreshing(fetching));
|
||||
viewModel.getUnseenCount().observe(getViewLifecycleOwner(), this::setBottomNavBarBadge);
|
||||
viewModel.getPendingRequestsTotal().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeExperimentalUsageError")
|
||||
private void attachPendingRequestsBadge(@Nullable final Integer count) {
|
||||
if (pendingRequestsMenuItem == null) return;
|
||||
if (pendingRequestTotalBadgeDrawable == null) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
pendingRequestTotalBadgeDrawable = BadgeDrawable.create(context);
|
||||
}
|
||||
if (count == null || count == 0) {
|
||||
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
|
||||
isPendingRequestTotalBadgeAttached = false;
|
||||
pendingRequestTotalBadgeDrawable.setNumber(0);
|
||||
pendingRequestsMenuItem.setVisible(false);
|
||||
return;
|
||||
}
|
||||
pendingRequestsMenuItem.setVisible(true);
|
||||
if (pendingRequestTotalBadgeDrawable.getNumber() == count) return;
|
||||
pendingRequestTotalBadgeDrawable.setNumber(count);
|
||||
if (!isPendingRequestTotalBadgeAttached) {
|
||||
BadgeUtils.attachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
|
||||
isPendingRequestTotalBadgeAttached = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeViewModelObservers() {
|
||||
@ -161,7 +221,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
private void init() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
observeViewModel();
|
||||
setupObservers();
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
binding.inboxList.setHasFixedSize(true);
|
||||
binding.inboxList.setItemViewCacheSize(20);
|
||||
|
@ -56,6 +56,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
||||
import awais.instagrabber.viewmodels.DirectInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectPendingInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectSettingsViewModel;
|
||||
|
||||
public class DirectMessageSettingsFragment extends Fragment implements ConfirmDialogFragmentCallback {
|
||||
@ -70,22 +71,28 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
private boolean isPendingRequestsSetupDone = false;
|
||||
private DirectPendingUsersAdapter pendingUsersAdapter;
|
||||
private Set<User> approvalRequiredUsers;
|
||||
// private List<Option<String>> options;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) return;
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
|
||||
final DirectInboxViewModel inboxViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
final List<DirectThread> threads = inboxViewModel.getThreads().getValue();
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) {
|
||||
navController.navigateUp();
|
||||
return;
|
||||
final DirectMessageSettingsFragmentArgs args = DirectMessageSettingsFragmentArgs.fromBundle(arguments);
|
||||
final boolean pending = args.getPending();
|
||||
final List<DirectThread> threads;
|
||||
final User viewer;
|
||||
if (pending) {
|
||||
final DirectPendingInboxViewModel inboxViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectPendingInboxViewModel.class);
|
||||
threads = inboxViewModel.getThreads().getValue();
|
||||
viewer = inboxViewModel.getViewer();
|
||||
} else {
|
||||
final DirectInboxViewModel inboxViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
threads = inboxViewModel.getThreads().getValue();
|
||||
viewer = inboxViewModel.getViewer();
|
||||
}
|
||||
final DirectMessageSettingsFragmentArgs fragmentArgs = DirectMessageSettingsFragmentArgs.fromBundle(arguments);
|
||||
final String threadId = fragmentArgs.getThreadId();
|
||||
final String threadId = args.getThreadId();
|
||||
final Optional<DirectThread> first = threads != null ? threads.stream()
|
||||
.filter(thread -> thread.getThreadId().equals(threadId))
|
||||
.findFirst()
|
||||
@ -95,7 +102,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
return;
|
||||
}
|
||||
viewModel = new ViewModelProvider(this).get(DirectSettingsViewModel.class);
|
||||
viewModel.setViewer(inboxViewModel.getViewer());
|
||||
viewModel.setViewer(viewer);
|
||||
viewModel.setThread(first.get());
|
||||
}
|
||||
|
||||
@ -105,37 +112,14 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
binding = FragmentDirectMessagesSettingsBinding.inflate(inflater, container, false);
|
||||
// final String threadId = DirectMessageSettingsFragmentArgs.fromBundle(getArguments()).getThreadId();
|
||||
// threadTitle = DirectMessageSettingsFragmentArgs.fromBundle(getArguments()).getTitle();
|
||||
// binding.swipeRefreshLayout.setEnabled(false);
|
||||
|
||||
// final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
// if (actionBar != null) {
|
||||
// actionBar.setTitle(threadTitle);
|
||||
// }
|
||||
|
||||
// titleSend.setOnClickListener(v -> new ChangeSettings(titleText.getText().toString()).execute("update_title"));
|
||||
|
||||
// binding.titleText.addTextChangedListener(new TextWatcherAdapter() {
|
||||
// @Override
|
||||
// public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// binding.titleSend.setVisibility(s.toString().equals(threadTitle) ? View.GONE : View.VISIBLE);
|
||||
// }
|
||||
// });
|
||||
|
||||
// final AppCompatButton btnLeave = binding.btnLeave;
|
||||
// btnLeave.setOnClickListener(v -> new AlertDialog.Builder(context)
|
||||
// .setTitle(R.string.dms_action_leave_question)
|
||||
// .setPositiveButton(R.string.yes, (x, y) -> new ChangeSettings(titleText.getText().toString()).execute("leave"))
|
||||
// .setNegativeButton(R.string.no, null)
|
||||
// .show());
|
||||
|
||||
// currentlyRunning = new DirectMessageInboxThreadFetcher(threadId, null, null, fetchListener).execute();
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) return;
|
||||
init();
|
||||
setupObservers();
|
||||
}
|
||||
@ -169,6 +153,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
usersAdapter.setAdminUserIds(adminUserIds);
|
||||
});
|
||||
viewModel.getMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
|
||||
viewModel.isPending().observe(getViewLifecycleOwner(), pending -> binding.muteMessages.setVisibility(pending ? View.GONE : View.VISIBLE));
|
||||
if (viewModel.isViewerAdmin()) {
|
||||
viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required));
|
||||
viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests);
|
||||
|
@ -105,6 +105,7 @@ import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.AppStateViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectPendingInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectThreadViewModel;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
|
||||
@ -347,9 +348,11 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
final int itemId = item.getItemId();
|
||||
if (itemId == R.id.info) {
|
||||
final NavDirections action = DirectMessageThreadFragmentDirections
|
||||
.actionDMThreadFragmentToDMSettingsFragment(viewModel.getThreadId(), null);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
final DirectMessageThreadFragmentDirections.ActionThreadToSettings directions = DirectMessageThreadFragmentDirections
|
||||
.actionThreadToSettings(viewModel.getThreadId(), null);
|
||||
final Boolean pending = viewModel.isPending().getValue();
|
||||
directions.setPending(pending == null ? false : pending);
|
||||
NavHostFragment.findNavController(this).navigate(directions);
|
||||
return true;
|
||||
}
|
||||
if (itemId == R.id.mark_as_seen) {
|
||||
@ -470,10 +473,20 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
}
|
||||
|
||||
private void getInitialData() {
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) return;
|
||||
final DirectMessageThreadFragmentArgs args = DirectMessageThreadFragmentArgs.fromBundle(arguments);
|
||||
final boolean pending = args.getPending();
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
|
||||
final DirectInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
final List<DirectThread> threads = threadListViewModel.getThreads().getValue();
|
||||
final List<DirectThread> threads;
|
||||
if (!pending) {
|
||||
final DirectInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
threads = threadListViewModel.getThreads().getValue();
|
||||
} else {
|
||||
final DirectPendingInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectPendingInboxViewModel.class);
|
||||
threads = threadListViewModel.getThreads().getValue();
|
||||
}
|
||||
final Optional<DirectThread> first = threads != null
|
||||
? threads.stream()
|
||||
.filter(thread -> thread.getThreadId().equals(viewModel.getThreadId()))
|
||||
@ -529,19 +542,26 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
}
|
||||
|
||||
private void setObservers() {
|
||||
viewModel.isPending().observe(getViewLifecycleOwner(), isPending -> {
|
||||
if (isPending == null) {
|
||||
hideInput();
|
||||
return;
|
||||
}
|
||||
if (isPending) {
|
||||
showPendingOptions();
|
||||
return;
|
||||
}
|
||||
hidePendingOptions();
|
||||
final Integer inputMode = viewModel.getInputMode().getValue();
|
||||
if (inputMode != null && inputMode == 1) return;
|
||||
showInput();
|
||||
});
|
||||
viewModel.getInputMode().observe(getViewLifecycleOwner(), inputMode -> {
|
||||
final Boolean isPending = viewModel.isPending().getValue();
|
||||
if (isPending != null && isPending) return;
|
||||
if (inputMode == null || inputMode == 0) return;
|
||||
if (inputMode == 1) {
|
||||
binding.emojiToggle.setVisibility(View.GONE);
|
||||
binding.camera.setVisibility(View.GONE);
|
||||
binding.gallery.setVisibility(View.GONE);
|
||||
binding.input.setVisibility(View.GONE);
|
||||
binding.inputBg.setVisibility(View.GONE);
|
||||
binding.recordView.setVisibility(View.GONE);
|
||||
binding.send.setVisibility(View.GONE);
|
||||
if (itemTouchHelper != null) {
|
||||
itemTouchHelper.attachToRecyclerView(null);
|
||||
}
|
||||
hideInput();
|
||||
}
|
||||
});
|
||||
viewModel.getThreadTitle().observe(getViewLifecycleOwner(), this::setTitle);
|
||||
@ -614,6 +634,81 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
prevLength = length;
|
||||
});
|
||||
viewModel.getPendingRequestsCount().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
|
||||
viewModel.getUsers().observe(getViewLifecycleOwner(), users -> {
|
||||
if (users == null || users.isEmpty()) return;
|
||||
final User user = users.get(0);
|
||||
binding.acceptPendingRequestQuestion.setText(getString(R.string.accept_request_from_user, user.getUsername(), user.getFullName()));
|
||||
});
|
||||
}
|
||||
|
||||
private void hidePendingOptions() {
|
||||
binding.acceptPendingRequestQuestion.setVisibility(View.GONE);
|
||||
binding.decline.setVisibility(View.GONE);
|
||||
binding.accept.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void showPendingOptions() {
|
||||
binding.acceptPendingRequestQuestion.setVisibility(View.VISIBLE);
|
||||
binding.decline.setVisibility(View.VISIBLE);
|
||||
binding.accept.setVisibility(View.VISIBLE);
|
||||
binding.accept.setOnClickListener(v -> {
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.acceptRequest();
|
||||
handlePendingChangeResource(resourceLiveData, false);
|
||||
});
|
||||
binding.decline.setOnClickListener(v -> {
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.declineRequest();
|
||||
handlePendingChangeResource(resourceLiveData, true);
|
||||
});
|
||||
}
|
||||
|
||||
private void handlePendingChangeResource(final LiveData<Resource<Object>> resourceLiveData, final boolean isDecline) {
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
final Resource.Status status = resource.status;
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
resourceLiveData.removeObservers(getViewLifecycleOwner());
|
||||
if (isDecline) {
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
navController.navigateUp();
|
||||
}
|
||||
break;
|
||||
case LOADING:
|
||||
break;
|
||||
case ERROR:
|
||||
if (resource.message != null) {
|
||||
Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
resourceLiveData.removeObservers(getViewLifecycleOwner());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void hideInput() {
|
||||
binding.emojiToggle.setVisibility(View.GONE);
|
||||
binding.camera.setVisibility(View.GONE);
|
||||
binding.gallery.setVisibility(View.GONE);
|
||||
binding.input.setVisibility(View.GONE);
|
||||
binding.inputBg.setVisibility(View.GONE);
|
||||
binding.recordView.setVisibility(View.GONE);
|
||||
binding.send.setVisibility(View.GONE);
|
||||
if (itemTouchHelper != null) {
|
||||
itemTouchHelper.attachToRecyclerView(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void showInput() {
|
||||
binding.emojiToggle.setVisibility(View.VISIBLE);
|
||||
binding.camera.setVisibility(View.VISIBLE);
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
binding.input.setVisibility(View.VISIBLE);
|
||||
binding.inputBg.setVisibility(View.VISIBLE);
|
||||
binding.recordView.setVisibility(View.VISIBLE);
|
||||
binding.send.setVisibility(View.VISIBLE);
|
||||
if (itemTouchHelper != null) {
|
||||
itemTouchHelper.attachToRecyclerView(binding.chats);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeExperimentalUsageError")
|
||||
|
@ -0,0 +1,152 @@
|
||||
package awais.instagrabber.fragments.directmessages;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelStoreOwner;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.adapters.DirectMessageInboxAdapter;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;
|
||||
import awais.instagrabber.databinding.FragmentDirectPendingInboxBinding;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.viewmodels.DirectPendingInboxViewModel;
|
||||
|
||||
public class DirectPendingInboxFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = DirectPendingInboxFragment.class.getSimpleName();
|
||||
|
||||
private CoordinatorLayout root;
|
||||
private RecyclerLazyLoaderAtEdge lazyLoader;
|
||||
private DirectPendingInboxViewModel viewModel;
|
||||
private boolean shouldRefresh = true;
|
||||
private FragmentDirectPendingInboxBinding binding;
|
||||
private DirectMessageInboxAdapter inboxAdapter;
|
||||
private MainActivity fragmentActivity;
|
||||
private boolean scrollToTop = false;
|
||||
private boolean navigating;
|
||||
private Observer<List<DirectThread>> threadsObserver;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) getActivity();
|
||||
if (fragmentActivity != null) {
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
|
||||
viewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectPendingInboxViewModel.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
if (root != null) {
|
||||
shouldRefresh = false;
|
||||
return root;
|
||||
}
|
||||
binding = FragmentDirectPendingInboxBinding.inflate(inflater, container, false);
|
||||
root = binding.getRoot();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
if (!shouldRefresh) return;
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
lazyLoader.resetState();
|
||||
scrollToTop = true;
|
||||
if (viewModel != null) {
|
||||
viewModel.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setupObservers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull final Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
removeViewModelObservers();
|
||||
viewModel.onDestroy();
|
||||
}
|
||||
|
||||
private void setupObservers() {
|
||||
threadsObserver = list -> {
|
||||
if (inboxAdapter == null) return;
|
||||
inboxAdapter.submitList(list, () -> {
|
||||
if (!scrollToTop) return;
|
||||
binding.pendingInboxList.smoothScrollToPosition(0);
|
||||
scrollToTop = false;
|
||||
});
|
||||
};
|
||||
viewModel.getThreads().observe(fragmentActivity, threadsObserver);
|
||||
viewModel.getFetchingInbox().observe(getViewLifecycleOwner(), fetching -> binding.swipeRefreshLayout.setRefreshing(fetching));
|
||||
}
|
||||
|
||||
private void removeViewModelObservers() {
|
||||
if (viewModel == null) return;
|
||||
if (threadsObserver != null) {
|
||||
viewModel.getThreads().removeObserver(threadsObserver);
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
setupObservers();
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
binding.pendingInboxList.setHasFixedSize(true);
|
||||
binding.pendingInboxList.setItemViewCacheSize(20);
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
|
||||
binding.pendingInboxList.setLayoutManager(layoutManager);
|
||||
inboxAdapter = new DirectMessageInboxAdapter(thread -> {
|
||||
if (navigating) return;
|
||||
navigating = true;
|
||||
if (isAdded()) {
|
||||
final DirectPendingInboxFragmentDirections.ActionPendingInboxToThread directions = DirectPendingInboxFragmentDirections
|
||||
.actionPendingInboxToThread(thread.getThreadId(), thread.getThreadTitle());
|
||||
directions.setPending(true);
|
||||
NavHostFragment.findNavController(this).navigate(directions);
|
||||
}
|
||||
navigating = false;
|
||||
});
|
||||
inboxAdapter.setHasStableIds(true);
|
||||
binding.pendingInboxList.setAdapter(inboxAdapter);
|
||||
lazyLoader = new RecyclerLazyLoaderAtEdge(layoutManager, page -> {
|
||||
if (viewModel == null) return;
|
||||
viewModel.fetchInbox();
|
||||
});
|
||||
binding.pendingInboxList.addOnScrollListener(lazyLoader);
|
||||
}
|
||||
}
|
@ -24,6 +24,9 @@ public interface DirectMessagesRepository {
|
||||
@GET("/api/v1/direct_v2/inbox/")
|
||||
Call<DirectInboxResponse> fetchInbox(@QueryMap Map<String, Object> queryMap);
|
||||
|
||||
@GET("/api/v1/direct_v2/pending_inbox/")
|
||||
Call<DirectInboxResponse> fetchPendingInbox(@QueryMap Map<String, Object> queryMap);
|
||||
|
||||
@GET("/api/v1/direct_v2/threads/{threadId}/")
|
||||
Call<DirectThreadFeedResponse> fetchThread(@Path("threadId") String threadId,
|
||||
@QueryMap Map<String, Object> queryMap);
|
||||
@ -132,4 +135,14 @@ public interface DirectMessagesRepository {
|
||||
@POST("/api/v1/direct_v2/threads/{threadId}/remove_all_users/")
|
||||
Call<DirectThreadDetailsChangeResponse> end(@Path("threadId") String threadId,
|
||||
@FieldMap final Map<String, String> form);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/direct_v2/threads/{threadId}/approve/")
|
||||
Call<String> approveRequest(@Path("threadId") String threadId,
|
||||
@FieldMap final Map<String, String> form);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/direct_v2/threads/{threadId}/decline/")
|
||||
Call<String> declineRequest(@Path("threadId") String threadId,
|
||||
@FieldMap final Map<String, String> form);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ public class DirectInboxResponse {
|
||||
private final long seqId;
|
||||
private final long snapshotAtMs;
|
||||
private final int pendingRequestsTotal;
|
||||
private final boolean hasPendingTopRequests;
|
||||
private final User mostRecentInviter;
|
||||
private final String status;
|
||||
|
||||
@ -16,6 +17,7 @@ public class DirectInboxResponse {
|
||||
final long seqId,
|
||||
final long snapshotAtMs,
|
||||
final int pendingRequestsTotal,
|
||||
final boolean hasPendingTopRequests,
|
||||
final User mostRecentInviter,
|
||||
final String status) {
|
||||
this.viewer = viewer;
|
||||
@ -23,6 +25,7 @@ public class DirectInboxResponse {
|
||||
this.seqId = seqId;
|
||||
this.snapshotAtMs = snapshotAtMs;
|
||||
this.pendingRequestsTotal = pendingRequestsTotal;
|
||||
this.hasPendingTopRequests = hasPendingTopRequests;
|
||||
this.mostRecentInviter = mostRecentInviter;
|
||||
this.status = status;
|
||||
}
|
||||
@ -47,6 +50,10 @@ public class DirectInboxResponse {
|
||||
return pendingRequestsTotal;
|
||||
}
|
||||
|
||||
public boolean hasPendingTopRequests() {
|
||||
return hasPendingTopRequests;
|
||||
}
|
||||
|
||||
public User getMostRecentInviter() {
|
||||
return mostRecentInviter;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class DirectThread implements Serializable {
|
||||
private final boolean isPin;
|
||||
private final boolean named;
|
||||
private final boolean canonical;
|
||||
private final boolean pending;
|
||||
private boolean pending;
|
||||
private final boolean archived;
|
||||
private final boolean valuedRequest;
|
||||
private final String threadType;
|
||||
@ -43,6 +43,7 @@ public class DirectThread implements Serializable {
|
||||
private final DirectThreadDirectStory directStory;
|
||||
private boolean approvalRequiredForNewMembers;
|
||||
private int inputMode;
|
||||
private final List<ThreadContext> threadContextItems;
|
||||
|
||||
public DirectThread(final String threadId,
|
||||
final String threadV2Id,
|
||||
@ -76,7 +77,8 @@ public class DirectThread implements Serializable {
|
||||
final DirectItem lastPermanentItem,
|
||||
final DirectThreadDirectStory directStory,
|
||||
final boolean approvalRequiredForNewMembers,
|
||||
final int inputMode) {
|
||||
final int inputMode,
|
||||
final List<ThreadContext> threadContextItems) {
|
||||
this.threadId = threadId;
|
||||
this.threadV2Id = threadV2Id;
|
||||
this.users = users;
|
||||
@ -110,6 +112,7 @@ public class DirectThread implements Serializable {
|
||||
this.directStory = directStory;
|
||||
this.approvalRequiredForNewMembers = approvalRequiredForNewMembers;
|
||||
this.inputMode = inputMode;
|
||||
this.threadContextItems = threadContextItems;
|
||||
}
|
||||
|
||||
public String getThreadId() {
|
||||
@ -164,6 +167,10 @@ public class DirectThread implements Serializable {
|
||||
return pending;
|
||||
}
|
||||
|
||||
public void setPending(final boolean pending) {
|
||||
this.pending = pending;
|
||||
}
|
||||
|
||||
public boolean isArchived() {
|
||||
return archived;
|
||||
}
|
||||
@ -260,6 +267,10 @@ public class DirectThread implements Serializable {
|
||||
this.inputMode = inputMode;
|
||||
}
|
||||
|
||||
public List<ThreadContext> getThreadContextItems() {
|
||||
return threadContextItems;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DirectItem getFirstDirectItem() {
|
||||
DirectItem firstItem = null;
|
||||
|
@ -0,0 +1,21 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ThreadContext implements Serializable {
|
||||
private final int type;
|
||||
private final String text;
|
||||
|
||||
public ThreadContext(final int type, final String text) {
|
||||
this.type = type;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ import awais.instagrabber.repositories.responses.VideoVersion;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia;
|
||||
|
||||
public class DirectItemFactory {
|
||||
public final class DirectItemFactory {
|
||||
|
||||
public static DirectItem createText(final long userId,
|
||||
final String clientContext,
|
||||
|
@ -3,6 +3,7 @@ package awais.instagrabber.viewmodels;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
@ -33,6 +34,7 @@ public class DirectInboxViewModel extends ViewModel {
|
||||
private final MutableLiveData<List<DirectThread>> threads = new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> fetchingUnseenCount = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<Integer> unseenCount = new MutableLiveData<>(0);
|
||||
private final MutableLiveData<Integer> pendingRequestsTotal = new MutableLiveData<>(0);
|
||||
|
||||
private Call<DirectInboxResponse> inboxRequest;
|
||||
private Call<DirectBadgeCount> unseenCountRequest;
|
||||
@ -54,12 +56,12 @@ public class DirectInboxViewModel extends ViewModel {
|
||||
fetchUnseenCount();
|
||||
}
|
||||
|
||||
public MutableLiveData<List<DirectThread>> getThreads() {
|
||||
public LiveData<List<DirectThread>> getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void setThreads(final List<DirectThread> threads) {
|
||||
getThreads().postValue(threads);
|
||||
this.threads.postValue(threads);
|
||||
}
|
||||
|
||||
public void addThreads(final Collection<DirectThread> threads) {
|
||||
@ -70,14 +72,18 @@ public class DirectInboxViewModel extends ViewModel {
|
||||
this.threads.postValue(list);
|
||||
}
|
||||
|
||||
public MutableLiveData<Integer> getUnseenCount() {
|
||||
public LiveData<Integer> getUnseenCount() {
|
||||
return unseenCount;
|
||||
}
|
||||
|
||||
public MutableLiveData<Boolean> getFetchingInbox() {
|
||||
public LiveData<Boolean> getFetchingInbox() {
|
||||
return fetchingInbox;
|
||||
}
|
||||
|
||||
public LiveData<Integer> getPendingRequestsTotal() {
|
||||
return pendingRequestsTotal;
|
||||
}
|
||||
|
||||
public User getViewer() {
|
||||
return viewer;
|
||||
}
|
||||
@ -126,6 +132,7 @@ public class DirectInboxViewModel extends ViewModel {
|
||||
}
|
||||
cursor = inbox.getOldestCursor();
|
||||
hasOlder = inbox.hasOlder();
|
||||
pendingRequestsTotal.postValue(response.getPendingRequestsTotal());
|
||||
// unseenCount.postValue(inbox.getUnseenCount());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,141 @@
|
||||
package awais.instagrabber.viewmodels;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.webservices.DirectMessagesService;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class DirectPendingInboxViewModel extends ViewModel {
|
||||
private static final String TAG = DirectPendingInboxViewModel.class.getSimpleName();
|
||||
|
||||
private final DirectMessagesService service;
|
||||
private final MutableLiveData<Boolean> fetchingInbox = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<List<DirectThread>> threads = new MutableLiveData<>();
|
||||
|
||||
private Call<DirectInboxResponse> inboxRequest;
|
||||
private Call<DirectBadgeCount> unseenCountRequest;
|
||||
private long seqId;
|
||||
private String cursor;
|
||||
private boolean hasOlder = true;
|
||||
private User viewer;
|
||||
|
||||
public DirectPendingInboxViewModel() {
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final long userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
if (TextUtils.isEmpty(csrfToken) || userId <= 0 || TextUtils.isEmpty(deviceUuid)) {
|
||||
throw new IllegalArgumentException("User is not logged in!");
|
||||
}
|
||||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid);
|
||||
fetchInbox();
|
||||
}
|
||||
|
||||
public LiveData<List<DirectThread>> getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void setThreads(final List<DirectThread> threads) {
|
||||
this.threads.postValue(threads);
|
||||
}
|
||||
|
||||
public void addThreads(final Collection<DirectThread> threads) {
|
||||
if (threads == null) return;
|
||||
List<DirectThread> list = getThreads().getValue();
|
||||
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
||||
list.addAll(threads);
|
||||
this.threads.postValue(list);
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getFetchingInbox() {
|
||||
return fetchingInbox;
|
||||
}
|
||||
|
||||
public User getViewer() {
|
||||
return viewer;
|
||||
}
|
||||
|
||||
public void fetchInbox() {
|
||||
if ((fetchingInbox.getValue() != null && fetchingInbox.getValue()) || !hasOlder) return;
|
||||
stopCurrentInboxRequest();
|
||||
fetchingInbox.postValue(true);
|
||||
inboxRequest = service.fetchPendingInbox(cursor, seqId);
|
||||
inboxRequest.enqueue(new Callback<DirectInboxResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectInboxResponse> call, @NonNull final Response<DirectInboxResponse> response) {
|
||||
parseInboxResponse(response.body());
|
||||
fetchingInbox.postValue(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectInboxResponse> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Failed fetching pending inbox", t);
|
||||
fetchingInbox.postValue(false);
|
||||
hasOlder = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void parseInboxResponse(final DirectInboxResponse response) {
|
||||
if (response == null) {
|
||||
hasOlder = false;
|
||||
return;
|
||||
}
|
||||
if (!response.getStatus().equals("ok")) {
|
||||
Log.e(TAG, "DM pending inbox fetch response: status not ok");
|
||||
hasOlder = false;
|
||||
return;
|
||||
}
|
||||
seqId = response.getSeqId();
|
||||
if (viewer == null) {
|
||||
viewer = response.getViewer();
|
||||
}
|
||||
final DirectInbox inbox = response.getInbox();
|
||||
final List<DirectThread> threads = inbox.getThreads();
|
||||
if (!TextUtils.isEmpty(cursor)) {
|
||||
addThreads(threads);
|
||||
} else {
|
||||
setThreads(threads);
|
||||
}
|
||||
cursor = inbox.getOldestCursor();
|
||||
hasOlder = inbox.hasOlder();
|
||||
}
|
||||
|
||||
private void stopCurrentInboxRequest() {
|
||||
if (inboxRequest == null || inboxRequest.isCanceled() || inboxRequest.isExecuted()) return;
|
||||
inboxRequest.cancel();
|
||||
inboxRequest = null;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
cursor = null;
|
||||
seqId = 0;
|
||||
hasOlder = true;
|
||||
fetchInbox();
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
stopCurrentInboxRequest();
|
||||
}
|
||||
}
|
@ -67,6 +67,7 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
||||
private final MutableLiveData<Boolean> approvalRequiredToJoin = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<DirectThreadParticipantRequestsResponse> pendingRequests = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<Integer> inputMode = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<Boolean> isPending = new MutableLiveData<>(false);
|
||||
private final DirectMessagesService directMessagesService;
|
||||
private final long userId;
|
||||
private final Resources resources;
|
||||
@ -115,6 +116,7 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
||||
muted.postValue(thread.isMuted());
|
||||
mentionsMuted.postValue(thread.isMentionsMuted());
|
||||
approvalRequiredToJoin.postValue(thread.isApprovalRequiredForNewMembers());
|
||||
isPending.postValue(thread.isPending());
|
||||
if (thread.getInputMode() != 1 && thread.isGroup() && viewerIsAdmin) {
|
||||
fetchPendingRequests();
|
||||
}
|
||||
@ -163,6 +165,10 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
||||
return pendingRequests;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> isPending() {
|
||||
return isPending;
|
||||
}
|
||||
|
||||
public boolean isViewerAdmin() {
|
||||
return viewerIsAdmin;
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
private final MutableLiveData<DirectItem> replyToItem = new MutableLiveData<>();
|
||||
private final MutableLiveData<Integer> pendingRequestsCount = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<Integer> inputMode = new MutableLiveData<>(0);
|
||||
private final MutableLiveData<Boolean> isPending = new MutableLiveData<>(null);
|
||||
|
||||
private final DirectMessagesService service;
|
||||
private final ContentResolver contentResolver;
|
||||
@ -340,6 +341,10 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
return inputMode;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> isPending() {
|
||||
return isPending;
|
||||
}
|
||||
|
||||
public void fetchChats() {
|
||||
final Boolean isFetching = fetching.getValue();
|
||||
if ((isFetching != null && isFetching) || !hasOlder) return;
|
||||
@ -404,6 +409,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
users.postValue(thread.getUsers());
|
||||
leftUsers.postValue(thread.getLeftUsers());
|
||||
fetching.postValue(false);
|
||||
isPending.postValue(thread.isPending());
|
||||
final List<Long> adminUserIds = thread.getAdminUserIds();
|
||||
viewerIsAdmin = adminUserIds.contains(viewerId);
|
||||
if (thread.getInputMode() != 1 && thread.isGroup() && viewerIsAdmin) {
|
||||
@ -1105,4 +1111,75 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<Resource<Object>> acceptRequest() {
|
||||
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
||||
final Call<String> request = service.approveRequest(threadId);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call,
|
||||
@NonNull final Response<String> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
try {
|
||||
final String string = response.errorBody() != null ? 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);
|
||||
data.postValue(Resource.error(msg, null));
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "onResponse: ", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
isPending.postValue(false);
|
||||
data.postValue(Resource.success(new Object()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
data.postValue(Resource.error(t.getMessage(), null));
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
public LiveData<Resource<Object>> declineRequest() {
|
||||
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
||||
final Call<String> request = service.declineRequest(threadId);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call,
|
||||
@NonNull final Response<String> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
try {
|
||||
final String string = response.errorBody() != null ? 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);
|
||||
data.postValue(Resource.error(msg, null));
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "onResponse: ", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
data.postValue(Resource.success(new Object()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
data.postValue(Resource.error(t.getMessage(), null));
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -409,4 +409,36 @@ public class DirectMessagesService extends BaseService {
|
||||
);
|
||||
return repository.end(threadId, form);
|
||||
}
|
||||
|
||||
public Call<DirectInboxResponse> fetchPendingInbox(final String cursor, final long seqId) {
|
||||
final ImmutableMap.Builder<String, Object> queryMapBuilder = ImmutableMap.<String, Object>builder()
|
||||
.put("visual_message_return_type", "unseen")
|
||||
.put("thread_message_limit", 10)
|
||||
.put("persistentBadging", true)
|
||||
.put("limit", 10);
|
||||
if (!TextUtils.isEmpty(cursor)) {
|
||||
queryMapBuilder.put("cursor", cursor);
|
||||
queryMapBuilder.put("direction", "older");
|
||||
}
|
||||
if (seqId != 0) {
|
||||
queryMapBuilder.put("seq_id", seqId);
|
||||
}
|
||||
return repository.fetchPendingInbox(queryMapBuilder.build());
|
||||
}
|
||||
|
||||
public Call<String> approveRequest(@NonNull final String threadId) {
|
||||
final ImmutableMap<String, String> form = ImmutableMap.of(
|
||||
"_csrftoken", csrfToken,
|
||||
"_uuid", deviceUuid
|
||||
);
|
||||
return repository.approveRequest(threadId, form);
|
||||
}
|
||||
|
||||
public Call<String> declineRequest(@NonNull final String threadId) {
|
||||
final ImmutableMap<String, String> form = ImmutableMap.of(
|
||||
"_csrftoken", csrfToken,
|
||||
"_uuid", deviceUuid
|
||||
);
|
||||
return repository.declineRequest(threadId, form);
|
||||
}
|
||||
}
|
||||
|
11
app/src/main/res/drawable/ic_account_clock_24.xml
Normal file
11
app/src/main/res/drawable/ic_account_clock_24.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M10.63,14.1C12.23,10.58 16.38,9.03 19.9,10.63C23.42,12.23 24.97,16.38 23.37,19.9C22.24,22.4 19.75,24 17,24C14.3,24 11.83,22.44 10.67,20H1V18C1.06,16.86 1.84,15.93 3.34,15.18C4.84,14.43 6.72,14.04 9,14C9.57,14 10.11,14.05 10.63,14.1V14.1M9,4C10.12,4.03 11.06,4.42 11.81,5.17C12.56,5.92 12.93,6.86 12.93,8C12.93,9.14 12.56,10.08 11.81,10.83C11.06,11.58 10.12,11.95 9,11.95C7.88,11.95 6.94,11.58 6.19,10.83C5.44,10.08 5.07,9.14 5.07,8C5.07,6.86 5.44,5.92 6.19,5.17C6.94,4.42 7.88,4.03 9,4M17,22A5,5 0 0,0 22,17A5,5 0 0,0 17,12A5,5 0 0,0 12,17A5,5 0 0,0 17,22M16,14H17.5V16.82L19.94,18.23L19.19,19.53L16,17.69V14Z" />
|
||||
</vector>
|
@ -11,12 +11,18 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintBottom_toTopOf="@id/reply_info"
|
||||
app:layout_constraintBottom_toTopOf="@id/chats_barrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:listitem="@layout/layout_dm_base" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/chats_barrier"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:barrierDirection="bottom" />
|
||||
|
||||
<View
|
||||
android:id="@+id/reply_bg"
|
||||
android:layout_width="0dp"
|
||||
@ -44,7 +50,7 @@
|
||||
app:layout_constraintBottom_toTopOf="@id/reply_preview_text"
|
||||
app:layout_constraintEnd_toStartOf="@id/reply_preview_image"
|
||||
app:layout_constraintStart_toStartOf="@id/input_bg"
|
||||
app:layout_constraintTop_toBottomOf="@id/chats"
|
||||
app:layout_constraintTop_toBottomOf="@id/chats_barrier"
|
||||
tools:text="Replying to yourself"
|
||||
tools:visibility="gone" />
|
||||
|
||||
@ -110,6 +116,7 @@
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/bg_input"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/input"
|
||||
app:layout_constraintEnd_toStartOf="@id/send"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -124,6 +131,7 @@
|
||||
android:layout_marginEnd="2dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:scrollbars="none"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_face_24"
|
||||
app:iconGravity="textStart"
|
||||
app:iconSize="24dp"
|
||||
@ -148,6 +156,7 @@
|
||||
android:paddingBottom="12dp"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHint="@color/grey_500"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/camera"
|
||||
app:layout_constraintStart_toEndOf="@id/emoji_toggle"
|
||||
@ -163,12 +172,13 @@
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/input_bg"
|
||||
app:layout_constraintEnd_toStartOf="@id/gallery"
|
||||
app:layout_constraintStart_toEndOf="@id/input"
|
||||
app:layout_constraintTop_toTopOf="@id/input"
|
||||
app:srcCompat="@drawable/ic_camera_24"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/gallery"
|
||||
@ -181,22 +191,23 @@
|
||||
android:paddingEnd="4dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_image_24"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/input_bg"
|
||||
app:layout_constraintEnd_toStartOf="@id/send"
|
||||
app:layout_constraintStart_toEndOf="@id/camera"
|
||||
app:layout_constraintTop_toTopOf="@id/input"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<awais.instagrabber.customviews.RecordView
|
||||
android:id="@+id/record_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="visible"
|
||||
android:visibility="gone"
|
||||
app:counter_time_color="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="@id/input_bg"
|
||||
app:layout_constraintEnd_toEndOf="@id/input_bg"
|
||||
app:layout_constraintStart_toStartOf="@id/input"
|
||||
app:layout_constraintTop_toBottomOf="@id/chats"
|
||||
app:layout_constraintTop_toBottomOf="@id/chats_barrier"
|
||||
app:slide_to_cancel_arrow="@drawable/recv_ic_arrow"
|
||||
app:slide_to_cancel_arrow_color="@color/white"
|
||||
app:slide_to_cancel_bounds="0dp"
|
||||
@ -210,6 +221,7 @@
|
||||
style="@style/Widget.MaterialComponents.Button.Icon.NoInsets"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/blue_900"
|
||||
app:elevation="4dp"
|
||||
app:icon="@drawable/avd_mic_to_send_anim"
|
||||
@ -226,17 +238,55 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="250dp"
|
||||
android:translationY="250dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/long_click_backdrop"
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/accept_pending_request_question"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clickable="true"
|
||||
android:elevation="5dp"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:visibility="gone" />
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:text="@string/accept_request_from_user"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/decline"
|
||||
app:layout_constraintTop_toBottomOf="@id/chats_barrier"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/decline"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:padding="16dp"
|
||||
android:text="@string/decline"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/red_500"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/accept"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/accept"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:padding="16dp"
|
||||
android:text="@string/accept"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/decline"
|
||||
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
23
app/src/main/res/layout/fragment_direct_pending_inbox.xml
Normal file
23
app/src/main/res/layout/fragment_direct_pending_inbox.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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="match_parent">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/pending_inbox_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="?attr/actionBarSize"
|
||||
tools:listitem="@layout/layout_dm_inbox_item" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -80,25 +80,7 @@
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_user_search"
|
||||
app:destination="@id/user_search_nav_graph">
|
||||
<!--<argument-->
|
||||
<!-- android:name="multiple"-->
|
||||
<!-- app:argType="boolean" />-->
|
||||
|
||||
<!--<argument-->
|
||||
<!-- android:name="title"-->
|
||||
<!-- app:argType="string"-->
|
||||
<!-- app:nullable="true" />-->
|
||||
|
||||
<!--<argument-->
|
||||
<!-- android:name="action_label"-->
|
||||
<!-- app:argType="string"-->
|
||||
<!-- app:nullable="true" />-->
|
||||
|
||||
<!--<argument-->
|
||||
<!-- android:name="hideUserIds"-->
|
||||
<!-- app:argType="long[]" />-->
|
||||
</action>
|
||||
app:destination="@id/user_search_nav_graph" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/directMessagesInboxFragment"
|
||||
@ -108,6 +90,9 @@
|
||||
<action
|
||||
android:id="@+id/action_inbox_to_thread"
|
||||
app:destination="@id/directMessagesThreadFragment" />
|
||||
<action
|
||||
android:id="@+id/action_inbox_to_pending_inbox"
|
||||
app:destination="@id/directPendingInboxFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/directMessagesThreadFragment"
|
||||
@ -119,8 +104,12 @@
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="pending"
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
<action
|
||||
android:id="@+id/action_dMThreadFragment_to_dMSettingsFragment"
|
||||
android:id="@+id/action_thread_to_settings"
|
||||
app:destination="@id/directMessagesSettingsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_thread_to_image_edit"
|
||||
@ -144,6 +133,11 @@
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
|
||||
<argument
|
||||
android:name="pending"
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_settings_to_inbox"
|
||||
app:destination="@id/directMessagesInboxFragment"
|
||||
@ -170,4 +164,13 @@
|
||||
android:name="options"
|
||||
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/directPendingInboxFragment"
|
||||
android:name="awais.instagrabber.fragments.directmessages.DirectPendingInboxFragment"
|
||||
android:label="@string/pending_requests"
|
||||
tools:layout="@layout/fragment_direct_pending_inbox">
|
||||
<action
|
||||
android:id="@+id/action_pending_inbox_to_thread"
|
||||
app:destination="@id/directMessagesThreadFragment" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -3,4 +3,5 @@
|
||||
<item name="reply" type="id" />
|
||||
<item name="unsend" type="id" />
|
||||
<item name="forward" type="id" />
|
||||
<item name="pending_requests" type="id" />
|
||||
</resources>
|
@ -418,4 +418,8 @@
|
||||
<string name="dms_action_end">End chat</string>
|
||||
<string name="dms_action_end_question">End chat?</string>
|
||||
<string name="dms_action_end_description">All members will be removed from the group. They will still be able to view the chat history.</string>
|
||||
<string name="pending_requests">Pending Requests</string>
|
||||
<string name="accept_request_from_user">Accept request from %1s (%2s)?</string>
|
||||
<string name="decline">Decline</string>
|
||||
<string name="accept">Accept</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user