mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 03:25:34 +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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user