mirror of
https://github.com/modrinth/code.git
synced 2026-08-30 11:36:05 +00:00
feat: moderation modpack scan button
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
<script setup lang="ts">
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { FolderSearchIcon, StarIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, injectModrinthClient, NewModal } from '@modrinth/ui'
|
||||
import { useTemplateRef } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
project_id: string
|
||||
}>()
|
||||
|
||||
const client = injectModrinthClient()
|
||||
const modalRef = useTemplateRef<InstanceType<typeof NewModal>>('modalRef')
|
||||
|
||||
const files = ref<Labrinth.Versions.v2.VersionFile[]>([])
|
||||
const scans = ref<Map<string, Labrinth.Attribution.Internal.FileScanResponse>>(new Map())
|
||||
const isFetching = ref(false)
|
||||
|
||||
async function fetchAllVersions() {
|
||||
const versions = await client.labrinth.versions_v2.getProjectVersions(props.project_id)
|
||||
files.value = versions.flatMap((version) => version.files)
|
||||
}
|
||||
|
||||
async function fetchAllScans() {
|
||||
isFetching.value = true
|
||||
for (const file of files.value) {
|
||||
scans.value.set(file.id!, await client.labrinth.attribution_internal.scanFile(file.id!))
|
||||
}
|
||||
isFetching.value = false
|
||||
}
|
||||
|
||||
function show() {
|
||||
isFetching.value = false
|
||||
fetchAllVersions()
|
||||
modalRef.value?.show()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
modalRef.value?.hide()
|
||||
}
|
||||
defineExpose({ show, hide })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NewModal
|
||||
ref="modalRef"
|
||||
width="60vw"
|
||||
:close-on-click-outside="false"
|
||||
:close-on-esc="false"
|
||||
:disable-close="isFetching"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex w-full items-center justify-between gap-2">
|
||||
<span class="text-2xl font-semibold text-contrast">
|
||||
Modpack Scan ({{ scans.size }}/{{ files.length }} Files)
|
||||
</span>
|
||||
<div>
|
||||
<ButtonStyled circular>
|
||||
<button v-tooltip="'Scan All Files'" :disabled="isFetching" @click="fetchAllScans">
|
||||
<FolderSearchIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="w-full">
|
||||
<table class="w-full table-fixed border-collapse border-0">
|
||||
<thead>
|
||||
<tr class="bg-surface-5 font-bold">
|
||||
<td class="p-2" style="width: 80%">Pack File Name</td>
|
||||
<td class="p-2 text-center" style="width: 10%">New Files</td>
|
||||
<td class="p-2 text-center" style="width: 10%">New Groups</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="(file, index) in files" :key="file.id">
|
||||
<tr :class="index % 2 === 0 ? 'bg-surface-1' : 'bg-surface-2'">
|
||||
<td class="flex h-full flex-row items-center gap-1 p-2">
|
||||
<StarIcon v-if="file.primary" />{{ file.filename }}
|
||||
</td>
|
||||
|
||||
<td class="p-2 text-center">
|
||||
<span v-if="!scans.get(file.id!!)">...</span>
|
||||
<span v-else>{{ scans.get(file.id!!)?.new_attribution_files!!! }}</span>
|
||||
</td>
|
||||
|
||||
<td class="p-2 text-center">
|
||||
<span v-if="!scans.get(file.id!!)">...</span>
|
||||
<span v-else>{{ scans.get(file.id!!)?.new_attribution_groups!!! }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="scans.get(file.id!!)?.scanned_file_names"
|
||||
:class="index % 2 === 0 ? 'bg-surface-1' : 'bg-surface-2'"
|
||||
class="w-full"
|
||||
>
|
||||
<td colspan="3" class="p-2 pt-0">
|
||||
<details>
|
||||
<summary>
|
||||
Override Files ({{ scans.get(file.id!!)?.scanned_file_names?.length || 0 }})
|
||||
</summary>
|
||||
<div class="flex flex-wrap gap-1 pt-2">
|
||||
<span
|
||||
v-for="name of scans.get(file.id!!)?.scanned_file_names || []"
|
||||
:key="name"
|
||||
v-tooltip="name"
|
||||
class="flex items-center gap-1 text-wrap rounded-full bg-button-bg px-2 py-0.5 text-xs font-medium text-contrast"
|
||||
>
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</NewModal>
|
||||
</template>
|
||||
@@ -95,6 +95,8 @@
|
||||
@download="triggerDownloadAnimation"
|
||||
/>
|
||||
<CollectionCreateModal ref="modal_collection" :project-ids="[project.id]" />
|
||||
<ModpackScanModal ref="scanModal" :project_id="project.id" />
|
||||
|
||||
<div
|
||||
class="new-page sidebar"
|
||||
:class="{
|
||||
@@ -424,13 +426,6 @@
|
||||
tags.staffRoles.includes(auth.user.role) &&
|
||||
!showModerationChecklist,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown:
|
||||
auth.user &&
|
||||
tags.staffRoles.includes(auth.user.role) &&
|
||||
!showModerationChecklist,
|
||||
},
|
||||
{
|
||||
id: 'tech-review',
|
||||
link: `/moderation/technical-review/${project.id}`,
|
||||
@@ -438,6 +433,16 @@
|
||||
hoverOnly: true,
|
||||
shown: auth.user && tags.staffRoles.includes(auth.user.role),
|
||||
},
|
||||
{
|
||||
id: 'moderation-modpack-rescan',
|
||||
action: () => scanModal.show(),
|
||||
color: 'orange',
|
||||
hoverOnly: true,
|
||||
shown:
|
||||
auth.user &&
|
||||
tags.staffRoles.includes(auth.user.role) &&
|
||||
project.actualProjectType === 'modpack',
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: auth.user && tags.staffRoles.includes(auth.user.role),
|
||||
@@ -469,6 +474,9 @@
|
||||
<ScaleIcon aria-hidden="true" /> {{ formatMessage(messages.reviewProject) }}
|
||||
</template>
|
||||
<template #tech-review> <ScanEyeIcon aria-hidden="true" /> Tech review </template>
|
||||
<template #moderation-modpack-rescan>
|
||||
<FolderSearchIcon aria-hidden="true" /> Rescan modpack
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.reportButton) }}
|
||||
@@ -726,6 +734,7 @@ import {
|
||||
ClipboardCopyIcon,
|
||||
DownloadIcon,
|
||||
ExternalIcon,
|
||||
FolderSearchIcon,
|
||||
HeartIcon,
|
||||
ListIcon,
|
||||
MoreVerticalIcon,
|
||||
@@ -785,6 +794,7 @@ import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.
|
||||
import MessageBanner from '~/components/ui/MessageBanner.vue'
|
||||
import ModerationChecklist from '~/components/ui/moderation/checklist/ModerationChecklist.vue'
|
||||
import ModerationProjectNags from '~/components/ui/moderation/ModerationProjectNags.vue'
|
||||
import ModpackScanModal from '~/components/ui/moderation/ModpackScanModal.vue'
|
||||
import ProjectDownloadModal from '~/components/ui/ProjectDownloadModal/index.vue'
|
||||
import ProjectMemberHeader from '~/components/ui/ProjectMemberHeader.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.ts'
|
||||
@@ -852,6 +862,7 @@ const debug = useDebugLogger('DownloadModal')
|
||||
const downloadModal = ref()
|
||||
const openInAppModal = ref()
|
||||
const overTheTopDownloadAnimation = ref()
|
||||
const scanModal = ref()
|
||||
|
||||
const projectV3Loaded = computed(() => !projectV3Pending.value || projectV3.value != null)
|
||||
const isServerProject = computed(() => projectV3.value?.minecraft_server != null)
|
||||
|
||||
@@ -100,4 +100,21 @@ export class LabrinthAttributionInternalModule extends AbstractModule {
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a file for attribution information.
|
||||
* POST /_internal/attribution/file/{file_id}/scan
|
||||
*
|
||||
* @param fileId - The file ID to scan.
|
||||
*/
|
||||
public async scanFile(fileId: string): Promise<Labrinth.Attribution.Internal.FileScanResponse> {
|
||||
return this.client.request<Labrinth.Attribution.Internal.FileScanResponse>(
|
||||
`/attribution/file/${fileId}/scan`,
|
||||
{
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'POST',
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +397,12 @@ export namespace Labrinth {
|
||||
sha1: string
|
||||
project_id: string
|
||||
}
|
||||
|
||||
export type FileScanResponse = {
|
||||
new_attribution_groups: number
|
||||
new_attribution_files: number
|
||||
scanned_file_names: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1333,6 +1339,7 @@ export namespace Labrinth {
|
||||
}
|
||||
|
||||
export type VersionFile = {
|
||||
id?: string
|
||||
hashes: VersionFileHash
|
||||
url: string
|
||||
filename: string
|
||||
@@ -1454,6 +1461,7 @@ export namespace Labrinth {
|
||||
}
|
||||
|
||||
export interface VersionFile {
|
||||
id?: string
|
||||
hashes: VersionFileHash
|
||||
url: string
|
||||
filename: string
|
||||
|
||||
Reference in New Issue
Block a user