Clean up chat folder records.

This commit is contained in:
Michelle Tang
2025-03-21 10:07:55 -04:00
committed by Cody Henthorne
parent 247c5de140
commit 82bb18e218
13 changed files with 102 additions and 83 deletions

View File

@@ -73,7 +73,6 @@ class ChatFolderTablesTest {
showUnread = true, showUnread = true,
showMutedChats = true, showMutedChats = true,
showGroupChats = true, showGroupChats = true,
isMuted = true,
folderType = ChatFolderRecord.FolderType.GROUP folderType = ChatFolderRecord.FolderType.GROUP
) )
@@ -96,7 +95,6 @@ class ChatFolderTablesTest {
val updatedFolder = folder.copy( val updatedFolder = folder.copy(
name = "updatedFolder2", name = "updatedFolder2",
position = 1, position = 1,
isMuted = true,
includedChats = listOf(aliceThread, charlieThread), includedChats = listOf(aliceThread, charlieThread),
excludedChats = listOf(bobThread) excludedChats = listOf(bobThread)
) )

View File

@@ -0,0 +1,12 @@
package org.thoughtcrime.securesms.components.settings.app.chats.folders
import org.thoughtcrime.securesms.recipients.Recipient
/**
* Information needed when creating/updating a chat folder. Used in [ChatFoldersSettingsState]
*/
data class ChatFolder(
val folderRecord: ChatFolderRecord = ChatFolderRecord(),
val includedRecipients: Set<Recipient> = emptySet(),
val excludedRecipients: Set<Recipient> = emptySet()
)

View File

@@ -1,9 +1,7 @@
package org.thoughtcrime.securesms.components.settings.app.chats.folders package org.thoughtcrime.securesms.components.settings.app.chats.folders
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.thoughtcrime.securesms.recipients.Recipient
/** /**
* Represents an entry in the [org.thoughtcrime.securesms.database.ChatFolderTables]. * Represents an entry in the [org.thoughtcrime.securesms.database.ChatFolderTables].
@@ -15,17 +13,11 @@ data class ChatFolderRecord(
val position: Int = -1, val position: Int = -1,
val includedChats: List<Long> = emptyList(), val includedChats: List<Long> = emptyList(),
val excludedChats: List<Long> = emptyList(), val excludedChats: List<Long> = emptyList(),
@IgnoredOnParcel
val includedRecipients: Set<Recipient> = emptySet(),
@IgnoredOnParcel
val excludedRecipients: Set<Recipient> = emptySet(),
val showUnread: Boolean = false, val showUnread: Boolean = false,
val showMutedChats: Boolean = true, val showMutedChats: Boolean = true,
val showIndividualChats: Boolean = false, val showIndividualChats: Boolean = false,
val showGroupChats: Boolean = false, val showGroupChats: Boolean = false,
val isMuted: Boolean = false, val folderType: FolderType = FolderType.CUSTOM
val folderType: FolderType = FolderType.CUSTOM,
val unreadCount: Int = 0
) : Parcelable { ) : Parcelable {
enum class FolderType(val value: Int) { enum class FolderType(val value: Int) {
/** Folder containing all chats */ /** Folder containing all chats */

View File

@@ -404,7 +404,6 @@ private fun ChatFolderPreview() {
showIndividualChats = true, showIndividualChats = true,
showGroupChats = true, showGroupChats = true,
showMutedChats = true, showMutedChats = true,
isMuted = false,
folderType = ChatFolderRecord.FolderType.CUSTOM folderType = ChatFolderRecord.FolderType.CUSTOM
), ),
ChatFolderRecord( ChatFolderRecord(
@@ -415,7 +414,6 @@ private fun ChatFolderPreview() {
showIndividualChats = true, showIndividualChats = true,
showGroupChats = false, showGroupChats = false,
showMutedChats = false, showMutedChats = false,
isMuted = false,
folderType = ChatFolderRecord.FolderType.CUSTOM folderType = ChatFolderRecord.FolderType.CUSTOM
) )
) )

View File

@@ -1,19 +1,24 @@
package org.thoughtcrime.securesms.components.settings.app.chats.folders package org.thoughtcrime.securesms.components.settings.app.chats.folders
import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.recipients.Recipient
/** /**
* Repository for chat folders that handles creation, deletion, listing, etc., * Repository for chat folders that handles creation, deletion, listing, etc.,
*/ */
object ChatFoldersRepository { object ChatFoldersRepository {
fun getCurrentFolders(includeUnreadAndMutedCounts: Boolean = false): List<ChatFolderRecord> { fun getCurrentFolders(): List<ChatFolderRecord> {
return SignalDatabase.chatFolders.getChatFolders(includeUnreadAndMutedCounts) return SignalDatabase.chatFolders.getChatFolders()
} }
fun createFolder(folder: ChatFolderRecord) { fun getUnreadCountAndMutedStatusForFolders(folders: List<ChatFolderRecord>): HashMap<Long, Pair<Int, Boolean>> {
val includedChats = folder.includedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) } return SignalDatabase.chatFolders.getUnreadCountAndMutedStatusForFolders(folders)
val excludedChats = folder.excludedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) } }
fun createFolder(folder: ChatFolderRecord, includedRecipients: Set<Recipient>, excludedRecipients: Set<Recipient>) {
val includedChats = includedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) }
val excludedChats = excludedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) }
val updatedFolder = folder.copy( val updatedFolder = folder.copy(
includedChats = includedChats, includedChats = includedChats,
excludedChats = excludedChats excludedChats = excludedChats
@@ -22,9 +27,9 @@ object ChatFoldersRepository {
SignalDatabase.chatFolders.createFolder(updatedFolder) SignalDatabase.chatFolders.createFolder(updatedFolder)
} }
fun updateFolder(folder: ChatFolderRecord) { fun updateFolder(folder: ChatFolderRecord, includedRecipients: Set<Recipient>, excludedRecipients: Set<Recipient>) {
val includedChats = folder.includedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) } val includedChats = includedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) }
val excludedChats = folder.excludedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) } val excludedChats = excludedRecipients.map { recipient -> SignalDatabase.threads.getOrCreateThreadIdFor(recipient) }
val updatedFolder = folder.copy( val updatedFolder = folder.copy(
includedChats = includedChats, includedChats = includedChats,
excludedChats = excludedChats excludedChats = excludedChats

View File

@@ -9,8 +9,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId
data class ChatFoldersSettingsState( data class ChatFoldersSettingsState(
val folders: List<ChatFolderRecord> = emptyList(), val folders: List<ChatFolderRecord> = emptyList(),
val suggestedFolders: List<ChatFolderRecord> = emptyList(), val suggestedFolders: List<ChatFolderRecord> = emptyList(),
val originalFolder: ChatFolderRecord = ChatFolderRecord(), val originalFolder: ChatFolder = ChatFolder(),
val currentFolder: ChatFolderRecord = ChatFolderRecord(), val currentFolder: ChatFolder = ChatFolder(),
val showDeleteDialog: Boolean = false, val showDeleteDialog: Boolean = false,
val showConfirmationDialog: Boolean = false, val showConfirmationDialog: Boolean = false,
val pendingIncludedRecipients: Set<RecipientId> = emptySet(), val pendingIncludedRecipients: Set<RecipientId> = emptySet(),

View File

@@ -24,15 +24,15 @@ class ChatFoldersViewModel : ViewModel() {
fun loadCurrentFolders(context: Context) { fun loadCurrentFolders(context: Context) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val folders = ChatFoldersRepository.getCurrentFolders(includeUnreadAndMutedCounts = false) val folders = ChatFoldersRepository.getCurrentFolders()
val suggestedFolders = getSuggestedFolders(context, folders) val suggestedFolders = getSuggestedFolders(context, folders)
internalState.update { internalState.update {
it.copy( it.copy(
folders = folders, folders = folders,
suggestedFolders = suggestedFolders, suggestedFolders = suggestedFolders,
currentFolder = ChatFolderRecord(), currentFolder = ChatFolder(),
originalFolder = ChatFolderRecord() originalFolder = ChatFolder()
) )
} }
} }
@@ -100,7 +100,8 @@ class ChatFoldersViewModel : ViewModel() {
SignalDatabase.threads.getRecipientForThreadId(threadId) SignalDatabase.threads.getRecipientForThreadId(threadId)
} }
val updatedFolder = folder.copy( val updatedFolder = ChatFolder(
folderRecord = folder,
includedRecipients = includedRecipients.toSet(), includedRecipients = includedRecipients.toSet(),
excludedRecipients = excludedRecipients.toSet() excludedRecipients = excludedRecipients.toSet()
) )
@@ -112,32 +113,32 @@ class ChatFoldersViewModel : ViewModel() {
} }
fun updateName(name: String) { fun updateName(name: String) {
val updatedFolder = internalState.value.currentFolder.copy( val updatedFolder = internalState.value.currentFolder.folderRecord.copy(
name = name.substring(0, minOf(name.length, 32)) name = name.substring(0, minOf(name.length, 32))
) )
internalState.update { internalState.update {
it.copy(currentFolder = updatedFolder) it.copy(currentFolder = it.currentFolder.copy(folderRecord = updatedFolder))
} }
} }
fun toggleShowUnread(showUnread: Boolean) { fun toggleShowUnread(showUnread: Boolean) {
val updatedFolder = internalState.value.currentFolder.copy( val updatedFolder = internalState.value.currentFolder.folderRecord.copy(
showUnread = showUnread showUnread = showUnread
) )
internalState.update { internalState.update {
it.copy(currentFolder = updatedFolder) it.copy(currentFolder = it.currentFolder.copy(folderRecord = updatedFolder))
} }
} }
fun toggleShowMutedChats(showMuted: Boolean) { fun toggleShowMutedChats(showMuted: Boolean) {
val updatedFolder = internalState.value.currentFolder.copy( val updatedFolder = internalState.value.currentFolder.folderRecord.copy(
showMutedChats = showMuted showMutedChats = showMuted
) )
internalState.update { internalState.update {
it.copy(currentFolder = updatedFolder) it.copy(currentFolder = it.currentFolder.copy(folderRecord = updatedFolder))
} }
} }
@@ -149,7 +150,7 @@ class ChatFoldersViewModel : ViewModel() {
fun deleteFolder(context: Context) { fun deleteFolder(context: Context) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
ChatFoldersRepository.deleteFolder(internalState.value.originalFolder) ChatFoldersRepository.deleteFolder(internalState.value.originalFolder.folderRecord)
loadCurrentFolders(context) loadCurrentFolders(context)
internalState.update { internalState.update {
@@ -166,8 +167,8 @@ class ChatFoldersViewModel : ViewModel() {
fun createFolder(context: Context, folder: ChatFolderRecord? = null) { fun createFolder(context: Context, folder: ChatFolderRecord? = null) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val currentFolder = folder ?: internalState.value.currentFolder val currentFolder = if (folder != null) ChatFolder(folder) else internalState.value.currentFolder
ChatFoldersRepository.createFolder(currentFolder) ChatFoldersRepository.createFolder(currentFolder.folderRecord, currentFolder.includedRecipients, currentFolder.excludedRecipients)
loadCurrentFolders(context) loadCurrentFolders(context)
internalState.update { internalState.update {
@@ -192,7 +193,8 @@ class ChatFoldersViewModel : ViewModel() {
fun updateFolder(context: Context) { fun updateFolder(context: Context) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
ChatFoldersRepository.updateFolder(internalState.value.currentFolder) val currentFolder = internalState.value.currentFolder
ChatFoldersRepository.updateFolder(currentFolder.folderRecord, currentFolder.includedRecipients, currentFolder.excludedRecipients)
loadCurrentFolders(context) loadCurrentFolders(context)
internalState.update { internalState.update {
@@ -208,10 +210,10 @@ class ChatFoldersViewModel : ViewModel() {
val excludedChats = currentFolder.excludedRecipients.map { recipient -> recipient.id }.toMutableSet() val excludedChats = currentFolder.excludedRecipients.map { recipient -> recipient.id }.toMutableSet()
val chatTypes: MutableSet<ChatType> = mutableSetOf() val chatTypes: MutableSet<ChatType> = mutableSetOf()
if (currentFolder.showIndividualChats) { if (currentFolder.folderRecord.showIndividualChats) {
chatTypes.add(ChatType.INDIVIDUAL) chatTypes.add(ChatType.INDIVIDUAL)
} }
if (currentFolder.showGroupChats) { if (currentFolder.folderRecord.showGroupChats) {
chatTypes.add(ChatType.GROUPS) chatTypes.add(ChatType.GROUPS)
} }
@@ -309,11 +311,13 @@ class ChatFoldersViewModel : ViewModel() {
internalState.update { internalState.update {
it.copy( it.copy(
currentFolder = updatedFolder.copy( currentFolder = updatedFolder.copy(
includedRecipients = includedRecipients, folderRecord = updatedFolder.folderRecord.copy(
excludedRecipients = excludedRecipients,
showIndividualChats = showIndividualChats, showIndividualChats = showIndividualChats,
showGroupChats = showGroupChats showGroupChats = showGroupChats
), ),
includedRecipients = includedRecipients,
excludedRecipients = excludedRecipients
),
pendingIncludedRecipients = emptySet(), pendingIncludedRecipients = emptySet(),
pendingExcludedRecipients = emptySet() pendingExcludedRecipients = emptySet()
) )
@@ -331,8 +335,8 @@ class ChatFoldersViewModel : ViewModel() {
val currentFolder = state.value.currentFolder val currentFolder = state.value.currentFolder
val originalFolder = state.value.originalFolder val originalFolder = state.value.originalFolder
return if (currentFolder.id == -1L) { return if (currentFolder.folderRecord.id == -1L) {
currentFolder.includedRecipients.isNotEmpty() || currentFolder.showIndividualChats || currentFolder.showGroupChats currentFolder.includedRecipients.isNotEmpty() || currentFolder.folderRecord.showIndividualChats || currentFolder.folderRecord.showGroupChats
} else { } else {
originalFolder != currentFolder || originalFolder != currentFolder ||
originalFolder.includedRecipients != currentFolder.includedRecipients || originalFolder.includedRecipients != currentFolder.includedRecipients ||
@@ -350,6 +354,6 @@ class ChatFoldersViewModel : ViewModel() {
} }
fun hasEmptyName(): Boolean { fun hasEmptyName(): Boolean {
return state.value.currentFolder.name.isEmpty() return state.value.currentFolder.folderRecord.name.isEmpty()
} }
} }

View File

@@ -94,7 +94,7 @@ class CreateFoldersFragment : ComposeFragment() {
val state by viewModel.state.collectAsStateWithLifecycle() val state by viewModel.state.collectAsStateWithLifecycle()
val navController: NavController by remember { mutableStateOf(findNavController()) } val navController: NavController by remember { mutableStateOf(findNavController()) }
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val isNewFolder = state.originalFolder.id == -1L val isNewFolder = state.originalFolder.folderRecord.id == -1L
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (state.originalFolder == state.currentFolder) { if (state.originalFolder == state.currentFolder) {
@@ -221,7 +221,7 @@ fun CreateFolderScreen(
LazyColumn { LazyColumn {
item { item {
TextField( TextField(
value = state.currentFolder.name, value = state.currentFolder.folderRecord.name,
label = { Text(text = stringResource(id = R.string.CreateFoldersFragment__folder_name)) }, label = { Text(text = stringResource(id = R.string.CreateFoldersFragment__folder_name)) },
onValueChange = onNameChange, onValueChange = onNameChange,
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences), keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences),
@@ -245,7 +245,7 @@ fun CreateFolderScreen(
onClick = onAddChat onClick = onAddChat
) )
if (state.currentFolder.showIndividualChats) { if (state.currentFolder.folderRecord.showIndividualChats) {
FolderRow( FolderRow(
icon = R.drawable.symbol_person_light_24, icon = R.drawable.symbol_person_light_24,
title = stringResource(R.string.ChatFoldersFragment__one_on_one_chats), title = stringResource(R.string.ChatFoldersFragment__one_on_one_chats),
@@ -253,7 +253,7 @@ fun CreateFolderScreen(
) )
} }
if (state.currentFolder.showGroupChats) { if (state.currentFolder.folderRecord.showGroupChats) {
FolderRow( FolderRow(
icon = R.drawable.symbol_group_light_20, icon = R.drawable.symbol_group_light_20,
title = stringResource(R.string.ChatFoldersFragment__groups), title = stringResource(R.string.ChatFoldersFragment__groups),
@@ -366,12 +366,12 @@ fun CreateFolderScreen(
Buttons.MediumTonal( Buttons.MediumTonal(
colors = ButtonDefaults.filledTonalButtonColors( colors = ButtonDefaults.filledTonalButtonColors(
contentColor = if (state.currentFolder.name.isEmpty()) { contentColor = if (state.currentFolder.folderRecord.name.isEmpty()) {
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
} else { } else {
MaterialTheme.colorScheme.onSurface MaterialTheme.colorScheme.onSurface
}, },
containerColor = if (state.currentFolder.name.isEmpty()) { containerColor = if (state.currentFolder.folderRecord.name.isEmpty()) {
MaterialTheme.colorScheme.surfaceVariant MaterialTheme.colorScheme.surfaceVariant
} else { } else {
MaterialTheme.colorScheme.primaryContainer MaterialTheme.colorScheme.primaryContainer
@@ -380,7 +380,7 @@ fun CreateFolderScreen(
), ),
enabled = hasChanges, enabled = hasChanges,
onClick = { onClick = {
if (state.currentFolder.name.isEmpty()) { if (state.currentFolder.folderRecord.name.isEmpty()) {
onShowToast() onShowToast()
} else { } else {
onCreateConfirmed() onCreateConfirmed()
@@ -419,7 +419,7 @@ private fun ShowUnreadSection(state: ChatFoldersSettingsState, onToggleShowUnrea
) )
} }
Switch( Switch(
checked = state.currentFolder.showUnread, checked = state.currentFolder.folderRecord.showUnread,
onCheckedChange = onToggleShowUnread onCheckedChange = onToggleShowUnread
) )
} }
@@ -440,7 +440,7 @@ private fun ShowMutedSection(state: ChatFoldersSettingsState, onToggleShowMuted:
) )
} }
Switch( Switch(
checked = state.currentFolder.showMutedChats, checked = state.currentFolder.folderRecord.showMutedChats,
onCheckedChange = onToggleShowMuted onCheckedChange = onToggleShowMuted
) )
} }
@@ -449,7 +449,7 @@ private fun ShowMutedSection(state: ChatFoldersSettingsState, onToggleShowMuted:
@SignalPreview @SignalPreview
@Composable @Composable
private fun CreateFolderPreview() { private fun CreateFolderPreview() {
val previewFolder = ChatFolderRecord(id = 1, name = "WIP") val previewFolder = ChatFolder(ChatFolderRecord(id = 1, name = "WIP"))
Previews.Preview { Previews.Preview {
CreateFolderScreen( CreateFolderScreen(
@@ -463,7 +463,7 @@ private fun CreateFolderPreview() {
@SignalPreview @SignalPreview
@Composable @Composable
private fun EditFolderPreview() { private fun EditFolderPreview() {
val previewFolder = ChatFolderRecord(id = 1, name = "Work") val previewFolder = ChatFolder(ChatFolderRecord(id = 1, name = "Work"))
Previews.Preview { Previews.Preview {
CreateFolderScreen( CreateFolderScreen(

View File

@@ -32,8 +32,8 @@ class ChatFolderAdapter(val callbacks: Callbacks) : MappingAdapter() {
val folder = model.chatFolder val folder = model.chatFolder
name.text = getName(itemView.context, folder) name.text = getName(itemView.context, folder)
unreadCount.visible = folder.unreadCount > 0 unreadCount.visible = model.unreadCount > 0
unreadCount.text = if (folder.unreadCount > 99) itemView.context.getString(R.string.ChatFolderAdapter__99p) else folder.unreadCount.toString() unreadCount.text = if (model.unreadCount > 99) itemView.context.getString(R.string.ChatFolderAdapter__99p) else model.unreadCount.toString()
itemView.setOnClickListener { itemView.setOnClickListener {
callbacks.onChatFolderClicked(model.chatFolder) callbacks.onChatFolderClicked(model.chatFolder)
} }
@@ -42,8 +42,8 @@ class ChatFolderAdapter(val callbacks: Callbacks) : MappingAdapter() {
context = itemView.context, context = itemView.context,
anchorView = view, anchorView = view,
folderType = model.chatFolder.folderType, folderType = model.chatFolder.folderType,
unreadCount = folder.unreadCount, unreadCount = model.unreadCount,
isMuted = folder.isMuted, isMuted = model.isMuted,
onEdit = { callbacks.onEdit(model.chatFolder) }, onEdit = { callbacks.onEdit(model.chatFolder) },
onMuteAll = { callbacks.onMuteAll(model.chatFolder) }, onMuteAll = { callbacks.onMuteAll(model.chatFolder) },
onUnmuteAll = { callbacks.onUnmuteAll(model.chatFolder) }, onUnmuteAll = { callbacks.onUnmuteAll(model.chatFolder) },

View File

@@ -3,8 +3,13 @@ package org.thoughtcrime.securesms.conversationlist
import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFolderRecord import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFolderRecord
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
/**
* Mapping model of folders used in [ChatFolderAdapter]
*/
data class ChatFolderMappingModel( data class ChatFolderMappingModel(
val chatFolder: ChatFolderRecord, val chatFolder: ChatFolderRecord,
val unreadCount: Int,
val isMuted: Boolean,
val isSelected: Boolean val isSelected: Boolean
) : MappingModel<ChatFolderMappingModel> { ) : MappingModel<ChatFolderMappingModel> {
override fun areItemsTheSame(newItem: ChatFolderMappingModel): Boolean { override fun areItemsTheSame(newItem: ChatFolderMappingModel): Boolean {
@@ -12,6 +17,9 @@ data class ChatFolderMappingModel(
} }
override fun areContentsTheSame(newItem: ChatFolderMappingModel): Boolean { override fun areContentsTheSame(newItem: ChatFolderMappingModel): Boolean {
return chatFolder == newItem.chatFolder && isSelected == newItem.isSelected return chatFolder == newItem.chatFolder &&
unreadCount == newItem.unreadCount &&
isMuted == newItem.isMuted &&
isSelected == newItem.isSelected
} }
} }

View File

@@ -218,7 +218,8 @@ class ConversationListViewModel(
private fun loadCurrentFolders() { private fun loadCurrentFolders() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val folders = ChatFoldersRepository.getCurrentFolders(includeUnreadAndMutedCounts = true) val folders = ChatFoldersRepository.getCurrentFolders()
val unreadCountAndMutedStatus = ChatFoldersRepository.getUnreadCountAndMutedStatusForFolders(folders)
val selectedFolderId = if (currentFolder.id == -1L) { val selectedFolderId = if (currentFolder.id == -1L) {
folders.firstOrNull()?.id folders.firstOrNull()?.id
@@ -226,7 +227,12 @@ class ConversationListViewModel(
currentFolder.id currentFolder.id
} }
val chatFolders = folders.map { folder -> val chatFolders = folders.map { folder ->
ChatFolderMappingModel(folder, selectedFolderId == folder.id) ChatFolderMappingModel(
chatFolder = folder,
unreadCount = unreadCountAndMutedStatus[folder.id]?.first ?: 0,
isMuted = unreadCountAndMutedStatus[folder.id]?.second ?: false,
isSelected = selectedFolderId == folder.id
)
} }
store.update { store.update {

View File

@@ -140,7 +140,6 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
showMutedChats = cursor.requireBoolean(ChatFolderTable.SHOW_MUTED), showMutedChats = cursor.requireBoolean(ChatFolderTable.SHOW_MUTED),
showIndividualChats = cursor.requireBoolean(ChatFolderTable.SHOW_INDIVIDUAL), showIndividualChats = cursor.requireBoolean(ChatFolderTable.SHOW_INDIVIDUAL),
showGroupChats = cursor.requireBoolean(ChatFolderTable.SHOW_GROUPS), showGroupChats = cursor.requireBoolean(ChatFolderTable.SHOW_GROUPS),
isMuted = cursor.requireBoolean(ChatFolderTable.IS_MUTED),
folderType = ChatFolderRecord.FolderType.deserialize(cursor.requireInt(ChatFolderTable.FOLDER_TYPE)), folderType = ChatFolderRecord.FolderType.deserialize(cursor.requireInt(ChatFolderTable.FOLDER_TYPE)),
includedChats = includedChats[id] ?: emptyList(), includedChats = includedChats[id] ?: emptyList(),
excludedChats = excludedChats[id] ?: emptyList() excludedChats = excludedChats[id] ?: emptyList()
@@ -153,7 +152,7 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
/** /**
* Maps the chat folder ids to its corresponding chat folder * Maps the chat folder ids to its corresponding chat folder
*/ */
fun getChatFolders(includeUnreadAndMutedCount: Boolean = false): List<ChatFolderRecord> { fun getChatFolders(): List<ChatFolderRecord> {
val includedChats: Map<Long, List<Long>> = getIncludedChats() val includedChats: Map<Long, List<Long>> = getIncludedChats()
val excludedChats: Map<Long, List<Long>> = getExcludedChats() val excludedChats: Map<Long, List<Long>> = getExcludedChats()
@@ -172,23 +171,26 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
showMutedChats = cursor.requireBoolean(ChatFolderTable.SHOW_MUTED), showMutedChats = cursor.requireBoolean(ChatFolderTable.SHOW_MUTED),
showIndividualChats = cursor.requireBoolean(ChatFolderTable.SHOW_INDIVIDUAL), showIndividualChats = cursor.requireBoolean(ChatFolderTable.SHOW_INDIVIDUAL),
showGroupChats = cursor.requireBoolean(ChatFolderTable.SHOW_GROUPS), showGroupChats = cursor.requireBoolean(ChatFolderTable.SHOW_GROUPS),
isMuted = cursor.requireBoolean(ChatFolderTable.IS_MUTED),
folderType = ChatFolderRecord.FolderType.deserialize(cursor.requireInt(ChatFolderTable.FOLDER_TYPE)), folderType = ChatFolderRecord.FolderType.deserialize(cursor.requireInt(ChatFolderTable.FOLDER_TYPE)),
includedChats = includedChats[id] ?: emptyList(), includedChats = includedChats[id] ?: emptyList(),
excludedChats = excludedChats[id] ?: emptyList() excludedChats = excludedChats[id] ?: emptyList()
) )
} }
if (includeUnreadAndMutedCount) { return folders
return folders.map { folder ->
folder.copy(
unreadCount = SignalDatabase.threads.getUnreadCountByChatFolder(folder),
isMuted = !SignalDatabase.threads.hasUnmutedChatsInFolder(folder)
)
}
} }
return folders /**
* Given a list of folders, maps a folder id to the folder's unread count and whether all the chats in the folder are muted
*/
fun getUnreadCountAndMutedStatusForFolders(folders: List<ChatFolderRecord>): HashMap<Long, Pair<Int, Boolean>> {
val map: HashMap<Long, Pair<Int, Boolean>> = hashMapOf()
folders.map { folder ->
val unreadCount = SignalDatabase.threads.getUnreadCountByChatFolder(folder)
val isMuted = !SignalDatabase.threads.hasUnmutedChatsInFolder(folder)
map[folder.id] = Pair(unreadCount, isMuted)
}
return map
} }
/** /**
@@ -263,7 +265,6 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
ChatFolderTable.SHOW_MUTED to chatFolder.showMutedChats, ChatFolderTable.SHOW_MUTED to chatFolder.showMutedChats,
ChatFolderTable.SHOW_INDIVIDUAL to chatFolder.showIndividualChats, ChatFolderTable.SHOW_INDIVIDUAL to chatFolder.showIndividualChats,
ChatFolderTable.SHOW_GROUPS to chatFolder.showGroupChats, ChatFolderTable.SHOW_GROUPS to chatFolder.showGroupChats,
ChatFolderTable.IS_MUTED to chatFolder.isMuted,
ChatFolderTable.POSITION to position ChatFolderTable.POSITION to position
) )
) )
@@ -304,8 +305,7 @@ class ChatFolderTables(context: Context?, databaseHelper: SignalDatabase?) : Dat
ChatFolderTable.SHOW_UNREAD to chatFolder.showUnread, ChatFolderTable.SHOW_UNREAD to chatFolder.showUnread,
ChatFolderTable.SHOW_MUTED to chatFolder.showMutedChats, ChatFolderTable.SHOW_MUTED to chatFolder.showMutedChats,
ChatFolderTable.SHOW_INDIVIDUAL to chatFolder.showIndividualChats, ChatFolderTable.SHOW_INDIVIDUAL to chatFolder.showIndividualChats,
ChatFolderTable.SHOW_GROUPS to chatFolder.showGroupChats, ChatFolderTable.SHOW_GROUPS to chatFolder.showGroupChats
ChatFolderTable.IS_MUTED to chatFolder.isMuted
) )
.where("${ChatFolderTable.ID} = ?", chatFolder.id) .where("${ChatFolderTable.ID} = ?", chatFolder.id)
.run(SQLiteDatabase.CONFLICT_IGNORE) .run(SQLiteDatabase.CONFLICT_IGNORE)

View File

@@ -291,15 +291,11 @@ class UnarchivedConversationListDataSourceTest {
position = -1, position = -1,
includedChats = emptyList(), includedChats = emptyList(),
excludedChats = emptyList(), excludedChats = emptyList(),
includedRecipients = emptySet(),
excludedRecipients = emptySet(),
showUnread = false, showUnread = false,
showMutedChats = false, showMutedChats = false,
showIndividualChats = false, showIndividualChats = false,
showGroupChats = false, showGroupChats = false,
isMuted = false, folderType = ChatFolderRecord.FolderType.ALL
folderType = ChatFolderRecord.FolderType.ALL,
unreadCount = 0
) )
} }
} }