A stale presence token stops reporting people offline
Same fault the iPhone had. While the relay still keeps a contact graph there are two ways to be granted a view of somebody, and a token that has gone stale answers offline for a contact the graph says is online. The merge took the token's answer outright; it takes the more visible of the two now. Once the relay keeps no graph the token is the only source and stands alone, including when it says offline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
55d8442ccc
commit
13d49fed8e
@@ -2130,14 +2130,32 @@ class Session(
|
||||
)
|
||||
}
|
||||
vault.saveRoster(mirror, offServer = false)
|
||||
// §8.9c. Where this account holds a contact's token, the token's
|
||||
// answer wins: it is the one that does not depend on the relay
|
||||
// keeping a copy of who knows whom. A contact with no token yet
|
||||
// keeps whatever the roster said, so a half-migrated pair does not
|
||||
// read as offline while both ends catch up.
|
||||
// §8.9c. Two ways to be granted a view of somebody: the relay's
|
||||
// contact graph, and holding their token. While BOTH exist they
|
||||
// can disagree, and a token that has gone stale answers offline
|
||||
// for somebody the graph says is online. Take the more visible
|
||||
// answer: either grant is a grant, and a stale token is not
|
||||
// somebody going offline.
|
||||
//
|
||||
// Once the relay keeps no graph its answer is the only one, so the
|
||||
// token stands alone including when it says offline. That is
|
||||
// revocation working rather than a disagreement.
|
||||
val byToken = presenceFromTokens()
|
||||
if (byToken.isEmpty()) fetched else fetched.map { b ->
|
||||
byToken[b.userId]?.let { b.copy(presence = it) } ?: b
|
||||
if (byToken.isEmpty()) {
|
||||
fetched
|
||||
} else {
|
||||
fetched.map { b ->
|
||||
val told = byToken[b.userId] ?: return@map b
|
||||
val take =
|
||||
if (!answer.relayKeepsRoster) {
|
||||
told
|
||||
} else if (visibilityOf(told) > visibilityOf(b.presence)) {
|
||||
told
|
||||
} else {
|
||||
b.presence
|
||||
}
|
||||
b.copy(presence = take)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (items.length() > 0) stored() else throw e
|
||||
@@ -2171,6 +2189,18 @@ class Session(
|
||||
* transient fault. Empty onto empty is allowed, or a new account could
|
||||
* never take its first contacts from the server.
|
||||
*/
|
||||
/**
|
||||
* How visible a presence reads, for choosing between two answers about the
|
||||
* same person. Higher is more present. See the merge in [buddies].
|
||||
*/
|
||||
private fun visibilityOf(presence: String): Int =
|
||||
when (presence) {
|
||||
"online" -> 3
|
||||
"away" -> 2
|
||||
"busy" -> 1
|
||||
else -> 0
|
||||
}
|
||||
|
||||
internal fun shouldAdoptRoster(
|
||||
incoming: Int,
|
||||
held: Int,
|
||||
|
||||
Reference in New Issue
Block a user