core: add fields to chat relay profiles; remove unique name requirement; update relay profile in relay address link data (#6743)

* core: add fields to chat relay profiles

* wip

* wip

* fix

* fix

* fix

* enable tests

* schema

* api

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
This commit is contained in:
Evgeny
2026-04-03 12:42:43 +00:00
committed by GitHub
co-authored by Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> spaced4ndy
parent 0ca7cdaf1d
commit 8167f7c2ab
29 changed files with 153 additions and 131 deletions
@@ -2279,7 +2279,10 @@ enum class RelayStatus {
@Serializable
data class RelayProfile(
val name: String
val displayName: String,
val fullName: String,
val shortDescr: String? = null,
val image: String? = null
)
@Serializable
@@ -2297,9 +2300,9 @@ data class UserChatRelay(
private val createdAt: Date = Date()
val id: String get() = "$address $createdAt"
val name: String get() = relayProfile.name
val displayName: String get() = relayProfile.displayName
fun copyWithName(name: String): UserChatRelay = copy(relayProfile = RelayProfile(name = name))
fun copyWithName(name: String): UserChatRelay = copy(relayProfile = relayProfile.copy(displayName = name))
}
@Serializable
@@ -4473,7 +4473,6 @@ sealed class UserServersError {
@Serializable @SerialName("storageMissing") data class StorageMissing(val protocol: ServerProtocol, val user: UserRef?): UserServersError()
@Serializable @SerialName("proxyMissing") data class ProxyMissing(val protocol: ServerProtocol, val user: UserRef?): UserServersError()
@Serializable @SerialName("duplicateServer") data class DuplicateServer(val protocol: ServerProtocol, val duplicateServer: String, val duplicateHost: String): UserServersError()
@Serializable @SerialName("duplicateChatRelayName") data class DuplicateChatRelayName(val duplicateChatRelay: String): UserServersError()
@Serializable @SerialName("duplicateChatRelayAddress") data class DuplicateChatRelayAddress(val duplicateChatRelay: String, val duplicateAddress: String): UserServersError()
val globalError: String?
@@ -4489,7 +4488,6 @@ sealed class UserServersError {
is StorageMissing -> this.protocol
is ProxyMissing -> this.protocol
is DuplicateServer -> this.protocol
is DuplicateChatRelayName -> null
is DuplicateChatRelayAddress -> null
}
@@ -539,7 +539,7 @@ private fun LinkStepView(
}
fun relayDisplayName(relay: GroupRelay): String {
if (relay.userChatRelay.name.isNotEmpty()) return relay.userChatRelay.name
if (relay.userChatRelay.displayName.isNotEmpty()) return relay.userChatRelay.displayName
relay.userChatRelay.domains.firstOrNull()?.let { return it }
relay.relayLink?.let { return hostFromRelayLink(it) }
return "relay ${relay.groupRelayId}"
@@ -72,11 +72,11 @@ fun addChatRelay(
rhId: Long?,
close: () -> Unit
) {
val nameEmpty = relay.name.trim().isEmpty()
val nameEmpty = relay.displayName.trim().isEmpty()
val addressEmpty = relay.address.trim().isEmpty()
if (nameEmpty && addressEmpty) {
close()
} else if (!validRelayName(relay.name)) {
} else if (!validRelayName(relay.displayName)) {
close()
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.invalid_relay_name),
@@ -131,7 +131,7 @@ fun ChatRelayView(
ModalView(
close = {
val validName = validRelayName(relayToEdit.value.name)
val validName = validRelayName(relayToEdit.value.displayName)
val validAddress = validRelayAddress(relayToEdit.value.address)
if (validName && validAddress) {
onUpdate(relayToEdit.value)
@@ -194,7 +194,7 @@ private fun PresetRelay(relay: MutableState<UserChatRelay>, testing: MutableStat
SectionDividerSpaced()
SectionView(stringResource(MR.strings.preset_relay_name).uppercase()) {
SectionItemView {
Text(relay.value.name)
Text(relay.value.displayName)
}
}
SectionDividerSpaced()
@@ -207,7 +207,7 @@ private fun CustomRelay(
onDelete: (() -> Unit)?,
testing: MutableState<Boolean>
) {
val relayName = remember { mutableStateOf(relay.value.name) }
val relayName = remember { mutableStateOf(relay.value.displayName) }
val relayAddress = remember { mutableStateOf(relay.value.address) }
val validName = remember { derivedStateOf { validRelayName(relayName.value) } }
val validAddress = remember { derivedStateOf { validRelayAddress(relayAddress.value) } }
@@ -218,7 +218,7 @@ private fun CustomRelay(
.collect { relay.value = relay.value.copyWithName(it) }
}
LaunchedEffect(Unit) {
snapshotFlow { relay.value.name }
snapshotFlow { relay.value.displayName }
.distinctUntilChanged()
.collect { relayName.value = it }
}
@@ -329,20 +329,19 @@ private fun UseRelaySection(
@Composable
fun ChatRelayViewLink(
relay: UserChatRelay,
duplicateRelayNames: Set<String>,
duplicateRelayAddresses: Set<String>,
onClick: () -> Unit
) {
SectionItemView(onClick) {
Box(Modifier.width(16.dp)) {
when {
relay.name in duplicateRelayNames || relay.address in duplicateRelayAddresses -> InvalidServer()
relay.address in duplicateRelayAddresses -> InvalidServer()
!relay.enabled -> Icon(painterResource(MR.images.ic_do_not_disturb_on), null, tint = MaterialTheme.colors.secondary)
else -> ShowRelayTestStatus(relay)
}
}
Spacer(Modifier.padding(horizontal = 4.dp))
val displayName = relay.name.ifEmpty { relay.domains.firstOrNull() ?: relay.address }
val displayName = relay.displayName.ifEmpty { relay.domains.firstOrNull() ?: relay.address }
if (relay.enabled) {
Text(displayName, color = MaterialTheme.colors.onBackground, maxLines = 1)
} else {
@@ -362,7 +361,7 @@ fun ModalData.NewChatRelayView(
val relayToEdit = remember {
mutableStateOf(
UserChatRelay(
chatRelayId = null, address = "", relayProfile = RelayProfile(name = ""), domains = emptyList(),
chatRelayId = null, address = "", relayProfile = RelayProfile(displayName = "", fullName = ""), domains = emptyList(),
preset = false, tested = null, enabled = true, deleted = false
)
)
@@ -406,7 +405,7 @@ suspend fun testRelayConnection(relay: MutableState<UserChatRelay>): RelayTestFa
testFailure
} else {
relay.value = relay.value.copy(tested = true).let {
if (relayProfile != null) it.copyWithName(relayProfile.name) else it
if (relayProfile != null) it.copyWithName(relayProfile.displayName) else it
}
null
}
@@ -990,9 +990,6 @@ fun findDuplicateHosts(serverErrors: List<UserServersError>): Set<String> {
return duplicateHostsList.toSet()
}
fun findDuplicateRelayNames(serverErrors: List<UserServersError>): Set<String> =
serverErrors.mapNotNull { (it as? UserServersError.DuplicateChatRelayName)?.duplicateChatRelay }.toSet()
fun findDuplicateRelayAddresses(serverErrors: List<UserServersError>): Set<String> =
serverErrors.mapNotNull { (it as? UserServersError.DuplicateChatRelayAddress)?.duplicateAddress }.toSet()
@@ -236,13 +236,12 @@ fun OperatorViewLayout(
if (operator.enabled) {
if (userServers.value[operatorIndex].chatRelays.any { !it.deleted }) {
val duplicateRelayNames = findDuplicateRelayNames(serverErrors.value)
val duplicateRelayAddresses = findDuplicateRelayAddresses(serverErrors.value)
SectionDividerSpaced()
SectionView(generalGetString(MR.strings.chat_relays).uppercase()) {
userServers.value[operatorIndex].chatRelays.forEachIndexed { index, relay ->
if (!relay.deleted) {
ChatRelayViewLink(relay, duplicateRelayNames, duplicateRelayAddresses) {
ChatRelayViewLink(relay, duplicateRelayAddresses) {
navigateToChatRelayView(userServers, serverErrors, serverWarnings, operatorIndex, index, relay, rhId)
}
}
@@ -85,12 +85,11 @@ fun YourServersViewLayout(
Column {
if (userServers.value[operatorIndex].chatRelays.any { !it.deleted }) {
val duplicateRelayNames = findDuplicateRelayNames(serverErrors.value)
val duplicateRelayAddresses = findDuplicateRelayAddresses(serverErrors.value)
SectionView(generalGetString(MR.strings.chat_relays).uppercase()) {
userServers.value[operatorIndex].chatRelays.forEachIndexed { i, relay ->
if (relay.deleted) return@forEachIndexed
ChatRelayViewLink(relay, duplicateRelayNames, duplicateRelayAddresses) {
ChatRelayViewLink(relay, duplicateRelayAddresses) {
navigateToChatRelayView(userServers, serverErrors, serverWarnings, operatorIndex, i, relay, rhId)
}
}
@@ -433,7 +432,7 @@ private suspend fun runRelaysTest(relays: List<UserChatRelay>, onUpdated: (List<
updatedRelays.add(index, relayState.value)
onUpdated(updatedRelays.toList())
if (f != null) {
val name = relayState.value.name.ifEmpty { relayState.value.domains.firstOrNull() ?: relayState.value.address }
val name = relayState.value.displayName.ifEmpty { relayState.value.domains.firstOrNull() ?: relayState.value.address }
fs[name] = f
}
}