From 52ef356c063cfb9c6c7430a8d59389ad0b2633d9 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 22 Jun 2023 11:39:56 -0400 Subject: [PATCH] Minor performance improvements throughout the code --- .../com/vitorpamplona/amethyst/model/Note.kt | 6 +- .../com/vitorpamplona/amethyst/model/User.kt | 6 +- .../amethyst/service/model/RepostEvent.kt | 4 +- .../amethyst/ui/actions/NewPostView.kt | 3 +- .../amethyst/ui/actions/NewRelayListView.kt | 9 +- .../amethyst/ui/components/RichTextViewer.kt | 46 +- .../amethyst/ui/components/TextSpinner.kt | 4 +- .../amethyst/ui/note/NoteCompose.kt | 320 ++++++----- .../amethyst/ui/note/PollNote.kt | 9 +- .../amethyst/ui/note/ReactionsRow.kt | 511 +++++++++++------- .../amethyst/ui/note/UpdateZapAmountDialog.kt | 4 +- .../amethyst/ui/screen/CardFeedViewModel.kt | 6 +- .../amethyst/ui/screen/ThreadFeedView.kt | 2 +- .../ui/screen/loggedIn/ChannelScreen.kt | 33 +- .../ui/screen/loggedIn/VideoScreen.kt | 2 +- .../ui/screen/loggedOff/LoginScreen.kt | 4 +- .../vitorpamplona/amethyst/ui/theme/Color.kt | 2 + .../vitorpamplona/amethyst/ui/theme/Shape.kt | 31 ++ .../vitorpamplona/amethyst/ui/theme/Theme.kt | 111 +++- .../vitorpamplona/amethyst/ui/theme/Type.kt | 7 +- 20 files changed, 712 insertions(+), 408 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt index c3a626bde..fd9598f13 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt @@ -490,9 +490,9 @@ class NoteLiveData(val note: Note) : LiveData(NoteState(note)) { fun invalidateData() { bundler.invalidate() { - // if (hasObservers()) { - postValue(NoteState(note)) - // } + if (hasActiveObservers()) { + postValue(NoteState(note)) + } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt index 1884bbc8e..61ef9c6a1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt @@ -144,8 +144,10 @@ class User(val pubkeyHex: String) { } fun updateAcceptedBadges(note: AddressableNote) { - acceptedBadges = note - liveSet?.badges?.invalidateData() + if (acceptedBadges?.idHex != note.idHex) { + acceptedBadges = note + liveSet?.badges?.invalidateData() + } } fun addZap(zapRequest: Note, zap: Note?) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/RepostEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/RepostEvent.kt index e289423c7..e58f6b7bb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/RepostEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/RepostEvent.kt @@ -17,8 +17,8 @@ class RepostEvent( sig: HexKey ) : Event(id, pubKey, createdAt, kind, tags, content, sig) { - fun boostedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) } - fun originalAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) } + fun boostedPost() = taggedEvents() + fun originalAuthor() = taggedUsers() fun containedPost() = try { fromJson(content, Client.lenient) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index c40e42a44..16cbe60d9 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner import com.vitorpamplona.amethyst.ui.screen.loggedIn.UserLine import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.ButtonBorder +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 @@ -621,7 +622,7 @@ private fun ForwardZapTo( Text( text = stringResource(R.string.zap_forward_lnAddress), color = MaterialTheme.colors.placeholderText, - fontSize = 14.sp + fontSize = Font14SP ) }, colors = TextFieldDefaults diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewRelayListView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewRelayListView.kt index 142b502c8..a88acd128 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewRelayListView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewRelayListView.kt @@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.model.RelaySetupInfo import com.vitorpamplona.amethyst.service.relays.FeedType import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.ButtonBorder +import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.placeholderText import kotlinx.coroutines.launch @@ -167,7 +168,7 @@ fun ServerConfigHeader() { Text( text = stringResource(R.string.bytes), maxLines = 1, - fontSize = 14.sp, + fontSize = Font14SP, modifier = Modifier.weight(1.2f), color = MaterialTheme.colors.placeholderText ) @@ -177,7 +178,7 @@ fun ServerConfigHeader() { Text( text = stringResource(id = R.string.bytes), maxLines = 1, - fontSize = 14.sp, + fontSize = Font14SP, modifier = Modifier.weight(1.2f), color = MaterialTheme.colors.placeholderText ) @@ -187,7 +188,7 @@ fun ServerConfigHeader() { Text( text = stringResource(R.string.errors), maxLines = 1, - fontSize = 14.sp, + fontSize = Font14SP, modifier = Modifier.weight(1f), color = MaterialTheme.colors.placeholderText ) @@ -197,7 +198,7 @@ fun ServerConfigHeader() { Text( text = stringResource(R.string.spam), maxLines = 1, - fontSize = 14.sp, + fontSize = Font14SP, modifier = Modifier.weight(1f), color = MaterialTheme.colors.placeholderText ) 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 34f2b8ac2..ea9903708 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 @@ -3,7 +3,6 @@ package com.vitorpamplona.amethyst.ui.components import android.util.Log import android.util.LruCache import android.util.Patterns -import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.ClickableText @@ -19,16 +18,13 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.compositeOver 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 @@ -47,7 +43,9 @@ import com.vitorpamplona.amethyst.ui.actions.ImmutableListOfLists import com.vitorpamplona.amethyst.ui.actions.toImmutableListOfLists import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.theme.MarkdownTextStyle import com.vitorpamplona.amethyst.ui.theme.QuoteBorder +import com.vitorpamplona.amethyst.ui.theme.markdownStyle import com.vitorpamplona.amethyst.ui.theme.subtleBorder import com.vitorpamplona.amethyst.ui.uriToRoute import kotlinx.collections.immutable.ImmutableList @@ -439,42 +437,6 @@ 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, - fontSize = 14.sp - ), - modifier = Modifier - .padding(0.dp) - .fillMaxWidth() - .clip(shape = QuoteBorder) - .border( - 1.dp, - MaterialTheme.colors.subtleBorder, - QuoteBorder - ) - .background( - MaterialTheme.colors.onSurface - .copy(alpha = 0.05f) - .compositeOver(backgroundColor.value) - ) - ), - stringStyle = richTextDefaults.stringStyle?.copy( - linkStyle = SpanStyle( - color = MaterialTheme.colors.primary - ), - codeStyle = SpanStyle( - fontFamily = FontFamily.Monospace, - fontSize = 14.sp, - background = MaterialTheme.colors.onSurface.copy(alpha = 0.22f) - .compositeOver(backgroundColor.value) - ) - ) - ) - val uri = LocalUriHandler.current val onClick = { link: String -> val route = uriToRoute(link) @@ -486,9 +448,9 @@ private fun RenderContentAsMarkdown(content: String, backgroundColor: MutableSta Unit } - ProvideTextStyle(TextStyle(lineHeight = 1.30.em)) { + ProvideTextStyle(MarkdownTextStyle) { MaterialRichText( - style = myMarkDownStyle + style = MaterialTheme.colors.markdownStyle ) { RefreshableContent(content, tags) { Markdown( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/TextSpinner.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/TextSpinner.kt index a01731873..e09fa5cde 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/TextSpinner.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/TextSpinner.kt @@ -29,8 +29,8 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog +import com.vitorpamplona.amethyst.ui.theme.Font14SP import kotlinx.collections.immutable.ImmutableList @Composable @@ -120,7 +120,7 @@ fun SpinnerSelectionDialog( modifier = Modifier .fillMaxWidth() ) { - Text(text = it, color = Color.Gray, fontSize = 14.sp) + Text(text = it, color = Color.Gray, fontSize = Font14SP) } } } 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 f4e9e2090..b712db948 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 @@ -96,7 +96,6 @@ import com.vitorpamplona.amethyst.service.model.BaseTextNoteEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent -import com.vitorpamplona.amethyst.service.model.EventInterface import com.vitorpamplona.amethyst.service.model.FileHeaderEvent import com.vitorpamplona.amethyst.service.model.FileStorageHeaderEvent import com.vitorpamplona.amethyst.service.model.GenericRepostEvent @@ -142,14 +141,28 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange +import com.vitorpamplona.amethyst.ui.theme.DiviserThickness +import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.Following +import com.vitorpamplona.amethyst.ui.theme.Font14SP +import com.vitorpamplona.amethyst.ui.theme.HalfPadding +import com.vitorpamplona.amethyst.ui.theme.HalfVertSpacer import com.vitorpamplona.amethyst.ui.theme.QuoteBorder +import com.vitorpamplona.amethyst.ui.theme.Size25dp +import com.vitorpamplona.amethyst.ui.theme.Size30Modifier +import com.vitorpamplona.amethyst.ui.theme.Size30dp import com.vitorpamplona.amethyst.ui.theme.Size35dp +import com.vitorpamplona.amethyst.ui.theme.Size55Modifier +import com.vitorpamplona.amethyst.ui.theme.Size55dp +import com.vitorpamplona.amethyst.ui.theme.StdStartPadding +import com.vitorpamplona.amethyst.ui.theme.WidthAuthorPictureModifier +import com.vitorpamplona.amethyst.ui.theme.lessImportantLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.replyBackground +import com.vitorpamplona.amethyst.ui.theme.repostProfileBorder import com.vitorpamplona.amethyst.ui.theme.subtleBorder import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet @@ -181,9 +194,13 @@ fun NoteCompose( nav: (String) -> Unit ) { val noteState by baseNote.live().metadata.observeAsState() - val noteEvent = remember(noteState) { noteState?.note?.event } + val missingEvent by remember(noteState) { + derivedStateOf { + noteState?.note?.event == null + } + } - if (noteEvent == null) { + if (missingEvent) { LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup -> BlankNote( remember { @@ -337,12 +354,12 @@ fun WatchForReports( accountViewModel: AccountViewModel, onChange: (Boolean, Boolean, Set) -> Unit ) { - val accountState by accountViewModel.accountLiveData.observeAsState() + val userFollowsState by accountViewModel.userFollows.observeAsState() val noteReportsState by note.live().reports.observeAsState() - LaunchedEffect(key1 = noteReportsState, key2 = accountState) { + LaunchedEffect(key1 = noteReportsState, key2 = userFollowsState) { launch(Dispatchers.Default) { - accountState?.account?.let { loggedIn -> + accountViewModel.account.let { loggedIn -> val newCanPreview = note.author?.pubkeyHex == loggedIn.userProfile().pubkeyHex || (note.author?.let { loggedIn.userProfile().isFollowingCached(it) } ?: true) || noteReportsState?.note?.hasAnyReports() != true @@ -376,10 +393,22 @@ fun NormalNote( nav: (String) -> Unit ) { val noteEvent = remember { baseNote.event } - val channelHex = remember { baseNote.channelHex() } - if ((noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) && channelHex != null) { - ChannelHeader(channelHex = channelHex, showVideo = !makeItShort, showBottomDiviser = true, accountViewModel = accountViewModel, nav = nav) + val isChannelHeader by remember { + derivedStateOf { + (baseNote.event is ChannelCreateEvent || baseNote.event is ChannelMetadataEvent) && baseNote.channelHex() != null + } + } + + if (isChannelHeader) { + ChannelHeader( + channelNote = baseNote, + showVideo = !makeItShort, + showBottomDiviser = true, + accountViewModel = + accountViewModel, + nav = nav + ) } else if (noteEvent is BadgeDefinitionEvent) { BadgeDisplay(baseNote = baseNote) } else if (noteEvent is FileHeaderEvent) { @@ -387,20 +416,23 @@ fun NormalNote( } else if (noteEvent is FileStorageHeaderEvent) { FileStorageHeaderDisplay(baseNote) } else { - CheckNewAndRenderNote( - baseNote, - routeForLastRead, - modifier, - isBoostedNote, - isQuotedNote, - unPackReply, - makeItShort, - addMarginTop, - canPreview, - parentBackgroundColor, - accountViewModel, - nav - ) + LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup -> + CheckNewAndRenderNote( + baseNote, + routeForLastRead, + modifier, + isBoostedNote, + isQuotedNote, + unPackReply, + makeItShort, + addMarginTop, + canPreview, + parentBackgroundColor, + accountViewModel, + showPopup, + nav + ) + } } } @@ -417,6 +449,7 @@ private fun CheckNewAndRenderNote( canPreview: Boolean = true, parentBackgroundColor: MutableState? = null, accountViewModel: AccountViewModel, + showPopup: () -> Unit, nav: (String) -> Unit ) { val newItemColor = MaterialTheme.colors.newItemBackgroundColor @@ -464,22 +497,20 @@ private fun CheckNewAndRenderNote( } } - LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup -> - RenderNoteWithReactions( - modifier = updatedModifier, - backgroundColor = backgroundColor, - baseNote = baseNote, - isBoostedNote = isBoostedNote, - isQuotedNote = isQuotedNote, - addMarginTop = addMarginTop, - unPackReply = unPackReply, - makeItShort = makeItShort, - canPreview = canPreview, - accountViewModel = accountViewModel, - showPopup = showPopup, - nav = nav - ) - } + RenderNoteWithReactions( + modifier = updatedModifier, + backgroundColor = backgroundColor, + baseNote = baseNote, + isBoostedNote = isBoostedNote, + isQuotedNote = isQuotedNote, + addMarginTop = addMarginTop, + unPackReply = unPackReply, + makeItShort = makeItShort, + canPreview = canPreview, + accountViewModel = accountViewModel, + showPopup = showPopup, + nav = nav + ) } @Composable @@ -539,8 +570,8 @@ private fun RenderNoteWithReactions( } ) { if (notBoostedNorQuote) { - DrawAuthorImages(baseNote, accountViewModel, nav) - Spacer(modifier = Modifier.width(10.dp)) + AuthorAndRelayInformation(baseNote, accountViewModel, nav) + Spacer(modifier = DoubleHorzSpacer) } NoteBody( @@ -556,22 +587,25 @@ private fun RenderNoteWithReactions( ) } - if (!makeItShort && baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent) { - ReactionsRow( - baseNote = baseNote, - showReactionDetail = notBoostedNorQuote, - accountViewModel = accountViewModel, - nav = nav - ) - } else { - if (!isBoostedNote && baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent) { - Spacer(modifier = Modifier.height(10.dp)) + val isRepost = remember { baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent } + if (!isRepost) { + if (!makeItShort) { + ReactionsRow( + baseNote = baseNote, + showReactionDetail = notBoostedNorQuote, + accountViewModel = accountViewModel, + nav = nav + ) + } else { + if (!isBoostedNote) { + Spacer(modifier = DoubleVertSpacer) + } } } if (!isQuotedNote && !isBoostedNote) { Divider( - thickness = 0.25.dp + thickness = DiviserThickness ) } } @@ -605,7 +639,7 @@ private fun NoteBody( ) } - Spacer(modifier = Modifier.height(2.dp)) + Spacer(modifier = HalfVertSpacer) if (!makeItShort) { ReplyRow( @@ -1766,38 +1800,40 @@ private fun FirstUserInfoRow( accountViewModel: AccountViewModel, nav: (String) -> Unit ) { - val eventNote = remember { baseNote.event } ?: return - val time = remember { baseNote.createdAt() } ?: return - val padding = remember { - Modifier.padding(horizontal = 5.dp) - } + val isRepost = remember { baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent } - Row(verticalAlignment = Alignment.CenterVertically) { + Row(verticalAlignment = CenterVertically) { if (showAuthorPicture) { - NoteAuthorPicture(baseNote, nav, accountViewModel, remember { 25.dp }) - Spacer(padding) + NoteAuthorPicture(baseNote, nav, accountViewModel, Size25dp) + Spacer(HalfPadding) NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) }) } else { NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) }) } - if (eventNote is RepostEvent || eventNote is GenericRepostEvent) { - Text( - " ${stringResource(id = R.string.boosted)}", - fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.placeholderText, - maxLines = 1 - ) + if (isRepost) { + BoostedMark() } else { - DisplayFollowingHashtagsInPost(eventNote, accountViewModel, nav) + DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav) } - TimeAgo(time) + TimeAgo(baseNote) MoreOptionsButton(baseNote, accountViewModel) } } +@Composable +private fun BoostedMark() { + Text( + stringResource(id = R.string.boosted), + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.placeholderText, + maxLines = 1, + modifier = StdStartPadding + ) +} + @Composable private fun MoreOptionsButton( baseNote: Note, @@ -1825,19 +1861,16 @@ private fun MoreOptionsButton( } } +@Composable +fun TimeAgo(note: Note) { + val time = remember { note.createdAt() } ?: return + TimeAgo(time) +} + @Composable fun TimeAgo(time: Long) { val context = LocalContext.current - var timeStr by remember { mutableStateOf("") } - - LaunchedEffect(key1 = time) { - launch(Dispatchers.IO) { - val newTimeStr = timeAgo(time, context = context) - if (newTimeStr != timeStr) { - timeStr = newTimeStr - } - } - } + val timeStr by remember { mutableStateOf(timeAgo(time, context = context)) } Text( text = timeStr, @@ -1847,35 +1880,60 @@ fun TimeAgo(time: Long) { } @Composable -private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { - val baseChannelHex = remember { baseNote.channelHex() } - val modifier = remember { Modifier.width(55.dp) } - - Column(modifier) { +private fun AuthorAndRelayInformation(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { + Column(WidthAuthorPictureModifier) { // Draws the boosted picture outside the boosted card. - Box(modifier = modifier, contentAlignment = Alignment.BottomEnd) { - NoteAuthorPicture(baseNote, nav, accountViewModel, 55.dp) - - if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) { - RepostNoteAuthorPicture(baseNote, accountViewModel, nav) - } - - if (baseNote.event is ChannelMessageEvent && baseChannelHex != null) { - LoadChannel(baseChannelHex) { channel -> - ChannelNotePicture(channel) - } - } + Box(modifier = Size55Modifier, contentAlignment = Alignment.BottomEnd) { + RenderAuthorImages(baseNote, nav, accountViewModel) } - if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) { - val baseReply = remember { + BadgeBox(baseNote, accountViewModel, nav) + } +} + +@Composable +private fun BadgeBox( + baseNote: Note, + accountViewModel: AccountViewModel, + nav: (String) -> Unit +) { + val isRepost = remember { baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent } + + if (isRepost) { + val baseReply by remember { + derivedStateOf { baseNote.replyTo?.lastOrNull() } - baseReply?.let { - RelayBadges(it, accountViewModel, nav) + } + baseReply?.let { + RelayBadges(it, accountViewModel, nav) + } + } else { + RelayBadges(baseNote, accountViewModel, nav) + } +} + +@Composable +private fun RenderAuthorImages( + baseNote: Note, + nav: (String) -> Unit, + accountViewModel: AccountViewModel +) { + val isRepost = remember { baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent } + val isChannel = remember { baseNote.event is ChannelMessageEvent && baseNote.channelHex() != null } + + NoteAuthorPicture(baseNote, nav, accountViewModel, Size55dp) + + if (isRepost) { + RepostNoteAuthorPicture(baseNote, accountViewModel, nav) + } + + if (isChannel) { + val baseChannelHex = remember { baseNote.channelHex() } + if (baseChannelHex != null) { + LoadChannel(baseChannelHex) { channel -> + ChannelNotePicture(channel) } - } else { - RelayBadges(baseNote, accountViewModel, nav) } } } @@ -1945,24 +2003,20 @@ private fun RepostNoteAuthorPicture( accountViewModel: AccountViewModel, nav: (String) -> Unit ) { - val baseRepost = remember { baseNote.replyTo?.lastOrNull() } - - val modifier = remember { - Modifier.size(30.dp) + val baseRepost by remember { + derivedStateOf { + baseNote.replyTo?.lastOrNull() + } } baseRepost?.let { - Box(modifier) { + Box(Size30Modifier) { NoteAuthorPicture( baseNote = it, nav = nav, accountViewModel = accountViewModel, - size = remember { 30.dp }, - pictureModifier = Modifier.border( - 2.dp, - MaterialTheme.colors.background, - CircleShape - ) + size = Size30dp, + pictureModifier = MaterialTheme.colors.repostProfileBorder ) } } @@ -2073,10 +2127,12 @@ private fun LoadAndDisplayUser( @Composable fun DisplayFollowingHashtagsInPost( - noteEvent: EventInterface, + baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit ) { + val noteEvent = remember { baseNote.event } ?: return + val accountState by accountViewModel.accountLiveData.observeAsState() var firstTag by remember { mutableStateOf(null) } @@ -2148,12 +2204,14 @@ fun DisplayUncitedHashtags( fun DisplayPoW( pow: Int ) { + val powStr = remember(pow) { + "PoW-$pow" + } + Text( - "PoW-$pow", - color = MaterialTheme.colors.primary.copy( - alpha = 0.52f - ), - fontSize = 14.sp, + powStr, + color = MaterialTheme.colors.lessImportantLink, + fontSize = Font14SP, fontWeight = FontWeight.Bold, maxLines = 1 ) @@ -2673,19 +2731,23 @@ private fun CreateImageHeader( painter = painterResource(R.drawable.profile_banner), contentDescription = stringResource(R.string.profile_banner), contentScale = ContentScale.FillWidth, - modifier = Modifier - .fillMaxWidth() - .height(150.dp) + modifier = remember { + Modifier + .fillMaxWidth() + .height(150.dp) + } ) Box( - Modifier - .width(75.dp) - .height(75.dp) - .padding(10.dp) - .align(Alignment.BottomStart) + remember { + Modifier + .width(75.dp) + .height(75.dp) + .padding(10.dp) + .align(Alignment.BottomStart) + } ) { - NoteAuthorPicture(baseNote = note, accountViewModel = accountViewModel, size = 55.dp) + NoteAuthorPicture(baseNote = note, accountViewModel = accountViewModel, size = Size55dp) } } } 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 68b9d08e9..e5d033be1 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 @@ -20,7 +20,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Popup import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R @@ -32,6 +31,7 @@ import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.ButtonBorder +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 @@ -428,9 +428,12 @@ fun ZapVote( // only show tallies after a user has zapped note if (!pollViewModel.canZap()) { + val amountStr = remember(poolOption.zappedValue) { + showAmount(poolOption.zappedValue) + } Text( - showAmount(poolOption.zappedValue), - fontSize = 14.sp, + text = amountStr, + fontSize = Font14SP, color = MaterialTheme.colors.placeholderText, modifier = modifier ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index 4c249cf00..4ded6f804 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -45,12 +45,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.Center import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role @@ -59,21 +59,34 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Popup import coil.compose.AsyncImage import coil.request.CachePolicy import coil.request.ImageRequest import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.actions.NewPostView import com.vitorpamplona.amethyst.ui.screen.CombinedZap import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.ButtonBorder +import com.vitorpamplona.amethyst.ui.theme.DarkerGreen +import com.vitorpamplona.amethyst.ui.theme.Font14SP +import com.vitorpamplona.amethyst.ui.theme.HalfDoubleVertSpacer +import com.vitorpamplona.amethyst.ui.theme.NoSoTinyBorders +import com.vitorpamplona.amethyst.ui.theme.ReactionRowExpandButton +import com.vitorpamplona.amethyst.ui.theme.ReactionRowHeight +import com.vitorpamplona.amethyst.ui.theme.ReactionRowZapraiserSize +import com.vitorpamplona.amethyst.ui.theme.Size18dp +import com.vitorpamplona.amethyst.ui.theme.Size19dp +import com.vitorpamplona.amethyst.ui.theme.Size20Modifier +import com.vitorpamplona.amethyst.ui.theme.Size20dp +import com.vitorpamplona.amethyst.ui.theme.Size22Modifier +import com.vitorpamplona.amethyst.ui.theme.Size24dp +import com.vitorpamplona.amethyst.ui.theme.TinyBorders import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.placeholderText +import com.vitorpamplona.amethyst.ui.theme.subtleButton import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineScope @@ -84,67 +97,118 @@ import java.math.RoundingMode import kotlin.math.roundToInt @Composable -fun ReactionsRow(baseNote: Note, showReactionDetail: Boolean, accountViewModel: AccountViewModel, nav: (String) -> Unit) { - val grayTint = MaterialTheme.colors.placeholderText - +fun ReactionsRow( + baseNote: Note, + showReactionDetail: Boolean, + accountViewModel: AccountViewModel, + nav: (String) -> Unit +) { val wantsToSeeReactions = remember { - mutableStateOf(false) + mutableStateOf(false) } - val zapraiserAmount = remember { - baseNote.event?.zapraiserAmount() - } + Spacer(modifier = HalfDoubleVertSpacer) - Spacer(modifier = Modifier.height(7.dp)) + InnerReactionRow(baseNote, showReactionDetail, wantsToSeeReactions, accountViewModel, nav) - Row(verticalAlignment = CenterVertically, modifier = Modifier.padding(start = 10.dp)) { - if (showReactionDetail) { - Row( - verticalAlignment = CenterVertically, - modifier = Modifier - .width(65.dp) - .padding(start = 31.dp), - horizontalArrangement = Arrangement.Start - ) { - ExpandButton(baseNote, wantsToSeeReactions) - } - } - - Row(verticalAlignment = CenterVertically, modifier = remember { Modifier.weight(1f) }) { - ReplyReactionWithDialog(accountViewModel, nav, baseNote, grayTint) - } - Row(verticalAlignment = CenterVertically, modifier = remember { Modifier.weight(1f) }) { - BoostWithDialog(accountViewModel, nav, baseNote, grayTint) - } - Row(verticalAlignment = CenterVertically, modifier = remember { Modifier.weight(1f) }) { - LikeReaction(baseNote, grayTint, accountViewModel) - } - Row(verticalAlignment = CenterVertically, modifier = remember { Modifier.weight(1f) }) { - ZapReaction(baseNote, grayTint, accountViewModel) - } - Row(verticalAlignment = CenterVertically, modifier = remember { Modifier.weight(1f) }) { - ViewCountReaction(baseNote.idHex, grayTint) - } - } - - if (zapraiserAmount != null && zapraiserAmount > 0) { - Spacer(modifier = Modifier.height(4.dp)) - Row( - verticalAlignment = CenterVertically, - modifier = Modifier - .fillMaxWidth() - .padding(start = if (showReactionDetail) 75.dp else 0.dp), - horizontalArrangement = Arrangement.Start - ) { - RenderZapRaiser(baseNote, zapraiserAmount, wantsToSeeReactions.value, accountViewModel) - } - } + LoadAndDisplayZapraiser(baseNote, showReactionDetail, wantsToSeeReactions, accountViewModel) if (showReactionDetail && wantsToSeeReactions.value) { ReactionDetailGallery(baseNote, nav, accountViewModel) } - Spacer(modifier = Modifier.height(7.dp)) + Spacer(modifier = HalfDoubleVertSpacer) +} + +@Composable +private fun InnerReactionRow( + baseNote: Note, + showReactionDetail: Boolean, + wantsToSeeReactions: MutableState, + accountViewModel: AccountViewModel, + nav: (String) -> Unit +) { + Row(verticalAlignment = CenterVertically, modifier = ReactionRowHeight) { + if (showReactionDetail) { + Column( + verticalArrangement = Arrangement.Center, + modifier = ReactionRowExpandButton + ) { + Row(verticalAlignment = CenterVertically) { + ExpandButton(baseNote, wantsToSeeReactions) + } + } + } + Column( + verticalArrangement = Arrangement.Center, + modifier = remember { Modifier.weight(1f) } + ) { + Row(verticalAlignment = CenterVertically) { + ReplyReactionWithDialog(baseNote, MaterialTheme.colors.placeholderText, accountViewModel, nav) + } + } + Column( + verticalArrangement = Arrangement.Center, + modifier = remember { Modifier.weight(1f) } + ) { + Row(verticalAlignment = CenterVertically) { + BoostWithDialog(baseNote, MaterialTheme.colors.placeholderText, accountViewModel, nav) + } + } + Column( + verticalArrangement = Arrangement.Center, + modifier = remember { Modifier.weight(1f) } + ) { + Row(verticalAlignment = CenterVertically) { + LikeReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel) + } + } + Column( + verticalArrangement = Arrangement.Center, + modifier = remember { Modifier.weight(1f) } + ) { + Row(verticalAlignment = CenterVertically) { + ZapReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel) + } + } + Column( + verticalArrangement = Arrangement.Center, + modifier = remember { Modifier.weight(1f) } + ) { + Row(verticalAlignment = CenterVertically) { + ViewCountReaction(baseNote.idHex, MaterialTheme.colors.placeholderText) + } + } + } +} + +@Composable +private fun LoadAndDisplayZapraiser( + baseNote: Note, + showReactionDetail: Boolean, + wantsToSeeReactions: MutableState, + accountViewModel: AccountViewModel +) { + val zapraiserAmount by remember { + derivedStateOf { + baseNote.event?.zapraiserAmount() ?: 0 + } + } + + if (zapraiserAmount > 0) { + Spacer(modifier = remember { Modifier.height(4.dp) }) + Row( + verticalAlignment = CenterVertically, + modifier = remember { + Modifier + .fillMaxWidth() + .padding(start = if (showReactionDetail) 75.dp else 0.dp) + }, + horizontalArrangement = Arrangement.Start + ) { + RenderZapRaiser(baseNote, zapraiserAmount, wantsToSeeReactions.value, accountViewModel) + } + } } @Composable @@ -165,11 +229,17 @@ fun RenderZapRaiser(baseNote: Note, zapraiserAmount: Long, details: Boolean, acc } if (Math.abs(zapraiserProgress - percentage) > 0.001) { - zapraiserProgress = percentage - if (percentage > 0.99) { - zapraiserLeft = "0" + val newZapraiserProgress = percentage + val newZapraiserLeft = if (percentage > 0.99) { + "0" } else { - zapraiserLeft = showAmount((zapraiserAmount * (1 - percentage)).toBigDecimal()) + showAmount((zapraiserAmount * (1 - percentage)).toBigDecimal()) + } + if (zapraiserLeft != newZapraiserLeft) { + zapraiserLeft = newZapraiserLeft + } + if (zapraiserProgress != newZapraiserProgress) { + zapraiserProgress = newZapraiserProgress } } } @@ -177,34 +247,34 @@ fun RenderZapRaiser(baseNote: Note, zapraiserAmount: Long, details: Boolean, acc } val color = if (zapraiserProgress > 0.99) { - Color.Green.copy(alpha = 0.32f) + DarkerGreen } else { MaterialTheme.colors.mediumImportanceLink } - Box( - Modifier.padding(end = 10.dp).fillMaxWidth() - ) { + Box(ReactionRowZapraiserSize) { LinearProgressIndicator( - modifier = Modifier.matchParentSize(), + modifier = ReactionRowZapraiserSize, color = color, progress = zapraiserProgress ) - Row( - verticalAlignment = CenterVertically, - modifier = Modifier.padding(2.dp) - ) { - if (details) { - val totalPercentage = remember(zapraiserProgress) { - "${(zapraiserProgress * 100).roundToInt()}%" + if (details) { + Row( + verticalAlignment = CenterVertically, + modifier = TinyBorders + ) { + val totalPercentage by remember(zapraiserProgress) { + derivedStateOf { + "${(zapraiserProgress * 100).roundToInt()}%" + } } Text( text = stringResource(id = R.string.sats_to_complete, totalPercentage, zapraiserLeft), - modifier = Modifier.padding(start = 5.dp, end = 5.dp, top = 2.dp, bottom = 2.dp), + modifier = NoSoTinyBorders, color = MaterialTheme.colors.placeholderText, - fontSize = 14.sp + fontSize = Font14SP ) } } @@ -226,27 +296,32 @@ private fun ExpandButton(baseNote: Note, wantsToSeeReactions: MutableState) { + IconButton( + onClick = { + wantsToSeeReactions.value = !wantsToSeeReactions.value + }, + modifier = Size20Modifier + ) { + if (wantsToSeeReactions.value) { + Icon( + imageVector = Icons.Default.ExpandLess, + null, + modifier = Size22Modifier, + tint = MaterialTheme.colors.subtleButton + ) + } else { + Icon( + imageVector = Icons.Default.ExpandMore, + null, + modifier = Size22Modifier, + tint = MaterialTheme.colors.subtleButton + ) } } } @@ -320,10 +395,10 @@ private fun ReactionDetailGallery( @Composable private fun BoostWithDialog( - accountViewModel: AccountViewModel, - nav: (String) -> Unit, baseNote: Note, - grayTint: Color + grayTint: Color, + accountViewModel: AccountViewModel, + nav: (String) -> Unit ) { var wantsToQuote by remember { mutableStateOf(null) @@ -340,10 +415,10 @@ private fun BoostWithDialog( @Composable private fun ReplyReactionWithDialog( - accountViewModel: AccountViewModel, - nav: (String) -> Unit, baseNote: Note, - grayTint: Color + grayTint: Color, + accountViewModel: AccountViewModel, + nav: (String) -> Unit ) { var wantsToReplyTo by remember { mutableStateOf(null) @@ -364,7 +439,7 @@ fun ReplyReaction( grayTint: Color, accountViewModel: AccountViewModel, showCounter: Boolean = true, - iconSize: Dp = 18.dp, + iconSize: Dp = Size18dp, onPress: () -> Unit ) { val context = LocalContext.current @@ -418,7 +493,7 @@ fun ReplyCounter(baseNote: Note, textColor: Color) { Text( text = replyCount, - fontSize = 14.sp, + fontSize = Font14SP, color = textColor ) } @@ -462,33 +537,33 @@ fun BoostReaction( } } ) { - if (wantsToBoost) { - BoostTypeChoicePopup( - baseNote, - accountViewModel, - onDismiss = { - wantsToBoost = false - }, - onQuote = { - wantsToBoost = false - onQuotePress() - } - ) - } + BoostIcon(baseNote, iconSize, grayTint, accountViewModel) + } - BoostIcon(baseNote, iconSize, grayTint, accountViewModel.userProfile()) + if (wantsToBoost) { + BoostTypeChoicePopup( + baseNote, + accountViewModel, + onDismiss = { + wantsToBoost = false + }, + onQuote = { + wantsToBoost = false + onQuotePress() + } + ) } BoostText(baseNote, grayTint) } @Composable -fun BoostIcon(baseNote: Note, iconSize: Dp = 20.dp, grayTint: Color, loggedIn: User) { +fun BoostIcon(baseNote: Note, iconSize: Dp = Size20dp, grayTint: Color, accountViewModel: AccountViewModel) { val boostsState by baseNote.live().boosts.observeAsState() val iconTint by remember(boostsState) { derivedStateOf { - if (boostsState?.note?.isBoostedBy(loggedIn) == true) Color.Unspecified else grayTint + if (boostsState?.note?.isBoostedBy(accountViewModel.userProfile()) == true) Color.Unspecified else grayTint } } @@ -514,8 +589,8 @@ fun BoostText(baseNote: Note, grayTint: Color) { } Text( - boostCount, - fontSize = 14.sp, + text = boostCount, + fontSize = Font14SP, color = grayTint ) } @@ -528,7 +603,7 @@ fun LikeReaction( accountViewModel: AccountViewModel, iconSize: Dp = 20.dp, heartSize: Dp = 16.dp, - iconFontSize: TextUnit = 14.sp + iconFontSize: TextUnit = Font14SP ) { val context = LocalContext.current val scope = rememberCoroutineScope() @@ -540,13 +615,12 @@ fun LikeReaction( var wantsToChangeReactionSymbol by remember { mutableStateOf(false) } var wantsToReact by remember { mutableStateOf(false) } - Row( - verticalAlignment = CenterVertically, - horizontalArrangement = Arrangement.Center, + Box( + contentAlignment = Center, modifier = iconButtonModifier.combinedClickable( role = Role.Button, interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(bounded = false, radius = 24.dp), + indication = rememberRipple(bounded = false, radius = Size24dp), onClick = { likeClick( baseNote, @@ -563,37 +637,37 @@ fun LikeReaction( } ) ) { - LikeIcon(baseNote, iconFontSize, heartSize, grayTint, accountViewModel.userProfile()) - - if (wantsToChangeReactionSymbol) { - UpdateReactionTypeDialog({ wantsToChangeReactionSymbol = false }, accountViewModel = accountViewModel) - } - - if (wantsToReact) { - ReactionChoicePopup( - baseNote, - accountViewModel, - onDismiss = { - wantsToReact = false - }, - onChangeAmount = { - wantsToReact = false - wantsToChangeReactionSymbol = true - } - ) - } + LikeIcon(baseNote, iconFontSize, heartSize, grayTint, accountViewModel) } LikeText(baseNote, grayTint) + + if (wantsToChangeReactionSymbol) { + UpdateReactionTypeDialog({ wantsToChangeReactionSymbol = false }, accountViewModel = accountViewModel) + } + + if (wantsToReact) { + ReactionChoicePopup( + baseNote, + accountViewModel, + onDismiss = { + wantsToReact = false + }, + onChangeAmount = { + wantsToReact = false + wantsToChangeReactionSymbol = true + } + ) + } } @Composable fun LikeIcon( baseNote: Note, - iconFontSize: TextUnit = 14.sp, - iconSize: Dp = 20.dp, + iconFontSize: TextUnit = Font14SP, + iconSize: Dp = Size20dp, grayTint: Color, - loggedIn: User + accountViewModel: AccountViewModel ) { val reactionsState by baseNote.live().reactions.observeAsState() @@ -603,37 +677,59 @@ fun LikeIcon( LaunchedEffect(key1 = reactionsState) { launch(Dispatchers.Default) { - val newReactionType = reactionsState?.note?.isReactedBy(loggedIn) + val newReactionType = reactionsState?.note?.isReactedBy(accountViewModel.userProfile())?.take(2) if (reactionType != newReactionType) { - reactionType = newReactionType?.take(2) + reactionType = newReactionType } } } + if (reactionType != null) { + RenderReactionType(reactionType!!, iconSize, iconFontSize) + } else { + RenderLikeIcon(iconSize, grayTint) + } +} + +@Composable +private fun RenderLikeIcon( + iconSize: Dp = Size20dp, + grayTint: Color +) { val iconModifier = remember { Modifier.size(iconSize) } - if (reactionType != null) { - when (reactionType) { - "+" -> { - Icon( - painter = painterResource(R.drawable.ic_liked), - null, - modifier = iconModifier, - tint = Color.Unspecified - ) - } - "-" -> Text(text = "\uD83D\uDC4E", fontSize = iconFontSize) - else -> Text(text = reactionType!!, fontSize = iconFontSize) + Icon( + painter = painterResource(R.drawable.ic_like), + null, + modifier = iconModifier, + tint = grayTint + ) +} + +@Composable +private fun RenderReactionType( + reactionType: String, + iconSize: Dp = Size20dp, + iconFontSize: TextUnit +) { + val iconModifier = remember { + Modifier.size(iconSize) + } + + when (reactionType) { + "+" -> { + Icon( + painter = painterResource(R.drawable.ic_liked), + null, + modifier = iconModifier, + tint = Color.Unspecified + ) } - } else { - Icon( - painter = painterResource(R.drawable.ic_like), - null, - modifier = iconModifier, - tint = grayTint - ) + + "-" -> Text(text = "\uD83D\uDC4E", fontSize = iconFontSize) + else -> Text(text = reactionType, fontSize = iconFontSize) } } @@ -655,8 +751,8 @@ fun LikeText(baseNote: Note, grayTint: Color) { } Text( - reactionsCount, - fontSize = 14.sp, + text = reactionsCount, + fontSize = Font14SP, color = grayTint ) } @@ -727,7 +823,7 @@ fun ZapReaction( .combinedClickable( role = Role.Button, interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(bounded = false, radius = 24.dp), + indication = rememberRipple(bounded = false, radius = Size24dp), onClick = { zapClick( baseNote, @@ -930,8 +1026,8 @@ private fun ZapAmountText( } Text( - zapAmountTxt, - fontSize = 14.sp, + text = zapAmountTxt, + fontSize = Font14SP, color = grayTint ) } @@ -940,17 +1036,38 @@ private fun ZapAmountText( fun ViewCountReaction( idHex: String, grayTint: Color, - iconSize: Dp = 20.dp, - barChartSize: Dp = 19.dp, - numberSize: Dp = 24.dp + barChartSize: Dp = Size19dp, + numberSize: Dp = Size24dp ) { - val uri = LocalUriHandler.current - val context = LocalContext.current + DrawViewCountIcon(barChartSize, grayTint) + DrawViewCount(idHex, numberSize, grayTint) +} +@Composable +private fun DrawViewCountIcon( + barChartSize: Dp = Size19dp, + grayTint: Color +) { val iconButtonModifier = remember { Modifier.size(barChartSize) } + Icon( + imageVector = Icons.Outlined.BarChart, + null, + modifier = iconButtonModifier, + tint = grayTint + ) +} + +@Composable +private fun DrawViewCount( + idHex: String, + numberSize: Dp = Size24dp, + grayTint: Color +) { + val context = LocalContext.current + val iconModifier = remember { Modifier.height(numberSize) } @@ -959,34 +1076,18 @@ fun ViewCountReaction( ColorFilter.tint(grayTint) } - IconButton( - modifier = Modifier.size(iconSize), - onClick = { uri.openUri("https://counter.amethyst.social/$idHex/") } - ) { - Icon( - imageVector = Icons.Outlined.BarChart, - null, - modifier = iconButtonModifier, - tint = grayTint - ) - } - - val request = remember { - ImageRequest.Builder(context) - .data("https://counter.amethyst.social/$idHex.svg?label=+&color=00000000") - .diskCachePolicy(CachePolicy.DISABLED) - .memoryCachePolicy(CachePolicy.ENABLED) - .build() - } - - Row { - AsyncImage( - model = request, - contentDescription = stringResource(R.string.view_count), - modifier = iconModifier, - colorFilter = colorFilter - ) - } + AsyncImage( + model = remember(idHex) { + ImageRequest.Builder(context) + .data("https://counter.amethyst.social/$idHex.svg?label=+&color=00000000") + .diskCachePolicy(CachePolicy.DISABLED) + .memoryCachePolicy(CachePolicy.ENABLED) + .build() + }, + contentDescription = stringResource(R.string.view_count), + modifier = iconModifier, + colorFilter = colorFilter + ) } @OptIn(ExperimentalLayoutApi::class) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt index 7f0449dce..c037587aa 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt @@ -60,7 +60,6 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.lifecycle.ViewModel @@ -78,6 +77,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner import com.vitorpamplona.amethyst.ui.screen.loggedIn.getFragmentActivity import com.vitorpamplona.amethyst.ui.theme.ButtonBorder +import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.placeholderText import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope @@ -415,7 +415,7 @@ fun UpdateZapAmountDialog(onClose: () -> Unit, nip47uri: String? = null, account stringResource(id = R.string.wallet_connect_service_explainer), Modifier.weight(1f), color = MaterialTheme.colors.placeholderText, - fontSize = 14.sp + fontSize = Font14SP ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index 3d9cf01fc..0839547a4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -101,7 +101,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter) : ViewModel() { val updatedCards = (oldNotesState.feed.value + newCards) .sortedWith(compareBy({ it.createdAt() }, { it.id() })) .reversed() - .take(1000) + .take(localFilter.limit()) .toImmutableList() if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) { @@ -115,7 +115,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter) : ViewModel() { val cards = convertToCard(notes) .sortedWith(compareBy({ it.createdAt() }, { it.id() })) .reversed() - .take(1000) + .take(localFilter.limit()) .toImmutableList() updateFeed(cards) @@ -255,7 +255,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter) : ViewModel() { val updatedCards = (oldNotesState.feed.value + newCards) .sortedWith(compareBy({ it.createdAt() }, { it.id() })) .reversed() - .take(1000) + .take(localFilter.limit()) .toImmutableList() if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) { 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 cd81dff9e..6132fd002 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 @@ -288,7 +288,7 @@ fun NoteMaster( Row(verticalAlignment = Alignment.CenterVertically) { NoteUsernameDisplay(baseNote, Modifier.weight(1f)) - DisplayFollowingHashtagsInPost(noteEvent, accountViewModel, nav) + DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav) Text( timeAgo(note.createdAt(), context = context), 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 43c89d4c4..7519190dd 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 @@ -364,14 +364,18 @@ fun EditFieldRow( onSendNewMessage() }, isActive = channelScreenModel.message.text.isNotBlank() && !channelScreenModel.isUploadingImage, - modifier = Modifier.height(32.dp).padding(end = 10.dp) + modifier = Modifier + .height(32.dp) + .padding(end = 10.dp) ) }, leadingIcon = { UploadFromGallery( isUploading = channelScreenModel.isUploadingImage, tint = MaterialTheme.colors.placeholderText, - modifier = Modifier.height(32.dp).padding(start = 2.dp) + modifier = Modifier + .height(32.dp) + .padding(start = 2.dp) ) { val fileServer = if (isPrivate) { // TODO: Make private servers @@ -478,6 +482,31 @@ fun MyTextField( ) } +@Composable +fun ChannelHeader( + channelNote: Note, + showVideo: Boolean, + showBottomDiviser: Boolean, + modifier: Modifier = StdPadding, + accountViewModel: AccountViewModel, + nav: (String) -> Unit +) { + val channelHex by remember { + derivedStateOf { + channelNote.channelHex() + } + } + channelHex?.let { + ChannelHeader( + channelHex = it, + showVideo = showVideo, + showBottomDiviser = showBottomDiviser, + accountViewModel = accountViewModel, + nav = nav + ) + } +} + @Composable fun ChannelHeader( channelHex: String, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 90e3c1f70..1127326aa 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -397,7 +397,7 @@ fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, nav: (St }*/ LikeReaction(baseNote, grayTint = MaterialTheme.colors.onBackground, accountViewModel, iconSize = 40.dp, heartSize = Size35dp, 28.sp) ZapReaction(baseNote, grayTint = MaterialTheme.colors.onBackground, accountViewModel, iconSize = 40.dp, animationSize = Size35dp) - ViewCountReaction(baseNote.idHex, grayTint = MaterialTheme.colors.onBackground, iconSize = 40.dp, barChartSize = 39.dp) + ViewCountReaction(baseNote.idHex, grayTint = MaterialTheme.colors.onBackground, barChartSize = 39.dp) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/LoginScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/LoginScreen.kt index 0bd54700a..324110bd0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/LoginScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/LoginScreen.kt @@ -35,11 +35,11 @@ import androidx.compose.ui.text.input.* import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog +import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.placeholderText import java.util.* @@ -305,7 +305,7 @@ fun LoginPage( } }, style = TextStyle( - fontSize = 14.sp, + fontSize = Font14SP, textDecoration = TextDecoration.Underline, color = MaterialTheme.colors.primary, textAlign = TextAlign.Center diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt index c97fb88c6..75ea18ebf 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt @@ -16,6 +16,8 @@ val Nip05 = Color(0xFF01BAFF) val FollowsFollow = Color.Yellow val NIP05Verified = Color.Blue +val DarkerGreen = Color.Green.copy(alpha = 0.32f) + val WarningColor = Color(0xFFC62828) val RelayIconFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0.5f) }) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt index 734899c69..7be9ac3f1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt @@ -1,5 +1,7 @@ package com.vitorpamplona.amethyst.ui.theme +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -23,17 +25,46 @@ val ChatBubbleShapeMe = RoundedCornerShape(15.dp, 15.dp, 3.dp, 15.dp) val ChatBubbleShapeThem = RoundedCornerShape(3.dp, 15.dp, 15.dp, 15.dp) val StdButtonSizeModifier = Modifier.size(20.dp) + +val HalfVertSpacer = Modifier.height(2.dp) + val StdHorzSpacer = Modifier.width(5.dp) val StdVertSpacer = Modifier.height(5.dp) + val DoubleHorzSpacer = Modifier.width(10.dp) val DoubleVertSpacer = Modifier.height(10.dp) +val HalfDoubleVertSpacer = Modifier.height(7.dp) + val Size13dp = 13.dp val Size16dp = 16.dp +val Size18dp = 18.dp +val Size19dp = 19.dp +val Size20dp = 20.dp +val Size24dp = 24.dp val Size25dp = 25.dp +val Size30dp = 30.dp val Size35dp = 35.dp val Size55dp = 55.dp +val StdStartPadding = Modifier.padding(start = 10.dp) + +val HalfPadding = Modifier.padding(5.dp) val StdPadding = Modifier.padding(10.dp) val Size15Modifier = Modifier.size(15.dp) +val Size20Modifier = Modifier.size(20.dp) +val Size22Modifier = Modifier.size(22.dp) +val Size30Modifier = Modifier.size(30.dp) +val Size55Modifier = Modifier.size(55.dp) + +val TinyBorders = Modifier.padding(2.dp) +val NoSoTinyBorders = Modifier.padding(start = 5.dp, end = 5.dp, top = 2.dp, bottom = 2.dp) +val ReactionRowZapraiserSize = Modifier.defaultMinSize(minHeight = 4.dp).fillMaxWidth() +val ReactionRowExpandButton = Modifier.width(65.dp).padding(start = 31.dp) + +val WidthAuthorPictureModifier = Modifier.width(55.dp) + +val DiviserThickness = 0.25.dp + +val ReactionRowHeight = Modifier.padding(start = 22.dp).padding(start = 10.dp) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt index 4cb643ddb..be8567965 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt @@ -1,17 +1,32 @@ package com.vitorpamplona.amethyst.ui.theme import android.app.Activity +import androidx.compose.foundation.border import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Colors import androidx.compose.material.MaterialTheme import androidx.compose.material.darkColors import androidx.compose.material.lightColors import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp +import com.halilibo.richtext.ui.RichTextStyle +import com.vitorpamplona.amethyst.ui.components.DefaultHeadingStyle +import com.vitorpamplona.amethyst.ui.components.DefaultParagraphSpacing +import com.vitorpamplona.amethyst.ui.components.richTextDefaults private val DarkColorPalette = darkColors( primary = Purple200, @@ -30,9 +45,6 @@ private val LightColorPalette = lightColors( private val DarkNewItemBackground = DarkColorPalette.primary.copy(0.12f) private val LightNewItemBackground = LightColorPalette.primary.copy(0.12f) -private val DarkReplyItemBackground = DarkColorPalette.onSurface.copy(alpha = 0.05f) -private val LightReplyItemBackground = LightColorPalette.onSurface.copy(alpha = 0.05f) - private val DarkSelectedNote = DarkNewItemBackground.compositeOver(DarkColorPalette.background) private val LightSelectedNote = LightNewItemBackground.compositeOver(LightColorPalette.background) @@ -54,9 +66,15 @@ private val LightGrayText = LightColorPalette.onSurface.copy(alpha = 0.52f) private val DarkPlaceholderText = DarkColorPalette.onSurface.copy(alpha = 0.32f) private val LightPlaceholderText = LightColorPalette.onSurface.copy(alpha = 0.32f) +private val DarkSubtleButton = DarkColorPalette.onSurface.copy(alpha = 0.22f) +private val LightSubtleButton = LightColorPalette.onSurface.copy(alpha = 0.22f) + private val DarkSubtleBorder = DarkColorPalette.onSurface.copy(alpha = 0.12f) private val LightSubtleBorder = LightColorPalette.onSurface.copy(alpha = 0.12f) +private val DarkReplyItemBackground = DarkColorPalette.onSurface.copy(alpha = 0.05f) +private val LightReplyItemBackground = LightColorPalette.onSurface.copy(alpha = 0.05f) + private val DarkZapraiserBackground = BitcoinOrange.copy(0.52f).compositeOver(DarkColorPalette.background) private val LightZapraiserBackground = BitcoinOrange.copy(0.52f).compositeOver(LightColorPalette.background) @@ -66,6 +84,84 @@ private val LightImageVerifier = Nip05.copy(0.52f).compositeOver(LightColorPalet private val DarkOverPictureBackground = DarkColorPalette.background.copy(0.62f) private val LightOverPictureBackground = LightColorPalette.background.copy(0.62f) +val RepostPictureBorderDark = Modifier.border( + 2.dp, + DarkColorPalette.background, + CircleShape +) + +val RepostPictureBorderLight = Modifier.border( + 2.dp, + LightColorPalette.background, + CircleShape +) + +val MarkDownStyleOnDark = richTextDefaults.copy( + paragraphSpacing = DefaultParagraphSpacing, + headingStyle = DefaultHeadingStyle, + codeBlockStyle = richTextDefaults.codeBlockStyle?.copy( + textStyle = TextStyle( + fontFamily = FontFamily.Monospace, + fontSize = Font14SP + ), + modifier = Modifier + .padding(0.dp) + .fillMaxWidth() + .clip(shape = QuoteBorder) + .border( + 1.dp, + DarkSubtleBorder, + QuoteBorder + ) + .drawBehind { + drawRect(DarkColorPalette.onSurface.copy(alpha = 0.05f)) + } + ), + stringStyle = richTextDefaults.stringStyle?.copy( + linkStyle = SpanStyle( + color = DarkColorPalette.primary + ), + codeStyle = SpanStyle( + fontFamily = FontFamily.Monospace, + fontSize = Font14SP, + background = DarkColorPalette.onSurface.copy(alpha = 0.22f) + ) + ) +) + +val MarkDownStyleOnLight = richTextDefaults.copy( + paragraphSpacing = DefaultParagraphSpacing, + headingStyle = DefaultHeadingStyle, + codeBlockStyle = richTextDefaults.codeBlockStyle?.copy( + textStyle = TextStyle( + fontFamily = FontFamily.Monospace, + fontSize = Font14SP + ), + modifier = Modifier + .padding(0.dp) + .fillMaxWidth() + .clip(shape = QuoteBorder) + .border( + 1.dp, + LightSubtleBorder, + QuoteBorder + ) + .drawBehind { + drawRect(LightColorPalette.onSurface.copy(alpha = 0.05f)) + } + ), + stringStyle = richTextDefaults.stringStyle?.copy( + linkStyle = SpanStyle( + color = LightColorPalette.primary + ), + codeStyle = SpanStyle( + fontFamily = FontFamily.Monospace, + fontSize = Font14SP, + background = LightColorPalette.onSurface.copy(alpha = 0.22f) + ) + ) +) + val Colors.newItemBackgroundColor: Color get() = if (isLight) LightNewItemBackground else DarkNewItemBackground @@ -98,12 +194,21 @@ val Colors.grayText: Color val Colors.subtleBorder: Color get() = if (isLight) LightSubtleBorder else DarkSubtleBorder +val Colors.subtleButton: Color + get() = if (isLight) LightSubtleButton else DarkSubtleButton + val Colors.hashVerified: Color get() = if (isLight) LightImageVerifier else DarkImageVerifier val Colors.overPictureBackground: Color get() = if (isLight) LightOverPictureBackground else DarkOverPictureBackground +val Colors.markdownStyle: RichTextStyle + get() = if (isLight) MarkDownStyleOnLight else MarkDownStyleOnDark + +val Colors.repostProfileBorder: Modifier + get() = if (isLight) RepostPictureBorderLight else RepostPictureBorderDark + @Composable fun AmethystTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colors = if (darkTheme) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Type.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Type.kt index 18ff3dd59..75b1a89a2 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Type.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Type.kt @@ -4,6 +4,7 @@ import androidx.compose.material.Typography import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp // Set of Material typography styles to start with @@ -17,7 +18,7 @@ val Typography = Typography( button = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.W500, - fontSize = 14.sp + fontSize = Font14SP ), caption = TextStyle( fontFamily = FontFamily.Default, @@ -26,3 +27,7 @@ val Typography = Typography( ) */ ) + +val Font14SP = 14.sp + +val MarkdownTextStyle = TextStyle(lineHeight = 1.30.em)