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 960efd0350..84b2ee971c 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 @@ -52,6 +52,7 @@ import kotlinx.coroutines.flow.* import kotlinx.datetime.* import java.io.File import java.net.URI +import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.* @Stable @@ -3233,7 +3234,10 @@ private fun findQuotedItemFromItem( val chatViewScrollState = MutableStateFlow(false) +val addGroupMembersOpening = AtomicBoolean(false) + fun addGroupMembers(groupInfo: GroupInfo, rhId: Long?, view: Any? = null, close: (() -> Unit)? = null) { + if (!addGroupMembersOpening.compareAndSet(false, true)) return hideKeyboard(view) withBGApi { setGroupMembers(rhId, groupInfo, chatModel) @@ -3241,7 +3245,7 @@ fun addGroupMembers(groupInfo: GroupInfo, rhId: Long?, view: Any? = null, close: ModalManager.end.showModalCloseable(showClose = true) { close -> AddGroupMembersView(rhId, groupInfo, false, chatModel, close) } - } + }.invokeOnCompletion { addGroupMembersOpening.set(false) } } fun openGroupLink(groupInfo: GroupInfo, rhId: Long?, view: Any? = null, close: (() -> Unit)? = null) { 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 973b684d38..13265bb0e6 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 @@ -52,6 +52,7 @@ import chat.simplex.common.views.newchat.SimpleXLinkQRCode import chat.simplex.res.* import dev.icerock.moko.resources.StringResource import kotlinx.coroutines.* +import java.util.concurrent.atomic.AtomicBoolean const val SMALL_GROUPS_RCPS_MEM_LIMIT: Int = 20 val MEMBER_ROW_AVATAR_SIZE = 42.dp @@ -113,6 +114,7 @@ fun ModalData.GroupChatInfoView( appBar, scrollToItemId, addMembers = { + if (!addGroupMembersOpening.compareAndSet(false, true)) return@GroupChatInfoLayout scope.launch(Dispatchers.Default) { setGroupMembers(rhId, groupInfo, chatModel) if (!isActive) return@launch @@ -120,7 +122,7 @@ fun ModalData.GroupChatInfoView( ModalManager.end.showModalCloseable(showClose = true) { close -> AddGroupMembersView(rhId, groupInfo, false, chatModel, close) } - } + }.invokeOnCompletion { addGroupMembersOpening.set(false) } }, showMemberInfo = { member, groupRelay -> val connStats = mutableStateOf(null) @@ -1293,6 +1295,8 @@ private fun ShareViaChatButton(onClick: () -> Unit) { } } +private val channelMembersOpening = AtomicBoolean(false) + @Composable private fun ChannelMembersButton(rhId: Long?, groupInfo: GroupInfo, showMemberInfo: (GroupMember, GroupRelay?) -> Unit) { val title = if (groupInfo.isOwner) { @@ -1304,29 +1308,33 @@ private fun ChannelMembersButton(rhId: Long?, groupInfo: GroupInfo, showMemberIn painterResource(MR.images.ic_group), title, click = { + if (!channelMembersOpening.compareAndSet(false, true)) return@SettingsActionItem withBGApi { setGroupMembers(rhId, groupInfo, chatModel) ModalManager.end.showModalCloseable(true) { close -> ChannelMembersView(rhId, groupInfo, chatModel, close) { member -> showMemberInfo(member, null) } } - } + }.invokeOnCompletion { channelMembersOpening.set(false) } }, iconColor = MaterialTheme.colors.secondary ) } +private val channelRelaysOpening = AtomicBoolean(false) + @Composable private fun ChannelRelaysButton(rhId: Long?, groupInfo: GroupInfo, showMemberInfo: (GroupMember, GroupRelay?) -> Unit) { SettingsActionItem( painterResource(MR.images.ic_wifi_tethering), stringResource(MR.strings.button_channel_relays), click = { + if (!channelRelaysOpening.compareAndSet(false, true)) return@SettingsActionItem withBGApi { setGroupMembers(rhId, groupInfo, chatModel) ModalManager.end.showModalCloseable(true) { close -> ChannelRelaysView(rhId, groupInfo, chatModel, close, showMemberInfo) } - } + }.invokeOnCompletion { channelRelaysOpening.set(false) } }, iconColor = MaterialTheme.colors.secondary ) 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 0c272ae98d..adb313fd70 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 @@ -77,8 +77,13 @@ fun ItemsReload(chatsCtx: ChatModel.ChatsContext,) { } suspend fun showGroupReportsView(staleChatId: State, scrollToItemId: MutableState, chatInfo: ChatInfo) { + if (!secondaryChatOpening.compareAndSet(false, true)) return val reportsChatsCtx = ChatModel.ChatsContext(secondaryContextFilter = SecondaryContextFilter.MsgContentTagContext(MsgContentTag.Report)) - openChat(secondaryChatsCtx = reportsChatsCtx, chatModel.remoteHostId(), chatInfo) + try { + openChat(secondaryChatsCtx = reportsChatsCtx, chatModel.remoteHostId(), chatInfo) + } finally { + secondaryChatOpening.set(false) + } ModalManager.end.showCustomModal(true, id = ModalViewId.SECONDARY_CHAT) { close -> ModalView({}, showAppBar = false) { val chatInfo = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chatModel.chatId.value }?.chatInfo } }.value 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 8e0b3321b5..260cf866f5 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 @@ -19,6 +19,7 @@ import chat.simplex.common.views.helpers.* import chat.simplex.res.* import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource +import java.util.concurrent.atomic.AtomicBoolean @Composable private fun MemberSupportChatView( @@ -156,9 +157,16 @@ fun MemberSupportChatToolbarTitle(member: GroupMember, imageSize: Dp = 40.dp, ic } } +val secondaryChatOpening = AtomicBoolean(false) + suspend fun showMemberSupportChatView(staleChatId: State, scrollToItemId: MutableState, chatInfo: ChatInfo, scopeInfo: GroupChatScopeInfo) { + if (!secondaryChatOpening.compareAndSet(false, true)) return val memberSupportChatsCtx = ChatModel.ChatsContext(secondaryContextFilter = SecondaryContextFilter.GroupChatScopeContext(scopeInfo)) - openChat(secondaryChatsCtx = memberSupportChatsCtx, chatModel.remoteHostId(), chatInfo) + try { + openChat(secondaryChatsCtx = memberSupportChatsCtx, chatModel.remoteHostId(), chatInfo) + } finally { + secondaryChatOpening.set(false) + } ModalManager.end.showCustomModal(true, id = ModalViewId.SECONDARY_CHAT) { close -> ModalView({}, showAppBar = false) { if (chatInfo is ChatInfo.Group && chatInfo.groupChatScope != null) {