mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-27 15:48:54 +00:00
fix review comments
This commit is contained in:
@@ -1452,7 +1452,6 @@ struct ConnectionPlanResult {
|
||||
|
||||
// APIConnectPlan resolution scope; .never is local-store-only (no network), used for per-keystroke name search
|
||||
enum PlanResolveMode: String {
|
||||
case allGroups
|
||||
case unknown
|
||||
case never
|
||||
case all
|
||||
@@ -1488,15 +1487,6 @@ enum NameRegistration: Hashable {
|
||||
// the registry may add reasons after this version, so any other value is just "not registrable"
|
||||
static let reservedCommunity = "community"
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case expires
|
||||
case graceUntil
|
||||
case reservedReason_
|
||||
case pricing
|
||||
case reservedReason
|
||||
}
|
||||
|
||||
// a name past its expiry does not connect: only its owner can renew it until the grace ends
|
||||
func expired(_ now: Int64) -> Bool {
|
||||
if case let .registered(expires, _, _) = self, let expires { expires < now } else { false }
|
||||
@@ -1511,7 +1501,12 @@ enum NameRegistration: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
// stock derivation cannot read this: the core tags it flat as {"type": ...}, not in swift's nested shape
|
||||
extension NameRegistration: Decodable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type, expires, graceUntil, reservedReason_, pricing, reservedReason
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let type = try c.decode(String.self, forKey: CodingKeys.type)
|
||||
|
||||
@@ -1500,50 +1500,8 @@ enum UIRemoteCtrlSessionState {
|
||||
case connected(remoteCtrl: RemoteCtrlInfo, sessionCode: String)
|
||||
}
|
||||
|
||||
// How recently each SimpleX name was resolved from the registry.
|
||||
//
|
||||
// The lookup canvas asks that a name you already have a chat for is resolved at most once a day,
|
||||
// or once its registration has expired, while any other name resolves on every tap. Core stays
|
||||
// stateless for this: .never answers from the store without a network round trip and reports a
|
||||
// miss, and .all always resolves. So a fresh name is tried locally first and only falls through
|
||||
// to the registry when no chat claims it - which is exactly "every tap" for a name you do not have.
|
||||
enum NameResolution {
|
||||
private static let daySeconds: Int64 = 24 * 60 * 60
|
||||
|
||||
private struct Resolved: Codable {
|
||||
var at: Int64
|
||||
var expires: Int64?
|
||||
}
|
||||
|
||||
// when each SimpleX name was last resolved, so a name with a chat is not re-resolved on every tap
|
||||
private static let resolvedAtDefault = CodableDefault<[String: Resolved]>(defaults: UserDefaults.standard, forKey: DEFAULT_SIMPLEX_NAMES_RESOLVED_AT, withDefault: [:])
|
||||
|
||||
private static func save(_ m: [String: Resolved]) {
|
||||
// only names looked up in the last week are worth remembering
|
||||
let now = nowSeconds()
|
||||
resolvedAtDefault.set(m.filter { now - $0.value.at < 7 * daySeconds })
|
||||
}
|
||||
|
||||
private static func nowSeconds() -> Int64 { Int64(Date.now.timeIntervalSince1970) }
|
||||
|
||||
// a cached answer is stale a day after it was taken, or as soon as the name it described expired
|
||||
static func isFresh(_ domain: SimplexDomain) -> Bool {
|
||||
guard let r = resolvedAtDefault.get()[domain.fullDomainName] else { return false }
|
||||
let now = nowSeconds()
|
||||
if now - r.at >= daySeconds { return false }
|
||||
return r.expires.map { now < $0 } ?? true
|
||||
}
|
||||
|
||||
static func record(_ domain: SimplexDomain, _ reg: NameRegistration?) {
|
||||
var m = resolvedAtDefault.get()
|
||||
let expires: Int64? = if case let .registered(expires, _, _) = reg { expires } else { nil }
|
||||
m[domain.fullDomainName] = Resolved(at: nowSeconds(), expires: expires)
|
||||
save(m)
|
||||
}
|
||||
|
||||
// a name registered, claimed or dropped on this device must not keep reading as it did before
|
||||
static func forget(_ fullDomainName: String) {
|
||||
var m = resolvedAtDefault.get()
|
||||
if m.removeValue(forKey: fullDomainName) != nil { save(m) }
|
||||
}
|
||||
// when a SimpleX name was last resolved from the registry, and when that registration runs out
|
||||
struct SimplexNameResolved: Codable {
|
||||
var at: Int64
|
||||
var expires: Int64?
|
||||
}
|
||||
|
||||
@@ -1390,8 +1390,6 @@ func showSetSimplexNameError<R>(_ r: APIResult<R>, isChannel: Bool) async {
|
||||
func apiSetUserDomain(_ simplexDomain: String?) async throws -> User {
|
||||
let userId = try currentUserId("apiSetUserDomain")
|
||||
let r: APIResult<ChatResponse1> = await chatApiSendCmd(.apiSetUserDomain(userId: userId, simplexDomain: simplexDomain))
|
||||
// a name claimed or dropped here must not keep reading from the answer taken before
|
||||
if let d = ChatModel.shared.currentUser?.profile.contactDomain?.domain { NameResolution.forget(d) }
|
||||
switch r {
|
||||
case let .result(.userProfileUpdated(user, _, _, _)): return user
|
||||
case let .result(.userProfileNoChange(user)): return user
|
||||
|
||||
@@ -1324,6 +1324,38 @@ private func showOpenKnownGroupAlert(
|
||||
|
||||
private let simplexNamesHowToURL = "https://simplex.domains/#testing"
|
||||
|
||||
private let nameResolvedDaySeconds: Int64 = 24 * 60 * 60
|
||||
|
||||
private func nameResolvedNow() -> Int64 { Int64(Date.now.timeIntervalSince1970) }
|
||||
|
||||
private func saveNamesResolvedAt(_ m: [String: SimplexNameResolved]) {
|
||||
// only names looked up in the last week are worth remembering
|
||||
let now = nameResolvedNow()
|
||||
simplexNamesResolvedAtDefault.set(m.filter { now - $0.value.at < 7 * nameResolvedDaySeconds })
|
||||
}
|
||||
|
||||
// a cached answer is stale a day after it was taken, or as soon as the name it described expired
|
||||
func simplexNameResolvedRecently(_ domain: SimplexDomain) -> Bool {
|
||||
guard let r = simplexNamesResolvedAtDefault.get()[domain.fullDomainName] else { return false }
|
||||
let now = nameResolvedNow()
|
||||
if now - r.at >= nameResolvedDaySeconds { return false }
|
||||
return r.expires.map { now < $0 } ?? true
|
||||
}
|
||||
|
||||
func recordSimplexNameResolved(_ domain: SimplexDomain, _ reg: NameRegistration?) {
|
||||
var m = simplexNamesResolvedAtDefault.get()
|
||||
let expires: Int64? = if case let .registered(expires, _, _) = reg { expires } else { nil }
|
||||
m[domain.fullDomainName] = SimplexNameResolved(at: nameResolvedNow(), expires: expires)
|
||||
saveNamesResolvedAt(m)
|
||||
}
|
||||
|
||||
// a name registered, claimed or dropped on this device must not keep reading as it did before
|
||||
func forgetSimplexNameResolved(_ fullDomainName: String) {
|
||||
var m = simplexNamesResolvedAtDefault.get()
|
||||
if m.removeValue(forKey: fullDomainName) != nil { saveNamesResolvedAt(m) }
|
||||
}
|
||||
|
||||
|
||||
private func nameDate(_ seconds: Int64) -> String {
|
||||
Date(timeIntervalSince1970: TimeInterval(seconds)).formatted(date: .abbreviated, time: .omitted)
|
||||
}
|
||||
@@ -1475,7 +1507,7 @@ func planAndConnect(
|
||||
// name with no chat gets on every tap. Anything that is not a name resolves as it always did.
|
||||
let nameTarget: SimplexNameInfo? = if case let .name(_, nameInfo) = strConnectTarget(shortOrFullLink) { nameInfo } else { nil }
|
||||
var result: ConnectionPlanResult? = nil
|
||||
if let nameTarget, NameResolution.isFresh(nameTarget.nameDomain) {
|
||||
if let nameTarget, simplexNameResolvedRecently(nameTarget.nameDomain) {
|
||||
// a local probe: no error alerts, so a miss falls through silently
|
||||
result = await apiConnectPlan(connLink: shortOrFullLink, resolveMode: .never, linkOwnerSig: linkOwnerSig, inProgress: BoxedValue(false))
|
||||
}
|
||||
@@ -1483,7 +1515,7 @@ func planAndConnect(
|
||||
result = await apiConnectPlan(connLink: shortOrFullLink, resolveMode: nameTarget != nil ? .all : .unknown, linkOwnerSig: linkOwnerSig, inProgress: inProgress)
|
||||
// remember when this name was last taken from the registry, and when its registration runs out
|
||||
if let nameTarget, let result {
|
||||
NameResolution.record(nameTarget.nameDomain, result.connectionPlan.nameRegistration)
|
||||
recordSimplexNameResolved(nameTarget.nameDomain, result.connectionPlan.nameRegistration)
|
||||
}
|
||||
}
|
||||
await MainActor.run {
|
||||
|
||||
@@ -291,6 +291,8 @@ public class CodableDefault<T: Codable> {
|
||||
|
||||
let networkProxyDefault: CodableDefault<NetworkProxy> = CodableDefault(defaults: UserDefaults.standard, forKey: DEFAULT_NETWORK_PROXY, withDefault: NetworkProxy.def)
|
||||
|
||||
let simplexNamesResolvedAtDefault = CodableDefault<[String: SimplexNameResolved]>(defaults: UserDefaults.standard, forKey: DEFAULT_SIMPLEX_NAMES_RESOLVED_AT, withDefault: [:])
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
@@ -205,6 +205,8 @@ struct UserAddressView: View {
|
||||
save: { simplexDomain in
|
||||
do {
|
||||
let u = try await apiSetUserDomain(simplexDomain)
|
||||
// a name claimed or dropped here must not keep reading from the answer taken before
|
||||
if let d = chatModel.currentUser?.profile.contactDomain?.domain { forgetSimplexNameResolved(d) }
|
||||
await MainActor.run { chatModel.updateUser(u) }
|
||||
return true
|
||||
} catch {
|
||||
|
||||
+3
-49
@@ -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
|
||||
|
||||
+1
-4
@@ -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"
|
||||
|
||||
+45
-2
@@ -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()
|
||||
|
||||
+2
@@ -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) {
|
||||
|
||||
@@ -543,7 +543,7 @@ directoryServiceEvent opts@DirectoryOpts {adminUsers, superUsers, serviceName, o
|
||||
<> ".\nIt is hidden from the directory until approved."
|
||||
notifyAdminUsers $ "The " <> gt <> " " <> groupRef <> " is updated" <> byMember <> "."
|
||||
verifyAndSendToApprove g' gr' n'
|
||||
sendChatCmd cc (APIConnectPlan userId (Just (aConnectTarget link)) PRMAllGroups Nothing) >>= \case
|
||||
sendChatCmd cc (APIConnectPlan userId (Just (aConnectTarget link)) PRMAll Nothing) >>= \case
|
||||
Right (CRConnectionPlan _ _ _ _ (CPGroupLink (GLPKnown {groupInfo = g'}) _)) ->
|
||||
case dbOwnerMemberId gr of
|
||||
Just ownerGMId ->
|
||||
@@ -776,7 +776,7 @@ directoryServiceEvent opts@DirectoryOpts {adminUsers, superUsers, serviceName, o
|
||||
forM_ pg_ $ \pg@PublicGroupProfile {groupLink} ->
|
||||
when (groupRegStatus == GRSActive || pendingApproval groupRegStatus) $ do
|
||||
let link = ACL SCMContact $ CLShort groupLink
|
||||
sendChatCmd cc (APIConnectPlan userId (Just (aConnectTarget link)) PRMAllGroups Nothing) >>= \case
|
||||
sendChatCmd cc (APIConnectPlan userId (Just (aConnectTarget link)) PRMAll Nothing) >>= \case
|
||||
Right (CRConnectionPlan _ _ _ _ (CPGroupLink (GLPKnown {groupInfo = g', groupUpdated, linkOwners = ListDef owners}) _)) ->
|
||||
checkValidOwner dbOwnerMemberId owners $ do
|
||||
-- re-verify every cycle: a name that stopped resolving to the link must lose verified status
|
||||
@@ -914,7 +914,7 @@ directoryServiceEvent opts@DirectoryOpts {adminUsers, superUsers, serviceName, o
|
||||
let link = ACL SCMContact $ CLShort connLink
|
||||
mId = MemberId oIdBytes
|
||||
gt' = groupTypeStr gt
|
||||
sendChatCmd cc (APIConnectPlan userId (Just (aConnectTarget link)) PRMAllGroups (Just ownerSig)) >>= \case
|
||||
sendChatCmd cc (APIConnectPlan userId (Just (aConnectTarget link)) PRMAll (Just ownerSig)) >>= \case
|
||||
Right (CRConnectionPlan _ (Just (ACCL SCMContact ccLink)) _ _ plan) ->
|
||||
handleGroupLinkPlan ct ccLink mId ownerSig gt' plan
|
||||
_ -> sendMessage cc ct "Error: could not connect. Please report it to directory admins."
|
||||
|
||||
@@ -709,8 +709,7 @@ data ChatCommand
|
||||
deriving (Show)
|
||||
|
||||
data PlanResolveMode
|
||||
= PRMAllGroups -- resolve all known groups and all unknown chats
|
||||
| PRMUnknown -- only resolve if chat is unknown (default)
|
||||
= PRMUnknown -- only resolve if chat is unknown (default)
|
||||
| PRMNever -- do not resolve links and names, only do local search
|
||||
| PRMAll -- always resolve, also known chats
|
||||
deriving (Eq, Show)
|
||||
@@ -718,8 +717,6 @@ data PlanResolveMode
|
||||
planResolveModeP :: A.Parser PlanResolveMode
|
||||
planResolveModeP =
|
||||
A.takeTill (== ' ') >>= \case
|
||||
"allGroups" -> pure PRMAllGroups
|
||||
"on" -> pure PRMAllGroups
|
||||
"unknown" -> pure PRMUnknown
|
||||
"off" -> pure PRMUnknown
|
||||
"never" -> pure PRMNever
|
||||
|
||||
@@ -4528,8 +4528,8 @@ processChatCommand cxt nm = \case
|
||||
CTLink l' -> pure l'
|
||||
CTName n -> serverShortLink <$> resolveNameLink n
|
||||
con l' cReq = ACCL SCMContact $ CCLink cReq (Just l')
|
||||
-- a name whose chat is known is re-resolved only on PRMAll, to see whether it still leads there
|
||||
reResolveKnown (_, p) = resolveMode == PRMAll && isJust simplexName_ && case p of
|
||||
-- a known chat is re-resolved only on PRMAll, to see whether it still leads there
|
||||
reResolveKnown (_, p) = resolveMode == PRMAll && case p of
|
||||
CPContactAddress (CAPKnown _) _ -> True
|
||||
CPGroupLink GLPKnown {} _ -> True
|
||||
_ -> False
|
||||
@@ -4541,14 +4541,12 @@ processChatCommand cxt nm = \case
|
||||
groupShortLinkPlan :: CM (ACreatedConnLink, ConnectionPlan)
|
||||
groupShortLinkPlan =
|
||||
knownLinkPlans >>= \case
|
||||
Just (_, CPGroupLink (GLPKnown g _ _ _) _)
|
||||
| resolveMode == PRMAllGroups -> resolveSLink >>= \l' -> resolveKnownGroup l' g
|
||||
Just r | not (reResolveKnown r) -> pure r
|
||||
known_ -> do
|
||||
when (resolveMode == PRMNever) $ throwChatError CENotResolvedLocally
|
||||
l' <- resolveSLink
|
||||
case known_ of
|
||||
-- the name still leads to the channel that claims it, refreshed as PRMAllGroups does
|
||||
-- the name still leads to the channel that claims it, refreshed from the link
|
||||
Just r@(_, CPGroupLink (GLPKnown g _ _ _) _) | knownLinkOf r == Just l' -> resolveKnownGroup l' g
|
||||
_ -> (if isJust known_ then second setAddressChanged else id) <$> resolvedGroupPlan l'
|
||||
where
|
||||
|
||||
@@ -155,7 +155,7 @@ testChannelDomainLinkJoinUnverified ps = withSmpServerAndNames $ \reg ->
|
||||
cath <## "updated public group access: domain=team.simplex"
|
||||
memberJoinChannel "team" [cath] [alice] shortLink fullLink bob
|
||||
-- a link-data refresh must not mark the self-claimed name verified
|
||||
bob ##> ("/_connect plan 1 " <> shortLink <> " resolve=allGroups")
|
||||
bob ##> ("/_connect plan 1 " <> shortLink <> " resolve=all")
|
||||
bob <## "group link: known group #team"
|
||||
bob <## "use #team <message> to send messages" -- no "SimpleX name" line: status stays unknown
|
||||
where
|
||||
@@ -184,7 +184,7 @@ testChannelDomainVerify ps = withSmpServerAndNames $ \reg ->
|
||||
bob ##> "/_verify domain #1"
|
||||
bob <## "SimpleX name #team not verified: the name does not resolve to the link in the group profile"
|
||||
-- a link-data refresh keeps the failed status, not overwritten with verified
|
||||
bob ##> ("/_connect plan 1 " <> shortLink <> " resolve=allGroups")
|
||||
bob ##> ("/_connect plan 1 " <> shortLink <> " resolve=all")
|
||||
bob <## "group link: known group #team"
|
||||
bob <## "SimpleX name: #team (verification failed)"
|
||||
bob <## "use #team <message> to send messages"
|
||||
|
||||
Reference in New Issue
Block a user