diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileReportsFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileReportsFeedFilter.kt new file mode 100644 index 000000000..3e0cc30b9 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/UserProfileReportsFeedFilter.kt @@ -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() { + var user: User? = null + + fun loadUserProfile(userId: String) { + user = LocalCache.getOrCreateUser(userId) + } + + override fun feed(): List { + return user?.reports?.values?.flatten()?.sortedBy { it.event?.createdAt }?.reversed() ?: emptyList() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 7190a5771..b7d880c53 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -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 ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt index a0f9e9178..5ad6a01af 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt @@ -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()