From 9d4cb99398f66ded53c19eac38671ec7d6362251 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 15:19:39 +0000 Subject: [PATCH] feat(podcasts): surface NIP-22 comments on episode list rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Episode comments already work end to end — replying to an episode routes to the NIP-22 GenericCommentPost composer (kind 1111 scoped to the episode address), the thread datasource fetches them via #a/#A, and the thread view renders them with a full reactions row. The only gap was discoverability on the podcast-specific surfaces. Add a comments chip (comment glyph + live reply count via observeNoteReplyCount) to PodcastEpisodeListItem. It reads "N comments" (or "Comment" when empty) and opens the episode's thread, where the discussion lives and new comments are composed. Reuses the existing thread/composer machinery — no new event plumbing. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01JGa1EM5KWyDo1o5Yr6sS18 --- .../podcasts/PodcastEpisodeListItem.kt | 46 +++++++++++++++++++ amethyst/src/main/res/values/strings.xml | 5 ++ 2 files changed, 51 insertions(+) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastEpisodeListItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastEpisodeListItem.kt index d088629e95..a55c2af060 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastEpisodeListItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/podcasts/PodcastEpisodeListItem.kt @@ -23,27 +23,34 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape 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.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteReplyCount import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor +import com.vitorpamplona.amethyst.ui.note.CommentIcon import com.vitorpamplona.amethyst.ui.note.timeAgo import com.vitorpamplona.amethyst.ui.note.types.PodcastEpisodeAudioPlayer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.stringRes +import com.vitorpamplona.amethyst.ui.theme.Size18Modifier import com.vitorpamplona.amethyst.ui.theme.Size5dp import com.vitorpamplona.amethyst.ui.theme.grayText import com.vitorpamplona.quartz.podcasts.PodcastEpisode @@ -132,5 +139,44 @@ fun PodcastEpisodeListItem( accountViewModel = accountViewModel, ) } + + EpisodeCommentsChip(note, accountViewModel, nav) + } +} + +/** + * A "comments" affordance for an episode list row: the NIP-22 (kind 1111) reply count plus a comment + * glyph, opening the episode's thread where the discussion lives and new comments are composed. + * Everything downstream — fetching `#a`/`#A` comments, the reply composer, the count — already works + * through the standard thread; this just surfaces it on the podcast-specific list. + */ +@Composable +private fun EpisodeCommentsChip( + note: Note, + accountViewModel: AccountViewModel, + nav: INav, +) { + val commentCount by observeNoteReplyCount(note, accountViewModel) + + Row( + modifier = + Modifier + .clip(RoundedCornerShape(8.dp)) + .clickable { routeFor(note, accountViewModel.account)?.let { nav.nav(it) } } + .padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + CommentIcon(Size18Modifier, MaterialTheme.colorScheme.grayText) + Text( + text = + if (commentCount == 0) { + stringRes(R.string.podcast_comment_action) + } else { + pluralStringResource(R.plurals.podcast_comment_count, commentCount, commentCount) + }, + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.grayText, + ) } } diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index b8b2b2be5e..dfd388bf96 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1011,6 +1011,11 @@ Value-for-Value %1$d%% Zaps to this are split between: + Comment + + %1$d comment + %1$d comments + Host Co-host Editor