From 7f92d39adf260ee7e9b34c09a180bb86e31667a5 Mon Sep 17 00:00:00 2001 From: aecsocket <43144841+aecsocket@users.noreply.github.com> Date: Sun, 12 Jul 2026 10:21:08 +0100 Subject: [PATCH 1/4] remaining globally safe/unsafe buttons --- .../ui/moderation/ModerationTechRevCard.vue | 213 +++++++++++++++--- 1 file changed, 181 insertions(+), 32 deletions(-) diff --git a/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue b/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue index e99376989b..a23d0e0f37 100644 --- a/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue +++ b/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue @@ -4,7 +4,6 @@ import { BanIcon, BugIcon, CheckCheckIcon, - CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, @@ -20,7 +19,6 @@ import { ShieldCheckIcon, SpinnerIcon, TimerIcon, - TriangleAlertIcon, XIcon, } from '@modrinth/assets' import { type TechReviewContext, techReviewQuickReplies } from '@modrinth/moderation' @@ -615,6 +613,18 @@ const remainingUnmarkedCount = computed(() => { return getFileDetailCount(selectedFile.value) - getFileMarkedCount(selectedFile.value) }) +function getSelectedFileFlags(): ClassGroup['flags'] { + if (!selectedFile.value) return [] + + return selectedFile.value.issues.flatMap((issue) => + issue.details.map((detail) => ({ + issueId: issue.id, + issueType: issue.issue_type, + detail, + })), + ) +} + function getJarFlags(jarGroup: JarGroup): ClassGroup['flags'] { return jarGroup.classes.flatMap((classItem) => classItem.flags) } @@ -629,6 +639,74 @@ function getJarRemainingUnmarkedCount(jarGroup: JarGroup): number { const isBatchUpdating = ref(false) +function getRemainingGlobalDetailCount(flags: ClassGroup['flags']): number { + return new Set( + flags + .filter( + (flag) => + getDetailDecision(flag.detail.id, flag.detail.status) === 'pending' && + canUpdateGlobalDetail(flag.detail), + ) + .map((flag) => flag.detail.key), + ).size +} + +async function batchMarkRemainingGlobally(flags: ClassGroup['flags'], verdict: 'safe' | 'unsafe') { + if (isBatchUpdating.value) return + + const detailsByKey = new Map( + flags + .filter( + (flag) => + getDetailDecision(flag.detail.id, flag.detail.status) === 'pending' && + canUpdateGlobalDetail(flag.detail), + ) + .map((flag) => [flag.detail.key, flag.detail]), + ) + const details = [...detailsByKey.values()] + + if (details.length === 0) return + + isBatchUpdating.value = true + try { + await client.labrinth.tech_review_internal.updateGlobalIssueDetails( + details.map((detail) => ({ detail_key: detail.key, verdict })), + ) + + applyDecisionToRelatedDetails( + details.map((detail) => detail.id), + verdictToDecision(verdict), + 'global', + ) + + addNotification({ + type: 'success', + title: `Globally marked ${details.length} trace keys as ${verdict}`, + text: `All remaining eligible traces have been globally marked as ${ + verdict === 'safe' ? 'false positives' : 'malicious' + }.`, + }) + + if ( + selectedFile.value && + getFileMarkedCount(selectedFile.value) === getFileDetailCount(selectedFile.value) + ) { + backToFileList() + } + + emit('refetch') + } catch (error) { + console.error('Failed to batch update global traces:', error) + addNotification({ + type: 'error', + title: 'Global batch update failed', + text: 'An error occurred while globally updating traces.', + }) + } finally { + isBatchUpdating.value = false + } +} + async function batchMarkRemaining(verdict: 'safe' | 'unsafe') { if (!selectedFile.value || isBatchUpdating.value) return @@ -1590,20 +1668,53 @@ function copyId() { diff --git a/apps/frontend/src/components/ui/moderation/GlobalDetailTracesList.vue b/apps/frontend/src/components/ui/moderation/GlobalDetailTracesList.vue index ec6cf6237a..11cf98ad77 100644 --- a/apps/frontend/src/components/ui/moderation/GlobalDetailTracesList.vue +++ b/apps/frontend/src/components/ui/moderation/GlobalDetailTracesList.vue @@ -44,16 +44,30 @@ >
-
+
+
+

+ Issue + {{ getLatestLocalTrace(trace)?.issue_type }} +

+

+ Path + {{ decodeTracePath(getLatestLocalTrace(trace)?.file_path ?? '') }} +

-

- {{ formatTraceCount(trace.local_trace_count) }} -

@@ -70,15 +84,14 @@
-
+

- Showing first {{ getPreviewLocalTraces(trace).length }} of - {{ getVisibleLocalTraceTotal(trace) }} local traces + Showing {{ getPreviewLocalTraces(trace).length }} of + {{ formatTraceCount(getVisibleLocalTraceTotal(trace)) }}

- +