diff --git a/app/src/androidTest/java/com/vitorpamplona/amethyst/RichTextParserTest.kt b/app/src/androidTest/java/com/vitorpamplona/amethyst/RichTextParserTest.kt index b6a58ba2a..9fbc7ce43 100644 --- a/app/src/androidTest/java/com/vitorpamplona/amethyst/RichTextParserTest.kt +++ b/app/src/androidTest/java/com/vitorpamplona/amethyst/RichTextParserTest.kt @@ -3,7 +3,7 @@ package com.vitorpamplona.amethyst import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.amethyst.service.RichTextParser import com.vitorpamplona.amethyst.service.RichTextViewerState -import com.vitorpamplona.quartz.events.ImmutableListOfLists +import com.vitorpamplona.quartz.events.EmptyTagList import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith @@ -666,7 +666,7 @@ class RichTextParserTest { @Test fun testTextToParse() { - val state = RichTextParser().parseText(textToParse, ImmutableListOfLists()) + val state = RichTextParser().parseText(textToParse, EmptyTagList) Assert.assertEquals( "relay.shitforce.one, relayable.org, universe.nostrich.land, nos.lol, universe.nostrich.land?lang=zh, universe.nostrich.land?lang=en, relay.damus.io, relay.nostr.wirednet.jp, offchain.pub, nostr.rocks, relay.wellorder.net, nostr.oxtr.dev, universe.nostrich.land?lang=ja, relay.mostr.pub, nostr.bitcoiner.social, Nostr-Check.com, MR.Rabbit, Ancap.su, zapper.lol, smies.me, baller.hodl", state.urlSet.joinToString(", ") @@ -3989,7 +3989,7 @@ class RichTextParserTest { @Test fun testShortTextToParse() { - val state = RichTextParser().parseText("Hi, how are you doing? ", ImmutableListOfLists()) + val state = RichTextParser().parseText("Hi, how are you doing? ", EmptyTagList) Assert.assertTrue(state.urlSet.isEmpty()) Assert.assertTrue(state.imagesForPager.isEmpty()) Assert.assertTrue(state.imageList.isEmpty()) @@ -3999,7 +3999,7 @@ class RichTextParserTest { @Test fun testShortNewLinesTextToParse() { - val state = RichTextParser().parseText("\nHi, \nhow\n\n\n are you doing? \n", ImmutableListOfLists()) + val state = RichTextParser().parseText("\nHi, \nhow\n\n\n are you doing? \n", EmptyTagList) Assert.assertTrue(state.urlSet.isEmpty()) Assert.assertTrue(state.imagesForPager.isEmpty()) Assert.assertTrue(state.imageList.isEmpty()) @@ -4019,7 +4019,7 @@ See how it can be done here: https://lnshort.it/live-stream-embeds/ https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db214e8a.jpg """.trimIndent() - val state = RichTextParser().parseText(text, ImmutableListOfLists()) + val state = RichTextParser().parseText(text, EmptyTagList) Assert.assertEquals("https://lnshort.it/live-stream-embeds/", state.urlSet.firstOrNull()) Assert.assertEquals("https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db214e8a.jpg", state.imagesForPager.keys.firstOrNull()) Assert.assertEquals("https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db214e8a.jpg", state.imageList.firstOrNull()?.url) @@ -4080,7 +4080,7 @@ https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db fun testNewLineAfterImage() { val text = "That’s it ! That’s the #note https://cdn.nostr.build/i/1dc0726b6cb0f94a92bd66765ffb90f6c67e90c17bb957fc3d5d4782cbd73de7.jpg " - val state = RichTextParser().parseText(text, ImmutableListOfLists()) + val state = RichTextParser().parseText(text, EmptyTagList) printStateForDebug(state) @@ -4106,7 +4106,7 @@ https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db fun testSapceAfterImage() { val text = "That’s it! https://cdn.nostr.build/i/1dc0726b6cb0f94a92bd66765ffb90f6c67e90c17bb957fc3d5d4782cbd73de7.jpg That’s the #note" - val state = RichTextParser().parseText(text, ImmutableListOfLists()) + val state = RichTextParser().parseText(text, EmptyTagList) printStateForDebug(state) 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 2aa55d4f9..617679a2d 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 @@ -6,6 +6,7 @@ import kotlinx.coroutines.withContext import okhttp3.Request import org.jsoup.Jsoup import org.jsoup.nodes.Document +import java.lang.IllegalArgumentException private const val ELEMENT_TAG_META = "meta" private const val ATTRIBUTE_VALUE_PROPERTY = "property" @@ -64,7 +65,11 @@ suspend fun getDocument(url: String, timeOut: Int = 30000): Document = withContext(Dispatchers.IO) { val request: Request = Request.Builder().url(url).get().build() val html = HttpClient.getHttpClient().newCall(request).execute().use { - it.body.string() + if (it.isSuccessful) { + it.body.string() + } else { + throw IllegalArgumentException("Website returned: " + it.code) + } } Jsoup.parse(html) 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 48634245c..6d7592ec5 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 @@ -62,7 +62,7 @@ fun UrlPreviewCard( style = MaterialTheme.typography.caption, modifier = Modifier .fillMaxWidth() - .padding(start = 10.dp, end = 10.dp, top = 10.dp), + .padding(start = 10.dp, end = 10.dp, top = 5.dp), color = Color.Gray, maxLines = 1, overflow = TextOverflow.Ellipsis diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt index 70a54ff35..ea3d887f8 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -612,7 +613,7 @@ fun Waveform( DrawWaveform(waveform, waveformProgress, modifier) val restartFlow = remember { - mutableStateOf(0) + mutableIntStateOf(0) } // Keeps the screen on while playing and viewing videos. 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 def9f9f4d..2e154fa0d 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 @@ -477,14 +477,16 @@ private fun AddedImageFeatures( if (content.blurhash != null) { DisplayBlurHash(content.blurhash, content.description, contentScale, myModifier) } else { - FlowRow() { + FlowRow(Modifier.fillMaxWidth()) { DisplayUrlWithLoadingSymbol(content) } } } is AsyncImagePainter.State.Error -> { - ClickableUrl(urlText = "${content.url} ", url = content.url) + FlowRow(Modifier.fillMaxWidth()) { + ClickableUrl(urlText = "${content.url} ", url = content.url) + } } is AsyncImagePainter.State.Success -> { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt index 93617ddea..6d1b76d71 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -66,6 +67,7 @@ import com.vitorpamplona.amethyst.ui.theme.subtleBorder import com.vitorpamplona.quartz.events.ChannelCreateEvent import com.vitorpamplona.quartz.events.ChannelMetadataEvent import com.vitorpamplona.quartz.events.ChatMessageEvent +import com.vitorpamplona.quartz.events.EmptyTagList import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.PrivateDmEvent import com.vitorpamplona.quartz.events.toImmutableListOfLists @@ -285,7 +287,7 @@ fun NormalChatNote( modifier = modif, horizontalArrangement = alignment ) { - val availableBubbleSize = remember { mutableStateOf(0) } + val availableBubbleSize = remember { mutableIntStateOf(0) } var popupExpanded by remember { mutableStateOf(false) } val modif2 = remember { @@ -355,7 +357,7 @@ private fun RenderBubble( accountViewModel: AccountViewModel, nav: (String) -> Unit ) { - val bubbleSize = remember { mutableStateOf(0) } + val bubbleSize = remember { mutableIntStateOf(0) } val bubbleModifier = remember { Modifier @@ -626,7 +628,7 @@ private fun RenderRegularTextNote( accountViewModel: AccountViewModel, nav: (String) -> Unit ) { - val tags = remember(note.event) { note.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + val tags = remember(note.event) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList } val eventContent by remember { mutableStateOf(accountViewModel.decrypt(note)) } val modifier = remember { Modifier.padding(top = 5.dp) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt index 614d36a51..601e1f535 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt @@ -70,7 +70,7 @@ import com.vitorpamplona.amethyst.ui.theme.bitcoinColor import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor import com.vitorpamplona.amethyst.ui.theme.overPictureBackground import com.vitorpamplona.amethyst.ui.theme.profile35dpModifier -import com.vitorpamplona.quartz.events.ImmutableListOfLists +import com.vitorpamplona.quartz.events.EmptyTagList import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.Dispatchers @@ -484,7 +484,7 @@ fun CrossfadeToDisplayComment( TranslatableRichTextViewer( content = it, canPreview = true, - tags = remember { ImmutableListOfLists() }, + tags = EmptyTagList, modifier = textBoxModifier, backgroundColor = backgroundColor, accountViewModel = accountViewModel, 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 3b5b6d2ae..616268c11 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 @@ -179,12 +179,12 @@ import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent import com.vitorpamplona.quartz.events.EmojiPackEvent import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent import com.vitorpamplona.quartz.events.EmojiUrl +import com.vitorpamplona.quartz.events.EmptyTagList import com.vitorpamplona.quartz.events.EventInterface import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileStorageHeaderEvent import com.vitorpamplona.quartz.events.GenericRepostEvent import com.vitorpamplona.quartz.events.HighlightEvent -import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent import com.vitorpamplona.quartz.events.LiveActivitiesEvent import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE @@ -631,7 +631,7 @@ fun LongCommunityHeader( TranslatableRichTextViewer( content = summary ?: stringResource(id = R.string.community_no_descriptor), canPreview = false, - tags = remember { ImmutableListOfLists(emptyList()) }, + tags = EmptyTagList, backgroundColor = background, accountViewModel = accountViewModel, nav = nav @@ -1343,7 +1343,7 @@ fun RenderTextEvent( accountViewModel = accountViewModel ) { val modifier = remember(note) { Modifier.fillMaxWidth() } - val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList } TranslatableRichTextViewer( content = eventContent, @@ -1382,7 +1382,7 @@ fun RenderPoll( overflow = TextOverflow.Ellipsis ) } else { - val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList } SensitivityWarning( note = note, @@ -1557,7 +1557,7 @@ fun RenderAppDefinition( Row( modifier = Modifier.padding(top = 5.dp, bottom = 5.dp) ) { - val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList } val bgColor = MaterialTheme.colors.background val backgroundColor = remember { mutableStateOf(bgColor) @@ -1630,7 +1630,7 @@ private fun RenderPrivateMessage( val modifier = remember(note.event?.id()) { Modifier.fillMaxWidth() } val isAuthorTheLoggedUser = remember(note.event?.id()) { accountViewModel.isLoggedUser(note.author) } - val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList } if (eventContent != null) { if (makeItShort && isAuthorTheLoggedUser) { @@ -1670,7 +1670,7 @@ private fun RenderPrivateMessage( ), canPreview = !makeItShort, Modifier.fillMaxWidth(), - ImmutableListOfLists(), + EmptyTagList, backgroundColor, accountViewModel, nav @@ -2308,7 +2308,7 @@ fun RenderPinListEvent( TranslatableRichTextViewer( content = pin, canPreview = true, - tags = remember { ImmutableListOfLists() }, + tags = EmptyTagList, backgroundColor = backgroundColor, accountViewModel = accountViewModel, nav = nav @@ -2407,8 +2407,8 @@ private fun RenderReport( TranslatableRichTextViewer( content = content, canPreview = true, - modifier = remember { Modifier }, - tags = remember { ImmutableListOfLists() }, + modifier = Modifier, + tags = EmptyTagList, backgroundColor = backgroundColor, accountViewModel = accountViewModel, nav = nav @@ -2880,7 +2880,7 @@ fun DisplayHighlight( quote, canPreview = canPreview && !makeItShort, remember { Modifier.fillMaxWidth() }, - remember { ImmutableListOfLists(emptyList()) }, + EmptyTagList, backgroundColor, accountViewModel, nav @@ -3527,7 +3527,7 @@ fun AudioHeader(noteEvent: AudioHeaderEvent, note: Note, accountViewModel: Accou val defaultBackground = MaterialTheme.colors.background val background = remember { mutableStateOf(defaultBackground) } - val tags = remember(noteEvent) { noteEvent?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + val tags = remember(noteEvent) { noteEvent?.tags()?.toImmutableListOfLists() ?: EmptyTagList } val eventContent = remember(note.event) { val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt index a573fb13b..628485cb4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt @@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.placeholderText +import com.vitorpamplona.quartz.events.EmptyTagList import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.toImmutableListOfLists @@ -116,7 +117,7 @@ private fun OptionNote( nav: (String) -> Unit ) { val tags = remember(baseNote) { - baseNote.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() + baseNote.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList } Row( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index c3a7c26e4..56de8f45f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -129,7 +129,7 @@ import com.vitorpamplona.amethyst.ui.theme.SmallBorder import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.placeholderText -import com.vitorpamplona.quartz.events.ImmutableListOfLists +import com.vitorpamplona.quartz.events.EmptyTagList import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE import com.vitorpamplona.quartz.events.Participant import com.vitorpamplona.quartz.events.toImmutableListOfLists @@ -751,9 +751,9 @@ fun LongChannelHeader( val tags = remember(channelState) { if (baseChannel is LiveActivitiesChannel) { - baseChannel.info?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() + baseChannel.info?.tags()?.toImmutableListOfLists() ?: EmptyTagList } else { - ImmutableListOfLists() + EmptyTagList } } 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 3e035b97b..cd22683a5 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 @@ -54,6 +54,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account +import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User @@ -97,9 +98,9 @@ import com.vitorpamplona.quartz.events.AppDefinitionEvent import com.vitorpamplona.quartz.events.BadgeDefinitionEvent import com.vitorpamplona.quartz.events.BadgeProfilesEvent import com.vitorpamplona.quartz.events.ChatroomKey +import com.vitorpamplona.quartz.events.EmptyTagList import com.vitorpamplona.quartz.events.GitHubIdentity import com.vitorpamplona.quartz.events.IdentityClaim -import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.MastodonIdentity import com.vitorpamplona.quartz.events.PayInvoiceErrorResponse import com.vitorpamplona.quartz.events.PayInvoiceSuccessResponse @@ -118,7 +119,7 @@ import java.math.BigDecimal fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, nav: (String) -> Unit) { if (userId == null) return - var userBase by remember { mutableStateOf(LocalCache.getUserIfExists(userId)) } + var userBase by remember { mutableStateOf(LocalCache.getUserIfExists(userId)) } if (userBase == null) { LaunchedEffect(userId) { @@ -280,7 +281,6 @@ fun ProfileScreen( } @Composable -@OptIn(ExperimentalFoundationApi::class) private fun RenderSurface( baseUser: User, threadsViewModel: NostrUserProfileNewThreadsFeedViewModel, @@ -383,7 +383,7 @@ private fun RenderScreen( ) { val pagerState = rememberPagerState { 9 } - Column() { + Column { ProfileHeader(baseUser, appRecommendations, nav, accountViewModel) ScrollableTabRow( backgroundColor = MaterialTheme.colors.background, @@ -449,7 +449,7 @@ private fun CreateAndRenderTabs( ) { val coroutineScope = rememberCoroutineScope() - val tabs = listOf<@Composable() (() -> Unit)?>( + val tabs = listOf<@Composable (() -> Unit)?>( { Text(text = stringResource(R.string.notes)) }, { Text(text = stringResource(R.string.replies)) }, { FollowTabHeader(baseUser) }, @@ -486,7 +486,7 @@ private fun RelaysTabHeader(baseUser: User) { @Composable private fun ReportsTabHeader(baseUser: User) { val userState by baseUser.live().reports.observeAsState() - var userReports by remember { mutableStateOf(0) } + var userReports by remember { mutableIntStateOf(0) } LaunchedEffect(key1 = userState) { launch(Dispatchers.IO) { @@ -503,11 +503,11 @@ private fun ReportsTabHeader(baseUser: User) { @Composable private fun FollowedTagsTabHeader(baseUser: User) { - var usertags by remember { mutableStateOf(0) } + var usertags by remember { mutableIntStateOf(0) } LaunchedEffect(key1 = baseUser) { launch(Dispatchers.IO) { - val contactList = baseUser?.latestContactList + val contactList = baseUser.latestContactList val newTags = (contactList?.verifiedFollowTagSet?.count() ?: 0) @@ -524,7 +524,7 @@ private fun FollowedTagsTabHeader(baseUser: User) { private fun BookmarkTabHeader(baseUser: User) { val userState by baseUser.live().bookmarks.observeAsState() - var userBookmarks by remember { mutableStateOf(0) } + var userBookmarks by remember { mutableIntStateOf(0) } LaunchedEffect(key1 = userState) { launch(Dispatchers.IO) { @@ -716,8 +716,6 @@ private fun ProfileActions( EditButton(accountViewModel.account) } - val scope = rememberCoroutineScope() - WatchIsHiddenUser(baseUser, accountViewModel) { isHidden -> if (isHidden) { ShowUserButton { @@ -1002,7 +1000,7 @@ private fun DrawAdditionalInfo( TranslatableRichTextViewer( content = it, canPreview = false, - tags = remember { ImmutableListOfLists(emptyList()) }, + tags = EmptyTagList, backgroundColor = background, accountViewModel = accountViewModel, nav = nav @@ -1114,7 +1112,7 @@ private fun DisplayAppRecommendations( ) { state -> when (state) { is FeedState.Loaded -> { - Column() { + Column { Text(stringResource(id = R.string.recommended_apps)) FlowRow( @@ -1137,7 +1135,7 @@ private fun DisplayAppRecommendations( private fun WatchApp(baseApp: Note, nav: (String) -> Unit) { val appState by baseApp.live().metadata.observeAsState() - var appLogo by remember { mutableStateOf(null) } + var appLogo by remember(baseApp) { mutableStateOf(null) } LaunchedEffect(key1 = appState) { launch(Dispatchers.Default) { @@ -1185,19 +1183,27 @@ private fun DisplayBadges( null ), accountViewModel - ) { - if (it != null) { - val badgeList by it.live().metadata.map { - (it.note.event as? BadgeProfilesEvent)?.badgeAwardEvents()?.toImmutableList() - }.distinctUntilChanged().observeAsState() - - badgeList?.let { list -> - RenderBadgeList(list, nav) - } + ) { note -> + if (note != null) { + WatchAndRenderBadgeList(note, nav) } } } +@Composable +private fun WatchAndRenderBadgeList( + note: AddressableNote, + nav: (String) -> Unit +) { + val badgeList by note.live().metadata.map { + (it.note.event as? BadgeProfilesEvent)?.badgeAwardEvents()?.toImmutableList() + }.distinctUntilChanged().observeAsState() + + badgeList?.let { list -> + RenderBadgeList(list, nav) + } +} + @Composable @OptIn(ExperimentalLayoutApi::class) private fun RenderBadgeList( @@ -1217,7 +1223,7 @@ private fun RenderBadgeList( @Composable private fun LoadAndRenderBadge(badgeAwardEventHex: String, nav: (String) -> Unit) { var baseNote by remember { - mutableStateOf(LocalCache.getNoteIfExists(badgeAwardEventHex)) + mutableStateOf(LocalCache.getNoteIfExists(badgeAwardEventHex)) } LaunchedEffect(key1 = badgeAwardEventHex) { @@ -1462,7 +1468,7 @@ fun TabFollows(baseUser: User, feedViewModel: UserFeedViewModel, accountViewMode WatchFollowChanges(baseUser, feedViewModel) Column(Modifier.fillMaxHeight()) { - Column() { + Column { RefreshingFeedUserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false) } } @@ -1473,7 +1479,7 @@ fun TabFollowers(baseUser: User, feedViewModel: UserFeedViewModel, accountViewMo WatchFollowerChanges(baseUser, feedViewModel) Column(Modifier.fillMaxHeight()) { - Column() { + Column { RefreshingFeedUserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false) } } @@ -1508,7 +1514,7 @@ fun TabReceivedZaps(baseUser: User, zapFeedViewModel: NostrUserProfileZapsFeedVi WatchZapsAndUpdateFeed(baseUser, zapFeedViewModel) Column(Modifier.fillMaxHeight()) { - Column() { + Column { LnZapFeedView(zapFeedViewModel, accountViewModel, nav) } } @@ -1531,7 +1537,7 @@ fun TabReports(baseUser: User, feedViewModel: NostrUserProfileReportFeedViewMode WatchReportsAndUpdateFeed(baseUser, feedViewModel) Column(Modifier.fillMaxHeight()) { - Column() { + Column { RefresheableFeedView( feedViewModel, null, @@ -1703,7 +1709,6 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () -> onDismissRequest = onDismiss ) { val clipboardManager = LocalClipboardManager.current - val scope = rememberCoroutineScope() DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); onDismiss() }) { Text(stringResource(R.string.copy_user_id)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt index c014bdc4f..04108eb10 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt @@ -15,16 +15,28 @@ class AppDefinitionEvent( content: String, sig: HexKey ) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) { - fun appMetaData() = try { - mapper.readValue( - ByteArrayInputStream(content.toByteArray(Charsets.UTF_8)), - UserMetadata::class.java - ) - } catch (e: Exception) { - e.printStackTrace() - Log.w("MT", "Content Parse Error ${e.localizedMessage} $content") - null - } + @Transient + private var cachedMetadata: UserMetadata? = null + + fun appMetaData() = + if (cachedMetadata != null) { + cachedMetadata + } else { + try { + val newMetadata = mapper.readValue( + ByteArrayInputStream(content.toByteArray(Charsets.UTF_8)), + UserMetadata::class.java + ) + + cachedMetadata = newMetadata + + newMetadata + } catch (e: Exception) { + e.printStackTrace() + Log.w("MT", "Content Parse Error ${e.localizedMessage} $content") + null + } + } fun supportedKinds() = tags.filter { it.size > 1 && it[0] == "k" }.mapNotNull { runCatching { it[1].toInt() }.getOrNull() diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt index df3aa0275..ace1b5f67 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt @@ -350,6 +350,8 @@ class UserMetadata { @Stable data class ImmutableListOfLists(val lists: List> = emptyList()) +val EmptyTagList = ImmutableListOfLists(emptyList()) + fun List>.toImmutableListOfLists(): ImmutableListOfLists { return ImmutableListOfLists(this) } \ No newline at end of file