diff --git a/apps/ios/Shared/Views/Chat/Group/ChannelMembersView.swift b/apps/ios/Shared/Views/Chat/Group/ChannelMembersView.swift index 6b7f5b65fc..e4d788b47c 100644 --- a/apps/ios/Shared/Views/Chat/Group/ChannelMembersView.swift +++ b/apps/ios/Shared/Views/Chat/Group/ChannelMembersView.swift @@ -26,7 +26,7 @@ struct ChannelMembersView: View { .sorted { $0.wrapped.memberRole > $1.wrapped.memberRole } let subscriberCount = groupInfo.groupSummary.publicMemberCount ?? Int64(members.count + 1) let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase - if groupInfo.isOwner { + if groupInfo.canModerate { List { Section(header: Text(subscriberCountStr(subscriberCount)).foregroundColor(theme.colors.secondary)) { searchFieldView(text: $searchText, focussed: $searchFocussed, theme.colors.onBackground, theme.colors.secondary) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index 1be8070259..d2644e600f 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -125,7 +125,7 @@ struct GroupChatInfoView: View { Label("Share via chat", systemImage: "arrowshape.turn.up.forward") } } - if groupInfo.isOwner || members.contains(where: { $0.wrapped.memberRole >= .owner }) { + if groupInfo.canModerate || members.contains(where: { $0.wrapped.memberRole >= .owner }) { channelMembersButton() } if groupInfo.membership.memberRole >= .moderator { @@ -734,7 +734,7 @@ struct GroupChatInfoView: View { } private func channelMembersButton() -> some View { - let label: LocalizedStringKey = groupInfo.isOwner ? "Subscribers" : "Owners & contributors" + let label: LocalizedStringKey = groupInfo.canModerate ? "Subscribers" : "Owners & contributors" return NavigationLink { ChannelMembersView(chat: chat, groupInfo: groupInfo) .navigationTitle(label) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index ebbdc6b6a3..d529bc4e76 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -38,6 +38,8 @@ struct GroupMemberInfoView: View { switch groupMember.wrapped.memberRole { case .relay: "Relay" case .owner: "Owner" + case .moderator: "Moderator" + case .member: "Contributor" default: "Subscriber" } } else { diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 68300193d5..60d8ca1da8 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -3414,10 +3414,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable { if groupInfo.useRelays && !groupInfo.isOwner { return nil } let userRole = groupInfo.membership.memberRole if groupInfo.useRelays { - // TODO [relays]: for now owners can only set observer/member in channels. - // Restore the full Owner-excluded picker when moderator/admin promotion is supported: - // return GroupMemberRole.supportedRoles.filter { $0 <= userRole && $0 != .owner } - return [.observer, .member] + return [.observer, .member, .moderator] } else { return GroupMemberRole.supportedRoles.filter { $0 <= userRole } } 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 094615eee1..8a0f18d5de 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 @@ -2987,10 +2987,7 @@ data class GroupMember ( else if (groupInfo.useRelays && !groupInfo.isOwner) null else groupInfo.membership.memberRole.let { userRole -> if (groupInfo.useRelays) - // TODO [relays]: for now owners can only set observer/member in channels. - // Restore the full Owner-excluded picker when moderator/admin promotion is supported: - // GroupMemberRole.selectableRoles.filter { it <= userRole && it != GroupMemberRole.Owner } - listOf(GroupMemberRole.Observer, GroupMemberRole.Member) + listOf(GroupMemberRole.Observer, GroupMemberRole.Member, GroupMemberRole.Moderator) else GroupMemberRole.selectableRoles.filter { it <= userRole } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelMembersView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelMembersView.kt index ea841acc86..3ad2906759 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelMembersView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/ChannelMembersView.kt @@ -44,7 +44,7 @@ fun ModalData.ChannelMembersView( val s = searchText.value.text.trim().lowercase() val subscriberCount = groupInfo.groupSummary.publicMemberCount ?: (members.size + 1).toLong() val oneHandUI = remember { ChatController.appPrefs.oneHandUI.state } - val title = if (groupInfo.isOwner) { + val title = if (groupInfo.canModerate) { generalGetString(MR.strings.channel_members_title_subscribers) } else { generalGetString(MR.strings.channel_members_section_owners) @@ -57,7 +57,7 @@ fun ModalData.ChannelMembersView( item { AppBarTitle(title) } - if (groupInfo.isOwner) { + if (groupInfo.canModerate) { val showSearch = members.size > 8 item { SectionView(title = subscriberCountStr(subscriberCount)) { 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..8cbbeb18c7 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 @@ -651,7 +651,7 @@ fun ModalData.GroupChatInfoLayout( ModalManager.closeAllModalsEverywhere() } } - if (groupInfo.isOwner || activeSortedMembers.any { it.memberRole >= GroupMemberRole.Owner }) { + if (groupInfo.canModerate || activeSortedMembers.any { it.memberRole >= GroupMemberRole.Owner }) { anyTopSectionRowShow = true ChannelMembersButton(chat.remoteHostId, groupInfo, showMemberInfo) } @@ -1295,7 +1295,7 @@ private fun ShareViaChatButton(onClick: () -> Unit) { @Composable private fun ChannelMembersButton(rhId: Long?, groupInfo: GroupInfo, showMemberInfo: (GroupMember, GroupRelay?) -> Unit) { - val title = if (groupInfo.isOwner) { + val title = if (groupInfo.canModerate) { stringResource(MR.strings.channel_members_title_subscribers) } else { stringResource(MR.strings.channel_members_section_owners) 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 ac1c6915b4..dc06d15ea6 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 @@ -594,6 +594,8 @@ fun GroupMemberInfoLayout( when (member.memberRole) { GroupMemberRole.Relay -> stringResource(MR.strings.member_info_section_title_relay) GroupMemberRole.Owner -> stringResource(MR.strings.member_info_section_title_owner) + GroupMemberRole.Moderator -> stringResource(MR.strings.member_info_section_title_moderator) + GroupMemberRole.Member -> stringResource(MR.strings.member_info_section_title_contributor) else -> stringResource(MR.strings.member_info_section_title_subscriber) } } else { diff --git a/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml b/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml index 793d72244b..b4d12e357f 100644 --- a/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml @@ -3090,6 +3090,8 @@ Relay Owner + Moderator + Contributor Subscriber Channel Relay link