- User Metadata clean up upon receipt instead of in every rendering.

- Simpler/Faster UserDisplay renderings
- Removes co-routing use for Hashtags.
This commit is contained in:
Vitor Pamplona
2024-03-06 10:32:08 -05:00
parent da41fbb4c9
commit b8619e3b61
5 changed files with 49 additions and 109 deletions
@@ -310,6 +310,7 @@ class User(val pubkeyHex: String) {
info?.latestMetadata = latestMetadata info?.latestMetadata = latestMetadata
info?.updatedMetadataAt = latestMetadata.createdAt info?.updatedMetadataAt = latestMetadata.createdAt
info?.tags = latestMetadata.tags.toImmutableListOfLists() info?.tags = latestMetadata.tags.toImmutableListOfLists()
info?.cleanBlankNames()
if (newUserInfo.lud16.isNullOrBlank()) { if (newUserInfo.lud16.isNullOrBlank()) {
info?.lud06?.let { info?.lud06?.let {
@@ -76,6 +76,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.DisplayZapSplits
import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
import com.vitorpamplona.amethyst.ui.note.elements.Reward import com.vitorpamplona.amethyst.ui.note.elements.Reward
import com.vitorpamplona.amethyst.ui.note.elements.ShowForkInformation import com.vitorpamplona.amethyst.ui.note.elements.ShowForkInformation
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
import com.vitorpamplona.amethyst.ui.note.types.BadgeDisplay import com.vitorpamplona.amethyst.ui.note.types.BadgeDisplay
import com.vitorpamplona.amethyst.ui.note.types.CommunityHeader import com.vitorpamplona.amethyst.ui.note.types.CommunityHeader
import com.vitorpamplona.amethyst.ui.note.types.DisplayPeopleList import com.vitorpamplona.amethyst.ui.note.types.DisplayPeopleList
@@ -1121,11 +1122,8 @@ fun FirstUserInfoRow(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
Row(verticalAlignment = CenterVertically, modifier = remember { UserNameRowHeight }) { Row(verticalAlignment = CenterVertically, modifier = UserNameRowHeight) {
val isRepost by val isRepost = baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent
remember(baseNote) {
derivedStateOf { baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent }
}
val isCommunityPost by val isCommunityPost by
remember(baseNote) { remember(baseNote) {
@@ -1139,9 +1137,9 @@ fun FirstUserInfoRow(
if (showAuthorPicture) { if (showAuthorPicture) {
NoteAuthorPicture(baseNote, nav, accountViewModel, Size25dp) NoteAuthorPicture(baseNote, nav, accountViewModel, Size25dp)
Spacer(HalfPadding) Spacer(HalfPadding)
NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) }, textColor = textColor) NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor)
} else { } else {
NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) }, textColor = textColor) NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor)
} }
if (isRepost) { if (isRepost) {
@@ -1158,7 +1156,7 @@ fun FirstUserInfoRow(
} }
} }
com.vitorpamplona.amethyst.ui.note.elements.TimeAgo(baseNote) TimeAgo(baseNote)
MoreOptionsButton(baseNote, editState, accountViewModel, nav) MoreOptionsButton(baseNote, editState, accountViewModel, nav)
} }
@@ -29,7 +29,6 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -72,67 +71,27 @@ fun UsernameDisplay(
fontWeight: FontWeight = FontWeight.Bold, fontWeight: FontWeight = FontWeight.Bold,
textColor: Color = Color.Unspecified, textColor: Color = Color.Unspecified,
) { ) {
val npubDisplay by remember { derivedStateOf { baseUser.pubkeyDisplayHex() } }
val userMetadata by baseUser.live().userMetadataInfo.observeAsState(baseUser.info) val userMetadata by baseUser.live().userMetadataInfo.observeAsState(baseUser.info)
Crossfade(targetState = userMetadata, modifier = weight, label = "UsernameDisplay") { Crossfade(targetState = userMetadata, modifier = weight, label = "UsernameDisplay") {
if (it != null) { val name = it?.bestDisplayName() ?: it?.bestUsername()
UserNameDisplay( if (name != null) {
it.bestUsername(), UserDisplay(name, it?.tags, weight, showPlayButton, fontWeight, textColor)
it.bestDisplayName(),
npubDisplay,
it.tags,
weight,
showPlayButton,
fontWeight,
textColor,
)
} else { } else {
NPubDisplay(npubDisplay, weight, fontWeight, textColor) NPubDisplay(baseUser, weight, fontWeight, textColor)
} }
} }
} }
@Composable @Composable
private fun UserNameDisplay( private fun NPubDisplay(
bestUserName: String?, user: User,
bestDisplayName: String?,
npubDisplay: String,
tags: ImmutableListOfLists<String>?,
modifier: Modifier,
showPlayButton: Boolean = true,
fontWeight: FontWeight = FontWeight.Bold,
textColor: Color = Color.Unspecified,
) {
if (bestUserName != null && bestDisplayName != null && bestDisplayName != bestUserName) {
UserAndUsernameDisplay(
bestDisplayName.trim(),
tags,
bestUserName.trim(),
modifier,
showPlayButton,
fontWeight,
textColor,
)
} else if (bestDisplayName != null) {
UserDisplay(bestDisplayName.trim(), tags, modifier, showPlayButton, fontWeight, textColor)
} else if (bestUserName != null) {
UserDisplay(bestUserName.trim(), tags, modifier, showPlayButton, fontWeight, textColor)
} else {
NPubDisplay(npubDisplay, modifier, fontWeight, textColor)
}
}
@Composable
fun NPubDisplay(
npubDisplay: String,
modifier: Modifier, modifier: Modifier,
fontWeight: FontWeight = FontWeight.Bold, fontWeight: FontWeight = FontWeight.Bold,
textColor: Color = Color.Unspecified, textColor: Color = Color.Unspecified,
) { ) {
Text( Text(
text = npubDisplay, text = remember { user.pubkeyDisplayHex() },
fontWeight = fontWeight, fontWeight = fontWeight,
modifier = modifier, modifier = modifier,
maxLines = 1, maxLines = 1,
@@ -160,41 +119,7 @@ private fun UserDisplay(
modifier = modifier, modifier = modifier,
color = textColor, color = textColor,
) )
if (showPlayButton) {
Spacer(StdHorzSpacer)
DrawPlayName(bestDisplayName)
}
}
}
@Composable
private fun UserAndUsernameDisplay(
bestDisplayName: String,
tags: ImmutableListOfLists<String>?,
bestUserName: String,
modifier: Modifier,
showPlayButton: Boolean = true,
fontWeight: FontWeight = FontWeight.Bold,
textColor: Color = Color.Unspecified,
) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
CreateTextWithEmoji(
text = bestDisplayName,
tags = tags,
fontWeight = fontWeight,
maxLines = 1,
modifier = modifier,
color = textColor,
)
/*
CreateTextWithEmoji(
text = remember { "@$bestUserName" },
tags = tags,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)*/
if (showPlayButton) { if (showPlayButton) {
Spacer(StdHorzSpacer) Spacer(StdHorzSpacer)
DrawPlayName(bestDisplayName) DrawPlayName(bestDisplayName)
@@ -207,12 +132,7 @@ fun DrawPlayName(name: String) {
val context = LocalContext.current val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
DrawPlayNameIcon { speak(name, context, lifecycleOwner) } IconButton(onClick = { speak(name, context, lifecycleOwner) }, modifier = StdButtonSizeModifier) {
}
@Composable
fun DrawPlayNameIcon(onClick: () -> Unit) {
IconButton(onClick = onClick, modifier = StdButtonSizeModifier) {
PlayIcon( PlayIcon(
modifier = StdButtonSizeModifier, modifier = StdButtonSizeModifier,
tint = MaterialTheme.colorScheme.placeholderText, tint = MaterialTheme.colorScheme.placeholderText,
@@ -37,8 +37,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable @Composable
fun DisplayFollowingHashtagsInPost( fun DisplayFollowingHashtagsInPost(
@@ -50,13 +48,11 @@ fun DisplayFollowingHashtagsInPost(
var firstTag by remember(baseNote) { mutableStateOf<String?>(null) } var firstTag by remember(baseNote) { mutableStateOf<String?>(null) }
LaunchedEffect(key1 = userFollowState) { LaunchedEffect(key1 = userFollowState) {
launch(Dispatchers.Default) { val followingTags = userFollowState?.user?.cachedFollowingTagSet() ?: emptySet()
val followingTags = userFollowState?.user?.cachedFollowingTagSet() ?: emptySet() val newFirstTag = baseNote.event?.firstIsTaggedHashes(followingTags)
val newFirstTag = baseNote.event?.firstIsTaggedHashes(followingTags)
if (firstTag != newFirstTag) { if (firstTag != newFirstTag) {
launch(Dispatchers.Main) { firstTag = newFirstTag } firstTag = newFirstTag
}
} }
} }
@@ -429,25 +429,50 @@ class UserMetadata {
} }
fun lnAddress(): String? { fun lnAddress(): String? {
return (lud16?.trim() ?: lud06?.trim())?.ifBlank { null } return lud16 ?: lud06
} }
fun bestUsername(): String? { fun bestUsername(): String? {
return name?.ifBlank { null } ?: username?.ifBlank { null } return name ?: username
} }
fun bestDisplayName(): String? { fun bestDisplayName(): String? {
return displayName?.ifBlank { null } return displayName
} }
fun nip05(): String? { fun nip05(): String? {
return nip05?.ifBlank { null } return nip05
} }
fun profilePicture(): String? { fun profilePicture(): String? {
if (picture.isNullOrBlank()) picture = null
return picture return picture
} }
fun cleanBlankNames() {
if (picture?.isNotEmpty() == true) picture = picture?.trim()
if (nip05?.isNotEmpty() == true) nip05 = nip05?.trim()
if (displayName?.isNotEmpty() == true) displayName = displayName?.trim()
if (name?.isNotEmpty() == true) name = name?.trim()
if (username?.isNotEmpty() == true) username = username?.trim()
if (lud06?.isNotEmpty() == true) lud06 = lud06?.trim()
if (lud16?.isNotEmpty() == true) lud16 = lud16?.trim()
if (banner?.isNotEmpty() == true) banner = banner?.trim()
if (website?.isNotEmpty() == true) website = website?.trim()
if (domain?.isNotEmpty() == true) domain = domain?.trim()
if (picture?.isBlank() == true) picture = null
if (nip05?.isBlank() == true) nip05 = null
if (displayName?.isBlank() == true) displayName = null
if (name?.isBlank() == true) name = null
if (username?.isBlank() == true) username = null
if (lud06?.isBlank() == true) lud06 = null
if (lud16?.isBlank() == true) lud16 = null
if (banner?.isBlank() == true) banner = null
if (website?.isBlank() == true) website = null
if (domain?.isBlank() == true) domain = null
}
} }
@Stable class ImmutableListOfLists<T>(val lists: Array<Array<T>>) @Stable class ImmutableListOfLists<T>(val lists: Array<Array<T>>)