feat: Android relay-grouped participant list matching desktop
Some checks failed
Mirror to GitHub / mirror (push) Failing after 39s
Build Release Binaries / build-amd64 (push) Failing after 2m4s

Participants now grouped by relay on Android:
- Green dot + "THIS RELAY" for local participants
- Blue dot + relay label for federated participants

Added relayLabel to RoomMember data class, parsed from
relay_label JSON field. UI groups and renders with headers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-08 13:15:12 +04:00
parent fc721c4217
commit f4b5996bdf
2 changed files with 46 additions and 21 deletions

View File

@@ -60,7 +60,8 @@ data class CallStats(
val o = arr.getJSONObject(i)
RoomMember(
fingerprint = o.optString("fingerprint", ""),
alias = if (o.isNull("alias")) null else o.optString("alias", null)
alias = if (o.isNull("alias")) null else o.optString("alias", null),
relayLabel = if (o.isNull("relay_label")) null else o.optString("relay_label", null)
)
}
}
@@ -97,7 +98,8 @@ data class CallStats(
data class RoomMember(
val fingerprint: String,
val alias: String? = null
val alias: String? = null,
val relayLabel: String? = null
) {
/** Short display name: alias if set, otherwise first 8 chars of fingerprint. */
val displayName: String

View File

@@ -411,31 +411,54 @@ fun InCallScreen(
if (stats.roomParticipantCount > 0) {
val unique = stats.roomParticipants
.distinctBy { it.fingerprint.ifEmpty { it.displayName } }
unique.forEach { member ->
// Group by relay
val grouped = unique.groupBy { it.relayLabel ?: "This Relay" }
grouped.forEach { (relay, members) ->
// Relay header
val isLocal = relay == "This Relay"
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 4.dp)
modifier = Modifier.padding(top = 4.dp, bottom = 2.dp)
) {
Identicon(
fingerprint = member.fingerprint.ifEmpty { member.displayName },
size = 40.dp,
Box(
modifier = Modifier
.size(6.dp)
.clip(CircleShape)
.background(if (isLocal) Green else Color(0xFF60A5FA))
)
Spacer(modifier = Modifier.width(12.dp))
Column {
Text(
text = member.displayName,
style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Medium),
color = Color.White
Spacer(modifier = Modifier.width(6.dp))
Text(
text = relay.uppercase(),
style = MaterialTheme.typography.labelSmall.copy(letterSpacing = 0.5.sp),
color = TextDim
)
}
members.forEach { member ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 4.dp)
) {
Identicon(
fingerprint = member.fingerprint.ifEmpty { member.displayName },
size = 40.dp,
)
if (member.fingerprint.isNotEmpty()) {
CopyableFingerprint(
fingerprint = member.fingerprint.take(16),
style = MaterialTheme.typography.labelSmall.copy(
fontSize = 10.sp,
fontFamily = FontFamily.Monospace,
),
color = TextDim,
Spacer(modifier = Modifier.width(12.dp))
Column {
Text(
text = member.displayName,
style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Medium),
color = Color.White
)
if (member.fingerprint.isNotEmpty()) {
CopyableFingerprint(
fingerprint = member.fingerprint.take(16),
style = MaterialTheme.typography.labelSmall.copy(
fontSize = 10.sp,
fontFamily = FontFamily.Monospace,
),
color = TextDim,
)
}
}
}
}