From 0b66e9409de92880cedf76b98b53fbd81a1cdb13 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Tue, 29 Oct 2024 14:14:20 -0400 Subject: [PATCH] Indicate when chats already belong in folder. --- .../AddToFolderBottomSheet.kt | 66 ++++++++++++++++--- .../ConversationListFragment.java | 21 ++++-- app/src/main/res/values/strings.xml | 2 + 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt index 734aab355a..54aa4793ec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -14,15 +15,18 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign @@ -50,13 +54,22 @@ class AddToFolderBottomSheet private constructor() : ComposeBottomSheetDialogFra companion object { private const val ARG_FOLDERS = "argument.folders" private const val ARG_THREAD_ID = "argument.thread.id" + private const val ARG_IS_INDIVIDUAL_CHAT = "argument.is.individual.chat" + /** + * Shows a bottom sheet that allows a thread to be added to a folder. + * + * @param folders list of available folders to add a thread to + * @param threadId the thread that is going to be added + * @param isIndividualChat whether the thread is an individual/1:1 chat as opposed to a group chat + */ @JvmStatic - fun showChatFolderSheet(folders: List, threadId: Long): ComposeBottomSheetDialogFragment { + fun showChatFolderSheet(folders: List, threadId: Long, isIndividualChat: Boolean): ComposeBottomSheetDialogFragment { return AddToFolderBottomSheet().apply { arguments = bundleOf( ARG_FOLDERS to folders, - ARG_THREAD_ID to threadId + ARG_THREAD_ID to threadId, + ARG_IS_INDIVIDUAL_CHAT to isIndividualChat ) } } @@ -64,17 +77,22 @@ class AddToFolderBottomSheet private constructor() : ComposeBottomSheetDialogFra @Composable override fun SheetContent() { - val folders = arguments?.getParcelableArrayListCompat(ARG_FOLDERS, ChatFolderRecord::class.java)?.filter { it.folderType != ChatFolderRecord.FolderType.ALL } - val threadId = arguments?.getLong(ARG_THREAD_ID) + val folders = requireArguments().getParcelableArrayListCompat(ARG_FOLDERS, ChatFolderRecord::class.java)?.filter { it.folderType != ChatFolderRecord.FolderType.ALL } + val threadId = requireArguments().getLong(ARG_THREAD_ID) + val isIndividualChat = requireArguments().getBoolean(ARG_IS_INDIVIDUAL_CHAT) AddToChatFolderSheetContent( + threadId = threadId, + isIndividualChat = isIndividualChat, folders = remember { folders ?: emptyList() }, - onClick = { folder -> - if (threadId != null) { + onClick = { folder, isAlreadyAdded -> + if (isAlreadyAdded) { + Toast.makeText(context, requireContext().getString(R.string.AddToFolderBottomSheet_this_chat_is_already, folder.name), Toast.LENGTH_SHORT).show() + } else { viewModel.addToFolder(folder.id, threadId) Toast.makeText(context, requireContext().getString(R.string.AddToFolderBottomSheet_added_to_s, folder.name), Toast.LENGTH_SHORT).show() + dismissAllowingStateLoss() } - dismissAllowingStateLoss() }, onCreate = { requireContext().startActivity(AppSettingsActivity.createChatFolder(requireContext(), -1, threadId)) @@ -86,8 +104,10 @@ class AddToFolderBottomSheet private constructor() : ComposeBottomSheetDialogFra @Composable private fun AddToChatFolderSheetContent( + threadId: Long, + isIndividualChat: Boolean, folders: List, - onClick: (ChatFolderRecord) -> Unit = {}, + onClick: (ChatFolderRecord, Boolean) -> Unit = { _, _ -> }, onCreate: () -> Unit = {} ) { Column( @@ -110,10 +130,16 @@ private fun AddToChatFolderSheetContent( .background(color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(18.dp)) ) { items(folders) { folder -> + val isIncludedViaChatType = (isIndividualChat && folder.showIndividualChats) || (!isIndividualChat && folder.showGroupChats) + val isIncludedExplicitly = folder.includedChats.contains(threadId) + val isExcludedExplicitly = folder.excludedChats.contains(threadId) + + val isAlreadyAdded = (isIncludedExplicitly || isIncludedViaChatType) && !isExcludedExplicitly + Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .clickable(onClick = { onClick(folder) }) + .clickable(onClick = { onClick(folder, isAlreadyAdded) }) .padding(start = 24.dp) .fillMaxWidth() .defaultMinSize(minHeight = dimensionResource(id = R.dimen.chat_folder_row_height)) @@ -133,6 +159,24 @@ private fun AddToChatFolderSheetContent( style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(start = 16.dp) ) + + Spacer(modifier = Modifier.weight(1f)) + + if (isAlreadyAdded) { + Icon( + painterResource(R.drawable.symbol_check_white_24), + contentDescription = null, + tint = Color.White, + modifier = Modifier + .padding(end = 16.dp) + .size(24.dp) + .background( + color = Color.Black.copy(.40f), + shape = CircleShape + ) + .padding(4.dp) + ) + } } } @@ -171,7 +215,9 @@ private fun AddToChatFolderSheetContent( private fun AddToChatFolderSheetContentPreview() { Previews.BottomSheetPreview { AddToChatFolderSheetContent( - folders = listOf(ChatFolderRecord(name = "Friends"), ChatFolderRecord(name = "Work")) + folders = listOf(ChatFolderRecord(name = "Friends"), ChatFolderRecord(name = "Work")), + threadId = 1, + isIndividualChat = false ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index cc73162aab..0b98d5dcf5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -33,6 +33,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; +import android.os.Parcelable; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; @@ -1066,7 +1067,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode private void onChatFoldersChanged(List folders) { chatFolderList.setVisibility(folders.size() > 1 && !isArchived() ? View.VISIBLE : View.GONE); - chatFolderAdapter.submitList(new ArrayList<>(folders)); + if (chatFolderList.getLayoutManager() != null) { + Parcelable savedState = chatFolderList.getLayoutManager().onSaveInstanceState(); + chatFolderAdapter.submitList(new ArrayList<>(folders), () -> chatFolderList.getLayoutManager().onRestoreInstanceState(savedState)); + } } private void onMegaphoneChanged(@NonNull Megaphone megaphone) { @@ -1485,10 +1489,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode if (conversation.getThreadRecord().isArchived()) { items.add(new ActionItem(R.drawable.symbol_archive_up_24, getResources().getString(R.string.ConversationListFragment_unarchive), () -> handleArchive(id, false))); } else { - if (viewModel.getCurrentFolder().getFolderType() == ChatFolderRecord.FolderType.ALL) { + if (viewModel.getCurrentFolder().getFolderType() == ChatFolderRecord.FolderType.ALL && + conversation.getThreadRecord().getRecipient().isIndividual() || + conversation.getThreadRecord().getRecipient().isPushV2Group()) { List folders = viewModel.getFolders().stream().map(ChatFolderMappingModel::getChatFolder).collect(Collectors.toList()); items.add(new ActionItem(R.drawable.symbol_folder_add, getString(R.string.ConversationListFragment_add_to_folder), () -> - AddToFolderBottomSheet.showChatFolderSheet(folders, conversation.getThreadRecord().getThreadId()).show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) + AddToFolderBottomSheet.showChatFolderSheet(folders, conversation.getThreadRecord().getThreadId(), conversation.getThreadRecord().getRecipient().isIndividual()).show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) )); } else { items.add(new ActionItem(R.drawable.symbol_folder_minus, getString(R.string.ConversationListFragment_remove_from_folder), () -> viewModel.removeChatFromFolder(conversation.getThreadRecord().getThreadId()))); @@ -1713,6 +1719,14 @@ public class ConversationListFragment extends MainFragment implements ActionMode } } + if (isScrolled()) { + list.smoothScrollToPosition(0); + } + + if (oldIndex == newIndex) { + return; + } + if (oldIndex < newIndex) { smoothScroller.setTargetPosition(Math.min(newIndex + 1, viewModel.getFolders().size())); } else { @@ -1724,7 +1738,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode } viewModel.select(chatFolder); - list.smoothScrollToPosition(0); } @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b7eef227ef..cff43ea754 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -749,6 +749,8 @@ Choose a folder Added to \"%1$s\" + + This chat is already in the folder \"%1$s\" Notification profile