Isolating state changes in the profile page.
This commit is contained in:
@@ -11,7 +11,6 @@ object NostrUserProfileDataSource : NostrDataSource("UserProfileFeed") {
|
|||||||
|
|
||||||
fun loadUserProfile(user: User?) {
|
fun loadUserProfile(user: User?) {
|
||||||
this.user = user
|
this.user = user
|
||||||
resetFilters()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createUserInfoFilter() = user?.let {
|
fun createUserInfoFilter() = user?.let {
|
||||||
|
|||||||
+226
-213
@@ -1,5 +1,6 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -18,7 +19,6 @@ import androidx.compose.material.icons.filled.ContentCopy
|
|||||||
import androidx.compose.material.icons.filled.EditNote
|
import androidx.compose.material.icons.filled.EditNote
|
||||||
import androidx.compose.material.icons.filled.Link
|
import androidx.compose.material.icons.filled.Link
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.Share
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -95,6 +95,7 @@ import com.vitorpamplona.amethyst.ui.screen.RelayFeedView
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.RelayFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.RelayFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.UserFeedView
|
import com.vitorpamplona.amethyst.ui.screen.UserFeedView
|
||||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -125,7 +126,7 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
|
|||||||
@Composable
|
@Composable
|
||||||
fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController: NavController) {
|
fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
val account = accountState?.account ?: return
|
val account = remember(accountState) { accountState?.account } ?: return
|
||||||
|
|
||||||
UserProfileNewThreadFeedFilter.loadUserProfile(account, user)
|
UserProfileNewThreadFeedFilter.loadUserProfile(account, user)
|
||||||
UserProfileConversationsFeedFilter.loadUserProfile(account, user)
|
UserProfileConversationsFeedFilter.loadUserProfile(account, user)
|
||||||
@@ -139,6 +140,10 @@ fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController:
|
|||||||
|
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
NostrUserProfileDataSource.start()
|
||||||
|
}
|
||||||
|
|
||||||
DisposableEffect(accountViewModel) {
|
DisposableEffect(accountViewModel) {
|
||||||
val observer = LifecycleEventObserver { _, event ->
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
if (event == Lifecycle.Event.ON_RESUME) {
|
if (event == Lifecycle.Event.ON_RESUME) {
|
||||||
@@ -214,64 +219,14 @@ fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController:
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val tabs = listOf<@Composable() (() -> Unit)?>(
|
val tabs = listOf<@Composable() (() -> Unit)?>(
|
||||||
{
|
{ Text(text = stringResource(R.string.notes)) },
|
||||||
Text(text = stringResource(R.string.notes))
|
{ Text(text = stringResource(R.string.replies)) },
|
||||||
},
|
{ FollowTabHeader(baseUser) },
|
||||||
{
|
{ FollowersTabHeader(baseUser) },
|
||||||
Text(text = stringResource(R.string.replies))
|
{ ZapTabHeader(baseUser) },
|
||||||
},
|
{ BookmarkTabHeader(baseUser) },
|
||||||
{
|
{ ReportsTabHeader(baseUser) },
|
||||||
val userState by baseUser.live().follows.observeAsState()
|
{ RelaysTabHeader(baseUser) }
|
||||||
val userFollows = userState?.user?.transientFollowCount() ?: "--"
|
|
||||||
|
|
||||||
Text(text = "$userFollows ${stringResource(R.string.follows)}")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
val userState by baseUser.live().follows.observeAsState()
|
|
||||||
val userFollowers = userState?.user?.transientFollowerCount() ?: "--"
|
|
||||||
|
|
||||||
Text(text = "$userFollowers ${stringResource(id = R.string.followers)}")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
val userState by baseUser.live().zaps.observeAsState()
|
|
||||||
val userZaps = userState?.user
|
|
||||||
|
|
||||||
var zapAmount by remember { mutableStateOf<BigDecimal?>(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = userState) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val tempAmount = userZaps?.zappedAmount()
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
zapAmount = tempAmount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(text = "${showAmount(zapAmount)} ${stringResource(id = R.string.zaps)}")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
val userState by baseUser.live().bookmarks.observeAsState()
|
|
||||||
val bookmarkList = userState?.user?.latestBookmarkList
|
|
||||||
val userBookmarks =
|
|
||||||
(bookmarkList?.taggedEvents()?.count() ?: 0) + (bookmarkList?.taggedAddresses()?.count() ?: 0)
|
|
||||||
|
|
||||||
Text(text = "$userBookmarks ${stringResource(R.string.bookmarks)}")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
val userState by baseUser.live().reports.observeAsState()
|
|
||||||
val userReports = userState?.user?.reports?.values?.flatten()?.count()
|
|
||||||
|
|
||||||
Text(text = "$userReports ${stringResource(R.string.reports)}")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
val userState by baseUser.live().relays.observeAsState()
|
|
||||||
val userRelaysBeingUsed = userState?.user?.relaysBeingUsed?.size ?: "--"
|
|
||||||
|
|
||||||
val userStateRelayInfo by baseUser.live().relayInfo.observeAsState()
|
|
||||||
val userRelays = userStateRelayInfo?.user?.latestContactList?.relays()?.size ?: "--"
|
|
||||||
|
|
||||||
Text(text = "$userRelaysBeingUsed / $userRelays ${stringResource(R.string.relays)}")
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
tabs.forEachIndexed { index, function ->
|
tabs.forEachIndexed { index, function ->
|
||||||
@@ -306,6 +261,72 @@ fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RelaysTabHeader(baseUser: User) {
|
||||||
|
val userState by baseUser.live().relays.observeAsState()
|
||||||
|
val userRelaysBeingUsed = remember(userState) { userState?.user?.relaysBeingUsed?.size ?: "--" }
|
||||||
|
|
||||||
|
val userStateRelayInfo by baseUser.live().relayInfo.observeAsState()
|
||||||
|
val userRelays = remember(userStateRelayInfo) { userStateRelayInfo?.user?.latestContactList?.relays()?.size ?: "--" }
|
||||||
|
|
||||||
|
Text(text = "$userRelaysBeingUsed / $userRelays ${stringResource(R.string.relays)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ReportsTabHeader(baseUser: User) {
|
||||||
|
val userState by baseUser.live().reports.observeAsState()
|
||||||
|
val userReports = remember(userState) { userState?.user?.reports?.values?.flatten()?.count() }
|
||||||
|
|
||||||
|
Text(text = "$userReports ${stringResource(R.string.reports)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BookmarkTabHeader(baseUser: User) {
|
||||||
|
val userState by baseUser.live().bookmarks.observeAsState()
|
||||||
|
val userBookmarks = remember(userState) {
|
||||||
|
val bookmarkList = userState?.user?.latestBookmarkList
|
||||||
|
(bookmarkList?.taggedEvents()?.count() ?: 0) + (
|
||||||
|
bookmarkList?.taggedAddresses()?.count()
|
||||||
|
?: 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(text = "$userBookmarks ${stringResource(R.string.bookmarks)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ZapTabHeader(baseUser: User) {
|
||||||
|
val userState by baseUser.live().zaps.observeAsState()
|
||||||
|
var zapAmount by remember { mutableStateOf<BigDecimal?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = userState) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val tempAmount = baseUser.zappedAmount()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
zapAmount = tempAmount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(text = "${showAmount(zapAmount)} ${stringResource(id = R.string.zaps)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun FollowersTabHeader(baseUser: User) {
|
||||||
|
val userState by baseUser.live().follows.observeAsState()
|
||||||
|
val userFollowers = remember(userState) { userState?.user?.transientFollowerCount() ?: "--" }
|
||||||
|
|
||||||
|
Text(text = "$userFollowers ${stringResource(id = R.string.followers)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun FollowTabHeader(baseUser: User) {
|
||||||
|
val userState by baseUser.live().follows.observeAsState()
|
||||||
|
val userFollows = remember(userState) { userState?.user?.transientFollowCount() ?: "--" }
|
||||||
|
|
||||||
|
Text(text = "$userFollows ${stringResource(R.string.follows)}")
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ProfileHeader(
|
private fun ProfileHeader(
|
||||||
baseUser: User,
|
baseUser: User,
|
||||||
@@ -316,9 +337,6 @@ private fun ProfileHeader(
|
|||||||
var popupExpanded by remember { mutableStateOf(false) }
|
var popupExpanded by remember { mutableStateOf(false) }
|
||||||
var zoomImageDialogOpen by remember { mutableStateOf(false) }
|
var zoomImageDialogOpen by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val accountUserState by account.userProfile().live().follows.observeAsState()
|
|
||||||
val accountUser = accountUserState?.user ?: return
|
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboardManager = LocalClipboardManager.current
|
||||||
|
|
||||||
@@ -398,23 +416,7 @@ private fun ProfileHeader(
|
|||||||
// No need for this button anymore
|
// No need for this button anymore
|
||||||
// NPubCopyButton(baseUser)
|
// NPubCopyButton(baseUser)
|
||||||
|
|
||||||
if (accountUser == baseUser) {
|
ProfileActions(baseUser, account, coroutineScope)
|
||||||
EditButton(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account.isHidden(baseUser)) {
|
|
||||||
ShowUserButton {
|
|
||||||
account.showUser(baseUser.pubkeyHex)
|
|
||||||
}
|
|
||||||
} else if (accountUser.isFollowingCached(baseUser)) {
|
|
||||||
UnfollowButton { coroutineScope.launch(Dispatchers.IO) { account.unfollow(baseUser) } }
|
|
||||||
} else {
|
|
||||||
if (baseUser.isFollowingCached(accountUser)) {
|
|
||||||
FollowButton({ coroutineScope.launch(Dispatchers.IO) { account.follow(baseUser) } }, R.string.follow_back)
|
|
||||||
} else {
|
|
||||||
FollowButton({ coroutineScope.launch(Dispatchers.IO) { account.follow(baseUser) } }, R.string.follow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,14 +432,44 @@ private fun ProfileHeader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@Composable
|
||||||
|
private fun ProfileActions(
|
||||||
|
baseUser: User,
|
||||||
|
account: Account,
|
||||||
|
coroutineScope: CoroutineScope
|
||||||
|
) {
|
||||||
|
val accountUserState by account.userProfile().live().follows.observeAsState()
|
||||||
|
val accountUser = remember(accountUserState) { accountUserState?.user } ?: return
|
||||||
|
|
||||||
|
if (accountUser == baseUser) {
|
||||||
|
EditButton(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.isHidden(baseUser)) {
|
||||||
|
ShowUserButton {
|
||||||
|
account.showUser(baseUser.pubkeyHex)
|
||||||
|
}
|
||||||
|
} else if (accountUser.isFollowingCached(baseUser)) {
|
||||||
|
UnfollowButton { coroutineScope.launch(Dispatchers.IO) { account.unfollow(baseUser) } }
|
||||||
|
} else {
|
||||||
|
if (baseUser.isFollowingCached(accountUser)) {
|
||||||
|
FollowButton(
|
||||||
|
{ coroutineScope.launch(Dispatchers.IO) { account.follow(baseUser) } },
|
||||||
|
R.string.follow_back
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
FollowButton(
|
||||||
|
{ coroutineScope.launch(Dispatchers.IO) { account.follow(baseUser) } },
|
||||||
|
R.string.follow
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewModel: AccountViewModel, navController: NavController) {
|
private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
val userState by baseUser.live().metadata.observeAsState()
|
val userState by baseUser.live().metadata.observeAsState()
|
||||||
val user = userState?.user ?: return
|
val user = remember(userState) { userState?.user } ?: return
|
||||||
|
|
||||||
val userBadgeState by baseUser.live().badges.observeAsState()
|
|
||||||
val userBadge = userBadgeState?.user ?: return
|
|
||||||
|
|
||||||
val uri = LocalUriHandler.current
|
val uri = LocalUriHandler.current
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboardManager = LocalClipboardManager.current
|
||||||
@@ -513,23 +545,7 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userBadge.acceptedBadges?.let { note ->
|
DisplayBadges(baseUser, navController)
|
||||||
(note.event as? BadgeProfilesEvent)?.let { event ->
|
|
||||||
FlowRow(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 5.dp)) {
|
|
||||||
event.badgeAwardEvents().forEach { badgeAwardEvent ->
|
|
||||||
val baseNote = LocalCache.notes[badgeAwardEvent]
|
|
||||||
if (baseNote != null) {
|
|
||||||
val badgeAwardState by baseNote.live().metadata.observeAsState()
|
|
||||||
val baseBadgeDefinition = badgeAwardState?.note?.replyTo?.firstOrNull()
|
|
||||||
|
|
||||||
if (baseBadgeDefinition != null) {
|
|
||||||
BadgeThumb(baseBadgeDefinition, navController, 35.dp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayNip05ProfileStatus(user)
|
DisplayNip05ProfileStatus(user)
|
||||||
|
|
||||||
@@ -552,9 +568,58 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var zapExpanded by remember { mutableStateOf(false) }
|
val lud16 = remember(userState) { user.info?.lud16?.trim() ?: user.info?.lud06?.trim() }
|
||||||
|
val pubkeyHex = remember { baseUser.pubkeyHex }
|
||||||
|
DisplayLNAddress(lud16, pubkeyHex, account, scope, context)
|
||||||
|
|
||||||
val lud16 = user.info?.lud16?.trim() ?: user.info?.lud06?.trim()
|
val identities = user.info?.latestMetadata?.identityClaims()
|
||||||
|
if (!identities.isNullOrEmpty()) {
|
||||||
|
identities.forEach { identity: IdentityClaim ->
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Icon(
|
||||||
|
tint = Color.Unspecified,
|
||||||
|
painter = painterResource(id = identity.toIcon()),
|
||||||
|
contentDescription = stringResource(identity.toDescriptor()),
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
ClickableText(
|
||||||
|
text = AnnotatedString(identity.identity),
|
||||||
|
onClick = { runCatching { uri.openUri(identity.toProofUrl()) } },
|
||||||
|
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 1.dp, bottom = 1.dp, start = 5.dp)
|
||||||
|
.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user.info?.about?.let {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
|
||||||
|
) {
|
||||||
|
TranslatableRichTextViewer(
|
||||||
|
content = it,
|
||||||
|
canPreview = false,
|
||||||
|
tags = null,
|
||||||
|
backgroundColor = MaterialTheme.colors.background,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DisplayLNAddress(
|
||||||
|
lud16: String?,
|
||||||
|
userHex: String,
|
||||||
|
account: Account,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
var zapExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
if (!lud16.isNullOrEmpty()) {
|
if (!lud16.isNullOrEmpty()) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
@@ -576,10 +641,13 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (zapExpanded) {
|
if (zapExpanded) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 5.dp)) {
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(vertical = 5.dp)
|
||||||
|
) {
|
||||||
InvoiceRequest(
|
InvoiceRequest(
|
||||||
lud16,
|
lud16,
|
||||||
baseUser.pubkeyHex,
|
userHex,
|
||||||
account,
|
account,
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
// pay directly
|
// pay directly
|
||||||
@@ -619,44 +687,34 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val identities = user.info?.latestMetadata?.identityClaims()
|
@Composable
|
||||||
if (!identities.isNullOrEmpty()) {
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
identities.forEach { identity: IdentityClaim ->
|
private fun DisplayBadges(
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
baseUser: User,
|
||||||
Icon(
|
navController: NavController
|
||||||
tint = Color.Unspecified,
|
) {
|
||||||
painter = painterResource(id = identity.toIcon()),
|
val userBadgeState by baseUser.live().badges.observeAsState()
|
||||||
contentDescription = stringResource(identity.toDescriptor()),
|
val userBadge = remember(userBadgeState) { userBadgeState?.user } ?: return
|
||||||
modifier = Modifier.size(16.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
ClickableText(
|
userBadge.acceptedBadges?.let { note ->
|
||||||
text = AnnotatedString(identity.identity),
|
(note.event as? BadgeProfilesEvent)?.let { event ->
|
||||||
onClick = { runCatching { uri.openUri(identity.toProofUrl()) } },
|
FlowRow(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 5.dp)) {
|
||||||
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary),
|
event.badgeAwardEvents().forEach { badgeAwardEvent ->
|
||||||
modifier = Modifier
|
val baseNote = LocalCache.notes[badgeAwardEvent]
|
||||||
.padding(top = 1.dp, bottom = 1.dp, start = 5.dp)
|
if (baseNote != null) {
|
||||||
.weight(1f)
|
val badgeAwardState by baseNote.live().metadata.observeAsState()
|
||||||
)
|
val baseBadgeDefinition = badgeAwardState?.note?.replyTo?.firstOrNull()
|
||||||
|
|
||||||
|
if (baseBadgeDefinition != null) {
|
||||||
|
BadgeThumb(baseBadgeDefinition, navController, 35.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user.info?.about?.let {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
|
|
||||||
) {
|
|
||||||
TranslatableRichTextViewer(
|
|
||||||
content = it,
|
|
||||||
canPreview = false,
|
|
||||||
tags = null,
|
|
||||||
backgroundColor = MaterialTheme.colors.background,
|
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -727,9 +785,8 @@ fun BadgeThumb(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun DrawBanner(baseUser: User) {
|
private fun DrawBanner(baseUser: User) {
|
||||||
val userState by baseUser.live().metadata.observeAsState()
|
val userState by baseUser.live().metadata.observeAsState()
|
||||||
val user = userState?.user ?: return
|
val banner = remember(userState) { userState?.user?.info?.banner }
|
||||||
|
|
||||||
val banner = user.info?.banner
|
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboardManager = LocalClipboardManager.current
|
||||||
var zoomImageDialogOpen by remember { mutableStateOf(false) }
|
var zoomImageDialogOpen by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -767,67 +824,58 @@ private fun DrawBanner(baseUser: User) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabNotesNewThreads(accountViewModel: AccountViewModel, navController: NavController) {
|
fun TabNotesNewThreads(accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val feedViewModel: NostrUserProfileNewThreadsFeedViewModel = viewModel()
|
||||||
if (accountState != null) {
|
|
||||||
val feedViewModel: NostrUserProfileNewThreadsFeedViewModel = viewModel()
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
feedViewModel.invalidateData()
|
feedViewModel.invalidateData()
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(Modifier.fillMaxHeight()) {
|
Column(Modifier.fillMaxHeight()) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(vertical = 0.dp)
|
modifier = Modifier.padding(vertical = 0.dp)
|
||||||
) {
|
) {
|
||||||
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
|
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabNotesConversations(accountViewModel: AccountViewModel, navController: NavController) {
|
fun TabNotesConversations(accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val feedViewModel: NostrUserProfileConversationsFeedViewModel = viewModel()
|
||||||
if (accountState != null) {
|
|
||||||
val feedViewModel: NostrUserProfileConversationsFeedViewModel = viewModel()
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
feedViewModel.invalidateData()
|
feedViewModel.invalidateData()
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(Modifier.fillMaxHeight()) {
|
Column(Modifier.fillMaxHeight()) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(vertical = 0.dp)
|
modifier = Modifier.padding(vertical = 0.dp)
|
||||||
) {
|
) {
|
||||||
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
|
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabBookmarks(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
|
fun TabBookmarks(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val feedViewModel: NostrUserProfileBookmarksFeedViewModel = viewModel()
|
||||||
val userState by baseUser.live().bookmarks.observeAsState()
|
|
||||||
if (accountState != null) {
|
|
||||||
val feedViewModel: NostrUserProfileBookmarksFeedViewModel = viewModel()
|
|
||||||
|
|
||||||
LaunchedEffect(userState) {
|
LaunchedEffect(Unit) {
|
||||||
feedViewModel.invalidateData()
|
feedViewModel.invalidateData()
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(Modifier.fillMaxHeight()) {
|
Column(Modifier.fillMaxHeight()) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(vertical = 0.dp)
|
modifier = Modifier.padding(vertical = 0.dp)
|
||||||
) {
|
) {
|
||||||
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
|
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabFollows(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
|
fun TabFollows(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
|
println("TabFollows")
|
||||||
val feedViewModel: NostrUserProfileFollowsUserFeedViewModel = viewModel()
|
val feedViewModel: NostrUserProfileFollowsUserFeedViewModel = viewModel()
|
||||||
|
|
||||||
val userState by baseUser.live().follows.observeAsState()
|
val userState by baseUser.live().follows.observeAsState()
|
||||||
@@ -937,41 +985,6 @@ fun TabRelays(user: User, accountViewModel: AccountViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun NPubCopyButton(
|
|
||||||
user: User
|
|
||||||
) {
|
|
||||||
val clipboardManager = LocalClipboardManager.current
|
|
||||||
var popupExpanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Button(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 3.dp)
|
|
||||||
.width(50.dp),
|
|
||||||
onClick = { popupExpanded = true },
|
|
||||||
shape = RoundedCornerShape(20.dp),
|
|
||||||
colors = ButtonDefaults
|
|
||||||
.buttonColors(
|
|
||||||
backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
tint = Color.White,
|
|
||||||
imageVector = Icons.Default.Share,
|
|
||||||
contentDescription = stringResource(R.string.copies_the_public_key_to_the_clipboard_for_sharing)
|
|
||||||
)
|
|
||||||
|
|
||||||
DropdownMenu(
|
|
||||||
expanded = popupExpanded,
|
|
||||||
onDismissRequest = { popupExpanded = false }
|
|
||||||
) {
|
|
||||||
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); popupExpanded = false }) {
|
|
||||||
Text(stringResource(R.string.copy_public_key_npub_to_the_clipboard))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MessageButton(user: User, navController: NavController) {
|
private fun MessageButton(user: User, navController: NavController) {
|
||||||
Button(
|
Button(
|
||||||
|
|||||||
Reference in New Issue
Block a user