feat: moderation settings (#6895)

* feat: moderation settings

* chore: move moderation specific feature flag to settings

* feat: force open collapsed regions if opened in new tab

* chore: remove unused field

* chore: run prepr
This commit is contained in:
ThatGravyBoat
2026-07-28 19:38:56 +00:00
committed by GitHub
parent 82348fe40a
commit fe69e04785
25 changed files with 698 additions and 416 deletions
+28 -29
View File
@@ -469,16 +469,12 @@
</div>
<ClientOnly>
<div
<ModerationChecklist
v-if="auth.user && tags.staffRoles.includes(auth.user.role) && showModerationChecklist"
class="moderation-checklist"
>
<ModerationChecklist
:collapsed="collapsedModerationChecklist"
@exit="setModerationChecklistOpen(false)"
@toggle-collapsed="collapsedModerationChecklist = !collapsedModerationChecklist"
/>
</div>
:collapsed="collapsedModerationChecklist"
@exit="setModerationChecklistOpen(false)"
@toggle-collapsed="collapsedModerationChecklist = !collapsedModerationChecklist"
/>
</ClientOnly>
<template v-if="hasEditDetailsPermission">
@@ -505,6 +501,7 @@ import {
SettingsIcon,
XIcon,
} from '@modrinth/assets'
import { moderationSettings } from '@modrinth/moderation'
import {
Admonition,
Avatar,
@@ -535,7 +532,7 @@ import {
useStickyObserver,
useVIntl,
} from '@modrinth/ui'
import { formatProjectType } from '@modrinth/utils'
import { formatProjectType, isStaff } from '@modrinth/utils'
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import { useLocalStorage } from '@vueuse/core'
import { Tooltip } from 'floating-vue'
@@ -572,6 +569,8 @@ const router = useRouter()
const signInRouteObj = computed(() => getSignInRouteObj(route))
const config = useRuntimeConfig()
const moderationQueue = useModerationQueue()
const keybinds = useModerationKeybinds()
const modSettings = useModerationSettings()
const notifications = injectNotificationManager()
const { addNotification } = notifications
@@ -1965,6 +1964,25 @@ async function deleteVersion(id) {
stopLoading()
}
// moderation project keybinds
onMounted(() => window.addEventListener('keydown', handleKeybinds))
onUnmounted(() => window.removeEventListener('keydown', handleKeybinds))
function handleKeybinds(event) {
if (!isStaff(auth.value.user)) return
if (
!showModerationChecklist.value &&
!modSettings.value.get(moderationSettings.General.ProjectKeybinds)
)
return
keybinds.value.handle(event, {
project: projectRaw.value,
scope: 'project',
})
}
const navLinks = computed(() => {
const routeType = route.params.type || project.value.project_type
const projectUrl = `/${routeType}/${project.value.slug ? project.value.slug : project.value.id}`
@@ -2189,26 +2207,7 @@ provideProjectPageContext({
}
}
.moderation-checklist {
position: fixed;
bottom: 1rem;
right: 1rem;
overflow-y: auto;
z-index: 50;
transition: bottom 0.25s ease-in-out;
> div {
box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
}
}
.new-page {
column-gap: 1.5rem;
}
</style>
<style lang="scss">
body.floating-action-bar-shown .moderation-checklist {
bottom: 6rem;
}
</style>
@@ -28,6 +28,6 @@ const { data: report } = useQuery({
<template>
<div class="flex flex-col gap-3">
<ModerationReportCard v-if="report" :report="report" />
<ModerationReportCard v-if="report" :report="report" :collapsed="false" />
</div>
</template>
@@ -177,7 +177,12 @@
<div class="flex flex-col gap-4">
<div v-if="paginatedReports.length === 0" class="universal-card h-24 animate-pulse"></div>
<ReportCard v-for="report in paginatedReports" :key="report.id" :report="report" />
<ReportCard
v-for="report in paginatedReports"
:key="report.id"
:report="report"
:collapsed="true"
/>
</div>
<div v-if="totalPages > 1" class="mt-4 flex justify-center">
@@ -305,6 +305,7 @@ function refetch() {
:focused-detail-id="focusedDetailId"
:loading-issues="loadingIssues"
:decompiled-sources="decompiledSources"
:collapsed="false"
@refetch="refetch"
@load-issue-sources="handleLoadIssueSources"
@mark-complete="handleMarkComplete"
@@ -719,6 +719,7 @@ watch(totalPages, (pages) => {
:item="item"
:loading-issues="loadingIssues"
:decompiled-sources="decompiledSources"
:collapsed="true"
@refetch="refetch"
@load-issue-sources="handleLoadIssueSources"
@mark-complete="handleMarkComplete"
+10
View File
@@ -79,6 +79,14 @@
icon: ServerIcon,
}
: null,
isStaff(auth.user) ? { type: 'heading', label: 'Staff' } : null,
isStaff(auth.user)
? {
link: '/settings/moderation',
label: 'Moderation',
icon: ScaleIcon,
}
: null,
].filter(Boolean)
"
/>
@@ -95,6 +103,7 @@ import {
LanguagesIcon,
MonitorSmartphoneIcon,
PaintbrushIcon,
ScaleIcon,
ServerIcon,
ShieldIcon,
ToggleRightIcon,
@@ -107,6 +116,7 @@ import {
NormalPage,
useVIntl,
} from '@modrinth/ui'
import { isStaff } from '@modrinth/utils'
import NavStack from '~/components/ui/NavStack.vue'
@@ -0,0 +1,13 @@
<script setup lang="ts">
import ModerationKeybinds from '~/components/ui/moderation/settings/ModerationKeybinds.vue'
import ModerationSettings from '~/components/ui/moderation/settings/ModerationSettings.vue'
useSeoMeta({
robots: 'noindex',
})
</script>
<template>
<ModerationKeybinds />
<ModerationSettings />
</template>