From 13de5973ca2385c8b584e46301ec6bbb06af0b45 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Sun, 7 May 2023 16:12:28 -0400 Subject: [PATCH] Improving performance of the RichText Viewer --- .../ui/components/ExpandableRichTextViewer.kt | 10 +- .../amethyst/ui/components/RichTextViewer.kt | 458 +++++++++++------- .../ui/components/ZoomableContentView.kt | 9 +- .../amethyst/ui/note/MessageSetCompose.kt | 7 +- .../amethyst/ui/note/MultiSetCompose.kt | 6 +- .../amethyst/ui/note/NoteCompose.kt | 31 +- .../components/TranslatableRichTextViewer.kt | 2 - 7 files changed, 330 insertions(+), 193 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt index 3b62e83bd..27abc13e7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt @@ -42,11 +42,13 @@ fun ExpandableRichTextViewer( ) { var showFullText by remember { mutableStateOf(false) } - // Cuts the text in the first space after 350 - val firstSpaceAfterCut = content.indexOf(' ', SHORT_TEXT_LENGTH).let { if (it < 0) content.length else it } - val firstNewLineAfterCut = content.indexOf('\n', SHORT_TEXT_LENGTH).let { if (it < 0) content.length else it } + val whereToCut = remember { + // Cuts the text in the first space after 350 + val firstSpaceAfterCut = content.indexOf(' ', SHORT_TEXT_LENGTH).let { if (it < 0) content.length else it } + val firstNewLineAfterCut = content.indexOf('\n', SHORT_TEXT_LENGTH).let { if (it < 0) content.length else it } - val whereToCut = minOf(firstSpaceAfterCut, firstNewLineAfterCut) + minOf(firstSpaceAfterCut, firstNewLineAfterCut) + } val text = if (showFullText) { content diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index f0e8a89b8..bc02931e5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -75,6 +75,14 @@ fun isValidURL(url: String?): Boolean { val richTextDefaults = RichTextStyle().resolveDefaults() +fun isMarkdown(content: String): Boolean { + return content.startsWith("> ") || + content.startsWith("# ") || + content.contains("##") || + content.contains("__") || + content.contains("```") +} + @Composable fun RichTextViewer( content: String, @@ -85,58 +93,39 @@ fun RichTextViewer( accountViewModel: AccountViewModel, navController: NavController ) { + val isMarkdown = remember { isMarkdown(content) } + Column(modifier = modifier) { - if (content.startsWith("> ") || - content.startsWith("# ") || - content.contains("##") || - content.contains("__") || - content.contains("```") - ) { - val myMarkDownStyle = richTextDefaults.copy( - codeBlockStyle = richTextDefaults.codeBlockStyle?.copy( - textStyle = TextStyle( - fontFamily = FontFamily.Monospace, - fontSize = 14.sp - ), - modifier = Modifier - .padding(0.dp) - .fillMaxWidth() - .clip(shape = RoundedCornerShape(15.dp)) - .border( - 1.dp, - MaterialTheme.colors.onSurface.copy(alpha = 0.12f), - RoundedCornerShape(15.dp) - ) - .background( - MaterialTheme.colors.onSurface - .copy(alpha = 0.05f) - .compositeOver(backgroundColor) - ) - ), - stringStyle = richTextDefaults.stringStyle?.copy( - linkStyle = SpanStyle( - textDecoration = TextDecoration.Underline, - color = MaterialTheme.colors.primary - ), - codeStyle = SpanStyle( - fontFamily = FontFamily.Monospace, - fontSize = 14.sp, - background = MaterialTheme.colors.onSurface.copy(alpha = 0.22f).compositeOver(backgroundColor) - ) - ) - ) - - val markdownWithSpecialContent = returnMarkdownWithSpecialContent(content) - - MaterialRichText( - style = myMarkDownStyle - ) { - Markdown( - content = markdownWithSpecialContent, - markdownParseOptions = MarkdownParseOptions.Default - ) - } + if (isMarkdown) { + RenderContentAsMarkdown(content, backgroundColor) } else { + RenderRegular(content, tags, canPreview, backgroundColor, accountViewModel, navController) + } + } +} + +class RichTextViewerState( + val content: String, + val urlSet: LinkedHashSet, + val imagesForPager: Map, + val imageList: List +) + +@Composable +private fun RenderRegular( + content: String, + tags: List>?, + canPreview: Boolean, + backgroundColor: Color, + accountViewModel: AccountViewModel, + navController: NavController +) { + var processedState by remember { + mutableStateOf(null) + } + + LaunchedEffect(key1 = content) { + withContext(Dispatchers.IO) { val urls = UrlDetector(content, UrlDetectorOptions.Default).detect() val urlSet = urls.mapTo(LinkedHashSet(urls.size)) { it.originalUrl } val imagesForPager = urlSet.mapNotNull { fullUrl -> @@ -151,67 +140,71 @@ fun RichTextViewer( }.associateBy { it.url } val imageList = imagesForPager.values.toList() - // FlowRow doesn't work well with paragraphs. So we need to split them - content.split('\n').forEach { paragraph -> - FlowRow() { - val s = if (isArabic(paragraph)) paragraph.trim().split(' ').reversed() else paragraph.trim().split(' ') - s.forEach { word: String -> - if (canPreview) { - // Explicit URL - val img = imagesForPager[word] - if (img != null) { - ZoomableContentView(img, imageList) - } else if (urlSet.contains(word)) { - UrlPreview(word, "$word ") - } else if (word.startsWith("lnbc", true)) { - MayBeInvoicePreview(word) - } else if (word.startsWith("lnurl", true)) { - MayBeWithdrawal(word) - } else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) { - ClickableEmail(word) - } else if (word.length > 6 && Patterns.PHONE.matcher(word).matches()) { - ClickablePhone(word) - } else if (isBechLink(word)) { - BechLink( + processedState = RichTextViewerState(content, urlSet, imagesForPager, imageList) + } + } + + // FlowRow doesn't work well with paragraphs. So we need to split them + processedState?.let { state -> + content.split('\n').forEach { paragraph -> + FlowRow() { + val s = if (isArabic(paragraph)) { + paragraph.trim().split(' ') + .reversed() + } else { + paragraph.trim().split(' ') + } + s.forEach { word: String -> + if (canPreview) { + // Explicit URL + val img = state.imagesForPager[word] + if (img != null) { + ZoomableContentView(img, state.imageList) + } else if (state.urlSet.contains(word)) { + UrlPreview(word, "$word ") + } else if (word.startsWith("lnbc", true)) { + MayBeInvoicePreview(word) + } else if (word.startsWith("lnurl", true)) { + MayBeWithdrawal(word) + } else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) { + ClickableEmail(word) + } else if (word.length > 6 && Patterns.PHONE.matcher(word).matches()) { + ClickablePhone(word) + } else if (isBechLink(word)) { + BechLink( + word, + canPreview, + backgroundColor, + accountViewModel, + navController + ) + } else if (word.startsWith("#")) { + if (tagIndex.matcher(word).matches() && tags != null) { + TagLink( word, + tags, canPreview, backgroundColor, accountViewModel, navController ) - } else if (word.startsWith("#")) { - if (tagIndex.matcher(word).matches() && tags != null) { - TagLink( - word, - tags, - canPreview, - backgroundColor, - accountViewModel, - navController - ) - } else if (hashTagsPattern.matcher(word).matches()) { - HashTag(word, navController) - } else { - Text( - text = "$word ", - style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) - ) - } - } else if (noProtocolUrlValidator.matcher(word).matches()) { - val matcher = noProtocolUrlValidator.matcher(word) - matcher.find() - val url = matcher.group(1) // url - val additionalChars = matcher.group(4) ?: "" // additional chars + } else if (hashTagsPattern.matcher(word).matches()) { + HashTag(word, navController) + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } + } else if (noProtocolUrlValidator.matcher(word).matches()) { + val matcher = noProtocolUrlValidator.matcher(word) + matcher.find() + val url = matcher.group(1) // url + val additionalChars = matcher.group(4) ?: "" // additional chars - if (url != null) { - ClickableUrl(url, "https://$url") - Text("$additionalChars ") - } else { - Text( - text = "$word ", - style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) - ) - } + if (url != null) { + ClickableUrl(url, "https://$url") + Text("$additionalChars ") } else { Text( text = "$word ", @@ -219,69 +212,74 @@ fun RichTextViewer( ) } } else { - if (urlSet.contains(word)) { - ClickableUrl("$word ", word) - } else if (word.startsWith("lnurl", true)) { - val lnWithdrawal = LnWithdrawalUtil.findWithdrawal(word) - if (lnWithdrawal != null) { - ClickableWithdrawal(withdrawalString = lnWithdrawal) - } else { - Text( - text = "$word ", - style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) - ) - } - } else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) { - ClickableEmail(word) - } else if (Patterns.PHONE.matcher(word).matches() && word.length > 6) { - ClickablePhone(word) - } else if (isBechLink(word)) { - BechLink( - word, - canPreview, - backgroundColor, - accountViewModel, - navController - ) - } else if (word.startsWith("#")) { - if (tagIndex.matcher(word).matches() && tags != null) { - TagLink( - word, - tags, - canPreview, - backgroundColor, - accountViewModel, - navController - ) - } else if (hashTagsPattern.matcher(word).matches()) { - HashTag(word, navController) - } else { - Text( - text = "$word ", - style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) - ) - } - } else if (noProtocolUrlValidator.matcher(word).matches()) { - val matcher = noProtocolUrlValidator.matcher(word) - matcher.find() - val url = matcher.group(1) // url - val additionalChars = matcher.group(4) ?: "" // additional chars - - if (url != null) { - ClickableUrl(url, "https://$url") - Text("$additionalChars ") - } else { - Text( - text = "$word ", - style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) - ) - } + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } + } else { + if (state.urlSet.contains(word)) { + ClickableUrl("$word ", word) + } else if (word.startsWith("lnurl", true)) { + val lnWithdrawal = LnWithdrawalUtil.findWithdrawal(word) + if (lnWithdrawal != null) { + ClickableWithdrawal(withdrawalString = lnWithdrawal) } else { Text( text = "$word ", style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) ) } + } else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) { + ClickableEmail(word) + } else if (Patterns.PHONE.matcher(word).matches() && word.length > 6) { + ClickablePhone(word) + } else if (isBechLink(word)) { + BechLink( + word, + canPreview, + backgroundColor, + accountViewModel, + navController + ) + } else if (word.startsWith("#")) { + if (tagIndex.matcher(word).matches() && tags != null) { + TagLink( + word, + tags, + canPreview, + backgroundColor, + accountViewModel, + navController + ) + } else if (hashTagsPattern.matcher(word).matches()) { + HashTag(word, navController) + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } + } else if (noProtocolUrlValidator.matcher(word).matches()) { + val matcher = noProtocolUrlValidator.matcher(word) + matcher.find() + val url = matcher.group(1) // url + val additionalChars = matcher.group(4) ?: "" // additional chars + + if (url != null) { + ClickableUrl(url, "https://$url") + Text("$additionalChars ") + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) } } } @@ -291,21 +289,137 @@ fun RichTextViewer( } @Composable -private fun getDisplayNameFromUserNip19(parsedNip19: Nip19.Return): String? { - if (parsedNip19.type == Nip19.Type.USER) { - val userHex = parsedNip19.hex - val userBase = LocalCache.getOrCreateUser(userHex) +private fun RenderContentAsMarkdown(content: String, backgroundColor: Color) { + val myMarkDownStyle = richTextDefaults.copy( + codeBlockStyle = richTextDefaults.codeBlockStyle?.copy( + textStyle = TextStyle( + fontFamily = FontFamily.Monospace, + fontSize = 14.sp + ), + modifier = Modifier + .padding(0.dp) + .fillMaxWidth() + .clip(shape = RoundedCornerShape(15.dp)) + .border( + 1.dp, + MaterialTheme.colors.onSurface.copy(alpha = 0.12f), + RoundedCornerShape(15.dp) + ) + .background( + MaterialTheme.colors.onSurface + .copy(alpha = 0.05f) + .compositeOver(backgroundColor) + ) + ), + stringStyle = richTextDefaults.stringStyle?.copy( + linkStyle = SpanStyle( + textDecoration = TextDecoration.Underline, + color = MaterialTheme.colors.primary + ), + codeStyle = SpanStyle( + fontFamily = FontFamily.Monospace, + fontSize = 14.sp, + background = MaterialTheme.colors.onSurface.copy(alpha = 0.22f) + .compositeOver(backgroundColor) + ) + ) + ) - val userState by userBase.live().metadata.observeAsState() - val displayName = userState?.user?.bestDisplayName() - if (displayName !== null) { - return displayName + var markdownWithSpecialContent by remember { mutableStateOf(null) } + var nip19References by remember { mutableStateOf>(emptyList()) } + var refresh by remember { mutableStateOf(0) } + + LaunchedEffect(key1 = content) { + withContext(Dispatchers.IO) { + nip19References = returnNIP19References(content) + markdownWithSpecialContent = returnMarkdownWithSpecialContent(content) + } + } + + LaunchedEffect(key1 = refresh) { + withContext(Dispatchers.IO) { + val newMarkdownWithSpecialContent = returnMarkdownWithSpecialContent(content) + if (markdownWithSpecialContent != newMarkdownWithSpecialContent) { + markdownWithSpecialContent = newMarkdownWithSpecialContent + } + } + } + + nip19References.forEach { + var baseUser by remember { mutableStateOf(null) } + var baseNote by remember { mutableStateOf(null) } + + LaunchedEffect(key1 = it.hex) { + withContext(Dispatchers.IO) { + if (it.type == Nip19.Type.NOTE || it.type == Nip19.Type.EVENT || it.type == Nip19.Type.ADDRESS) { + LocalCache.checkGetOrCreateNote(it.hex)?.let { note -> + baseNote = note + } + } + + if (it.type == Nip19.Type.USER) { + LocalCache.checkGetOrCreateUser(it.hex)?.let { user -> + baseUser = user + } + } + } + } + + baseNote?.let { + val noteState by it.live().metadata.observeAsState() + if (noteState?.note?.event != null) { + refresh++ + } + } + baseUser?.let { + val userState by it.live().metadata.observeAsState() + if (userState?.user?.info != null) { + refresh++ + } + } + } + + markdownWithSpecialContent?.let { + MaterialRichText( + style = myMarkDownStyle + ) { + Markdown( + content = it, + markdownParseOptions = MarkdownParseOptions.Default + ) } } - return null } -@Composable +private fun getDisplayNameFromNip19(nip19: Nip19.Return): String? { + if (nip19.type == Nip19.Type.USER) { + return LocalCache.users[nip19.hex]?.bestDisplayName() + } else if (nip19.type == Nip19.Type.NOTE) { + return LocalCache.notes[nip19.hex]?.idDisplayNote() + } else if (nip19.type == Nip19.Type.ADDRESS) { + return LocalCache.addressables[nip19.hex]?.idDisplayNote() + } else if (nip19.type == Nip19.Type.EVENT) { + return LocalCache.notes[nip19.hex]?.idDisplayNote() ?: LocalCache.addressables[nip19.hex]?.idDisplayNote() + } else { + return null + } +} + +private fun returnNIP19References(content: String): List { + val listOfReferences = mutableListOf() + content.split('\n').forEach { paragraph -> + paragraph.split(' ').forEach { word: String -> + if (isBechLink(word)) { + val parsedNip19 = Nip19.uriToRoute(word) + parsedNip19?.let { + listOfReferences.add(it) + } + } + } + } + return listOfReferences +} + private fun returnMarkdownWithSpecialContent(content: String): String { var returnContent = "" content.split('\n').forEach { paragraph -> @@ -319,7 +433,7 @@ private fun returnMarkdownWithSpecialContent(content: String): String { } else if (isBechLink(word)) { val parsedNip19 = Nip19.uriToRoute(word) returnContent += if (parsedNip19 !== null) { - val displayName = getDisplayNameFromUserNip19(parsedNip19) + val displayName = getDisplayNameFromNip19(parsedNip19) if (displayName != null) { "[@$displayName](nostr://$word) " } else { 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 dbd57c466..fec774000 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 @@ -209,7 +209,9 @@ private fun LocalImageView( mutableStateOf(null) } - val ratio = aspectRatio(content.dim) + val ratio = remember { + aspectRatio(content.dim) + } BoxWithConstraints(contentAlignment = Alignment.Center) { val myModifier = mainImageModifier.also { @@ -257,6 +259,7 @@ private fun UrlImageView( content: ZoomableUrlImage, mainImageModifier: Modifier ) { + println("UrlImageView") val scope = rememberCoroutineScope() val context = LocalContext.current @@ -269,7 +272,9 @@ private fun UrlImageView( mutableStateOf(null) } - val ratio = aspectRatio(content.dim) + val ratio = remember { + aspectRatio(content.dim) + } LaunchedEffect(key1 = content.url, key2 = imageState) { if (imageState is AsyncImagePainter.State.Success) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt index 1557a09b0..094f6f033 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt @@ -38,7 +38,6 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, isInnerNote: Boolean = fal val noteState by messageSetCard.note.live().metadata.observeAsState() val note = noteState?.note - val noteEvent = note?.event var popupExpanded by remember { mutableStateOf(false) } val scope = rememberCoroutineScope() @@ -50,10 +49,14 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, isInnerNote: Boolean = fal LaunchedEffect(key1 = messageSetCard.createdAt()) { scope.launch(Dispatchers.IO) { - isNew = + val newIsNew = messageSetCard.createdAt() > NotificationCache.load(routeForLastRead) NotificationCache.markAsRead(routeForLastRead, messageSetCard.createdAt()) + + if (newIsNew != isNew) { + isNew = newIsNew + } } } 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 5b4464834..7b454639a 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 @@ -68,9 +68,13 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun LaunchedEffect(key1 = multiSetCard.createdAt()) { scope.launch(Dispatchers.IO) { - isNew = multiSetCard.createdAt > NotificationCache.load(routeForLastRead) + val newIsNew = multiSetCard.createdAt > NotificationCache.load(routeForLastRead) NotificationCache.markAsRead(routeForLastRead, multiSetCard.createdAt) + + if (newIsNew != isNew) { + isNew = newIsNew + } } } 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 f5a9d90d4..d552c92a3 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 @@ -249,7 +249,11 @@ fun NoteComposeInner( val createdAt = note.createdAt() if (createdAt != null) { NotificationCache.markAsRead(it, createdAt) - isNew = createdAt > lastTime + + val newIsNew = createdAt > lastTime + if (newIsNew != isNew) { + isNew = newIsNew + } } } } @@ -420,7 +424,8 @@ private fun RenderTextEvent( ) { val noteEvent = note.event ?: return - val eventContent = accountViewModel.decrypt(note) + val eventContent = remember { accountViewModel.decrypt(note) } + val modifier = remember { Modifier.fillMaxWidth() } if (eventContent != null) { if (makeItShort && accountViewModel.isLoggedUser(note.author)) { @@ -434,7 +439,7 @@ private fun RenderTextEvent( TranslatableRichTextViewer( content = eventContent, canPreview = canPreview && !makeItShort, - modifier = Modifier.fillMaxWidth(), + modifier = modifier, tags = noteEvent.tags(), backgroundColor = backgroundColor, accountViewModel = accountViewModel, @@ -548,10 +553,10 @@ private fun RenderPrivateMessage( navController: NavController ) { val noteEvent = note.event as? PrivateDmEvent ?: return - val withMe = noteEvent.with(accountViewModel.userProfile().pubkeyHex) + val withMe = remember { noteEvent.with(accountViewModel.userProfile().pubkeyHex) } if (withMe) { - val eventContent = accountViewModel.decrypt(note) + val eventContent = remember { accountViewModel.decrypt(note) } if (eventContent != null) { if (makeItShort && accountViewModel.isLoggedUser(note.author)) { @@ -908,7 +913,7 @@ fun TimeAgo(time: Long) { @Composable private fun DrawAuthorImages(baseNote: Note, loggedIn: User, navController: NavController) { - val baseChannel = baseNote.channel() + val baseChannel = remember { baseNote.channel() } Column(Modifier.width(55.dp)) { // Draws the boosted picture outside the boosted card. @@ -1002,7 +1007,13 @@ fun DisplayHighlight( accountViewModel: AccountViewModel, navController: NavController ) { - val quote = highlight.split("\n").map { "> *${it.removeSuffix(" ")}*" }.joinToString("\n") + val quote = + remember { + highlight + .split("\n") + .map { "> *${it.removeSuffix(" ")}*" } + .joinToString("\n") + } TranslatableRichTextViewer( quote, @@ -1205,9 +1216,9 @@ fun BadgeDisplay(baseNote: Note) { val background = MaterialTheme.colors.background val badgeData = baseNote.event as? BadgeDefinitionEvent ?: return - val image = badgeData.image() - val name = badgeData.name() - val description = badgeData.description() + val image = remember { 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) } diff --git a/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt b/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt index 7c4c04457..4c6e17a6c 100644 --- a/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt +++ b/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt @@ -73,8 +73,6 @@ fun TranslatableRichTextViewer( showOriginal = preference == task.result.sourceLang } translatedTextState.value = task.result - } else { - translatedTextState.value = ResultOrError(content, null, null, null) } } }