mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 00:55:25 +00:00
add AI report types + fix report page fetching (#7175)
* add AI report types + fix report page fetching * use auth middleware on report
This commit is contained in:
@@ -4322,12 +4322,21 @@
|
||||
"report.not-for.dmca.description": {
|
||||
"message": "See our <policy-link>Copyright Policy</policy-link>."
|
||||
},
|
||||
"report.note.ai-images.1": {
|
||||
"message": "We've just updated our rules to prohibit AI-generated images in icons, galleries, and descriptions. We're giving creators a grace period to remove AI images from their projects before we begin accepting reports for them."
|
||||
},
|
||||
"report.note.check-back-later": {
|
||||
"message": "Check back later once the grace period has ended. Reports of this type are not being accepted at this time."
|
||||
},
|
||||
"report.note.copyright.1": {
|
||||
"message": "Please note that you are *not* submitting a DMCA takedown request, but rather a report of reuploaded content."
|
||||
},
|
||||
"report.note.copyright.2": {
|
||||
"message": "If you meant to file a DMCA takedown request (which is a legal action) instead, please see our <copyright-policy-link>Copyright Policy</copyright-policy-link>."
|
||||
},
|
||||
"report.note.fully-ai-generated.1": {
|
||||
"message": "We've just updated our rules to prohibit fully AI-generated projects. We're giving creators a grace period to update or take down fully AI-generated projects before we begin accepting reports for them."
|
||||
},
|
||||
"report.note.malicious.1": {
|
||||
"message": "Reports for malicious or deceptive content must include substantial evidence of the behavior, such as code samples."
|
||||
},
|
||||
@@ -4337,9 +4346,6 @@
|
||||
"report.note.missing-disclosure.1": {
|
||||
"message": "Content disclosures are a new feature we've just added. We're giving creators a grace period to get their disclosures up-to-date before we begin accepting reports for missing or incorrect disclosures."
|
||||
},
|
||||
"report.note.missing-disclosure.2": {
|
||||
"message": "Check back later once the grace period has ended. Reports of this type are not being accepted at this time."
|
||||
},
|
||||
"report.please-report": {
|
||||
"message": "Please report:"
|
||||
},
|
||||
|
||||
@@ -109,17 +109,7 @@
|
||||
<span class="text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.whatContentType) }}
|
||||
</span>
|
||||
<RadioButtons
|
||||
v-slot="{ item }"
|
||||
v-model="reportItem"
|
||||
:items="reportItems"
|
||||
@update:model-value="
|
||||
() => {
|
||||
prefilled = false
|
||||
fetchItem()
|
||||
}
|
||||
"
|
||||
>
|
||||
<RadioButtons v-slot="{ item }" v-model="reportItem" :items="reportItems">
|
||||
{{ capitalizeString(item) }}
|
||||
</RadioButtons>
|
||||
</div>
|
||||
@@ -139,13 +129,6 @@
|
||||
autocomplete="off"
|
||||
:disabled="reportItem === ''"
|
||||
wrapper-class="w-40"
|
||||
@blur="
|
||||
() => {
|
||||
prefilled = false
|
||||
reportItemID = reportItemID.trim()
|
||||
fetchItem()
|
||||
}
|
||||
"
|
||||
/>
|
||||
<div v-if="checkingId || checkedId" class="flex items-center gap-1">
|
||||
<template v-if="checkingId">
|
||||
@@ -163,8 +146,8 @@
|
||||
class="flex items-center gap-1 font-semibold text-contrast hover:underline"
|
||||
>
|
||||
<Avatar
|
||||
v-if="typeof itemIcon === 'string'"
|
||||
:src="itemIcon"
|
||||
v-if="typeof itemIcon === 'string' || !itemIcon"
|
||||
:src="itemIcon ?? null"
|
||||
:alt="itemName"
|
||||
size="24px"
|
||||
:circle="reportItem === 'user'"
|
||||
@@ -303,22 +286,22 @@ import {
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import type { Project, Report, User, Version } from '@modrinth/utils'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
import { useImageUpload } from '~/composables/image-upload.ts'
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
})
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
|
||||
const tags = useGeneratedState()
|
||||
const route = useNativeRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const auth = await useAuth()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
if (!auth.value.user) {
|
||||
router.push('/auth/sign-in?redirect=' + encodeURIComponent(route.fullPath))
|
||||
}
|
||||
|
||||
const accessQuery = (id: string): string => {
|
||||
return route.query?.[id]?.toString() || ''
|
||||
}
|
||||
@@ -368,58 +351,83 @@ async function fetchExistingReports() {
|
||||
)
|
||||
}
|
||||
|
||||
const fetchItemDebounced = useDebounceFn(fetchItem, 500)
|
||||
|
||||
watch([reportItem, reportItemID], ([type, id], [prevType]) => {
|
||||
prefilled.value = false
|
||||
if (!id.trim() || type !== prevType) {
|
||||
fetchItem()
|
||||
} else {
|
||||
fetchItemDebounced()
|
||||
}
|
||||
})
|
||||
|
||||
async function fetchItem() {
|
||||
if (reportItem.value && reportItemID.value) {
|
||||
checkingId.value = true
|
||||
const type = reportItem.value
|
||||
const id = reportItemID.value.trim()
|
||||
|
||||
currentProject.value = null
|
||||
currentVersion.value = null
|
||||
currentUser.value = null
|
||||
itemIcon.value = undefined
|
||||
itemName.value = undefined
|
||||
itemLink.value = undefined
|
||||
itemId.value = undefined
|
||||
itemIssueTracker.value = undefined
|
||||
try {
|
||||
if (reportItem.value === 'project') {
|
||||
const project = (await useBaseFetch(`project/${reportItemID.value}`)) as Project
|
||||
currentProject.value = project
|
||||
|
||||
if (!type || !id) {
|
||||
checkedId.value = false
|
||||
checkingId.value = false
|
||||
return
|
||||
}
|
||||
|
||||
checkingId.value = true
|
||||
try {
|
||||
if (type === 'project') {
|
||||
const project = (await useBaseFetch(`project/${id}`)) as Project
|
||||
currentProject.value = project
|
||||
itemIcon.value = project.icon_url
|
||||
itemName.value = project.title
|
||||
itemLink.value = `/project/${project.id}`
|
||||
itemId.value = project.id
|
||||
itemIssueTracker.value = project.issues_url
|
||||
} else if (reportItem.value === 'version') {
|
||||
const version = (await useBaseFetch(`version/${reportItemID.value}`)) as Version
|
||||
} else if (type === 'version') {
|
||||
const version = (await useBaseFetch(`version/${id}`)) as Version
|
||||
currentVersion.value = version
|
||||
|
||||
itemIcon.value = VersionIcon
|
||||
itemName.value = version.version_number
|
||||
itemLink.value = `project/${version.project_id}/version/${version.id}`
|
||||
itemId.value = version.id
|
||||
} else if (reportItem.value === 'user') {
|
||||
const user = (await useBaseFetch(`user/${reportItemID.value}`)) as User
|
||||
} else if (type === 'user') {
|
||||
const user = (await useBaseFetch(`user/${id}`)) as User
|
||||
currentUser.value = user
|
||||
|
||||
itemIcon.value = user.avatar_url
|
||||
itemName.value = user.username
|
||||
itemLink.value = `/user/${user.username}`
|
||||
itemId.value = user.id
|
||||
}
|
||||
} catch {
|
||||
// Ignored
|
||||
// do nothing
|
||||
}
|
||||
checkedId.value = true
|
||||
checkingId.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const reportItems = ['project', 'version', 'user']
|
||||
const dummyProjectReportTypes = ['missing-disclosure', 'ai-images', 'fully-ai-generated'] as const
|
||||
const reportTypes = computed(() => {
|
||||
const types = [...tags.value.reportTypes]
|
||||
if (reportItem.value === 'project') {
|
||||
types.push('missing-disclosure')
|
||||
const otherIndex = types.indexOf('other')
|
||||
if (otherIndex === -1) {
|
||||
types.push(...dummyProjectReportTypes)
|
||||
} else {
|
||||
types.splice(otherIndex, 0, ...dummyProjectReportTypes)
|
||||
}
|
||||
}
|
||||
return types
|
||||
})
|
||||
const disableReporting = computed(() => reportType.value === 'missing-disclosure')
|
||||
const disableReporting = computed(() => dummyProjectReportTypes.includes(reportType.value))
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
return (
|
||||
@@ -531,44 +539,6 @@ const onImageUpload = async (file: File) => {
|
||||
return item.url
|
||||
}
|
||||
|
||||
const warnings: Record<string, MessageDescriptor[]> = {
|
||||
copyright: [
|
||||
defineMessage({
|
||||
id: 'report.note.copyright.1',
|
||||
defaultMessage:
|
||||
'Please note that you are *not* submitting a DMCA takedown request, but rather a report of reuploaded content.',
|
||||
}),
|
||||
defineMessage({
|
||||
id: 'report.note.copyright.2',
|
||||
defaultMessage:
|
||||
'If you meant to file a DMCA takedown request (which is a legal action) instead, please see our <copyright-policy-link>Copyright Policy</copyright-policy-link>.',
|
||||
}),
|
||||
],
|
||||
malicious: [
|
||||
defineMessage({
|
||||
id: 'report.note.malicious.1',
|
||||
defaultMessage:
|
||||
'Reports for malicious or deceptive content must include substantial evidence of the behavior, such as code samples.',
|
||||
}),
|
||||
defineMessage({
|
||||
id: 'report.note.malicious.2',
|
||||
defaultMessage:
|
||||
'Summaries from Microsoft Defender, VirusTotal, or AI malware detection are not sufficient forms of evidence and will not be accepted.',
|
||||
}),
|
||||
],
|
||||
'missing-disclosure': [
|
||||
defineMessage({
|
||||
id: 'report.note.missing-disclosure.1',
|
||||
defaultMessage: `Content disclosures are a new feature we've just added. We're giving creators a grace period to get their disclosures up-to-date before we begin accepting reports for missing or incorrect disclosures.`,
|
||||
}),
|
||||
defineMessage({
|
||||
id: 'report.note.missing-disclosure.2',
|
||||
defaultMessage:
|
||||
'Check back later once the grace period has ended. Reports of this type are not being accepted at this time.',
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
reportContent: {
|
||||
id: 'report.report-content',
|
||||
@@ -662,7 +632,60 @@ const messages = defineMessages({
|
||||
id: 'report.submit',
|
||||
defaultMessage: 'Submit report',
|
||||
},
|
||||
checkBackLater: {
|
||||
id: 'report.note.check-back-later',
|
||||
defaultMessage:
|
||||
'Check back later once the grace period has ended. Reports of this type are not being accepted at this time.',
|
||||
},
|
||||
})
|
||||
|
||||
const warnings: Record<string, MessageDescriptor[]> = {
|
||||
copyright: [
|
||||
defineMessage({
|
||||
id: 'report.note.copyright.1',
|
||||
defaultMessage:
|
||||
'Please note that you are *not* submitting a DMCA takedown request, but rather a report of reuploaded content.',
|
||||
}),
|
||||
defineMessage({
|
||||
id: 'report.note.copyright.2',
|
||||
defaultMessage:
|
||||
'If you meant to file a DMCA takedown request (which is a legal action) instead, please see our <copyright-policy-link>Copyright Policy</copyright-policy-link>.',
|
||||
}),
|
||||
],
|
||||
malicious: [
|
||||
defineMessage({
|
||||
id: 'report.note.malicious.1',
|
||||
defaultMessage:
|
||||
'Reports for malicious or deceptive content must include substantial evidence of the behavior, such as code samples.',
|
||||
}),
|
||||
defineMessage({
|
||||
id: 'report.note.malicious.2',
|
||||
defaultMessage:
|
||||
'Summaries from Microsoft Defender, VirusTotal, or AI malware detection are not sufficient forms of evidence and will not be accepted.',
|
||||
}),
|
||||
],
|
||||
'missing-disclosure': [
|
||||
defineMessage({
|
||||
id: 'report.note.missing-disclosure.1',
|
||||
defaultMessage: `Content disclosures are a new feature we've just added. We're giving creators a grace period to get their disclosures up-to-date before we begin accepting reports for missing or incorrect disclosures.`,
|
||||
}),
|
||||
messages.checkBackLater,
|
||||
],
|
||||
'ai-images': [
|
||||
defineMessage({
|
||||
id: 'report.note.ai-images.1',
|
||||
defaultMessage: `We've just updated our rules to prohibit AI-generated images in icons, galleries, and descriptions. We're giving creators a grace period to remove AI images from their projects before we begin accepting reports for them.`,
|
||||
}),
|
||||
messages.checkBackLater,
|
||||
],
|
||||
'fully-ai-generated': [
|
||||
defineMessage({
|
||||
id: 'report.note.fully-ai-generated.1',
|
||||
defaultMessage: `We've just updated our rules to prohibit fully AI-generated projects. We're giving creators a grace period to update or take down fully AI-generated projects before we begin accepting reports for them.`,
|
||||
}),
|
||||
messages.checkBackLater,
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -3995,9 +3995,15 @@
|
||||
"report.item-type.version": {
|
||||
"defaultMessage": "version"
|
||||
},
|
||||
"report.type.ai-images": {
|
||||
"defaultMessage": "AI-generated images in icon, gallery, or description"
|
||||
},
|
||||
"report.type.copyright": {
|
||||
"defaultMessage": "Reuploaded work"
|
||||
},
|
||||
"report.type.fully-ai-generated": {
|
||||
"defaultMessage": "Fully AI-generated project"
|
||||
},
|
||||
"report.type.inappropriate": {
|
||||
"defaultMessage": "Inappropriate"
|
||||
},
|
||||
|
||||
@@ -1049,6 +1049,14 @@ export const reportTypeMessages = defineMessages({
|
||||
id: 'report.type.missing-disclosure',
|
||||
defaultMessage: 'Missing or incorrect disclosure',
|
||||
},
|
||||
'ai-images': {
|
||||
id: 'report.type.ai-images',
|
||||
defaultMessage: 'AI-generated images in icon, gallery, or description',
|
||||
},
|
||||
'fully-ai-generated': {
|
||||
id: 'report.type.fully-ai-generated',
|
||||
defaultMessage: 'Fully AI-generated project',
|
||||
},
|
||||
})
|
||||
|
||||
export function formatReportType(formatMessage: FormatMessage, type: string | undefined): string {
|
||||
|
||||
Reference in New Issue
Block a user