diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index b4d2c085f6..1f83d7d68e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -230,7 +230,7 @@ object ChatModel { for (chat in chats.value.filter { it.remoteHostId == rhId }) { for (tag in PresetTagKind.entries) { - if (presetTagMatchesChat(tag, chat.chatInfo)) { + if (presetTagMatchesChat(tag, chat.chatInfo, chat.chatStats)) { newPresetTags[tag] = (newPresetTags[tag] ?: 0) + 1 } } @@ -270,17 +270,17 @@ object ChatModel { } } - private fun addPresetChatTags(chatInfo: ChatInfo) { + private fun addPresetChatTags(chatInfo: ChatInfo, chatStats: Chat.ChatStats) { for (tag in PresetTagKind.entries) { - if (presetTagMatchesChat(tag, chatInfo)) { + if (presetTagMatchesChat(tag, chatInfo, chatStats)) { presetTags[tag] = (presetTags[tag] ?: 0) + 1 } } } - fun removePresetChatTags(chatInfo: ChatInfo) { + fun removePresetChatTags(chatInfo: ChatInfo, chatStats: Chat.ChatStats) { for (tag in PresetTagKind.entries) { - if (presetTagMatchesChat(tag, chatInfo)) { + if (presetTagMatchesChat(tag, chatInfo, chatStats)) { val count = presetTags[tag] if (count != null) { presetTags[tag] = maxOf(0, count - 1) @@ -417,7 +417,7 @@ object ChatModel { updateChatInfo(rhId, cInfo) } else if (addMissing) { addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf())) - addPresetChatTags(cInfo) + addPresetChatTags(cInfo, Chat.ChatStats()) } } @@ -726,17 +726,17 @@ object ChatModel { } fun removeChat(rhId: Long?, id: String) { - var removed: ChatInfo? = null + var removed: Chat? = null chats.removeAll { val found = it.id == id && it.remoteHostId == rhId if (found) { - removed = it.chatInfo + removed = it } found } removed?.let { - removePresetChatTags(it) + removePresetChatTags(it.chatInfo, it.chatStats) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index b4a381809d..8f0b67cf6b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -49,7 +49,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.serialization.json.Json import kotlin.time.Duration.Companion.seconds -enum class PresetTagKind { FAVORITES, CONTACTS, GROUPS, BUSINESS, NOTES } +enum class PresetTagKind { FAVORITES, CONTACTS, GROUPS, BUSINESS, NOTES, GROUP_REPORTS } sealed class ActiveFilter { data class PresetTag(val tag: PresetTagKind) : ActiveFilter() @@ -928,6 +928,8 @@ private val TAG_MIN_HEIGHT = 35.dp private fun TagsView(searchText: MutableState) { val userTags = remember { chatModel.userTags } val presetTags = remember { chatModel.presetTags } + val collapsedPresetTags = presetTags.filter { presetCanBeCollapsed(it.key) } + val alwaysShownPresetTags = presetTags.filter { !presetCanBeCollapsed(it.key) } val activeFilter = remember { chatModel.activeChatTagFilter } val unreadTags = remember { chatModel.unreadTags } val rhId = chatModel.remoteHostId() @@ -935,13 +937,19 @@ private fun TagsView(searchText: MutableState) { val rowSizeModifier = Modifier.sizeIn(minHeight = TAG_MIN_HEIGHT * fontSizeSqrtMultiplier) TagsRow { - if (presetTags.size > 1) { + if (collapsedPresetTags.size > 1) { if (presetTags.size + userTags.value.size <= 3) { - PresetTagKind.entries.filter { t -> (presetTags[t] ?: 0) > 0 }.forEach { tag -> + val comparator = Comparator { t1, t2 -> + if (t1 == PresetTagKind.GROUP_REPORTS) -1 else if (t2 == PresetTagKind.GROUP_REPORTS) 1 else 0 + } + PresetTagKind.entries.filter { t -> (presetTags[t] ?: 0) > 0 }.sortedWith(comparator).forEach { tag -> ExpandedTagFilterView(tag) } } else { CollapsedTagsFilterView(searchText) + alwaysShownPresetTags.forEach { tag -> + ExpandedTagFilterView(tag.key) + } } } @@ -1106,7 +1114,7 @@ private fun CollapsedTagsFilterView(searchText: MutableState) { val showMenu = remember { mutableStateOf(false) } val selectedPresetTag = when (val af = activeFilter.value) { - is ActiveFilter.PresetTag -> af.tag + is ActiveFilter.PresetTag -> if (presetCanBeCollapsed(af.tag)) af.tag else null else -> null } @@ -1152,7 +1160,7 @@ private fun CollapsedTagsFilterView(searchText: MutableState) { ) } PresetTagKind.entries.forEach { tag -> - if ((presetTags[tag] ?: 0) > 0) { + if ((presetTags[tag] ?: 0) > 0 && presetCanBeCollapsed(tag)) { ItemPresetFilterAction(tag, tag == selectedPresetTag, showMenu, onCloseMenuAction) } } @@ -1214,13 +1222,13 @@ fun filteredChats( private fun filtered(chat: Chat, activeFilter: ActiveFilter?): Boolean = when (activeFilter) { - is ActiveFilter.PresetTag -> presetTagMatchesChat(activeFilter.tag, chat.chatInfo) + is ActiveFilter.PresetTag -> presetTagMatchesChat(activeFilter.tag, chat.chatInfo, chat.chatStats) is ActiveFilter.UserTag -> chat.chatInfo.chatTags?.contains(activeFilter.tag.chatTagId) ?: false is ActiveFilter.Unread -> chat.chatStats.unreadChat || chat.chatInfo.ntfsEnabled && chat.chatStats.unreadCount > 0 else -> true } -fun presetTagMatchesChat(tag: PresetTagKind, chatInfo: ChatInfo): Boolean = +fun presetTagMatchesChat(tag: PresetTagKind, chatInfo: ChatInfo, chatStats: Chat.ChatStats): Boolean = when (tag) { PresetTagKind.FAVORITES -> chatInfo.chatSettings?.favorite == true PresetTagKind.CONTACTS -> when (chatInfo) { @@ -1242,6 +1250,10 @@ fun presetTagMatchesChat(tag: PresetTagKind, chatInfo: ChatInfo): Boolean = is ChatInfo.Local -> !chatInfo.noteFolder.chatDeleted else -> false } + PresetTagKind.GROUP_REPORTS -> when (chatInfo) { + is ChatInfo.Group -> chatStats.reportsCount > 0 + else -> false + } } private fun presetTagLabel(tag: PresetTagKind, active: Boolean): Pair = @@ -1251,8 +1263,14 @@ private fun presetTagLabel(tag: PresetTagKind, active: Boolean): Pair (if (active) MR.images.ic_group_filled else MR.images.ic_group) to MR.strings.chat_list_groups PresetTagKind.BUSINESS -> (if (active) MR.images.ic_work_filled else MR.images.ic_work) to MR.strings.chat_list_businesses PresetTagKind.NOTES -> (if (active) MR.images.ic_folder_closed_filled else MR.images.ic_folder_closed) to MR.strings.chat_list_notes + PresetTagKind.GROUP_REPORTS -> (if (active) MR.images.ic_flag_filled else MR.images.ic_flag) to MR.strings.chat_list_group_reports } +private fun presetCanBeCollapsed(tag: PresetTagKind): Boolean = when (tag) { + PresetTagKind.GROUP_REPORTS -> false + else -> true +} + fun scrollToBottom(scope: CoroutineScope, listState: LazyListState) { scope.launch { try { listState.animateScrollToItem(0) } catch (e: Exception) { Log.e(TAG, e.stackTraceToString()) } } } diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 2912962ad9..5baf05e0ce 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -440,6 +440,7 @@ Groups Businesses Notes + Reports All Add list 1 report diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_flag_filled.svg b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_flag_filled.svg new file mode 100644 index 0000000000..a6f5a70618 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_flag_filled.svg @@ -0,0 +1 @@ + \ No newline at end of file