core, ui: relay reject rejoin (#6978)

This commit is contained in:
spaced4ndy
2026-05-18 09:06:25 +00:00
committed by GitHub
parent c165663555
commit 92e9640e4f
53 changed files with 1169 additions and 112 deletions
@@ -2286,18 +2286,20 @@ data class GroupShortLinkData (
@Serializable
enum class RelayStatus {
@SerialName("new") RsNew,
@SerialName("invited") RsInvited,
@SerialName("accepted") RsAccepted,
@SerialName("active") RsActive,
@SerialName("inactive") RsInactive;
@SerialName("new") New,
@SerialName("invited") Invited,
@SerialName("accepted") Accepted,
@SerialName("active") Active,
@SerialName("inactive") Inactive,
@SerialName("rejected") Rejected;
val text: String get() = when (this) {
RsNew -> generalGetString(MR.strings.relay_status_new)
RsInvited -> generalGetString(MR.strings.relay_status_invited)
RsAccepted -> generalGetString(MR.strings.relay_status_accepted)
RsActive -> generalGetString(MR.strings.relay_status_active)
RsInactive -> generalGetString(MR.strings.relay_status_inactive)
New -> generalGetString(MR.strings.relay_status_new)
Invited -> generalGetString(MR.strings.relay_status_invited)
Accepted -> generalGetString(MR.strings.relay_status_accepted)
Active -> generalGetString(MR.strings.relay_status_active)
Inactive -> generalGetString(MR.strings.relay_status_inactive)
Rejected -> generalGetString(MR.strings.relay_status_rejected)
}
}
@@ -2011,7 +2011,7 @@ private fun ownerRelayState(chat: Chat, chatModel: ChatModel): OwnerRelayState?
relay to chatModel.groupMembers.value.firstOrNull { it.groupMemberId == relay.groupMemberId }
}
val removedCount = relayMembers.count { (_, m) -> relayMemberRemoved(m?.memberStatus) }
val activeCount = relayMembers.count { (relay, m) -> !relayMemberRemoved(m?.memberStatus) && relay.relayStatus == RelayStatus.RsActive && m?.activeConn?.connFailedErr == null }
val activeCount = relayMembers.count { (relay, m) -> !relayMemberRemoved(m?.memberStatus) && relay.relayStatus == RelayStatus.Active && m?.activeConn?.connFailedErr == null }
val failedCount = relayMembers.count { (_, m) -> !relayMemberRemoved(m?.memberStatus) && m?.activeConn?.connFailedErr != null }
val noActiveRelays = activeCount == 0 && (failedCount + removedCount) == relays.size
return OwnerRelayState(relays, activeCount, failedCount, removedCount, noActiveRelays)
@@ -114,7 +114,9 @@ private fun ChannelRelaysLayout(
if (groupInfo.isOwner) {
SectionView {
SectionItemView(click = {
val existingRelayIds = groupRelays.filter { it.relayStatus != RelayStatus.RsInactive }.mapNotNull { it.userChatRelay.chatRelayId }.toSet()
// Backend gate (APIAddGroupRelays) rejects any chatRelayId already in group_relays
// regardless of relayStatus, so all current rows must be excluded from the add list.
val existingRelayIds = groupRelays.mapNotNull { it.userChatRelay.chatRelayId }.toSet()
ModalManager.end.showModalCloseable(true) { close ->
AddGroupRelayView(
groupInfo = groupInfo,
@@ -179,7 +181,10 @@ private fun subscriberRelayStatusText(member: GroupMember): String {
}
private fun ownerRelayStatusText(member: GroupMember, groupRelays: List<GroupRelay>): String {
return if (member.memberStatus in listOf(GroupMemberStatus.MemLeft, GroupMemberStatus.MemRemoved, GroupMemberStatus.MemGroupDeleted)) {
val relayStatus = groupRelays.firstOrNull { it.groupMemberId == member.groupMemberId }?.relayStatus
return if (relayStatus == RelayStatus.Rejected) {
generalGetString(MR.strings.relay_status_rejected)
} else if (member.memberStatus in listOf(GroupMemberStatus.MemLeft, GroupMemberStatus.MemRemoved, GroupMemberStatus.MemGroupDeleted)) {
relayConnStatus(member).first
} else if (member.activeConn?.connStatus is ConnStatus.Failed) {
generalGetString(MR.strings.relay_conn_status_failed)
@@ -188,8 +193,7 @@ private fun ownerRelayStatusText(member: GroupMember, groupRelays: List<GroupRel
} else if (member.activeConn?.connInactive == true) {
generalGetString(MR.strings.member_info_member_inactive)
} else {
groupRelays.firstOrNull { it.groupMemberId == member.groupMemberId }?.relayStatus?.text
?: relayConnStatus(member).first
relayStatus?.text ?: relayConnStatus(member).first
}
}
@@ -616,6 +616,9 @@ fun GroupMemberInfoLayout(
val clipboard = LocalClipboardManager.current
ShareRelayAddressButton { clipboard.shareText(simplexChatLink(relayAddress)) }
}
if (groupRelay?.relayStatus == RelayStatus.Rejected) {
InfoRow(stringResource(MR.strings.member_info_status), stringResource(MR.strings.member_info_relay_status_rejected_by_operator))
}
}
if (groupInfo.useRelays && member.memberRole == GroupMemberRole.Relay) {
SectionTextFooter(
@@ -361,11 +361,11 @@ private fun ProgressStepView(
cancelChannelCreation: () -> Unit
) {
val failedCount = groupRelays.value.count { relayMemberConnFailed(chatModel, it) != null }
val activeCount = groupRelays.value.count { it.relayStatus == RelayStatus.RsActive && relayMemberConnFailed(chatModel, it) == null }
val activeCount = groupRelays.value.count { it.relayStatus == RelayStatus.Active && relayMemberConnFailed(chatModel, it) == null }
val total = groupRelays.value.size
fun showCancelAlert() {
val active = groupRelays.value.count { it.relayStatus == RelayStatus.RsActive && relayMemberConnFailed(chatModel, it) == null }
val active = groupRelays.value.count { it.relayStatus == RelayStatus.Active && relayMemberConnFailed(chatModel, it) == null }
val tot = groupRelays.value.size
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.cancel_creating_channel_question),
@@ -394,7 +394,7 @@ private fun ProgressStepView(
.collect { relays ->
if (ChannelRelaysModel.groupId.value != gInfo.groupId) return@collect
groupRelays.value = relays.sortedBy { relayDisplayName(it) }
if (relays.all { it.relayStatus == RelayStatus.RsActive && relayMemberConnFailed(chatModel, it) == null }) {
if (relays.all { it.relayStatus == RelayStatus.Active && relayMemberConnFailed(chatModel, it) == null }) {
onLinkReady()
ChannelRelaysModel.reset()
}
@@ -596,8 +596,14 @@ fun chatRelayDisplayName(relay: UserChatRelay): String {
@Composable
fun RelayStatusIndicator(status: RelayStatus, connFailed: Boolean = false, memberStatus: GroupMemberStatus? = null) {
val removed = memberStatus in listOf(GroupMemberStatus.MemLeft, GroupMemberStatus.MemRemoved, GroupMemberStatus.MemGroupDeleted)
val color = if (connFailed || removed) Color.Red else if (status == RelayStatus.RsActive) Color.Green else WarningYellow
val text = if (connFailed) generalGetString(MR.strings.relay_status_failed) else if (memberStatus == GroupMemberStatus.MemLeft) generalGetString(MR.strings.relay_conn_status_removed_by_operator) else if (removed) generalGetString(MR.strings.relay_conn_status_removed) else status.text
val isRejected = status == RelayStatus.Rejected
val color = if (connFailed || removed || isRejected) Color.Red else if (status == RelayStatus.Active) Color.Green else WarningYellow
val text =
if (connFailed) generalGetString(MR.strings.relay_status_failed)
else if (isRejected) generalGetString(MR.strings.relay_status_rejected)
else if (memberStatus == GroupMemberStatus.MemLeft) generalGetString(MR.strings.relay_conn_status_removed_by_operator)
else if (removed) generalGetString(MR.strings.relay_conn_status_removed)
else status.text
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
@@ -2996,6 +2996,9 @@
<string name="relay_status_accepted">accepted</string>
<string name="relay_status_active">active</string>
<string name="relay_status_inactive">inactive</string>
<string name="relay_status_rejected">rejected</string>
<string name="member_info_status">Status</string>
<string name="member_info_relay_status_rejected_by_operator">rejected by relay operator</string>
<!-- ComposeView.kt channel relay bars -->
<string name="relay_bar_all_relays_removed">All relays removed</string>