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) val o = arr.getJSONObject(i)
RoomMember( RoomMember(
fingerprint = o.optString("fingerprint", ""), 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( data class RoomMember(
val fingerprint: String, 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. */ /** Short display name: alias if set, otherwise first 8 chars of fingerprint. */
val displayName: String val displayName: String

View File

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