Improves rendering of chat messages when scrolling.
This commit is contained in:
@@ -502,35 +502,49 @@ private fun StatusRow(
|
|||||||
onWantsToReply: (Note) -> Unit
|
onWantsToReply: (Note) -> Unit
|
||||||
) {
|
) {
|
||||||
val grayTint = MaterialTheme.colors.placeholderText
|
val grayTint = MaterialTheme.colors.placeholderText
|
||||||
val time = remember { baseNote.createdAt() ?: 0 }
|
|
||||||
|
|
||||||
|
Column() {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
ChatTimeAgo(time)
|
ChatTimeAgo(baseNote)
|
||||||
RelayBadges(baseNote)
|
RelayBadges(baseNote)
|
||||||
Spacer(modifier = DoubleHorzSpacer)
|
Spacer(modifier = DoubleHorzSpacer)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column() {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
LikeReaction(baseNote, grayTint, accountViewModel)
|
LikeReaction(baseNote, grayTint, accountViewModel)
|
||||||
Spacer(modifier = StdHorzSpacer)
|
Spacer(modifier = StdHorzSpacer)
|
||||||
ZapReaction(baseNote, grayTint, accountViewModel)
|
ZapReaction(baseNote, grayTint, accountViewModel)
|
||||||
Spacer(modifier = StdHorzSpacer)
|
Spacer(modifier = StdHorzSpacer)
|
||||||
ReplyReaction(baseNote, grayTint, accountViewModel, showCounter = false, iconSize = Size16dp) {
|
ReplyReaction(
|
||||||
|
baseNote,
|
||||||
|
grayTint,
|
||||||
|
accountViewModel,
|
||||||
|
showCounter = false,
|
||||||
|
iconSize = Size16dp
|
||||||
|
) {
|
||||||
onWantsToReply(baseNote)
|
onWantsToReply(baseNote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatTimeAgo(time: Long) {
|
fun ChatTimeAgo(baseNote: Note) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val timeStr = remember { timeAgoShort(time, context = context) }
|
val timeStr by remember(baseNote) {
|
||||||
|
derivedStateOf {
|
||||||
|
timeAgoShort(baseNote.createdAt() ?: 0, context = context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
timeStr,
|
text = timeStr,
|
||||||
color = MaterialTheme.colors.placeholderText,
|
color = MaterialTheme.colors.placeholderText,
|
||||||
fontSize = 12.sp
|
fontSize = 12.sp,
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,6 @@ import kotlinx.collections.immutable.toImmutableList
|
|||||||
import kotlinx.collections.immutable.toImmutableSet
|
import kotlinx.collections.immutable.toImmutableSet
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import nostr.postr.toNpub
|
import nostr.postr.toNpub
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@@ -1282,8 +1281,14 @@ fun DisplayPeopleList(
|
|||||||
members.take(3)
|
members.take(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val name by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
"#${noteEvent.dTag()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "#${noteEvent.dTag()}",
|
text = name,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
@@ -1385,7 +1390,7 @@ private fun RenderBadgeAward(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (awardees.size > 100) {
|
if (awardees.size > 100) {
|
||||||
Text(" and ${awardees.size - 100} others")
|
Text(" and ${awardees.size - 100} others", maxLines = 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1427,7 +1432,8 @@ private fun RenderReaction(
|
|||||||
if (note.event?.content() == "+") "❤" else note.event?.content() ?: ""
|
if (note.event?.content() == "+") "❤" else note.event?.content() ?: ""
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = refactorReactionText
|
text = refactorReactionText,
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1778,7 +1784,8 @@ private fun FirstUserInfoRow(
|
|||||||
Text(
|
Text(
|
||||||
" ${stringResource(id = R.string.boosted)}",
|
" ${stringResource(id = R.string.boosted)}",
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = MaterialTheme.colors.placeholderText
|
color = MaterialTheme.colors.placeholderText,
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
DisplayFollowingHashtagsInPost(eventNote, accountViewModel, nav)
|
DisplayFollowingHashtagsInPost(eventNote, accountViewModel, nav)
|
||||||
@@ -1832,7 +1839,7 @@ fun TimeAgo(time: Long) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
timeStr,
|
text = timeStr,
|
||||||
color = MaterialTheme.colors.placeholderText,
|
color = MaterialTheme.colors.placeholderText,
|
||||||
maxLines = 1
|
maxLines = 1
|
||||||
)
|
)
|
||||||
@@ -1990,37 +1997,41 @@ fun DisplayHighlight(
|
|||||||
nav
|
nav
|
||||||
)
|
)
|
||||||
|
|
||||||
var userBase by remember { mutableStateOf<User?>(null) }
|
authorHex?.let {
|
||||||
|
DisplayQuoteAuthor(authorHex, url, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
private fun DisplayQuoteAuthor(
|
||||||
|
authorHex: String,
|
||||||
|
url: String?,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
|
var userBase by remember { mutableStateOf<User?>(LocalCache.getUserIfExists(authorHex)) }
|
||||||
|
|
||||||
|
if (userBase == null) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
withContext(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
if (authorHex != null) {
|
|
||||||
userBase = LocalCache.checkGetOrCreateUser(authorHex)
|
userBase = LocalCache.checkGetOrCreateUser(authorHex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlowRow() {
|
Row() {
|
||||||
authorHex?.let { authorHex ->
|
|
||||||
userBase?.let { userBase ->
|
userBase?.let { userBase ->
|
||||||
val userState by userBase.live().metadata.observeAsState()
|
LoadAndDisplayUser(userBase, nav)
|
||||||
val route = remember { "User/${userBase.pubkeyHex}" }
|
|
||||||
val userDisplayName = remember(userState) { userState?.user?.toBestDisplayName() }
|
|
||||||
val userTags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() }
|
|
||||||
|
|
||||||
if (userDisplayName != null) {
|
|
||||||
CreateClickableTextWithEmoji(
|
|
||||||
clickablePart = userDisplayName,
|
|
||||||
suffix = " ",
|
|
||||||
tags = userTags,
|
|
||||||
route = route,
|
|
||||||
nav = nav
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
url?.let { url ->
|
url?.let { url ->
|
||||||
|
LoadAndDisplayUrl(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LoadAndDisplayUrl(url: String) {
|
||||||
val validatedUrl = remember {
|
val validatedUrl = remember {
|
||||||
try {
|
try {
|
||||||
URL(url)
|
URL(url)
|
||||||
@@ -2031,10 +2042,30 @@ fun DisplayHighlight(
|
|||||||
}
|
}
|
||||||
|
|
||||||
validatedUrl?.host?.let { host ->
|
validatedUrl?.host?.let { host ->
|
||||||
Text("on ")
|
Text(remember { "on " }, maxLines = 1)
|
||||||
ClickableUrl(urlText = host, url = url)
|
ClickableUrl(urlText = host, url = url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LoadAndDisplayUser(
|
||||||
|
userBase: User,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val userState by userBase.live().metadata.observeAsState()
|
||||||
|
val route = remember { "User/${userBase.pubkeyHex}" }
|
||||||
|
val userDisplayName = remember(userState) { userState?.user?.toBestDisplayName() }
|
||||||
|
val userTags =
|
||||||
|
remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() }
|
||||||
|
|
||||||
|
if (userDisplayName != null) {
|
||||||
|
CreateClickableTextWithEmoji(
|
||||||
|
clickablePart = userDisplayName,
|
||||||
|
suffix = " ",
|
||||||
|
tags = userTags,
|
||||||
|
route = route,
|
||||||
|
nav = nav
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2071,7 +2102,8 @@ fun DisplayFollowingHashtagsInPost(
|
|||||||
color = MaterialTheme.colors.primary.copy(
|
color = MaterialTheme.colors.primary.copy(
|
||||||
alpha = 0.52f
|
alpha = 0.52f
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2120,7 +2152,8 @@ fun DisplayPoW(
|
|||||||
alpha = 0.52f
|
alpha = 0.52f
|
||||||
),
|
),
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2214,7 +2247,8 @@ private fun RenderPledgeAmount(
|
|||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = reward,
|
text = reward,
|
||||||
color = MaterialTheme.colors.placeholderText
|
color = MaterialTheme.colors.placeholderText,
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2421,7 +2455,7 @@ fun AudioTrackHeader(noteEvent: AudioTrackEvent, accountViewModel: AccountViewMo
|
|||||||
var participantUsers by remember { mutableStateOf<List<Pair<Participant, User>>>(emptyList()) }
|
var participantUsers by remember { mutableStateOf<List<Pair<Participant, User>>>(emptyList()) }
|
||||||
|
|
||||||
LaunchedEffect(key1 = participants) {
|
LaunchedEffect(key1 = participants) {
|
||||||
withContext(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
participantUsers = participants.mapNotNull { part -> LocalCache.checkGetOrCreateUser(part.key)?.let { Pair(part, it) } }
|
participantUsers = participants.mapNotNull { part -> LocalCache.checkGetOrCreateUser(part.key)?.let { Pair(part, it) } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2925,7 +2959,7 @@ private fun FollowingIcon(iconSize: Dp) {
|
|||||||
val myIconBackgroundModifier = remember {
|
val myIconBackgroundModifier = remember {
|
||||||
Modifier
|
Modifier
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.fillMaxSize(0.6f)
|
.size(iconSize.times(0.6f))
|
||||||
.drawBehind { drawRect(backgroundColor) }
|
.drawBehind { drawRect(backgroundColor) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ private fun UserNameDisplay(
|
|||||||
CreateTextWithEmoji(
|
CreateTextWithEmoji(
|
||||||
text = bestDisplayName,
|
text = bestDisplayName,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
CreateTextWithEmoji(
|
CreateTextWithEmoji(
|
||||||
text = remember { "@$bestUserName" },
|
text = remember { "@$bestUserName" },
|
||||||
|
|||||||
@@ -656,43 +656,47 @@ private fun LiveChannelActionOptions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val isLive by remember(channel) {
|
||||||
val isFollowing by remember(accountState) {
|
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
accountState?.account?.followingChannels?.contains(channel.idHex) ?: false
|
channel.info?.status() == "live"
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val status by remember {
|
|
||||||
derivedStateOf {
|
|
||||||
channel.info?.status()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMe) {
|
if (isMe) {
|
||||||
// EditButton(accountViewModel, channel)
|
// EditButton(accountViewModel, channel)
|
||||||
} else {
|
} else {
|
||||||
LocalCache.addressables[channel.idHex]?.let {
|
val note = remember(channel.idHex) {
|
||||||
if (status == "live") {
|
LocalCache.getNoteIfExists(channel.idHex)
|
||||||
Text(
|
}
|
||||||
text = "LIVE",
|
|
||||||
color = Color.White,
|
note?.let {
|
||||||
fontWeight = FontWeight.Bold,
|
if (isLive) {
|
||||||
modifier = Modifier
|
LiveFlag()
|
||||||
.clip(SmallBorder)
|
|
||||||
.drawBehind { drawRect(Color.Red) }
|
|
||||||
.padding(horizontal = 5.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = StdHorzSpacer)
|
Spacer(modifier = StdHorzSpacer)
|
||||||
}
|
}
|
||||||
|
|
||||||
LikeReaction(it, MaterialTheme.colors.onSurface, accountViewModel)
|
LikeReaction(baseNote = it, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel)
|
||||||
Spacer(modifier = StdHorzSpacer)
|
Spacer(modifier = StdHorzSpacer)
|
||||||
ZapReaction(baseNote = it, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel)
|
ZapReaction(baseNote = it, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LiveFlag() {
|
||||||
|
Text(
|
||||||
|
text = "LIVE",
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = remember {
|
||||||
|
Modifier
|
||||||
|
.clip(SmallBorder)
|
||||||
|
.drawBehind { drawRect(Color.Red) }
|
||||||
|
.padding(horizontal = 5.dp)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NoteCopyButton(
|
private fun NoteCopyButton(
|
||||||
note: Channel
|
note: Channel
|
||||||
|
|||||||
Reference in New Issue
Block a user