This commit is contained in:
Vitor Pamplona
2026-04-30 19:05:44 -04:00
parent 39344afe81
commit 5c78b0dfcd
4 changed files with 31 additions and 24 deletions
@@ -56,6 +56,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteAndMap
@@ -176,6 +177,7 @@ private fun NestFeedCard(
true
}
}
val addressableNote = baseNote as? AddressableNote ?: return
val onClick =
remember(meetingEvent, isUiClosed) {
@@ -205,7 +207,7 @@ private fun NestFeedCard(
note = baseNote,
accountViewModel = accountViewModel,
) {
ObserveAndRenderSpace(baseNote, accountViewModel, nav)
ObserveAndRenderSpace(addressableNote, accountViewModel, nav)
}
}
}
@@ -213,7 +215,7 @@ private fun NestFeedCard(
@Composable
fun ObserveAndRenderSpace(
baseNote: Note,
baseNote: AddressableNote,
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -284,14 +286,14 @@ private const val PRESENCE_FRESHNESS_WINDOW_SECONDS = 180L
*/
@Composable
private fun RenderLiveOrEndedFromPresence(
address: Address?,
note: AddressableNote?,
accountViewModel: AccountViewModel,
) {
if (address == null) {
if (note == null) {
LiveFlag()
return
}
val latestPresence by observeRoomLatestPresence(address, accountViewModel)
val latestPresence by observeRoomLatestPresence(note, accountViewModel)
val fresh = latestPresence == null || latestPresence!! > TimeUtils.now() - PRESENCE_FRESHNESS_WINDOW_SECONDS
if (fresh) LiveFlag() else EndedFlag()
}
@@ -310,12 +312,12 @@ private fun RenderLiveOrEndedFromPresence(
@OptIn(ExperimentalCoroutinesApi::class)
@Composable
private fun observeRoomLatestPresence(
address: Address,
note: AddressableNote,
accountViewModel: AccountViewModel,
): State<Long?> {
NestRoomFilterAssemblerSubscription(address.toValue(), accountViewModel)
NestRoomFilterAssemblerSubscription(note, accountViewModel)
val channel = remember(address) { LocalCache.getOrCreateLiveChannel(address) }
val channel = remember(note.idHex) { LocalCache.getOrCreateLiveChannel(note.address) }
val flow =
remember(channel) {
channel
@@ -340,7 +342,7 @@ private fun observeRoomLatestPresence(
@Composable
fun RenderLiveSpacesThumb(
card: NestCard,
baseNote: Note,
baseNote: AddressableNote,
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -370,7 +372,7 @@ fun RenderLiveSpacesThumb(
CrossfadeIfEnabled(targetState = card.status, accountViewModel = accountViewModel) {
when (it) {
StatusTag.STATUS.LIVE -> {
RenderLiveOrEndedFromPresence(card.id, accountViewModel)
RenderLiveOrEndedFromPresence(baseNote, accountViewModel)
}
StatusTag.STATUS.ENDED -> {
@@ -419,7 +421,7 @@ fun RenderLiveSpacesThumb(
@Composable
fun SpaceHostAndReactions(
name: String?,
baseNote: Note,
baseNote: AddressableNote,
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -26,6 +26,7 @@ import androidx.compose.runtime.remember
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUniqueIdEoseManager
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -35,6 +36,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
import com.vitorpamplona.quartz.nip53LiveActivities.presence.MeetingRoomPresenceEvent
/**
@@ -46,7 +48,7 @@ import com.vitorpamplona.quartz.nip53LiveActivities.presence.MeetingRoomPresence
*/
@Stable
class NestRoomQueryState(
val roomATag: String,
val note: AddressableNote,
val account: Account,
)
@@ -79,7 +81,8 @@ class NestRoomFilterSubAssembler(
key: NestRoomQueryState,
since: SincePerRelayMap?,
): List<RelayBasedFilter> {
val relays = key.account.outboxRelays.flow.value
val relays = key.account.outboxRelays.flow.value + key.note.relays + ((key.note.event as? MeetingSpaceEvent)?.relays()?.toSet() ?: emptySet())
return relays.flatMap { relay ->
listOf(
RelayBasedFilter(
@@ -92,7 +95,7 @@ class NestRoomFilterSubAssembler(
MeetingRoomPresenceEvent.KIND,
ReactionEvent.KIND,
),
tags = mapOf("a" to listOf(key.roomATag)),
tags = mapOf("a" to listOf(key.note.idHex)),
since = since?.get(relay)?.time,
),
),
@@ -101,7 +104,7 @@ class NestRoomFilterSubAssembler(
filter =
Filter(
kinds = listOf(AdminCommandEvent.KIND),
tags = mapOf("a" to listOf(key.roomATag), "p" to listOf(key.account.pubKey)),
tags = mapOf("a" to listOf(key.note.idHex), "p" to listOf(key.account.pubKey)),
since = since?.get(relay)?.time,
),
),
@@ -109,7 +112,7 @@ class NestRoomFilterSubAssembler(
}
}
override fun id(key: NestRoomQueryState): String = key.roomATag
override fun id(key: NestRoomQueryState): String = key.note.idHex
}
@Stable
@@ -135,23 +138,23 @@ class NestRoomFilterAssembler(
*/
@Composable
fun NestRoomFilterAssemblerSubscription(
roomATag: String,
note: AddressableNote,
accountViewModel: AccountViewModel,
) = NestRoomFilterAssemblerSubscription(
roomATag,
note,
accountViewModel.account,
accountViewModel.dataSources().nestRoom,
)
@Composable
fun NestRoomFilterAssemblerSubscription(
roomATag: String,
note: AddressableNote,
account: Account,
filterAssembler: NestRoomFilterAssembler,
) {
val state =
remember(roomATag, account.pubKey) {
NestRoomQueryState(roomATag, account)
remember(note, account.pubKey) {
NestRoomQueryState(note, account)
}
KeyDataSourceSubscription(state, filterAssembler)
}
@@ -161,7 +161,7 @@ private fun NestActivityBody(
// Single REQ per relay covering chat, presence, reactions, admin
// commands. See NestRoomFilterAssembler for the filter shape.
NestRoomFilterAssemblerSubscription(roomATag, accountViewModel)
NestRoomFilterAssemblerSubscription(roomNote, accountViewModel)
// Read side: LocalCache → VM. Each kind has its own collector
// (different VM hook per kind) plus the two eviction tickers.
@@ -62,6 +62,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
@@ -122,7 +123,7 @@ fun NestLobbyScreen(
LoadAddressableNote(address, accountViewModel) { addressableNote ->
addressableNote ?: return@LoadAddressableNote
val event = addressableNote.event as? MeetingSpaceEvent ?: return@LoadAddressableNote
NestLobbyContent(event, accountViewModel, nav)
NestLobbyContent(event, addressableNote, accountViewModel, nav)
}
}
@@ -130,6 +131,7 @@ fun NestLobbyScreen(
@Composable
private fun NestLobbyContent(
event: MeetingSpaceEvent,
addressableNote: AddressableNote,
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -137,7 +139,7 @@ private fun NestLobbyContent(
// Keep cached chat / presence warm while the user is on the lobby.
// No MoQ session is opened — that's gated behind the Open action.
NestRoomFilterAssemblerSubscription(roomATag, accountViewModel)
NestRoomFilterAssemblerSubscription(addressableNote, accountViewModel)
// Composer for kind-1311 chat. Same widget the in-room screen uses,
// so the user gets @-mention picker, file uploads, draft auto-save,