mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +00:00
296 lines
9.1 KiB
TypeScript
296 lines
9.1 KiB
TypeScript
import { defineMessage, formatProjectTypeSentence, useVIntl } from '@modrinth/ui'
|
|
|
|
import type { Nag, NagContext } from '../../types/nags'
|
|
import { licenseRequiresSource, notSourceAsDistributed } from '../../utils'
|
|
import {
|
|
getBlockedProjectExternalLink,
|
|
getLinkHostname,
|
|
isCommonProjectLink,
|
|
isDiscordLink,
|
|
isInappropriateLicenseLink,
|
|
} from '../../validators/links'
|
|
|
|
export function findBlockedProjectExternalLink(context: Pick<NagContext, 'project' | 'projectV3'>) {
|
|
const urls = [
|
|
context.project.source_url,
|
|
context.project.issues_url,
|
|
context.project.wiki_url,
|
|
context.project.discord_url,
|
|
context.project.license.url,
|
|
...(context.project.donation_urls ?? []).map(({ url }) => url),
|
|
...Object.values(context.projectV3?.link_urls ?? {}).map(({ url }) => url),
|
|
]
|
|
|
|
for (const url of urls) {
|
|
if (!url) continue
|
|
const blockedLink = getBlockedProjectExternalLink(url)
|
|
if (blockedLink) return blockedLink
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export const linksNags: Nag[] = [
|
|
{
|
|
id: 'add-links',
|
|
title: defineMessage({
|
|
id: 'nags.add-links.title',
|
|
defaultMessage: 'Add external links',
|
|
}),
|
|
description: defineMessage({
|
|
id: 'nags.add-links.description',
|
|
defaultMessage:
|
|
'Add any relevant links targeted outside of Modrinth, such as source code, an issue tracker, or a Discord invite.',
|
|
}),
|
|
status: 'suggestion',
|
|
shouldShow: (context: NagContext) => {
|
|
return (
|
|
!context.projectV3?.minecraft_server &&
|
|
Object.keys(context.projectV3?.link_urls ?? {}).length === 0
|
|
)
|
|
},
|
|
link: {
|
|
path: 'settings/links',
|
|
title: defineMessage({
|
|
id: 'nags.settings.links.title',
|
|
defaultMessage: 'Visit links settings',
|
|
}),
|
|
shouldShow: (context: NagContext) => context.currentRoute !== 'type-project-settings-links',
|
|
},
|
|
},
|
|
{
|
|
id: 'add-links-server',
|
|
title: defineMessage({
|
|
id: 'nags.add-links-server.title',
|
|
defaultMessage: 'Add external links',
|
|
}),
|
|
description: defineMessage({
|
|
id: 'nags.add-links-server.description',
|
|
defaultMessage:
|
|
'Add any relevant links targeted outside of Modrinth, such as a website, store, or a Discord invite.',
|
|
}),
|
|
status: 'suggestion',
|
|
shouldShow: (context: NagContext) => {
|
|
return (
|
|
!!context.projectV3?.minecraft_server &&
|
|
Object.keys(context.projectV3?.link_urls ?? {}).length === 0
|
|
)
|
|
},
|
|
link: {
|
|
path: 'settings/links',
|
|
title: defineMessage({
|
|
id: 'nags.settings.links.title',
|
|
defaultMessage: 'Visit links settings',
|
|
}),
|
|
shouldShow: (context: NagContext) => context.currentRoute !== 'type-project-settings-links',
|
|
},
|
|
},
|
|
{
|
|
id: 'identical-links',
|
|
title: defineMessage({
|
|
id: 'nags.identical-links.title',
|
|
defaultMessage: 'Clean up identical links',
|
|
}),
|
|
description: defineMessage({
|
|
id: 'nags.identical-links.description',
|
|
defaultMessage:
|
|
'Some of your external links appear to be identical. Each link should be entered only once and with the appropriate link type.',
|
|
}),
|
|
status: 'required',
|
|
shouldShow: (context: NagContext) =>
|
|
new Set(Object.values(context.projectV3?.link_urls ?? {}).map((link) => link.url)).size !==
|
|
Object.values(context.projectV3?.link_urls ?? {}).map((link) => link.url).length,
|
|
link: {
|
|
path: 'settings/links',
|
|
title: defineMessage({
|
|
id: 'nags.settings.links.title',
|
|
defaultMessage: 'Visit links settings',
|
|
}),
|
|
shouldShow: (context: NagContext) => context.currentRoute !== 'type-project-settings-links',
|
|
},
|
|
},
|
|
{
|
|
id: 'verify-external-links',
|
|
title: defineMessage({
|
|
id: 'nags.verify-external-links.title',
|
|
defaultMessage: 'Verify external links',
|
|
}),
|
|
description: defineMessage({
|
|
id: 'nags.verify-external-links.description',
|
|
defaultMessage:
|
|
'Some of your external links may be using domains that are inappropriate for that type of link.',
|
|
}),
|
|
status: 'warning',
|
|
shouldShow: (context: NagContext) => {
|
|
const sourceUrl = context.project.source_url
|
|
const issuesUrl = context.project.issues_url
|
|
const discordUrl = context.project.discord_url
|
|
return (
|
|
(!!sourceUrl && !isCommonProjectLink(sourceUrl, 'source')) ||
|
|
(!!issuesUrl && !isCommonProjectLink(issuesUrl, 'issues')) ||
|
|
(!!discordUrl && !isCommonProjectLink(discordUrl, 'discord'))
|
|
)
|
|
},
|
|
link: {
|
|
path: 'settings/links',
|
|
title: defineMessage({
|
|
id: 'nags.visit-links-settings.title',
|
|
defaultMessage: 'Visit links settings',
|
|
}),
|
|
shouldShow: (context: NagContext) => context.currentRoute !== 'type-project-settings-links',
|
|
},
|
|
},
|
|
{
|
|
id: 'misused-discord-link',
|
|
title: defineMessage({
|
|
id: 'nags.misused-discord-link.title',
|
|
defaultMessage: 'Move Discord invite',
|
|
}),
|
|
description: defineMessage({
|
|
id: 'nags.misused-discord-link-description',
|
|
defaultMessage:
|
|
'Discord invites can not be used for other link types. Please put your Discord link in the Discord Invite link field only.',
|
|
}),
|
|
status: 'required',
|
|
shouldShow: (context: NagContext) =>
|
|
isDiscordLink(context.project.source_url) ||
|
|
isDiscordLink(context.project.issues_url) ||
|
|
isDiscordLink(context.project.wiki_url) ||
|
|
isDiscordLink(context.projectV3?.link_urls?.site?.url) ||
|
|
isDiscordLink(context.projectV3?.link_urls?.store?.url),
|
|
link: {
|
|
path: 'settings/links',
|
|
title: defineMessage({
|
|
id: 'nags.visit-links-settings.title',
|
|
defaultMessage: 'Visit links settings',
|
|
}),
|
|
shouldShow: (context: NagContext) => context.currentRoute !== 'type-project-settings-links',
|
|
},
|
|
},
|
|
{
|
|
id: 'banned-link-usage',
|
|
title: defineMessage({
|
|
id: 'nags.banned-link-usage.title',
|
|
defaultMessage: 'Remove prohibited links',
|
|
}),
|
|
description: (context: NagContext) => {
|
|
const blockedLink = findBlockedProjectExternalLink(context)
|
|
if (!blockedLink) return ''
|
|
|
|
const { formatMessage } = useVIntl()
|
|
return formatMessage(
|
|
defineMessage({
|
|
id: 'nags.banned-link-usage.description',
|
|
defaultMessage: '“{url}” is not allowed in project links.',
|
|
}),
|
|
blockedLink,
|
|
)
|
|
},
|
|
status: 'required',
|
|
shouldShow: (context: NagContext) => findBlockedProjectExternalLink(context) !== null,
|
|
},
|
|
{
|
|
id: 'invalid-license-url',
|
|
title: defineMessage({
|
|
id: 'nags.invalid-license-url.title',
|
|
defaultMessage: 'Add a valid license link',
|
|
}),
|
|
description: (context: NagContext) => {
|
|
const { formatMessage } = useVIntl()
|
|
const licenseUrl = context.project.license.url
|
|
|
|
if (!licenseUrl) {
|
|
return formatMessage(
|
|
defineMessage({
|
|
id: 'nags.invalid-license-url.description.default',
|
|
defaultMessage: 'License URL is invalid.',
|
|
}),
|
|
)
|
|
}
|
|
|
|
const domain = getLinkHostname(licenseUrl)
|
|
if (domain) {
|
|
return formatMessage(
|
|
defineMessage({
|
|
id: 'nags.invalid-license-url.description.domain',
|
|
defaultMessage:
|
|
'Your license URL points to {domain}, which is not appropriate for license information. License URLs should link directly to your license file, not social media, gaming platforms, etc.',
|
|
}),
|
|
{ domain },
|
|
)
|
|
}
|
|
|
|
return formatMessage(
|
|
defineMessage({
|
|
id: 'nags.invalid-license-url.description.malformed',
|
|
defaultMessage:
|
|
'Your license URL appears to be malformed. Please provide a valid URL to your license text.',
|
|
}),
|
|
)
|
|
},
|
|
status: 'required',
|
|
shouldShow: (context: NagContext) => {
|
|
const licenseUrl = context.project.license.url
|
|
if (!licenseUrl) return false
|
|
return getLinkHostname(licenseUrl) === null || isInappropriateLicenseLink(licenseUrl)
|
|
},
|
|
link: {
|
|
path: 'settings',
|
|
title: defineMessage({
|
|
id: 'nags.edit-license.title',
|
|
defaultMessage: 'Edit license',
|
|
}),
|
|
shouldShow: (context: NagContext) => context.currentRoute !== 'type-project-settings',
|
|
},
|
|
},
|
|
{
|
|
id: 'gpl-license-source-required',
|
|
title: defineMessage({
|
|
id: 'nags.gpl-license-source-required.title',
|
|
defaultMessage: 'Provide source code',
|
|
}),
|
|
description: (context: NagContext) => {
|
|
const { formatMessage } = useVIntl()
|
|
|
|
return formatMessage(
|
|
defineMessage({
|
|
id: 'nags.gpl-license-source-required.description',
|
|
defaultMessage:
|
|
'Your {type} uses a license which requires source code to be available. Please provide a source code link or sources file for each additional version, or consider using a different license.',
|
|
}),
|
|
{
|
|
type: formatProjectTypeSentence(formatMessage, context.project.project_type),
|
|
},
|
|
)
|
|
},
|
|
status: 'required',
|
|
shouldShow: (context: NagContext) => {
|
|
if (context.projectV3.project_types.includes('datapack')) return false
|
|
|
|
const hasSourceUrl = !!context.project.source_url
|
|
const hasAdditionalFiles = (context: NagContext) => {
|
|
let hasAdditional = true
|
|
context.versions.forEach((version) => {
|
|
if (version.files.length < 2) hasAdditional = false
|
|
})
|
|
return hasAdditional
|
|
}
|
|
|
|
return (
|
|
licenseRequiresSource(context.projectV3.license.id) &&
|
|
notSourceAsDistributed(context.projectV3.project_types) &&
|
|
!hasSourceUrl &&
|
|
!hasAdditionalFiles(context)
|
|
)
|
|
},
|
|
link: {
|
|
path: 'settings/links',
|
|
title: defineMessage({
|
|
id: 'nags.visit-links-settings.title',
|
|
defaultMessage: 'Visit links settings',
|
|
}),
|
|
shouldShow: (context: NagContext) => context.currentRoute !== 'type-project-settings-links',
|
|
},
|
|
},
|
|
]
|