mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-04 15:07:32 +00:00
ui: ignore a second click while its page is still loading
These buttons open their page only after a load, and nothing covered or disabled the button meanwhile, so a second click during the load opened a second page: the reports bar, the support bar as a member, group info "Chat with admins", "Member reports", "Invite" and "Invite members", "Chats with members" rows, member info "Chat with member", and a channel's subscribers/owners and relays rows. Each opener now ignores a click while its load is in progress, on desktop and Android. The support and reports chats share one flag, as they share one secondary chat context, so the support chat a pending member's chat opens by itself is skipped while another of them loads. Group info's "Invite" and "Invite members" share one flag with the chat menu's "Invite members", as they open the same page.
This commit is contained in:
1 parent
813f914d3a
commit
306bbabe0d
4 files changed
+31
-6
No files matched your search
+5
-1
@@ -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) {
|
||||
|
||||
+11
-3
@@ -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<ConnectionStats?>(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
|
||||
)
|
||||
|
||||
+6
-1
@@ -77,8 +77,13 @@ fun ItemsReload(chatsCtx: ChatModel.ChatsContext,) {
|
||||
}
|
||||
|
||||
suspend fun showGroupReportsView(staleChatId: State<String?>, scrollToItemId: MutableState<Long?>, 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
|
||||
|
||||
+9
-1
@@ -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<String?>, scrollToItemId: MutableState<Long?>, 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) {
|
||||
|
||||
Reference in new issue
Block a user