ui: show subsriber roles in list; contributor list for subscribers (#7126)

This commit is contained in:
spaced4ndy
2026-06-25 08:28:54 +00:00
committed by GitHub
parent 4f855e6bba
commit aa9b147aa8
6 changed files with 49 additions and 14 deletions
@@ -131,6 +131,15 @@ public func subscriberCountStr(_ count: Int64) -> String {
: String.localizedStringWithFormat(NSLocalizedString("%d subscribers", comment: "channel subscriber count"), count)
}
public func ownersContributorsCountStr(_ count: Int, withContributors: Bool) -> String {
if withContributors {
return String.localizedStringWithFormat(NSLocalizedString("%d owners & contributors", comment: "channel members count"), count)
}
return count == 1
? String.localizedStringWithFormat(NSLocalizedString("%d owner", comment: "channel owners count"), count)
: String.localizedStringWithFormat(NSLocalizedString("%d owners", comment: "channel owners count"), count)
}
struct ChatInfoToolbar_Previews: PreviewProvider {
static var previews: some View {
ChatInfoToolbar(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []))
@@ -21,22 +21,29 @@ struct ChannelMembersView: View {
let s = m.wrapped.memberStatus
return s != .memLeft && s != .memRemoved && m.wrapped.memberRole != .relay
}
.sorted { $0.wrapped.memberRole > $1.wrapped.memberRole }
let subscriberCount = groupInfo.groupSummary.publicMemberCount ?? Int64(members.count + 1)
if groupInfo.isOwner {
let subscriberCount = groupInfo.groupSummary.publicMemberCount ?? Int64(members.count + 1)
List {
Section(header: Text(subscriberCountStr(subscriberCount)).foregroundColor(theme.colors.secondary)) {
memberRow(GMember(groupInfo.membership), user: true, showRole: true)
ForEach(members) { member in
memberRow(member, user: false, showRole: member.wrapped.memberRole >= .owner)
memberRow(member, user: false, showRole: member.wrapped.memberRole >= .member)
}
}
}
} else {
let owners = members.filter { $0.wrapped.memberRole >= .owner }
let contributors = members.filter { $0.wrapped.memberRole >= .member && $0.wrapped.memberStatus != .memUnknown }
let contributorCount = contributors.count + (groupInfo.membership.memberRole >= .member ? 1 : 0)
let withContributors = contributors.contains { $0.wrapped.memberRole < .owner }
|| groupInfo.membership.memberRole >= .member
List {
Section(header: Text("Owners").foregroundColor(theme.colors.secondary)) {
ForEach(owners) { member in
memberRow(member, user: false, showRole: false)
Section(header: Text(ownersContributorsCountStr(contributorCount, withContributors: withContributors)).foregroundColor(theme.colors.secondary)) {
if groupInfo.membership.memberRole >= .member {
memberRow(GMember(groupInfo.membership), user: true, showRole: true)
}
ForEach(contributors) { member in
memberRow(member, user: false, showRole: member.wrapped.memberRole >= .moderator)
}
}
}
@@ -691,7 +691,7 @@ struct GroupChatInfoView: View {
}
private func channelMembersButton() -> some View {
let label: LocalizedStringKey = groupInfo.isOwner ? "Subscribers" : "Owners"
let label: LocalizedStringKey = groupInfo.isOwner ? "Subscribers" : "Owners & contributors"
return NavigationLink {
ChannelMembersView(chat: chat, groupInfo: groupInfo)
.navigationTitle(label)
@@ -1547,6 +1547,11 @@ fun subscriberCountStr(count: Long): String =
if (count == 1L) String.format(generalGetString(MR.strings.channel_subscriber_count_singular), count)
else String.format(generalGetString(MR.strings.channel_subscriber_count_plural), count)
fun ownersContributorsCountStr(count: Int, withContributors: Boolean): String =
if (withContributors) String.format(generalGetString(MR.strings.channel_owners_contributors_count), count)
else if (count == 1) String.format(generalGetString(MR.strings.channel_owner_count_singular), count)
else String.format(generalGetString(MR.strings.channel_owner_count_plural), count)
@Composable
fun ChatInfoToolbarTitle(cInfo: ChatInfo, imageSize: Dp = 40.dp, iconColor: Color = MaterialTheme.colors.secondaryVariant.mixWith(MaterialTheme.colors.onBackground, 0.97f)) {
Row(
@@ -14,6 +14,7 @@ import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.ownersContributorsCountStr
import chat.simplex.common.views.chat.subscriberCountStr
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
@@ -33,6 +34,7 @@ fun ChannelMembersView(
&& m.memberStatus != GroupMemberStatus.MemRemoved
&& m.memberRole != GroupMemberRole.Relay
}
.sortedByDescending { it.memberRole }
ColumnWithScrollBar {
val title = if (groupInfo.isOwner) {
@@ -42,8 +44,8 @@ fun ChannelMembersView(
}
AppBarTitle(title)
val subscriberCount = groupInfo.groupSummary.publicMemberCount ?: (members.size + 1).toLong()
if (groupInfo.isOwner) {
val subscriberCount = groupInfo.groupSummary.publicMemberCount ?: (members.size + 1).toLong()
SectionView(title = subscriberCountStr(subscriberCount)) {
SectionItemView(minHeight = 54.dp, padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
ChannelMemberRow(groupInfo.membership, user = true, showRole = true, isChannel = groupInfo.isChannel)
@@ -55,14 +57,23 @@ fun ChannelMembersView(
minHeight = 54.dp,
padding = PaddingValues(horizontal = DEFAULT_PADDING)
) {
ChannelMemberRow(member, user = false, showRole = member.memberRole >= GroupMemberRole.Owner, isChannel = groupInfo.isChannel)
ChannelMemberRow(member, user = false, showRole = member.memberRole >= GroupMemberRole.Member, isChannel = groupInfo.isChannel)
}
}
}
} else {
val owners = members.filter { it.memberRole >= GroupMemberRole.Owner }
SectionView(title = generalGetString(MR.strings.channel_members_section_owners)) {
owners.forEachIndexed { index, member ->
val contributors = members.filter { it.memberRole >= GroupMemberRole.Member && it.memberStatus != GroupMemberStatus.MemUnknown }
val contributorCount = contributors.size + if (groupInfo.membership.memberRole >= GroupMemberRole.Member) 1 else 0
val withContributors = contributors.any { it.memberRole < GroupMemberRole.Owner } ||
groupInfo.membership.memberRole >= GroupMemberRole.Member
SectionView(title = ownersContributorsCountStr(contributorCount, withContributors)) {
if (groupInfo.membership.memberRole >= GroupMemberRole.Member) {
SectionItemView(minHeight = 54.dp, padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
ChannelMemberRow(groupInfo.membership, user = true, showRole = true, isChannel = groupInfo.isChannel)
}
Divider()
}
contributors.forEachIndexed { index, member ->
if (index > 0) {
Divider()
}
@@ -71,7 +82,7 @@ fun ChannelMembersView(
minHeight = 54.dp,
padding = PaddingValues(horizontal = DEFAULT_PADDING)
) {
ChannelMemberRow(member, user = false, showRole = false, isChannel = groupInfo.isChannel)
ChannelMemberRow(member, user = false, showRole = member.memberRole >= GroupMemberRole.Moderator, isChannel = groupInfo.isChannel)
}
}
}
@@ -2976,9 +2976,12 @@
<!-- ChannelMembersView.kt -->
<string name="channel_members_title_subscribers">Subscribers</string>
<string name="channel_members_section_owners">Owners</string>
<string name="channel_members_section_owners">Owners &amp; contributors</string>
<string name="channel_subscriber_count_singular">%1$d subscriber</string>
<string name="channel_subscriber_count_plural">%1$d subscribers</string>
<string name="channel_owner_count_singular">%1$d owner</string>
<string name="channel_owner_count_plural">%1$d owners</string>
<string name="channel_owners_contributors_count">%1$d owners &amp; contributors</string>
<string name="channel_member_you">you</string>
<!-- ChatRelayView.kt -->