Memory improvements on relay lists and LiveData objects

This commit is contained in:
Vitor Pamplona
2023-12-13 18:51:30 -05:00
parent d4f060d509
commit c7be0e4a95
12 changed files with 133 additions and 82 deletions

View File

@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.model package com.vitorpamplona.amethyst.model
import android.util.LruCache
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
@ -41,7 +42,6 @@ import com.vitorpamplona.quartz.events.RepostEvent
import com.vitorpamplona.quartz.events.WrappedEvent import com.vitorpamplona.quartz.events.WrappedEvent
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import java.math.BigDecimal import java.math.BigDecimal
@ -93,7 +93,7 @@ open class Note(val idHex: String) {
var zapPayments = mapOf<Note, Note?>() var zapPayments = mapOf<Note, Note?>()
private set private set
var relays = listOf<String>() var relays = listOf<RelayBriefInfoCache.RelayBriefInfo>()
private set private set
var lastReactionsDownloadTime: Map<String, EOSETime> = emptyMap() var lastReactionsDownloadTime: Map<String, EOSETime> = emptyMap()
@ -110,13 +110,13 @@ open class Note(val idHex: String) {
host.id, host.id,
host.pubKey, host.pubKey,
host.kind(), host.kind(),
relays.firstOrNull() relays.firstOrNull()?.url
) )
} else { } else {
Nip19.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relays.firstOrNull()) Nip19.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relays.firstOrNull()?.url)
} }
} else { } else {
Nip19.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relays.firstOrNull()) Nip19.createNEvent(idHex, author?.pubkeyHex, event?.kind(), relays.firstOrNull()?.url)
} }
} }
@ -271,7 +271,7 @@ open class Note(val idHex: String) {
zaps = mapOf<Note, Note?>() zaps = mapOf<Note, Note?>()
zapPayments = mapOf<Note, Note?>() zapPayments = mapOf<Note, Note?>()
zapsAmount = BigDecimal.ZERO zapsAmount = BigDecimal.ZERO
relays = listOf<String>() relays = listOf<RelayBriefInfoCache.RelayBriefInfo>()
lastReactionsDownloadTime = emptyMap() lastReactionsDownloadTime = emptyMap()
liveSet?.innerReplies?.invalidateData() liveSet?.innerReplies?.invalidateData()
@ -428,15 +428,15 @@ open class Note(val idHex: String) {
} }
@Synchronized @Synchronized
fun addRelaySync(url: String) { fun addRelaySync(briefInfo: RelayBriefInfoCache.RelayBriefInfo) {
if (url !in relays) { if (briefInfo !in relays) {
relays = relays + url relays = relays + briefInfo
} }
} }
fun addRelay(relay: Relay) { fun addRelay(relay: Relay) {
if (relay.url !in relays) { if (relay.brief !in relays) {
addRelaySync(relay.url) addRelaySync(relay.brief)
liveSet?.innerRelays?.invalidateData() liveSet?.innerRelays?.invalidateData()
} }
} }
@ -870,14 +870,8 @@ class NoteLiveSet(u: Note) {
it.note.boosts.size it.note.boosts.size
}.distinctUntilChanged() }.distinctUntilChanged()
val boostList = innerBoosts.map {
it.note.boosts.toImmutableList()
}.distinctUntilChanged()
val relayInfo = innerRelays.map { val relayInfo = innerRelays.map {
it.note.relays.map { it.note.relays
RelayBriefInfo(it)
}.toImmutableList()
} }
val content = innerMetadata.map { val content = innerMetadata.map {
@ -897,8 +891,7 @@ class NoteLiveSet(u: Note) {
hasReactions.hasObservers() || hasReactions.hasObservers() ||
replyCount.hasObservers() || replyCount.hasObservers() ||
reactionCount.hasObservers() || reactionCount.hasObservers() ||
boostCount.hasObservers() || boostCount.hasObservers()
boostList.hasObservers()
} }
fun destroy() { fun destroy() {
@ -986,9 +979,22 @@ class NoteLoadingLiveData<Y>(val note: Note, initialValue: Y?) : MediatorLiveDat
@Immutable @Immutable
class NoteState(val note: Note) class NoteState(val note: Note)
@Immutable object RelayBriefInfoCache {
data class RelayBriefInfo( val cache = LruCache<String, RelayBriefInfo?>(50)
@Immutable
data class RelayBriefInfo(
val url: String, val url: String,
val displayUrl: String = url.trim().removePrefix("wss://").removePrefix("ws://").removeSuffix("/").intern(), val displayUrl: String = url.trim().removePrefix("wss://").removePrefix("ws://").removeSuffix("/").intern(),
val favIcon: String = "https://$displayUrl/favicon.ico".intern() val favIcon: String = "https://$displayUrl/favicon.ico".intern()
) )
fun get(url: String): RelayBriefInfo {
val info = cache[url]
if (info != null) return info
val newInfo = RelayBriefInfo(url)
cache.put(url, newInfo)
return newInfo
}
}

View File

@ -15,5 +15,5 @@ data class RelaySetupInfo(
val feedTypes: Set<FeedType>, val feedTypes: Set<FeedType>,
val paidRelay: Boolean = false val paidRelay: Boolean = false
) { ) {
val briefInfo: RelayBriefInfo = RelayBriefInfo(url) val briefInfo: RelayBriefInfoCache.RelayBriefInfo = RelayBriefInfoCache.RelayBriefInfo(url)
} }

View File

@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.service.relays
import android.util.Log import android.util.Log
import com.vitorpamplona.amethyst.BuildConfig import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
import com.vitorpamplona.amethyst.service.HttpClient import com.vitorpamplona.amethyst.service.HttpClient
import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@ -23,11 +24,13 @@ enum class FeedType {
val COMMON_FEED_TYPES = setOf(FeedType.FOLLOWS, FeedType.PUBLIC_CHATS, FeedType.PRIVATE_DMS, FeedType.GLOBAL) val COMMON_FEED_TYPES = setOf(FeedType.FOLLOWS, FeedType.PUBLIC_CHATS, FeedType.PRIVATE_DMS, FeedType.GLOBAL)
class Relay( class Relay(
var url: String, val url: String,
var read: Boolean = true, val read: Boolean = true,
var write: Boolean = true, val write: Boolean = true,
var activeTypes: Set<FeedType> = FeedType.values().toSet() val activeTypes: Set<FeedType> = FeedType.values().toSet()
) { ) {
val brief = RelayBriefInfoCache.get(url)
companion object { companion object {
// waits 3 minutes to reconnect once things fail // waits 3 minutes to reconnect once things fail
const val RECONNECTING_IN_SECONDS = 60 * 3 const val RECONNECTING_IN_SECONDS = 60 * 3

View File

@ -57,7 +57,7 @@ import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
import com.vitorpamplona.amethyst.model.RelaySetupInfo import com.vitorpamplona.amethyst.model.RelaySetupInfo
import com.vitorpamplona.amethyst.service.Nip11Retriever import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.service.relays.Constants import com.vitorpamplona.amethyst.service.relays.Constants
@ -329,7 +329,7 @@ fun ServerConfig(
accountViewModel.retrieveRelayDocument( accountViewModel.retrieveRelayDocument(
item.url, item.url,
onInfo = { onInfo = {
relayInfo = RelayInfoDialog(RelayBriefInfo(item.url), it) relayInfo = RelayInfoDialog(RelayBriefInfoCache.RelayBriefInfo(item.url), it)
}, },
onError = { url, errorCode, exceptionMessage -> onError = { url, errorCode, exceptionMessage ->
val msg = when (errorCode) { val msg = when (errorCode) {

View File

@ -26,7 +26,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
import com.vitorpamplona.amethyst.model.RelayInformation import com.vitorpamplona.amethyst.model.RelayInformation
import com.vitorpamplona.amethyst.ui.components.ClickableEmail import com.vitorpamplona.amethyst.ui.components.ClickableEmail
import com.vitorpamplona.amethyst.ui.components.ClickableUrl import com.vitorpamplona.amethyst.ui.components.ClickableUrl
@ -43,7 +43,7 @@ import com.vitorpamplona.amethyst.ui.theme.StdPadding
@Composable @Composable
fun RelayInformationDialog( fun RelayInformationDialog(
onClose: () -> Unit, onClose: () -> Unit,
relayBriefInfo: RelayBriefInfo, relayBriefInfo: RelayBriefInfoCache.RelayBriefInfo,
relayInfo: RelayInformation, relayInfo: RelayInformation,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit

View File

@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
import com.vitorpamplona.amethyst.model.RelayInformation import com.vitorpamplona.amethyst.model.RelayInformation
import com.vitorpamplona.amethyst.service.Nip11Retriever import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.service.relays.Relay
@ -37,12 +37,12 @@ import kotlinx.collections.immutable.toImmutableList
data class RelayList( data class RelayList(
val relay: Relay, val relay: Relay,
val relayInfo: RelayBriefInfo, val relayInfo: RelayBriefInfoCache.RelayBriefInfo,
val isSelected: Boolean val isSelected: Boolean
) )
data class RelayInfoDialog( data class RelayInfoDialog(
val relayBriefInfo: RelayBriefInfo, val relayBriefInfo: RelayBriefInfoCache.RelayBriefInfo,
val relayInfo: RelayInformation val relayInfo: RelayInformation
) )
@ -61,7 +61,7 @@ fun RelaySelectionDialog(
accountViewModel.account.activeWriteRelays().map { accountViewModel.account.activeWriteRelays().map {
RelayList( RelayList(
relay = it, relay = it,
relayInfo = RelayBriefInfo(it.url), relayInfo = RelayBriefInfoCache.RelayBriefInfo(it.url),
isSelected = preSelectedList.any { relay -> it.url == relay.url } isSelected = preSelectedList.any { relay -> it.url == relay.url }
) )
} }
@ -167,7 +167,12 @@ fun RelaySelectionDialog(
accountViewModel.retrieveRelayDocument( accountViewModel.retrieveRelayDocument(
item.relay.url, item.relay.url,
onInfo = { onInfo = {
relayInfo = RelayInfoDialog(RelayBriefInfo(item.relay.url), it) relayInfo = RelayInfoDialog(
RelayBriefInfoCache.RelayBriefInfo(
item.relay.url
),
it
)
}, },
onError = { url, errorCode, exceptionMessage -> onError = { url, errorCode, exceptionMessage ->
val msg = when (errorCode) { val msg = when (errorCode) {

View File

@ -56,7 +56,7 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
.asSequence() .asSequence()
.filter { it -> .filter { it ->
val noteEvent = it.event val noteEvent = it.event
val isGlobalRelay = it.relays.any { gRelays.contains(it) } val isGlobalRelay = it.relays.any { gRelays.contains(it.url) }
(noteEvent is TextNoteEvent || noteEvent is ClassifiedsEvent || noteEvent is RepostEvent || noteEvent is GenericRepostEvent || noteEvent is LongTextNoteEvent || noteEvent is PollNoteEvent || noteEvent is HighlightEvent || noteEvent is AudioTrackEvent || noteEvent is AudioHeaderEvent) && (noteEvent is TextNoteEvent || noteEvent is ClassifiedsEvent || noteEvent is RepostEvent || noteEvent is GenericRepostEvent || noteEvent is LongTextNoteEvent || noteEvent is PollNoteEvent || noteEvent is HighlightEvent || noteEvent is AudioTrackEvent || noteEvent is AudioHeaderEvent) &&
(!ignoreAddressables || noteEvent.kind() < 10000) && (!ignoreAddressables || noteEvent.kind() < 10000) &&
((isGlobal && isGlobalRelay) || it.author?.pubkeyHex in followingKeySet || noteEvent.isTaggedHashes(followingTagSet) || noteEvent.isTaggedGeoHashes(followingGeohashSet) || noteEvent.isTaggedAddressableNotes(followingCommunities)) && ((isGlobal && isGlobalRelay) || it.author?.pubkeyHex in followingKeySet || noteEvent.isTaggedHashes(followingTagSet) || noteEvent.isTaggedGeoHashes(followingGeohashSet) || noteEvent.isTaggedAddressableNotes(followingCommunities)) &&

View File

@ -44,6 +44,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.NoteState
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.components.ImageUrlType import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
@ -303,6 +304,31 @@ fun RenderBoostGallery(
} }
} }
@Composable
fun RenderBoostGallery(
noteToGetBoostEvents: NoteState,
nav: (String) -> Unit,
accountViewModel: AccountViewModel
) {
Row(
modifier = Modifier.fillMaxWidth()
) {
Box(
modifier = NotificationIconModifierSmaller
) {
RepostedIcon(
modifier = remember {
Modifier
.size(Size19dp)
.align(Alignment.TopEnd)
}
)
}
AuthorGallery(noteToGetBoostEvents, nav, accountViewModel)
}
}
@Composable @Composable
fun MapZaps( fun MapZaps(
zaps: ImmutableList<CombinedZap>, zaps: ImmutableList<CombinedZap>,
@ -491,6 +517,22 @@ fun AuthorGallery(
} }
} }
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun AuthorGallery(
noteToGetBoostEvents: NoteState,
nav: (String) -> Unit,
accountViewModel: AccountViewModel
) {
Column(modifier = StdStartPadding) {
FlowRow() {
noteToGetBoostEvents.note.boosts.forEach { note ->
BoxedAuthor(note, nav, accountViewModel)
}
}
}
}
@Composable @Composable
private fun BoxedAuthor( private fun BoxedAuthor(
note: Note, note: Note,

View File

@ -88,7 +88,7 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.Channel import com.vitorpamplona.amethyst.model.Channel
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
@ -1746,7 +1746,7 @@ fun DisplayRelaySet(
val relays by remember(baseNote) { val relays by remember(baseNote) {
mutableStateOf( mutableStateOf(
noteEvent.relays().map { RelayBriefInfo(it) }.toImmutableList() noteEvent.relays().map { RelayBriefInfoCache.RelayBriefInfo(it) }.toImmutableList()
) )
} }

View File

@ -406,10 +406,10 @@ private fun WatchBoostsAndRenderGallery(
nav: (String) -> Unit, nav: (String) -> Unit,
accountViewModel: AccountViewModel accountViewModel: AccountViewModel
) { ) {
val boostsEvents by baseNote.live().boostList.observeAsState() val boostsEvents by baseNote.live().boosts.observeAsState()
boostsEvents?.let { boostsEvents?.let {
if (it.isNotEmpty()) { if (it.note.boosts.isNotEmpty()) {
RenderBoostGallery( RenderBoostGallery(
it, it,
nav, nav,

View File

@ -11,8 +11,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
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.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -20,17 +18,15 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.RelayBriefInfo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonBoxModifer import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonBoxModifer
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconButtonModifier import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconButtonModifier
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconModifier import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconModifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
@ -39,16 +35,23 @@ fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String
Spacer(DoubleVertSpacer) Spacer(DoubleVertSpacer)
// FlowRow Seems to be a lot faster than LazyVerticalGrid
FlowRow() {
if (expanded) { if (expanded) {
VerticalRelayPanelWithFlow(relayList, accountViewModel, nav) relayList?.forEach {
RenderRelay(it, accountViewModel, nav)
}
} else { } else {
val shortRelayList by remember { relayList?.getOrNull(0)?.let {
derivedStateOf { RenderRelay(it, accountViewModel, nav)
relayList.take(3).toImmutableList() }
relayList?.getOrNull(1)?.let {
RenderRelay(it, accountViewModel, nav)
}
relayList?.getOrNull(2)?.let {
RenderRelay(it, accountViewModel, nav)
} }
} }
VerticalRelayPanelWithFlow(shortRelayList, accountViewModel, nav)
} }
if (relayList.size > 3 && !expanded) { if (relayList.size > 3 && !expanded) {
@ -58,22 +61,6 @@ fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String
} }
} }
@OptIn(ExperimentalLayoutApi::class)
@Composable
@Stable
private fun VerticalRelayPanelWithFlow(
relays: ImmutableList<RelayBriefInfo>,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
// FlowRow Seems to be a lot faster than LazyVerticalGrid
FlowRow() {
relays.forEach { url ->
RenderRelay(url, accountViewModel, nav)
}
}
}
@Composable @Composable
private fun ShowMoreRelaysButton(onClick: () -> Unit) { private fun ShowMoreRelaysButton(onClick: () -> Unit) {
Row( Row(

View File

@ -34,7 +34,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.map import androidx.lifecycle.map
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
import com.vitorpamplona.amethyst.model.RelayInformation import com.vitorpamplona.amethyst.model.RelayInformation
import com.vitorpamplona.amethyst.service.Nip11Retriever import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.ui.actions.RelayInformationDialog import com.vitorpamplona.amethyst.ui.actions.RelayInformationDialog
@ -46,7 +46,6 @@ import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
import com.vitorpamplona.amethyst.ui.theme.Size15dp import com.vitorpamplona.amethyst.ui.theme.Size15dp
import com.vitorpamplona.amethyst.ui.theme.StdStartPadding import com.vitorpamplona.amethyst.ui.theme.StdStartPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.collections.immutable.toImmutableList
@Composable @Composable
public fun RelayBadgesHorizontal(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { public fun RelayBadgesHorizontal(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
@ -65,12 +64,21 @@ fun RenderRelayList(baseNote: Note, expanded: MutableState<Boolean>, accountView
val noteRelays by baseNote.live().relayInfo.observeAsState() val noteRelays by baseNote.live().relayInfo.observeAsState()
FlowRow(StdStartPadding, verticalArrangement = Arrangement.Center) { FlowRow(StdStartPadding, verticalArrangement = Arrangement.Center) {
val relaysToDisplay = remember(noteRelays, expanded.value) { if (expanded.value) {
if (expanded.value) noteRelays else noteRelays?.take(3)?.toImmutableList() noteRelays?.forEach {
}
relaysToDisplay?.forEach {
RenderRelay(it, accountViewModel, nav) RenderRelay(it, accountViewModel, nav)
} }
} else {
noteRelays?.getOrNull(0)?.let {
RenderRelay(it, accountViewModel, nav)
}
noteRelays?.getOrNull(1)?.let {
RenderRelay(it, accountViewModel, nav)
}
noteRelays?.getOrNull(2)?.let {
RenderRelay(it, accountViewModel, nav)
}
}
} }
} }
@ -105,7 +113,7 @@ fun ChatRelayExpandButton(onClick: () -> Unit) {
} }
@Composable @Composable
fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav: (String) -> Unit) { fun RenderRelay(relay: RelayBriefInfoCache.RelayBriefInfo, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var relayInfo: RelayInformation? by remember { mutableStateOf(null) } var relayInfo: RelayInformation? by remember { mutableStateOf(null) }
if (relayInfo != null) { if (relayInfo != null) {