feat: set up typed nag (validators) system
This commit is contained in:
parent
359fbd4738
commit
b98c1fe7b8
4
packages/moderation/data/nags.ts
Normal file
4
packages/moderation/data/nags.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import type { Nag } from '../types/nags'
|
||||
import { coreNags } from './nags/core'
|
||||
|
||||
export default [...coreNags] as Nag[]
|
||||
128
packages/moderation/data/nags/core.ts
Normal file
128
packages/moderation/data/nags/core.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import type { Nag, NagContext } from '../../types/nags'
|
||||
import { formatProjectType } from '@modrinth/utils'
|
||||
|
||||
export const coreNags: Nag[] = [
|
||||
{
|
||||
id: 'upload-version',
|
||||
title: 'Upload a version',
|
||||
description: () => 'At least one version is required for a project to be submitted for review.',
|
||||
status: 'required',
|
||||
shouldShow: (context: NagContext) => context.versions.length < 1,
|
||||
link: {
|
||||
path: 'versions',
|
||||
title: 'Visit versions page',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-versions',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'add-description',
|
||||
title: 'Add a description',
|
||||
description: () =>
|
||||
"A description that clearly describes the project's purpose and function is required.",
|
||||
status: 'required',
|
||||
shouldShow: (context: NagContext) =>
|
||||
context.project.body === '' || context.project.body.startsWith('# Placeholder description'),
|
||||
link: {
|
||||
path: 'settings/description',
|
||||
title: 'Visit description settings',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-settings-description',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'add-icon',
|
||||
title: 'Add an icon',
|
||||
description: () =>
|
||||
'Your project should have a nice-looking icon to uniquely identify your project at a glance.',
|
||||
status: 'suggestion',
|
||||
shouldShow: (context: NagContext) => !context.project.icon_url,
|
||||
link: {
|
||||
path: 'settings',
|
||||
title: 'Visit general settings',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-settings',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'feature-gallery-image',
|
||||
title: 'Feature a gallery image',
|
||||
description: () => 'Featured gallery images may be the first impression of many users.',
|
||||
status: 'suggestion',
|
||||
shouldShow: (context: NagContext) => {
|
||||
const featuredGalleryImage = context.project.gallery?.find((img) => img.featured)
|
||||
return context.project?.gallery?.length === 0 || !featuredGalleryImage
|
||||
},
|
||||
link: {
|
||||
path: 'gallery',
|
||||
title: 'Visit gallery page',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-gallery',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'select-tags',
|
||||
title: 'Select tags',
|
||||
description: () => 'Select all tags that apply to your project.',
|
||||
status: 'suggestion',
|
||||
shouldShow: (context: NagContext) =>
|
||||
context.project.versions.length > 0 && context.project.categories.length < 1,
|
||||
link: {
|
||||
path: 'settings/tags',
|
||||
title: 'Visit tag settings',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-settings-tags',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'add-links',
|
||||
title: 'Add external links',
|
||||
description: () =>
|
||||
'Add any relevant links targeted outside of Modrinth, such as sources, issues, or a Discord invite.',
|
||||
status: 'suggestion',
|
||||
shouldShow: (context: NagContext) =>
|
||||
!(
|
||||
context.project.issues_url ||
|
||||
context.project.source_url ||
|
||||
context.project.wiki_url ||
|
||||
context.project.discord_url ||
|
||||
context.project.donation_urls.length > 0
|
||||
),
|
||||
link: {
|
||||
path: 'settings/links',
|
||||
title: 'Visit links settings',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-settings-links',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'select-environments',
|
||||
title: 'Select supported environments',
|
||||
description: (context: NagContext) =>
|
||||
`Select if the ${formatProjectType(context.project.project_type).toLowerCase()} functions on the client-side and/or server-side.`,
|
||||
status: 'required',
|
||||
shouldShow: (context: NagContext) => {
|
||||
const excludedTypes = ['resourcepack', 'plugin', 'shader', 'datapack']
|
||||
return (
|
||||
context.project.versions.length > 0 &&
|
||||
!excludedTypes.includes(context.project.project_type) &&
|
||||
(context.project.client_side === 'unknown' ||
|
||||
context.project.server_side === 'unknown' ||
|
||||
(context.project.client_side === 'unsupported' &&
|
||||
context.project.server_side === 'unsupported'))
|
||||
)
|
||||
},
|
||||
link: {
|
||||
path: 'settings',
|
||||
title: 'Visit general settings',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-settings',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'select-license',
|
||||
title: 'Select license',
|
||||
description: (context: NagContext) =>
|
||||
`Select the license your ${formatProjectType(context.project.project_type).toLowerCase()} is distributed under.`,
|
||||
status: 'required',
|
||||
shouldShow: (context: NagContext) => context.project.license.id === 'LicenseRef-Unknown',
|
||||
link: {
|
||||
path: 'settings/license',
|
||||
title: 'Visit license settings',
|
||||
shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-settings-license',
|
||||
},
|
||||
},
|
||||
]
|
||||
@ -2,7 +2,9 @@ export * from './types/actions'
|
||||
export * from './types/messages'
|
||||
export * from './types/stage'
|
||||
export * from './types/keybinds'
|
||||
export * from './types/nags'
|
||||
export * from './utils'
|
||||
|
||||
export { default as checklist } from './data/checklist'
|
||||
export { default as keybinds } from './data/keybinds'
|
||||
export { default as nags } from './data/nags'
|
||||
|
||||
90
packages/moderation/types/nags.ts
Normal file
90
packages/moderation/types/nags.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import type { Project, User, Version } from '@modrinth/utils'
|
||||
import type { FunctionalComponent, SVGAttributes } from 'vue'
|
||||
|
||||
/**
|
||||
* Type which represents the status type of a nag.
|
||||
*
|
||||
* - `required` indicates that the nag must be addressed.
|
||||
* - `warning` indicates that the nag is important but not critical, and can be ignored. It is often used for issues that should be resolved but do not block project submission.
|
||||
* - `suggestion` indicates that the nag is a recommendation and can be ignored.
|
||||
*/
|
||||
export type NagStatus = 'required' | 'warning' | 'suggestion'
|
||||
|
||||
/**
|
||||
* Interface representing the context in which a nag is displayed.
|
||||
* It includes the project, versions, current member, all members, and the current route.
|
||||
* This context is used to determine whether a nag or it's link should be shown and how it should be presented.
|
||||
*/
|
||||
export interface NagContext {
|
||||
/**
|
||||
* The project associated with the nag.
|
||||
*/
|
||||
project: Project
|
||||
/**
|
||||
* The versions associated with the project.
|
||||
*/
|
||||
versions: Version[]
|
||||
/**
|
||||
* The current project member viewing the nag.
|
||||
*/
|
||||
currentMember: User
|
||||
/**
|
||||
* The current route in the application.
|
||||
*/
|
||||
currentRoute: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface representing a nag's link.
|
||||
*/
|
||||
export interface NagLink {
|
||||
/**
|
||||
* A relative path to the nag's link, e.g. '/settings'.
|
||||
*/
|
||||
path: string
|
||||
/**
|
||||
* The text to display for the nag's link.
|
||||
*/
|
||||
title: string
|
||||
/**
|
||||
* The status of the nag, which can be 'required', 'warning', or 'suggestion'.
|
||||
*/
|
||||
shouldShow?: (context: NagContext) => boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface representing a nag.
|
||||
*/
|
||||
export interface Nag {
|
||||
/**
|
||||
* A unique identifier for the nag.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The title of the nag.
|
||||
*/
|
||||
title: string
|
||||
/**
|
||||
* A function that returns the description of the nag.
|
||||
* It can accept a context to provide dynamic descriptions.
|
||||
*/
|
||||
description: (context: NagContext) => string
|
||||
/**
|
||||
* The status of the nag, which can be 'required', 'warning', or 'suggestion'.
|
||||
*/
|
||||
status: NagStatus
|
||||
/**
|
||||
* An optional icon for the nag, usually from `@modrinth/assets`.
|
||||
* If not specified it will use the default icon associated with the nag status.
|
||||
*/
|
||||
icon?: FunctionalComponent<SVGAttributes>
|
||||
/**
|
||||
* A function that determines whether the nag should be shown based on the context.
|
||||
*/
|
||||
shouldShow?: (context: NagContext) => boolean
|
||||
/**
|
||||
* An optional link associated with the nag.
|
||||
* If provided, it should be displayed alongside the nag.
|
||||
*/
|
||||
link?: NagLink
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user