From ed919ae531cc2fb5fdc91452a9696935675ffc97 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Wed, 31 May 2023 20:08:30 -0400 Subject: [PATCH] UrlPreviewer using Main thread :( --- .../amethyst/model/UrlCachedPreviewer.kt | 33 +++--- .../service/previews/BahaUrlPreview.kt | 5 +- .../amethyst/service/previews/UrlInfoItem.kt | 20 +++- .../service/previews/UrlPreviewUtils.kt | 51 ++++---- .../ui/actions/JoinUserOrChannelView.kt | 2 +- .../amethyst/ui/components/ClickableRoute.kt | 6 +- .../amethyst/ui/components/UrlPreview.kt | 4 +- .../amethyst/ui/components/UrlPreviewCard.kt | 19 +-- .../amethyst/ui/components/UrlPreviewState.kt | 2 + .../ui/components/ZoomableContentView.kt | 8 ++ .../amethyst/ui/note/NoteCompose.kt | 110 +++++++++++------- .../amethyst/ui/note/PollNoteViewModel.kt | 2 - .../ui/screen/loggedIn/ProfileScreen.kt | 10 +- .../ui/screen/loggedIn/SearchScreen.kt | 15 +-- 14 files changed, 153 insertions(+), 134 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/UrlCachedPreviewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/UrlCachedPreviewer.kt index e13a063961..5bce8ec624 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/UrlCachedPreviewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/UrlCachedPreviewer.kt @@ -3,10 +3,6 @@ package com.vitorpamplona.amethyst.model import com.vitorpamplona.amethyst.service.previews.BahaUrlPreview import com.vitorpamplona.amethyst.service.previews.IUrlPreviewCallback import com.vitorpamplona.amethyst.service.previews.UrlInfoItem -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch object UrlCachedPreviewer { var cache = mapOf() @@ -25,22 +21,19 @@ object UrlCachedPreviewer { return } - val scope = CoroutineScope(Job() + Dispatchers.IO) - scope.launch { - BahaUrlPreview( - url, - object : IUrlPreviewCallback { - override fun onComplete(urlInfo: UrlInfoItem) { - cache = cache + Pair(url, urlInfo) - callback?.onComplete(urlInfo) - } - - override fun onFailed(throwable: Throwable) { - failures = failures + Pair(url, throwable) - callback?.onFailed(throwable) - } + BahaUrlPreview( + url, + object : IUrlPreviewCallback { + override fun onComplete(urlInfo: UrlInfoItem) { + cache = cache + Pair(url, urlInfo) + callback?.onComplete(urlInfo) } - ).fetchUrlPreview() - } + + override fun onFailed(throwable: Throwable) { + failures = failures + Pair(url, throwable) + callback?.onFailed(throwable) + } + } + ).fetchUrlPreview() } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/BahaUrlPreview.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/BahaUrlPreview.kt index e56493ceba..be7d3ddfb7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/BahaUrlPreview.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/BahaUrlPreview.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.* import java.util.* class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) { - val scope = CoroutineScope(Job() + Dispatchers.Main) + val scope = CoroutineScope(Job() + Dispatchers.IO) private val imageExtensionArray = arrayOf(".gif", ".png", ".jpg", ".jpeg", ".bmp", ".webp") fun fetchUrlPreview(timeOut: Int = 30000) { @@ -23,8 +23,7 @@ class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) { urlInfoItem = UrlInfoItem(url = url, image = url) } else { val document = getDocument(url, timeOut) - urlInfoItem = parseHtml(document) - urlInfoItem.url = url + urlInfoItem = parseHtml(url, document) } callback?.onComplete(urlInfoItem) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlInfoItem.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlInfoItem.kt index 553f9b1c1c..2547f2d224 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlInfoItem.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlInfoItem.kt @@ -1,11 +1,23 @@ package com.vitorpamplona.amethyst.service.previews +import androidx.compose.runtime.Immutable +import java.net.URL + +@Immutable data class UrlInfoItem( - var url: String = "", - var title: String = "", - var description: String = "", - var image: String = "" + val url: String = "", + val title: String = "", + val description: String = "", + val image: String = "" ) { + val verifiedUrl = kotlin.runCatching { URL(url) }.getOrNull() + val imageUrlFullPath = + if (image.startsWith("/")) { + URL(verifiedUrl, image).toString() + } else { + image + } + fun allFetchComplete(): Boolean { return title.isNotEmpty() && description.isNotEmpty() && image.isNotEmpty() } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt index 62c6e83e98..78c5d7bdda 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt @@ -67,50 +67,47 @@ suspend fun getDocument(url: String, timeOut: Int = 30000): Document = .get() } -suspend fun parseHtml(document: Document): UrlInfoItem = +suspend fun parseHtml(url: String, document: Document): UrlInfoItem = withContext(Dispatchers.IO) { val metaTags = document.getElementsByTag(ELEMENT_TAG_META) - val urlInfo = UrlInfoItem() + + var title: String = "" + var description: String = "" + var image: String = "" + metaTags.forEach { val propertyTag = it.attr(ATTRIBUTE_VALUE_PROPERTY) when (propertyTag) { - in META_OG_TITLE -> if (urlInfo.title.isEmpty()) urlInfo.title = it.attr(CONTENT) - in META_OG_DESCRIPTION -> if (urlInfo.description.isEmpty()) { - urlInfo.description = - it.attr(CONTENT) + in META_OG_TITLE -> if (title.isEmpty()) title = it.attr(CONTENT) + in META_OG_DESCRIPTION -> if (description.isEmpty()) { + description = it.attr(CONTENT) } - in META_OG_IMAGE -> if (urlInfo.image.isEmpty()) urlInfo.image = it.attr(CONTENT) + in META_OG_IMAGE -> if (image.isEmpty()) image = it.attr(CONTENT) } when (it.attr(ATTRIBUTE_VALUE_NAME)) { - in META_NAME_TITLE -> if (urlInfo.title.isEmpty()) urlInfo.title = it.attr(CONTENT) - in META_NAME_DESCRIPTION -> if (urlInfo.description.isEmpty()) { - urlInfo.description = - it.attr(CONTENT) + in META_NAME_TITLE -> if (title.isEmpty()) title = it.attr(CONTENT) + in META_NAME_DESCRIPTION -> if (description.isEmpty()) { + description = it.attr(CONTENT) } - in META_OG_IMAGE -> if (urlInfo.image.isEmpty()) urlInfo.image = it.attr(CONTENT) + in META_OG_IMAGE -> if (image.isEmpty()) image = it.attr(CONTENT) } when (it.attr(ATTRIBUTE_VALUE_ITEMPROP)) { - in META_ITEMPROP_TITLE -> if (urlInfo.title.isEmpty()) { - urlInfo.title = - it.attr(CONTENT) + in META_ITEMPROP_TITLE -> if (title.isEmpty()) { + title = it.attr(CONTENT) } - in META_ITEMPROP_DESCRIPTION -> if (urlInfo.description.isEmpty()) { - urlInfo.description = - it.attr( - CONTENT - ) + in META_ITEMPROP_DESCRIPTION -> if (description.isEmpty()) { + description = it.attr(CONTENT) } - in META_ITEMPROP_IMAGE -> if (urlInfo.image.isEmpty()) { - urlInfo.image = it.attr( - CONTENT - ) + in META_ITEMPROP_IMAGE -> if (image.isEmpty()) { + image = it.attr(CONTENT) } } - if (urlInfo.allFetchComplete()) { - return@withContext urlInfo + + if (title.isNotEmpty() && description.isNotEmpty() && image.isNotEmpty()) { + return@withContext UrlInfoItem(url, title, description, image) } } - return@withContext urlInfo + return@withContext UrlInfoItem(url, title, description, image) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt index 08ff4090ec..c609d58d4c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt @@ -268,7 +268,7 @@ private fun RenderSeach( state = listState ) { itemsIndexed( - searchBarViewModel.searchResults.value, + searchBarViewModel.searchResultsUsers.value, key = { _, item -> "u" + item.pubkeyHex } ) { _, item -> UserComposeForChat( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt index 0da6a33d73..15d560fe36 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt @@ -489,15 +489,15 @@ fun InLineIconRenderer( "inlineContent$idx", InlineTextContent( Placeholder( - width = 17.sp, - height = 17.sp, + width = 20.sp, + height = 20.sp, placeholderVerticalAlign = PlaceholderVerticalAlign.Center ) ) { AsyncImage( model = value.url, contentDescription = null, - modifier = Modifier.fillMaxSize().padding(1.dp) + modifier = Modifier.fillMaxSize().padding(horizontal = 1.dp) ) } ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreview.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreview.kt index 30bdb93fe7..ec1cc38f5b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreview.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreview.kt @@ -7,7 +7,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import com.vitorpamplona.amethyst.R @@ -32,12 +31,11 @@ fun UrlPreview(url: String, urlText: String) { mutableStateOf(default) } - val scope = rememberCoroutineScope() // Doesn't use a viewModel because of viewModel reusing issues (too many UrlPreview are created). LaunchedEffect(url) { if (urlPreviewState == UrlPreviewState.Loading) { - scope.launch(Dispatchers.IO) { + launch(Dispatchers.IO) { UrlCachedPreviewer.previewInfo( url, object : IUrlPreviewCallback { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt index 54046bde97..475b2410fd 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -22,7 +21,6 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.service.previews.UrlInfoItem -import java.net.URL @Composable fun UrlPreviewCard( @@ -42,26 +40,15 @@ fun UrlPreviewCard( ) ) { Column { - val validatedUrl = remember(url) { URL(previewInfo.url) } - - // correctly treating relative images - val imageUrl = remember(url) { - if (previewInfo.image.startsWith("/")) { - URL(validatedUrl, previewInfo.image).toString() - } else { - previewInfo.image - } - } - AsyncImage( - model = imageUrl, - contentDescription = stringResource(R.string.preview_card_image_for, validatedUrl), + model = previewInfo.imageUrlFullPath, + contentDescription = stringResource(R.string.preview_card_image_for, previewInfo.url), contentScale = ContentScale.FillWidth, modifier = Modifier.fillMaxWidth() ) Text( - text = validatedUrl.host, + text = previewInfo.verifiedUrl?.host ?: previewInfo.url, style = MaterialTheme.typography.caption, modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewState.kt index e992924e10..585d0d63b6 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewState.kt @@ -1,7 +1,9 @@ package com.vitorpamplona.amethyst.ui.components +import androidx.compose.runtime.Immutable import com.vitorpamplona.amethyst.service.previews.UrlInfoItem +@Immutable sealed class UrlPreviewState { object Loading : UrlPreviewState() class Loaded(val previewInfo: UrlInfoItem) : UrlPreviewState() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index 6c32a1d710..79f89dffcf 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -36,6 +36,7 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Report import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -82,11 +83,13 @@ import net.engawapg.lib.zoomable.zoomable import java.io.File import java.security.MessageDigest +@Immutable abstract class ZoomableContent( val description: String? = null, val dim: String? = null ) +@Immutable abstract class ZoomableUrlContent( val url: String, description: String? = null, @@ -95,6 +98,7 @@ abstract class ZoomableUrlContent( val uri: String? = null ) : ZoomableContent(description, dim) +@Immutable class ZoomableUrlImage( url: String, description: String? = null, @@ -104,6 +108,7 @@ class ZoomableUrlImage( uri: String? = null ) : ZoomableUrlContent(url, description, hash, dim, uri) +@Immutable class ZoomableUrlVideo( url: String, description: String? = null, @@ -112,6 +117,7 @@ class ZoomableUrlVideo( uri: String? = null ) : ZoomableUrlContent(url, description, hash, dim, uri) +@Immutable abstract class ZoomablePreloadedContent( val localFile: File?, description: String? = null, @@ -121,6 +127,7 @@ abstract class ZoomablePreloadedContent( val uri: String ) : ZoomableContent(description, dim) +@Immutable class ZoomableLocalImage( localFile: File?, mimeType: String? = null, @@ -131,6 +138,7 @@ class ZoomableLocalImage( uri: String ) : ZoomablePreloadedContent(localFile, description, mimeType, isVerified, dim, uri) +@Immutable class ZoomableLocalVideo( localFile: File?, mimeType: String? = null, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 6d5db9f434..4b54b9485a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -78,6 +78,7 @@ import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.get import coil.compose.AsyncImage import coil.compose.AsyncImagePainter +import coil.request.SuccessResult import com.google.accompanist.flowlayout.FlowRow import com.vitorpamplona.amethyst.NotificationCache import com.vitorpamplona.amethyst.R @@ -1679,19 +1680,24 @@ fun BadgeDisplay(baseNote: Note) { val background = MaterialTheme.colors.background val badgeData = baseNote.event as? BadgeDefinitionEvent ?: return - val image = remember { badgeData.image() } + val image = remember { badgeData.thumb()?.ifBlank { null } ?: badgeData.image() } val name = remember { badgeData.name() } val description = remember { badgeData.description() } var backgroundFromImage by remember { mutableStateOf(Pair(background, background)) } - var imageResult by remember { mutableStateOf(null) } + var imageResult by remember { mutableStateOf(null) } LaunchedEffect(key1 = imageResult) { - withContext(Dispatchers.IO) { + launch(Dispatchers.IO) { imageResult?.let { - val backgroundColor = it.result.drawable.toBitmap(200, 200).copy(Bitmap.Config.ARGB_8888, false).get(0, 199) + val backgroundColor = it.drawable.toBitmap(200, 200).copy(Bitmap.Config.ARGB_8888, false).get(0, 199) val colorFromImage = Color(backgroundColor) - val textBackground = if (colorFromImage.luminance() > 0.5) lightColors().onBackground else darkColors().onBackground + val textBackground = if (colorFromImage.luminance() > 0.5) { + lightColors().onBackground + } else { + darkColors().onBackground + } + backgroundFromImage = Pair(colorFromImage, textBackground) } } @@ -1708,47 +1714,65 @@ fun BadgeDisplay(baseNote: Note) { ) .background(backgroundFromImage.first) ) { - Column { - image.let { - AsyncImage( - model = it, - contentDescription = stringResource( - R.string.badge_award_image_for, - name ?: "" - ), - contentScale = ContentScale.FillWidth, - modifier = Modifier.fillMaxWidth(), - onSuccess = { - imageResult = it - } - ) + RenderBadge( + image, + name, + backgroundFromImage.second, + description + ) { + if (imageResult == null) { + imageResult = it.result } + } + } +} - name?.let { - Text( - text = it, - style = MaterialTheme.typography.body1, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(start = 10.dp, end = 10.dp), - color = backgroundFromImage.second - ) - } +@Composable +private fun RenderBadge( + image: String?, + name: String?, + backgroundFromImage: Color, + description: String?, + onSuccess: (AsyncImagePainter.State.Success) -> Unit +) { + Column { + image.let { + AsyncImage( + model = it, + contentDescription = stringResource( + R.string.badge_award_image_for, + name ?: "" + ), + contentScale = ContentScale.FillWidth, + modifier = Modifier.fillMaxWidth(), + onSuccess = onSuccess + ) + } - description?.let { - Text( - text = it, - style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(start = 10.dp, end = 10.dp, bottom = 10.dp), - color = Color.Gray, - maxLines = 3, - overflow = TextOverflow.Ellipsis - ) - } + name?.let { + Text( + text = it, + style = MaterialTheme.typography.body1, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(start = 10.dp, end = 10.dp), + color = backgroundFromImage + ) + } + + description?.let { + Text( + text = it, + style = MaterialTheme.typography.caption, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(start = 10.dp, end = 10.dp, bottom = 10.dp), + color = Color.Gray, + maxLines = 3, + overflow = TextOverflow.Ellipsis + ) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt index 76fc7a8075..59b6fbb781 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNoteViewModel.kt @@ -3,12 +3,10 @@ package com.vitorpamplona.amethyst.ui.note import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.model.RelaySetupInfo import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.model.* import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt index 27908bd3d3..fa9ea6c153 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt @@ -95,6 +95,7 @@ import com.vitorpamplona.amethyst.ui.screen.RelayFeedView import com.vitorpamplona.amethyst.ui.screen.RelayFeedViewModel import com.vitorpamplona.amethyst.ui.screen.UserFeedView import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -736,7 +737,7 @@ private fun DisplayBadges( if (list.isNullOrEmpty()) { null } else { - list + list.toImmutableList() } } } @@ -759,9 +760,8 @@ private fun LoadAndRenderBadge(badgeAwardEventHex: String, nav: (String) -> Unit mutableStateOf(null) } - val scope = rememberCoroutineScope() LaunchedEffect(key1 = badgeAwardEventHex) { - scope.launch(Dispatchers.IO) { + launch(Dispatchers.IO) { baseNote = LocalCache.getOrCreateNote(badgeAwardEventHex) } } @@ -788,7 +788,7 @@ fun BadgeThumb( pictureModifier: Modifier = Modifier ) { BadgeThumb(note, size, pictureModifier) { - nav("Note/$it") + nav("Note/${note.idHex}") } } @@ -808,7 +808,7 @@ fun BadgeThumb( ) { val noteState by baseNote.live().metadata.observeAsState() val event = remember(noteState) { noteState?.note?.event as? BadgeDefinitionEvent } ?: return - val image = remember(noteState) { event.thumb() ?: event.image() } + val image = remember(noteState) { event.thumb()?.ifBlank { null } ?: event.image()?.ifBlank { null } } if (image == null) { RobohashAsyncImage( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt index d96aceda39..b64f3b7f74 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt @@ -122,6 +122,7 @@ fun SearchScreen( fun WatchAccountForSearchScreen(searchFeedViewModel: NostrGlobalFeedViewModel, accountViewModel: AccountViewModel) { LaunchedEffect(accountViewModel) { NostrGlobalDataSource.resetFilters() + NostrSearchEventOrUserDataSource.start() searchFeedViewModel.invalidateData(true) } } @@ -130,7 +131,7 @@ class SearchBarViewModel : ViewModel() { var account: Account? = null var searchValue by mutableStateOf("") - val searchResults = mutableStateOf>(emptyList()) + val searchResultsUsers = mutableStateOf>(emptyList()) val searchResultsNotes = mutableStateOf>(emptyList()) val searchResultsChannels = mutableStateOf>(emptyList()) val hashtagResults = mutableStateOf>(emptyList()) @@ -147,21 +148,21 @@ class SearchBarViewModel : ViewModel() { private fun runSearch() { if (searchValue.isBlank()) { hashtagResults.value = emptyList() - searchResults.value = emptyList() + searchResultsUsers.value = emptyList() searchResultsChannels.value = emptyList() searchResultsNotes.value = emptyList() return } hashtagResults.value = findHashtags(searchValue) - searchResults.value = LocalCache.findUsersStartingWith(searchValue).sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() })).reversed() + searchResultsUsers.value = LocalCache.findUsersStartingWith(searchValue).sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() })).reversed() searchResultsNotes.value = LocalCache.findNotesStartingWith(searchValue).sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed() searchResultsChannels.value = LocalCache.findChannelsStartingWith(searchValue) } fun clean() { searchValue = "" - searchResults.value = emptyList() + searchResultsUsers.value = emptyList() searchResultsChannels.value = emptyList() searchResultsNotes.value = emptyList() } @@ -183,7 +184,7 @@ class SearchBarViewModel : ViewModel() { @Composable private fun SearchBar(accountViewModel: AccountViewModel, nav: (String) -> Unit) { val searchBarViewModel: SearchBarViewModel = viewModel() - searchBarViewModel.account = accountViewModel.accountLiveData.value?.account + searchBarViewModel.account = accountViewModel.account val scope = rememberCoroutineScope() val listState = rememberLazyListState() @@ -196,7 +197,7 @@ private fun SearchBar(accountViewModel: AccountViewModel, nav: (String) -> Unit) } LaunchedEffect(Unit) { - scope.launch(Dispatchers.IO) { + launch(Dispatchers.IO) { LocalCache.live.newEventBundles.collect { if (searchBarViewModel.isSearching()) { searchBarViewModel.invalidateData() @@ -306,7 +307,7 @@ private fun SearchBar(accountViewModel: AccountViewModel, nav: (String) -> Unit) } } - itemsIndexed(searchBarViewModel.searchResults.value, key = { _, item -> "u" + item.pubkeyHex }) { _, item -> + itemsIndexed(searchBarViewModel.searchResultsUsers.value, key = { _, item -> "u" + item.pubkeyHex }) { _, item -> UserCompose(item, accountViewModel = accountViewModel, nav = nav) }