Code clean up

This commit is contained in:
Vitor Pamplona
2023-09-19 16:43:57 -04:00
parent c2beaf5f80
commit 15e7540b26
13 changed files with 102 additions and 72 deletions
@@ -3,7 +3,7 @@ package com.vitorpamplona.amethyst
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.vitorpamplona.amethyst.service.RichTextParser import com.vitorpamplona.amethyst.service.RichTextParser
import com.vitorpamplona.amethyst.service.RichTextViewerState 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.Assert
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -666,7 +666,7 @@ class RichTextParserTest {
@Test @Test
fun testTextToParse() { fun testTextToParse() {
val state = RichTextParser().parseText(textToParse, ImmutableListOfLists()) val state = RichTextParser().parseText(textToParse, EmptyTagList)
Assert.assertEquals( 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", "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(", ") state.urlSet.joinToString(", ")
@@ -3989,7 +3989,7 @@ class RichTextParserTest {
@Test @Test
fun testShortTextToParse() { 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.urlSet.isEmpty())
Assert.assertTrue(state.imagesForPager.isEmpty()) Assert.assertTrue(state.imagesForPager.isEmpty())
Assert.assertTrue(state.imageList.isEmpty()) Assert.assertTrue(state.imageList.isEmpty())
@@ -3999,7 +3999,7 @@ class RichTextParserTest {
@Test @Test
fun testShortNewLinesTextToParse() { 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.urlSet.isEmpty())
Assert.assertTrue(state.imagesForPager.isEmpty()) Assert.assertTrue(state.imagesForPager.isEmpty())
Assert.assertTrue(state.imageList.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 https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db214e8a.jpg
""".trimIndent() """.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://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.imagesForPager.keys.firstOrNull())
Assert.assertEquals("https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db214e8a.jpg", state.imageList.firstOrNull()?.url) Assert.assertEquals("https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db214e8a.jpg", state.imageList.firstOrNull()?.url)
@@ -4080,7 +4080,7 @@ https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db
fun testNewLineAfterImage() { fun testNewLineAfterImage() {
val text = "Thats it ! Thats the #note https://cdn.nostr.build/i/1dc0726b6cb0f94a92bd66765ffb90f6c67e90c17bb957fc3d5d4782cbd73de7.jpg " val text = "Thats it ! Thats the #note https://cdn.nostr.build/i/1dc0726b6cb0f94a92bd66765ffb90f6c67e90c17bb957fc3d5d4782cbd73de7.jpg "
val state = RichTextParser().parseText(text, ImmutableListOfLists()) val state = RichTextParser().parseText(text, EmptyTagList)
printStateForDebug(state) printStateForDebug(state)
@@ -4106,7 +4106,7 @@ https://nostr.build/i/fd53fcf5ad950fbe45127e4bcee1b59e8301d41de6beee211f45e344db
fun testSapceAfterImage() { fun testSapceAfterImage() {
val text = "Thats it! https://cdn.nostr.build/i/1dc0726b6cb0f94a92bd66765ffb90f6c67e90c17bb957fc3d5d4782cbd73de7.jpg Thats the #note" val text = "Thats it! https://cdn.nostr.build/i/1dc0726b6cb0f94a92bd66765ffb90f6c67e90c17bb957fc3d5d4782cbd73de7.jpg Thats the #note"
val state = RichTextParser().parseText(text, ImmutableListOfLists()) val state = RichTextParser().parseText(text, EmptyTagList)
printStateForDebug(state) printStateForDebug(state)
@@ -6,6 +6,7 @@ import kotlinx.coroutines.withContext
import okhttp3.Request import okhttp3.Request
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.lang.IllegalArgumentException
private const val ELEMENT_TAG_META = "meta" private const val ELEMENT_TAG_META = "meta"
private const val ATTRIBUTE_VALUE_PROPERTY = "property" private const val ATTRIBUTE_VALUE_PROPERTY = "property"
@@ -64,7 +65,11 @@ suspend fun getDocument(url: String, timeOut: Int = 30000): Document =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val request: Request = Request.Builder().url(url).get().build() val request: Request = Request.Builder().url(url).get().build()
val html = HttpClient.getHttpClient().newCall(request).execute().use { 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) Jsoup.parse(html)
@@ -62,7 +62,7 @@ fun UrlPreviewCard(
style = MaterialTheme.typography.caption, style = MaterialTheme.typography.caption,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = 10.dp, end = 10.dp, top = 10.dp), .padding(start = 10.dp, end = 10.dp, top = 5.dp),
color = Color.Gray, color = Color.Gray,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -29,6 +29,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@@ -612,7 +613,7 @@ fun Waveform(
DrawWaveform(waveform, waveformProgress, modifier) DrawWaveform(waveform, waveformProgress, modifier)
val restartFlow = remember { val restartFlow = remember {
mutableStateOf(0) mutableIntStateOf(0)
} }
// Keeps the screen on while playing and viewing videos. // Keeps the screen on while playing and viewing videos.
@@ -477,14 +477,16 @@ private fun AddedImageFeatures(
if (content.blurhash != null) { if (content.blurhash != null) {
DisplayBlurHash(content.blurhash, content.description, contentScale, myModifier) DisplayBlurHash(content.blurhash, content.description, contentScale, myModifier)
} else { } else {
FlowRow() { FlowRow(Modifier.fillMaxWidth()) {
DisplayUrlWithLoadingSymbol(content) DisplayUrlWithLoadingSymbol(content)
} }
} }
} }
is AsyncImagePainter.State.Error -> { 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 -> { is AsyncImagePainter.State.Success -> {
@@ -25,6 +25,7 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue 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.ChannelCreateEvent
import com.vitorpamplona.quartz.events.ChannelMetadataEvent import com.vitorpamplona.quartz.events.ChannelMetadataEvent
import com.vitorpamplona.quartz.events.ChatMessageEvent import com.vitorpamplona.quartz.events.ChatMessageEvent
import com.vitorpamplona.quartz.events.EmptyTagList
import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.ImmutableListOfLists
import com.vitorpamplona.quartz.events.PrivateDmEvent import com.vitorpamplona.quartz.events.PrivateDmEvent
import com.vitorpamplona.quartz.events.toImmutableListOfLists import com.vitorpamplona.quartz.events.toImmutableListOfLists
@@ -285,7 +287,7 @@ fun NormalChatNote(
modifier = modif, modifier = modif,
horizontalArrangement = alignment horizontalArrangement = alignment
) { ) {
val availableBubbleSize = remember { mutableStateOf(0) } val availableBubbleSize = remember { mutableIntStateOf(0) }
var popupExpanded by remember { mutableStateOf(false) } var popupExpanded by remember { mutableStateOf(false) }
val modif2 = remember { val modif2 = remember {
@@ -355,7 +357,7 @@ private fun RenderBubble(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val bubbleSize = remember { mutableStateOf(0) } val bubbleSize = remember { mutableIntStateOf(0) }
val bubbleModifier = remember { val bubbleModifier = remember {
Modifier Modifier
@@ -626,7 +628,7 @@ private fun RenderRegularTextNote(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit 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 eventContent by remember { mutableStateOf(accountViewModel.decrypt(note)) }
val modifier = remember { Modifier.padding(top = 5.dp) } val modifier = remember { Modifier.padding(top = 5.dp) }
@@ -70,7 +70,7 @@ import com.vitorpamplona.amethyst.ui.theme.bitcoinColor
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
import com.vitorpamplona.amethyst.ui.theme.overPictureBackground import com.vitorpamplona.amethyst.ui.theme.overPictureBackground
import com.vitorpamplona.amethyst.ui.theme.profile35dpModifier 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.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -484,7 +484,7 @@ fun CrossfadeToDisplayComment(
TranslatableRichTextViewer( TranslatableRichTextViewer(
content = it, content = it,
canPreview = true, canPreview = true,
tags = remember { ImmutableListOfLists() }, tags = EmptyTagList,
modifier = textBoxModifier, modifier = textBoxModifier,
backgroundColor = backgroundColor, backgroundColor = backgroundColor,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
@@ -179,12 +179,12 @@ import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
import com.vitorpamplona.quartz.events.EmojiPackEvent import com.vitorpamplona.quartz.events.EmojiPackEvent
import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent
import com.vitorpamplona.quartz.events.EmojiUrl import com.vitorpamplona.quartz.events.EmojiUrl
import com.vitorpamplona.quartz.events.EmptyTagList
import com.vitorpamplona.quartz.events.EventInterface import com.vitorpamplona.quartz.events.EventInterface
import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
import com.vitorpamplona.quartz.events.GenericRepostEvent import com.vitorpamplona.quartz.events.GenericRepostEvent
import com.vitorpamplona.quartz.events.HighlightEvent import com.vitorpamplona.quartz.events.HighlightEvent
import com.vitorpamplona.quartz.events.ImmutableListOfLists
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.events.LiveActivitiesEvent import com.vitorpamplona.quartz.events.LiveActivitiesEvent
import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE
@@ -631,7 +631,7 @@ fun LongCommunityHeader(
TranslatableRichTextViewer( TranslatableRichTextViewer(
content = summary ?: stringResource(id = R.string.community_no_descriptor), content = summary ?: stringResource(id = R.string.community_no_descriptor),
canPreview = false, canPreview = false,
tags = remember { ImmutableListOfLists(emptyList()) }, tags = EmptyTagList,
backgroundColor = background, backgroundColor = background,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav nav = nav
@@ -1343,7 +1343,7 @@ fun RenderTextEvent(
accountViewModel = accountViewModel accountViewModel = accountViewModel
) { ) {
val modifier = remember(note) { Modifier.fillMaxWidth() } 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( TranslatableRichTextViewer(
content = eventContent, content = eventContent,
@@ -1382,7 +1382,7 @@ fun RenderPoll(
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} else { } else {
val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList }
SensitivityWarning( SensitivityWarning(
note = note, note = note,
@@ -1557,7 +1557,7 @@ fun RenderAppDefinition(
Row( Row(
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp) 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 bgColor = MaterialTheme.colors.background
val backgroundColor = remember { val backgroundColor = remember {
mutableStateOf(bgColor) mutableStateOf(bgColor)
@@ -1630,7 +1630,7 @@ private fun RenderPrivateMessage(
val modifier = remember(note.event?.id()) { Modifier.fillMaxWidth() } val modifier = remember(note.event?.id()) { Modifier.fillMaxWidth() }
val isAuthorTheLoggedUser = remember(note.event?.id()) { accountViewModel.isLoggedUser(note.author) } 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 (eventContent != null) {
if (makeItShort && isAuthorTheLoggedUser) { if (makeItShort && isAuthorTheLoggedUser) {
@@ -1670,7 +1670,7 @@ private fun RenderPrivateMessage(
), ),
canPreview = !makeItShort, canPreview = !makeItShort,
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
ImmutableListOfLists(), EmptyTagList,
backgroundColor, backgroundColor,
accountViewModel, accountViewModel,
nav nav
@@ -2308,7 +2308,7 @@ fun RenderPinListEvent(
TranslatableRichTextViewer( TranslatableRichTextViewer(
content = pin, content = pin,
canPreview = true, canPreview = true,
tags = remember { ImmutableListOfLists() }, tags = EmptyTagList,
backgroundColor = backgroundColor, backgroundColor = backgroundColor,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav nav = nav
@@ -2407,8 +2407,8 @@ private fun RenderReport(
TranslatableRichTextViewer( TranslatableRichTextViewer(
content = content, content = content,
canPreview = true, canPreview = true,
modifier = remember { Modifier }, modifier = Modifier,
tags = remember { ImmutableListOfLists() }, tags = EmptyTagList,
backgroundColor = backgroundColor, backgroundColor = backgroundColor,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav nav = nav
@@ -2880,7 +2880,7 @@ fun DisplayHighlight(
quote, quote,
canPreview = canPreview && !makeItShort, canPreview = canPreview && !makeItShort,
remember { Modifier.fillMaxWidth() }, remember { Modifier.fillMaxWidth() },
remember { ImmutableListOfLists<String>(emptyList()) }, EmptyTagList,
backgroundColor, backgroundColor,
accountViewModel, accountViewModel,
nav nav
@@ -3527,7 +3527,7 @@ fun AudioHeader(noteEvent: AudioHeaderEvent, note: Note, accountViewModel: Accou
val defaultBackground = MaterialTheme.colors.background val defaultBackground = MaterialTheme.colors.background
val background = remember { mutableStateOf(defaultBackground) } 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 eventContent = remember(note.event) {
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null } val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
@@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.EmptyTagList
import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.ImmutableListOfLists
import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.toImmutableListOfLists import com.vitorpamplona.quartz.events.toImmutableListOfLists
@@ -116,7 +117,7 @@ private fun OptionNote(
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val tags = remember(baseNote) { val tags = remember(baseNote) {
baseNote.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() baseNote.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList
} }
Row( Row(
@@ -129,7 +129,7 @@ import com.vitorpamplona.amethyst.ui.theme.SmallBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.StdPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText 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.LiveActivitiesEvent.Companion.STATUS_LIVE
import com.vitorpamplona.quartz.events.Participant import com.vitorpamplona.quartz.events.Participant
import com.vitorpamplona.quartz.events.toImmutableListOfLists import com.vitorpamplona.quartz.events.toImmutableListOfLists
@@ -751,9 +751,9 @@ fun LongChannelHeader(
val tags = remember(channelState) { val tags = remember(channelState) {
if (baseChannel is LiveActivitiesChannel) { if (baseChannel is LiveActivitiesChannel) {
baseChannel.info?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() baseChannel.info?.tags()?.toImmutableListOfLists() ?: EmptyTagList
} else { } else {
ImmutableListOfLists() EmptyTagList
} }
} }
@@ -54,6 +54,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User 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.BadgeDefinitionEvent
import com.vitorpamplona.quartz.events.BadgeProfilesEvent import com.vitorpamplona.quartz.events.BadgeProfilesEvent
import com.vitorpamplona.quartz.events.ChatroomKey import com.vitorpamplona.quartz.events.ChatroomKey
import com.vitorpamplona.quartz.events.EmptyTagList
import com.vitorpamplona.quartz.events.GitHubIdentity import com.vitorpamplona.quartz.events.GitHubIdentity
import com.vitorpamplona.quartz.events.IdentityClaim import com.vitorpamplona.quartz.events.IdentityClaim
import com.vitorpamplona.quartz.events.ImmutableListOfLists
import com.vitorpamplona.quartz.events.MastodonIdentity import com.vitorpamplona.quartz.events.MastodonIdentity
import com.vitorpamplona.quartz.events.PayInvoiceErrorResponse import com.vitorpamplona.quartz.events.PayInvoiceErrorResponse
import com.vitorpamplona.quartz.events.PayInvoiceSuccessResponse import com.vitorpamplona.quartz.events.PayInvoiceSuccessResponse
@@ -118,7 +119,7 @@ import java.math.BigDecimal
fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, nav: (String) -> Unit) { fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
if (userId == null) return if (userId == null) return
var userBase by remember { mutableStateOf<User?>(LocalCache.getUserIfExists(userId)) } var userBase by remember { mutableStateOf(LocalCache.getUserIfExists(userId)) }
if (userBase == null) { if (userBase == null) {
LaunchedEffect(userId) { LaunchedEffect(userId) {
@@ -280,7 +281,6 @@ fun ProfileScreen(
} }
@Composable @Composable
@OptIn(ExperimentalFoundationApi::class)
private fun RenderSurface( private fun RenderSurface(
baseUser: User, baseUser: User,
threadsViewModel: NostrUserProfileNewThreadsFeedViewModel, threadsViewModel: NostrUserProfileNewThreadsFeedViewModel,
@@ -383,7 +383,7 @@ private fun RenderScreen(
) { ) {
val pagerState = rememberPagerState { 9 } val pagerState = rememberPagerState { 9 }
Column() { Column {
ProfileHeader(baseUser, appRecommendations, nav, accountViewModel) ProfileHeader(baseUser, appRecommendations, nav, accountViewModel)
ScrollableTabRow( ScrollableTabRow(
backgroundColor = MaterialTheme.colors.background, backgroundColor = MaterialTheme.colors.background,
@@ -449,7 +449,7 @@ private fun CreateAndRenderTabs(
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val tabs = listOf<@Composable() (() -> Unit)?>( val tabs = listOf<@Composable (() -> Unit)?>(
{ Text(text = stringResource(R.string.notes)) }, { Text(text = stringResource(R.string.notes)) },
{ Text(text = stringResource(R.string.replies)) }, { Text(text = stringResource(R.string.replies)) },
{ FollowTabHeader(baseUser) }, { FollowTabHeader(baseUser) },
@@ -486,7 +486,7 @@ private fun RelaysTabHeader(baseUser: User) {
@Composable @Composable
private fun ReportsTabHeader(baseUser: User) { private fun ReportsTabHeader(baseUser: User) {
val userState by baseUser.live().reports.observeAsState() val userState by baseUser.live().reports.observeAsState()
var userReports by remember { mutableStateOf(0) } var userReports by remember { mutableIntStateOf(0) }
LaunchedEffect(key1 = userState) { LaunchedEffect(key1 = userState) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
@@ -503,11 +503,11 @@ private fun ReportsTabHeader(baseUser: User) {
@Composable @Composable
private fun FollowedTagsTabHeader(baseUser: User) { private fun FollowedTagsTabHeader(baseUser: User) {
var usertags by remember { mutableStateOf(0) } var usertags by remember { mutableIntStateOf(0) }
LaunchedEffect(key1 = baseUser) { LaunchedEffect(key1 = baseUser) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
val contactList = baseUser?.latestContactList val contactList = baseUser.latestContactList
val newTags = (contactList?.verifiedFollowTagSet?.count() ?: 0) val newTags = (contactList?.verifiedFollowTagSet?.count() ?: 0)
@@ -524,7 +524,7 @@ private fun FollowedTagsTabHeader(baseUser: User) {
private fun BookmarkTabHeader(baseUser: User) { private fun BookmarkTabHeader(baseUser: User) {
val userState by baseUser.live().bookmarks.observeAsState() val userState by baseUser.live().bookmarks.observeAsState()
var userBookmarks by remember { mutableStateOf(0) } var userBookmarks by remember { mutableIntStateOf(0) }
LaunchedEffect(key1 = userState) { LaunchedEffect(key1 = userState) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
@@ -716,8 +716,6 @@ private fun ProfileActions(
EditButton(accountViewModel.account) EditButton(accountViewModel.account)
} }
val scope = rememberCoroutineScope()
WatchIsHiddenUser(baseUser, accountViewModel) { isHidden -> WatchIsHiddenUser(baseUser, accountViewModel) { isHidden ->
if (isHidden) { if (isHidden) {
ShowUserButton { ShowUserButton {
@@ -1002,7 +1000,7 @@ private fun DrawAdditionalInfo(
TranslatableRichTextViewer( TranslatableRichTextViewer(
content = it, content = it,
canPreview = false, canPreview = false,
tags = remember { ImmutableListOfLists<String>(emptyList()) }, tags = EmptyTagList,
backgroundColor = background, backgroundColor = background,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav nav = nav
@@ -1114,7 +1112,7 @@ private fun DisplayAppRecommendations(
) { state -> ) { state ->
when (state) { when (state) {
is FeedState.Loaded -> { is FeedState.Loaded -> {
Column() { Column {
Text(stringResource(id = R.string.recommended_apps)) Text(stringResource(id = R.string.recommended_apps))
FlowRow( FlowRow(
@@ -1137,7 +1135,7 @@ private fun DisplayAppRecommendations(
private fun WatchApp(baseApp: Note, nav: (String) -> Unit) { private fun WatchApp(baseApp: Note, nav: (String) -> Unit) {
val appState by baseApp.live().metadata.observeAsState() val appState by baseApp.live().metadata.observeAsState()
var appLogo by remember { mutableStateOf<String?>(null) } var appLogo by remember(baseApp) { mutableStateOf<String?>(null) }
LaunchedEffect(key1 = appState) { LaunchedEffect(key1 = appState) {
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
@@ -1185,19 +1183,27 @@ private fun DisplayBadges(
null null
), ),
accountViewModel accountViewModel
) { ) { note ->
if (it != null) { if (note != null) {
val badgeList by it.live().metadata.map { WatchAndRenderBadgeList(note, nav)
(it.note.event as? BadgeProfilesEvent)?.badgeAwardEvents()?.toImmutableList()
}.distinctUntilChanged().observeAsState()
badgeList?.let { list ->
RenderBadgeList(list, 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 @Composable
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
private fun RenderBadgeList( private fun RenderBadgeList(
@@ -1217,7 +1223,7 @@ private fun RenderBadgeList(
@Composable @Composable
private fun LoadAndRenderBadge(badgeAwardEventHex: String, nav: (String) -> Unit) { private fun LoadAndRenderBadge(badgeAwardEventHex: String, nav: (String) -> Unit) {
var baseNote by remember { var baseNote by remember {
mutableStateOf<Note?>(LocalCache.getNoteIfExists(badgeAwardEventHex)) mutableStateOf(LocalCache.getNoteIfExists(badgeAwardEventHex))
} }
LaunchedEffect(key1 = badgeAwardEventHex) { LaunchedEffect(key1 = badgeAwardEventHex) {
@@ -1462,7 +1468,7 @@ fun TabFollows(baseUser: User, feedViewModel: UserFeedViewModel, accountViewMode
WatchFollowChanges(baseUser, feedViewModel) WatchFollowChanges(baseUser, feedViewModel)
Column(Modifier.fillMaxHeight()) { Column(Modifier.fillMaxHeight()) {
Column() { Column {
RefreshingFeedUserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false) RefreshingFeedUserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false)
} }
} }
@@ -1473,7 +1479,7 @@ fun TabFollowers(baseUser: User, feedViewModel: UserFeedViewModel, accountViewMo
WatchFollowerChanges(baseUser, feedViewModel) WatchFollowerChanges(baseUser, feedViewModel)
Column(Modifier.fillMaxHeight()) { Column(Modifier.fillMaxHeight()) {
Column() { Column {
RefreshingFeedUserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false) RefreshingFeedUserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false)
} }
} }
@@ -1508,7 +1514,7 @@ fun TabReceivedZaps(baseUser: User, zapFeedViewModel: NostrUserProfileZapsFeedVi
WatchZapsAndUpdateFeed(baseUser, zapFeedViewModel) WatchZapsAndUpdateFeed(baseUser, zapFeedViewModel)
Column(Modifier.fillMaxHeight()) { Column(Modifier.fillMaxHeight()) {
Column() { Column {
LnZapFeedView(zapFeedViewModel, accountViewModel, nav) LnZapFeedView(zapFeedViewModel, accountViewModel, nav)
} }
} }
@@ -1531,7 +1537,7 @@ fun TabReports(baseUser: User, feedViewModel: NostrUserProfileReportFeedViewMode
WatchReportsAndUpdateFeed(baseUser, feedViewModel) WatchReportsAndUpdateFeed(baseUser, feedViewModel)
Column(Modifier.fillMaxHeight()) { Column(Modifier.fillMaxHeight()) {
Column() { Column {
RefresheableFeedView( RefresheableFeedView(
feedViewModel, feedViewModel,
null, null,
@@ -1703,7 +1709,6 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () ->
onDismissRequest = onDismiss onDismissRequest = onDismiss
) { ) {
val clipboardManager = LocalClipboardManager.current val clipboardManager = LocalClipboardManager.current
val scope = rememberCoroutineScope()
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); onDismiss() }) { DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); onDismiss() }) {
Text(stringResource(R.string.copy_user_id)) Text(stringResource(R.string.copy_user_id))
@@ -15,16 +15,28 @@ class AppDefinitionEvent(
content: String, content: String,
sig: HexKey sig: HexKey
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) { ) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
fun appMetaData() = try { @Transient
mapper.readValue( private var cachedMetadata: UserMetadata? = null
ByteArrayInputStream(content.toByteArray(Charsets.UTF_8)),
UserMetadata::class.java fun appMetaData() =
) if (cachedMetadata != null) {
} catch (e: Exception) { cachedMetadata
e.printStackTrace() } else {
Log.w("MT", "Content Parse Error ${e.localizedMessage} $content") try {
null 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 { fun supportedKinds() = tags.filter { it.size > 1 && it[0] == "k" }.mapNotNull {
runCatching { it[1].toInt() }.getOrNull() runCatching { it[1].toInt() }.getOrNull()
@@ -350,6 +350,8 @@ class UserMetadata {
@Stable @Stable
data class ImmutableListOfLists<T>(val lists: List<List<T>> = emptyList()) data class ImmutableListOfLists<T>(val lists: List<List<T>> = emptyList())
val EmptyTagList = ImmutableListOfLists<String>(emptyList())
fun List<List<String>>.toImmutableListOfLists(): ImmutableListOfLists<String> { fun List<List<String>>.toImmutableListOfLists(): ImmutableListOfLists<String> {
return ImmutableListOfLists(this) return ImmutableListOfLists(this)
} }