mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-11-03 21:15:35 +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 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);
 | 
			
		||||
        final List<DirectThread> threads = inboxViewModel.getThreads().getValue();
 | 
			
		||||
        final Bundle arguments = getArguments();
 | 
			
		||||
        if (arguments == null) {
 | 
			
		||||
            navController.navigateUp();
 | 
			
		||||
            return;
 | 
			
		||||
            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 List<DirectThread> threads;
 | 
			
		||||
        if (!pending) {
 | 
			
		||||
            final DirectInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
 | 
			
		||||
        final List<DirectThread> threads = threadListViewModel.getThreads().getValue();
 | 
			
		||||
            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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user