use domain without prefix in profiles and in database, rename fields and types

This commit is contained in:
Evgeny Poberezkin
2026-07-02 22:03:31 +01:00
parent 80ddf976bd
commit c863283b86
56 changed files with 816 additions and 884 deletions
@@ -2039,14 +2039,15 @@ data class Profile(
val peerType: ChatPeerType? = null,
// the badge proof from the wire profile: not interpreted by the UI (display uses crypto-free LocalBadge),
// but preserved so passing a link profile back to the core (apiPrepareContact) keeps the proof
val badge: BadgeProof? = null
val badge: BadgeProof? = null,
val contactDomain: SimplexDomainClaim? = null
): NamedChat {
val profileViewName: String
get() {
return if (fullName == "" || displayName == fullName) displayName else "$displayName ($fullName)"
}
fun toLocalProfile(profileId: Long): LocalProfile = LocalProfile(profileId, displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType)
fun toLocalProfile(profileId: Long): LocalProfile = LocalProfile(profileId, displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType, contactDomain = contactDomain)
companion object {
val sampleData = Profile(
@@ -2069,12 +2070,12 @@ data class LocalProfile(
val preferences: ChatPreferences? = null,
val peerType: ChatPeerType? = null,
val localBadge: LocalBadge? = null,
val simplexName: SimplexNameClaim? = null,
val simplexNameVerification: Boolean? = null
val contactDomain: SimplexDomainClaim? = null,
val contactDomainVerified: Boolean? = null
): NamedChat {
val profileViewName: String = localAlias.ifEmpty { if (fullName == "" || displayName == fullName) displayName else "$displayName ($fullName)" }
fun toProfile(): Profile = Profile(displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType)
fun toProfile(): Profile = Profile(displayName, fullName, shortDescr, image, localAlias, contactLink, preferences, peerType, contactDomain = contactDomain)
companion object {
val sampleData = LocalProfile(
@@ -2200,7 +2201,7 @@ data class GroupInfo (
val chatTags: List<Long>,
val chatItemTTL: Long?,
override val localAlias: String,
val simplexNameVerification: Boolean? = null,
val groupDomainVerified: Boolean? = null,
): SomeChat, NamedChat {
override val chatType get() = ChatType.Group
override val id get() = "#$groupId"
@@ -2323,17 +2324,17 @@ object GroupTypeSerializer : KSerializer<GroupType> {
}
@Serializable
data class SimplexNameClaim(
val name: String,
val proof: NameClaimProof? = null
data class SimplexDomainClaim(
val domain: String,
val proof: SimplexDomainProof? = null
) {
val shortName: String get() = name.removePrefix("simplex:/name")
val shortName: String get() = domain.removeSuffix(".simplex")
}
@Serializable
data class PublicGroupAccess(
val groupWebPage: String? = null,
val simplexName: SimplexNameClaim? = null,
val groupDomainClaim: SimplexDomainClaim? = null,
val domainWebPage: Boolean = false,
val allowEmbedding: Boolean = false
)
@@ -4885,21 +4886,11 @@ enum class SimplexLinkType(val linkType: String) {
@Serializable
data class SimplexNameInfo(
val nameType: SimplexNameType,
val nameDomain: SimplexNameDomain
) {
// prefix-less domain for prefilling the set-name field (shortName without the @/# prefix)
val editDomain: String
get() = if (nameType == SimplexNameType.publicGroup && nameDomain.nameTLD == SimplexTLD.simplex && nameDomain.subDomain.isEmpty())
nameDomain.domain
else nameDomain.fullDomainName
// user-facing display string, mirrors backend shortNameInfoStr
val shortName: String
get() = (if (nameType == SimplexNameType.publicGroup) "#" else "@") + editDomain
}
val nameDomain: SimplexDomain
)
@Serializable
data class SimplexNameDomain(
data class SimplexDomain(
val nameTLD: SimplexTLD,
val domain: String,
val subDomain: List<String>
@@ -4931,7 +4922,7 @@ enum class SimplexNameType {
// peer's signed name claim; UI only checks presence
@Serializable
data class NameClaimProof(
data class SimplexDomainProof(
val presHeader: String,
val signature: String,
val linkOwnerId: String? = null
@@ -122,7 +122,7 @@ class AppPreferences {
val privacyProtectScreen = mkBoolPreference(SHARED_PREFS_PRIVACY_PROTECT_SCREEN, true)
val privacyAcceptImages = mkBoolPreference(SHARED_PREFS_PRIVACY_ACCEPT_IMAGES, true)
val privacyLinkPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS, true)
val privacyVerifySimplexNames = mkBoolPreference(SHARED_PREFS_PRIVACY_VERIFY_SIMPLEX_NAMES, true)
val privacyVerifySimplexNames = mkBoolPreference(SHARED_PREFS_PRIVACY_VERIFY_SIMPLEX_NAMES, false)
val privacyLinkPreviewsShowAlert = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS_SHOW_ALERT, true)
val privacySanitizeLinks = mkBoolPreference(SHARED_PREFS_PRIVACY_SANITIZE_LINKS, false)
// TODO remove
@@ -1558,17 +1558,17 @@ object ChatController {
)
}
r is API.Error && r.err is ChatError.ChatErrorChat
&& r.err.errorType is ChatErrorType.SimplexName -> {
val name = r.err.errorType.simplexName.shortName
if (r.err.errorType.simplexNameError is SimplexNameError.NoValidLink) {
&& r.err.errorType is ChatErrorType.CESimplexDomain -> {
val domain = r.err.errorType.simplexDomain.fullDomainName
if (r.err.errorType.simplexDomainError is SimplexDomainError.NoValidLink) {
AlertManager.shared.showAlertMsg(
generalGetString(MR.strings.simplex_name_no_valid_link),
generalGetString(MR.strings.simplex_name_no_valid_link_desc).format(name)
generalGetString(MR.strings.simplex_name_no_valid_link_desc).format(domain)
)
} else {
AlertManager.shared.showAlertMsg(
generalGetString(MR.strings.simplex_name_unconfirmed),
generalGetString(MR.strings.simplex_name_unconfirmed_desc).format(name)
generalGetString(MR.strings.simplex_name_unconfirmed_desc).format(domain)
)
}
}
@@ -1631,10 +1631,10 @@ object ChatController {
// owner-specific wording for setting one's own/channel name; null for other errors (handled by apiConnectResponseAlert)
fun simplexNameOwnerError(err: ChatError, isChannel: Boolean): String? =
if (err is ChatError.ChatErrorChat && err.errorType is ChatErrorType.SimplexName && err.errorType.simplexNameError is SimplexNameError.NoValidLink) {
val name = err.errorType.simplexName.shortName
if (isChannel) generalGetString(MR.strings.simplex_name_owner_no_channel_link).format(name)
else generalGetString(MR.strings.simplex_name_owner_no_address).format(name)
if (err is ChatError.ChatErrorChat && err.errorType is ChatErrorType.CESimplexDomain && err.errorType.simplexDomainError is SimplexDomainError.NoValidLink) {
val domain = err.errorType.simplexDomain.fullDomainName
if (isChannel) generalGetString(MR.strings.simplex_name_owner_no_channel_link).format(domain)
else generalGetString(MR.strings.simplex_name_owner_no_address).format(domain)
} else null
fun connErrorText(e: ChatError): String = when {
@@ -1642,8 +1642,8 @@ object ChatController {
generalGetString(MR.strings.invalid_connection_link)
e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.UnsupportedConnReq ->
generalGetString(MR.strings.unsupported_connection_link)
e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.SimplexName ->
if (e.errorType.simplexNameError is SimplexNameError.NoValidLink)
e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.CESimplexDomain ->
if (e.errorType.simplexDomainError is SimplexDomainError.NoValidLink)
generalGetString(MR.strings.simplex_name_no_valid_link)
else generalGetString(MR.strings.simplex_name_unconfirmed)
e is ChatError.ChatErrorAgent && e.agentError is AgentErrorType.NO_NAME_SERVERS ->
@@ -1823,9 +1823,9 @@ object ChatController {
}
// name is the encoded SimplexName (e.g. "@alice.simplex"); null clears it. Throws on rejection.
suspend fun apiSetUserName(rh: Long?, name: String?): User {
val userId = currentUserId("apiSetUserName")
val r = sendCmd(rh, CC.ApiSetUserName(userId, name))
suspend fun apiSetUserDomain(rh: Long?, simplexDomain: String?): User {
val userId = currentUserId("apiSetUserDomain")
val r = sendCmd(rh, CC.ApiSetUserDomain(userId, simplexDomain))
return when {
r is API.Result && r.res is CR.UserProfileUpdated -> r.res.user.updateRemoteHostId(rh)
r is API.Result && r.res is CR.UserProfileNoChange -> r.res.user.updateRemoteHostId(rh)
@@ -1840,17 +1840,17 @@ object ChatController {
}
}
suspend fun apiVerifyContactName(rh: Long?, contactId: Long): Pair<Contact, String?>? {
val r = sendCmd(rh, CC.ApiVerifyContactName(contactId))
suspend fun apiVerifyContactDomain(rh: Long?, contactId: Long): Pair<Contact, String?>? {
val r = sendCmd(rh, CC.ApiVerifyContactDomain(contactId))
if (r is API.Result && r.res is CR.ContactNameVerified) return r.res.contact to r.res.verificationFailure
Log.e(TAG, "apiVerifyContactName bad response: ${r.responseType} ${r.details}")
Log.e(TAG, "apiVerifyContactDomain bad response: ${r.responseType} ${r.details}")
return null
}
suspend fun apiVerifyPublicGroupName(rh: Long?, groupId: Long): Pair<GroupInfo, String?>? {
val r = sendCmd(rh, CC.ApiVerifyPublicGroupName(groupId))
suspend fun apiVerifyGroupDomain(rh: Long?, groupId: Long): Pair<GroupInfo, String?>? {
val r = sendCmd(rh, CC.ApiVerifyGroupDomain(groupId))
if (r is API.Result && r.res is CR.GroupNameVerified) return r.res.groupInfo to r.res.verificationFailure
Log.e(TAG, "apiVerifyPublicGroupName bad response: ${r.responseType} ${r.details}")
Log.e(TAG, "apiVerifyGroupDomain bad response: ${r.responseType} ${r.details}")
return null
}
@@ -3885,9 +3885,9 @@ sealed class CC {
class ApiShowMyAddress(val userId: Long): CC()
class ApiAddMyAddressShortLink(val userId: Long): CC()
class ApiSetProfileAddress(val userId: Long, val on: Boolean): CC()
class ApiSetUserName(val userId: Long, val name: String?): CC()
class ApiVerifyContactName(val contactId: Long): CC()
class ApiVerifyPublicGroupName(val groupId: Long): CC()
class ApiSetUserDomain(val userId: Long, val simplexDomain: String?): CC()
class ApiVerifyContactDomain(val contactId: Long): CC()
class ApiVerifyGroupDomain(val groupId: Long): CC()
class ApiSetAddressSettings(val userId: Long, val addressSettings: AddressSettings): CC()
class ApiGetCallInvitations: CC()
class ApiSendCallInvitation(val contact: Contact, val callType: CallType): CC()
@@ -4096,10 +4096,10 @@ sealed class CC {
is ApiShowMyAddress -> "/_show_address $userId"
is ApiAddMyAddressShortLink -> "/_short_link_address $userId"
is ApiSetProfileAddress -> "/_profile_address $userId ${onOff(on)}"
is ApiSetUserName -> "/_set_name $userId" + (if (name != null) " $name" else "")
is ApiSetUserDomain -> "/_set domain $userId" + (if (simplexDomain != null) " $simplexDomain" else "")
is ApiSetPublicGroupAccess -> "/_public group access #$groupId ${json.encodeToString(access)}"
is ApiVerifyContactName -> "/_verify name @$contactId"
is ApiVerifyPublicGroupName -> "/_verify name #$groupId"
is ApiVerifyContactDomain -> "/_verify domain @$contactId"
is ApiVerifyGroupDomain -> "/_verify domain #$groupId"
is ApiSetAddressSettings -> "/_address_settings $userId ${json.encodeToString(addressSettings)}"
is ApiAcceptContact -> "/_accept incognito=${onOff(incognito)} $contactReqId"
is ApiRejectContact -> "/_reject $contactReqId"
@@ -4281,10 +4281,10 @@ sealed class CC {
is ApiShowMyAddress -> "apiShowMyAddress"
is ApiAddMyAddressShortLink -> "apiAddMyAddressShortLink"
is ApiSetProfileAddress -> "apiSetProfileAddress"
is ApiSetUserName -> "apiSetUserName"
is ApiSetUserDomain -> "apiSetUserDomain"
is ApiSetPublicGroupAccess -> "apiSetPublicGroupAccess"
is ApiVerifyContactName -> "apiVerifyContactName"
is ApiVerifyPublicGroupName -> "apiVerifyPublicGroupName"
is ApiVerifyContactDomain -> "apiVerifyContactDomain"
is ApiVerifyGroupDomain -> "apiVerifyGroupDomain"
is ApiSetAddressSettings -> "apiSetAddressSettings"
is ApiAcceptContact -> "apiAcceptContact"
is ApiRejectContact -> "apiRejectContact"
@@ -7096,9 +7096,9 @@ sealed class OwnerVerification {
}
@Serializable
sealed class SimplexNameError {
@Serializable @SerialName("noValidLink") object NoValidLink : SimplexNameError()
@Serializable @SerialName("unknownName") object UnknownName : SimplexNameError()
sealed class SimplexDomainError {
@Serializable @SerialName("noValidLink") object NoValidLink : SimplexDomainError()
@Serializable @SerialName("unknownDomain") object UnknownDomain : SimplexDomainError()
}
@Serializable
@@ -7438,7 +7438,7 @@ sealed class ChatErrorType {
is ChatStoreChanged -> "chatStoreChanged"
is ConnectionPlanChatError -> "connectionPlan"
is InvalidConnReq -> "invalidConnReq"
is SimplexName -> "simplexName"
is CESimplexDomain -> "simplexDomain"
is UnsupportedConnReq -> "unsupportedConnReq"
is InvalidChatMessage -> "invalidChatMessage"
is ConnReqMessageProhibited -> "connReqMessageProhibited"
@@ -7521,7 +7521,7 @@ sealed class ChatErrorType {
@Serializable @SerialName("chatStoreChanged") object ChatStoreChanged: ChatErrorType()
@Serializable @SerialName("connectionPlan") class ConnectionPlanChatError(val connectionPlan: ConnectionPlan): ChatErrorType()
@Serializable @SerialName("invalidConnReq") object InvalidConnReq: ChatErrorType()
@Serializable @SerialName("simplexName") class SimplexName(val simplexName: SimplexNameInfo, val simplexNameError: SimplexNameError): ChatErrorType()
@Serializable @SerialName("simplexDomain") class CESimplexDomain(val simplexDomain: SimplexDomain, val simplexDomainError: SimplexDomainError): ChatErrorType()
@Serializable @SerialName("unsupportedConnReq") object UnsupportedConnReq: ChatErrorType()
@Serializable @SerialName("invalidChatMessage") class InvalidChatMessage(val connection: Connection, val message: String): ChatErrorType()
@Serializable @SerialName("connReqMessageProhibited") object ConnReqMessageProhibited: ChatErrorType()
@@ -757,17 +757,16 @@ fun ChatInfoHeader(cInfo: ChatInfo, contact: Contact) {
modifier = Modifier.combinedClickable(onClick = copyDisplayName, onLongClick = copyDisplayName).onRightClick(copyDisplayName)
)
ChatInfoDescription(cInfo, displayName, copyNameToClipboard)
val simplexName = contact.profile.simplexName
if (simplexName != null && (contact.profile.simplexNameVerification != null || simplexName.proof != null)) {
val domain = contact.profile.contactDomain
if (domain != null && (contact.profile.contactDomainVerified != null || domain.proof != null)) {
SimplexNameView(
name = simplexName.shortName,
verification = contact.profile.simplexNameVerification,
autoVerify = chatModel.controller.appPrefs.privacyVerifySimplexNames.get(),
simplexName = "@${domain.shortName}",
verified = contact.profile.contactDomainVerified,
verify = {
val rhId = chatModel.remoteHostId()
chatModel.controller.apiVerifyContactName(rhId, contact.contactId)?.let { (ct, reason) ->
chatModel.controller.apiVerifyContactDomain(rhId, contact.contactId)?.let { (ct, reason) ->
chatModel.chatsContext.updateContact(rhId, ct)
ct.profile.simplexNameVerification to reason
ct.profile.contactDomainVerified to reason
}
}
)
@@ -24,9 +24,8 @@ import kotlinx.coroutines.*
// null on network error. With `autoVerify`, it runs once on open when state is null.
@Composable
fun SimplexNameView(
name: String,
verification: Boolean?,
autoVerify: Boolean,
simplexName: String,
verified: Boolean?,
verify: suspend () -> Pair<Boolean?, String?>?
) {
val scope = rememberCoroutineScope()
@@ -59,7 +58,7 @@ fun SimplexNameView(
}
LaunchedEffect(Unit) {
if (autoVerify && verification == null) runVerify(manual = false)
if (chatModel.controller.appPrefs.privacyVerifySimplexNames.get() && verified == null) runVerify(manual = false)
}
Row(
@@ -68,18 +67,18 @@ fun SimplexNameView(
modifier = Modifier.padding(top = DEFAULT_PADDING_HALF)
) {
Text(
name,
simplexName,
style = MaterialTheme.typography.body2.copy(
color = if (verification == true) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
fontFamily = if (verification == true) FontFamily.Default else FontFamily.Monospace
color = if (verified == true) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
fontFamily = if (verified == true) FontFamily.Default else FontFamily.Monospace
)
)
when {
showSpinner.value ->
CircularProgressIndicator(Modifier.size(16.dp), strokeWidth = 2.dp, color = MaterialTheme.colors.secondary)
verification == true ->
verified == true ->
Icon(painterResource(MR.images.ic_check_filled), null, Modifier.size(18.dp), tint = MaterialTheme.colors.onBackground)
verification == false ->
verified == false ->
Icon(
painterResource(MR.images.ic_close), null, tint = Color.Red,
modifier = Modifier.size(18.dp).clickable { runVerify(manual = true) }
@@ -49,7 +49,7 @@ fun ChannelWebPageView(
val trimmedPage = webPage.value.trim()
val newAccess = PublicGroupAccess(
groupWebPage = trimmedPage.ifEmpty { null },
simplexName = access?.simplexName,
groupDomainClaim = access?.groupDomainClaim,
domainWebPage = access?.domainWebPage ?: false,
allowEmbedding = allowEmbedding.value
)
@@ -180,14 +180,15 @@ fun ModalData.GroupChatInfoView(
},
setSimplexName = {
ModalManager.end.showCustomModal { close ->
SetSimplexNameView(
val domain = groupInfo.groupProfile.publicGroup?.publicGroupAccess?.groupDomainClaim?.shortName
SetSimplexDomainView(
title = generalGetString(MR.strings.set_simplex_name),
footer = generalGetString(MR.strings.set_channel_simplex_name_footer),
prefix = "#",
initial = groupInfo.groupProfile.publicGroup?.publicGroupAccess?.simplexName?.shortName ?: "",
save = { name ->
placeholder = "#channelname.testing",
simplexName = if (domain == null) "" else "#$domain",
save = { domain ->
val access = groupInfo.groupProfile.publicGroup?.publicGroupAccess ?: PublicGroupAccess()
val newAccess = access.copy(simplexName = name?.let { SimplexNameClaim(it) })
val newAccess = access.copy(groupDomainClaim = domain?.let { SimplexDomainClaim(it) })
val gInfo = chatModel.controller.apiSetPublicGroupAccess(rhId, groupInfo.groupId, newAccess)
if (gInfo != null) {
withContext(Dispatchers.Main) { chatModel.chatsContext.updateGroup(rhId, gInfo) }
@@ -637,6 +638,12 @@ fun ModalData.GroupChatInfoLayout(
if (groupInfo.isOwner && groupLink != null) {
anyTopSectionRowShow = true
ChannelLinkButton(manageGroupLink)
SettingsActionItem(
painterResource(MR.images.ic_tag),
stringResource(MR.strings.simplex_name),
setSimplexName,
iconColor = MaterialTheme.colors.secondary
)
} else if (channelLink != null) {
anyTopSectionRowShow = true
ChannelLinkQRCodeSection(channelLink)
@@ -825,14 +832,6 @@ fun ModalData.GroupChatInfoLayout(
SectionDividerSpaced()
SectionView(title = stringResource(MR.strings.advanced_options)) {
ChannelWebPageButton(groupInfo, manageWebPage)
if (groupInfo.groupProfile.publicGroup != null) {
SettingsActionItem(
painterResource(MR.images.ic_tag),
stringResource(MR.strings.simplex_name),
setSimplexName,
iconColor = MaterialTheme.colors.secondary
)
}
}
}
@@ -975,17 +974,16 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) {
)
ChatInfoDescription(cInfo, displayName, copyNameToClipboard)
val access = groupInfo.groupProfile.publicGroup?.publicGroupAccess
val groupName = access?.simplexName?.shortName
if (groupName != null && (groupInfo.simplexNameVerification != null || access.simplexName?.proof != null)) {
val domain = access?.groupDomainClaim?.shortName
if (domain != null && (groupInfo.groupDomainVerified != null || access.groupDomainClaim?.proof != null)) {
SimplexNameView(
name = groupName,
verification = groupInfo.simplexNameVerification,
autoVerify = chatModel.controller.appPrefs.privacyVerifySimplexNames.get(),
simplexName = "#${domain}",
verified = groupInfo.groupDomainVerified,
verify = {
val rhId = chatModel.remoteHostId()
chatModel.controller.apiVerifyPublicGroupName(rhId, groupInfo.groupId)?.let { (gInfo, reason) ->
chatModel.controller.apiVerifyGroupDomain(rhId, groupInfo.groupId)?.let { (gInfo, reason) ->
chatModel.chatsContext.updateGroup(rhId, gInfo)
gInfo.simplexNameVerification to reason
gInfo.groupDomainVerified to reason
}
}
)
@@ -19,24 +19,28 @@ import kotlinx.coroutines.*
// The field is prefilled with the full prefixed name; `save` receives the encoded name (or null to
// clear) and returns true on success (it shows its own error alert otherwise).
@Composable
fun SetSimplexNameView(
fun SetSimplexDomainView(
title: String,
footer: String,
prefix: String,
initial: String,
placeholder: String,
simplexName: String,
save: suspend (String?) -> Boolean,
close: () -> Unit
) {
val name = rememberSaveable { mutableStateOf(initial) }
val name = rememberSaveable { mutableStateOf(simplexName) }
val saving = remember { mutableStateOf(false) }
val unchanged = name.value.trim() == initial.trim()
val unchanged = name.value.trim() == simplexName.trim()
fun addSimplexTLD(s: String): String {
return if (s.contains(".")) s else "$s.simplex"
}
fun normalized(): String? {
val s = name.value.trim()
return when {
s.isEmpty() -> null
s.startsWith("@") || s.startsWith("#") -> prefix + s.substring(1)
else -> prefix + s
s.startsWith("@") || s.startsWith("#") -> addSimplexTLD(s.substring(1))
else -> addSimplexTLD(s)
}
}
@@ -44,7 +48,7 @@ fun SetSimplexNameView(
withBGApi {
saving.value = true
val ok = try { save(normalized()) } catch (e: Exception) {
Log.e(TAG, "SetSimplexNameView save: ${e.stackTraceToString()}")
Log.e(TAG, "SetSimplexDomainView save: ${e.stackTraceToString()}")
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_saving_simplex_name), e.message ?: "")
false
}
@@ -57,7 +61,7 @@ fun SetSimplexNameView(
ColumnWithScrollBar {
AppBarTitle(title)
SectionView {
PlainTextEditor(name, placeholder = prefix + stringResource(MR.strings.simplex_name_placeholder))
PlainTextEditor(name, placeholder)
}
SectionTextFooter(footer)
SectionDividerSpaced()
@@ -369,18 +369,19 @@ private fun UserAddressLayout(
stringResource(MR.strings.your_simplex_name),
click = {
ModalManager.start.showCustomModal { close ->
SetSimplexNameView(
val domain = user?.profile?.contactDomain?.shortName
SetSimplexDomainView(
title = generalGetString(MR.strings.set_simplex_name),
footer = generalGetString(MR.strings.set_user_simplex_name_footer),
prefix = "@",
initial = user?.profile?.simplexName?.shortName ?: "",
save = { name ->
placeholder = "@yourname.testing",
simplexName = if (domain == null) "" else "@$domain",
save = { simplexDomain ->
try {
val u = chatModel.controller.apiSetUserName(user?.remoteHostId, name)
val u = chatModel.controller.apiSetUserDomain(user?.remoteHostId, simplexDomain)
withContext(Dispatchers.Main) { chatModel.updateUser(u) }
true
} catch (e: Exception) {
Log.e(TAG, "apiSetUserName: ${e.message}")
Log.e(TAG, "apiSetUserDomain: ${e.message}")
false
}
},
@@ -947,7 +947,6 @@
<string name="simplex_name">SimpleX name</string>
<string name="your_simplex_name">Your SimpleX name</string>
<string name="set_simplex_name">Set SimpleX name</string>
<string name="simplex_name_placeholder">name.simplex</string>
<string name="error_saving_simplex_name">Error saving name</string>
<string name="simplex_name_owner_no_channel_link">The SimpleX name %1$s is registered without channel link. Add channel link to the name via the registration page.</string>
<string name="simplex_name_owner_no_address">The SimpleX name %1$s is registered without SimpleX address. Add your SimpleX address to the name via the registration page.</string>