Minor performance improvements throughout the code

This commit is contained in:
Vitor Pamplona
2023-06-22 11:39:56 -04:00
parent 69481a046b
commit 52ef356c06
20 changed files with 712 additions and 408 deletions
@@ -490,9 +490,9 @@ class NoteLiveData(val note: Note) : LiveData<NoteState>(NoteState(note)) {
fun invalidateData() { fun invalidateData() {
bundler.invalidate() { bundler.invalidate() {
// if (hasObservers()) { if (hasActiveObservers()) {
postValue(NoteState(note)) postValue(NoteState(note))
// } }
} }
} }
@@ -144,9 +144,11 @@ class User(val pubkeyHex: String) {
} }
fun updateAcceptedBadges(note: AddressableNote) { fun updateAcceptedBadges(note: AddressableNote) {
if (acceptedBadges?.idHex != note.idHex) {
acceptedBadges = note acceptedBadges = note
liveSet?.badges?.invalidateData() liveSet?.badges?.invalidateData()
} }
}
fun addZap(zapRequest: Note, zap: Note?) { fun addZap(zapRequest: Note, zap: Note?) {
if (zapRequest !in zaps.keys) { if (zapRequest !in zaps.keys) {
@@ -17,8 +17,8 @@ class RepostEvent(
sig: HexKey sig: HexKey
) : Event(id, pubKey, createdAt, kind, tags, content, sig) { ) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
fun boostedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) } fun boostedPost() = taggedEvents()
fun originalAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) } fun originalAuthor() = taggedUsers()
fun containedPost() = try { fun containedPost() = try {
fromJson(content, Client.lenient) fromJson(content, Client.lenient)
@@ -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.screen.loggedIn.UserLine
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder 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.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@@ -621,7 +622,7 @@ private fun ForwardZapTo(
Text( Text(
text = stringResource(R.string.zap_forward_lnAddress), text = stringResource(R.string.zap_forward_lnAddress),
color = MaterialTheme.colors.placeholderText, color = MaterialTheme.colors.placeholderText,
fontSize = 14.sp fontSize = Font14SP
) )
}, },
colors = TextFieldDefaults colors = TextFieldDefaults
@@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.model.RelaySetupInfo
import com.vitorpamplona.amethyst.service.relays.FeedType import com.vitorpamplona.amethyst.service.relays.FeedType
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder 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.Size35dp
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -167,7 +168,7 @@ fun ServerConfigHeader() {
Text( Text(
text = stringResource(R.string.bytes), text = stringResource(R.string.bytes),
maxLines = 1, maxLines = 1,
fontSize = 14.sp, fontSize = Font14SP,
modifier = Modifier.weight(1.2f), modifier = Modifier.weight(1.2f),
color = MaterialTheme.colors.placeholderText color = MaterialTheme.colors.placeholderText
) )
@@ -177,7 +178,7 @@ fun ServerConfigHeader() {
Text( Text(
text = stringResource(id = R.string.bytes), text = stringResource(id = R.string.bytes),
maxLines = 1, maxLines = 1,
fontSize = 14.sp, fontSize = Font14SP,
modifier = Modifier.weight(1.2f), modifier = Modifier.weight(1.2f),
color = MaterialTheme.colors.placeholderText color = MaterialTheme.colors.placeholderText
) )
@@ -187,7 +188,7 @@ fun ServerConfigHeader() {
Text( Text(
text = stringResource(R.string.errors), text = stringResource(R.string.errors),
maxLines = 1, maxLines = 1,
fontSize = 14.sp, fontSize = Font14SP,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
color = MaterialTheme.colors.placeholderText color = MaterialTheme.colors.placeholderText
) )
@@ -197,7 +198,7 @@ fun ServerConfigHeader() {
Text( Text(
text = stringResource(R.string.spam), text = stringResource(R.string.spam),
maxLines = 1, maxLines = 1,
fontSize = 14.sp, fontSize = Font14SP,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
color = MaterialTheme.colors.placeholderText color = MaterialTheme.colors.placeholderText
) )
@@ -3,7 +3,6 @@ package com.vitorpamplona.amethyst.ui.components
import android.util.Log import android.util.Log
import android.util.LruCache import android.util.LruCache
import android.util.Patterns import android.util.Patterns
import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.ClickableText 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.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.* import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.halilibo.richtext.markdown.Markdown import com.halilibo.richtext.markdown.Markdown
import com.halilibo.richtext.markdown.MarkdownParseOptions 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.actions.toImmutableListOfLists
import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel 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.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.markdownStyle
import com.vitorpamplona.amethyst.ui.theme.subtleBorder import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import com.vitorpamplona.amethyst.ui.uriToRoute import com.vitorpamplona.amethyst.ui.uriToRoute
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@@ -439,42 +437,6 @@ fun RenderCustomEmoji(word: String, state: RichTextViewerState) {
@Composable @Composable
private fun RenderContentAsMarkdown(content: String, backgroundColor: MutableState<Color>, tags: ImmutableListOfLists<String>?, nav: (String) -> Unit) { private fun RenderContentAsMarkdown(content: String, backgroundColor: MutableState<Color>, tags: ImmutableListOfLists<String>?, 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 uri = LocalUriHandler.current
val onClick = { link: String -> val onClick = { link: String ->
val route = uriToRoute(link) val route = uriToRoute(link)
@@ -486,9 +448,9 @@ private fun RenderContentAsMarkdown(content: String, backgroundColor: MutableSta
Unit Unit
} }
ProvideTextStyle(TextStyle(lineHeight = 1.30.em)) { ProvideTextStyle(MarkdownTextStyle) {
MaterialRichText( MaterialRichText(
style = myMarkDownStyle style = MaterialTheme.colors.markdownStyle
) { ) {
RefreshableContent(content, tags) { RefreshableContent(content, tags) {
Markdown( Markdown(
@@ -29,8 +29,8 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import com.vitorpamplona.amethyst.ui.theme.Font14SP
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@Composable @Composable
@@ -120,7 +120,7 @@ fun SpinnerSelectionDialog(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
) { ) {
Text(text = it, color = Color.Gray, fontSize = 14.sp) Text(text = it, color = Color.Gray, fontSize = Font14SP)
} }
} }
} }
@@ -96,7 +96,6 @@ import com.vitorpamplona.amethyst.service.model.BaseTextNoteEvent
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent 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.FileHeaderEvent
import com.vitorpamplona.amethyst.service.model.FileStorageHeaderEvent import com.vitorpamplona.amethyst.service.model.FileStorageHeaderEvent
import com.vitorpamplona.amethyst.service.model.GenericRepostEvent 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.ChannelHeader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange 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.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.Following 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.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.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.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.replyBackground import com.vitorpamplona.amethyst.ui.theme.replyBackground
import com.vitorpamplona.amethyst.ui.theme.repostProfileBorder
import com.vitorpamplona.amethyst.ui.theme.subtleBorder import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
@@ -181,9 +194,13 @@ fun NoteCompose(
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val noteState by baseNote.live().metadata.observeAsState() 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 -> LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
BlankNote( BlankNote(
remember { remember {
@@ -337,12 +354,12 @@ fun WatchForReports(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
onChange: (Boolean, Boolean, Set<Note>) -> Unit onChange: (Boolean, Boolean, Set<Note>) -> Unit
) { ) {
val accountState by accountViewModel.accountLiveData.observeAsState() val userFollowsState by accountViewModel.userFollows.observeAsState()
val noteReportsState by note.live().reports.observeAsState() val noteReportsState by note.live().reports.observeAsState()
LaunchedEffect(key1 = noteReportsState, key2 = accountState) { LaunchedEffect(key1 = noteReportsState, key2 = userFollowsState) {
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
accountState?.account?.let { loggedIn -> accountViewModel.account.let { loggedIn ->
val newCanPreview = note.author?.pubkeyHex == loggedIn.userProfile().pubkeyHex || val newCanPreview = note.author?.pubkeyHex == loggedIn.userProfile().pubkeyHex ||
(note.author?.let { loggedIn.userProfile().isFollowingCached(it) } ?: true) || (note.author?.let { loggedIn.userProfile().isFollowingCached(it) } ?: true) ||
noteReportsState?.note?.hasAnyReports() != true noteReportsState?.note?.hasAnyReports() != true
@@ -376,10 +393,22 @@ fun NormalNote(
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val noteEvent = remember { baseNote.event } val noteEvent = remember { baseNote.event }
val channelHex = remember { baseNote.channelHex() }
if ((noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) && channelHex != null) { val isChannelHeader by remember {
ChannelHeader(channelHex = channelHex, showVideo = !makeItShort, showBottomDiviser = true, accountViewModel = accountViewModel, nav = nav) 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) { } else if (noteEvent is BadgeDefinitionEvent) {
BadgeDisplay(baseNote = baseNote) BadgeDisplay(baseNote = baseNote)
} else if (noteEvent is FileHeaderEvent) { } else if (noteEvent is FileHeaderEvent) {
@@ -387,6 +416,7 @@ fun NormalNote(
} else if (noteEvent is FileStorageHeaderEvent) { } else if (noteEvent is FileStorageHeaderEvent) {
FileStorageHeaderDisplay(baseNote) FileStorageHeaderDisplay(baseNote)
} else { } else {
LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
CheckNewAndRenderNote( CheckNewAndRenderNote(
baseNote, baseNote,
routeForLastRead, routeForLastRead,
@@ -399,9 +429,11 @@ fun NormalNote(
canPreview, canPreview,
parentBackgroundColor, parentBackgroundColor,
accountViewModel, accountViewModel,
showPopup,
nav nav
) )
} }
}
} }
@Composable @Composable
@@ -417,6 +449,7 @@ private fun CheckNewAndRenderNote(
canPreview: Boolean = true, canPreview: Boolean = true,
parentBackgroundColor: MutableState<Color>? = null, parentBackgroundColor: MutableState<Color>? = null,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
showPopup: () -> Unit,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val newItemColor = MaterialTheme.colors.newItemBackgroundColor val newItemColor = MaterialTheme.colors.newItemBackgroundColor
@@ -464,7 +497,6 @@ private fun CheckNewAndRenderNote(
} }
} }
LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
RenderNoteWithReactions( RenderNoteWithReactions(
modifier = updatedModifier, modifier = updatedModifier,
backgroundColor = backgroundColor, backgroundColor = backgroundColor,
@@ -479,7 +511,6 @@ private fun CheckNewAndRenderNote(
showPopup = showPopup, showPopup = showPopup,
nav = nav nav = nav
) )
}
} }
@Composable @Composable
@@ -539,8 +570,8 @@ private fun RenderNoteWithReactions(
} }
) { ) {
if (notBoostedNorQuote) { if (notBoostedNorQuote) {
DrawAuthorImages(baseNote, accountViewModel, nav) AuthorAndRelayInformation(baseNote, accountViewModel, nav)
Spacer(modifier = Modifier.width(10.dp)) Spacer(modifier = DoubleHorzSpacer)
} }
NoteBody( NoteBody(
@@ -556,7 +587,9 @@ private fun RenderNoteWithReactions(
) )
} }
if (!makeItShort && baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent) { val isRepost = remember { baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent }
if (!isRepost) {
if (!makeItShort) {
ReactionsRow( ReactionsRow(
baseNote = baseNote, baseNote = baseNote,
showReactionDetail = notBoostedNorQuote, showReactionDetail = notBoostedNorQuote,
@@ -564,14 +597,15 @@ private fun RenderNoteWithReactions(
nav = nav nav = nav
) )
} else { } else {
if (!isBoostedNote && baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent) { if (!isBoostedNote) {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = DoubleVertSpacer)
}
} }
} }
if (!isQuotedNote && !isBoostedNote) { if (!isQuotedNote && !isBoostedNote) {
Divider( 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) { if (!makeItShort) {
ReplyRow( ReplyRow(
@@ -1766,38 +1800,40 @@ private fun FirstUserInfoRow(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val eventNote = remember { baseNote.event } ?: return val isRepost = remember { baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent }
val time = remember { baseNote.createdAt() } ?: return
val padding = remember {
Modifier.padding(horizontal = 5.dp)
}
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = CenterVertically) {
if (showAuthorPicture) { if (showAuthorPicture) {
NoteAuthorPicture(baseNote, nav, accountViewModel, remember { 25.dp }) NoteAuthorPicture(baseNote, nav, accountViewModel, Size25dp)
Spacer(padding) Spacer(HalfPadding)
NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) }) NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) })
} else { } else {
NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) }) NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) })
} }
if (eventNote is RepostEvent || eventNote is GenericRepostEvent) { if (isRepost) {
Text( BoostedMark()
" ${stringResource(id = R.string.boosted)}",
fontWeight = FontWeight.Bold,
color = MaterialTheme.colors.placeholderText,
maxLines = 1
)
} else { } else {
DisplayFollowingHashtagsInPost(eventNote, accountViewModel, nav) DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav)
} }
TimeAgo(time) TimeAgo(baseNote)
MoreOptionsButton(baseNote, accountViewModel) 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 @Composable
private fun MoreOptionsButton( private fun MoreOptionsButton(
baseNote: Note, baseNote: Note,
@@ -1825,19 +1861,16 @@ private fun MoreOptionsButton(
} }
} }
@Composable
fun TimeAgo(note: Note) {
val time = remember { note.createdAt() } ?: return
TimeAgo(time)
}
@Composable @Composable
fun TimeAgo(time: Long) { fun TimeAgo(time: Long) {
val context = LocalContext.current val context = LocalContext.current
var timeStr by remember { mutableStateOf("") } val timeStr by remember { mutableStateOf(timeAgo(time, context = context)) }
LaunchedEffect(key1 = time) {
launch(Dispatchers.IO) {
val newTimeStr = timeAgo(time, context = context)
if (newTimeStr != timeStr) {
timeStr = newTimeStr
}
}
}
Text( Text(
text = timeStr, text = timeStr,
@@ -1847,36 +1880,61 @@ fun TimeAgo(time: Long) {
} }
@Composable @Composable
private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { private fun AuthorAndRelayInformation(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val baseChannelHex = remember { baseNote.channelHex() } Column(WidthAuthorPictureModifier) {
val modifier = remember { Modifier.width(55.dp) }
Column(modifier) {
// Draws the boosted picture outside the boosted card. // Draws the boosted picture outside the boosted card.
Box(modifier = modifier, contentAlignment = Alignment.BottomEnd) { Box(modifier = Size55Modifier, contentAlignment = Alignment.BottomEnd) {
NoteAuthorPicture(baseNote, nav, accountViewModel, 55.dp) RenderAuthorImages(baseNote, nav, accountViewModel)
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
RepostNoteAuthorPicture(baseNote, accountViewModel, nav)
} }
if (baseNote.event is ChannelMessageEvent && baseChannelHex != null) { BadgeBox(baseNote, accountViewModel, nav)
LoadChannel(baseChannelHex) { channel ->
ChannelNotePicture(channel)
}
}
} }
}
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) { @Composable
val baseReply = remember { 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() baseNote.replyTo?.lastOrNull()
} }
}
baseReply?.let { baseReply?.let {
RelayBadges(it, accountViewModel, nav) RelayBadges(it, accountViewModel, nav)
} }
} else { } else {
RelayBadges(baseNote, accountViewModel, nav) 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)
}
}
} }
} }
@@ -1945,24 +2003,20 @@ private fun RepostNoteAuthorPicture(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val baseRepost = remember { baseNote.replyTo?.lastOrNull() } val baseRepost by remember {
derivedStateOf {
val modifier = remember { baseNote.replyTo?.lastOrNull()
Modifier.size(30.dp) }
} }
baseRepost?.let { baseRepost?.let {
Box(modifier) { Box(Size30Modifier) {
NoteAuthorPicture( NoteAuthorPicture(
baseNote = it, baseNote = it,
nav = nav, nav = nav,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
size = remember { 30.dp }, size = Size30dp,
pictureModifier = Modifier.border( pictureModifier = MaterialTheme.colors.repostProfileBorder
2.dp,
MaterialTheme.colors.background,
CircleShape
)
) )
} }
} }
@@ -2073,10 +2127,12 @@ private fun LoadAndDisplayUser(
@Composable @Composable
fun DisplayFollowingHashtagsInPost( fun DisplayFollowingHashtagsInPost(
noteEvent: EventInterface, baseNote: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val noteEvent = remember { baseNote.event } ?: return
val accountState by accountViewModel.accountLiveData.observeAsState() val accountState by accountViewModel.accountLiveData.observeAsState()
var firstTag by remember { mutableStateOf<String?>(null) } var firstTag by remember { mutableStateOf<String?>(null) }
@@ -2148,12 +2204,14 @@ fun DisplayUncitedHashtags(
fun DisplayPoW( fun DisplayPoW(
pow: Int pow: Int
) { ) {
val powStr = remember(pow) {
"PoW-$pow"
}
Text( Text(
"PoW-$pow", powStr,
color = MaterialTheme.colors.primary.copy( color = MaterialTheme.colors.lessImportantLink,
alpha = 0.52f fontSize = Font14SP,
),
fontSize = 14.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
maxLines = 1 maxLines = 1
) )
@@ -2673,19 +2731,23 @@ private fun CreateImageHeader(
painter = painterResource(R.drawable.profile_banner), painter = painterResource(R.drawable.profile_banner),
contentDescription = stringResource(R.string.profile_banner), contentDescription = stringResource(R.string.profile_banner),
contentScale = ContentScale.FillWidth, contentScale = ContentScale.FillWidth,
modifier = Modifier modifier = remember {
Modifier
.fillMaxWidth() .fillMaxWidth()
.height(150.dp) .height(150.dp)
}
) )
Box( Box(
remember {
Modifier Modifier
.width(75.dp) .width(75.dp)
.height(75.dp) .height(75.dp)
.padding(10.dp) .padding(10.dp)
.align(Alignment.BottomStart) .align(Alignment.BottomStart)
}
) { ) {
NoteAuthorPicture(baseNote = note, accountViewModel = accountViewModel, size = 55.dp) NoteAuthorPicture(baseNote = note, accountViewModel = accountViewModel, size = Size55dp)
} }
} }
} }
@@ -20,7 +20,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Popup import androidx.compose.ui.window.Popup
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R 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.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder 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.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@@ -428,9 +428,12 @@ fun ZapVote(
// only show tallies after a user has zapped note // only show tallies after a user has zapped note
if (!pollViewModel.canZap()) { if (!pollViewModel.canZap()) {
val amountStr = remember(poolOption.zappedValue) {
showAmount(poolOption.zappedValue)
}
Text( Text(
showAmount(poolOption.zappedValue), text = amountStr,
fontSize = 14.sp, fontSize = Font14SP,
color = MaterialTheme.colors.placeholderText, color = MaterialTheme.colors.placeholderText,
modifier = modifier modifier = modifier
) )
@@ -45,12 +45,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.Center
import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role 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.IntOffset
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Popup import androidx.compose.ui.window.Popup
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.CachePolicy import coil.request.CachePolicy
import coil.request.ImageRequest import coil.request.ImageRequest
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.actions.NewPostView import com.vitorpamplona.amethyst.ui.actions.NewPostView
import com.vitorpamplona.amethyst.ui.screen.CombinedZap import com.vitorpamplona.amethyst.ui.screen.CombinedZap
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder 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.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.subtleButton
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -84,67 +97,118 @@ import java.math.RoundingMode
import kotlin.math.roundToInt import kotlin.math.roundToInt
@Composable @Composable
fun ReactionsRow(baseNote: Note, showReactionDetail: Boolean, accountViewModel: AccountViewModel, nav: (String) -> Unit) { fun ReactionsRow(
val grayTint = MaterialTheme.colors.placeholderText baseNote: Note,
showReactionDetail: Boolean,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
val wantsToSeeReactions = remember { val wantsToSeeReactions = remember {
mutableStateOf<Boolean>(false) mutableStateOf(false)
} }
val zapraiserAmount = remember { Spacer(modifier = HalfDoubleVertSpacer)
baseNote.event?.zapraiserAmount()
}
Spacer(modifier = Modifier.height(7.dp)) InnerReactionRow(baseNote, showReactionDetail, wantsToSeeReactions, accountViewModel, nav)
Row(verticalAlignment = CenterVertically, modifier = Modifier.padding(start = 10.dp)) { LoadAndDisplayZapraiser(baseNote, showReactionDetail, wantsToSeeReactions, accountViewModel)
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)
}
}
if (showReactionDetail && wantsToSeeReactions.value) { if (showReactionDetail && wantsToSeeReactions.value) {
ReactionDetailGallery(baseNote, nav, accountViewModel) ReactionDetailGallery(baseNote, nav, accountViewModel)
} }
Spacer(modifier = Modifier.height(7.dp)) Spacer(modifier = HalfDoubleVertSpacer)
}
@Composable
private fun InnerReactionRow(
baseNote: Note,
showReactionDetail: Boolean,
wantsToSeeReactions: MutableState<Boolean>,
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<Boolean>,
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 @Composable
@@ -165,11 +229,17 @@ fun RenderZapRaiser(baseNote: Note, zapraiserAmount: Long, details: Boolean, acc
} }
if (Math.abs(zapraiserProgress - percentage) > 0.001) { if (Math.abs(zapraiserProgress - percentage) > 0.001) {
zapraiserProgress = percentage val newZapraiserProgress = percentage
if (percentage > 0.99) { val newZapraiserLeft = if (percentage > 0.99) {
zapraiserLeft = "0" "0"
} else { } 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) { val color = if (zapraiserProgress > 0.99) {
Color.Green.copy(alpha = 0.32f) DarkerGreen
} else { } else {
MaterialTheme.colors.mediumImportanceLink MaterialTheme.colors.mediumImportanceLink
} }
Box( Box(ReactionRowZapraiserSize) {
Modifier.padding(end = 10.dp).fillMaxWidth()
) {
LinearProgressIndicator( LinearProgressIndicator(
modifier = Modifier.matchParentSize(), modifier = ReactionRowZapraiserSize,
color = color, color = color,
progress = zapraiserProgress progress = zapraiserProgress
) )
if (details) {
Row( Row(
verticalAlignment = CenterVertically, verticalAlignment = CenterVertically,
modifier = Modifier.padding(2.dp) modifier = TinyBorders
) { ) {
if (details) { val totalPercentage by remember(zapraiserProgress) {
val totalPercentage = remember(zapraiserProgress) { derivedStateOf {
"${(zapraiserProgress * 100).roundToInt()}%" "${(zapraiserProgress * 100).roundToInt()}%"
} }
}
Text( Text(
text = stringResource(id = R.string.sats_to_complete, totalPercentage, zapraiserLeft), 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, color = MaterialTheme.colors.placeholderText,
fontSize = 14.sp fontSize = Font14SP
) )
} }
} }
@@ -226,29 +296,34 @@ private fun ExpandButton(baseNote: Note, wantsToSeeReactions: MutableState<Boole
} }
if (hasReactions) { if (hasReactions) {
RenderShowIndividualReactionsButton(wantsToSeeReactions)
}
}
@Composable
private fun RenderShowIndividualReactionsButton(wantsToSeeReactions: MutableState<Boolean>) {
IconButton( IconButton(
onClick = { onClick = {
wantsToSeeReactions.value = !wantsToSeeReactions.value wantsToSeeReactions.value = !wantsToSeeReactions.value
}, },
modifier = Modifier.size(20.dp) modifier = Size20Modifier
) { ) {
if (wantsToSeeReactions.value) { if (wantsToSeeReactions.value) {
Icon( Icon(
imageVector = Icons.Default.ExpandLess, imageVector = Icons.Default.ExpandLess,
null, null,
modifier = Modifier.size(22.dp), modifier = Size22Modifier,
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.22f) tint = MaterialTheme.colors.subtleButton
) )
} else { } else {
Icon( Icon(
imageVector = Icons.Default.ExpandMore, imageVector = Icons.Default.ExpandMore,
null, null,
modifier = Modifier.size(22.dp), modifier = Size22Modifier,
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.22f) tint = MaterialTheme.colors.subtleButton
) )
} }
} }
}
} }
@Composable @Composable
@@ -320,10 +395,10 @@ private fun ReactionDetailGallery(
@Composable @Composable
private fun BoostWithDialog( private fun BoostWithDialog(
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
baseNote: Note, baseNote: Note,
grayTint: Color grayTint: Color,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) { ) {
var wantsToQuote by remember { var wantsToQuote by remember {
mutableStateOf<Note?>(null) mutableStateOf<Note?>(null)
@@ -340,10 +415,10 @@ private fun BoostWithDialog(
@Composable @Composable
private fun ReplyReactionWithDialog( private fun ReplyReactionWithDialog(
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
baseNote: Note, baseNote: Note,
grayTint: Color grayTint: Color,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) { ) {
var wantsToReplyTo by remember { var wantsToReplyTo by remember {
mutableStateOf<Note?>(null) mutableStateOf<Note?>(null)
@@ -364,7 +439,7 @@ fun ReplyReaction(
grayTint: Color, grayTint: Color,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
showCounter: Boolean = true, showCounter: Boolean = true,
iconSize: Dp = 18.dp, iconSize: Dp = Size18dp,
onPress: () -> Unit onPress: () -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
@@ -418,7 +493,7 @@ fun ReplyCounter(baseNote: Note, textColor: Color) {
Text( Text(
text = replyCount, text = replyCount,
fontSize = 14.sp, fontSize = Font14SP,
color = textColor color = textColor
) )
} }
@@ -462,6 +537,9 @@ fun BoostReaction(
} }
} }
) { ) {
BoostIcon(baseNote, iconSize, grayTint, accountViewModel)
}
if (wantsToBoost) { if (wantsToBoost) {
BoostTypeChoicePopup( BoostTypeChoicePopup(
baseNote, baseNote,
@@ -476,19 +554,16 @@ fun BoostReaction(
) )
} }
BoostIcon(baseNote, iconSize, grayTint, accountViewModel.userProfile())
}
BoostText(baseNote, grayTint) BoostText(baseNote, grayTint)
} }
@Composable @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 boostsState by baseNote.live().boosts.observeAsState()
val iconTint by remember(boostsState) { val iconTint by remember(boostsState) {
derivedStateOf { 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( Text(
boostCount, text = boostCount,
fontSize = 14.sp, fontSize = Font14SP,
color = grayTint color = grayTint
) )
} }
@@ -528,7 +603,7 @@ fun LikeReaction(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
iconSize: Dp = 20.dp, iconSize: Dp = 20.dp,
heartSize: Dp = 16.dp, heartSize: Dp = 16.dp,
iconFontSize: TextUnit = 14.sp iconFontSize: TextUnit = Font14SP
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -540,13 +615,12 @@ fun LikeReaction(
var wantsToChangeReactionSymbol by remember { mutableStateOf(false) } var wantsToChangeReactionSymbol by remember { mutableStateOf(false) }
var wantsToReact by remember { mutableStateOf(false) } var wantsToReact by remember { mutableStateOf(false) }
Row( Box(
verticalAlignment = CenterVertically, contentAlignment = Center,
horizontalArrangement = Arrangement.Center,
modifier = iconButtonModifier.combinedClickable( modifier = iconButtonModifier.combinedClickable(
role = Role.Button, role = Role.Button,
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false, radius = 24.dp), indication = rememberRipple(bounded = false, radius = Size24dp),
onClick = { onClick = {
likeClick( likeClick(
baseNote, baseNote,
@@ -563,7 +637,10 @@ fun LikeReaction(
} }
) )
) { ) {
LikeIcon(baseNote, iconFontSize, heartSize, grayTint, accountViewModel.userProfile()) LikeIcon(baseNote, iconFontSize, heartSize, grayTint, accountViewModel)
}
LikeText(baseNote, grayTint)
if (wantsToChangeReactionSymbol) { if (wantsToChangeReactionSymbol) {
UpdateReactionTypeDialog({ wantsToChangeReactionSymbol = false }, accountViewModel = accountViewModel) UpdateReactionTypeDialog({ wantsToChangeReactionSymbol = false }, accountViewModel = accountViewModel)
@@ -582,18 +659,15 @@ fun LikeReaction(
} }
) )
} }
}
LikeText(baseNote, grayTint)
} }
@Composable @Composable
fun LikeIcon( fun LikeIcon(
baseNote: Note, baseNote: Note,
iconFontSize: TextUnit = 14.sp, iconFontSize: TextUnit = Font14SP,
iconSize: Dp = 20.dp, iconSize: Dp = Size20dp,
grayTint: Color, grayTint: Color,
loggedIn: User accountViewModel: AccountViewModel
) { ) {
val reactionsState by baseNote.live().reactions.observeAsState() val reactionsState by baseNote.live().reactions.observeAsState()
@@ -603,18 +677,47 @@ fun LikeIcon(
LaunchedEffect(key1 = reactionsState) { LaunchedEffect(key1 = reactionsState) {
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
val newReactionType = reactionsState?.note?.isReactedBy(loggedIn) val newReactionType = reactionsState?.note?.isReactedBy(accountViewModel.userProfile())?.take(2)
if (reactionType != newReactionType) { 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)
}
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 { val iconModifier = remember {
Modifier.size(iconSize) Modifier.size(iconSize)
} }
if (reactionType != null) {
when (reactionType) { when (reactionType) {
"+" -> { "+" -> {
Icon( Icon(
@@ -624,16 +727,9 @@ fun LikeIcon(
tint = Color.Unspecified tint = Color.Unspecified
) )
} }
"-" -> Text(text = "\uD83D\uDC4E", fontSize = iconFontSize) "-" -> Text(text = "\uD83D\uDC4E", fontSize = iconFontSize)
else -> Text(text = reactionType!!, fontSize = iconFontSize) else -> Text(text = reactionType, fontSize = iconFontSize)
}
} else {
Icon(
painter = painterResource(R.drawable.ic_like),
null,
modifier = iconModifier,
tint = grayTint
)
} }
} }
@@ -655,8 +751,8 @@ fun LikeText(baseNote: Note, grayTint: Color) {
} }
Text( Text(
reactionsCount, text = reactionsCount,
fontSize = 14.sp, fontSize = Font14SP,
color = grayTint color = grayTint
) )
} }
@@ -727,7 +823,7 @@ fun ZapReaction(
.combinedClickable( .combinedClickable(
role = Role.Button, role = Role.Button,
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false, radius = 24.dp), indication = rememberRipple(bounded = false, radius = Size24dp),
onClick = { onClick = {
zapClick( zapClick(
baseNote, baseNote,
@@ -930,8 +1026,8 @@ private fun ZapAmountText(
} }
Text( Text(
zapAmountTxt, text = zapAmountTxt,
fontSize = 14.sp, fontSize = Font14SP,
color = grayTint color = grayTint
) )
} }
@@ -940,17 +1036,38 @@ private fun ZapAmountText(
fun ViewCountReaction( fun ViewCountReaction(
idHex: String, idHex: String,
grayTint: Color, grayTint: Color,
iconSize: Dp = 20.dp, barChartSize: Dp = Size19dp,
barChartSize: Dp = 19.dp, numberSize: Dp = Size24dp
numberSize: Dp = 24.dp
) { ) {
val uri = LocalUriHandler.current DrawViewCountIcon(barChartSize, grayTint)
val context = LocalContext.current DrawViewCount(idHex, numberSize, grayTint)
}
@Composable
private fun DrawViewCountIcon(
barChartSize: Dp = Size19dp,
grayTint: Color
) {
val iconButtonModifier = remember { val iconButtonModifier = remember {
Modifier.size(barChartSize) 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 { val iconModifier = remember {
Modifier.height(numberSize) Modifier.height(numberSize)
} }
@@ -959,34 +1076,18 @@ fun ViewCountReaction(
ColorFilter.tint(grayTint) ColorFilter.tint(grayTint)
} }
IconButton( AsyncImage(
modifier = Modifier.size(iconSize), model = remember(idHex) {
onClick = { uri.openUri("https://counter.amethyst.social/$idHex/") }
) {
Icon(
imageVector = Icons.Outlined.BarChart,
null,
modifier = iconButtonModifier,
tint = grayTint
)
}
val request = remember {
ImageRequest.Builder(context) ImageRequest.Builder(context)
.data("https://counter.amethyst.social/$idHex.svg?label=+&color=00000000") .data("https://counter.amethyst.social/$idHex.svg?label=+&color=00000000")
.diskCachePolicy(CachePolicy.DISABLED) .diskCachePolicy(CachePolicy.DISABLED)
.memoryCachePolicy(CachePolicy.ENABLED) .memoryCachePolicy(CachePolicy.ENABLED)
.build() .build()
} },
Row {
AsyncImage(
model = request,
contentDescription = stringResource(R.string.view_count), contentDescription = stringResource(R.string.view_count),
modifier = iconModifier, modifier = iconModifier,
colorFilter = colorFilter colorFilter = colorFilter
) )
}
} }
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@@ -60,7 +60,6 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.ViewModel 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.TextSpinner
import com.vitorpamplona.amethyst.ui.screen.loggedIn.getFragmentActivity import com.vitorpamplona.amethyst.ui.screen.loggedIn.getFragmentActivity
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -415,7 +415,7 @@ fun UpdateZapAmountDialog(onClose: () -> Unit, nip47uri: String? = null, account
stringResource(id = R.string.wallet_connect_service_explainer), stringResource(id = R.string.wallet_connect_service_explainer),
Modifier.weight(1f), Modifier.weight(1f),
color = MaterialTheme.colors.placeholderText, color = MaterialTheme.colors.placeholderText,
fontSize = 14.sp fontSize = Font14SP
) )
} }
@@ -101,7 +101,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
val updatedCards = (oldNotesState.feed.value + newCards) val updatedCards = (oldNotesState.feed.value + newCards)
.sortedWith(compareBy({ it.createdAt() }, { it.id() })) .sortedWith(compareBy({ it.createdAt() }, { it.id() }))
.reversed() .reversed()
.take(1000) .take(localFilter.limit())
.toImmutableList() .toImmutableList()
if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) { if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) {
@@ -115,7 +115,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
val cards = convertToCard(notes) val cards = convertToCard(notes)
.sortedWith(compareBy({ it.createdAt() }, { it.id() })) .sortedWith(compareBy({ it.createdAt() }, { it.id() }))
.reversed() .reversed()
.take(1000) .take(localFilter.limit())
.toImmutableList() .toImmutableList()
updateFeed(cards) updateFeed(cards)
@@ -255,7 +255,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
val updatedCards = (oldNotesState.feed.value + newCards) val updatedCards = (oldNotesState.feed.value + newCards)
.sortedWith(compareBy({ it.createdAt() }, { it.id() })) .sortedWith(compareBy({ it.createdAt() }, { it.id() }))
.reversed() .reversed()
.take(1000) .take(localFilter.limit())
.toImmutableList() .toImmutableList()
if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) { if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) {
@@ -288,7 +288,7 @@ fun NoteMaster(
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
NoteUsernameDisplay(baseNote, Modifier.weight(1f)) NoteUsernameDisplay(baseNote, Modifier.weight(1f))
DisplayFollowingHashtagsInPost(noteEvent, accountViewModel, nav) DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav)
Text( Text(
timeAgo(note.createdAt(), context = context), timeAgo(note.createdAt(), context = context),
@@ -364,14 +364,18 @@ fun EditFieldRow(
onSendNewMessage() onSendNewMessage()
}, },
isActive = channelScreenModel.message.text.isNotBlank() && !channelScreenModel.isUploadingImage, 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 = { leadingIcon = {
UploadFromGallery( UploadFromGallery(
isUploading = channelScreenModel.isUploadingImage, isUploading = channelScreenModel.isUploadingImage,
tint = MaterialTheme.colors.placeholderText, 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) { val fileServer = if (isPrivate) {
// TODO: Make private servers // 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 @Composable
fun ChannelHeader( fun ChannelHeader(
channelHex: String, channelHex: String,
@@ -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) 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) 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)
} }
} }
@@ -35,11 +35,11 @@ import androidx.compose.ui.text.input.*
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog 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.Size35dp
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import java.util.* import java.util.*
@@ -305,7 +305,7 @@ fun LoginPage(
} }
}, },
style = TextStyle( style = TextStyle(
fontSize = 14.sp, fontSize = Font14SP,
textDecoration = TextDecoration.Underline, textDecoration = TextDecoration.Underline,
color = MaterialTheme.colors.primary, color = MaterialTheme.colors.primary,
textAlign = TextAlign.Center textAlign = TextAlign.Center
@@ -16,6 +16,8 @@ val Nip05 = Color(0xFF01BAFF)
val FollowsFollow = Color.Yellow val FollowsFollow = Color.Yellow
val NIP05Verified = Color.Blue val NIP05Verified = Color.Blue
val DarkerGreen = Color.Green.copy(alpha = 0.32f)
val WarningColor = Color(0xFFC62828) val WarningColor = Color(0xFFC62828)
val RelayIconFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0.5f) }) val RelayIconFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0.5f) })
@@ -1,5 +1,7 @@
package com.vitorpamplona.amethyst.ui.theme 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.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size 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 ChatBubbleShapeThem = RoundedCornerShape(3.dp, 15.dp, 15.dp, 15.dp)
val StdButtonSizeModifier = Modifier.size(20.dp) val StdButtonSizeModifier = Modifier.size(20.dp)
val HalfVertSpacer = Modifier.height(2.dp)
val StdHorzSpacer = Modifier.width(5.dp) val StdHorzSpacer = Modifier.width(5.dp)
val StdVertSpacer = Modifier.height(5.dp) val StdVertSpacer = Modifier.height(5.dp)
val DoubleHorzSpacer = Modifier.width(10.dp) val DoubleHorzSpacer = Modifier.width(10.dp)
val DoubleVertSpacer = Modifier.height(10.dp) val DoubleVertSpacer = Modifier.height(10.dp)
val HalfDoubleVertSpacer = Modifier.height(7.dp)
val Size13dp = 13.dp val Size13dp = 13.dp
val Size16dp = 16.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 Size25dp = 25.dp
val Size30dp = 30.dp
val Size35dp = 35.dp val Size35dp = 35.dp
val Size55dp = 55.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 StdPadding = Modifier.padding(10.dp)
val Size15Modifier = Modifier.size(15.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)
@@ -1,17 +1,32 @@
package com.vitorpamplona.amethyst.ui.theme package com.vitorpamplona.amethyst.ui.theme
import android.app.Activity import android.app.Activity
import androidx.compose.foundation.border
import androidx.compose.foundation.isSystemInDarkTheme 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.Colors
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors import androidx.compose.material.darkColors
import androidx.compose.material.lightColors import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect 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.Color
import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView 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( private val DarkColorPalette = darkColors(
primary = Purple200, primary = Purple200,
@@ -30,9 +45,6 @@ private val LightColorPalette = lightColors(
private val DarkNewItemBackground = DarkColorPalette.primary.copy(0.12f) private val DarkNewItemBackground = DarkColorPalette.primary.copy(0.12f)
private val LightNewItemBackground = LightColorPalette.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 DarkSelectedNote = DarkNewItemBackground.compositeOver(DarkColorPalette.background)
private val LightSelectedNote = LightNewItemBackground.compositeOver(LightColorPalette.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 DarkPlaceholderText = DarkColorPalette.onSurface.copy(alpha = 0.32f)
private val LightPlaceholderText = LightColorPalette.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 DarkSubtleBorder = DarkColorPalette.onSurface.copy(alpha = 0.12f)
private val LightSubtleBorder = LightColorPalette.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 DarkZapraiserBackground = BitcoinOrange.copy(0.52f).compositeOver(DarkColorPalette.background)
private val LightZapraiserBackground = BitcoinOrange.copy(0.52f).compositeOver(LightColorPalette.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 DarkOverPictureBackground = DarkColorPalette.background.copy(0.62f)
private val LightOverPictureBackground = LightColorPalette.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 val Colors.newItemBackgroundColor: Color
get() = if (isLight) LightNewItemBackground else DarkNewItemBackground get() = if (isLight) LightNewItemBackground else DarkNewItemBackground
@@ -98,12 +194,21 @@ val Colors.grayText: Color
val Colors.subtleBorder: Color val Colors.subtleBorder: Color
get() = if (isLight) LightSubtleBorder else DarkSubtleBorder get() = if (isLight) LightSubtleBorder else DarkSubtleBorder
val Colors.subtleButton: Color
get() = if (isLight) LightSubtleButton else DarkSubtleButton
val Colors.hashVerified: Color val Colors.hashVerified: Color
get() = if (isLight) LightImageVerifier else DarkImageVerifier get() = if (isLight) LightImageVerifier else DarkImageVerifier
val Colors.overPictureBackground: Color val Colors.overPictureBackground: Color
get() = if (isLight) LightOverPictureBackground else DarkOverPictureBackground 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 @Composable
fun AmethystTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { fun AmethystTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
val colors = if (darkTheme) { val colors = if (darkTheme) {
@@ -4,6 +4,7 @@ import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with // Set of Material typography styles to start with
@@ -17,7 +18,7 @@ val Typography = Typography(
button = TextStyle( button = TextStyle(
fontFamily = FontFamily.Default, fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500, fontWeight = FontWeight.W500,
fontSize = 14.sp fontSize = Font14SP
), ),
caption = TextStyle( caption = TextStyle(
fontFamily = FontFamily.Default, fontFamily = FontFamily.Default,
@@ -26,3 +27,7 @@ val Typography = Typography(
) )
*/ */
) )
val Font14SP = 14.sp
val MarkdownTextStyle = TextStyle(lineHeight = 1.30.em)