mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2024-09-29 16:30:49 +00:00
Increases the speed of the Zap Tab in Profiles.
This commit is contained in:
parent
d6a6a52821
commit
d32d2da280
@ -2,17 +2,21 @@ package com.vitorpamplona.amethyst.service.model.zaps
|
||||
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapEventInterface
|
||||
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
|
||||
|
||||
object UserZaps {
|
||||
fun forProfileFeed(zaps: Map<Note, Note?>?): List<Pair<Note, Note>> {
|
||||
fun forProfileFeed(zaps: Map<Note, Note?>?): List<ZapReqResponse> {
|
||||
if (zaps == null) return emptyList()
|
||||
|
||||
return (
|
||||
zaps
|
||||
.filter { it.value != null }
|
||||
.toList()
|
||||
.sortedBy { (it.second?.event as? LnZapEventInterface)?.amount() }
|
||||
.mapNotNull { entry ->
|
||||
entry.value?.let {
|
||||
ZapReqResponse(entry.key, it)
|
||||
}
|
||||
}
|
||||
.sortedBy { (it.zapEvent.event as? LnZapEventInterface)?.amount() }
|
||||
.reversed()
|
||||
) as List<Pair<Note, Note>>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.UserLine
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
@ -385,11 +386,13 @@ fun Notifying(baseMentions: List<User>?, onClick: (User) -> Unit) {
|
||||
|
||||
mentions.forEachIndexed { idx, user ->
|
||||
val innerUserState by user.live().metadata.observeAsState()
|
||||
val innerUser = innerUserState?.user
|
||||
|
||||
innerUser?.let { myUser ->
|
||||
innerUserState?.user?.let { myUser ->
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
|
||||
val tags = remember(innerUserState) {
|
||||
myUser.info?.latestMetadata?.tags?.toImmutableList()
|
||||
}
|
||||
|
||||
Button(
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
@ -400,8 +403,8 @@ fun Notifying(baseMentions: List<User>?, onClick: (User) -> Unit) {
|
||||
}
|
||||
) {
|
||||
CreateTextWithEmoji(
|
||||
text = "✖ ${myUser.toBestDisplayName()}",
|
||||
tags = myUser.info?.latestMetadata?.tags,
|
||||
text = remember(innerUserState) { "✖ ${myUser.toBestDisplayName()}" },
|
||||
tags = tags,
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
@ -13,6 +13,7 @@ import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -48,6 +49,7 @@ import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
|
||||
import com.vitorpamplona.amethyst.service.nip19.Nip19
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadChannel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -269,7 +271,7 @@ private fun DisplayUser(
|
||||
val userState by it.live().metadata.observeAsState()
|
||||
val route = remember(userState) { "User/${it.pubkeyHex}" }
|
||||
val userDisplayName = remember(userState) { userState?.user?.toBestDisplayName() }
|
||||
val userTags = remember(userState) { userState?.user?.info?.latestMetadata?.tags }
|
||||
val userTags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
val addedCharts = remember {
|
||||
"${nip19.additionalChars} "
|
||||
}
|
||||
@ -323,7 +325,7 @@ fun CreateClickableText(
|
||||
@Composable
|
||||
fun CreateTextWithEmoji(
|
||||
text: String,
|
||||
tags: List<List<String>>?,
|
||||
tags: ImmutableList<List<String>>?,
|
||||
color: Color = Color.Unspecified,
|
||||
textAlign: TextAlign? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
@ -382,7 +384,7 @@ fun CreateTextWithEmoji(
|
||||
@Composable
|
||||
fun CreateTextWithEmoji(
|
||||
text: String,
|
||||
emojis: Map<String, String>,
|
||||
emojis: ImmutableMap<String, String>,
|
||||
color: Color = Color.Unspecified,
|
||||
textAlign: TextAlign? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
@ -438,7 +440,7 @@ fun CreateTextWithEmoji(
|
||||
@Composable
|
||||
fun CreateClickableTextWithEmoji(
|
||||
clickablePart: String,
|
||||
tags: List<List<String>>?,
|
||||
tags: ImmutableList<List<String>>?,
|
||||
style: TextStyle,
|
||||
onClick: (Int) -> Unit
|
||||
) {
|
||||
@ -475,7 +477,7 @@ fun CreateClickableTextWithEmoji(
|
||||
fun CreateClickableTextWithEmoji(
|
||||
clickablePart: String,
|
||||
suffix: String,
|
||||
tags: List<List<String>>?,
|
||||
tags: ImmutableList<List<String>>?,
|
||||
overrideColor: Color? = null,
|
||||
fontWeight: FontWeight = FontWeight.Normal,
|
||||
route: String,
|
||||
@ -512,7 +514,7 @@ fun CreateClickableTextWithEmoji(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun assembleAnnotatedList(text: String, emojis: Map<String, String>): List<Renderable> {
|
||||
suspend fun assembleAnnotatedList(text: String, emojis: Map<String, String>): ImmutableList<Renderable> {
|
||||
return NIP30Parser().buildArray(text).map {
|
||||
val url = emojis[it]
|
||||
if (url != null) {
|
||||
@ -520,15 +522,20 @@ suspend fun assembleAnnotatedList(text: String, emojis: Map<String, String>): Li
|
||||
} else {
|
||||
TextType(it)
|
||||
}
|
||||
}
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
open class Renderable()
|
||||
|
||||
@Immutable
|
||||
class TextType(val text: String) : Renderable()
|
||||
|
||||
@Immutable
|
||||
class ImageUrlType(val url: String) : Renderable()
|
||||
|
||||
@Composable
|
||||
fun ClickableInLineIconRenderer(wordsInOrder: List<Renderable>, style: SpanStyle, onClick: (Int) -> Unit) {
|
||||
fun ClickableInLineIconRenderer(wordsInOrder: ImmutableList<Renderable>, style: SpanStyle, onClick: (Int) -> Unit) {
|
||||
val inlineContent = wordsInOrder.mapIndexedNotNull { idx, value ->
|
||||
if (value is ImageUrlType) {
|
||||
Pair(
|
||||
@ -589,7 +596,7 @@ fun ClickableInLineIconRenderer(wordsInOrder: List<Renderable>, style: SpanStyle
|
||||
|
||||
@Composable
|
||||
fun InLineIconRenderer(
|
||||
wordsInOrder: List<Renderable>,
|
||||
wordsInOrder: ImmutableList<Renderable>,
|
||||
style: SpanStyle,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
|
@ -702,55 +702,90 @@ fun startsWithNIP19Scheme(word: String): Boolean {
|
||||
return listOf("npub1", "naddr1", "note1", "nprofile1", "nevent1").any { cleaned.startsWith(it) }
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class LoadedBechLink(val baseNote: Note?, val nip19: Nip19.Return)
|
||||
|
||||
@Composable
|
||||
fun BechLink(word: String, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
var nip19Route by remember { mutableStateOf<Nip19.Return?>(null) }
|
||||
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
|
||||
var loadedLink by remember { mutableStateOf<LoadedBechLink?>(null) }
|
||||
|
||||
LaunchedEffect(key1 = word) {
|
||||
launch(Dispatchers.IO) {
|
||||
Nip19.uriToRoute(word)?.let {
|
||||
var returningNote: Note? = null
|
||||
if (it.type == Nip19.Type.NOTE || it.type == Nip19.Type.EVENT || it.type == Nip19.Type.ADDRESS) {
|
||||
LocalCache.checkGetOrCreateNote(it.hex)?.let { note ->
|
||||
baseNotePair = Pair(note, it.additionalChars)
|
||||
returningNote = note
|
||||
}
|
||||
}
|
||||
|
||||
nip19Route = it
|
||||
loadedLink = LoadedBechLink(returningNote, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canPreview) {
|
||||
baseNotePair?.let {
|
||||
NoteCompose(
|
||||
baseNote = it.first,
|
||||
accountViewModel = accountViewModel,
|
||||
modifier = Modifier
|
||||
.padding(top = 2.dp, bottom = 0.dp, start = 0.dp, end = 0.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = RoundedCornerShape(15.dp))
|
||||
.border(
|
||||
1.dp,
|
||||
MaterialTheme.colors.onSurface.copy(alpha = 0.12f),
|
||||
RoundedCornerShape(15.dp)
|
||||
),
|
||||
parentBackgroundColor = backgroundColor,
|
||||
isQuotedNote = true,
|
||||
nav = nav
|
||||
)
|
||||
if (!it.second.isNullOrEmpty()) {
|
||||
Text(
|
||||
"${it.second} "
|
||||
)
|
||||
loadedLink?.let { loadedLink ->
|
||||
loadedLink.baseNote?.let {
|
||||
DisplayFullNote(it, accountViewModel, backgroundColor, nav, loadedLink)
|
||||
} ?: run {
|
||||
ClickableRoute(loadedLink.nip19, nav)
|
||||
}
|
||||
} ?: nip19Route?.let {
|
||||
ClickableRoute(it, nav)
|
||||
} ?: Text(text = "$word ")
|
||||
} ?: run {
|
||||
Text(text = remember { "$word " })
|
||||
}
|
||||
} else {
|
||||
nip19Route?.let {
|
||||
ClickableRoute(it, nav)
|
||||
} ?: Text(text = "$word ")
|
||||
loadedLink?.let {
|
||||
ClickableRoute(it.nip19, nav)
|
||||
} ?: run {
|
||||
Text(text = remember { "$word " })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayFullNote(
|
||||
it: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
backgroundColor: Color,
|
||||
nav: (String) -> Unit,
|
||||
loadedLink: LoadedBechLink
|
||||
) {
|
||||
val borderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.12f)
|
||||
|
||||
val modifier = remember {
|
||||
Modifier
|
||||
.padding(top = 2.dp, bottom = 0.dp, start = 0.dp, end = 0.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = RoundedCornerShape(15.dp))
|
||||
.border(
|
||||
1.dp,
|
||||
borderColor,
|
||||
RoundedCornerShape(15.dp)
|
||||
)
|
||||
}
|
||||
|
||||
NoteCompose(
|
||||
baseNote = it,
|
||||
accountViewModel = accountViewModel,
|
||||
modifier = modifier,
|
||||
parentBackgroundColor = backgroundColor,
|
||||
isQuotedNote = true,
|
||||
nav = nav
|
||||
)
|
||||
|
||||
val extraChars = remember(loadedLink) {
|
||||
if (loadedLink.nip19.additionalChars.isNotBlank()) {
|
||||
"${loadedLink.nip19.additionalChars} "
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
extraChars?.let {
|
||||
Text(
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -876,12 +911,12 @@ fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgro
|
||||
"User/${it.first.pubkeyHex}"
|
||||
}
|
||||
val userTags = remember(innerUserState) {
|
||||
innerUserState?.user?.info?.latestMetadata?.tags
|
||||
innerUserState?.user?.info?.latestMetadata?.tags?.toImmutableList()
|
||||
}
|
||||
|
||||
CreateClickableTextWithEmoji(
|
||||
clickablePart = displayName,
|
||||
suffix = "${it.second} ",
|
||||
suffix = remember { "${it.second} " },
|
||||
tags = userTags,
|
||||
route = route,
|
||||
nav = nav
|
||||
|
@ -1,17 +1,11 @@
|
||||
package com.vitorpamplona.amethyst.ui.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.model.zaps.UserZaps
|
||||
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
|
||||
|
||||
object UserProfileZapsFeedFilter : FeedFilter<Pair<Note, Note>>() {
|
||||
var user: User? = null
|
||||
|
||||
fun loadUserProfile(user: User?) {
|
||||
this.user = user
|
||||
}
|
||||
|
||||
override fun feed(): List<Pair<Note, Note>> {
|
||||
return UserZaps.forProfileFeed(user?.zaps)
|
||||
class UserProfileZapsFeedFilter(val user: User) : FeedFilter<ZapReqResponse>() {
|
||||
override fun feed(): List<ZapReqResponse> {
|
||||
return UserZaps.forProfileFeed(user.zaps)
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginPage
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -227,7 +228,7 @@ private fun AccountName(
|
||||
}
|
||||
val tags by remember(userState) {
|
||||
derivedStateOf {
|
||||
user.info?.latestMetadata?.tags
|
||||
user.info?.latestMetadata?.tags?.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -128,7 +129,7 @@ fun ProfileContent(
|
||||
val profilePicture = remember(accountUserState) { accountUserState?.user?.profilePicture()?.ifBlank { null }?.let { ResizeImage(it, 100.dp) } }
|
||||
val bestUserName = remember(accountUserState) { accountUserState?.user?.bestUsername() }
|
||||
val bestDisplayName = remember(accountUserState) { accountUserState?.user?.bestDisplayName() }
|
||||
val tags = remember(accountUserState) { accountUserState?.user?.info?.latestMetadata?.tags }
|
||||
val tags = remember(accountUserState) { accountUserState?.user?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
val route = remember(accountUserState) { "User/${accountUserState?.user?.pubkeyHex}" }
|
||||
|
||||
Box {
|
||||
@ -193,8 +194,8 @@ fun ProfileContent(
|
||||
}
|
||||
if (bestUserName != null) {
|
||||
CreateTextWithEmoji(
|
||||
text = " @$bestUserName",
|
||||
tags = accountUser.info?.latestMetadata?.tags,
|
||||
text = remember { " @$bestUserName" },
|
||||
tags = tags,
|
||||
color = Color.LightGray,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
@ -418,7 +418,7 @@ private fun RenderChangeChannelMetadataNote(
|
||||
|
||||
CreateTextWithEmoji(
|
||||
text = text,
|
||||
tags = note.author?.info?.latestMetadata?.tags
|
||||
tags = remember { note.author?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
)
|
||||
}
|
||||
|
||||
@ -441,7 +441,7 @@ private fun RenderCreateChannelNote(note: Note) {
|
||||
|
||||
CreateTextWithEmoji(
|
||||
text = text,
|
||||
tags = note.author?.info?.latestMetadata?.tags
|
||||
tags = remember { note.author?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
)
|
||||
}
|
||||
|
||||
@ -457,7 +457,7 @@ private fun DrawAuthorInfo(
|
||||
val route = remember { "User/$pubkeyHex" }
|
||||
val userDisplayName = remember(userState) { userState?.user?.toBestDisplayName() }
|
||||
val userProfilePicture = remember(userState) { ResizeImage(userState?.user?.profilePicture(), 25.dp) }
|
||||
val userTags = remember(userState) { userState?.user?.info?.latestMetadata?.tags }
|
||||
val userTags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@ -468,17 +468,19 @@ private fun DrawAuthorInfo(
|
||||
robot = pubkeyHex,
|
||||
model = userProfilePicture,
|
||||
contentDescription = stringResource(id = R.string.profile_image),
|
||||
modifier = Modifier
|
||||
.width(25.dp)
|
||||
.height(25.dp)
|
||||
.clip(shape = CircleShape)
|
||||
.clickable(onClick = {
|
||||
nav(route)
|
||||
})
|
||||
modifier = remember {
|
||||
Modifier
|
||||
.width(25.dp)
|
||||
.height(25.dp)
|
||||
.clip(shape = CircleShape)
|
||||
.clickable(onClick = {
|
||||
nav(route)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
CreateClickableTextWithEmoji(
|
||||
clickablePart = " $userDisplayName",
|
||||
clickablePart = remember { " $userDisplayName" },
|
||||
suffix = "",
|
||||
tags = userTags,
|
||||
fontWeight = FontWeight.Bold,
|
||||
|
@ -792,18 +792,19 @@ fun RenderAppDefinition(
|
||||
}
|
||||
}
|
||||
|
||||
it.anyName()?.let {
|
||||
val name = remember(it) { it.anyName() }
|
||||
name?.let {
|
||||
Row(verticalAlignment = Alignment.Bottom, modifier = Modifier.padding(top = 7.dp)) {
|
||||
CreateTextWithEmoji(
|
||||
text = it,
|
||||
tags = remember { note.event?.tags() ?: emptyList() },
|
||||
tags = remember { (note.event?.tags() ?: emptyList()).toImmutableList() },
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 25.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val website = it.website
|
||||
val website = remember(it) { it.website }
|
||||
if (!website.isNullOrEmpty()) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
@ -1717,7 +1718,7 @@ fun DisplayHighlight(
|
||||
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 }
|
||||
val userTags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
|
||||
if (userDisplayName != null) {
|
||||
CreateClickableTextWithEmoji(
|
||||
|
@ -20,6 +20,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.*
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -231,17 +232,14 @@ private fun ReplyInfoMention(
|
||||
onUserTagClick: (User) -> Unit
|
||||
) {
|
||||
val innerUserState by user.live().metadata.observeAsState()
|
||||
val innerUser = remember(innerUserState) {
|
||||
innerUserState?.user
|
||||
} ?: return
|
||||
|
||||
CreateClickableTextWithEmoji(
|
||||
clickablePart = "$prefix${innerUser.toBestDisplayName()}",
|
||||
tags = innerUser.info?.latestMetadata?.tags,
|
||||
clickablePart = remember(innerUserState) { "$prefix${innerUserState?.user?.toBestDisplayName()}" },
|
||||
tags = remember(innerUserState) { innerUserState?.user?.info?.latestMetadata?.tags?.toImmutableList() },
|
||||
style = LocalTextStyle.current.copy(
|
||||
color = MaterialTheme.colors.primary.copy(alpha = 0.52f),
|
||||
fontSize = 13.sp
|
||||
),
|
||||
onClick = { onUserTagClick(innerUser) }
|
||||
onClick = { onUserTagClick(user) }
|
||||
)
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun NoteUsernameDisplay(baseNote: Note, weight: Modifier = Modifier) {
|
||||
@ -29,7 +31,7 @@ fun UsernameDisplay(baseUser: User, weight: Modifier = Modifier) {
|
||||
val bestUserName = remember(userState) { userState?.user?.bestUsername() }
|
||||
val bestDisplayName = remember(userState) { userState?.user?.bestDisplayName() }
|
||||
val npubDisplay = remember { baseUser.pubkeyDisplayHex() }
|
||||
val tags = remember(userState) { userState?.user?.info?.latestMetadata?.tags }
|
||||
val tags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
|
||||
UserNameDisplay(bestUserName, bestDisplayName, npubDisplay, tags, weight)
|
||||
}
|
||||
@ -39,7 +41,7 @@ private fun UserNameDisplay(
|
||||
bestUserName: String?,
|
||||
bestDisplayName: String?,
|
||||
npubDisplay: String,
|
||||
tags: List<List<String>>?,
|
||||
tags: ImmutableList<List<String>>?,
|
||||
modifier: Modifier
|
||||
) {
|
||||
if (bestUserName != null && bestDisplayName != null) {
|
||||
@ -49,7 +51,7 @@ private fun UserNameDisplay(
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
CreateTextWithEmoji(
|
||||
text = "@$bestUserName",
|
||||
text = remember { "@$bestUserName" },
|
||||
tags = tags,
|
||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
|
||||
maxLines = 1,
|
||||
@ -67,7 +69,7 @@ private fun UserNameDisplay(
|
||||
)
|
||||
} else if (bestUserName != null) {
|
||||
CreateTextWithEmoji(
|
||||
text = "@$bestUserName",
|
||||
text = remember { "@$bestUserName" },
|
||||
tags = tags,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
|
@ -28,6 +28,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
||||
import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
|
||||
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.FollowButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShowUserButton
|
||||
@ -38,37 +39,44 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun ZapNoteCompose(baseNote: Pair<Note, Note>, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
val baseNoteRequest by baseNote.first.live().metadata.observeAsState()
|
||||
val noteZapRequest = remember(baseNoteRequest) { baseNoteRequest?.note } ?: return
|
||||
fun ZapNoteCompose(baseReqResponse: ZapReqResponse, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
val baseNoteRequest by baseReqResponse.request.live().metadata.observeAsState()
|
||||
|
||||
var baseAuthor by remember {
|
||||
mutableStateOf(noteZapRequest.author)
|
||||
mutableStateOf<User?>(null)
|
||||
}
|
||||
|
||||
LaunchedEffect(baseNoteRequest) {
|
||||
launch(Dispatchers.Default) {
|
||||
(baseNoteRequest?.note?.event as? LnZapRequestEvent)?.let {
|
||||
baseNoteRequest?.note?.let {
|
||||
val decryptedContent = accountViewModel.decryptZap(it)
|
||||
if (decryptedContent != null) {
|
||||
baseAuthor = LocalCache.getOrCreateUser(decryptedContent.pubKey)
|
||||
} else {
|
||||
baseAuthor = it.author
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (baseAuthor == null) {
|
||||
BlankNote()
|
||||
} else {
|
||||
val route = remember(baseAuthor) {
|
||||
"User/${baseAuthor?.pubkeyHex}"
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.clickable(
|
||||
onClick = { nav("User/${baseAuthor?.pubkeyHex}") }
|
||||
onClick = { nav(route) }
|
||||
),
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
launch(Dispatchers.Default) {
|
||||
(noteZapRequest.event as? LnZapRequestEvent)?.let {
|
||||
val decryptedContent = accountViewModel.decryptZap(noteZapRequest)
|
||||
if (decryptedContent != null) {
|
||||
baseAuthor = LocalCache.getOrCreateUser(decryptedContent.pubKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baseAuthor?.let {
|
||||
RenderZapNote(it, baseNote.second, nav, accountViewModel)
|
||||
RenderZapNote(it, baseReqResponse.zapEvent, nav, accountViewModel)
|
||||
}
|
||||
|
||||
Divider(
|
||||
@ -87,30 +95,37 @@ private fun RenderZapNote(
|
||||
accountViewModel: AccountViewModel
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = 10.dp
|
||||
),
|
||||
modifier = remember {
|
||||
Modifier
|
||||
.padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = 10.dp
|
||||
)
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
UserPicture(baseAuthor, nav, accountViewModel, 55.dp)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 10.dp)
|
||||
.weight(1f)
|
||||
modifier = remember {
|
||||
Modifier
|
||||
.padding(start = 10.dp)
|
||||
.weight(1f)
|
||||
}
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
UsernameDisplay(baseAuthor)
|
||||
}
|
||||
|
||||
AboutDisplay(baseAuthor)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
AboutDisplay(baseAuthor)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
modifier = remember {
|
||||
Modifier.padding(start = 10.dp)
|
||||
},
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
ZapAmount(zapNote)
|
||||
@ -125,13 +140,15 @@ private fun RenderZapNote(
|
||||
@Composable
|
||||
private fun ZapAmount(zapEventNote: Note) {
|
||||
val noteState by zapEventNote.live().metadata.observeAsState()
|
||||
val noteZap = remember(noteState) { noteState?.note } ?: return
|
||||
|
||||
var zapAmount by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
LaunchedEffect(key1 = noteZap) {
|
||||
LaunchedEffect(key1 = noteState) {
|
||||
launch(Dispatchers.IO) {
|
||||
zapAmount = showAmountAxis((noteZap.event as? LnZapEvent)?.amount)
|
||||
val newZapAmount = showAmountAxis((noteState?.note?.event as? LnZapEvent)?.amount)
|
||||
if (zapAmount != newZapAmount) {
|
||||
zapAmount = newZapAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +168,6 @@ fun UserActionOptions(
|
||||
accountViewModel: AccountViewModel
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val isHidden by remember(accountState) {
|
||||
derivedStateOf {
|
||||
@ -159,20 +175,39 @@ fun UserActionOptions(
|
||||
}
|
||||
}
|
||||
|
||||
val userState by accountViewModel.account.userProfile().live().follows.observeAsState()
|
||||
val isFollowing by remember(userState) {
|
||||
derivedStateOf {
|
||||
userState?.user?.isFollowingCached(baseAuthor) ?: false
|
||||
}
|
||||
}
|
||||
|
||||
if (isHidden) {
|
||||
ShowUserButton {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.show(baseAuthor)
|
||||
}
|
||||
}
|
||||
} else if (isFollowing) {
|
||||
} else {
|
||||
ShowFollowingOrUnfollowingButton(baseAuthor, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShowFollowingOrUnfollowingButton(
|
||||
baseAuthor: User,
|
||||
accountViewModel: AccountViewModel
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var isFollowing by remember { mutableStateOf(false) }
|
||||
val accountFollowsState by accountViewModel.account.userProfile().live().follows.observeAsState()
|
||||
|
||||
LaunchedEffect(key1 = accountFollowsState) {
|
||||
launch(Dispatchers.Default) {
|
||||
val newShowFollowingMark =
|
||||
accountFollowsState?.user?.isFollowing(baseAuthor) == true
|
||||
|
||||
if (newShowFollowingMark != isFollowing) {
|
||||
isFollowing = newShowFollowingMark
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFollowing) {
|
||||
UnfollowButton { scope.launch(Dispatchers.IO) { accountViewModel.unfollow(baseAuthor) } }
|
||||
} else {
|
||||
FollowButton({ scope.launch(Dispatchers.IO) { accountViewModel.follow(baseAuthor) } })
|
||||
|
@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.ResizeImage
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
||||
import com.vitorpamplona.amethyst.ui.qrcode.NIP19QrCodeScanner
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
|
||||
@ -87,7 +88,7 @@ fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
|
||||
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().padding(top = 5.dp)) {
|
||||
CreateTextWithEmoji(
|
||||
text = user.bestDisplayName() ?: user.bestUsername() ?: "",
|
||||
tags = user.info?.latestMetadata?.tags,
|
||||
tags = user.info?.latestMetadata?.tags?.toImmutableList(),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
|
@ -1,11 +1,18 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
data class ZapReqResponse(val request: Note, val zapEvent: Note)
|
||||
|
||||
@Stable
|
||||
sealed class LnZapFeedState {
|
||||
object Loading : LnZapFeedState()
|
||||
class Loaded(val feed: MutableState<List<Pair<Note, Note>>>) : LnZapFeedState()
|
||||
class Loaded(val feed: MutableState<ImmutableList<ZapReqResponse>>) : LnZapFeedState()
|
||||
object Empty : LnZapFeedState()
|
||||
class FeedError(val errorMessage: String) : LnZapFeedState()
|
||||
}
|
||||
|
@ -2,78 +2,44 @@ package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapNoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun LnZapFeedView(
|
||||
viewModel: LnZapFeedViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
enablePullRefresh: Boolean = true
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val feedState by viewModel.feedContent.collectAsState()
|
||||
|
||||
var refreshing by remember { mutableStateOf(false) }
|
||||
val refresh = { refreshing = true; viewModel.invalidateData(); refreshing = false }
|
||||
val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh = refresh)
|
||||
|
||||
val modifier = if (enablePullRefresh) {
|
||||
Modifier.pullRefresh(pullRefreshState)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
Box(modifier) {
|
||||
Column() {
|
||||
Crossfade(targetState = feedState, animationSpec = tween(durationMillis = 100)) { state ->
|
||||
when (state) {
|
||||
is LnZapFeedState.Empty -> {
|
||||
FeedEmpty {
|
||||
refreshing = true
|
||||
}
|
||||
}
|
||||
is LnZapFeedState.FeedError -> {
|
||||
FeedError(state.errorMessage) {
|
||||
refreshing = true
|
||||
}
|
||||
}
|
||||
is LnZapFeedState.Loaded -> {
|
||||
if (refreshing) {
|
||||
refreshing = false
|
||||
}
|
||||
|
||||
LnZapFeedLoaded(state, accountViewModel, nav)
|
||||
}
|
||||
is LnZapFeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
}
|
||||
Crossfade(targetState = feedState, animationSpec = tween(durationMillis = 100)) { state ->
|
||||
when (state) {
|
||||
is LnZapFeedState.Empty -> {
|
||||
FeedEmpty {
|
||||
viewModel.invalidateData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enablePullRefresh) {
|
||||
PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter))
|
||||
is LnZapFeedState.FeedError -> {
|
||||
FeedError(state.errorMessage) {
|
||||
viewModel.invalidateData()
|
||||
}
|
||||
}
|
||||
is LnZapFeedState.Loaded -> {
|
||||
LnZapFeedLoaded(state, accountViewModel, nav)
|
||||
}
|
||||
is LnZapFeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,7 +59,7 @@ private fun LnZapFeedLoaded(
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
itemsIndexed(state.feed.value, key = { _, item -> item.second.idHex }) { _, item ->
|
||||
itemsIndexed(state.feed.value, key = { _, item -> item.zapEvent.idHex }) { _, item ->
|
||||
ZapNoteCompose(item, accountViewModel = accountViewModel, nav = nav)
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,16 @@ package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
|
||||
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.UserProfileZapsFeedFilter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@ -17,9 +20,15 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NostrUserProfileZapsFeedViewModel : LnZapFeedViewModel(UserProfileZapsFeedFilter)
|
||||
class NostrUserProfileZapsFeedViewModel(user: User) : LnZapFeedViewModel(UserProfileZapsFeedFilter(user)) {
|
||||
class Factory(val user: User) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileZapsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileZapsFeedViewModel>): NostrUserProfileZapsFeedViewModel {
|
||||
return NostrUserProfileZapsFeedViewModel(user) as NostrUserProfileZapsFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class LnZapFeedViewModel(val dataSource: FeedFilter<Pair<Note, Note>>) : ViewModel() {
|
||||
open class LnZapFeedViewModel(val dataSource: FeedFilter<ZapReqResponse>) : ViewModel() {
|
||||
private val _feedContent = MutableStateFlow<LnZapFeedState>(LnZapFeedState.Loading)
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
@ -32,12 +41,12 @@ open class LnZapFeedViewModel(val dataSource: FeedFilter<Pair<Note, Note>>) : Vi
|
||||
|
||||
private fun refreshSuspended() {
|
||||
checkNotInMainThread()
|
||||
val notes = dataSource.loadTop()
|
||||
val notes = dataSource.loadTop().toImmutableList()
|
||||
|
||||
val oldNotesState = _feedContent.value
|
||||
if (oldNotesState is LnZapFeedState.Loaded) {
|
||||
// Using size as a proxy for has changed.
|
||||
if (notes != oldNotesState.feed.value) {
|
||||
if (!equalImmutableLists(notes, oldNotesState.feed.value)) {
|
||||
updateFeed(notes)
|
||||
}
|
||||
} else {
|
||||
@ -45,7 +54,7 @@ open class LnZapFeedViewModel(val dataSource: FeedFilter<Pair<Note, Note>>) : Vi
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFeed(notes: List<Pair<Note, Note>>) {
|
||||
private fun updateFeed(notes: ImmutableList<ZapReqResponse>) {
|
||||
val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
scope.launch {
|
||||
val currentState = _feedContent.value
|
||||
|
@ -78,7 +78,6 @@ import com.vitorpamplona.amethyst.ui.dal.UserProfileBookmarksFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.UserProfileConversationsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.UserProfileNewThreadFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.UserProfileReportsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.UserProfileZapsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.navigation.ShowQRDialog
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.ui.screen.FeedState
|
||||
@ -154,11 +153,19 @@ fun PrepareViewModels(baseUser: User, accountViewModel: AccountViewModel, nav: (
|
||||
)
|
||||
)
|
||||
|
||||
val zapFeedViewModel: NostrUserProfileZapsFeedViewModel = viewModel(
|
||||
key = baseUser.pubkeyHex + "UserProfileZapsFeedViewModel",
|
||||
factory = NostrUserProfileZapsFeedViewModel.Factory(
|
||||
baseUser
|
||||
)
|
||||
)
|
||||
|
||||
ProfileScreen(
|
||||
baseUser = baseUser,
|
||||
followsFeedViewModel,
|
||||
followersFeedViewModel,
|
||||
appRecommendations,
|
||||
zapFeedViewModel,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav
|
||||
)
|
||||
@ -171,12 +178,12 @@ fun ProfileScreen(
|
||||
followsFeedViewModel: NostrUserProfileFollowsUserFeedViewModel,
|
||||
followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel,
|
||||
appRecommendations: NostrUserAppRecommendationsFeedViewModel,
|
||||
zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
UserProfileNewThreadFeedFilter.loadUserProfile(accountViewModel.account, baseUser)
|
||||
UserProfileConversationsFeedFilter.loadUserProfile(accountViewModel.account, baseUser)
|
||||
UserProfileZapsFeedFilter.loadUserProfile(baseUser)
|
||||
UserProfileReportsFeedFilter.loadUserProfile(baseUser)
|
||||
UserProfileBookmarksFeedFilter.loadUserProfile(accountViewModel.account, baseUser)
|
||||
|
||||
@ -293,10 +300,10 @@ fun ProfileScreen(
|
||||
1 -> TabNotesConversations(accountViewModel, nav)
|
||||
2 -> TabFollows(baseUser, followsFeedViewModel, accountViewModel, nav)
|
||||
3 -> TabFollows(baseUser, followersFeedViewModel, accountViewModel, nav)
|
||||
4 -> TabReceivedZaps(baseUser, accountViewModel, nav)
|
||||
6 -> TabBookmarks(baseUser, accountViewModel, nav)
|
||||
7 -> TabReports(baseUser, accountViewModel, nav)
|
||||
8 -> TabRelays(baseUser, accountViewModel)
|
||||
4 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
||||
5 -> TabBookmarks(baseUser, accountViewModel, nav)
|
||||
6 -> TabReports(baseUser, accountViewModel, nav)
|
||||
7 -> TabRelays(baseUser, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -579,7 +586,7 @@ private fun DrawAdditionalInfo(
|
||||
) {
|
||||
val userState by baseUser.live().metadata.observeAsState()
|
||||
val user = remember(userState) { userState?.user } ?: return
|
||||
val tags = remember(userState) { userState?.user?.info?.latestMetadata?.tags }
|
||||
val tags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableList() }
|
||||
|
||||
val uri = LocalUriHandler.current
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
@ -1120,16 +1127,14 @@ private fun WatchFollowChanges(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TabReceivedZaps(baseUser: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
val feedViewModel: NostrUserProfileZapsFeedViewModel = viewModel()
|
||||
|
||||
WatchZapsAndUpdateFeed(baseUser, feedViewModel)
|
||||
fun TabReceivedZaps(baseUser: User, zapFeedViewModel: NostrUserProfileZapsFeedViewModel, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
WatchZapsAndUpdateFeed(baseUser, zapFeedViewModel)
|
||||
|
||||
Column(Modifier.fillMaxHeight()) {
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 0.dp)
|
||||
) {
|
||||
LnZapFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false)
|
||||
LnZapFeedView(zapFeedViewModel, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user