From 89f39fa77e67134866349e83bc892185ce7ed727 Mon Sep 17 00:00:00 2001 From: nrobi144 Date: Tue, 17 Mar 2026 15:40:48 +0200 Subject: [PATCH] feat(search): show images and media in search results Replace plain-text NotePreviewCard with full NoteCard in search results so images, videos, and author avatars are visible inline without having to open the note. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../amethyst/desktop/ui/SearchScreen.kt | 1 + .../desktop/ui/search/SearchResultsList.kt | 93 ++++++------------- 2 files changed, 30 insertions(+), 64 deletions(-) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt index da405b6c4..c866b0ba5 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt @@ -449,6 +449,7 @@ fun SearchScreen( state = state, onNavigateToProfile = onNavigateToProfile, onNavigateToThread = onNavigateToThread, + localCache = localCache, ) } else if (!debouncedQuery.isEmpty && !isSearching) { Text( diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchResultsList.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchResultsList.kt index 2a8a36085..caf9539f7 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchResultsList.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchResultsList.kt @@ -40,8 +40,6 @@ import androidx.compose.material.icons.filled.Description import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Forum import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.HorizontalDivider @@ -61,15 +59,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.search.AdvancedSearchBarState -import com.vitorpamplona.amethyst.commons.search.KindRegistry import com.vitorpamplona.amethyst.commons.search.SearchSortOrder import com.vitorpamplona.amethyst.commons.ui.components.UserSearchCard -import com.vitorpamplona.amethyst.commons.util.toTimeAgo -import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.amethyst.desktop.cache.DesktopLocalCache +import com.vitorpamplona.amethyst.desktop.ui.note.NoteCard +import com.vitorpamplona.amethyst.desktop.ui.toNoteDisplayData import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent @Composable @@ -77,6 +73,7 @@ fun SearchResultsList( state: AdvancedSearchBarState, onNavigateToProfile: (String) -> Unit, onNavigateToThread: (String) -> Unit, + localCache: DesktopLocalCache? = null, modifier: Modifier = Modifier, listState: LazyListState = rememberLazyListState(), ) { @@ -161,14 +158,22 @@ fun SearchResultsList( if (!collapsed) { val displayNotes = textNotes.take(5) items(displayNotes, key = { "note-${it.id}" }) { event -> - NotePreviewCard(event = event, onClick = { onNavigateToThread(event.id) }) + NoteCard( + note = event.toNoteDisplayData(localCache), + onClick = { onNavigateToThread(event.id) }, + onAuthorClick = onNavigateToProfile, + ) } if (textNotes.size > 5) { item(key = "notes-expand") { ExpandableSection( remaining = textNotes.drop(5), ) { event -> - NotePreviewCard(event = event, onClick = { onNavigateToThread(event.id) }) + NoteCard( + note = event.toNoteDisplayData(localCache), + onClick = { onNavigateToThread(event.id) }, + onAuthorClick = onNavigateToProfile, + ) } } } @@ -195,14 +200,22 @@ fun SearchResultsList( } if (!collapsed) { items(articles.take(5), key = { "article-${it.id}" }) { event -> - NotePreviewCard(event = event, onClick = { onNavigateToThread(event.id) }) + NoteCard( + note = event.toNoteDisplayData(localCache), + onClick = { onNavigateToThread(event.id) }, + onAuthorClick = onNavigateToProfile, + ) } if (articles.size > 5) { item(key = "articles-expand") { ExpandableSection( remaining = articles.drop(5), ) { event -> - NotePreviewCard(event = event, onClick = { onNavigateToThread(event.id) }) + NoteCard( + note = event.toNoteDisplayData(localCache), + onClick = { onNavigateToThread(event.id) }, + onAuthorClick = onNavigateToProfile, + ) } } } @@ -227,7 +240,11 @@ fun SearchResultsList( } if (!collapsed) { items(otherNotes.take(5), key = { "other-${it.id}" }) { event -> - NotePreviewCard(event = event, onClick = { onNavigateToThread(event.id) }) + NoteCard( + note = event.toNoteDisplayData(localCache), + onClick = { onNavigateToThread(event.id) }, + onAuthorClick = onNavigateToProfile, + ) } } } @@ -303,58 +320,6 @@ private fun SortableHeader( } } -@Composable -private fun NotePreviewCard( - event: Event, - onClick: () -> Unit, -) { - Card( - modifier = Modifier.fillMaxWidth().clickable(onClick = onClick), - colors = - CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceVariant, - ), - ) { - Column(modifier = Modifier.padding(12.dp)) { - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - // Kind badge - val kindName = KindRegistry.nameFor(event.kind) ?: "kind ${event.kind}" - Text( - kindName, - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.primary, - ) - // Author (hex truncated) - Text( - event.pubKey.take(8) + "..." + event.pubKey.takeLast(4), - style = MaterialTheme.typography.bodySmall, - fontFamily = FontFamily.Monospace, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - Spacer(Modifier.weight(1f)) - // Timestamp - Text( - event.createdAt.toTimeAgo(withDot = false), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - Spacer(Modifier.height(4.dp)) - // Content preview - Text( - event.content.take(200), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface, - maxLines = 3, - overflow = TextOverflow.Ellipsis, - ) - } - } -} - @Composable private fun ExpandableSection( remaining: List,