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 2d4987e63b..3a84f225cc 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 @@ -376,7 +376,7 @@ object ChatModel { updateContact(rhId, updatedContact) } - suspend fun updateGroup(rhId: Long?, groupInfo: GroupInfo) = updateChat(rhId, ChatInfo.Group(groupInfo, groupChatScope = null)) // TODO [knocking] review + suspend fun updateGroup(rhId: Long?, groupInfo: GroupInfo) = updateChat(rhId, ChatInfo.Group(groupInfo, groupChatScope = null)) private suspend fun updateChat(rhId: Long?, cInfo: ChatInfo, addMissing: Boolean = true) { if (hasChat(rhId, cInfo.id)) { @@ -567,7 +567,6 @@ object ChatModel { } } - // TODO [knocking] why does this function not use `withContext(Dispatchers.Main) { ... }` ? fun removeChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) { // update chat list if (cInfo.groupChatScope() == null) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index a8ae4f8302..b8f57259bf 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -191,6 +191,7 @@ class AppPreferences { val oneHandUICardShown = mkBoolPreference(SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN, false) val addressCreationCardShown = mkBoolPreference(SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN, false) val showMuteProfileAlert = mkBoolPreference(SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT, true) + val showReportsInSupportChatAlert = mkBoolPreference(SHARED_PREFS_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT, true) val appLanguage = mkStrPreference(SHARED_PREFS_APP_LANGUAGE, null) val appUpdateChannel = mkEnumPreference(SHARED_PREFS_APP_UPDATE_CHANNEL, AppUpdatesChannel.DISABLED) { AppUpdatesChannel.entries.firstOrNull { it.name == this } } val appSkippedUpdate = mkStrPreference(SHARED_PREFS_APP_SKIPPED_UPDATE, "") @@ -273,6 +274,7 @@ class AppPreferences { liveMessageAlertShown to false, showHiddenProfilesNotice to true, showMuteProfileAlert to true, + showReportsInSupportChatAlert to true, showDeleteConversationNotice to true, showDeleteContactNotice to true, ) @@ -431,6 +433,7 @@ class AppPreferences { private const val SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN = "OneHandUICardShown" private const val SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN = "AddressCreationCardShown" private const val SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT = "ShowMuteProfileAlert" + private const val SHARED_PREFS_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT = "ShowReportsInSupportChatAlert" private const val SHARED_PREFS_STORE_DB_PASSPHRASE = "StoreDBPassphrase" private const val SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE = "InitialRandomDBPassphrase" private const val SHARED_PREFS_ENCRYPTED_DB_PASSPHRASE = "EncryptedDBPassphrase" @@ -2711,7 +2714,6 @@ object ChatController { } is CR.MemberAcceptedByOther -> if (active(r.user)) { - // TODO [knocking] update secondary context? withContext(Dispatchers.Main) { chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index d3dad36d62..a440266958 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -124,7 +124,7 @@ fun ChatView( val supportChatInfo = ChatInfo.Group(chatInfo.groupInfo, groupChatScope = scopeInfo) showMemberSupportChatView( chatModel.chatId, - scrollToItemId = mutableStateOf(null), + scrollToItemId, supportChatInfo, scopeInfo ) @@ -371,7 +371,7 @@ fun ChatView( } ModalManager.end.showModalCloseable(true) { close -> remember { derivedStateOf { chatModel.getGroupMember(member.groupMemberId) } }.value?.let { mem -> - GroupMemberInfoView(chatRh, groupInfo, mem, stats, code, chatModel, close, close) + GroupMemberInfoView(chatRh, groupInfo, mem, scrollToItemId, stats, code, chatModel, close, close) } } } @@ -806,7 +806,7 @@ fun ChatLayout( } } } - if (chatsCtx.contentTag == MsgContentTag.Report) { // TODO [knocking] similar bar for support chats, without archiveItems? + if (chatsCtx.contentTag == MsgContentTag.Report) { Column( Modifier .layoutId(CHAT_COMPOSE_LAYOUT_ID) @@ -1322,8 +1322,8 @@ fun BoxScope.ChatItemsList( val chatInfoUpdated = rememberUpdatedState(chatInfo) val scope = rememberCoroutineScope() val scrollToItem: (Long) -> Unit = remember { - // In group reports just set the itemId to scroll to so the main ChatView will handle scrolling - if (chatsCtx.contentTag == MsgContentTag.Report) return@remember { scrollToItemId.value = it } + // In secondary chat just set the itemId to scroll to so the main ChatView will handle scrolling + if (chatsCtx.contentTag == MsgContentTag.Report || chatsCtx.secondaryContextFilter is SecondaryContextFilter.GroupChatScopeContext) return@remember { scrollToItemId.value = it } scrollToItem(searchValue, loadingMoreItems, animatedScrollingInProgress, highlightedItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages) } val scrollToQuotedItemFromItem: (Long) -> Unit = remember { findQuotedItemFromItem(chatsCtx, remoteHostIdUpdated, chatInfoUpdated, scope, scrollToItem) } @@ -2167,7 +2167,6 @@ private fun FloatingDate( } } -// TODO [knocking] same for member support chats? @Composable private fun SaveReportsStateOnDispose(chatsCtx: ChatModel.ChatsContext, listState: State) { DisposableEffect(Unit) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt index 0f96a551d1..f377540a95 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt @@ -568,17 +568,21 @@ fun ComposeView( } } + fun showReportsInSupportChatAlert() { + AlertManager.shared.showAlertDialog( + title = generalGetString(MR.strings.report_sent_alert_title), + text = generalGetString(MR.strings.report_sent_alert_msg_view_in_support_chat), + confirmText = generalGetString(MR.strings.ok), + dismissText = generalGetString(MR.strings.dont_show_again), + onDismiss = { + chatModel.controller.appPrefs.showReportsInSupportChatAlert.set(false) + }, + ) + } + suspend fun sendReport(reportReason: ReportReason, chatItemId: Long): List? { val cItems = chatModel.controller.apiReportMessage(chat.remoteHostId, chat.chatInfo.apiId, chatItemId, reportReason, msgText) - if (cItems != null) { - // TODO [knocking] create report chat items in support scope - withContext(Dispatchers.Main) { - cItems.forEach { chatItem -> - chatsCtx.addChatItem(chat.remoteHostId, chat.chatInfo, chatItem.chatItem) - } - } - } - + if (chatModel.controller.appPrefs.showReportsInSupportChatAlert.get()) showReportsInSupportChatAlert() return cItems?.map { it.chatItem } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt index c934b6d5d7..ac722783a3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt @@ -101,21 +101,21 @@ fun SelectedItemsButtonsToolbar( ) } - IconButton({ moderateItems() }, Modifier.alpha(if (canModerate.value) 1f else 0f), enabled = moderateEnabled.value && !deleteCountProhibited.value) { + IconButton({ moderateItems() }, Modifier.alpha(if (canModerate.value) 1f else 0f), enabled = moderateEnabled.value && !deleteCountProhibited.value && chatsCtx.secondaryContextFilter == null) { Icon( painterResource(MR.images.ic_flag), null, Modifier.size(22.dp), - tint = if (!moderateEnabled.value || deleteCountProhibited.value) MaterialTheme.colors.secondary else MaterialTheme.colors.error + tint = if (!moderateEnabled.value || deleteCountProhibited.value || chatsCtx.secondaryContextFilter != null) MaterialTheme.colors.secondary else MaterialTheme.colors.error ) } - IconButton({ forwardItems() }, enabled = forwardEnabled.value && !forwardCountProhibited.value) { + IconButton({ forwardItems() }, enabled = forwardEnabled.value && !forwardCountProhibited.value && chatsCtx.secondaryContextFilter == null) { Icon( painterResource(MR.images.ic_forward), null, Modifier.size(22.dp), - tint = if (!forwardEnabled.value || forwardCountProhibited.value) MaterialTheme.colors.secondary else MaterialTheme.colors.primary + tint = if (!forwardEnabled.value || forwardCountProhibited.value || chatsCtx.secondaryContextFilter != null) MaterialTheme.colors.secondary else MaterialTheme.colors.primary ) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index aeb4041008..a28aca2236 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -78,8 +78,6 @@ fun ModalData.GroupChatInfoView( .filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved } .sortedByDescending { it.memberRole } - Log.e(TAG, "######### GroupChatInfoView chatModel.groupMembers length = ${chatModel.groupMembers.value.count()}") - GroupChatInfoLayout( chat, groupInfo, @@ -129,7 +127,7 @@ fun ModalData.GroupChatInfoView( } ModalManager.end.showModalCloseable(true) { closeCurrent -> remember { derivedStateOf { chatModel.getGroupMember(member.groupMemberId) } }.value?.let { mem -> - GroupMemberInfoView(rhId, groupInfo, mem, stats, code, chatModel, closeCurrent) { + GroupMemberInfoView(rhId, groupInfo, mem, scrollToItemId, stats, code, chatModel, closeCurrent) { closeCurrent() close() } @@ -148,6 +146,7 @@ fun ModalData.GroupChatInfoView( MemberSupportView( chat, groupInfo, + scrollToItemId, close ) } @@ -330,9 +329,11 @@ fun AddGroupMembersButton( } @Composable -fun UserSupportChatButton(groupInfo: GroupInfo) { +fun UserSupportChatButton( + groupInfo: GroupInfo, + scrollToItemId: MutableState +) { val scope = rememberCoroutineScope() - val scrollToItemId: MutableState = remember { mutableStateOf(null) } SettingsActionItem( painterResource(MR.images.ic_flag), @@ -463,7 +464,7 @@ fun ModalData.GroupChatInfoLayout( SectionView { if (groupInfo.membership.supportChat != null) { anyTopSectionRowShow = true - UserSupportChatButton(groupInfo) + UserSupportChatButton(groupInfo, scrollToItemId) } if (groupInfo.businessChat == null && groupInfo.membership.memberRole >= GroupMemberRole.Moderator) { anyTopSectionRowShow = true @@ -738,7 +739,7 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) { @Composable private fun MemberSupportButton(onClick: () -> Unit) { SettingsActionItem( - painterResource(MR.images.ic_flag), // TODO [knocking] change icon + painterResource(MR.images.ic_flag), stringResource(MR.strings.member_support), click = onClick ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index a6c21574ef..7a3bd60e14 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -45,6 +45,7 @@ fun GroupMemberInfoView( rhId: Long?, groupInfo: GroupInfo, member: GroupMember, + scrollToItemId: MutableState, connectionStats: ConnectionStats?, connectionCode: String?, chatModel: ChatModel, @@ -79,6 +80,7 @@ fun GroupMemberInfoView( rhId = rhId, groupInfo, member, + scrollToItemId, connStats, newRole, developerTools, @@ -269,6 +271,7 @@ fun GroupMemberInfoLayout( rhId: Long?, groupInfo: GroupInfo, member: GroupMember, + scrollToItemId: MutableState, connStats: MutableState, newRole: MutableState, developerTools: Boolean, @@ -302,7 +305,6 @@ fun GroupMemberInfoLayout( @Composable fun SupportChatButton() { val scope = rememberCoroutineScope() - val scrollToItemId: MutableState = remember { mutableStateOf(null) } // TODO [knocking] scroll to report from support chat? SettingsActionItem( painterResource(MR.images.ic_flag), @@ -908,6 +910,7 @@ fun PreviewGroupMemberInfoLayout() { rhId = null, groupInfo = GroupInfo.sampleData, member = GroupMember.sampleData, + scrollToItemId = remember { mutableStateOf(null) }, connStats = remember { mutableStateOf(null) }, newRole = remember { mutableStateOf(GroupMemberRole.Member) }, developerTools = false, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt index bb05ede81b..2cc2402c0a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt @@ -15,7 +15,15 @@ import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.flow.* @Composable -private fun GroupReportsView(reportsChatsCtx: ChatModel.ChatsContext, staleChatId: State, scrollToItemId: MutableState) { +private fun GroupReportsView( + reportsChatsCtx: ChatModel.ChatsContext, + staleChatId: State, + scrollToItemId: MutableState, + close: () -> Unit +) { + KeyChangeEffect(chatModel.chatId.value) { + close() + } ChatView(reportsChatsCtx, staleChatId, scrollToItemId, onComposed = {}) } @@ -75,7 +83,7 @@ suspend fun showGroupReportsView(staleChatId: State, scrollToItemId: Mu ModalView({}, showAppBar = false) { val chatInfo = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chatModel.chatId.value }?.chatInfo } }.value if (chatInfo is ChatInfo.Group && chatInfo.groupInfo.canModerate) { - GroupReportsView(reportsChatsCtx, staleChatId, scrollToItemId) + GroupReportsView(reportsChatsCtx, staleChatId, scrollToItemId, close) } else { LaunchedEffect(Unit) { close() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportChatView.kt index 86bb9b7255..3196cae15c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportChatView.kt @@ -21,7 +21,14 @@ import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource @Composable -private fun MemberSupportChatView(memberSupportChatsCtx: ChatModel.ChatsContext, staleChatId: State, scrollToItemId: MutableState) { +private fun MemberSupportChatView( + memberSupportChatsCtx: ChatModel.ChatsContext, + staleChatId: State, + scrollToItemId: MutableState +) { + KeyChangeEffect(chatModel.chatId.value) { + ModalManager.end.closeModals() + } ChatView(memberSupportChatsCtx, staleChatId, scrollToItemId, onComposed = {}) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportView.kt index 55c2126771..bd0e866c03 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/MemberSupportView.kt @@ -34,15 +34,17 @@ import kotlinx.coroutines.launch fun ModalData.MemberSupportView( chat: Chat, groupInfo: GroupInfo, + scrollToItemId: MutableState, close: () -> Unit ) { - KeyChangeEffect(chat.id) { - close() + KeyChangeEffect(chatModel.chatId.value) { + ModalManager.end.closeModals() } ModalView(close = close) { MemberSupportViewLayout( chat, - groupInfo + groupInfo, + scrollToItemId ) } } @@ -50,11 +52,11 @@ fun ModalData.MemberSupportView( @Composable private fun ModalData.MemberSupportViewLayout( chat: Chat, - groupInfo: GroupInfo + groupInfo: GroupInfo, + scrollToItemId: MutableState ) { val oneHandUI = remember { ChatController.appPrefs.oneHandUI.state } val scope = rememberCoroutineScope() - val scrollToItemId: MutableState = remember { mutableStateOf(null) } // TODO [knocking] scroll to report from support chat? val membersWithChats = remember { chatModel.groupMembers }.value .filter { it.supportChat != null && it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved } 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 53ae1abb50..41cde59499 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -509,6 +509,8 @@ Report violation: only group moderators will see it. Report content: only group moderators will see it. Report other: only group moderators will see it. + Report sent to moderators + You can view your reports in Support Chat. Image