android: Change member role (#1186)
* android: Change member role * Don't do anything when role is unchanged
This commit is contained in:
committed by
GitHub
parent
af17daafaa
commit
ca423024c5
@@ -733,12 +733,18 @@ class GroupMember (
|
||||
GroupMemberStatus.MemCreator -> true
|
||||
}
|
||||
|
||||
fun canBeRemoved(membership: GroupMember): Boolean {
|
||||
val userRole = membership.memberRole
|
||||
fun canBeRemoved(groupInfo: GroupInfo): Boolean {
|
||||
val userRole = groupInfo.membership.memberRole
|
||||
return memberStatus != GroupMemberStatus.MemRemoved && memberStatus != GroupMemberStatus.MemLeft
|
||||
&& userRole >= GroupMemberRole.Admin && userRole >= memberRole && membership.memberCurrent
|
||||
&& userRole >= GroupMemberRole.Admin && userRole >= memberRole && groupInfo.membership.memberCurrent
|
||||
}
|
||||
|
||||
fun canChangeRoleTo(groupInfo: GroupInfo): List<GroupMemberRole>? =
|
||||
if (!canBeRemoved(groupInfo)) null
|
||||
else groupInfo.membership.memberRole.let { userRole ->
|
||||
GroupMemberRole.values().filter { it <= userRole }
|
||||
}
|
||||
|
||||
val memberIncognito = memberProfile.profileId != memberContactProfileId
|
||||
|
||||
companion object {
|
||||
@@ -1025,6 +1031,8 @@ data class ChatItem (
|
||||
is RcvGroupEvent.GroupDeleted -> false
|
||||
is RcvGroupEvent.MemberAdded -> false
|
||||
is RcvGroupEvent.MemberLeft -> false
|
||||
is RcvGroupEvent.MemberRole -> true
|
||||
is RcvGroupEvent.UserRole -> false
|
||||
is RcvGroupEvent.MemberDeleted -> false
|
||||
}
|
||||
is CIContent.SndGroupEventContent -> true
|
||||
@@ -1527,6 +1535,8 @@ sealed class RcvGroupEvent() {
|
||||
@Serializable @SerialName("memberAdded") class MemberAdded(val groupMemberId: Long, val profile: Profile): RcvGroupEvent()
|
||||
@Serializable @SerialName("memberConnected") class MemberConnected(): RcvGroupEvent()
|
||||
@Serializable @SerialName("memberLeft") class MemberLeft(): RcvGroupEvent()
|
||||
@Serializable @SerialName("memberRole") class MemberRole(val groupMemberId: Long, val profile: Profile, val role: GroupMemberRole): RcvGroupEvent()
|
||||
@Serializable @SerialName("userRole") class UserRole(val role: GroupMemberRole): RcvGroupEvent()
|
||||
@Serializable @SerialName("memberDeleted") class MemberDeleted(val groupMemberId: Long, val profile: Profile): RcvGroupEvent()
|
||||
@Serializable @SerialName("userDeleted") class UserDeleted(): RcvGroupEvent()
|
||||
@Serializable @SerialName("groupDeleted") class GroupDeleted(): RcvGroupEvent()
|
||||
@@ -1536,6 +1546,8 @@ sealed class RcvGroupEvent() {
|
||||
is MemberAdded -> String.format(generalGetString(R.string.rcv_group_event_member_added), profile.profileViewName)
|
||||
is MemberConnected -> generalGetString(R.string.rcv_group_event_member_connected)
|
||||
is MemberLeft -> generalGetString(R.string.rcv_group_event_member_left)
|
||||
is MemberRole -> String.format(generalGetString(R.string.member_role), profile.profileViewName, role.text)
|
||||
is UserRole -> String.format(generalGetString(R.string.your_member_role), role.text)
|
||||
is MemberDeleted -> String.format(generalGetString(R.string.rcv_group_event_member_deleted), profile.profileViewName)
|
||||
is UserDeleted -> generalGetString(R.string.rcv_group_event_user_deleted)
|
||||
is GroupDeleted -> generalGetString(R.string.rcv_group_event_group_deleted)
|
||||
@@ -1545,11 +1557,15 @@ sealed class RcvGroupEvent() {
|
||||
|
||||
@Serializable
|
||||
sealed class SndGroupEvent() {
|
||||
@Serializable @SerialName("memberRole") class MemberRole(val groupMemberId: Long, val profile: Profile, val role: GroupMemberRole): SndGroupEvent()
|
||||
@Serializable @SerialName("userRole") class UserRole(val role: GroupMemberRole): SndGroupEvent()
|
||||
@Serializable @SerialName("memberDeleted") class MemberDeleted(val groupMemberId: Long, val profile: Profile): SndGroupEvent()
|
||||
@Serializable @SerialName("userLeft") class UserLeft(): SndGroupEvent()
|
||||
@Serializable @SerialName("groupUpdated") class GroupUpdated(val groupProfile: GroupProfile): SndGroupEvent()
|
||||
|
||||
val text: String get() = when (this) {
|
||||
is MemberRole -> String.format(generalGetString(R.string.member_role), profile.profileViewName, role.text)
|
||||
is UserRole -> String.format(generalGetString(R.string.your_member_role), role.text)
|
||||
is MemberDeleted -> String.format(generalGetString(R.string.snd_group_event_member_deleted), profile.profileViewName)
|
||||
is UserLeft -> generalGetString(R.string.snd_group_event_user_left)
|
||||
is GroupUpdated -> generalGetString(R.string.snd_group_event_group_profile_updated)
|
||||
|
||||
@@ -785,12 +785,27 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiRemoveMember(groupId: Long, memberId: Long): GroupMember? {
|
||||
val r = sendCmd(CC.ApiRemoveMember(groupId, memberId))
|
||||
if (r is CR.UserDeletedMember) return r.member
|
||||
Log.e(TAG, "apiRemoveMember bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
suspend fun apiRemoveMember(groupId: Long, memberId: Long): GroupMember? =
|
||||
when (val r = sendCmd(CC.ApiRemoveMember(groupId, memberId))) {
|
||||
is CR.UserDeletedMember -> r.member
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiRemoveMember", generalGetString(R.string.error_removing_member), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiMemberRole(groupId: Long, memberId: Long, memberRole: GroupMemberRole): GroupMember =
|
||||
when (val r = sendCmd(CC.ApiMemberRole(groupId, memberId, memberRole))) {
|
||||
is CR.MemberRoleUser -> r.member
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiMemberRole", generalGetString(R.string.error_changing_role), r)
|
||||
}
|
||||
throw Exception("failed to change member role: ${r.responseType} ${r.details}")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiLeaveGroup(groupId: Long): GroupInfo? {
|
||||
val r = sendCmd(CC.ApiLeaveGroup(groupId))
|
||||
@@ -1342,7 +1357,7 @@ sealed class CC {
|
||||
class NewGroup(val groupProfile: GroupProfile): CC()
|
||||
class ApiAddMember(val groupId: Long, val contactId: Long, val memberRole: GroupMemberRole): CC()
|
||||
class ApiJoinGroup(val groupId: Long): CC()
|
||||
// class ApiMemberRole(val groupId: Long, val memberId: Long, val memberRole: GroupMemberRole): CC()
|
||||
class ApiMemberRole(val groupId: Long, val memberId: Long, val memberRole: GroupMemberRole): CC()
|
||||
class ApiRemoveMember(val groupId: Long, val memberId: Long): CC()
|
||||
class ApiLeaveGroup(val groupId: Long): CC()
|
||||
class ApiListMembers(val groupId: Long): CC()
|
||||
@@ -1400,6 +1415,7 @@ sealed class CC {
|
||||
is NewGroup -> "/_group ${json.encodeToString(groupProfile)}"
|
||||
is ApiAddMember -> "/_add #$groupId $contactId ${memberRole.memberRole}"
|
||||
is ApiJoinGroup -> "/_join #$groupId"
|
||||
is ApiMemberRole -> "/_member role #$groupId $memberId ${memberRole.memberRole}"
|
||||
is ApiRemoveMember -> "/_remove #$groupId $memberId"
|
||||
is ApiLeaveGroup -> "/_leave #$groupId"
|
||||
is ApiListMembers -> "/_members #$groupId"
|
||||
@@ -1458,6 +1474,7 @@ sealed class CC {
|
||||
is NewGroup -> "newGroup"
|
||||
is ApiAddMember -> "apiAddMember"
|
||||
is ApiJoinGroup -> "apiJoinGroup"
|
||||
is ApiMemberRole -> "apiMemberRole"
|
||||
is ApiRemoveMember -> "apiRemoveMember"
|
||||
is ApiLeaveGroup -> "apiLeaveGroup"
|
||||
is ApiListMembers -> "apiListMembers"
|
||||
@@ -1710,6 +1727,8 @@ sealed class CR {
|
||||
@Serializable @SerialName("receivedGroupInvitation") class ReceivedGroupInvitation(val groupInfo: GroupInfo, val contact: Contact, val memberRole: GroupMemberRole): CR()
|
||||
@Serializable @SerialName("groupDeletedUser") class GroupDeletedUser(val groupInfo: GroupInfo): CR()
|
||||
@Serializable @SerialName("joinedGroupMemberConnecting") class JoinedGroupMemberConnecting(val groupInfo: GroupInfo, val hostMember: GroupMember, val member: GroupMember): CR()
|
||||
@Serializable @SerialName("memberRole") class MemberRole(val groupInfo: GroupInfo, val byMember: GroupMember, val member: GroupMember, val fromRole: GroupMemberRole, val toRole: GroupMemberRole): CR()
|
||||
@Serializable @SerialName("memberRoleUser") class MemberRoleUser(val groupInfo: GroupInfo, val member: GroupMember, val fromRole: GroupMemberRole, val toRole: GroupMemberRole): CR()
|
||||
@Serializable @SerialName("deletedMemberUser") class DeletedMemberUser(val groupInfo: GroupInfo, val member: GroupMember): CR()
|
||||
@Serializable @SerialName("deletedMember") class DeletedMember(val groupInfo: GroupInfo, val byMember: GroupMember, val deletedMember: GroupMember): CR()
|
||||
@Serializable @SerialName("leftMember") class LeftMember(val groupInfo: GroupInfo, val member: GroupMember): CR()
|
||||
@@ -1799,6 +1818,8 @@ sealed class CR {
|
||||
is ReceivedGroupInvitation -> "receivedGroupInvitation"
|
||||
is GroupDeletedUser -> "groupDeletedUser"
|
||||
is JoinedGroupMemberConnecting -> "joinedGroupMemberConnecting"
|
||||
is MemberRole -> "memberRole"
|
||||
is MemberRoleUser -> "memberRoleUser"
|
||||
is DeletedMemberUser -> "deletedMemberUser"
|
||||
is DeletedMember -> "deletedMember"
|
||||
is LeftMember -> "leftMember"
|
||||
@@ -1887,6 +1908,8 @@ sealed class CR {
|
||||
is ReceivedGroupInvitation -> "groupInfo: $groupInfo\ncontact: $contact\nmemberRole: $memberRole"
|
||||
is GroupDeletedUser -> json.encodeToString(groupInfo)
|
||||
is JoinedGroupMemberConnecting -> "groupInfo: $groupInfo\nhostMember: $hostMember\nmember: $member"
|
||||
is MemberRole -> "groupInfo: $groupInfo\nbyMember: $byMember\nmember: $member\nfromRole: $fromRole\ntoRole: $toRole"
|
||||
is MemberRoleUser -> "groupInfo: $groupInfo\nmember: $member\nfromRole: $fromRole\ntoRole: $toRole"
|
||||
is DeletedMemberUser -> "groupInfo: $groupInfo\nmember: $member"
|
||||
is DeletedMember -> "groupInfo: $groupInfo\nbyMember: $byMember\ndeletedMember: $deletedMember"
|
||||
is LeftMember -> "groupInfo: $groupInfo\nmember: $member"
|
||||
|
||||
@@ -138,7 +138,7 @@ fun AddGroupMembersLayout(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<GroupMemberRole>) {
|
||||
private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<GroupMemberRole>) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
@@ -2,6 +2,7 @@ package chat.simplex.app.views.chat.group
|
||||
|
||||
import InfoRow
|
||||
import SectionDivider
|
||||
import SectionItemView
|
||||
import SectionSpacer
|
||||
import SectionView
|
||||
import androidx.activity.compose.BackHandler
|
||||
@@ -10,7 +11,7 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -40,10 +41,12 @@ fun GroupMemberInfoView(
|
||||
val chat = chatModel.chats.firstOrNull { it.id == chatModel.chatId.value }
|
||||
val developerTools = chatModel.controller.appPrefs.developerTools.get()
|
||||
if (chat != null) {
|
||||
val newRole = remember { mutableStateOf(member.memberRole) }
|
||||
GroupMemberInfoLayout(
|
||||
groupInfo,
|
||||
member,
|
||||
connStats,
|
||||
newRole,
|
||||
developerTools,
|
||||
openDirectChat = {
|
||||
withApi {
|
||||
@@ -61,7 +64,24 @@ fun GroupMemberInfoView(
|
||||
closeAll()
|
||||
}
|
||||
},
|
||||
removeMember = { removeMemberDialog(groupInfo, member, chatModel, close) }
|
||||
removeMember = { removeMemberDialog(groupInfo, member, chatModel, close) },
|
||||
onRoleSelected = {
|
||||
if (it == newRole.value) return@GroupMemberInfoLayout
|
||||
val prevValue = newRole.value
|
||||
newRole.value = it
|
||||
updateMemberRoleDialog(it, member, onDismiss = {
|
||||
newRole.value = prevValue
|
||||
}) {
|
||||
withApi {
|
||||
kotlin.runCatching {
|
||||
val mem = chatModel.controller.apiMemberRole(groupInfo.groupId, member.groupMemberId, it)
|
||||
chatModel.upsertGroupMember(groupInfo, mem)
|
||||
}.onFailure {
|
||||
newRole.value = prevValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -88,9 +108,11 @@ fun GroupMemberInfoLayout(
|
||||
groupInfo: GroupInfo,
|
||||
member: GroupMember,
|
||||
connStats: ConnectionStats?,
|
||||
newRole: MutableState<GroupMemberRole>,
|
||||
developerTools: Boolean,
|
||||
openDirectChat: () -> Unit,
|
||||
removeMember: () -> Unit,
|
||||
onRoleSelected: (GroupMemberRole) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
@@ -113,6 +135,15 @@ fun GroupMemberInfoLayout(
|
||||
|
||||
SectionView(title = stringResource(R.string.member_info_section_title_member)) {
|
||||
InfoRow(stringResource(R.string.info_row_group), groupInfo.displayName)
|
||||
SectionDivider()
|
||||
val roles = remember { member.canChangeRoleTo(groupInfo) }
|
||||
if (roles != null) {
|
||||
SectionItemView {
|
||||
RoleSelectionRow(roles, newRole, onRoleSelected)
|
||||
}
|
||||
} else {
|
||||
InfoRow(stringResource(R.string.role_in_group), member.memberRole.text)
|
||||
}
|
||||
val conn = member.activeConn
|
||||
if (conn != null) {
|
||||
SectionDivider()
|
||||
@@ -143,7 +174,7 @@ fun GroupMemberInfoLayout(
|
||||
}
|
||||
}
|
||||
|
||||
if (member.canBeRemoved(groupInfo.membership)) {
|
||||
if (member.canBeRemoved(groupInfo)) {
|
||||
SectionView {
|
||||
RemoveMemberButton(removeMember)
|
||||
}
|
||||
@@ -207,6 +238,48 @@ fun OpenChatButton(onClick: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoleSelectionRow(
|
||||
roles: List<GroupMemberRole>,
|
||||
selectedRole: MutableState<GroupMemberRole>,
|
||||
onSelected: (GroupMemberRole) -> Unit
|
||||
) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
val values = remember { roles.map { it to it.text } }
|
||||
ExposedDropDownSettingRow(
|
||||
generalGetString(R.string.change_role),
|
||||
values,
|
||||
selectedRole,
|
||||
icon = null,
|
||||
enabled = remember { mutableStateOf(true) },
|
||||
onSelected = onSelected
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMemberRoleDialog(
|
||||
newRole: GroupMemberRole,
|
||||
member: GroupMember,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit
|
||||
) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(R.string.change_member_role_question),
|
||||
text = if (member.memberCurrent)
|
||||
String.format(generalGetString(R.string.member_role_will_be_changed_with_notification), newRole.text)
|
||||
else
|
||||
String.format(generalGetString(R.string.member_role_will_be_changed_with_invitation), newRole.text),
|
||||
confirmText = generalGetString(R.string.change_verb),
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onConfirm,
|
||||
onDismissRequest = onDismiss
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewGroupMemberInfoLayout() {
|
||||
@@ -215,9 +288,11 @@ fun PreviewGroupMemberInfoLayout() {
|
||||
groupInfo = GroupInfo.sampleData,
|
||||
member = GroupMember.sampleData,
|
||||
connStats = null,
|
||||
newRole = remember { mutableStateOf(GroupMemberRole.Member) },
|
||||
developerTools = false,
|
||||
openDirectChat = {},
|
||||
removeMember = {}
|
||||
removeMember = {},
|
||||
onRoleSelected = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,13 @@ class AlertManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun showAlertMsg(
|
||||
title: Int,
|
||||
text: Int? = null,
|
||||
confirmText: Int = R.string.ok,
|
||||
onConfirm: (() -> Unit)? = null
|
||||
) = showAlertMsg(generalGetString(title), if (text != null) generalGetString(text) else null, generalGetString(confirmText), onConfirm)
|
||||
|
||||
@Composable
|
||||
fun showInView() {
|
||||
if (presentAlert.value) alertView.value?.invoke()
|
||||
|
||||
@@ -770,6 +770,16 @@
|
||||
<string name="member_will_be_removed_from_group_cannot_be_undone">Das Mitglied wird aus der Gruppe entfernt - dies kann nicht rückgängig gemacht werden!</string>
|
||||
<string name="remove_member_confirmation">Entfernen</string>
|
||||
<string name="member_info_section_title_member">MITGLIED</string>
|
||||
<string name="role_in_group">Role</string>
|
||||
<string name="change_role">Change role</string>
|
||||
<string name="change_verb">Change</string>
|
||||
<string name="change_member_role_question">Change group role?</string>
|
||||
<string name="member_role_will_be_changed_with_notification">The role will be changed to \"%s\". Everyone in the group will be notified.</string>
|
||||
<string name="member_role_will_be_changed_with_invitation">The role will be changed to \"%s\". The member will receive a new invitation.</string>
|
||||
<string name="error_removing_member">Error removing member</string>
|
||||
<string name="error_changing_role">Error changing role</string>
|
||||
<string name="member_role">member %s role: %s</string>
|
||||
<string name="your_member_role">your role: %s</string>
|
||||
<string name="info_row_group">Gruppe</string>
|
||||
<string name="info_row_connection">Verbindung</string>
|
||||
<string name="conn_level_desc_direct">direkt</string>
|
||||
|
||||
@@ -770,6 +770,16 @@
|
||||
<string name="member_will_be_removed_from_group_cannot_be_undone">Член группы будет удален - это действие нельзя отменить!</string>
|
||||
<string name="remove_member_confirmation">Удалить</string>
|
||||
<string name="member_info_section_title_member">ЧЛЕН ГРУППЫ</string>
|
||||
<string name="role_in_group">Роль</string>
|
||||
<string name="change_role">Поменять роль</string>
|
||||
<string name="change_verb">Поменять</string>
|
||||
<string name="change_member_role_question">Поменять роль в группе?</string>
|
||||
<string name="member_role_will_be_changed_with_notification">Роль будет изменена на \"%s\". Все в группе получат сообщение.</string>
|
||||
<string name="member_role_will_be_changed_with_invitation">Роль будет изменена на \"%s\". Будет отправлено новое приглашение.</string>
|
||||
<string name="error_removing_member">Ошибка при удалении члена группы</string>
|
||||
<string name="error_changing_role">Ошибка при изменении роли</string>
|
||||
<string name="member_role">роль %s: %s</string>
|
||||
<string name="your_member_role">ваша роль: %s</string>
|
||||
<string name="info_row_group">Группа</string>
|
||||
<string name="info_row_connection">Соединение</string>
|
||||
<string name="conn_level_desc_direct">прямое</string>
|
||||
|
||||
@@ -770,6 +770,16 @@
|
||||
<string name="member_will_be_removed_from_group_cannot_be_undone">Member will be removed from group - this cannot be undone!</string>
|
||||
<string name="remove_member_confirmation">Remove</string>
|
||||
<string name="member_info_section_title_member">MEMBER</string>
|
||||
<string name="role_in_group">Role</string>
|
||||
<string name="change_role">Change role</string>
|
||||
<string name="change_verb">Change</string>
|
||||
<string name="change_member_role_question">Change group role?</string>
|
||||
<string name="member_role_will_be_changed_with_notification">The role will be changed to \"%s\". Everyone in the group will be notified.</string>
|
||||
<string name="member_role_will_be_changed_with_invitation">The role will be changed to \"%s\". The member will receive a new invitation.</string>
|
||||
<string name="error_removing_member">Error removing member</string>
|
||||
<string name="error_changing_role">Error changing role</string>
|
||||
<string name="member_role">member %s role: %s</string>
|
||||
<string name="your_member_role">your role: %s</string>
|
||||
<string name="info_row_group">Group</string>
|
||||
<string name="info_row_connection">Connection</string>
|
||||
<string name="conn_level_desc_direct">direct</string>
|
||||
|
||||
Reference in New Issue
Block a user