forked from didirus/AstralRinth
feat: moderation locking (#5070)
* feat: base locking impl * feat: lock logic in place in rev endpoint + fetch rev * feat: frontend impl and finalize * feat: auto skip if using the moderation queue page * fix: qa issues * fix: async state + locking fix * fix: lint * fix: fmt * fix: qa issue * fix: qa + redirect bug * fix: lint * feat: delete all locks endpoint for admins * fix: dedupe * fix: fmt * fix: project redirect move to middleware * fix: lint
This commit is contained in:
@@ -1019,8 +1019,12 @@ import { userCollectProject, userFollowProject } from '~/composables/user.js'
|
||||
import { useModerationStore } from '~/store/moderation.ts'
|
||||
import { reportProject } from '~/utils/report-helpers.ts'
|
||||
|
||||
definePageMeta({
|
||||
key: (route) => route.fullPath,
|
||||
})
|
||||
|
||||
const data = useNuxtApp()
|
||||
const route = useNativeRoute()
|
||||
const route = useRoute()
|
||||
const config = useRuntimeConfig()
|
||||
const moderationStore = useModerationStore()
|
||||
const notifications = injectNotificationManager()
|
||||
@@ -1539,8 +1543,6 @@ try {
|
||||
),
|
||||
])
|
||||
|
||||
await updateProjectRoute()
|
||||
|
||||
versions = shallowRef(toRaw(versions))
|
||||
versionsV3 = shallowRef(toRaw(versionsV3))
|
||||
versions.value = (versions.value ?? []).map((v) => ({
|
||||
@@ -1621,22 +1623,6 @@ if (!project.value) {
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
project.value.project_type !== route.params.type ||
|
||||
(route.params.id !== project.value.slug && !flags.value.disablePrettyProjectUrlRedirects)
|
||||
) {
|
||||
let path = route.fullPath.split('/')
|
||||
path.splice(0, 3)
|
||||
path = path.filter((x) => x)
|
||||
|
||||
await navigateTo(
|
||||
`/${project.value.project_type}/${project.value.slug}${
|
||||
path.length > 0 ? `/${path.join('/')}` : ''
|
||||
}`,
|
||||
{ redirectCode: 301, replace: true },
|
||||
)
|
||||
}
|
||||
|
||||
// Members should be an array of all members, without the accepted ones, and with the user with the Owner role at the start
|
||||
// The rest of the members should be sorted by role, then by name
|
||||
const members = computed(() => {
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
:queue-entry="item"
|
||||
:owner="item.owner"
|
||||
:org="item.org"
|
||||
@start-from-project="startFromProject"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -112,6 +113,7 @@ import {
|
||||
Combobox,
|
||||
type ComboboxOption,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Pagination,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -123,6 +125,7 @@ import { enrichProjectBatch, type ModerationProject } from '~/helpers/moderation
|
||||
import { useModerationStore } from '~/store/moderation.ts'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const moderationStore = useModerationStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -342,13 +345,105 @@ function goToPage(page: number) {
|
||||
currentPage.value = page
|
||||
}
|
||||
|
||||
function moderateAllInFilter() {
|
||||
moderationStore.setQueue(filteredProjects.value.map((queueItem) => queueItem.project.id))
|
||||
async function findFirstUnlockedProject(): Promise<ModerationProject | null> {
|
||||
let skippedCount = 0
|
||||
|
||||
while (moderationStore.hasItems) {
|
||||
const currentId = moderationStore.getCurrentProjectId()
|
||||
if (!currentId) return null
|
||||
|
||||
const project = filteredProjects.value.find((p) => p.project.id === currentId)
|
||||
if (!project) {
|
||||
moderationStore.completeCurrentProject(currentId, 'skipped')
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
const lockStatus = await moderationStore.checkLock(currentId)
|
||||
|
||||
if (!lockStatus.locked || lockStatus.expired) {
|
||||
if (skippedCount > 0) {
|
||||
addNotification({
|
||||
title: 'Skipped locked projects',
|
||||
text: `Skipped ${skippedCount} project(s) being moderated by others.`,
|
||||
type: 'info',
|
||||
})
|
||||
}
|
||||
return project
|
||||
}
|
||||
|
||||
// Project is locked, skip it
|
||||
moderationStore.completeCurrentProject(currentId, 'skipped')
|
||||
skippedCount++
|
||||
} catch {
|
||||
return project
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
async function moderateAllInFilter() {
|
||||
// Start from the current page - get projects from current page onwards
|
||||
const startIndex = (currentPage.value - 1) * itemsPerPage
|
||||
const projectsFromCurrentPage = filteredProjects.value.slice(startIndex)
|
||||
const projectIds = projectsFromCurrentPage.map((queueItem) => queueItem.project.id)
|
||||
moderationStore.setQueue(projectIds)
|
||||
|
||||
// Find first unlocked project
|
||||
const targetProject = await findFirstUnlockedProject()
|
||||
|
||||
if (!targetProject) {
|
||||
addNotification({
|
||||
title: 'All projects locked',
|
||||
text: 'All projects in queue are currently being moderated by others.',
|
||||
type: 'warning',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
navigateTo({
|
||||
name: 'type-id',
|
||||
params: {
|
||||
type: 'project',
|
||||
id: moderationStore.getCurrentProjectId(),
|
||||
id: targetProject.project.slug,
|
||||
},
|
||||
state: {
|
||||
showChecklist: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function startFromProject(projectId: string) {
|
||||
// Find the index of the clicked project in the filtered list
|
||||
const projectIndex = filteredProjects.value.findIndex((p) => p.project.id === projectId)
|
||||
if (projectIndex === -1) {
|
||||
// Project not found in filtered list, just moderate it alone
|
||||
moderationStore.setSingleProject(projectId)
|
||||
} else {
|
||||
// Start queue from this project onwards
|
||||
const projectsFromHere = filteredProjects.value.slice(projectIndex)
|
||||
const projectIds = projectsFromHere.map((queueItem) => queueItem.project.id)
|
||||
moderationStore.setQueue(projectIds)
|
||||
}
|
||||
|
||||
// Find first unlocked project
|
||||
const targetProject = await findFirstUnlockedProject()
|
||||
|
||||
if (!targetProject) {
|
||||
addNotification({
|
||||
title: 'All projects locked',
|
||||
text: 'All projects in queue are currently being moderated by others.',
|
||||
type: 'warning',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
navigateTo({
|
||||
name: 'type-id',
|
||||
params: {
|
||||
type: 'project',
|
||||
id: targetProject.project.slug,
|
||||
},
|
||||
state: {
|
||||
showChecklist: true,
|
||||
|
||||
Reference in New Issue
Block a user