From 678f4f5e87482283f4b7125307c2881ed2b51c0f Mon Sep 17 00:00:00 2001 From: JRoberts <8711996+jr-simplex@users.noreply.github.com> Date: Fri, 11 Nov 2022 21:08:02 +0400 Subject: [PATCH] android: prevent exception on concurrent attempts to add members to group (#1348) * android: Random crashes fix (cherry picked from commit 0f7789c4113ea8ef587d393a65ec91def928b003) * Better way of disabling members adding ato a group (cherry picked from commit 96ca7f0d85b20464ac8a11d2dc68405c4a9b7ba4) * check outside Co-authored-by: Avently <7953703+avently@users.noreply.github.com> --- .../views/chat/group/AddGroupMembersView.kt | 58 +++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt index b86389bff..502e43b7b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt @@ -14,7 +14,6 @@ import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.TheaterComedy import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* -import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -35,14 +34,16 @@ import chat.simplex.app.views.usersettings.SettingsActionItem fun AddGroupMembersView(groupInfo: GroupInfo, chatModel: ChatModel, close: () -> Unit) { val selectedContacts = remember { mutableStateListOf() } val selectedRole = remember { mutableStateOf(GroupMemberRole.Member) } - + var allowModifyMembers by remember { mutableStateOf(true) } BackHandler(onBack = close) AddGroupMembersLayout( groupInfo = groupInfo, contactsToAdd = getContactsToAdd(chatModel), selectedContacts = selectedContacts, selectedRole = selectedRole, + allowModifyMembers = allowModifyMembers, inviteMembers = { + allowModifyMembers = false withApi { for (contactId in selectedContacts) { val member = chatModel.controller.apiAddMember(groupInfo.groupId, contactId, selectedRole.value) @@ -79,8 +80,9 @@ fun getContactsToAdd(chatModel: ChatModel): List { fun AddGroupMembersLayout( groupInfo: GroupInfo, contactsToAdd: List, - selectedContacts: SnapshotStateList, + selectedContacts: List, selectedRole: MutableState, + allowModifyMembers: Boolean, inviteMembers: () -> Unit, clearSelection: () -> Unit, addContact: (Long) -> Unit, @@ -119,18 +121,18 @@ fun AddGroupMembersLayout( } else { SectionView { SectionItemView { - RoleSelectionRow(groupInfo, selectedRole) + RoleSelectionRow(groupInfo, selectedRole, allowModifyMembers) } SectionDivider() - InviteMembersButton(inviteMembers, disabled = selectedContacts.isEmpty()) + InviteMembersButton(inviteMembers, disabled = selectedContacts.isEmpty() || !allowModifyMembers) } SectionCustomFooter { - InviteSectionFooter(selectedContactsCount = selectedContacts.count(), clearSelection) + InviteSectionFooter(selectedContactsCount = selectedContacts.size, allowModifyMembers, clearSelection) } SectionSpacer() SectionView { - ContactList(contacts = contactsToAdd, selectedContacts, groupInfo, addContact, removeContact) + ContactList(contacts = contactsToAdd, selectedContacts, groupInfo, allowModifyMembers, addContact, removeContact) } SectionSpacer() } @@ -138,7 +140,7 @@ fun AddGroupMembersLayout( } @Composable -private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState) { +private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState, enabled: Boolean) { Row( Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, @@ -150,7 +152,7 @@ private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState Unit, disabled: Boolean) { } @Composable -fun InviteSectionFooter(selectedContactsCount: Int, clearSelection: () -> Unit) { +fun InviteSectionFooter(selectedContactsCount: Int, enabled: Boolean, clearSelection: () -> Unit) { Row( Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, @@ -182,11 +184,11 @@ fun InviteSectionFooter(selectedContactsCount: Int, clearSelection: () -> Unit) fontSize = 12.sp ) Box( - Modifier.clickable { clearSelection() } + Modifier.clickable { if (enabled) clearSelection() } ) { Text( stringResource(R.string.clear_contacts_selection_button), - color = MaterialTheme.colors.primary, + color = if (enabled) MaterialTheme.colors.primary else HighOrLowlight, fontSize = 12.sp ) } @@ -203,8 +205,9 @@ fun InviteSectionFooter(selectedContactsCount: Int, clearSelection: () -> Unit) @Composable fun ContactList( contacts: List, - selectedContacts: SnapshotStateList, + selectedContacts: List, groupInfo: GroupInfo, + enabled: Boolean, addContact: (Long) -> Unit, removeContact: (Long) -> Unit ) { @@ -212,7 +215,8 @@ fun ContactList( contacts.forEachIndexed { index, contact -> ContactCheckRow( contact, groupInfo, addContact, removeContact, - checked = selectedContacts.contains(contact.apiId) + checked = selectedContacts.contains(contact.apiId), + enabled = enabled, ) if (index < contacts.lastIndex) { SectionDivider() @@ -227,7 +231,8 @@ fun ContactCheckRow( groupInfo: GroupInfo, addContact: (Long) -> Unit, removeContact: (Long) -> Unit, - checked: Boolean + checked: Boolean, + enabled: Boolean, ) { val prohibitedToInviteIncognito = !groupInfo.membership.memberIncognito && contact.contactConnIncognito val icon: ImageVector @@ -237,19 +242,23 @@ fun ContactCheckRow( iconColor = HighOrLowlight } else if (checked) { icon = Icons.Filled.CheckCircle - iconColor = MaterialTheme.colors.primary + iconColor = if (enabled) MaterialTheme.colors.primary else HighOrLowlight } else { icon = Icons.Outlined.Circle iconColor = HighOrLowlight } - SectionItemView(click = { - if (prohibitedToInviteIncognito) { - showProhibitedToInviteIncognitoAlertDialog() - } else if (!checked) - addContact(contact.apiId) - else - removeContact(contact.apiId) - }) { + SectionItemView( + click = if (enabled) { + { + if (prohibitedToInviteIncognito) { + showProhibitedToInviteIncognitoAlertDialog() + } else if (!checked) + addContact(contact.apiId) + else + removeContact(contact.apiId) + } + } else null + ) { ProfileImage(size = 36.dp, contact.image) Spacer(Modifier.width(DEFAULT_SPACE_AFTER_ICON)) Text( @@ -282,6 +291,7 @@ fun PreviewAddGroupMembersLayout() { contactsToAdd = listOf(Contact.sampleData, Contact.sampleData, Contact.sampleData), selectedContacts = remember { mutableStateListOf() }, selectedRole = remember { mutableStateOf(GroupMemberRole.Admin) }, + allowModifyMembers = true, inviteMembers = {}, clearSelection = {}, addContact = {},