diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt index 8531830af4..84d8a38b78 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt @@ -71,7 +71,8 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent { StartLocation.REMOTE_BACKUPS -> AppSettingsFragmentDirections.actionDirectToRemoteBackupsSettingsFragment() StartLocation.CHAT_FOLDERS -> AppSettingsFragmentDirections.actionDirectToChatFoldersFragment() StartLocation.CREATE_CHAT_FOLDER -> AppSettingsFragmentDirections.actionDirectToCreateFoldersFragment( - CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).folderId + CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).folderId, + CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).threadId ) } } @@ -207,8 +208,8 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent { fun chatFolders(context: Context): Intent = getIntentForStartLocation(context, StartLocation.CHAT_FOLDERS) @JvmStatic - fun createChatFolder(context: Context, id: Long = -1): Intent { - val arguments = CreateFoldersFragmentArgs.Builder(id) + fun createChatFolder(context: Context, id: Long = -1, threadId: Long?): Intent { + val arguments = CreateFoldersFragmentArgs.Builder(id, threadId ?: -1) .build() .toBundle() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt index 74094d31aa..5012b7dee2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt @@ -84,7 +84,7 @@ class ChatFoldersFragment : ComposeFragment() { navController = navController, modifier = Modifier.padding(contentPadding), onFolderClicked = { - navController.safeNavigate(ChatFoldersFragmentDirections.actionChatFoldersFragmentToCreateFoldersFragment(it.id)) + navController.safeNavigate(ChatFoldersFragmentDirections.actionChatFoldersFragmentToCreateFoldersFragment(it.id, -1)) }, onAdd = { folder -> Toast.makeText(requireContext(), getString(R.string.ChatFoldersFragment__folder_added, folder.name), Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt index 339807d864..8ffb6c79ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt @@ -225,6 +225,25 @@ class ChatFoldersViewModel : ViewModel() { } } + fun addThreadToIncludedChat(threadId: Long?) { + if (threadId == null || threadId == -1L) { + return + } + viewModelScope.launch { + val updatedFolder = internalState.value.currentFolder + val recipient = SignalDatabase.threads.getRecipientForThreadId(threadId) + if (recipient != null) { + internalState.update { + it.copy( + currentFolder = updatedFolder.copy( + includedRecipients = setOf(recipient) + ) + ) + } + } + } + } + fun addIncludedChat(recipientId: RecipientId) { val includedChats = internalState.value.pendingIncludedRecipients.plus(recipientId) internalState.update { @@ -325,4 +344,8 @@ class ChatFoldersViewModel : ViewModel() { } } } + + fun hasEmptyName(): Boolean { + return state.value.currentFolder.name.isEmpty() + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt index 960530164f..593c7121f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/CreateFoldersFragment.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material3.ButtonDefaults @@ -44,6 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.unit.dp import androidx.fragment.app.activityViewModels import androidx.navigation.NavController @@ -57,6 +59,7 @@ import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.avatar.AvatarImage import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -76,7 +79,7 @@ class CreateFoldersFragment : ComposeFragment() { viewLifecycleOwner, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (viewModel.hasChanges()) { + if (viewModel.hasChanges() && !viewModel.hasEmptyName()) { viewModel.showConfirmationDialog(true) } else { findNavController().popBackStack() @@ -96,13 +99,21 @@ class CreateFoldersFragment : ComposeFragment() { LaunchedEffect(Unit) { if (state.originalFolder == state.currentFolder) { viewModel.setCurrentFolderId(arguments?.getLong(KEY_FOLDER_ID) ?: -1) + viewModel.addThreadToIncludedChat(arguments?.getLong(KEY_THREAD_ID)) + } + } + + LaunchedEffect(Unit) { + if (!SignalStore.uiHints.hasSeenChatFoldersEducationSheet) { + SignalStore.uiHints.hasSeenChatFoldersEducationSheet = true + navController.safeNavigate(R.id.action_createFoldersFragment_to_chatFoldersEducationSheet) } } Scaffolds.Settings( title = if (isNewFolder) stringResource(id = R.string.CreateFoldersFragment__create_a_folder) else stringResource(id = R.string.CreateFoldersFragment__edit_folder), onNavigationClick = { - if (viewModel.hasChanges()) { + if (viewModel.hasChanges() && !viewModel.hasEmptyName()) { viewModel.showConfirmationDialog(true) } else { navController.popBackStack() @@ -161,6 +172,7 @@ class CreateFoldersFragment : ComposeFragment() { companion object { private val KEY_FOLDER_ID = "folder_id" + private val KEY_THREAD_ID = "thread_id" } } @@ -195,24 +207,14 @@ fun CreateFolderScreen( dismiss = stringResource(id = android.R.string.cancel), onDismiss = onDeleteDismissed ) - } else if (state.showConfirmationDialog && isNewFolder) { - Dialogs.SimpleAlertDialog( - title = stringResource(id = R.string.CreateFoldersFragment__create_folder_title), - body = stringResource(id = R.string.CreateFoldersFragment__do_you_want_to_create, state.currentFolder.name), - confirm = stringResource(id = R.string.CreateFoldersFragment__create_folder), - onConfirm = { onCreateConfirmed(false) }, - dismiss = stringResource(id = R.string.CreateFoldersFragment__discard), - onDismiss = { onCreateDismissed(true) }, - onDismissRequest = { onCreateDismissed(false) } - ) } else if (state.showConfirmationDialog) { Dialogs.SimpleAlertDialog( - title = stringResource(id = R.string.CreateFoldersFragment__save_changes_title), - body = stringResource(id = R.string.CreateFoldersFragment__do_you_want_to_save), - confirm = stringResource(id = R.string.CreateFoldersFragment__save_changes), - onConfirm = { onCreateConfirmed(false) }, - dismiss = stringResource(id = R.string.CreateFoldersFragment__discard), - onDismiss = { onCreateDismissed(true) }, + title = stringResource(id = R.string.CreateFoldersFragment__discard_changes_title), + body = stringResource(id = R.string.CreateFoldersFragment__you_will_lose_changes), + confirm = stringResource(id = R.string.CreateFoldersFragment__discard), + onConfirm = { onCreateDismissed(true) }, + dismiss = stringResource(id = android.R.string.cancel), + onDismiss = { onCreateDismissed(false) }, onDismissRequest = { onCreateDismissed(false) } ) } @@ -224,6 +226,7 @@ fun CreateFolderScreen( value = state.currentFolder.name, label = { Text(text = stringResource(id = R.string.CreateFoldersFragment__folder_name)) }, onValueChange = onNameChange, + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences), singleLine = true, modifier = modifier .fillMaxWidth() 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 6b3984557b..734aab355a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/AddToFolderBottomSheet.kt @@ -77,7 +77,7 @@ class AddToFolderBottomSheet private constructor() : ComposeBottomSheetDialogFra dismissAllowingStateLoss() }, onCreate = { - requireContext().startActivity(AppSettingsActivity.createChatFolder(requireContext(), -1)) + requireContext().startActivity(AppSettingsActivity.createChatFolder(requireContext(), -1, threadId)) dismissAllowingStateLoss() } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ChatFolderAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ChatFolderAdapter.kt index e78b99ee21..4dd875ac39 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ChatFolderAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ChatFolderAdapter.kt @@ -53,7 +53,11 @@ class ChatFolderAdapter(val callbacks: Callbacks) : MappingAdapter() { true } if (model.isSelected) { - itemView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.signal_colorSurface2)) + itemView.backgroundTintList = if (callbacks.isScrolled()) { + ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.signal_colorBackground)) + } else { + ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.signal_colorSurface2)) + } } else { itemView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.transparent)) } @@ -75,5 +79,6 @@ class ChatFolderAdapter(val callbacks: Callbacks) : MappingAdapter() { fun onUnmuteAll(chatFolder: ChatFolderRecord) fun onReadAll(chatFolder: ChatFolderRecord) fun onFolderSettings() + fun isScrolled(): Boolean } } 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 f73fcfb3ad..cc73162aab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -1485,12 +1485,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 (RemoteConfig.internalUser() && viewModel.getCurrentFolder().getFolderType() == ChatFolderRecord.FolderType.ALL) { + if (viewModel.getCurrentFolder().getFolderType() == ChatFolderRecord.FolderType.ALL) { 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) )); - } else if (RemoteConfig.internalUser()){ + } else { items.add(new ActionItem(R.drawable.symbol_folder_minus, getString(R.string.ConversationListFragment_remove_from_folder), () -> viewModel.removeChatFromFolder(conversation.getThreadRecord().getThreadId()))); } items.add(new ActionItem(R.drawable.symbol_archive_24, getResources().getString(R.string.ConversationListFragment_archive), () -> handleArchive(id, false))); @@ -1689,6 +1689,11 @@ public class ConversationListFragment extends MainFragment implements ActionMode pullViewAppBarLayout.setExpanded(false, true); } + @Override + public boolean isScrolled() { + return list.canScrollVertically(-1); + } + @Override public void onChatFolderClicked(@NonNull ChatFolderRecord chatFolder) { int oldIndex = -1; @@ -1724,7 +1729,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode @Override public void onEdit(@NonNull ChatFolderRecord chatFolder) { - startActivity(AppSettingsActivity.createChatFolder(requireContext(), chatFolder.getId())); + startActivity(AppSettingsActivity.createChatFolder(requireContext(), chatFolder.getId(), null)); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt index 7ef1d431c7..655aa9c869 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.kt @@ -143,7 +143,9 @@ class ConversationListViewModel( if (conversations.isNotEmpty()) { false } else { - SignalDatabase.threads.getArchivedConversationListCount(filterRequest.filter) == 0 + val archivedCount = SignalDatabase.threads.getArchivedConversationListCount(filterRequest.filter) + val unarchivedCount = SignalDatabase.threads.getUnarchivedConversationListCount(filterRequest.filter) + (archivedCount + unarchivedCount) == 0 } } .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 7e9cde8598..6ee4e7f661 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -377,6 +377,10 @@ android:name="folder_id" app:argType="long" /> + + + + + + Include muted chats Create - - Create folder? - - Do you want to create the chat folder \"%1$s\"? - - Create folder Edit folder Save - - Save changes? - - Do you want to save the changes you\'ve made to this chat folder? - - Save changes - + + Discard changes? + + You\'ll lose the changes you\'ve made to this folder. + Discard Delete folder