Adjustments to the loading and updating of the nest event in a PiPActivity

This commit is contained in:
Vitor Pamplona
2026-04-27 17:54:16 -04:00
parent 81b82198e2
commit 1e86e15291
8 changed files with 55 additions and 107 deletions
@@ -77,11 +77,6 @@ fun RenderLiveActivityBubble(
NestActivity.launch(
context = context,
addressValue = meetingEvent.address().toValue(),
authBaseUrl = service,
endpoint = endpoint,
hostPubkey = meetingEvent.pubKey,
roomId = dTag,
kind = meetingEvent.kind,
)
} else {
// Fall back to the channel route so the user
@@ -149,11 +149,6 @@ private fun NestFeedCard(
NestActivity.launch(
context = context,
addressValue = meetingEvent.address().toValue(),
authBaseUrl = service,
endpoint = endpoint,
hostPubkey = meetingEvent.pubKey,
roomId = dTag,
kind = meetingEvent.kind,
)
} else {
nav.nav { routeFor(baseNote, accountViewModel.account) }
@@ -195,11 +195,6 @@ fun CreateNestSheet(
NestActivity.launch(
context = context,
addressValue = launchInfo.addressValue,
authBaseUrl = launchInfo.authBaseUrl,
endpoint = launchInfo.endpoint,
hostPubkey = launchInfo.hostPubkey,
roomId = launchInfo.roomId,
kind = launchInfo.kind,
)
onDismiss()
}
@@ -98,18 +98,7 @@ class NestActivity : AppCompatActivity() {
val accountViewModel = NestBridge.accountViewModel
val addressValue = intent.getStringExtra(EXTRA_ADDRESS)
val authBaseUrl = intent.getStringExtra(EXTRA_AUTH_BASE_URL)
val endpoint = intent.getStringExtra(EXTRA_ENDPOINT)
val hostPubkey = intent.getStringExtra(EXTRA_HOST_PUBKEY)
val roomId = intent.getStringExtra(EXTRA_ROOM_ID)
val kind = intent.getIntExtra(EXTRA_KIND, com.vitorpamplona.nestsclient.NestsRoomConfig.MEETING_SPACE_KIND)
if (accountViewModel == null ||
addressValue == null ||
authBaseUrl == null ||
endpoint == null ||
hostPubkey == null ||
roomId == null
) {
if (accountViewModel == null || addressValue == null) {
// After process death the bridge is empty (the previous
// process's AccountViewModel is gone). Bounce the user back to
// MainActivity so they land on the lobby instead of a black
@@ -147,14 +136,6 @@ class NestActivity : AppCompatActivity() {
AmethystTheme {
NestActivityContent(
addressValue = addressValue,
room =
com.vitorpamplona.nestsclient.NestsRoomConfig(
authBaseUrl = authBaseUrl,
endpoint = endpoint,
hostPubkey = hostPubkey,
roomId = roomId,
kind = kind,
),
accountViewModel = accountViewModel,
isInPipMode = isInPipMode.value,
onMuteState = { muted ->
@@ -252,11 +233,6 @@ class NestActivity : AppCompatActivity() {
companion object {
const val EXTRA_ADDRESS = "com.vitorpamplona.amethyst.NEST_ADDRESS"
const val EXTRA_AUTH_BASE_URL = "com.vitorpamplona.amethyst.NEST_AUTH_BASE_URL"
const val EXTRA_ENDPOINT = "com.vitorpamplona.amethyst.NEST_ENDPOINT"
const val EXTRA_HOST_PUBKEY = "com.vitorpamplona.amethyst.NEST_HOST_PUBKEY"
const val EXTRA_ROOM_ID = "com.vitorpamplona.amethyst.NEST_ROOM_ID"
const val EXTRA_KIND = "com.vitorpamplona.amethyst.NEST_KIND"
private const val ACTION_PIP_TOGGLE_MUTE = "com.vitorpamplona.amethyst.NEST_PIP_MUTE"
private const val ACTION_PIP_LEAVE = "com.vitorpamplona.amethyst.NEST_PIP_LEAVE"
private const val PIP_MUTE_REQ = 0x6A001
@@ -265,21 +241,11 @@ class NestActivity : AppCompatActivity() {
fun launch(
context: Context,
addressValue: String,
authBaseUrl: String,
endpoint: String,
hostPubkey: String,
roomId: String,
kind: Int,
) {
context.startActivity(
Intent(context, NestActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(EXTRA_ADDRESS, addressValue)
putExtra(EXTRA_AUTH_BASE_URL, authBaseUrl)
putExtra(EXTRA_ENDPOINT, endpoint)
putExtra(EXTRA_HOST_PUBKEY, hostPubkey)
putExtra(EXTRA_ROOM_ID, roomId)
putExtra(EXTRA_KIND, kind)
},
)
}
@@ -27,9 +27,14 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.commons.model.AddressableNote
import com.vitorpamplona.amethyst.commons.viewmodels.NestViewModel
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEvent
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.datasource.NestRoomFilterAssemblerSubscription
import com.vitorpamplona.nestsclient.NestsRoomConfig
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ROLE
@@ -43,7 +48,6 @@ import kotlinx.coroutines.flow.SharedFlow
@Composable
internal fun NestActivityContent(
addressValue: String,
room: com.vitorpamplona.nestsclient.NestsRoomConfig,
accountViewModel: AccountViewModel,
isInPipMode: Boolean,
onMuteState: (Boolean) -> Unit,
@@ -61,18 +65,37 @@ internal fun NestActivityContent(
LoadAddressableNote(parsedAddress, accountViewModel) { addressableNote ->
addressableNote ?: return@LoadAddressableNote
val event = addressableNote.event as? MeetingSpaceEvent ?: return@LoadAddressableNote
NestActivityBody(
event = event,
roomNote = addressableNote,
room = room,
accountViewModel = accountViewModel,
isInPipMode = isInPipMode,
onMuteState = onMuteState,
onConnectedChange = onConnectedChange,
pipMuteSignal = pipMuteSignal,
onLeave = onLeave,
)
val event by observeNoteEvent<MeetingSpaceEvent>(addressableNote, accountViewModel)
event?.let {
val service = it.service()
val endPoint = it.endpoint()
if (service != null && endPoint != null) {
val viewModel =
rememberNestViewModel(
NestsRoomConfig(
authBaseUrl = service,
endpoint = endPoint,
hostPubkey = it.pubKey,
roomId = it.dTag(),
kind = it.kind,
),
accountViewModel.account.signer,
)
NestActivityBody(
event = it,
roomNote = addressableNote,
viewModel = viewModel,
accountViewModel = accountViewModel,
isInPipMode = isInPipMode,
onMuteState = onMuteState,
onConnectedChange = onConnectedChange,
pipMuteSignal = pipMuteSignal,
onLeave = onLeave,
)
}
}
}
}
@@ -90,8 +113,8 @@ internal fun NestActivityContent(
@Composable
private fun NestActivityBody(
event: MeetingSpaceEvent,
roomNote: com.vitorpamplona.amethyst.model.AddressableNote,
room: com.vitorpamplona.nestsclient.NestsRoomConfig,
roomNote: AddressableNote,
viewModel: NestViewModel,
accountViewModel: AccountViewModel,
isInPipMode: Boolean,
onMuteState: (Boolean) -> Unit,
@@ -101,7 +124,7 @@ private fun NestActivityBody(
) {
val account = accountViewModel.account
val localPubkey = account.signer.pubKey
val roomATag = remember(event) { event.address().toValue() }
val roomATag = roomNote.idHex
// Static room layout: hosts + speakers from the kind-30312 `p`-tags.
// The grid renderer derives audience from the kind-10312 presence
@@ -116,7 +139,6 @@ private fun NestActivityBody(
}
val onStageKeys = remember(onStage) { onStage.map { it.pubKey }.toSet() }
val viewModel = rememberNestViewModel(room, account.signer)
AutoConnectAndTrackSpeakers(viewModel, onStageKeys)
// Single REQ per relay covering chat, presence, reactions, admin
@@ -48,7 +48,6 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -72,6 +71,8 @@ import com.vitorpamplona.amethyst.commons.viewmodels.ConnectionUiState
import com.vitorpamplona.amethyst.commons.viewmodels.NestUiState
import com.vitorpamplona.amethyst.commons.viewmodels.NestViewModel
import com.vitorpamplona.amethyst.commons.viewmodels.RoomTheme
import com.vitorpamplona.amethyst.commons.viewmodels.buildParticipantGrid
import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
@@ -151,17 +152,15 @@ internal fun NestFullScreen(
Column(
modifier =
Modifier
.weight(1f, fill = false)
.weight(1f)
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp)
.padding(top = 8.dp),
.padding(horizontal = 16.dp),
) {
event.summary()?.let {
Text(
text = it,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp),
)
}
@@ -169,20 +168,6 @@ internal fun NestFullScreen(
// in the room. Hidden until the aggregator has at least one
// entry so the placeholder doesn't flash on entry.
val presences by viewModel.presences.collectAsState()
val listenerCount = presences.size
if (listenerCount > 0) {
Text(
text =
androidx.compose.ui.res.pluralStringResource(
R.plurals.nest_listener_count,
listenerCount,
listenerCount,
),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp),
)
}
val reactionsByPubkey by viewModel.recentReactions.collectAsState()
var hostMenuTarget by rememberSaveable { mutableStateOf<String?>(null) }
@@ -198,7 +183,7 @@ internal fun NestFullScreen(
// greys out at 50 % alpha, matching nostrnests' web client.
val participantGrid =
androidx.compose.runtime.remember(event, presences) {
com.vitorpamplona.amethyst.commons.viewmodels.buildParticipantGrid(
buildParticipantGrid(
participants = event.participants(),
presences = presences,
)
@@ -324,11 +309,7 @@ internal fun NestFullScreen(
event = event,
viewModel = viewModel,
accountViewModel = accountViewModel,
modifier =
Modifier
.weight(1f, fill = true)
.padding(horizontal = 16.dp)
.padding(top = 12.dp, bottom = 16.dp),
modifier = Modifier.weight(1f),
)
}
}
@@ -367,7 +348,7 @@ private fun NestTopAppBar(
onShare: () -> Unit,
onEdit: () -> Unit,
) {
TopAppBar(
ShorterTopAppBar(
title = {
Text(
text = title,
@@ -296,10 +296,6 @@ fun JoinNestButton(
if (serviceBase.isNullOrBlank() || endpoint.isNullOrBlank() || roomId.isBlank()) return
val context = LocalContext.current
val addressValue = remember(event) { event.address().toValue() }
val hostPubkey = event.pubKey
val kind = event.kind
val colors =
if (primaryColorOverride != null) {
androidx.compose.material3.ButtonDefaults
@@ -314,12 +310,7 @@ fun JoinNestButton(
NestBridge.set(accountViewModel)
NestActivity.launch(
context = context,
addressValue = addressValue,
authBaseUrl = serviceBase,
endpoint = endpoint,
hostPubkey = hostPubkey,
roomId = roomId,
kind = kind,
addressValue = event.address().toValue(),
)
},
colors = colors,
@@ -28,7 +28,6 @@ import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -60,10 +59,13 @@ import com.vitorpamplona.amethyst.commons.viewmodels.ParticipantGrid
import com.vitorpamplona.amethyst.commons.viewmodels.RoomMember
import com.vitorpamplona.amethyst.commons.viewmodels.RoomReaction
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.Size40dp
import com.vitorpamplona.amethyst.ui.theme.SpacedBy10dp
import com.vitorpamplona.amethyst.ui.theme.SpacedBy5dp
import kotlinx.collections.immutable.ImmutableSet
/**
@@ -155,7 +157,7 @@ private fun ParticipantsSection(
val absentAlphaModifier = remember { Modifier.alpha(0.5f) }
val speakingBorderModifier = remember(ringColor) { Modifier.border(2.dp, ringColor, CircleShape) }
val spinnerModifier = remember(avatarSize) { Modifier.size(avatarSize - 8.dp) }
Column(modifier = Modifier.padding(top = 8.dp)) {
Column(modifier = Modifier.padding(top = 8.dp), verticalArrangement = SpacedBy5dp) {
Text(
text = title,
style = MaterialTheme.typography.labelMedium,
@@ -168,7 +170,7 @@ private fun ParticipantsSection(
LazyHorizontalGrid(
rows = GridCells.Fixed(1),
modifier = gridModifier,
horizontalArrangement = Arrangement.spacedBy(6.dp),
horizontalArrangement = SpacedBy5dp,
) {
items(items = members, key = { it.pubkey }) { member ->
val isSpeaking = member.pubkey in speakingNow
@@ -196,6 +198,7 @@ private fun ParticipantsSection(
val isConnecting = member.pubkey in connectingSpeakers
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = SpacedBy10dp,
modifier = cellWidthModifier,
) {
Box(contentAlignment = Alignment.Center) {
@@ -234,7 +237,7 @@ private fun ParticipantsSection(
)
}
}
com.vitorpamplona.amethyst.ui.note.UsernameDisplay(
UsernameDisplay(
baseUser = user,
weight = Modifier.fillMaxWidth(),
accountViewModel = accountViewModel,