feat: remove loader detection from project name and set version as required not allowed

This commit is contained in:
tdgao
2026-08-26 17:48:20 -06:00
parent 6b0ae54352
commit ed859272d3
13 changed files with 101 additions and 179 deletions
@@ -40,6 +40,7 @@
autocomplete="off"
:disabled="hasHitLimit"
@update:model-value="updatedName()"
@blur="nameForValidation = name"
/>
<ValidationMessage :check="nameValidation" />
</div>
@@ -334,7 +335,8 @@ const visibilities = ref<VisibilityOption[]>([
])
const visibility = ref<VisibilityOption>(visibilities.value[0])
const nameValidation = useProjectTitleValidation(name)
const nameForValidation = ref(name.value)
const nameValidation = useProjectTitleValidation(nameForValidation)
const summaryValidation = useProjectSummaryValidation(description, name)
const disableCreate = computed(() => {
@@ -514,6 +516,7 @@ async function createProject() {
async function show(event?: MouseEvent, options?: ShowOptions) {
name.value = ''
nameForValidation.value = name.value
slug.value = ''
description.value = ''
manualSlug.value = false
@@ -59,14 +59,14 @@
<div
v-for="nag in visibleNags"
:key="nag.id"
class="flex w-[268px] shrink-0 flex-col gap-3 rounded-2xl border border-solid border-surface-5 bg-surface-2 p-4"
class="flex w-[268px] shrink-0 flex-col gap-2.5 rounded-2xl border border-solid border-surface-5 bg-surface-2 p-4"
>
<span class="flex items-center gap-2 font-medium text-contrast">
<span class="flex items-start gap-2 font-medium text-contrast">
<component
:is="nag.icon || getDefaultIcon(nag.status)"
v-tooltip="getStatusTooltip(nag.status)"
:class="[
'size-4',
'mt-0.5 size-4 min-w-4',
nag.status === 'required' && 'text-red',
nag.status === 'warning' && 'text-orange',
nag.status === 'suggestion' && 'text-purple',
@@ -123,7 +123,7 @@ import {
SendIcon,
TriangleAlertIcon,
} from '@modrinth/assets'
import type { Nag, NagContext, NagStatus, ProjectTitleMetadata } from '@modrinth/moderation'
import type { Nag, NagContext, NagStatus } from '@modrinth/moderation'
import { nags, validateProjectFields } from '@modrinth/moderation'
import { Accordion, Button, IconButton } from '@modrinth/ui'
import { defineMessages, type MessageDescriptor, useVIntl } from '@modrinth/ui'
@@ -327,14 +327,7 @@ watch(nagScroller, (el, previousEl) => {
nextTick(updateNagScrollShadows)
})
const titleMetadata = computed<ProjectTitleMetadata>(() => ({
gameVersions: props.tags.gameVersions.map(({ version }) => version),
loaders: props.tags.loaders.map(({ name }) => name),
}))
const projectValidation = computed(() =>
validateProjectFields(props.projectV3, titleMetadata.value),
)
const projectValidation = computed(() => validateProjectFields(props.projectV3))
const nagContext = computed<NagContext>(() => ({
project: props.project,
@@ -5,7 +5,6 @@ import {
type LinkCheckContext,
type LinkCheckResult,
type ProjectTextValidationResult,
type ProjectTitleMetadata,
validateProjectDescription,
validateProjectSummary,
validateProjectTitle,
@@ -20,18 +19,8 @@ export const projectTextValidationMessages = defineMessages({
},
})
function useProjectTitleMetadata() {
const generatedState = useGeneratedState()
return computed<ProjectTitleMetadata>(() => ({
gameVersions: generatedState.value.gameVersions.map(({ version }) => version),
loaders: generatedState.value.loaders.map(({ name }) => name),
}))
}
export function useProjectTitleValidation(text: MaybeRefOrGetter<string | null | undefined>) {
const metadata = useProjectTitleMetadata()
return computed(() => validateProjectTitle(toValue(text), metadata.value))
return computed(() => validateProjectTitle(toValue(text)))
}
export function useProjectSummaryValidation(
@@ -37,7 +37,7 @@ const {
current,
saving,
hasChanges,
reset,
reset: resetForm,
save: saveForm,
} = useSavable(
() => ({
@@ -57,7 +57,8 @@ const {
const { confirmLeaveModal } = usePageLeaveSafety(hasChanges)
const titleValidation = useProjectTitleValidation(() => current.value.title)
const titleForValidation = ref(current.value.title)
const titleValidation = useProjectTitleValidation(titleForValidation)
const taglineValidation = useProjectSummaryValidation(
() => current.value.tagline,
() => current.value.title,
@@ -85,6 +86,11 @@ async function save() {
await saveForm()
}
function reset() {
resetForm()
titleForValidation.value = current.value.title
}
const messages = defineMessages({
nameTitle: {
id: 'project.settings.general.name.title',
@@ -198,6 +204,7 @@ const placeholder = computed(() => placeholders[placeholderIndex.value] ?? place
autocomplete="off"
:maxlength="50"
wrapper-class="flex-grow"
@blur="titleForValidation = current.title"
/>
</div>
<ValidationMessage :check="titleValidation" class="mt-2" />
@@ -22,7 +22,14 @@
<label for="project-name">
<span class="label__title">Name</span>
</label>
<Input id="project-name" v-model="name" :maxlength="2048" :disabled="!hasPermission" />
<Input
id="project-name"
v-model="name"
:maxlength="2048"
wrapper-class="w-full max-w-72"
:disabled="!hasPermission"
@blur="nameForValidation = name"
/>
<ValidationMessage :check="nameValidation" class="mt-2" />
</div>
@@ -430,7 +437,8 @@ const hasPermission = computed(() => {
)
})
const nameValidation = useProjectTitleValidation(name)
const nameForValidation = ref(name.value)
const nameValidation = useProjectTitleValidation(nameForValidation)
const summaryValidation = useProjectSummaryValidation(summary, name)
const hasValidationIssues = computed(
() =>
@@ -537,6 +545,7 @@ const { confirmLeaveModal } = usePageLeaveSafety(hasChanges)
function resetChanges() {
name.value = project.value.name
nameForValidation.value = name.value
slug.value = project.value.slug ?? ''
summary.value = project.value.summary
visibility.value = tags.value.approvedStatuses.includes(project.value.status)
@@ -222,7 +222,6 @@ const route = useRoute()
const router = useRouter()
const client = injectModrinthClient()
const queryClient = useQueryClient()
const generatedState = useGeneratedState()
const debugValidationFilter = useDebugLogger('moderation-validation-filter')
const debugProjectIdsFilter = useDebugLogger('moderation-project-ids-filter')
@@ -598,10 +597,6 @@ const {
const response = await scanProjectsWithValidationIssues({
client,
request: queryKey[3],
titleMetadata: {
gameVersions: generatedState.value.gameVersions.map(({ version }) => version),
loaders: generatedState.value.loaders.map(({ name }) => name),
},
includeWarnings: queryKey[2],
signal,
log: debugValidationFilter,
@@ -1,5 +1,5 @@
import type { AbstractModrinthClient, Labrinth } from '@modrinth/api-client'
import { type ProjectTitleMetadata, validateProjectFields } from '@modrinth/moderation'
import { validateProjectFields } from '@modrinth/moderation'
export type ValidationFilterRequest = Omit<
Labrinth.Moderation.Internal.ProjectsRequest,
@@ -16,7 +16,6 @@ export interface ModerationQueueFetchOptions {
interface ValidationFilterScanOptions {
client: AbstractModrinthClient
request: ValidationFilterRequest
titleMetadata: ProjectTitleMetadata
includeWarnings: boolean
signal: AbortSignal
log: (message: string) => void
@@ -130,7 +129,6 @@ export async function fetchAllModerationQueueProjects(
export async function scanProjectsWithValidationIssues({
client,
request,
titleMetadata,
includeWarnings,
signal,
log,
@@ -170,7 +168,7 @@ export async function scanProjectsWithValidationIssues({
if (!project) {
throw new Error(`V3 projects response omitted queued project ${projectId}`)
}
const validation = validateProjectFields(project, titleMetadata)
const validation = validateProjectFields(project)
if (includeWarnings ? validation.failures.length > 0 : !validation.valid) {
matchingProjectIds.add(projectId)
}