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.ExperimentalFoundationApi
import androidx.compose.foundation.clickable 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.Column
import androidx.compose.foundation.layout.Row 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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember 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.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext 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 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.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.model.Note 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.layouts.rememberFeedContentPadding
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor 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.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.NestActivity
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.room.NestBridge 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.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.FeedPadding 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.amethyst.ui.theme.StdPadding
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag
import kotlinx.collections.immutable.toImmutableList
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @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 * routes a tap straight into [NestActivity] when the underlying event
* is a [MeetingSpaceEvent], instead of the thread view that the generic * is a [MeetingSpaceEvent], instead of the thread view that the generic
* `ChannelCardCompose` → `ClickableNote` chain would otherwise open. * `ChannelCardCompose` → `ClickableNote` chain would otherwise open.
@@ -121,7 +163,212 @@ private fun NestFeedCard(
Column(modifier.clickable(onClick = onClick)) { Column(modifier.clickable(onClick = onClick)) {
Column(StdPadding) { 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,
)
} }
} }
} }