mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-04-25 16:01:09 +02:00
Performance Improvements
This commit is contained in:
parent
743a4c9d87
commit
f9d652f849
@ -1134,7 +1134,7 @@ class Account(
|
||||
user.countReportAuthorsBy(followingKeySet()) < 5
|
||||
}
|
||||
|
||||
fun isAcceptableDirect(note: Note): Boolean {
|
||||
private fun isAcceptableDirect(note: Note): Boolean {
|
||||
if (!warnAboutPostsWithReports) {
|
||||
return note.reportsBy(userProfile()).isEmpty()
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class PublicChatChannel(idHex: String) : Channel(idHex) {
|
||||
}
|
||||
|
||||
override fun profilePicture(): String? {
|
||||
if (info.picture.isNullOrBlank()) return null
|
||||
if (info.picture.isNullOrBlank()) return super.profilePicture()
|
||||
return info.picture ?: super.profilePicture()
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ class LiveActivitiesChannel(val address: ATag) : Channel(address.toTag()) {
|
||||
}
|
||||
|
||||
override fun profilePicture(): String? {
|
||||
return info?.image()?.ifBlank { null } ?: super.profilePicture()
|
||||
return info?.image()?.ifBlank { null }
|
||||
}
|
||||
|
||||
override fun anyNameStartsWith(prefix: String): Boolean {
|
||||
|
@ -12,6 +12,8 @@ import com.vitorpamplona.amethyst.service.model.MetadataEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
||||
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||
import com.vitorpamplona.amethyst.ui.actions.ImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.ui.actions.toImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
|
||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||
import fr.acinq.secp256k1.Hex
|
||||
@ -237,6 +239,7 @@ class User(val pubkeyHex: String) {
|
||||
info = newUserInfo
|
||||
info?.latestMetadata = latestMetadata
|
||||
info?.updatedMetadataAt = latestMetadata.createdAt
|
||||
info?.tags = latestMetadata.tags.toImmutableListOfLists()
|
||||
|
||||
if (newUserInfo.lud16.isNullOrBlank() && newUserInfo.lud06?.lowercase()?.startsWith("lnurl") == true) {
|
||||
try {
|
||||
@ -421,6 +424,7 @@ class UserMetadata {
|
||||
|
||||
var updatedMetadataAt: Long = 0
|
||||
var latestMetadata: MetadataEvent? = null
|
||||
var tags: ImmutableListOfLists<String>? = null
|
||||
|
||||
fun anyName(): String? {
|
||||
return display_name ?: displayName ?: name ?: username
|
||||
@ -434,6 +438,23 @@ class UserMetadata {
|
||||
fun lnAddress(): String? {
|
||||
return (lud16?.trim() ?: lud06?.trim())?.ifBlank { null }
|
||||
}
|
||||
|
||||
fun bestUsername(): String? {
|
||||
return name?.ifBlank { null } ?: username?.ifBlank { null }
|
||||
}
|
||||
|
||||
fun bestDisplayName(): String? {
|
||||
return displayName?.ifBlank { null } ?: display_name?.ifBlank { null }
|
||||
}
|
||||
|
||||
fun nip05(): String? {
|
||||
return nip05?.ifBlank { null }
|
||||
}
|
||||
|
||||
fun profilePicture(): String? {
|
||||
if (picture.isNullOrBlank()) picture = null
|
||||
return picture
|
||||
}
|
||||
}
|
||||
|
||||
class UserLiveData(val user: User) : LiveData<UserState>(UserState(user)) {
|
||||
|
@ -65,6 +65,7 @@ import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchBarViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
@ -389,7 +390,7 @@ fun UserComposeForChat(
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
ClickableUserPicture(baseUser, 55.dp, accountViewModel)
|
||||
ClickableUserPicture(baseUser, Size55dp, accountViewModel)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
@ -3,6 +3,7 @@ package com.vitorpamplona.amethyst.ui.components
|
||||
import android.util.Log
|
||||
import android.util.LruCache
|
||||
import android.util.Patterns
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
@ -26,6 +27,8 @@ import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.map
|
||||
import com.halilibo.richtext.markdown.Markdown
|
||||
import com.halilibo.richtext.markdown.MarkdownParseOptions
|
||||
import com.halilibo.richtext.ui.HeadingStyle
|
||||
@ -40,7 +43,6 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.model.checkForHashtagWithIcon
|
||||
import com.vitorpamplona.amethyst.service.nip19.Nip19
|
||||
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
|
||||
@ -751,7 +753,11 @@ fun BechLink(word: String, canPreview: Boolean, backgroundColor: MutableState<Co
|
||||
}
|
||||
}
|
||||
|
||||
loadedLink = LoadedBechLink(returningNote, it)
|
||||
val newLink = LoadedBechLink(returningNote, it)
|
||||
|
||||
launch(Dispatchers.Main) {
|
||||
loadedLink = newLink
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -838,10 +844,12 @@ fun HashTag(word: String, nav: (String) -> Unit) {
|
||||
}
|
||||
|
||||
if (myTag != null) {
|
||||
launch(Dispatchers.Main) {
|
||||
tagSuffixPair = Pair(myTag, mySuffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tagSuffixPair?.let { tagPair ->
|
||||
val hashtagIcon = remember(tagPair.first) { checkForHashtagWithIcon(tagPair.first) }
|
||||
@ -922,10 +930,13 @@ fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolea
|
||||
if (tag.size > 1) {
|
||||
if (tag[0] == "p") {
|
||||
LocalCache.checkGetOrCreateUser(tag[1])?.let {
|
||||
launch(Dispatchers.Main) {
|
||||
loadedTag = LoadedTag(it, null, suffix)
|
||||
}
|
||||
}
|
||||
} else if (tag[0] == "e" || tag[0] == "a") {
|
||||
LocalCache.checkGetOrCreateNote(tag[1])?.let {
|
||||
launch(Dispatchers.Main) {
|
||||
loadedTag = LoadedTag(null, it, suffix)
|
||||
}
|
||||
}
|
||||
@ -934,6 +945,7 @@ fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolea
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loadedTag == null) {
|
||||
Text(
|
||||
@ -995,25 +1007,23 @@ private fun DisplayUserFromTag(
|
||||
) {
|
||||
val route = remember { "User/${baseUser.pubkeyHex}" }
|
||||
val suffix = remember { "$addedChars " }
|
||||
val hex = remember { baseUser.pubkeyDisplayHex() }
|
||||
|
||||
val innerUserState by baseUser.live().metadata.observeAsState()
|
||||
val displayName by remember(innerUserState) {
|
||||
derivedStateOf {
|
||||
innerUserState?.user?.toBestDisplayName() ?: ""
|
||||
}
|
||||
}
|
||||
val userTags by remember(innerUserState) {
|
||||
derivedStateOf {
|
||||
innerUserState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists()
|
||||
}
|
||||
}
|
||||
val meta by baseUser.live().metadata.map {
|
||||
it.user.info
|
||||
}.distinctUntilChanged().observeAsState(baseUser.info)
|
||||
|
||||
Crossfade(targetState = meta) {
|
||||
val displayName = remember(it) {
|
||||
it?.bestDisplayName() ?: hex
|
||||
}
|
||||
CreateClickableTextWithEmoji(
|
||||
clickablePart = displayName,
|
||||
suffix = suffix,
|
||||
maxLines = 1,
|
||||
route = route,
|
||||
nav = nav,
|
||||
tags = userTags
|
||||
tags = it?.tags ?: ImmutableListOfLists()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ fun RobohashAsyncImage(
|
||||
@Composable
|
||||
fun RobohashFallbackAsyncImage(
|
||||
robot: String,
|
||||
model: String,
|
||||
model: String?,
|
||||
contentDescription: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
alignment: Alignment = Alignment.Center,
|
||||
@ -91,18 +91,6 @@ fun RobohashAsyncImageProxy(
|
||||
colorFilter: ColorFilter? = null,
|
||||
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
|
||||
) {
|
||||
if (model == null) {
|
||||
RobohashAsyncImage(
|
||||
robot = robot,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
alignment = alignment,
|
||||
contentScale = contentScale,
|
||||
alpha = alpha,
|
||||
colorFilter = colorFilter,
|
||||
filterQuality = filterQuality
|
||||
)
|
||||
} else {
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = robot,
|
||||
model = model,
|
||||
@ -115,4 +103,3 @@ fun RobohashAsyncImageProxy(
|
||||
filterQuality = filterQuality
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@ -28,6 +29,7 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.map
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@ -109,31 +111,28 @@ fun ObserveDisplayNip05Status(baseNote: Note, columnModifier: Modifier = Modifie
|
||||
|
||||
@Composable
|
||||
fun ObserveDisplayNip05Status(baseUser: User, columnModifier: Modifier = Modifier) {
|
||||
val userState by baseUser.live().metadata.observeAsState()
|
||||
val isValidNIP05 by remember(userState) {
|
||||
derivedStateOf {
|
||||
userState?.user?.nip05()?.split("@")?.size == 2
|
||||
}
|
||||
}
|
||||
val nip05 by remember(userState) {
|
||||
derivedStateOf {
|
||||
userState?.user?.nip05()
|
||||
}
|
||||
}
|
||||
val nip05 by baseUser.live().metadata.map {
|
||||
it.user.nip05()
|
||||
}.observeAsState()
|
||||
|
||||
if (isValidNIP05) {
|
||||
nip05?.let {
|
||||
Crossfade(targetState = nip05, modifier = columnModifier) {
|
||||
if (it != null) {
|
||||
val isValid = it.split("@").size == 2
|
||||
if (isValid) {
|
||||
DisplayNIP05Line(it, baseUser, columnModifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayNIP05Line(nip05: String, baseUser: User, columnModifier: Modifier = Modifier) {
|
||||
Column(modifier = columnModifier) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
val nip05Verified = nip05VerificationAsAState(baseUser.info!!, baseUser.pubkeyHex)
|
||||
DisplayNIP05(nip05, nip05Verified)
|
||||
Crossfade(targetState = nip05Verified) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
DisplayNIP05(nip05, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,7 +149,7 @@ private fun DisplayNIP05(
|
||||
|
||||
if (user != "_") {
|
||||
Text(
|
||||
text = AnnotatedString(user),
|
||||
text = remember(nip05) { AnnotatedString(user) },
|
||||
color = MaterialTheme.colors.placeholderText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
@ -160,7 +159,7 @@ private fun DisplayNIP05(
|
||||
NIP05VerifiedSymbol(nip05Verified)
|
||||
|
||||
ClickableText(
|
||||
text = AnnotatedString(domain),
|
||||
text = remember(nip05) { AnnotatedString(domain) },
|
||||
onClick = { runCatching { uri.openUri("https://$domain") } },
|
||||
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary.copy(0.52f)),
|
||||
maxLines = 1,
|
||||
|
@ -83,6 +83,7 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.graphics.get
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.map
|
||||
import coil.compose.AsyncImage
|
||||
import coil.compose.AsyncImagePainter
|
||||
@ -160,6 +161,8 @@ import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonBoxModifer
|
||||
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconButtonModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size24Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size25dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size30dp
|
||||
@ -168,12 +171,16 @@ import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdStartPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.UserNameMaxRowHeight
|
||||
import com.vitorpamplona.amethyst.ui.theme.UserNameRowHeight
|
||||
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.replyModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.repostProfileBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@ -195,7 +202,7 @@ import java.util.Locale
|
||||
fun NoteCompose(
|
||||
baseNote: Note,
|
||||
routeForLastRead: String? = null,
|
||||
modifier: Modifier = remember { Modifier },
|
||||
modifier: Modifier = Modifier,
|
||||
isBoostedNote: Boolean = false,
|
||||
isQuotedNote: Boolean = false,
|
||||
unPackReply: Boolean = true,
|
||||
@ -207,9 +214,10 @@ fun NoteCompose(
|
||||
) {
|
||||
val isBlank by baseNote.live().metadata.map {
|
||||
it.note.event == null
|
||||
}.observeAsState(baseNote.event == null)
|
||||
}.distinctUntilChanged().observeAsState(baseNote.event == null)
|
||||
|
||||
if (isBlank) {
|
||||
Crossfade(targetState = isBlank) {
|
||||
if (it) {
|
||||
LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
|
||||
BlankNote(
|
||||
remember {
|
||||
@ -237,6 +245,7 @@ fun NoteCompose(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CheckHiddenNoteCompose(
|
||||
@ -261,7 +270,8 @@ fun CheckHiddenNoteCompose(
|
||||
}
|
||||
}
|
||||
|
||||
if (!isHidden) {
|
||||
Crossfade(targetState = isHidden) {
|
||||
if (!it) {
|
||||
LoadedNoteCompose(
|
||||
note,
|
||||
routeForLastRead,
|
||||
@ -277,6 +287,7 @@ fun CheckHiddenNoteCompose(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class NoteComposeReportState(
|
||||
@ -309,20 +320,52 @@ fun LoadedNoteCompose(
|
||||
)
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
WatchForReports(note, accountViewModel) { newIsAcceptable, newCanPreview, newRelevantReports ->
|
||||
if (newIsAcceptable != state.isAcceptable || newCanPreview != state.canPreview) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
state = NoteComposeReportState(newIsAcceptable, newCanPreview, newRelevantReports)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(targetState = state) {
|
||||
RenderReportState(
|
||||
it,
|
||||
note,
|
||||
routeForLastRead,
|
||||
modifier,
|
||||
isBoostedNote,
|
||||
isQuotedNote,
|
||||
unPackReply,
|
||||
makeItShort,
|
||||
addMarginTop,
|
||||
parentBackgroundColor,
|
||||
accountViewModel,
|
||||
nav
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderReportState(
|
||||
state: NoteComposeReportState,
|
||||
note: Note,
|
||||
routeForLastRead: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
isBoostedNote: Boolean = false,
|
||||
isQuotedNote: Boolean = false,
|
||||
unPackReply: Boolean = true,
|
||||
makeItShort: Boolean = false,
|
||||
addMarginTop: Boolean = true,
|
||||
parentBackgroundColor: MutableState<Color>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
var showReportedNote by remember { mutableStateOf(false) }
|
||||
|
||||
val showHiddenNote by remember(state, showReportedNote) {
|
||||
derivedStateOf {
|
||||
!state.isAcceptable && !showReportedNote
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(targetState = !state.isAcceptable && !showReportedNote) { showHiddenNote ->
|
||||
if (showHiddenNote) {
|
||||
HiddenNote(
|
||||
state.relevantReports,
|
||||
@ -333,11 +376,7 @@ fun LoadedNoteCompose(
|
||||
onClick = { showReportedNote = true }
|
||||
)
|
||||
} else {
|
||||
val canPreview by remember(state, showReportedNote) {
|
||||
derivedStateOf {
|
||||
(!state.isAcceptable && showReportedNote) || state.canPreview
|
||||
}
|
||||
}
|
||||
val canPreview = (!state.isAcceptable && showReportedNote) || state.canPreview
|
||||
|
||||
NormalNote(
|
||||
note,
|
||||
@ -355,6 +394,7 @@ fun LoadedNoteCompose(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WatchForReports(
|
||||
@ -368,13 +408,23 @@ fun WatchForReports(
|
||||
LaunchedEffect(key1 = noteReportsState, key2 = userFollowsState) {
|
||||
launch(Dispatchers.Default) {
|
||||
accountViewModel.account.let { loggedIn ->
|
||||
val newCanPreview = note.author?.pubkeyHex == loggedIn.userProfile().pubkeyHex ||
|
||||
(note.author?.let { loggedIn.userProfile().isFollowingCached(it) } ?: true) ||
|
||||
noteReportsState?.note?.hasAnyReports() != true
|
||||
val isFromLoggedIn = note.author?.pubkeyHex == loggedIn.userProfile().pubkeyHex
|
||||
val isFromLoggedInFollow = note.author?.let { loggedIn.userProfile().isFollowingCached(it) } ?: true
|
||||
|
||||
if (isFromLoggedIn || isFromLoggedInFollow) {
|
||||
// No need to process if from trusted people
|
||||
onChange(true, true, persistentSetOf())
|
||||
} else {
|
||||
val newCanPreview = noteReportsState?.note?.hasAnyReports() != true
|
||||
|
||||
val newIsAcceptable = noteReportsState?.note?.let {
|
||||
loggedIn.isAcceptable(it)
|
||||
} ?: true
|
||||
|
||||
if (newCanPreview && newIsAcceptable) {
|
||||
// No need to process reports if nothing is wrong
|
||||
onChange(true, true, persistentSetOf())
|
||||
} else {
|
||||
val newRelevantReports = noteReportsState?.note?.let {
|
||||
loggedIn.getRelevantReports(it)
|
||||
} ?: emptySet()
|
||||
@ -384,6 +434,8 @@ fun WatchForReports(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NormalNote(
|
||||
@ -400,29 +452,18 @@ fun NormalNote(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val noteEvent = remember { baseNote.event }
|
||||
|
||||
val isChannelHeader by remember {
|
||||
derivedStateOf {
|
||||
(baseNote.event is ChannelCreateEvent || baseNote.event is ChannelMetadataEvent) && baseNote.channelHex() != null
|
||||
}
|
||||
}
|
||||
|
||||
if (isChannelHeader) {
|
||||
ChannelHeader(
|
||||
when (baseNote.event) {
|
||||
is ChannelCreateEvent, is ChannelMetadataEvent -> ChannelHeader(
|
||||
channelNote = baseNote,
|
||||
showVideo = !makeItShort,
|
||||
showBottomDiviser = true,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav
|
||||
)
|
||||
} else if (noteEvent is BadgeDefinitionEvent) {
|
||||
BadgeDisplay(baseNote = baseNote)
|
||||
} else if (noteEvent is FileHeaderEvent) {
|
||||
FileHeaderDisplay(baseNote)
|
||||
} else if (noteEvent is FileStorageHeaderEvent) {
|
||||
FileStorageHeaderDisplay(baseNote)
|
||||
} else {
|
||||
is BadgeDefinitionEvent -> BadgeDisplay(baseNote = baseNote)
|
||||
is FileHeaderEvent -> FileHeaderDisplay(baseNote)
|
||||
is FileStorageHeaderEvent -> FileStorageHeaderDisplay(baseNote)
|
||||
else ->
|
||||
LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
|
||||
CheckNewAndRenderNote(
|
||||
baseNote,
|
||||
@ -534,7 +575,7 @@ private fun ClickableNote(
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val updatedModifier = remember(backgroundColor.value) {
|
||||
val updatedModifier = remember(backgroundColor) {
|
||||
modifier
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
@ -569,17 +610,8 @@ fun InnerNoteWithReactions(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val notBoostedNorQuote by remember {
|
||||
derivedStateOf {
|
||||
!isBoostedNote && !isQuotedNote
|
||||
}
|
||||
}
|
||||
|
||||
val showSecondRow by remember {
|
||||
derivedStateOf {
|
||||
baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent && !isBoostedNote && !isQuotedNote
|
||||
}
|
||||
}
|
||||
val notBoostedNorQuote = !isBoostedNote && !isQuotedNote
|
||||
val showSecondRow = baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent && !isBoostedNote && !isQuotedNote
|
||||
|
||||
Row(
|
||||
modifier = remember {
|
||||
@ -593,10 +625,13 @@ fun InnerNoteWithReactions(
|
||||
}
|
||||
) {
|
||||
if (notBoostedNorQuote) {
|
||||
Column(WidthAuthorPictureModifier) {
|
||||
AuthorAndRelayInformation(baseNote, accountViewModel, nav)
|
||||
}
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
NoteBody(
|
||||
baseNote = baseNote,
|
||||
showAuthorPicture = isQuotedNote,
|
||||
@ -609,12 +644,9 @@ fun InnerNoteWithReactions(
|
||||
nav = nav
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val isNotRepost by remember {
|
||||
derivedStateOf {
|
||||
baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent
|
||||
}
|
||||
}
|
||||
val isNotRepost = baseNote.event !is RepostEvent && baseNote.event !is GenericRepostEvent
|
||||
|
||||
if (isNotRepost) {
|
||||
if (makeItShort) {
|
||||
@ -651,7 +683,6 @@ private fun NoteBody(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
FirstUserInfoRow(
|
||||
baseNote = baseNote,
|
||||
showAuthorPicture = showAuthorPicture,
|
||||
@ -688,7 +719,6 @@ private fun NoteBody(
|
||||
nav
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderNoteRow(
|
||||
@ -1711,6 +1741,15 @@ private fun ReplyRow(
|
||||
}
|
||||
}
|
||||
|
||||
if (showReply) {
|
||||
val replyingDirectlyTo = remember { note.replyTo?.lastOrNull() }
|
||||
if (replyingDirectlyTo != null && unPackReply) {
|
||||
ReplyNoteComposition(replyingDirectlyTo, backgroundColor, accountViewModel, nav)
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
} else {
|
||||
// ReplyInformation(note.replyTo, noteEvent.mentions(), accountViewModel, nav)
|
||||
}
|
||||
} else {
|
||||
val showChannelReply by remember {
|
||||
derivedStateOf {
|
||||
(noteEvent is ChannelMessageEvent && (note.replyTo != null || noteEvent.hasAnyTaggedUser())) ||
|
||||
@ -1718,15 +1757,7 @@ private fun ReplyRow(
|
||||
}
|
||||
}
|
||||
|
||||
if (showReply) {
|
||||
val replyingDirectlyTo = remember { note.replyTo?.lastOrNull() }
|
||||
if (replyingDirectlyTo != null && unPackReply) {
|
||||
ReplyNoteComposition(replyingDirectlyTo, backgroundColor, accountViewModel, nav)
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
} else {
|
||||
// ReplyInformation(note.replyTo, noteEvent.mentions(), accountViewModel, nav)
|
||||
}
|
||||
} else if (showChannelReply) {
|
||||
if (showChannelReply) {
|
||||
val channelHex = note.channelHex()
|
||||
channelHex?.let {
|
||||
ChannelHeader(
|
||||
@ -1745,6 +1776,7 @@ private fun ReplyRow(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReplyNoteComposition(
|
||||
@ -1759,7 +1791,7 @@ private fun ReplyNoteComposition(
|
||||
val defaultReplyBackground = MaterialTheme.colors.replyBackground
|
||||
|
||||
LaunchedEffect(key1 = backgroundColor.value, key2 = defaultReplyBackground) {
|
||||
launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.Default) {
|
||||
val newReplyBackgroundColor =
|
||||
defaultReplyBackground.compositeOver(backgroundColor.value)
|
||||
if (replyBackgroundColor.value != newReplyBackgroundColor) {
|
||||
@ -1768,22 +1800,10 @@ private fun ReplyNoteComposition(
|
||||
}
|
||||
}
|
||||
|
||||
val borderColor = MaterialTheme.colors.subtleBorder
|
||||
|
||||
NoteCompose(
|
||||
baseNote = replyingDirectlyTo,
|
||||
isQuotedNote = true,
|
||||
modifier = remember {
|
||||
Modifier
|
||||
.padding(top = 5.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = QuoteBorder)
|
||||
.border(
|
||||
1.dp,
|
||||
borderColor,
|
||||
QuoteBorder
|
||||
)
|
||||
},
|
||||
modifier = MaterialTheme.colors.replyModifier,
|
||||
unPackReply = false,
|
||||
makeItShort = true,
|
||||
parentBackgroundColor = replyBackgroundColor,
|
||||
@ -1801,7 +1821,7 @@ private fun SecondUserInfoRow(
|
||||
val noteEvent = remember { note.event } ?: return
|
||||
val noteAuthor = remember { note.author } ?: return
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(verticalAlignment = CenterVertically, modifier = UserNameMaxRowHeight) {
|
||||
ObserveDisplayNip05Status(noteAuthor, remember { Modifier.weight(1f) })
|
||||
|
||||
val baseReward = remember { noteEvent.getReward()?.let { Reward(it) } }
|
||||
@ -1823,7 +1843,7 @@ private fun FirstUserInfoRow(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
Row(verticalAlignment = CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
||||
Row(verticalAlignment = CenterVertically, modifier = remember { UserNameRowHeight }) {
|
||||
val isRepost by remember {
|
||||
derivedStateOf {
|
||||
baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent
|
||||
@ -1869,13 +1889,13 @@ private fun MoreOptionsButton(
|
||||
var moreActionsExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(
|
||||
modifier = remember { Modifier.size(24.dp) },
|
||||
modifier = Size24Modifier,
|
||||
onClick = { moreActionsExpanded = true }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
null,
|
||||
modifier = remember { Modifier.size(15.dp) },
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colors.placeholderText
|
||||
)
|
||||
|
||||
@ -1908,7 +1928,6 @@ fun TimeAgo(time: Long) {
|
||||
|
||||
@Composable
|
||||
private fun AuthorAndRelayInformation(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
Column(WidthAuthorPictureModifier) {
|
||||
// Draws the boosted picture outside the boosted card.
|
||||
Box(modifier = Size55Modifier, contentAlignment = Alignment.BottomEnd) {
|
||||
RenderAuthorImages(baseNote, nav, accountViewModel)
|
||||
@ -1916,7 +1935,6 @@ private fun AuthorAndRelayInformation(baseNote: Note, accountViewModel: AccountV
|
||||
|
||||
BadgeBox(baseNote, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BadgeBox(
|
||||
@ -1950,11 +1968,7 @@ private fun RenderAuthorImages(
|
||||
nav: (String) -> Unit,
|
||||
accountViewModel: AccountViewModel
|
||||
) {
|
||||
val isRepost by remember {
|
||||
derivedStateOf {
|
||||
baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent
|
||||
}
|
||||
}
|
||||
val isRepost = baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent
|
||||
|
||||
NoteAuthorPicture(baseNote, nav, accountViewModel, Size55dp)
|
||||
|
||||
@ -1962,11 +1976,7 @@ private fun RenderAuthorImages(
|
||||
RepostNoteAuthorPicture(baseNote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
val isChannel by remember {
|
||||
derivedStateOf {
|
||||
baseNote.event is ChannelMessageEvent && baseNote.channelHex() != null
|
||||
}
|
||||
}
|
||||
val isChannel = baseNote.event is ChannelMessageEvent && baseNote.channelHex() != null
|
||||
|
||||
if (isChannel) {
|
||||
val baseChannelHex = remember { baseNote.channelHex() }
|
||||
@ -2001,7 +2011,7 @@ fun LoadChannel(baseChannelHex: String, content: @Composable (Channel) -> Unit)
|
||||
private fun ChannelNotePicture(baseChannel: Channel) {
|
||||
val model by baseChannel.live.map {
|
||||
it.channel.profilePicture()
|
||||
}.observeAsState()
|
||||
}.distinctUntilChanged().observeAsState()
|
||||
|
||||
val backgroundColor = MaterialTheme.colors.background
|
||||
|
||||
@ -2879,7 +2889,10 @@ private fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav:
|
||||
mutableStateOf(lazyRelayList.take(3).toImmutableList())
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
WatchRelayLists(baseNote) { relayList ->
|
||||
scope.launch(Dispatchers.Main) {
|
||||
if (!equalImmutableLists(relayList, lazyRelayList)) {
|
||||
lazyRelayList = relayList
|
||||
shortRelayList = relayList.take(3).toImmutableList()
|
||||
@ -2891,6 +2904,7 @@ private fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav:
|
||||
showShowMore = nextShowMore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(DoubleVertSpacer)
|
||||
|
||||
@ -2982,12 +2996,14 @@ fun NoteAuthorPicture(
|
||||
) {
|
||||
val author by baseNote.live().metadata.map {
|
||||
it.note.author
|
||||
}.observeAsState()
|
||||
}.distinctUntilChanged().observeAsState(baseNote.author)
|
||||
|
||||
if (author == null) {
|
||||
Crossfade(targetState = author) {
|
||||
if (it == null) {
|
||||
DisplayBlankAuthor(size, modifier)
|
||||
} else {
|
||||
ClickableUserPicture(author!!, size, accountViewModel, modifier, onClick)
|
||||
ClickableUserPicture(it, size, accountViewModel, modifier, onClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3111,16 +3127,24 @@ fun BaseUserPicture(
|
||||
|
||||
val userProfile by baseUser.live().metadata.map {
|
||||
it.user.profilePicture()
|
||||
}.observeAsState()
|
||||
}.distinctUntilChanged().observeAsState(baseUser.profilePicture())
|
||||
|
||||
val myBoxModifier = remember {
|
||||
Modifier.size(size)
|
||||
}
|
||||
|
||||
Crossfade(targetState = userProfile) {
|
||||
Box(myBoxModifier, contentAlignment = TopEnd) {
|
||||
PictureAndFollowingMark(
|
||||
userHex = userPubkey,
|
||||
userPicture = userProfile,
|
||||
userPicture = it,
|
||||
size = size,
|
||||
modifier = modifier,
|
||||
accountViewModel = accountViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PictureAndFollowingMark(
|
||||
@ -3157,14 +3181,18 @@ fun PictureAndFollowingMark(
|
||||
private fun ObserveAndDisplayFollowingMark(userHex: String, iconSize: Dp, accountViewModel: AccountViewModel) {
|
||||
val showFollowingMark by accountViewModel.userFollows.map {
|
||||
it.user.isFollowingCached(userHex) || (userHex == accountViewModel.account.userProfile().pubkeyHex)
|
||||
}.observeAsState(
|
||||
}.distinctUntilChanged().observeAsState(
|
||||
accountViewModel.account.userProfile().isFollowingCached(userHex) || (userHex == accountViewModel.account.userProfile().pubkeyHex)
|
||||
)
|
||||
|
||||
if (showFollowingMark) {
|
||||
Crossfade(targetState = showFollowingMark) {
|
||||
if (it) {
|
||||
Box(contentAlignment = TopEnd) {
|
||||
FollowingIcon(iconSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FollowingIcon(iconSize: Dp) {
|
||||
|
@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
|
||||
@Composable
|
||||
fun UserCompose(
|
||||
@ -38,9 +39,9 @@ fun UserCompose(
|
||||
modifier = overallModifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
UserPicture(baseUser, 55.dp, accountViewModel = accountViewModel, nav = nav)
|
||||
UserPicture(baseUser, Size55dp, accountViewModel = accountViewModel, nav = nav)
|
||||
|
||||
Column(modifier = Modifier.padding(start = 10.dp).weight(1f)) {
|
||||
Column(modifier = remember { Modifier.padding(start = 10.dp).weight(1f) }) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
UsernameDisplay(baseUser)
|
||||
}
|
||||
@ -48,7 +49,7 @@ fun UserCompose(
|
||||
AboutDisplay(baseUser)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(start = 10.dp)) {
|
||||
Column(modifier = remember { Modifier.padding(start = 10.dp) }) {
|
||||
UserActionOptions(baseUser, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
@ -20,13 +22,11 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.map
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.tts.TextToSpeechHelper
|
||||
import com.vitorpamplona.amethyst.ui.actions.ImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.ui.actions.toImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdButtonSizeModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
@ -36,38 +36,34 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
fun NoteUsernameDisplay(baseNote: Note, weight: Modifier = Modifier) {
|
||||
val authorState by baseNote.live().metadata.map {
|
||||
it.note.author
|
||||
}.distinctUntilChanged().observeAsState()
|
||||
}.observeAsState(baseNote.author)
|
||||
|
||||
authorState?.let {
|
||||
Crossfade(targetState = authorState, modifier = weight) {
|
||||
it?.let {
|
||||
UsernameDisplay(it, weight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UsernameDisplay(baseUser: User, weight: Modifier = Modifier) {
|
||||
val userState by baseUser.live().metadata.observeAsState()
|
||||
val bestUserName by remember(userState) {
|
||||
derivedStateOf {
|
||||
userState?.user?.bestUsername()
|
||||
}
|
||||
}
|
||||
val bestDisplayName by remember(userState) {
|
||||
derivedStateOf {
|
||||
userState?.user?.bestDisplayName()
|
||||
}
|
||||
}
|
||||
val npubDisplay by remember {
|
||||
derivedStateOf {
|
||||
baseUser.pubkeyDisplayHex()
|
||||
}
|
||||
}
|
||||
val tags by remember(userState) {
|
||||
derivedStateOf {
|
||||
userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists()
|
||||
}
|
||||
}
|
||||
|
||||
UserNameDisplay(bestUserName, bestDisplayName, npubDisplay, tags, weight)
|
||||
val userMetadata by baseUser.live().metadata.map {
|
||||
it.user.info
|
||||
}.observeAsState(baseUser.info)
|
||||
|
||||
Crossfade(targetState = userMetadata, modifier = weight) {
|
||||
if (it != null) {
|
||||
UserNameDisplay(it.bestUsername(), it.bestDisplayName(), npubDisplay, it.tags, weight)
|
||||
} else {
|
||||
NPubDisplay(npubDisplay, weight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -106,6 +102,7 @@ private fun UserDisplay(
|
||||
tags: ImmutableListOfLists<String>?,
|
||||
modifier: Modifier
|
||||
) {
|
||||
Row(modifier = modifier) {
|
||||
CreateTextWithEmoji(
|
||||
text = bestDisplayName,
|
||||
tags = tags,
|
||||
@ -117,6 +114,7 @@ private fun UserDisplay(
|
||||
Spacer(StdHorzSpacer)
|
||||
DrawPlayName(bestDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserAndUsernameDisplay(
|
||||
@ -125,6 +123,7 @@ private fun UserAndUsernameDisplay(
|
||||
bestUserName: String,
|
||||
modifier: Modifier
|
||||
) {
|
||||
Row(modifier = modifier) {
|
||||
CreateTextWithEmoji(
|
||||
text = bestDisplayName,
|
||||
tags = tags,
|
||||
@ -142,6 +141,7 @@ private fun UserAndUsernameDisplay(
|
||||
Spacer(StdHorzSpacer)
|
||||
DrawPlayName(bestDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DrawPlayName(name: String) {
|
||||
|
@ -35,6 +35,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShowUserButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.UnfollowButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.showAmountAxis
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@ -106,7 +107,7 @@ private fun RenderZapNote(
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
UserPicture(baseAuthor, 55.dp, accountViewModel = accountViewModel, nav = nav)
|
||||
UserPicture(baseAuthor, Size55dp, accountViewModel = accountViewModel, nav = nav)
|
||||
|
||||
Column(
|
||||
modifier = remember {
|
||||
|
@ -197,8 +197,7 @@ private fun FeedLoaded(
|
||||
) {
|
||||
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
|
||||
val defaultModifier = remember {
|
||||
Modifier
|
||||
.fillMaxWidth().animateItemPlacement()
|
||||
Modifier.fillMaxWidth().animateItemPlacement()
|
||||
}
|
||||
|
||||
Row(defaultModifier) {
|
||||
|
@ -586,9 +586,10 @@ fun ChannelHeader(
|
||||
)
|
||||
}
|
||||
|
||||
channel.profilePicture()?.let {
|
||||
RobohashAsyncImageProxy(
|
||||
robot = channel.idHex,
|
||||
model = channel.profilePicture(),
|
||||
model = it,
|
||||
contentDescription = stringResource(R.string.profile_image),
|
||||
modifier = Modifier
|
||||
.width(Size35dp)
|
||||
@ -596,6 +597,7 @@ fun ChannelHeader(
|
||||
.padding(start = 10.dp)
|
||||
.clip(shape = CircleShape)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
@ -3,6 +3,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.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
@ -62,6 +63,7 @@ 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 Size24Modifier = Modifier.size(24.dp)
|
||||
val Size30Modifier = Modifier.size(30.dp)
|
||||
val Size55Modifier = Modifier.size(55.dp)
|
||||
|
||||
@ -76,6 +78,8 @@ val DiviserThickness = 0.25.dp
|
||||
|
||||
val ReactionRowHeight = Modifier.height(24.dp).padding(start = 10.dp)
|
||||
val ReactionRowHeightChat = Modifier.height(25.dp)
|
||||
val UserNameRowHeight = Modifier.height(22.dp).fillMaxWidth()
|
||||
val UserNameMaxRowHeight = Modifier.heightIn(max = 22.dp).fillMaxWidth()
|
||||
|
||||
val Height4dpModifier = Modifier.height(4.dp)
|
||||
|
||||
|
@ -121,6 +121,26 @@ val LightImageModifier = Modifier
|
||||
QuoteBorder
|
||||
)
|
||||
|
||||
val DarkReplyBorderModifier = Modifier
|
||||
.padding(top = 5.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = QuoteBorder)
|
||||
.border(
|
||||
1.dp,
|
||||
DarkSubtleBorder,
|
||||
QuoteBorder
|
||||
)
|
||||
|
||||
val LightReplyBorderModifier = Modifier
|
||||
.padding(top = 5.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = QuoteBorder)
|
||||
.border(
|
||||
1.dp,
|
||||
LightSubtleBorder,
|
||||
QuoteBorder
|
||||
)
|
||||
|
||||
val MarkDownStyleOnDark = richTextDefaults.copy(
|
||||
paragraphSpacing = DefaultParagraphSpacing,
|
||||
headingStyle = DefaultHeadingStyle,
|
||||
@ -243,6 +263,9 @@ val Colors.repostProfileBorder: Modifier
|
||||
val Colors.imageModifier: Modifier
|
||||
get() = if (isLight) LightImageModifier else DarkImageModifier
|
||||
|
||||
val Colors.replyModifier: Modifier
|
||||
get() = if (isLight) LightReplyBorderModifier else DarkReplyBorderModifier
|
||||
|
||||
@Composable
|
||||
fun AmethystTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
|
||||
val colors = if (darkTheme) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user