fix(nests): promote/demote/kick actions no longer cancelled by sheet dismiss
The per-participant host actions (Promote to Speaker, Promote to Moderator, Demote, Force Mute, Kick) and the Hand-Raise queue's Approve button all launched their sign+broadcast on a rememberCoroutineScope() bound to the local composable. Because every action row calls onDismiss() (and the hand-raise row may disappear when canSpeak() flips true), the composable leaves the tree synchronously after the click, cancelling the scope before the launched coroutine ever runs sign(template). The user saw the sheet close and nothing else — the kind-30312 republish / kind-4312 admin event was never actually emitted. Launch on accountViewModel.viewModelScope instead so the broadcast outlives the dismissing UI.
This commit is contained in:
+9
-3
@@ -37,12 +37,12 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.MainActivity
|
||||
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
|
||||
@@ -88,7 +88,6 @@ internal fun ParticipantHostActionsSheet(
|
||||
catalog: com.vitorpamplona.amethyst.commons.viewmodels.RoomSpeakerCatalog? = null,
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val roomATag =
|
||||
ATag(
|
||||
@@ -98,9 +97,16 @@ internal fun ParticipantHostActionsSheet(
|
||||
relay = null,
|
||||
)
|
||||
|
||||
// Use the AccountViewModel's scope so the launched broadcast
|
||||
// survives the onDismiss() that every action row triggers. A
|
||||
// composition-scoped rememberCoroutineScope() gets cancelled the
|
||||
// moment hostMenuTarget flips to null and the sheet leaves the
|
||||
// tree — which races (and reliably loses) against the suspending
|
||||
// signer.sign(template) call, so the promote/demote/kick events
|
||||
// never actually went out.
|
||||
fun broadcast(template: com.vitorpamplona.quartz.nip01Core.signers.EventTemplate<out com.vitorpamplona.quartz.nip01Core.core.Event>?) {
|
||||
template ?: return
|
||||
scope.launch { runCatching { accountViewModel.account.signAndComputeBroadcast(template) } }
|
||||
accountViewModel.viewModelScope.launch { runCatching { accountViewModel.account.signAndComputeBroadcast(template) } }
|
||||
}
|
||||
|
||||
val targetUser =
|
||||
|
||||
+9
-3
@@ -37,10 +37,10 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.NestViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.RoomPresence
|
||||
@@ -96,7 +96,6 @@ internal fun HandRaiseQueueSection(
|
||||
|
||||
if (hands.isEmpty()) return
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
Column(modifier = modifier.fillMaxSize().padding(top = 12.dp)) {
|
||||
Text(
|
||||
text = stringRes(R.string.nest_hand_raise_queue_title),
|
||||
@@ -112,7 +111,14 @@ internal fun HandRaiseQueueSection(
|
||||
hand = hand,
|
||||
accountViewModel = accountViewModel,
|
||||
onApprove = {
|
||||
scope.launch {
|
||||
// Approving makes `canSpeak()` true for this user, which
|
||||
// removes them from `hands` on the next recompose. If the
|
||||
// queue empties (no other raised hands), the whole
|
||||
// section leaves composition — a rememberCoroutineScope()
|
||||
// bound here would cancel the in-flight signer.sign(...)
|
||||
// and the promote never goes out. Launch on the
|
||||
// AccountViewModel's scope so signing outlives the row.
|
||||
accountViewModel.viewModelScope.launch {
|
||||
val template = RoomParticipantActions.setRole(event, hand.pubkey, ROLE.SPEAKER)
|
||||
template?.let { runCatching { accountViewModel.account.signAndComputeBroadcast(it) } }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user