From 00981ef15c0bf48abdc83c47b187f62393bd7527 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Wed, 8 Feb 2023 14:02:42 -0500 Subject: [PATCH] Refactoring RichTextViewer file --- .../ui/components/ExpandableRichTextViewer.kt | 70 +++++++ .../amethyst/ui/components/RichTextViewer.kt | 180 ------------------ .../components/TranslateableRichTextViewer.kt | 151 +++++++++++++++ 3 files changed, 221 insertions(+), 180 deletions(-) create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/ui/components/TranslateableRichTextViewer.kt 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 new file mode 100644 index 000000000..96628b991 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt @@ -0,0 +1,70 @@ +package com.vitorpamplona.amethyst.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController + +@Composable +fun ExpandableRichTextViewer( + content: String, + canPreview: Boolean, + tags: List>?, + navController: NavController +) { + var showFullText by remember { mutableStateOf(false) } + + val text = if (showFullText) content else content.take(350) + + Box(contentAlignment = Alignment.BottomCenter) { + RichTextViewer(text, canPreview, tags, navController) + + if (content.length > 350 && !showFullText) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .background( + brush = Brush.verticalGradient( + colors = listOf( + MaterialTheme.colors.background.copy(alpha = 0f), + MaterialTheme.colors.background + ) + ) + ) + ) { + Button( + modifier = Modifier.padding(top = 10.dp), + onClick = { showFullText = !showFullText }, + shape = RoundedCornerShape(20.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.primary + ), + contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp) + ) { + Text(text = "Show More", color = Color.White) + } + } + } + } +} \ No newline at end of file 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 d01adf36a..63a687fc4 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 @@ -1,45 +1,24 @@ package com.vitorpamplona.amethyst.ui.components -import android.content.res.Resources import android.util.Patterns import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.ClickableText import androidx.compose.material.* import androidx.compose.runtime.* -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextDirection -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp -import androidx.core.os.ConfigurationCompat import androidx.navigation.NavController import com.google.accompanist.flowlayout.FlowRow -import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.lnurl.LnInvoiceUtil -import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.toByteArray import com.vitorpamplona.amethyst.model.toNote import com.vitorpamplona.amethyst.service.Nip19 -import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService -import com.vitorpamplona.amethyst.service.lang.ResultOrError import com.vitorpamplona.amethyst.ui.note.toShortenHex -import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import nostr.postr.toNpub import java.net.MalformedURLException import java.net.URISyntaxException import java.net.URL -import java.util.* import java.util.regex.Pattern val imageExtension = Pattern.compile("(.*/)*.+\\.(png|jpg|gif|bmp|jpeg|webp|svg)$") @@ -62,165 +41,6 @@ fun isValidURL(url: String?): Boolean { } } -@Composable -fun TranslateableRichTextViewer( - content: String, - canPreview: Boolean, - tags: List>?, - accountViewModel: AccountViewModel, - navController: NavController -) { - val translatedTextState = remember { - mutableStateOf(ResultOrError(content, null, null, null)) - } - - var showOriginal by remember { mutableStateOf(false) } - var langSettingsPopupExpanded by remember { mutableStateOf(false) } - - val context = LocalContext.current - - val accountState by accountViewModel.accountLanguagesLiveData.observeAsState() - val account = accountState?.account ?: return - - LaunchedEffect(accountState) { - LanguageTranslatorService.autoTranslate(content, account.dontTranslateFrom, account.translateTo).addOnCompleteListener { task -> - if (task.isSuccessful) { - translatedTextState.value = task.result - } else { - translatedTextState.value = ResultOrError(content, null, null, null) - } - } - } - - val toBeViewed = if (showOriginal) content else translatedTextState.value.result ?: content - - Column(modifier = Modifier.padding(top = 5.dp)) { - ExpandableRichTextViewer( - toBeViewed, - canPreview, - tags, - navController - ) - - val target = translatedTextState.value.targetLang - val source = translatedTextState.value.sourceLang - - if (source != null && target != null) { - if (source != target) { - Row(modifier = Modifier.fillMaxWidth().padding(top = 5.dp)) { - val clickableTextStyle = SpanStyle(color = MaterialTheme.colors.primary.copy(alpha = 0.52f)) - - val annotatedTranslationString= buildAnnotatedString { - withStyle(clickableTextStyle) { - pushStringAnnotation("langSettings", true.toString()) - append("Auto") - } - - append("-translated from ") - - withStyle(clickableTextStyle) { - pushStringAnnotation("showOriginal", true.toString()) - append(Locale(source).displayName) - } - - append(" to ") - - withStyle(clickableTextStyle) { - pushStringAnnotation("showOriginal", false.toString()) - append(Locale(target).displayName) - } - } - - ClickableText( - text = annotatedTranslationString, - style = LocalTextStyle.current.copy(color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)), - overflow = TextOverflow.Visible, - maxLines = 3 - ) { spanOffset -> annotatedTranslationString.getStringAnnotations(spanOffset, spanOffset) - .firstOrNull() - ?.also { span -> - if (span.tag == "showOriginal") - showOriginal = span.item.toBoolean() - else - langSettingsPopupExpanded = !langSettingsPopupExpanded - } - } - - DropdownMenu( - expanded = langSettingsPopupExpanded, - onDismissRequest = { langSettingsPopupExpanded = false } - ) { - DropdownMenuItem(onClick = { - accountViewModel.dontTranslateFrom(source, context) - langSettingsPopupExpanded = false - }) { - Text("Never translate from ${Locale(source).displayName}") - } - Divider() - val languageList = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()) - for (i in 0 until languageList.size()) { - languageList.get(i)?.let { lang -> - DropdownMenuItem(onClick = { - accountViewModel.translateTo(lang, context) - langSettingsPopupExpanded = false - }) { - Text("Always translate to ${lang.displayName}") - } - } - } - } - } - } - } - } -} - -@Composable -fun ExpandableRichTextViewer( - content: String, - canPreview: Boolean, - tags: List>?, - navController: NavController -) { - var showFullText by remember { mutableStateOf(false) } - - val text = if (showFullText) content else content.take(350) - - Box(contentAlignment = Alignment.BottomCenter) { - RichTextViewer(text, canPreview, tags, navController) - - if (content.length > 350 && !showFullText) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .background( - brush = Brush.verticalGradient( - colors = listOf( - MaterialTheme.colors.background.copy(alpha = 0f), - MaterialTheme.colors.background - ) - ) - ) - ) { - Button( - modifier = Modifier.padding(top = 10.dp), - onClick = { showFullText = !showFullText }, - shape = RoundedCornerShape(20.dp), - colors = ButtonDefaults - .buttonColors( - backgroundColor = MaterialTheme.colors.primary - ), - contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp) - ) { - Text(text = "Show More", color = Color.White) - } - } - } - } -} - @Composable fun RichTextViewer( content: String, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/TranslateableRichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/TranslateableRichTextViewer.kt new file mode 100644 index 000000000..97f5f3b61 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/TranslateableRichTextViewer.kt @@ -0,0 +1,151 @@ +package com.vitorpamplona.amethyst.ui.components + +import android.content.res.Resources +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.ClickableText +import androidx.compose.material.Divider +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.LocalTextStyle +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.core.os.ConfigurationCompat +import androidx.navigation.NavController +import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService +import com.vitorpamplona.amethyst.service.lang.ResultOrError +import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import java.util.Locale + +@Composable +fun TranslateableRichTextViewer( + content: String, + canPreview: Boolean, + tags: List>?, + accountViewModel: AccountViewModel, + navController: NavController +) { + val translatedTextState = remember { + mutableStateOf(ResultOrError(content, null, null, null)) + } + + var showOriginal by remember { mutableStateOf(false) } + var langSettingsPopupExpanded by remember { mutableStateOf(false) } + + val context = LocalContext.current + + val accountState by accountViewModel.accountLanguagesLiveData.observeAsState() + val account = accountState?.account ?: return + + LaunchedEffect(accountState) { + LanguageTranslatorService.autoTranslate(content, account.dontTranslateFrom, account.translateTo) + .addOnCompleteListener { task -> + if (task.isSuccessful) { + translatedTextState.value = task.result + } else { + translatedTextState.value = ResultOrError(content, null, null, null) + } + } + } + + val toBeViewed = if (showOriginal) content else translatedTextState.value.result ?: content + + Column(modifier = Modifier.padding(top = 5.dp)) { + ExpandableRichTextViewer( + toBeViewed, + canPreview, + tags, + navController + ) + + val target = translatedTextState.value.targetLang + val source = translatedTextState.value.sourceLang + + if (source != null && target != null) { + if (source != target) { + Row(modifier = Modifier.fillMaxWidth().padding(top = 5.dp)) { + val clickableTextStyle = + SpanStyle(color = MaterialTheme.colors.primary.copy(alpha = 0.52f)) + + val annotatedTranslationString = buildAnnotatedString { + withStyle(clickableTextStyle) { + pushStringAnnotation("langSettings", true.toString()) + append("Auto") + } + + append("-translated from ") + + withStyle(clickableTextStyle) { + pushStringAnnotation("showOriginal", true.toString()) + append(Locale(source).displayName) + } + + append(" to ") + + withStyle(clickableTextStyle) { + pushStringAnnotation("showOriginal", false.toString()) + append(Locale(target).displayName) + } + } + + ClickableText( + text = annotatedTranslationString, + style = LocalTextStyle.current.copy(color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)), + overflow = TextOverflow.Visible, + maxLines = 3 + ) { spanOffset -> + annotatedTranslationString.getStringAnnotations(spanOffset, spanOffset) + .firstOrNull() + ?.also { span -> + if (span.tag == "showOriginal") + showOriginal = span.item.toBoolean() + else + langSettingsPopupExpanded = !langSettingsPopupExpanded + } + } + + DropdownMenu( + expanded = langSettingsPopupExpanded, + onDismissRequest = { langSettingsPopupExpanded = false } + ) { + DropdownMenuItem(onClick = { + accountViewModel.dontTranslateFrom(source, context) + langSettingsPopupExpanded = false + }) { + Text("Never translate from ${Locale(source).displayName}") + } + Divider() + val languageList = + ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()) + for (i in 0 until languageList.size()) { + languageList.get(i)?.let { lang -> + DropdownMenuItem(onClick = { + accountViewModel.translateTo(lang, context) + langSettingsPopupExpanded = false + }) { + Text("Always translate to ${lang.displayName}") + } + } + } + } + } + } + } + } +} \ No newline at end of file