Minor updates to remember states in composition

This commit is contained in:
Vitor Pamplona
2023-06-07 12:26:32 -04:00
parent eaae672a30
commit 6a7d6a843d
4 changed files with 148 additions and 100 deletions

View File

@ -143,16 +143,36 @@ private fun RenderRegular(
} }
} }
val paragraphs = remember(content) {
content.split('\n').toImmutableList()
}
// FlowRow doesn't work well with paragraphs. So we need to split them // FlowRow doesn't work well with paragraphs. So we need to split them
content.split('\n').forEach { paragraph -> paragraphs.forEach { paragraph ->
FlowRow() { RenderParagraph(paragraph, state, canPreview, backgroundColor, accountViewModel, nav, tags)
}
}
@Composable
@OptIn(ExperimentalLayoutApi::class)
private fun RenderParagraph(
paragraph: String,
state: RichTextViewerState,
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
tags: ImmutableListOfLists<String>
) {
val s = remember(paragraph) { val s = remember(paragraph) {
if (isArabic(paragraph)) { if (isArabic(paragraph)) {
paragraph.trim().split(' ').reversed() paragraph.trim().split(' ').reversed().toImmutableList()
} else { } else {
paragraph.trim().split(' ') paragraph.trim().split(' ').toImmutableList()
} }
} }
FlowRow() {
s.forEach { word: String -> s.forEach { word: String ->
RenderWord( RenderWord(
word, word,
@ -165,7 +185,6 @@ private fun RenderRegular(
) )
} }
} }
}
} }
private fun parseUrls( private fun parseUrls(
@ -863,13 +882,14 @@ fun HashTag(word: String, nav: (String) -> Unit) {
} ?: Text(text = "$word ") } ?: Text(text = "$word ")
} }
data class LoadedTag(val user: User?, val note: Note?, val addedChars: String)
@Composable @Composable
fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, nav: (String) -> Unit) { fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var baseUserPair by remember { mutableStateOf<Pair<User, String?>?>(null) } var loadedTag by remember { mutableStateOf<LoadedTag?>(null) }
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
LaunchedEffect(key1 = word) { LaunchedEffect(key1 = word) {
if (baseUserPair == null && baseNotePair == null) { if (loadedTag == null) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
val matcher = tagIndex.matcher(word) val matcher = tagIndex.matcher(word)
val (index, suffix) = try { val (index, suffix) = try {
@ -877,7 +897,7 @@ fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolea
Pair(matcher.group(1)?.toInt(), matcher.group(2) ?: "") Pair(matcher.group(1)?.toInt(), matcher.group(2) ?: "")
} catch (e: Exception) { } catch (e: Exception) {
Log.w("Tag Parser", "Couldn't link tag $word", e) Log.w("Tag Parser", "Couldn't link tag $word", e)
Pair(null, null) Pair(null, "")
} }
if (index != null && index >= 0 && index < tags.lists.size) { if (index != null && index >= 0 && index < tags.lists.size) {
@ -886,11 +906,11 @@ fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolea
if (tag.size > 1) { if (tag.size > 1) {
if (tag[0] == "p") { if (tag[0] == "p") {
LocalCache.checkGetOrCreateUser(tag[1])?.let { LocalCache.checkGetOrCreateUser(tag[1])?.let {
baseUserPair = Pair(it, suffix) loadedTag = LoadedTag(it, null, suffix)
} }
} else if (tag[0] == "e" || tag[0] == "a") { } else if (tag[0] == "e" || tag[0] == "a") {
LocalCache.checkGetOrCreateNote(tag[1])?.let { LocalCache.checkGetOrCreateNote(tag[1])?.let {
baseNotePair = Pair(it, suffix) loadedTag = LoadedTag(null, it, suffix)
} }
} }
} }
@ -899,31 +919,35 @@ fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolea
} }
} }
baseUserPair?.let { if (loadedTag == null) {
val innerUserState by it.first.live().metadata.observeAsState() Text(
val displayName = remember(innerUserState) { text = remember {
innerUserState?.user?.toBestDisplayName() ?: "" "$word "
} }
val route = remember(innerUserState) {
"User/${it.first.pubkeyHex}"
}
val userTags = remember(innerUserState) {
innerUserState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists()
}
CreateClickableTextWithEmoji(
clickablePart = displayName,
suffix = remember { "${it.second} " },
tags = userTags,
route = route,
nav = nav
) )
} else {
loadedTag?.user?.let {
DisplayUserFromTag(it, loadedTag?.addedChars ?: "", nav)
} }
baseNotePair?.let { loadedTag?.note?.let {
DisplayNoteFromTag(it, loadedTag?.addedChars ?: "", canPreview, accountViewModel, backgroundColor, nav)
}
}
}
@Composable
private fun DisplayNoteFromTag(
baseNote: Note,
addedChars: String,
canPreview: Boolean,
accountViewModel: AccountViewModel,
backgroundColor: Color,
nav: (String) -> Unit
) {
if (canPreview) { if (canPreview) {
NoteCompose( NoteCompose(
baseNote = it.first, baseNote = baseNote,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
modifier = Modifier modifier = Modifier
.padding(top = 2.dp, bottom = 0.dp, start = 0.dp, end = 0.dp) .padding(top = 2.dp, bottom = 0.dp, start = 0.dp, end = 0.dp)
@ -938,16 +962,37 @@ fun TagLink(word: String, tags: ImmutableListOfLists<String>, canPreview: Boolea
isQuotedNote = true, isQuotedNote = true,
nav = nav nav = nav
) )
it.second?.ifBlank { null }?.let { addedChars.ifBlank { null }?.let {
Text(text = "$it ") Text(text = remember { "$it " })
} }
} else { } else {
ClickableNoteTag(it.first, nav) ClickableNoteTag(baseNote, nav)
Text(text = "${it.second} ") Text(text = remember { "$addedChars " })
}
}
if (baseNotePair == null && baseUserPair == null) {
Text(text = "$word ")
} }
} }
@Composable
private fun DisplayUserFromTag(
baseUser: User,
addedChars: String,
nav: (String) -> Unit
) {
val innerUserState by baseUser.live().metadata.observeAsState()
val displayName = remember(innerUserState) {
innerUserState?.user?.toBestDisplayName() ?: ""
}
val route = remember(innerUserState) {
"User/${baseUser.pubkeyHex}"
}
val userTags = remember(innerUserState) {
innerUserState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists()
}
CreateClickableTextWithEmoji(
clickablePart = displayName,
suffix = remember { "$addedChars " },
tags = userTags,
route = route,
nav = nav
)
}

View File

@ -39,7 +39,7 @@ abstract class AdditiveFeedFilter<T> : FeedFilter<T>() {
} }
} }
Log.d("Time", "${this.javaClass.simpleName} Additive Feed in $elapsed with ${feed.size} objects") // Log.d("Time", "${this.javaClass.simpleName} Additive Feed in $elapsed with ${feed.size} objects")
return feed return feed
} }
} }

View File

@ -65,12 +65,10 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
val baseNote = remember { multiSetCard.note } val baseNote = remember { multiSetCard.note }
var popupExpanded by remember { mutableStateOf(false) } var popupExpanded by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var isNew by remember { mutableStateOf(false) } var isNew by remember { mutableStateOf(false) }
LaunchedEffect(key1 = multiSetCard.createdAt()) { LaunchedEffect(key1 = multiSetCard) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
val newIsNew = multiSetCard.maxCreatedAt > NotificationCache.load(routeForLastRead) val newIsNew = multiSetCard.maxCreatedAt > NotificationCache.load(routeForLastRead)
@ -85,11 +83,15 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
val primaryColor = MaterialTheme.colors.newItemBackgroundColor val primaryColor = MaterialTheme.colors.newItemBackgroundColor
val defaultBackgroundColor = MaterialTheme.colors.background val defaultBackgroundColor = MaterialTheme.colors.background
val backgroundColor = if (isNew) { val backgroundColor by remember(isNew) {
derivedStateOf {
if (isNew) {
primaryColor.compositeOver(defaultBackgroundColor) primaryColor.compositeOver(defaultBackgroundColor)
} else { } else {
defaultBackgroundColor defaultBackgroundColor
} }
}
}
val columnModifier = Modifier val columnModifier = Modifier
.background(backgroundColor) .background(backgroundColor)
@ -332,7 +334,7 @@ private fun AuthorPictureAndComment(
Box(modifier = remember { Modifier.size(35.dp) }, contentAlignment = Alignment.BottomCenter) { Box(modifier = remember { Modifier.size(35.dp) }, contentAlignment = Alignment.BottomCenter) {
FastNoteAuthorPicture( FastNoteAuthorPicture(
author = author, author = author,
size = 35.dp, size = remember { 35.dp },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
pictureModifier = authorPictureModifier pictureModifier = authorPictureModifier
) )
@ -380,10 +382,10 @@ fun AuthorGallery(
nav: (String) -> Unit, nav: (String) -> Unit,
accountViewModel: AccountViewModel accountViewModel: AccountViewModel
) { ) {
Column(modifier = Modifier.padding(start = 10.dp)) { Column(modifier = remember { Modifier.padding(start = 10.dp) }) {
FlowRow() { FlowRow() {
authorNotes.forEach { note -> authorNotes.forEach { note ->
Box(Modifier.size(35.dp)) { Box(remember { Modifier.size(35.dp) }) {
NotePictureAndComment(note, backgroundColor, nav, accountViewModel) NotePictureAndComment(note, backgroundColor, nav, accountViewModel)
} }
} }

View File

@ -154,7 +154,6 @@ import java.io.File
import java.math.BigDecimal import java.math.BigDecimal
import java.net.URL import java.net.URL
import java.util.Locale import java.util.Locale
import kotlin.time.ExperimentalTime
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@ -331,20 +330,21 @@ private fun WatchForReports(
onChange: (Boolean, Boolean, Set<Note>) -> Unit onChange: (Boolean, Boolean, Set<Note>) -> Unit
) { ) {
val accountState by accountViewModel.accountLiveData.observeAsState() val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account } ?: return
val noteReportsState by note.live().reports.observeAsState() val noteReportsState by note.live().reports.observeAsState()
val noteForReports = remember(noteReportsState) { noteReportsState?.note } ?: return
LaunchedEffect(key1 = noteReportsState, key2 = accountState) { LaunchedEffect(key1 = noteReportsState, key2 = accountState) {
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
account.userProfile().let { loggedIn -> accountState?.account?.let { loggedIn ->
val newCanPreview = note.author?.pubkeyHex == loggedIn.pubkeyHex || val newCanPreview = note.author?.pubkeyHex == loggedIn.userProfile().pubkeyHex ||
(note.author?.let { loggedIn.isFollowingCached(it) } ?: true) || (note.author?.let { loggedIn.userProfile().isFollowingCached(it) } ?: true) ||
!(noteForReports.hasAnyReports()) noteReportsState?.note?.hasAnyReports() != true
val newIsAcceptable = account.isAcceptable(noteForReports) val newIsAcceptable = noteReportsState?.note?.let {
val newRelevantReports = account.getRelevantReports(noteForReports) loggedIn.isAcceptable(it)
} ?: true
val newRelevantReports = noteReportsState?.note?.let {
loggedIn.getRelevantReports(it)
} ?: emptySet()
onChange(newIsAcceptable, newCanPreview, newRelevantReports) onChange(newIsAcceptable, newCanPreview, newRelevantReports)
} }
@ -1489,11 +1489,11 @@ private fun FirstUserInfoRow(
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
if (showAuthorPicture) { if (showAuthorPicture) {
NoteAuthorPicture(baseNote, nav, accountViewModel, 25.dp) NoteAuthorPicture(baseNote, nav, accountViewModel, remember { 25.dp })
Spacer(padding) Spacer(padding)
NoteUsernameDisplay(baseNote, Modifier.weight(1f)) NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) })
} else { } else {
NoteUsernameDisplay(baseNote, Modifier.weight(1f)) NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) })
} }
if (eventNote is RepostEvent) { if (eventNote is RepostEvent) {
@ -1560,7 +1560,6 @@ fun TimeAgo(time: Long) {
) )
} }
@OptIn(ExperimentalTime::class)
@Composable @Composable
private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val baseChannelHex = remember { baseNote.channelHex() } val baseChannelHex = remember { baseNote.channelHex() }
@ -1670,7 +1669,7 @@ private fun RepostNoteAuthorPicture(
baseNote = it, baseNote = it,
nav = nav, nav = nav,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
size = 30.dp, size = remember { 30.dp },
pictureModifier = Modifier.border( pictureModifier = Modifier.border(
2.dp, 2.dp,
MaterialTheme.colors.background, MaterialTheme.colors.background,
@ -2511,6 +2510,8 @@ fun UserPicture(
.clip(shape = CircleShape) .clip(shape = CircleShape)
} }
val myIconSize = remember(size) { size.div(3.5f) }
Box(myBoxModifier, contentAlignment = TopEnd) { Box(myBoxModifier, contentAlignment = TopEnd) {
RobohashAsyncImageProxy( RobohashAsyncImageProxy(
robot = userHex, robot = userHex,
@ -2521,7 +2522,7 @@ fun UserPicture(
modifier = myImageModifier.background(MaterialTheme.colors.background) modifier = myImageModifier.background(MaterialTheme.colors.background)
) )
ObserveAndDisplayFollowingMark(userHex, size.div(3.5f), accountViewModel) ObserveAndDisplayFollowingMark(userHex, myIconSize, accountViewModel)
} }
} }