Reactions and votes stop failing silently

`reactToStory` ran its work inside `runCatching` and threw the result
away. A reaction that never reached the relay looked exactly like one that
did: the strip redrew, nothing said otherwise, and the emoji was simply
not there afterwards. That is "I react to a story and the reaction doesn't
take", reported and until now unexplained.

`reactToMessage` and `votePoll` do the same thing, so they get the same
treatment. `replyToStory`, sitting immediately below the story reaction,
has always handled its failure properly; these three were the ones that
did not.

There are more of these in this file. They are not all bugs, plenty are
genuinely fire-and-forget, and a heuristic sweep gets it wrong in both
directions: mine first reported `addBuddy` as unhandled when its handler
is fifteen lines down, then missed `reactToStory` because it saw the
neighbouring reply's handler. These three were read rather than matched.

1474 unit tests green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-09-18 07:30:19 -04:00
co-authored by Claude Opus 5
parent 91a46b4d2a
commit 223d40e88e
@@ -1219,6 +1219,9 @@ class MessengerViewModel(
val current = session ?: return@launch
withContext(Dispatchers.IO) {
runCatching { current.conversations.vote(pollId, current.userId, choice) }
}.onFailure { failure ->
// A vote that did not land must not look like one that did.
update { it.copy(error = describe(failure)) }
}
refreshPolls()
}
@@ -3215,6 +3218,9 @@ class MessengerViewModel(
privateToAuthor = privately,
)
}
}.onFailure { failure ->
// Same as the story reaction: a tap that silently did nothing.
update { it.copy(error = describe(failure)) }
}
update {
it.copy(
@@ -3335,8 +3341,16 @@ class MessengerViewModel(
story: Vault.Story,
) = viewModelScope.launch {
val current = session ?: return@launch
withContext(Dispatchers.IO) { runCatching { current.reactToStory(emoji, story) } }
// The outcome is looked at. This threw the failure away, so a reaction
// that did not reach the relay looked exactly like one that did: the
// strip redrew and nothing said otherwise. "The reaction doesn't take"
// is what that is from the outside.
val done =
withContext(Dispatchers.IO) { runCatching { current.reactToStory(emoji, story) } }
update { it.copy(stories = current.stories()) }
done.onFailure { failure ->
update { it.copy(error = describe(failure)) }
}
}
/** Reply privately to somebody's story, in the ordinary conversation. */