Displays the amount and list of reports in the Profile page.

This commit is contained in:
Vitor Pamplona 2023-02-18 19:14:52 -05:00
parent 0914a7b68c
commit 666972fd62
3 changed files with 92 additions and 17 deletions

View File

@ -0,0 +1,18 @@
package com.vitorpamplona.amethyst.ui.dal
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.model.LnZapEvent
object UserProfileReportsFeedFilter: FeedFilter<Note>() {
var user: User? = null
fun loadUserProfile(userId: String) {
user = LocalCache.getOrCreateUser(userId)
}
override fun feed(): List<Note> {
return user?.reports?.values?.flatten()?.sortedBy { it.event?.createdAt }?.reversed() ?: emptyList()
}
}

View File

@ -105,8 +105,9 @@ fun NoteCompose(
var moreActionsExpanded by remember { mutableStateOf(false) }
val noteEvent = note?.event
if (note?.event == null) {
if (noteEvent == null) {
BlankNote(modifier.combinedClickable(
onClick = { },
onLongClick = { popupExpanded = true },
@ -127,7 +128,7 @@ fun NoteCompose(
routeForLastRead?.let {
val lastTime = NotificationCache.load(it, context)
val createdAt = note.event?.createdAt
val createdAt = noteEvent.createdAt
if (createdAt != null) {
NotificationCache.markAsRead(it, createdAt, context)
isNew = createdAt > lastTime
@ -138,7 +139,7 @@ fun NoteCompose(
Column(modifier =
modifier.combinedClickable(
onClick = {
if (note.event !is ChannelMessageEvent) {
if (noteEvent !is ChannelMessageEvent) {
navController.navigate("Note/${note.idHex}"){
launchSingleTop = true
}
@ -174,7 +175,7 @@ fun NoteCompose(
NoteAuthorPicture(note, navController, account.userProfile(), 55.dp)
if (note.event is RepostEvent) {
if (noteEvent is RepostEvent) {
note.replyTo?.lastOrNull()?.let {
Box(
Modifier
@ -190,7 +191,7 @@ fun NoteCompose(
// boosted picture
val baseChannel = note.channel
if (note.event is ChannelMessageEvent && baseChannel != null) {
if (noteEvent is ChannelMessageEvent && baseChannel != null) {
val channelState by baseChannel.live.observeAsState()
val channel = channelState?.channel
@ -222,7 +223,7 @@ fun NoteCompose(
}
}
if (note.event is RepostEvent) {
if (noteEvent is RepostEvent) {
note.replyTo?.lastOrNull()?.let {
RelayBadges(it)
}
@ -236,9 +237,9 @@ fun NoteCompose(
Row(verticalAlignment = Alignment.CenterVertically) {
NoteUsernameDisplay(note, Modifier.weight(1f))
if (note.event !is RepostEvent) {
if (noteEvent !is RepostEvent) {
Text(
timeAgo(note.event?.createdAt),
timeAgo(noteEvent.createdAt),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
maxLines = 1
)
@ -265,15 +266,15 @@ fun NoteCompose(
}
}
if (note.event is TextNoteEvent && (note.replyTo != null || note.mentions != null)) {
if (noteEvent is TextNoteEvent && (note.replyTo != null || note.mentions != null)) {
ReplyInformation(note.replyTo, note.mentions, navController)
} else if (note.event is ChannelMessageEvent && (note.replyTo != null || note.mentions != null)) {
} else if (noteEvent is ChannelMessageEvent && (note.replyTo != null || note.mentions != null)) {
note.channel?.let {
ReplyInformationChannel(note.replyTo, note.mentions, it, navController)
}
}
if (note.event is ReactionEvent || note.event is RepostEvent) {
if (noteEvent is ReactionEvent || noteEvent is RepostEvent) {
note.replyTo?.lastOrNull()?.let {
NoteCompose(
it,
@ -285,16 +286,35 @@ fun NoteCompose(
}
// Reposts have trash in their contents.
if (note.event is ReactionEvent) {
if (noteEvent is ReactionEvent) {
val refactorReactionText =
if (note.event?.content == "+") "" else note.event?.content ?: " "
if (noteEvent.content == "+") "" else noteEvent.content ?: " "
Text(
text = refactorReactionText
)
}
} else if (noteEvent is ReportEvent) {
val reportType = noteEvent.reportType.map {
when (it) {
ReportEvent.ReportType.EXPLICIT -> "Explicit Content"
ReportEvent.ReportType.SPAM -> "Spam"
ReportEvent.ReportType.IMPERSONATION -> "Impersonation"
ReportEvent.ReportType.ILLEGAL -> "Illegal Behavior"
else -> "Unkown"
}
}.joinToString(", ")
Text(
text = reportType
)
Divider(
modifier = Modifier.padding(top = 10.dp),
thickness = 0.25.dp
)
} else {
val eventContent = note.event?.content
val eventContent = noteEvent.content
val canPreview = note.author == account.userProfile()
|| (note.author?.let { account.userProfile().isFollowing(it) } ?: true )
|| !noteForReports.hasAnyReports()
@ -303,7 +323,7 @@ fun NoteCompose(
TranslateableRichTextViewer(
eventContent,
canPreview,
note.event?.tags,
noteEvent.tags,
accountViewModel,
navController
)

View File

@ -58,6 +58,7 @@ import com.vitorpamplona.amethyst.ui.components.InvoiceRequest
import com.vitorpamplona.amethyst.ui.dal.UserProfileFollowersFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileFollowsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileNoteFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileReportsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileZapsFeedFilter
import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.note.showAmount
@ -79,6 +80,7 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
UserProfileFollowersFeedFilter.loadUserProfile(account, userId)
UserProfileFollowsFeedFilter.loadUserProfile(account, userId)
UserProfileZapsFeedFilter.loadUserProfile(userId)
UserProfileReportsFeedFilter.loadUserProfile(userId)
NostrUserProfileDataSource.loadUserProfile(userId)
@ -101,6 +103,9 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
println("Profile Dispose")
NostrUserProfileDataSource.loadUserProfile(null)
NostrUserProfileDataSource.stop()
}
}
@ -203,6 +208,17 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
Tab(
selected = pagerState.currentPage == 4,
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(4) } },
text = {
val userState by baseUser.liveReports.observeAsState()
val userReports = userState?.user?.reports?.values?.flatten()?.count()
Text(text = "${userReports} Reports")
}
)
Tab(
selected = pagerState.currentPage == 5,
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(5) } },
text = {
val userState by baseUser.liveRelays.observeAsState()
val userRelaysBeingUsed =
@ -216,7 +232,7 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
)
}
HorizontalPager(
count = 5,
count = 6,
state = pagerState,
modifier = with(LocalDensity.current) {
Modifier.height((columnSize.height - tabsSize.height).toDp())
@ -227,7 +243,8 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
1 -> TabFollows(baseUser, accountViewModel, navController)
2 -> TabFollowers(baseUser, accountViewModel, navController)
3 -> TabReceivedZaps(baseUser, accountViewModel, navController)
4 -> TabRelays(baseUser, accountViewModel, navController)
4 -> TabReports(baseUser, accountViewModel, navController)
5 -> TabRelays(baseUser, accountViewModel, navController)
}
}
}
@ -515,6 +532,26 @@ fun TabReceivedZaps(user: User, accountViewModel: AccountViewModel, navControlle
}
}
@Composable
fun TabReports(user: User, accountViewModel: AccountViewModel, navController: NavController) {
val accountState by accountViewModel.accountLiveData.observeAsState()
if (accountState != null) {
val feedViewModel: NostrUserProfileReportFeedViewModel = viewModel()
LaunchedEffect(Unit) {
feedViewModel.refresh()
}
Column(Modifier.fillMaxHeight()) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
FeedView(feedViewModel, accountViewModel, navController, null)
}
}
}
}
@Composable
fun TabRelays(user: User, accountViewModel: AccountViewModel, navController: NavController) {
val feedViewModel: RelayFeedViewModel = viewModel()