diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java deleted file mode 100644 index ea895eba..00000000 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java +++ /dev/null @@ -1,531 +0,0 @@ -package awais.instagrabber.fragments.directmessages; - -import android.content.Context; -import android.os.Bundle; -import android.text.Editable; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModelProvider; -import androidx.navigation.NavBackStackEntry; -import androidx.navigation.NavController; -import androidx.navigation.NavDestination; -import androidx.navigation.NavDirections; -import androidx.navigation.fragment.NavHostFragment; -import androidx.recyclerview.widget.LinearLayoutManager; - -import com.google.android.material.snackbar.Snackbar; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import awais.instagrabber.ProfileNavGraphDirections; -import awais.instagrabber.R; -import awais.instagrabber.UserSearchNavGraphDirections; -import awais.instagrabber.activities.MainActivity; -import awais.instagrabber.adapters.DirectPendingUsersAdapter; -import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser; -import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback; -import awais.instagrabber.adapters.DirectUsersAdapter; -import awais.instagrabber.customviews.helpers.TextWatcherAdapter; -import awais.instagrabber.databinding.FragmentDirectMessagesSettingsBinding; -import awais.instagrabber.dialogs.ConfirmDialogFragment; -import awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback; -import awais.instagrabber.dialogs.MultiOptionDialogFragment; -import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option; -import awais.instagrabber.fragments.UserSearchFragment; -import awais.instagrabber.fragments.UserSearchFragmentDirections; -import awais.instagrabber.models.Resource; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse; -import awais.instagrabber.repositories.responses.directmessages.RankedRecipient; -import awais.instagrabber.utils.TextUtils; -import awais.instagrabber.utils.Utils; -import awais.instagrabber.viewmodels.AppStateViewModel; -import awais.instagrabber.viewmodels.DirectSettingsViewModel; -import awais.instagrabber.viewmodels.factories.DirectSettingsViewModelFactory; - -public class DirectMessageSettingsFragment extends Fragment implements ConfirmDialogFragmentCallback { - private static final String TAG = DirectMessageSettingsFragment.class.getSimpleName(); - private static final int APPROVAL_REQUIRED_REQUEST_CODE = 200; - private static final int LEAVE_THREAD_REQUEST_CODE = 201; - private static final int END_THREAD_REQUEST_CODE = 202; - - private FragmentDirectMessagesSettingsBinding binding; - private DirectSettingsViewModel viewModel; - private DirectUsersAdapter usersAdapter; - private boolean isPendingRequestsSetupDone = false; - private DirectPendingUsersAdapter pendingUsersAdapter; - private Set approvalRequiredUsers; - - @Override - public void onCreate(@Nullable final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - final Bundle arguments = getArguments(); - if (arguments == null) return; - final DirectMessageSettingsFragmentArgs args = DirectMessageSettingsFragmentArgs.fromBundle(arguments); - final MainActivity fragmentActivity = (MainActivity) requireActivity(); - final AppStateViewModel appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class); - final DirectSettingsViewModelFactory viewModelFactory = new DirectSettingsViewModelFactory( - fragmentActivity.getApplication(), - args.getThreadId(), - args.getPending(), - appStateViewModel.getCurrentUser() - ); - viewModel = new ViewModelProvider(this, viewModelFactory).get(DirectSettingsViewModel.class); - } - - @NonNull - @Override - public View onCreateView(@NonNull final LayoutInflater inflater, - final ViewGroup container, - final Bundle savedInstanceState) { - binding = FragmentDirectMessagesSettingsBinding.inflate(inflater, container, false); - // 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(); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - binding = null; - isPendingRequestsSetupDone = false; - } - - private void setupObservers() { - viewModel.getInputMode().observe(getViewLifecycleOwner(), inputMode -> { - if (inputMode == null || inputMode == 0) return; - if (inputMode == 1) { - binding.groupSettings.setVisibility(View.GONE); - binding.pendingMembersGroup.setVisibility(View.GONE); - binding.approvalRequired.setVisibility(View.GONE); - binding.approvalRequiredLabel.setVisibility(View.GONE); - binding.muteMessagesLabel.setVisibility(View.GONE); - binding.muteMessages.setVisibility(View.GONE); - } - }); - // Need to observe, so that getValue is correct - viewModel.getUsers().observe(getViewLifecycleOwner(), users -> {}); - viewModel.getLeftUsers().observe(getViewLifecycleOwner(), users -> {}); - viewModel.getUsersAndLeftUsers().observe(getViewLifecycleOwner(), usersPair -> { - if (usersAdapter == null) return; - usersAdapter.submitUsers(usersPair.first, usersPair.second); - }); - viewModel.getTitle().observe(getViewLifecycleOwner(), title -> binding.titleEdit.setText(title)); - viewModel.getAdminUserIds().observe(getViewLifecycleOwner(), adminUserIds -> { - if (usersAdapter == null) return; - usersAdapter.setAdminUserIds(adminUserIds); - }); - viewModel.isMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted)); - viewModel.isPending().observe(getViewLifecycleOwner(), pending -> binding.muteMessages.setVisibility(pending ? View.GONE : View.VISIBLE)); - viewModel.isViewerAdmin().observe(getViewLifecycleOwner(), this::setApprovalRelatedUI); - viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required)); - viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests); - viewModel.isGroup().observe(getViewLifecycleOwner(), this::setupSettings); - final NavController navController = NavHostFragment.findNavController(this); - final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry(); - if (backStackEntry != null) { - final MutableLiveData resultLiveData = backStackEntry.getSavedStateHandle().getLiveData("result"); - resultLiveData.observe(getViewLifecycleOwner(), result -> { - if ((result instanceof RankedRecipient)) { - final RankedRecipient recipient = (RankedRecipient) result; - final User user = getUser(recipient); - // Log.d(TAG, "result: " + user); - if (user != null) { - addMembers(Collections.singleton(recipient.getUser())); - } - } else if ((result instanceof Set)) { - try { - //noinspection unchecked - final Set recipients = (Set) result; - final Set users = recipients.stream() - .filter(Objects::nonNull) - .map(this::getUser) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - // Log.d(TAG, "result: " + users); - addMembers(users); - } catch (Exception e) { - Log.e(TAG, "search users result: ", e); - Snackbar.make(binding.getRoot(), e.getMessage() != null ? e.getMessage() : "", Snackbar.LENGTH_LONG).show(); - } - } - }); - } - } - - private void addMembers(final Set users) { - final Boolean approvalRequired = viewModel.getApprovalRequiredToJoin().getValue(); - Boolean isViewerAdmin = viewModel.isViewerAdmin().getValue(); - if (isViewerAdmin == null) { - isViewerAdmin = false; - } - if (!isViewerAdmin && approvalRequired != null && approvalRequired) { - approvalRequiredUsers = users; - final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( - APPROVAL_REQUIRED_REQUEST_CODE, - R.string.admin_approval_required, - R.string.admin_approval_required_description, - R.string.ok, - R.string.cancel, - 0 - ); - confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog"); - return; - } - final LiveData> detailsChangeResourceLiveData = viewModel.addMembers(users); - observeDetailsChange(detailsChangeResourceLiveData); - } - - @Nullable - private User getUser(@NonNull final RankedRecipient recipient) { - User user = null; - if (recipient.getUser() != null) { - user = recipient.getUser(); - } else if (recipient.getThread() != null && !recipient.getThread().isGroup()) { - user = recipient.getThread().getUsers().get(0); - } - return user; - } - - private void init() { - // setupSettings(); - setupMembers(); - } - - private void setupSettings(final boolean isGroup) { - binding.groupSettings.setVisibility(isGroup ? View.VISIBLE : View.GONE); - binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle()); - binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> { - final LiveData> resourceLiveData = isChecked ? viewModel.mute() : viewModel.unmute(); - handleSwitchChangeResource(resourceLiveData, buttonView); - }); - if (!isGroup) return; - binding.titleEdit.addTextChangedListener(new TextWatcherAdapter() { - @Override - public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { - if (s.toString().trim().equals(viewModel.getTitle().getValue())) { - binding.titleEditInputLayout.setSuffixText(null); - return; - } - binding.titleEditInputLayout.setSuffixText(getString(R.string.save)); - } - }); - binding.titleEditInputLayout.getSuffixTextView().setOnClickListener(v -> { - final Editable text = binding.titleEdit.getText(); - if (text == null) return; - final String newTitle = text.toString().trim(); - if (newTitle.equals(viewModel.getTitle().getValue())) return; - observeDetailsChange(viewModel.updateTitle(newTitle)); - }); - binding.addMembers.setOnClickListener(v -> { - if (!isAdded()) return; - final NavController navController = NavHostFragment.findNavController(this); - final NavDestination currentDestination = navController.getCurrentDestination(); - if (currentDestination == null) return; - if (currentDestination.getId() != R.id.directMessagesSettingsFragment) return; - final List users = viewModel.getUsers().getValue(); - final long[] currentUserIds; - if (users != null) { - currentUserIds = users.stream() - .mapToLong(User::getPk) - .sorted() - .toArray(); - } else { - currentUserIds = new long[0]; - } - final UserSearchNavGraphDirections.ActionGlobalUserSearch actionGlobalUserSearch = UserSearchFragmentDirections - .actionGlobalUserSearch() - .setTitle(getString(R.string.add_members)) - .setActionLabel(getString(R.string.add)) - .setHideUserIds(currentUserIds) - .setSearchMode(UserSearchFragment.SearchMode.RAVEN) - .setMultiple(true); - navController.navigate(actionGlobalUserSearch); - }); - binding.muteMentionsLabel.setOnClickListener(v -> binding.muteMentions.toggle()); - binding.muteMentions.setOnCheckedChangeListener((buttonView, isChecked) -> { - final LiveData> resourceLiveData = isChecked ? viewModel.muteMentions() : viewModel.unmuteMentions(); - handleSwitchChangeResource(resourceLiveData, buttonView); - }); - binding.leave.setOnClickListener(v -> { - final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( - LEAVE_THREAD_REQUEST_CODE, - R.string.dms_action_leave_question, - 0, - R.string.yes, - R.string.no, - 0 - ); - confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog"); - }); - Boolean isViewerAdmin = viewModel.isViewerAdmin().getValue(); - if (isViewerAdmin == null) isViewerAdmin = false; - if (isViewerAdmin) { - binding.end.setVisibility(View.VISIBLE); - binding.end.setOnClickListener(v -> { - final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( - END_THREAD_REQUEST_CODE, - R.string.dms_action_end_question, - R.string.dms_action_end_description, - R.string.yes, - R.string.no, - 0 - ); - confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog"); - }); - } else { - binding.end.setVisibility(View.GONE); - } - } - - private void setApprovalRelatedUI(final boolean isViewerAdmin) { - if (!isViewerAdmin) { - binding.pendingMembersGroup.setVisibility(View.GONE); - binding.approvalRequired.setVisibility(View.GONE); - binding.approvalRequiredLabel.setVisibility(View.GONE); - return; - } - binding.approvalRequired.setVisibility(View.VISIBLE); - binding.approvalRequiredLabel.setVisibility(View.VISIBLE); - binding.approvalRequiredLabel.setOnClickListener(v -> binding.approvalRequired.toggle()); - binding.approvalRequired.setOnCheckedChangeListener((buttonView, isChecked) -> { - final LiveData> resourceLiveData = isChecked ? viewModel.approvalRequired() : viewModel.approvalNotRequired(); - handleSwitchChangeResource(resourceLiveData, buttonView); - }); - } - - private void handleSwitchChangeResource(final LiveData> resourceLiveData, final CompoundButton buttonView) { - resourceLiveData.observe(getViewLifecycleOwner(), resource -> { - if (resource == null) return; - switch (resource.status) { - case SUCCESS: - buttonView.setEnabled(true); - break; - case ERROR: - buttonView.setEnabled(true); - buttonView.setChecked(!buttonView.isChecked()); - if (resource.message != null) { - Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show(); - } - if (resource.resId != 0) { - Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show(); - } - break; - case LOADING: - buttonView.setEnabled(false); - break; - } - }); - } - - private void setupMembers() { - final Context context = getContext(); - if (context == null) return; - binding.users.setLayoutManager(new LinearLayoutManager(context)); - final User inviter = viewModel.getInviter().getValue(); - usersAdapter = new DirectUsersAdapter( - inviter != null ? inviter.getPk() : -1, - (position, user, selected) -> { - if (TextUtils.isEmpty(user.getUsername()) && !TextUtils.isEmpty(user.getFbId())) { - Utils.openURL(context, "https://facebook.com/" + user.getFbId()); - return; - } - if (TextUtils.isEmpty(user.getUsername())) return; - final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections - .actionGlobalProfileFragment("@" + user.getUsername()); - NavHostFragment.findNavController(this).navigate(directions); - }, - (position, user) -> { - final ArrayList> options = viewModel.createUserOptions(user); - if (options == null || options.isEmpty()) return true; - final MultiOptionDialogFragment fragment = MultiOptionDialogFragment.newInstance(-1, options); - fragment.setSingleCallback(new MultiOptionDialogFragment.MultiOptionDialogSingleCallback() { - @Override - public void onSelect(final String action) { - if (action == null) return; - observeDetailsChange(viewModel.doAction(user, action)); - } - - @Override - public void onCancel() {} - }); - final FragmentManager fragmentManager = getChildFragmentManager(); - fragment.show(fragmentManager, "actions"); - return true; - } - ); - binding.users.setAdapter(usersAdapter); - } - - private void setPendingRequests(final DirectThreadParticipantRequestsResponse requests) { - if (requests == null || requests.getUsers() == null || requests.getUsers().isEmpty()) { - binding.pendingMembersGroup.setVisibility(View.GONE); - return; - } - if (!isPendingRequestsSetupDone) { - final Context context = getContext(); - if (context == null) return; - binding.pendingMembers.setLayoutManager(new LinearLayoutManager(context)); - pendingUsersAdapter = new DirectPendingUsersAdapter(new PendingUserCallback() { - @Override - public void onClick(final int position, final PendingUser pendingUser) { - final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections - .actionGlobalProfileFragment("@" + pendingUser.getUser().getUsername()); - NavHostFragment.findNavController(DirectMessageSettingsFragment.this).navigate(directions); - } - - @Override - public void onApprove(final int position, final PendingUser pendingUser) { - final LiveData> resourceLiveData = viewModel.approveUsers(Collections.singletonList(pendingUser.getUser())); - observeApprovalChange(resourceLiveData, position, pendingUser); - } - - @Override - public void onDeny(final int position, final PendingUser pendingUser) { - final LiveData> resourceLiveData = viewModel.denyUsers(Collections.singletonList(pendingUser.getUser())); - observeApprovalChange(resourceLiveData, position, pendingUser); - } - }); - binding.pendingMembers.setAdapter(pendingUsersAdapter); - binding.pendingMembersGroup.setVisibility(View.VISIBLE); - isPendingRequestsSetupDone = true; - } - if (pendingUsersAdapter != null) { - pendingUsersAdapter.submitPendingRequests(requests); - } - } - - private void observeDetailsChange(@NonNull final LiveData> resourceLiveData) { - resourceLiveData.observe(getViewLifecycleOwner(), resource -> { - if (resource == null) return; - switch (resource.status) { - case SUCCESS: - case LOADING: - break; - case ERROR: - if (resource.message != null) { - Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show(); - } - if (resource.resId != 0) { - Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show(); - } - break; - } - }); - } - - private void observeApprovalChange(@NonNull final LiveData> detailsChangeResourceLiveData, - final int position, - @NonNull final PendingUser pendingUser) { - detailsChangeResourceLiveData.observe(getViewLifecycleOwner(), resource -> { - if (resource == null) return; - switch (resource.status) { - case SUCCESS: - // pending user will be removed from the list, so no need to set the progress to false - // pendingUser.setInProgress(false); - break; - case LOADING: - pendingUser.setInProgress(true); - break; - case ERROR: - pendingUser.setInProgress(false); - if (resource.message != null) { - Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show(); - } - if (resource.resId != 0) { - Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show(); - } - break; - } - pendingUsersAdapter.notifyItemChanged(position); - }); - } - - @Override - public void onPositiveButtonClicked(final int requestCode) { - if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE && approvalRequiredUsers != null) { - final LiveData> detailsChangeResourceLiveData = viewModel.addMembers(approvalRequiredUsers); - observeDetailsChange(detailsChangeResourceLiveData); - return; - } - if (requestCode == LEAVE_THREAD_REQUEST_CODE) { - final LiveData> resourceLiveData = viewModel.leave(); - resourceLiveData.observe(getViewLifecycleOwner(), resource -> { - if (resource == null) return; - switch (resource.status) { - case SUCCESS: - final NavDirections directions = DirectMessageSettingsFragmentDirections.actionSettingsToInbox(); - NavHostFragment.findNavController(this).navigate(directions); - break; - case ERROR: - binding.leave.setEnabled(true); - if (resource.message != null) { - Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show(); - } - if (resource.resId != 0) { - Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show(); - } - break; - case LOADING: - binding.leave.setEnabled(false); - break; - } - }); - return; - } - if (requestCode == END_THREAD_REQUEST_CODE) { - final LiveData> resourceLiveData = viewModel.end(); - resourceLiveData.observe(getViewLifecycleOwner(), resource -> { - if (resource == null) return; - switch (resource.status) { - case SUCCESS: - break; - case ERROR: - binding.end.setEnabled(true); - if (resource.message != null) { - Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show(); - } - if (resource.resId != 0) { - Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show(); - } - break; - case LOADING: - binding.end.setEnabled(false); - break; - } - }); - } - } - - @Override - public void onNegativeButtonClicked(final int requestCode) { - if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE) { - approvalRequiredUsers = null; - } - } - - @Override - public void onNeutralButtonClicked(final int requestCode) {} -} diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.kt b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.kt new file mode 100644 index 00000000..069ebe09 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.kt @@ -0,0 +1,475 @@ +package awais.instagrabber.fragments.directmessages + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.CompoundButton +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.fragment.NavHostFragment +import androidx.recyclerview.widget.LinearLayoutManager +import awais.instagrabber.ProfileNavGraphDirections +import awais.instagrabber.R +import awais.instagrabber.activities.MainActivity +import awais.instagrabber.adapters.DirectPendingUsersAdapter +import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser +import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback +import awais.instagrabber.adapters.DirectUsersAdapter +import awais.instagrabber.customviews.helpers.TextWatcherAdapter +import awais.instagrabber.databinding.FragmentDirectMessagesSettingsBinding +import awais.instagrabber.dialogs.ConfirmDialogFragment +import awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback +import awais.instagrabber.dialogs.MultiOptionDialogFragment +import awais.instagrabber.dialogs.MultiOptionDialogFragment.MultiOptionDialogSingleCallback +import awais.instagrabber.fragments.UserSearchFragment +import awais.instagrabber.fragments.UserSearchFragmentDirections +import awais.instagrabber.models.Resource +import awais.instagrabber.repositories.responses.User +import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse +import awais.instagrabber.repositories.responses.directmessages.RankedRecipient +import awais.instagrabber.utils.TextUtils.isEmpty +import awais.instagrabber.utils.Utils +import awais.instagrabber.utils.extensions.TAG +import awais.instagrabber.viewmodels.AppStateViewModel +import awais.instagrabber.viewmodels.DirectSettingsViewModel +import awais.instagrabber.viewmodels.factories.DirectSettingsViewModelFactory +import com.google.android.material.snackbar.Snackbar +import java.util.* + +class DirectMessageSettingsFragment : Fragment(), ConfirmDialogFragmentCallback { + private lateinit var viewModel: DirectSettingsViewModel + private lateinit var binding: FragmentDirectMessagesSettingsBinding + + private var usersAdapter: DirectUsersAdapter? = null + private var isPendingRequestsSetupDone = false + private var pendingUsersAdapter: DirectPendingUsersAdapter? = null + private var approvalRequiredUsers: Set? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val arguments = arguments ?: return + val args = DirectMessageSettingsFragmentArgs.fromBundle(arguments) + val fragmentActivity = requireActivity() as MainActivity + val appStateViewModel: AppStateViewModel by activityViewModels() + val currentUser = appStateViewModel.currentUser ?: return + val viewModelFactory = DirectSettingsViewModelFactory( + fragmentActivity.application, + args.threadId, + args.pending, + currentUser + ) + viewModel = ViewModelProvider(this, viewModelFactory).get(DirectSettingsViewModel::class.java) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + binding = FragmentDirectMessagesSettingsBinding.inflate(inflater, container, false) + // currentlyRunning = new DirectMessageInboxThreadFetcher(threadId, null, null, fetchListener).execute(); + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + init() + setupObservers() + } + + override fun onDestroyView() { + super.onDestroyView() + isPendingRequestsSetupDone = false + } + + private fun setupObservers() { + viewModel.inputMode.observe(viewLifecycleOwner, { inputMode: Int? -> + if (inputMode == null || inputMode == 0) return@observe + if (inputMode == 1) { + binding.groupSettings.visibility = View.GONE + binding.pendingMembersGroup.visibility = View.GONE + binding.approvalRequired.visibility = View.GONE + binding.approvalRequiredLabel.visibility = View.GONE + binding.muteMessagesLabel.visibility = View.GONE + binding.muteMessages.visibility = View.GONE + } + }) + // Need to observe, so that getValue is correct + viewModel.getUsers().observe(viewLifecycleOwner, { }) + viewModel.getLeftUsers().observe(viewLifecycleOwner, { }) + viewModel.getUsersAndLeftUsers().observe(viewLifecycleOwner, { usersAdapter?.submitUsers(it.first, it.second) }) + viewModel.getTitle().observe(viewLifecycleOwner, { binding.titleEdit.setText(it) }) + viewModel.getAdminUserIds().observe(viewLifecycleOwner, { usersAdapter?.setAdminUserIds(it) }) + viewModel.isMuted().observe(viewLifecycleOwner, { binding.muteMessages.isChecked = it }) + viewModel.isPending().observe(viewLifecycleOwner, { binding.muteMessages.visibility = if (it) View.GONE else View.VISIBLE }) + viewModel.isViewerAdmin().observe(viewLifecycleOwner, { setApprovalRelatedUI(it) }) + viewModel.getApprovalRequiredToJoin().observe(viewLifecycleOwner, { binding.approvalRequired.isChecked = it }) + viewModel.getPendingRequests().observe(viewLifecycleOwner, { setPendingRequests(it) }) + viewModel.isGroup().observe(viewLifecycleOwner, { isGroup: Boolean -> setupSettings(isGroup) }) + val navController = NavHostFragment.findNavController(this) + val backStackEntry = navController.currentBackStackEntry + if (backStackEntry != null) { + val resultLiveData = backStackEntry.savedStateHandle.getLiveData("result") + resultLiveData.observe(viewLifecycleOwner, { result: Any? -> + if (result == null) return@observe + if (result is RankedRecipient) { + val user = getUser(result) + // Log.d(TAG, "result: " + user); + if (user != null) { + addMembers(setOf(user)) + } + } else if (result is Set<*>) { + try { + @Suppress("UNCHECKED_CAST") val recipients = result as Set + val users: Set = recipients.asSequence() + .filterNotNull() + .map { getUser(it) } + .filterNotNull() + .toSet() + // Log.d(TAG, "result: " + users); + addMembers(users) + } catch (e: Exception) { + Log.e(TAG, "search users result: ", e) + Snackbar.make(binding.root, e.message ?: "", Snackbar.LENGTH_LONG).show() + } + } + }) + } + } + + private fun addMembers(users: Set) { + val approvalRequired = viewModel.getApprovalRequiredToJoin().value + var isViewerAdmin = viewModel.isViewerAdmin().value + if (isViewerAdmin == null) { + isViewerAdmin = false + } + if (!isViewerAdmin && approvalRequired != null && approvalRequired) { + approvalRequiredUsers = users + val confirmDialogFragment = ConfirmDialogFragment.newInstance( + APPROVAL_REQUIRED_REQUEST_CODE, + R.string.admin_approval_required, + R.string.admin_approval_required_description, + R.string.ok, + R.string.cancel, + 0 + ) + confirmDialogFragment.show(childFragmentManager, "approval_required_dialog") + return + } + val detailsChangeResourceLiveData = viewModel.addMembers(users) + observeDetailsChange(detailsChangeResourceLiveData) + } + + private fun getUser(recipient: RankedRecipient): User? { + var user: User? = null + if (recipient.user != null) { + user = recipient.user + } else if (recipient.thread != null && !recipient.thread.isGroup) { + user = recipient.thread.users?.get(0) + } + return user + } + + private fun init() { + // setupSettings(); + setupMembers() + } + + private fun setupSettings(isGroup: Boolean) { + binding.groupSettings.visibility = if (isGroup) View.VISIBLE else View.GONE + binding.muteMessagesLabel.setOnClickListener { binding.muteMessages.toggle() } + binding.muteMessages.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean -> + val resourceLiveData = if (isChecked) viewModel.mute() else viewModel.unmute() + handleSwitchChangeResource(resourceLiveData, buttonView) + } + if (!isGroup) return + binding.titleEdit.addTextChangedListener(object : TextWatcherAdapter() { + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + if (s.toString().trim { it <= ' ' } == viewModel.getTitle().value) { + binding.titleEditInputLayout.suffixText = null + return + } + binding.titleEditInputLayout.suffixText = getString(R.string.save) + } + }) + binding.titleEditInputLayout.suffixTextView.setOnClickListener { + val text = binding.titleEdit.text ?: return@setOnClickListener + val newTitle = text.toString().trim { it <= ' ' } + if (newTitle == viewModel.getTitle().value) return@setOnClickListener + observeDetailsChange(viewModel.updateTitle(newTitle)) + } + binding.addMembers.setOnClickListener { + if (!isAdded) return@setOnClickListener + val navController = NavHostFragment.findNavController(this) + val currentDestination = navController.currentDestination ?: return@setOnClickListener + if (currentDestination.id != R.id.directMessagesSettingsFragment) return@setOnClickListener + val users = viewModel.getUsers().value + val currentUserIds: LongArray = users?.asSequence()?.map { obj: User -> obj.pk }?.sorted()?.toList()?.toLongArray() ?: LongArray(0) + val actionGlobalUserSearch = UserSearchFragmentDirections + .actionGlobalUserSearch() + .setTitle(getString(R.string.add_members)) + .setActionLabel(getString(R.string.add)) + .setHideUserIds(currentUserIds) + .setSearchMode(UserSearchFragment.SearchMode.RAVEN) + .setMultiple(true) + navController.navigate(actionGlobalUserSearch) + } + binding.muteMentionsLabel.setOnClickListener { binding.muteMentions.toggle() } + binding.muteMentions.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean -> + val resourceLiveData = if (isChecked) viewModel.muteMentions() else viewModel.unmuteMentions() + handleSwitchChangeResource(resourceLiveData, buttonView) + } + binding.leave.setOnClickListener { + val confirmDialogFragment = ConfirmDialogFragment.newInstance( + LEAVE_THREAD_REQUEST_CODE, + R.string.dms_action_leave_question, + 0, + R.string.yes, + R.string.no, + 0 + ) + confirmDialogFragment.show(childFragmentManager, "leave_thread_confirmation_dialog") + } + var isViewerAdmin = viewModel.isViewerAdmin().value + if (isViewerAdmin == null) isViewerAdmin = false + if (isViewerAdmin) { + binding.end.visibility = View.VISIBLE + binding.end.setOnClickListener { + val confirmDialogFragment = ConfirmDialogFragment.newInstance( + END_THREAD_REQUEST_CODE, + R.string.dms_action_end_question, + R.string.dms_action_end_description, + R.string.yes, + R.string.no, + 0 + ) + confirmDialogFragment.show(childFragmentManager, "end_thread_confirmation_dialog") + } + } else { + binding.end.visibility = View.GONE + } + } + + private fun setApprovalRelatedUI(isViewerAdmin: Boolean) { + if (!isViewerAdmin) { + binding.pendingMembersGroup.visibility = View.GONE + binding.approvalRequired.visibility = View.GONE + binding.approvalRequiredLabel.visibility = View.GONE + return + } + binding.approvalRequired.visibility = View.VISIBLE + binding.approvalRequiredLabel.visibility = View.VISIBLE + binding.approvalRequiredLabel.setOnClickListener { binding.approvalRequired.toggle() } + binding.approvalRequired.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean -> + val resourceLiveData = if (isChecked) viewModel.approvalRequired() else viewModel.approvalNotRequired() + handleSwitchChangeResource(resourceLiveData, buttonView) + } + } + + private fun handleSwitchChangeResource(resourceLiveData: LiveData>, buttonView: CompoundButton) { + resourceLiveData.observe(viewLifecycleOwner, { resource: Resource? -> + if (resource == null) return@observe + when (resource.status) { + Resource.Status.SUCCESS -> buttonView.isEnabled = true + Resource.Status.ERROR -> { + buttonView.isEnabled = true + buttonView.isChecked = !buttonView.isChecked + if (resource.message != null) { + Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show() + } + if (resource.resId != 0) { + Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show() + } + } + Resource.Status.LOADING -> buttonView.isEnabled = false + } + }) + } + + private fun setupMembers() { + val context = context ?: return + binding.users.layoutManager = LinearLayoutManager(context) + val inviter = viewModel.getInviter().value + usersAdapter = DirectUsersAdapter( + inviter?.pk ?: -1, + { _: Int, user: User, _: Boolean -> + if (isEmpty(user.username) && !isEmpty(user.fbId)) { + Utils.openURL(context, "https://facebook.com/" + user.fbId) + return@DirectUsersAdapter + } + if (isEmpty(user.username)) return@DirectUsersAdapter + val directions = ProfileNavGraphDirections + .actionGlobalProfileFragment("@" + user.username) + NavHostFragment.findNavController(this).navigate(directions) + }, + { _: Int, user: User? -> + val options = viewModel.createUserOptions(user) + if (options.isEmpty()) return@DirectUsersAdapter true + val fragment = MultiOptionDialogFragment.newInstance(-1, options) + fragment.setSingleCallback(object : MultiOptionDialogSingleCallback { + override fun onSelect(action: String?) { + if (action == null) return + val resourceLiveData = viewModel.doAction(user, action) + if (resourceLiveData != null) { + observeDetailsChange(resourceLiveData) + } + } + + override fun onCancel() {} + }) + val fragmentManager = childFragmentManager + fragment.show(fragmentManager, "actions") + true + } + ) + binding.users.adapter = usersAdapter + } + + private fun setPendingRequests(requests: DirectThreadParticipantRequestsResponse?) { + val nullOrEmpty: Boolean = requests?.users?.isNullOrEmpty() ?: true + if (nullOrEmpty) { + binding.pendingMembersGroup.visibility = View.GONE + return + } + if (!isPendingRequestsSetupDone) { + val context = context ?: return + binding.pendingMembers.layoutManager = LinearLayoutManager(context) + pendingUsersAdapter = DirectPendingUsersAdapter(object : PendingUserCallback { + override fun onClick(position: Int, pendingUser: PendingUser) { + val directions = ProfileNavGraphDirections + .actionGlobalProfileFragment("@" + pendingUser.user.username) + NavHostFragment.findNavController(this@DirectMessageSettingsFragment).navigate(directions) + } + + override fun onApprove(position: Int, pendingUser: PendingUser) { + val resourceLiveData = viewModel.approveUsers(listOf(pendingUser.user)) + observeApprovalChange(resourceLiveData, position, pendingUser) + } + + override fun onDeny(position: Int, pendingUser: PendingUser) { + val resourceLiveData = viewModel.denyUsers(listOf(pendingUser.user)) + observeApprovalChange(resourceLiveData, position, pendingUser) + } + }) + binding.pendingMembers.adapter = pendingUsersAdapter + binding.pendingMembersGroup.visibility = View.VISIBLE + isPendingRequestsSetupDone = true + } + pendingUsersAdapter?.submitPendingRequests(requests) + } + + private fun observeDetailsChange(resourceLiveData: LiveData>) { + resourceLiveData.observe(viewLifecycleOwner, { resource: Resource? -> + if (resource == null) return@observe + when (resource.status) { + Resource.Status.SUCCESS, + Resource.Status.LOADING, + -> { + } + Resource.Status.ERROR -> { + if (resource.message != null) { + Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show() + } + if (resource.resId != 0) { + Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show() + } + } + } + }) + } + + private fun observeApprovalChange( + detailsChangeResourceLiveData: LiveData>, + position: Int, + pendingUser: PendingUser, + ) { + detailsChangeResourceLiveData.observe(viewLifecycleOwner, { resource: Resource? -> + if (resource == null) return@observe + when (resource.status) { + Resource.Status.SUCCESS -> { + } + Resource.Status.LOADING -> pendingUser.isInProgress = true + Resource.Status.ERROR -> { + pendingUser.isInProgress = false + if (resource.message != null) { + Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show() + } + if (resource.resId != 0) { + Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show() + } + } + } + pendingUsersAdapter?.notifyItemChanged(position) + }) + } + + override fun onPositiveButtonClicked(requestCode: Int) { + if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE) { + approvalRequiredUsers?.let { + val detailsChangeResourceLiveData = viewModel.addMembers(it) + observeDetailsChange(detailsChangeResourceLiveData) + } + return + } + if (requestCode == LEAVE_THREAD_REQUEST_CODE) { + val resourceLiveData = viewModel.leave() + resourceLiveData.observe(viewLifecycleOwner, { resource: Resource? -> + if (resource == null) return@observe + when (resource.status) { + Resource.Status.SUCCESS -> { + val directions = DirectMessageSettingsFragmentDirections.actionSettingsToInbox() + NavHostFragment.findNavController(this).navigate(directions) + } + Resource.Status.ERROR -> { + binding.leave.isEnabled = true + if (resource.message != null) { + Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show() + } + if (resource.resId != 0) { + Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show() + } + } + Resource.Status.LOADING -> binding.leave.isEnabled = false + } + }) + return + } + if (requestCode == END_THREAD_REQUEST_CODE) { + val resourceLiveData = viewModel.end() + resourceLiveData.observe(viewLifecycleOwner, { resource: Resource? -> + if (resource == null) return@observe + when (resource.status) { + Resource.Status.SUCCESS -> { + } + Resource.Status.ERROR -> { + binding.end.isEnabled = true + if (resource.message != null) { + Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show() + } + if (resource.resId != 0) { + Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show() + } + } + Resource.Status.LOADING -> binding.end.isEnabled = false + } + }) + } + } + + override fun onNegativeButtonClicked(requestCode: Int) { + if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE) { + approvalRequiredUsers = null + } + } + + override fun onNeutralButtonClicked(requestCode: Int) {} + + companion object { + private const val APPROVAL_REQUIRED_REQUEST_CODE = 200 + private const val LEAVE_THREAD_REQUEST_CODE = 201 + private const val END_THREAD_REQUEST_CODE = 202 + } +} \ No newline at end of file