Show the presence the relay stored, not the one we asked for
`setPresence` did `it.copy(presence = status)` on success — the value it had requested. The relay answered 200 for statuses it did not recognise and stored nothing, and `busy` and `offline` were both in that set, so tapping either lit the chip and changed nothing. Someone who chose "offline" stayed visible to every contact with no way to find out. The relay is fixed too, but a client that reports its own request back to itself is wrong regardless of how the server behaves — so `setStatus` now returns what the relay says the status is, and the screen renders that. ⚠️ The reply is not always the word sent: `offline` is stored as `invisible`, the same choice under two names, so it is mapped back to this screen's vocabulary or no chip would appear selected right after a successful change. `PRESENCE_CHOICES` moves to the companion object so the chips and the test read one list. It is a contract with another repository that no compiler checks, and the test says so: every entry must be a value the relay's `ChosenStatus` accepts. engine 141, client 90, app 32 — all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
076e85d756
commit
cc8f4ca1d0
@@ -505,7 +505,7 @@ fun PresencePicker(model: MessengerViewModel, current: String) {
|
||||
// ⚠️ Chips, not four orange words in a row. As text buttons these read
|
||||
// as four links and gave no sign that they were a single choice with
|
||||
// one answer — the selected one was merely bold.
|
||||
listOf("online", "away", "busy", "offline").forEach { status ->
|
||||
MessengerViewModel.PRESENCE_CHOICES.forEach { status ->
|
||||
KitChip(
|
||||
text = status,
|
||||
selected = status == current,
|
||||
|
||||
@@ -794,13 +794,34 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
|
||||
|
||||
// -- Presence ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Choose a presence state.
|
||||
*
|
||||
* 🔴 **Shows what the relay stored, not what we asked for.** This used to
|
||||
* do `it.copy(presence = status)` on success — and the relay used to answer
|
||||
* 200 for statuses it did not recognise while storing nothing. `busy` and
|
||||
* `offline` were both in that set, so tapping either lit the chip and
|
||||
* changed nothing: someone who chose "offline" stayed visible to every
|
||||
* contact with no way to find out. Both ends are fixed; this half stays
|
||||
* correct even if a relay ever behaves that way again.
|
||||
*
|
||||
* ⚠️ `offline` is stored as `invisible` — the two are the same choice under
|
||||
* different names — so the reply is mapped back to this screen's vocabulary
|
||||
* or no chip would appear selected.
|
||||
*/
|
||||
fun setPresence(status: String) = viewModelScope.launch {
|
||||
val current = session ?: return@launch
|
||||
val done = withContext(Dispatchers.IO) { runCatching { current.api.setStatus(status) } }
|
||||
done.onSuccess { update { it.copy(presence = status, error = null) } }
|
||||
.onFailure { failure -> update { it.copy(error = describe(failure)) } }
|
||||
done.onSuccess { stored ->
|
||||
update { it.copy(presence = displayPresence(stored), error = null) }
|
||||
}.onFailure { failure -> update { it.copy(error = describe(failure)) } }
|
||||
}
|
||||
|
||||
/** Relay vocabulary -> the words on the presence chips. */
|
||||
internal fun displayPresence(stored: String): String =
|
||||
if (stored == "invisible") "offline" else stored
|
||||
|
||||
|
||||
// -- Notifications -------------------------------------------------------
|
||||
|
||||
fun refreshNotifications() = viewModelScope.launch {
|
||||
@@ -1797,7 +1818,19 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
companion object {
|
||||
/**
|
||||
* The presence chips, in order.
|
||||
*
|
||||
* ⚠️ **A contract with the relay, checked by no compiler.** Every entry
|
||||
* must be a value `ChosenStatus` accepts — `offline` is accepted there
|
||||
* as an alias for `invisible`. Adding a word the relay does not know
|
||||
* brings the original bug back in a newer form: the relay answers 400
|
||||
* now rather than lying, so the chip fails visibly instead of silently,
|
||||
* but it is still a control that does not work.
|
||||
*/
|
||||
val PRESENCE_CHOICES = listOf("online", "away", "busy", "offline")
|
||||
|
||||
const val CALLS_SEEN_AT = "calls_seen_at"
|
||||
const val POLL_INTERVAL_MS = 5_000L
|
||||
const val CALL_SETUP_POLL_MS = 1_000L
|
||||
|
||||
Reference in New Issue
Block a user