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:
Paul Hitt
2026-08-29 11:00:06 -04:00
co-authored by Claude Opus 4.8
parent 076e85d756
commit cc8f4ca1d0
4 changed files with 114 additions and 6 deletions
@@ -109,8 +109,22 @@ class MessengerApi(
delete("buddies")
}
fun setStatus(status: String) {
post("status", JSONObject().put("status", status))
/**
* Set our presence, and return what the relay says it now is.
*
* 🔴 **Return the server's answer, never the value we asked for.** The
* relay used to accept an unknown status with 200 and store nothing, so a
* caller that assumed success showed a state the server had discarded —
* "offline" on this screen while every contact still saw us online. The
* relay refuses unknown values now, but a client that reports its own
* request back to itself is wrong regardless of how the server behaves.
*
* ⚠️ The answer is not always the word sent: `offline` is stored as
* `invisible`, which is what "appear offline" has always meant.
*/
fun setStatus(status: String): String {
val reply = post("status", JSONObject().put("status", status))
return reply.optString("status").ifEmpty { status }
}
fun notifications(): List<Notification> {