Corrects the name of the room in Nests feed

This commit is contained in:
Vitor Pamplona
2026-04-27 17:27:11 -04:00
parent 414707c3cb
commit 81b82198e2
@@ -22,32 +22,74 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.nests
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.BottomStart
import androidx.compose.ui.Alignment.Companion.TopEnd
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
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.Note
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteAndMap
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner
import com.vitorpamplona.amethyst.ui.note.Gallery
import com.vitorpamplona.amethyst.ui.note.LikeReaction
import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
import com.vitorpamplona.amethyst.ui.note.ZapReaction
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip53LiveActivities.RenderLiveActivityThumb
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.EndedFlag
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.LiveFlag
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.OfflineFlag
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip53LiveActivities.ScheduledFlag
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip53LiveActivities.LiveActivityCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip53LiveActivities.LoadParticipants
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfVideoIsOnline
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.room.NestActivity
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.room.NestBridge
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
import com.vitorpamplona.amethyst.ui.theme.Size34dp
import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdPadding
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag
import kotlinx.collections.immutable.toImmutableList
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -81,7 +123,7 @@ fun NestsFeedLoaded(
}
/**
* Audio-rooms list card. Mirrors [RenderLiveActivityThumb] visually but
* Audio-rooms list card. Mirrors [ObserveAndRenderSpace] visually but
* routes a tap straight into [NestActivity] when the underlying event
* is a [MeetingSpaceEvent], instead of the thread view that the generic
* `ChannelCardCompose` → `ClickableNote` chain would otherwise open.
@@ -121,7 +163,212 @@ private fun NestFeedCard(
Column(modifier.clickable(onClick = onClick)) {
Column(StdPadding) {
RenderLiveActivityThumb(baseNote, accountViewModel, nav)
SensitivityWarning(
note = baseNote,
accountViewModel = accountViewModel,
) {
ObserveAndRenderSpace(baseNote, accountViewModel, nav)
}
}
}
}
@Composable
fun ObserveAndRenderSpace(
baseNote: Note,
accountViewModel: AccountViewModel,
nav: INav,
) {
val card by observeNoteAndMap(baseNote, accountViewModel) {
when (val noteEvent = it.event) {
is MeetingSpaceEvent -> {
LiveActivityCard(
id = noteEvent.address(),
name = noteEvent.dTag(),
cover = noteEvent.image()?.ifBlank { null },
media = null,
subject = noteEvent.room()?.ifBlank { null },
content = noteEvent.summary(),
participants = noteEvent.participants().toImmutableList(),
status =
when (noteEvent.status()) {
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.OPEN -> StatusTag.STATUS.LIVE
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.PRIVATE -> StatusTag.STATUS.PLANNED
com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag.STATUS.CLOSED -> StatusTag.STATUS.ENDED
else -> null
},
starts = null,
)
}
else -> {
null
}
}
}
card?.let {
RenderLiveSpacesThumb(
it,
baseNote,
accountViewModel,
nav,
)
}
}
@Composable
fun RenderLiveSpacesThumb(
card: LiveActivityCard,
baseNote: Note,
accountViewModel: AccountViewModel,
nav: INav,
) {
Column(
modifier = Modifier.fillMaxWidth(),
) {
Box(
contentAlignment = TopEnd,
modifier =
Modifier
.aspectRatio(ratio = 16f / 9f)
.fillMaxWidth(),
) {
card.cover?.let {
AsyncImage(
model = it,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier =
Modifier
.fillMaxSize()
.clip(QuoteBorder),
)
} ?: run { DisplayAuthorBanner(baseNote, accountViewModel) }
Box(Modifier.padding(10.dp)) {
CrossfadeIfEnabled(targetState = card.status, accountViewModel = accountViewModel) {
when (it) {
StatusTag.STATUS.LIVE -> {
val url = card.media
if (url.isNullOrBlank()) {
EndedFlag()
} else {
CheckIfVideoIsOnline(url, accountViewModel) { isOnline ->
if (isOnline) {
LiveFlag()
} else {
OfflineFlag()
}
}
}
}
StatusTag.STATUS.ENDED -> {
EndedFlag()
}
StatusTag.STATUS.PLANNED -> {
ScheduledFlag(card.starts)
}
else -> {
EndedFlag()
}
}
}
}
LoadParticipants(card.participants, baseNote, accountViewModel) { participantUsers ->
Box(
Modifier
.padding(10.dp)
.align(BottomStart),
) {
if (participantUsers.isNotEmpty()) {
Gallery(participantUsers, Modifier, accountViewModel, nav)
}
}
}
}
Spacer(modifier = DoubleVertSpacer)
SpaceHostAndReactions(
card.subject,
baseNote,
accountViewModel,
nav,
)
}
}
@Composable
fun SpaceHostAndReactions(
name: String?,
baseNote: Note,
accountViewModel: AccountViewModel,
nav: INav,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
val creator = baseNote.author
if (creator == null) {
Text(
text = stringRes(R.string.wallet_loading),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
} else {
UserPicture(
user = creator,
size = Size34dp,
accountViewModel = accountViewModel,
nav = nav,
)
Column(
modifier =
Modifier
.padding(start = 10.dp)
.height(35.dp)
.weight(1f),
verticalArrangement = Arrangement.Center,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (!name.isNullOrBlank()) {
Text(
text = name,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
} else {
UsernameDisplay(creator, accountViewModel = accountViewModel)
}
}
}
}
Row(
modifier =
Modifier
.height(Size35dp)
.padding(start = 5.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = RowColSpacing,
) {
LikeReaction(
baseNote = baseNote,
grayTint = MaterialTheme.colorScheme.onSurface,
accountViewModel = accountViewModel,
nav = nav,
)
Spacer(modifier = StdHorzSpacer)
ZapReaction(
baseNote = baseNote,
grayTint = MaterialTheme.colorScheme.onSurface,
accountViewModel = accountViewModel,
nav = nav,
)
}
}
}