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 14a462fdd..4e578302d 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 @@ -12,6 +12,7 @@ import androidx.compose.foundation.text.appendInlineContent import androidx.compose.material.Icon import androidx.compose.material.LocalTextStyle import androidx.compose.material.MaterialTheme +import androidx.compose.material.ProvideTextStyle import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState @@ -23,11 +24,15 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDirection +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import com.halilibo.richtext.markdown.Markdown import com.halilibo.richtext.markdown.MarkdownParseOptions +import com.halilibo.richtext.ui.HeadingStyle import com.halilibo.richtext.ui.RichTextStyle import com.halilibo.richtext.ui.material.MaterialRichText import com.halilibo.richtext.ui.resolveDefaults @@ -84,6 +89,36 @@ fun isValidURL(url: String?): Boolean { } } +internal val DefaultHeadingStyle: HeadingStyle = { level, textStyle -> + when (level) { + 0 -> textStyle.copy( + fontSize = 30.sp, + fontWeight = FontWeight.Light + ) + 1 -> textStyle.copy( + fontSize = 26.sp, + fontWeight = FontWeight.Light + ) + 2 -> textStyle.copy( + fontSize = 22.sp, + fontWeight = FontWeight.Light + ) + 3 -> textStyle.copy( + fontSize = 20.sp, + fontWeight = FontWeight.Bold + ) + 4 -> textStyle.copy( + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + 5 -> textStyle.copy( + fontWeight = FontWeight.Bold + ) + else -> textStyle + } +} + +internal val DefaultParagraphSpacing: TextUnit = 12.sp val richTextDefaults = RichTextStyle().resolveDefaults() fun isMarkdown(content: String): Boolean { @@ -405,6 +440,8 @@ fun RenderCustomEmoji(word: String, state: RichTextViewerState) { @Composable private fun RenderContentAsMarkdown(content: String, backgroundColor: MutableState, tags: ImmutableListOfLists?, nav: (String) -> Unit) { val myMarkDownStyle = richTextDefaults.copy( + paragraphSpacing = DefaultParagraphSpacing, + headingStyle = DefaultHeadingStyle, codeBlockStyle = richTextDefaults.codeBlockStyle?.copy( textStyle = TextStyle( fontFamily = FontFamily.Monospace, @@ -449,15 +486,17 @@ private fun RenderContentAsMarkdown(content: String, backgroundColor: MutableSta Unit } - MaterialRichText( - style = myMarkDownStyle - ) { - RefreshableContent(content, tags) { - Markdown( - content = it, - markdownParseOptions = MarkdownParseOptions.Default, - onLinkClicked = onClick - ) + ProvideTextStyle(TextStyle(lineHeight = 1.30.em)) { + MaterialRichText( + style = myMarkDownStyle + ) { + RefreshableContent(content, tags) { + Markdown( + content = it, + markdownParseOptions = MarkdownParseOptions.Default, + onLinkClicked = onClick + ) + } } } } @@ -652,7 +691,7 @@ private fun returnMarkdownWithSpecialContent(content: String, tags: ImmutableLis if (isValidURL(word)) { val removedParamsFromUrl = word.split("?")[0].lowercase() if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) { - returnContent += "$word " + returnContent += "![]($word) " } else { returnContent += "[$word]($word) " } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt index 4228c994a..2f7e6d657 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt @@ -22,6 +22,7 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme +import androidx.compose.material.ProvideTextStyle import androidx.compose.material.SnackbarDefaults.backgroundColor import androidx.compose.material.Text import androidx.compose.material.icons.Icons @@ -45,8 +46,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import coil.compose.AsyncImage import com.vitorpamplona.amethyst.R @@ -60,18 +63,13 @@ import com.vitorpamplona.amethyst.service.model.PeopleListEvent import com.vitorpamplona.amethyst.service.model.PinListEvent import com.vitorpamplona.amethyst.service.model.PollNoteEvent import com.vitorpamplona.amethyst.service.model.RelaySetEvent -import com.vitorpamplona.amethyst.ui.actions.ImmutableListOfLists -import com.vitorpamplona.amethyst.ui.actions.toImmutableListOfLists import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status -import com.vitorpamplona.amethyst.ui.components.SensitivityWarning -import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.note.* import com.vitorpamplona.amethyst.ui.note.BadgeDisplay import com.vitorpamplona.amethyst.ui.note.BlankNote import com.vitorpamplona.amethyst.ui.note.DisplayFollowingHashtagsInPost import com.vitorpamplona.amethyst.ui.note.DisplayPoW import com.vitorpamplona.amethyst.ui.note.DisplayReward -import com.vitorpamplona.amethyst.ui.note.DisplayUncitedHashtags import com.vitorpamplona.amethyst.ui.note.HiddenNote import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture import com.vitorpamplona.amethyst.ui.note.NoteCompose @@ -84,7 +82,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.lessImportantLink import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.selectedNote -import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.delay @@ -139,16 +136,18 @@ fun ThreadFeedView(noteId: String, viewModel: FeedViewModel, accountViewModel: A ) { itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { index, item -> if (index == 0) { - NoteMaster( - item, - modifier = Modifier.drawReplyLevel( - item.replyLevel(), - MaterialTheme.colors.placeholderText, - if (item.idHex == noteId) MaterialTheme.colors.lessImportantLink else MaterialTheme.colors.placeholderText - ), - accountViewModel = accountViewModel, - nav = nav - ) + ProvideTextStyle(TextStyle(fontSize = 16.sp, lineHeight = 1.20.em)) { + NoteMaster( + item, + modifier = Modifier.drawReplyLevel( + item.replyLevel(), + MaterialTheme.colors.placeholderText, + if (item.idHex == noteId) MaterialTheme.colors.lessImportantLink else MaterialTheme.colors.placeholderText + ), + accountViewModel = accountViewModel, + nav = nav + ) + } } else { Column() { Row() { @@ -326,7 +325,7 @@ fun NoteMaster( if (noteEvent is BadgeDefinitionEvent) { BadgeDisplay(baseNote = note) } else if (noteEvent is LongTextNoteEvent) { - Row(modifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 10.dp, bottom = 12.dp)) { + Row(modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 12.dp)) { Column { noteEvent.image()?.let { AsyncImage( @@ -338,25 +337,23 @@ fun NoteMaster( contentScale = ContentScale.FillWidth, modifier = Modifier.fillMaxWidth() ) + Spacer(modifier = Modifier.height(10.dp)) } noteEvent.title()?.let { Text( text = it, - fontSize = 30.sp, - fontWeight = FontWeight.Bold, - modifier = Modifier - .fillMaxWidth() - .padding(top = 10.dp) + fontSize = 28.sp, + fontWeight = FontWeight.Light, + modifier = Modifier.fillMaxWidth() ) + Spacer(modifier = Modifier.height(10.dp)) } noteEvent.summary()?.let { Text( text = it, - modifier = Modifier - .fillMaxWidth() - .padding(top = 10.dp), + modifier = Modifier.fillMaxWidth(), color = Color.Gray ) } @@ -404,47 +401,32 @@ fun NoteMaster( accountViewModel, nav ) - } else { - val eventContent = note.event?.content() - + } else if (noteEvent is PollNoteEvent) { val canPreview = note.author == account.userProfile() || (note.author?.let { account.userProfile().isFollowingCached(it) } ?: true) || !noteForReports.hasAnyReports() - if (eventContent != null) { - val hasSensitiveContent = remember(note.event) { note.event?.isSensitive() ?: false } - val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: ImmutableListOfLists() } + RenderPoll( + baseNote, + false, + canPreview, + backgroundColor, + accountViewModel, + nav + ) + } else { + val canPreview = note.author == account.userProfile() || + (note.author?.let { account.userProfile().isFollowingCached(it) } ?: true) || + !noteForReports.hasAnyReports() - SensitivityWarning( - hasSensitiveContent = hasSensitiveContent, - accountViewModel = accountViewModel - ) { - TranslatableRichTextViewer( - eventContent, - canPreview, - remember { Modifier.fillMaxWidth() }, - tags, - backgroundColor, - accountViewModel, - nav - ) - } - - val hashtags = remember { - noteEvent.hashtags().toImmutableList() - } - DisplayUncitedHashtags(hashtags, eventContent, nav) - - if (noteEvent is PollNoteEvent) { - PollNote( - note, - canPreview, - backgroundColor, - accountViewModel, - nav - ) - } - } + RenderTextEvent( + baseNote, + false, + canPreview, + backgroundColor, + accountViewModel, + nav + ) } } }