fix review comments

This commit is contained in:
Alain Brenzikofer committed 2026-09-24 08:48:29 +02:00
1 parent 75b59f4dec
commit d9d6acd97f
14 files changed
+107 -129

No files matched your search

@@ -5252,55 +5252,9 @@ enum class SimplexTLD {
@SerialName("web") web
}
// How recently each SimpleX name was resolved: one with a local chat resolves once a day, any other on every tap.
object NameResolution {
private const val DAY_SECONDS = 24 * 60 * 60L
private val prefs: AppPreferences get() = ChatController.appPrefs
@Serializable
private data class Resolved(val at: Long, val expires: Long? = null)
private fun load(): MutableMap<String, Resolved> =
try {
val s = prefs.simplexNamesResolvedAt.get() ?: return mutableMapOf()
json.decodeFromString<Map<String, Resolved>>(s).toMutableMap()
} catch (e: Exception) {
mutableMapOf()
}
private fun save(m: Map<String, Resolved>) {
// only names looked up in the last week are worth remembering
val now = nowSeconds()
val kept = m.filterValues { now - it.at < 7 * DAY_SECONDS }
try {
prefs.simplexNamesResolvedAt.set(json.encodeToString<Map<String, Resolved>>(kept))
} catch (e: Exception) {
Log.e(TAG, "NameResolution.save: ${e.stackTraceToString()}")
}
}
private fun nowSeconds(): Long = Clock.System.now().epochSeconds
// a cached answer is stale a day after it was taken, or as soon as the name it described expired
fun isFresh(domain: SimplexDomain): Boolean {
val r = load()[domain.fullDomainName] ?: return false
val now = nowSeconds()
if (now - r.at >= DAY_SECONDS) return false
return r.expires == null || now < r.expires
}
fun record(domain: SimplexDomain, reg: NameRegistration?) {
val m = load()
m[domain.fullDomainName] = Resolved(nowSeconds(), (reg as? NameRegistration.Registered)?.expires)
save(m)
}
// a name registered, claimed or dropped on this device must not keep reading as it did before
fun forget(fullDomainName: String) {
val m = load()
if (m.remove(fullDomainName) != null) save(m)
}
}
// when a SimpleX name was last resolved from the registry, and when that registration runs out
@Serializable
data class SimplexNameResolved(val at: Long, val expires: Long? = null)
// What the registry holds for a name - the RNAME payload, "type"-tagged on every platform.
@Serializable
@@ -1944,8 +1944,6 @@ object ChatController {
suspend fun apiSetUserDomain(rh: Long?, simplexDomain: String?): User {
val userId = currentUserId("apiSetUserDomain")
val r = sendCmd(rh, CC.ApiSetUserDomain(userId, simplexDomain))
// a name claimed or dropped here must not keep reading from the answer taken before
chatModel.currentUser.value?.profile?.contactDomain?.let { NameResolution.forget(it.domain) }
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)
@@ -7435,9 +7433,8 @@ data class ConnectionPlanResult(
// APIConnectPlan resolution scope; PRMNever is local-store-only (no network), used for per-keystroke name search
enum class PlanResolveMode {
PRMAllGroups, PRMUnknown, PRMNever, PRMAll;
PRMUnknown, PRMNever, PRMAll;
val cmdString: String get() = when (this) {
PRMAllGroups -> "allGroups"
PRMUnknown -> "unknown"
PRMNever -> "never"
PRMAll -> "all"
@@ -22,6 +22,7 @@ import chat.simplex.common.views.usersettings.simplexTeamUri
import chat.simplex.res.MR
import kotlinx.coroutines.*
import kotlinx.datetime.*
import kotlinx.serialization.*
enum class ConnectionLinkType {
INVITATION, CONTACT, GROUP
@@ -74,6 +75,48 @@ private fun openNameHowTo(uriHandler: UriHandler) = openBrowserAlert(SIMPLEX_NAM
private const val SIMPLEX_NAMES_HOWTO_URL = "https://simplex.domains/#testing"
private const val NAME_RESOLVED_DAY_SECONDS = 24 * 60 * 60L
private fun loadNamesResolvedAt(): MutableMap<String, SimplexNameResolved> =
try {
val s = ChatController.appPrefs.simplexNamesResolvedAt.get() ?: return mutableMapOf()
json.decodeFromString<Map<String, SimplexNameResolved>>(s).toMutableMap()
} catch (e: Exception) {
mutableMapOf()
}
private fun saveNamesResolvedAt(m: Map<String, SimplexNameResolved>) {
// only names looked up in the last week are worth remembering
val now = Clock.System.now().epochSeconds
val kept = m.filterValues { now - it.at < 7 * NAME_RESOLVED_DAY_SECONDS }
try {
ChatController.appPrefs.simplexNamesResolvedAt.set(json.encodeToString<Map<String, SimplexNameResolved>>(kept))
} catch (e: Exception) {
Log.e(TAG, "saveNamesResolvedAt: ${e.stackTraceToString()}")
}
}
// a cached answer is stale a day after it was taken, or as soon as the name it described expired
fun simplexNameResolvedRecently(domain: SimplexDomain): Boolean {
val r = loadNamesResolvedAt()[domain.fullDomainName] ?: return false
val now = Clock.System.now().epochSeconds
if (now - r.at >= NAME_RESOLVED_DAY_SECONDS) return false
return r.expires == null || now < r.expires
}
fun recordSimplexNameResolved(domain: SimplexDomain, reg: NameRegistration?) {
val m = loadNamesResolvedAt()
m[domain.fullDomainName] = SimplexNameResolved(Clock.System.now().epochSeconds, (reg as? NameRegistration.Registered)?.expires)
saveNamesResolvedAt(m)
}
// a name registered, claimed or dropped on this device must not keep reading as it did before
fun forgetSimplexNameResolved(fullDomainName: String) {
val m = loadNamesResolvedAt()
if (m.remove(fullDomainName) != null) saveNamesResolvedAt(m)
}
// what the registry said, when the user can act on it; false leaves the caller to show its usual plan UI.
private fun showNameRegistrationAlert(
rhId: Long?,
@@ -209,7 +252,7 @@ private suspend fun planAndConnectTask(
}
// A fresh name is tried against the store first; a miss falls through to a full resolution.
val nameTarget = (strConnectTarget(shortOrFullLink.trim()) as? ConnectTarget.Name)?.nameInfo
val freshName = nameTarget != null && NameResolution.isFresh(nameTarget.nameDomain)
val freshName = nameTarget != null && simplexNameResolvedRecently(nameTarget.nameDomain)
var result = if (freshName) {
// a local probe: no spinner and no error alerts, so a miss falls through silently
chatModel.controller.apiConnectPlan(rhId, shortOrFullLink, PlanResolveMode.PRMNever, linkOwnerSig, mutableStateOf(false))
@@ -219,7 +262,7 @@ private suspend fun planAndConnectTask(
result = chatModel.controller.apiConnectPlan(rhId, shortOrFullLink, mode, linkOwnerSig, inProgress)
// remember when this name was last taken from the registry, and when its registration runs out
if (nameTarget != null && result != null) {
NameResolution.record(nameTarget.nameDomain, result.connectionPlan.nameRegistration())
recordSimplexNameResolved(nameTarget.nameDomain, result.connectionPlan.nameRegistration())
}
}
connectProgressManager.stopConnectProgress()
@@ -388,6 +388,8 @@ private fun UserAddressLayout(
save = { simplexDomain ->
try {
val u = chatModel.controller.apiSetUserDomain(user?.remoteHostId, simplexDomain)
// a name claimed or dropped here must not keep reading from the answer taken before
domain?.let { forgetSimplexNameResolved(it) }
withContext(Dispatchers.Main) { chatModel.updateUser(u) }
true
} catch (e: Exception) {