Moving clickable to Row and not AsyncImage because it's faster

This commit is contained in:
Vitor Pamplona
2023-05-06 19:19:57 -04:00
parent fdca0af97b
commit bba45dd0a7
3 changed files with 162 additions and 110 deletions
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.note
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -54,7 +55,6 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
val accountState by accountViewModel.accountLiveData.observeAsState() val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return val account = accountState?.account ?: return
val noteEvent = note?.event
var popupExpanded by remember { mutableStateOf(false) } var popupExpanded by remember { mutableStateOf(false) }
if (note == null) { if (note == null) {
@@ -160,12 +160,7 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
} }
Row(Modifier.fillMaxWidth()) { Row(Modifier.fillMaxWidth()) {
Box( Spacer(modifier = Modifier.width(65.dp))
modifier = Modifier
.width(65.dp)
.padding(0.dp)
) {
}
NoteCompose( NoteCompose(
baseNote = multiSetCard.note, baseNote = multiSetCard.note,
@@ -216,6 +211,7 @@ private fun AuthorPictureAndComment(
var content by remember { mutableStateOf<Pair<User, String?>>(Pair(author, null)) } var content by remember { mutableStateOf<Pair<User, String?>>(Pair(author, null)) }
LaunchedEffect(key1 = zapRequest.idHex) { LaunchedEffect(key1 = zapRequest.idHex) {
withContext(Dispatchers.IO) {
(zapRequest.event as? LnZapRequestEvent)?.let { (zapRequest.event as? LnZapRequestEvent)?.let {
val decryptedContent = accountViewModel.decryptZap(zapRequest) val decryptedContent = accountViewModel.decryptZap(zapRequest)
if (decryptedContent != null) { if (decryptedContent != null) {
@@ -228,6 +224,7 @@ private fun AuthorPictureAndComment(
} }
} }
} }
}
AuthorPictureAndComment(content.first, content.second, navController, accountUser, accountViewModel) AuthorPictureAndComment(content.first, content.second, navController, accountUser, accountViewModel)
} }
@@ -246,10 +243,14 @@ private fun AuthorPictureAndComment(
Modifier Modifier
} }
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { Row(
modifier = modifier.clickable {
navController.navigate("User/${author.pubkeyHex}")
},
verticalAlignment = Alignment.CenterVertically
) {
FastNoteAuthorPicture( FastNoteAuthorPicture(
author = author, author = author,
navController = navController,
userAccount = accountUser, userAccount = accountUser,
size = 35.dp size = 35.dp
) )
@@ -294,7 +295,6 @@ fun AuthorGallery(
@Composable @Composable
fun FastNoteAuthorPicture( fun FastNoteAuthorPicture(
author: User, author: User,
navController: NavController,
userAccount: User, userAccount: User,
size: Dp, size: Dp,
pictureModifier: Modifier = Modifier pictureModifier: Modifier = Modifier
@@ -303,14 +303,12 @@ fun FastNoteAuthorPicture(
val user = userState?.user ?: return val user = userState?.user ?: return
val showFollowingMark = userAccount.isFollowingCached(user) || user === userAccount val showFollowingMark = userAccount.isFollowingCached(user) || user === userAccount
UserPicture( UserPicture(
userHex = user.pubkeyHex, userHex = user.pubkeyHex,
userPicture = user.profilePicture(), userPicture = user.profilePicture(),
showFollowingMark = showFollowingMark, showFollowingMark = showFollowingMark,
size = size, size = size,
modifier = pictureModifier, modifier = pictureModifier
onClick = {
navController.navigate("User/${user.pubkeyHex}")
}
) )
} }
@@ -42,6 +42,7 @@ import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.get import androidx.core.graphics.get
import androidx.navigation.NavController import androidx.navigation.NavController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
@@ -99,7 +100,7 @@ fun NoteCompose(
Log.d("Time", "Note Compose in $elapsed for ${baseNote.idHex} ${baseNote.event?.kind()} ${baseNote.event?.content()?.split("\n")?.get(0)?.take(100)}") Log.d("Time", "Note Compose in $elapsed for ${baseNote.idHex} ${baseNote.event?.kind()} ${baseNote.event?.content()?.split("\n")?.get(0)?.take(100)}")
} }
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class, ExperimentalTime::class)
@Composable @Composable
fun NoteComposeInner( fun NoteComposeInner(
baseNote: Note, baseNote: Note,
@@ -199,12 +200,14 @@ fun NoteComposeInner(
} }
Column( Column(
modifier = modifier.combinedClickable( modifier = modifier
.combinedClickable(
onClick = { onClick = {
routeFor(note, loggedIn)?.let { navController.navigate(it) } routeFor(note, loggedIn)?.let { navController.navigate(it) }
}, },
onLongClick = { popupExpanded = true } onLongClick = { popupExpanded = true }
).background(backgroundColor) )
.background(backgroundColor)
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -222,6 +225,7 @@ fun NoteComposeInner(
modifier = Modifier modifier = Modifier
.padding(start = if (!isBoostedNote && !isQuotedNote) 10.dp else 0.dp) .padding(start = if (!isBoostedNote && !isQuotedNote) 10.dp else 0.dp)
) { ) {
val (value, elapsed1) = measureTimedValue {
FirstUserInfoRow( FirstUserInfoRow(
baseNote = baseNote, baseNote = baseNote,
showAuthorPicture = isQuotedNote, showAuthorPicture = isQuotedNote,
@@ -229,14 +233,19 @@ fun NoteComposeInner(
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
navController = navController navController = navController
) )
}
Log.d("Time", "$elapsed1 Line 1 of each post ${noteEvent.content()}")
if (noteEvent !is RepostEvent && !makeItShort && !isQuotedNote) { if (noteEvent !is RepostEvent && !makeItShort && !isQuotedNote) {
val (value, elapsed2) = measureTimedValue {
SecondUserInfoRow( SecondUserInfoRow(
note, note,
account, account,
navController navController
) )
} }
Log.d("Time", "$elapsed2 Line 2 of each post")
}
Spacer(modifier = Modifier.height(2.dp)) Spacer(modifier = Modifier.height(2.dp))
@@ -543,21 +552,33 @@ private fun RenderBadgeAward(
if (note.replyTo.isNullOrEmpty()) return if (note.replyTo.isNullOrEmpty()) return
val noteEvent = note.event as? BadgeAwardEvent ?: return val noteEvent = note.event as? BadgeAwardEvent ?: return
var awardees by remember { mutableStateOf<List<User>>(listOf()) }
Text(text = stringResource(R.string.award_granted_to)) Text(text = stringResource(R.string.award_granted_to))
/*
LaunchedEffect(key1 = note) {
withContext(Dispatchers.IO) {
awardees = noteEvent.awardees().mapNotNull { hex ->
LocalCache.checkGetOrCreateUser(hex)
}
}
}
FlowRow(modifier = Modifier.padding(top = 5.dp)) { FlowRow(modifier = Modifier.padding(top = 5.dp)) {
noteEvent.awardees() awardees.forEach { user ->
.map { LocalCache.getOrCreateUser(it) } Row(modifier = Modifier.clickable {
.forEach { navController.navigate("User/${user.pubkeyHex}")
},
verticalAlignment = Alignment.CenterVertically
) {
UserPicture( UserPicture(
user = it, baseUser = user,
navController = navController, baseUserAccount = accountViewModel.userProfile(),
userAccount = accountViewModel.userProfile(),
size = 35.dp size = 35.dp
) )
} }
} }
}
note.replyTo?.firstOrNull()?.let { note.replyTo?.firstOrNull()?.let {
NoteCompose( NoteCompose(
@@ -570,7 +591,7 @@ private fun RenderBadgeAward(
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
navController = navController navController = navController
) )
} }*/
ReactionsRow(note, accountViewModel, navController) ReactionsRow(note, accountViewModel, navController)
@@ -762,34 +783,30 @@ private fun FirstUserInfoRow(
navController: NavController navController: NavController
) { ) {
var moreActionsExpanded by remember { mutableStateOf(false) } var moreActionsExpanded by remember { mutableStateOf(false) }
val context = LocalContext.current.applicationContext val eventNote = baseNote.event ?: return
val time = baseNote.createdAt() ?: return
val loggedIn = account.userProfile()
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
if (showAuthorPicture) { if (showAuthorPicture) {
NoteAuthorPicture(baseNote, navController, account.userProfile(), 25.dp) NoteAuthorPicture(baseNote, navController, loggedIn, 25.dp)
Spacer(Modifier.padding(horizontal = 5.dp)) Spacer(Modifier.padding(horizontal = 5.dp))
NoteUsernameDisplay(baseNote, Modifier.weight(1f)) NoteUsernameDisplay(baseNote, Modifier.weight(1f))
} else { } else {
NoteUsernameDisplay(baseNote, Modifier.weight(1f)) NoteUsernameDisplay(baseNote, Modifier.weight(1f))
} }
if (baseNote.event is RepostEvent) { if (eventNote is RepostEvent) {
Text( Text(
" ${stringResource(id = R.string.boosted)}", " ${stringResource(id = R.string.boosted)}",
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
) )
} else { } else {
baseNote.event?.let { DisplayFollowingHashtagsInPost(eventNote, account, navController)
DisplayFollowingHashtagsInPost(it, account, navController)
}
} }
Text( TimeAgo(time)
timeAgo(baseNote.createdAt(), context = context),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
maxLines = 1
)
IconButton( IconButton(
modifier = Modifier.size(24.dp), modifier = Modifier.size(24.dp),
@@ -812,6 +829,25 @@ private fun FirstUserInfoRow(
} }
} }
@Composable
fun TimeAgo(time: Long) {
val context = LocalContext.current
var timeStr by remember { mutableStateOf("") }
LaunchedEffect(key1 = time) {
withContext(Dispatchers.IO) {
timeStr = timeAgo(time, context = context)
}
}
Text(
timeStr,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
maxLines = 1
)
}
@Composable @Composable
private fun DrawAuthorImages(baseNote: Note, loggedIn: User, navController: NavController) { private fun DrawAuthorImages(baseNote: Note, loggedIn: User, navController: NavController) {
val baseChannel = baseNote.channel() val baseChannel = baseNote.channel()
@@ -963,7 +999,7 @@ fun DisplayFollowingHashtagsInPost(
) { ) {
var firstTag by remember { mutableStateOf<String?>(null) } var firstTag by remember { mutableStateOf<String?>(null) }
LaunchedEffect(key1 = noteEvent) { LaunchedEffect(key1 = noteEvent.id()) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
firstTag = noteEvent.firstIsTaggedHashes(account.followingTagSet()) firstTag = noteEvent.firstIsTaggedHashes(account.followingTagSet())
} }
@@ -1102,7 +1138,24 @@ fun DisplayReward(
fun BadgeDisplay(baseNote: Note) { fun BadgeDisplay(baseNote: Note) {
val background = MaterialTheme.colors.background val background = MaterialTheme.colors.background
val badgeData = baseNote.event as? BadgeDefinitionEvent ?: return val badgeData = baseNote.event as? BadgeDefinitionEvent ?: return
var backgroundFromImage by remember { mutableStateOf(background) }
val image = badgeData.image()
val name = badgeData.name()
val description = badgeData.description()
var backgroundFromImage by remember { mutableStateOf(Pair(background, background)) }
var imageResult by remember { mutableStateOf<AsyncImagePainter.State.Success?>(null) }
LaunchedEffect(key1 = imageResult) {
withContext(Dispatchers.IO) {
imageResult?.let {
val backgroundColor = it.result.drawable.toBitmap(200, 200).copy(Bitmap.Config.ARGB_8888, false).get(0, 199)
val colorFromImage = Color(backgroundColor)
val textBackground = if (colorFromImage.luminance() > 0.5) lightColors().onBackground else darkColors().onBackground
backgroundFromImage = Pair(colorFromImage, textBackground)
}
}
}
Row( Row(
modifier = Modifier modifier = Modifier
@@ -1113,26 +1166,25 @@ fun BadgeDisplay(baseNote: Note) {
MaterialTheme.colors.primary.copy(alpha = 0.32f), MaterialTheme.colors.primary.copy(alpha = 0.32f),
CutCornerShape(20) CutCornerShape(20)
) )
.background(backgroundFromImage) .background(backgroundFromImage.first)
) { ) {
Column { Column {
badgeData.image()?.let { image.let {
AsyncImage( AsyncImage(
model = it, model = it,
contentDescription = stringResource( contentDescription = stringResource(
R.string.badge_award_image_for, R.string.badge_award_image_for,
it name ?: ""
), ),
contentScale = ContentScale.FillWidth, contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onSuccess = { onSuccess = {
val backgroundColor = it.result.drawable.toBitmap(200, 200).copy(Bitmap.Config.ARGB_8888, false).get(0, 199) imageResult = it
backgroundFromImage = Color(backgroundColor)
} }
) )
} }
badgeData.name()?.let { name?.let {
Text( Text(
text = it, text = it,
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.body1,
@@ -1140,11 +1192,11 @@ fun BadgeDisplay(baseNote: Note) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = 10.dp, end = 10.dp), .padding(start = 10.dp, end = 10.dp),
color = if (backgroundFromImage.luminance() > 0.5) lightColors().onBackground else darkColors().onBackground color = backgroundFromImage.second
) )
} }
badgeData.description()?.let { description?.let {
Text( Text(
text = it, text = it,
style = MaterialTheme.typography.caption, style = MaterialTheme.typography.caption,
@@ -1499,6 +1551,7 @@ fun UserPicture(
} }
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun UserPicture( fun UserPicture(
baseUser: User, baseUser: User,
@@ -1516,27 +1569,38 @@ fun UserPicture(
val showFollowingMark = accountUser.isFollowingCached(user) || user == accountUser val showFollowingMark = accountUser.isFollowingCached(user) || user == accountUser
Row(
modifier = Modifier
.run {
if (onClick != null && onLongClick != null) {
this.combinedClickable(
onClick = { onClick(user) },
onLongClick = { onLongClick(user) }
)
} else if (onClick != null) {
this.clickable(onClick = { onClick(user) })
} else {
this
}
}
) {
UserPicture( UserPicture(
userHex = user.pubkeyHex, userHex = user.pubkeyHex,
userPicture = user.profilePicture(), userPicture = user.profilePicture(),
showFollowingMark = showFollowingMark, showFollowingMark = showFollowingMark,
size = size, size = size,
modifier = modifier, modifier = modifier
onClick = onClick?.let { { it(user) } },
onLongClick = onLongClick?.let { { it(user) } }
) )
}
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun UserPicture( fun UserPicture(
userHex: String, userHex: String,
userPicture: String?, userPicture: String?,
showFollowingMark: Boolean, showFollowingMark: Boolean,
size: Dp, size: Dp,
modifier: Modifier = Modifier, modifier: Modifier = Modifier
onClick: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null
) { ) {
Box( Box(
Modifier Modifier
@@ -1552,16 +1616,6 @@ fun UserPicture(
.height(size) .height(size)
.clip(shape = CircleShape) .clip(shape = CircleShape)
.background(MaterialTheme.colors.background) .background(MaterialTheme.colors.background)
.run {
if (onClick != null && onLongClick != null) {
this.combinedClickable(onClick = onClick, onLongClick = onLongClick)
} else if (onClick != null) {
this.clickable(onClick = onClick)
} else {
this
}
}
) )
if (showFollowingMark) { if (showFollowingMark) {
@@ -175,7 +175,7 @@ private fun FeedLoaded(
) )
} }
} }
Log.d("Time", "${item.javaClass.simpleName} Feed in $elapsed") Log.d("Time", "${item.javaClass.simpleName} Feed in $elapsed ${item.id()}")
} }
} }
} }