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() {
bundler.invalidate() {
// if (hasObservers()) {
postValue(NoteState(note))
// }
if (hasActiveObservers()) {
postValue(NoteState(note))
}
}
}
@@ -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?) {
@@ -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)
@@ -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
@@ -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
)
@@ -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<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 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(
@@ -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)
}
}
}
@@ -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<Note>) -> 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<Color>? = 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<String?>(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)
}
}
}
@@ -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
)
@@ -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<Boolean>(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<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
@@ -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<Boole
}
if (hasReactions) {
IconButton(
onClick = {
wantsToSeeReactions.value = !wantsToSeeReactions.value
},
modifier = Modifier.size(20.dp)
) {
if (wantsToSeeReactions.value) {
Icon(
imageVector = Icons.Default.ExpandLess,
null,
modifier = Modifier.size(22.dp),
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.22f)
)
} else {
Icon(
imageVector = Icons.Default.ExpandMore,
null,
modifier = Modifier.size(22.dp),
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.22f)
)
}
RenderShowIndividualReactionsButton(wantsToSeeReactions)
}
}
@Composable
private fun RenderShowIndividualReactionsButton(wantsToSeeReactions: MutableState<Boolean>) {
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<Note?>(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<Note?>(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)
@@ -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
)
}
@@ -101,7 +101,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : 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<Note>) : 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<Note>) : 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)) {
@@ -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),
@@ -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,
@@ -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)
}
}
@@ -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
@@ -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) })
@@ -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)
@@ -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) {
@@ -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)