fix(marmot): an out-of-sync reset survives leaving the chat

The reset ran on the banner's rememberCoroutineScope, cancelled as soon as the chat
closed; leaving right after confirming could stop it between dropping the local state,
flagging the group as awaiting a re-invite, and publishing the KeyPackage the admin needs.
It now runs on the account scope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vitor Pamplona
2026-09-28 10:48:32 -04:00
co-authored by Claude Opus 5.5
parent 0c9f4cbf0b
commit 1f05937752
2 changed files with 9 additions and 3 deletions
@@ -2556,7 +2556,14 @@ class AccountViewModel(
/** True when this account has somewhere to upload a group's encrypted media. */
fun hasBlossomServers(): Boolean = account.marmot.marmotMediaPolicyServers().isNotEmpty()
suspend fun resetOutOfSyncMarmotGroup(nostrGroupId: String) = account.marmot.resetOutOfSyncMarmotGroup(nostrGroupId)
/**
* On the account scope, not the screen's: the reset drops local state, flags the group as
* awaiting a re-invite and publishes a KeyPackage, and leaving the chat mid-way must not
* stop it between those steps.
*/
fun resetOutOfSyncMarmotGroup(nostrGroupId: String) {
account.scope.launch(Dispatchers.IO) { account.marmot.resetOutOfSyncMarmotGroup(nostrGroupId) }
}
suspend fun enableMarmotEncryptedMediaV2(nostrGroupId: String) {
account.marmot.enableMarmotEncryptedMediaV2(nostrGroupId)
@@ -416,7 +416,6 @@ private fun MarmotOutOfSyncBanner(
nostrGroupId: HexKey,
accountViewModel: AccountViewModel,
) {
val scope = rememberCoroutineScope()
var confirming by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 6.dp)) {
@@ -439,7 +438,7 @@ private fun MarmotOutOfSyncBanner(
TextButton(
onClick = {
confirming = false
scope.launch(Dispatchers.IO) { accountViewModel.resetOutOfSyncMarmotGroup(nostrGroupId) }
accountViewModel.resetOutOfSyncMarmotGroup(nostrGroupId)
},
) { Text(stringRes(Res.string.marmot_out_of_sync_reset)) }
},