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) <noreply@anthropic.com>
This commit is contained in:
@@ -449,6 +449,7 @@ fun SearchScreen(
|
|||||||
state = state,
|
state = state,
|
||||||
onNavigateToProfile = onNavigateToProfile,
|
onNavigateToProfile = onNavigateToProfile,
|
||||||
onNavigateToThread = onNavigateToThread,
|
onNavigateToThread = onNavigateToThread,
|
||||||
|
localCache = localCache,
|
||||||
)
|
)
|
||||||
} else if (!debouncedQuery.isEmpty && !isSearching) {
|
} else if (!debouncedQuery.isEmpty && !isSearching) {
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
+29
-64
@@ -40,8 +40,6 @@ import androidx.compose.material.icons.filled.Description
|
|||||||
import androidx.compose.material.icons.filled.ExpandMore
|
import androidx.compose.material.icons.filled.ExpandMore
|
||||||
import androidx.compose.material.icons.filled.Forum
|
import androidx.compose.material.icons.filled.Forum
|
||||||
import androidx.compose.material.icons.filled.Person
|
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.FilterChip
|
||||||
import androidx.compose.material3.FilterChipDefaults
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
@@ -61,15 +59,13 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
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 androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.amethyst.commons.search.AdvancedSearchBarState
|
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.search.SearchSortOrder
|
||||||
import com.vitorpamplona.amethyst.commons.ui.components.UserSearchCard
|
import com.vitorpamplona.amethyst.commons.ui.components.UserSearchCard
|
||||||
import com.vitorpamplona.amethyst.commons.util.toTimeAgo
|
import com.vitorpamplona.amethyst.desktop.cache.DesktopLocalCache
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.amethyst.desktop.ui.note.NoteCard
|
||||||
|
import com.vitorpamplona.amethyst.desktop.ui.toNoteDisplayData
|
||||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -77,6 +73,7 @@ fun SearchResultsList(
|
|||||||
state: AdvancedSearchBarState,
|
state: AdvancedSearchBarState,
|
||||||
onNavigateToProfile: (String) -> Unit,
|
onNavigateToProfile: (String) -> Unit,
|
||||||
onNavigateToThread: (String) -> Unit,
|
onNavigateToThread: (String) -> Unit,
|
||||||
|
localCache: DesktopLocalCache? = null,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
listState: LazyListState = rememberLazyListState(),
|
listState: LazyListState = rememberLazyListState(),
|
||||||
) {
|
) {
|
||||||
@@ -161,14 +158,22 @@ fun SearchResultsList(
|
|||||||
if (!collapsed) {
|
if (!collapsed) {
|
||||||
val displayNotes = textNotes.take(5)
|
val displayNotes = textNotes.take(5)
|
||||||
items(displayNotes, key = { "note-${it.id}" }) { event ->
|
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) {
|
if (textNotes.size > 5) {
|
||||||
item(key = "notes-expand") {
|
item(key = "notes-expand") {
|
||||||
ExpandableSection(
|
ExpandableSection(
|
||||||
remaining = textNotes.drop(5),
|
remaining = textNotes.drop(5),
|
||||||
) { event ->
|
) { 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) {
|
if (!collapsed) {
|
||||||
items(articles.take(5), key = { "article-${it.id}" }) { event ->
|
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) {
|
if (articles.size > 5) {
|
||||||
item(key = "articles-expand") {
|
item(key = "articles-expand") {
|
||||||
ExpandableSection(
|
ExpandableSection(
|
||||||
remaining = articles.drop(5),
|
remaining = articles.drop(5),
|
||||||
) { event ->
|
) { 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) {
|
if (!collapsed) {
|
||||||
items(otherNotes.take(5), key = { "other-${it.id}" }) { event ->
|
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
|
@Composable
|
||||||
private fun <T> ExpandableSection(
|
private fun <T> ExpandableSection(
|
||||||
remaining: List<T>,
|
remaining: List<T>,
|
||||||
|
|||||||
Reference in New Issue
Block a user