Skip to content

Author Validation Improvements #4025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 60 commits into from
Aug 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
b98c1fe
feat: set up typed nag (validators) system
IMB11 Jul 11, 2025
bcfceec
feat: start on frontend impl
IMB11 Jul 11, 2025
b297809
fix: shouldShow issues
IMB11 Jul 11, 2025
d33b06e
feat: continue work
IMB11 Jul 11, 2025
59ad8eb
feat: re add submitting/re-submit nags
IMB11 Jul 11, 2025
729d584
feat: start work implementing validation checks using new nag system
IMB11 Jul 12, 2025
27caf33
fix: links page + add more validations
IMB11 Jul 13, 2025
debcb57
feat: tags validations
IMB11 Jul 13, 2025
2f1627c
fix: lint issues
IMB11 Jul 13, 2025
4a7e8e9
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Jul 13, 2025
4934fb1
fix: lint
IMB11 Jul 13, 2025
24ca268
Merge branch 'cal/dev-124-project-validation' of https://github.com/m…
IMB11 Jul 13, 2025
bddf731
fix: issues
IMB11 Jul 13, 2025
35636f7
feat: start on i18nifying nags
IMB11 Jul 14, 2025
2f1a31a
feat: impl intl
IMB11 Jul 14, 2025
28c8b59
fix: minecraft title clause update
IMB11 Jul 16, 2025
252b7bf
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Jul 16, 2025
1261c7e
Merge branch 'main' into cal/dev-124-project-validation
Prospector Jul 16, 2025
89351b4
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Jul 21, 2025
fe3d360
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Jul 28, 2025
b87fb1d
fix: locale issues
IMB11 Jul 28, 2025
5c48779
refactor: inline i18n
IMB11 Jul 28, 2025
54cfe29
fix: summary char min
IMB11 Jul 28, 2025
dc258de
fix: issues
IMB11 Jul 28, 2025
c594e32
Rephrase a few core nags
coolbot100s Jul 29, 2025
1d34a59
Modify character limit numbers
coolbot100s Jul 29, 2025
f66bafc
Remove redundant sentanceEnders check to reduce false positive.
coolbot100s Jul 29, 2025
9dbc960
Description nag rephrasing and tweaks
coolbot100s Jul 29, 2025
ba0b09d
Tweak links nags adding project type checking for source publication …
coolbot100s Jul 29, 2025
ad1fed9
fix: description nag
IMB11 Jul 29, 2025
b5f44ac
Merge branch 'cal/dev-124-project-validation' of https://github.com/m…
coolbot100s Jul 29, 2025
2806d20
bump source publication nag to warn until additional files can be che…
coolbot100s Jul 29, 2025
f720438
refactor link checking helper functions, prevent misuse of dsc links,…
coolbot100s Jul 30, 2025
26c81e3
Correct plugin project type checking
coolbot100s Jul 30, 2025
f606b20
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Jul 31, 2025
88f6533
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Aug 1, 2025
c470eea
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Aug 2, 2025
56699fc
fix: lint issues
IMB11 Aug 2, 2025
f15ceaf
update links.ts
coolbot100s Aug 3, 2025
02bbac0
feat: key + sort nags by type
IMB11 Aug 4, 2025
63787ee
Tweak core and description nag titles, change image accessability nag…
coolbot100s Aug 4, 2025
9db2fd3
Merge branch 'cal/dev-124-project-validation' of https://github.com/m…
coolbot100s Aug 4, 2025
8c0afd1
feat: update readme
IMB11 Aug 4, 2025
87dcb04
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Aug 4, 2025
9c6c368
updates to tags checking and rest of the nag titles
coolbot100s Aug 4, 2025
c850a34
Merge branch 'cal/dev-124-project-validation' of https://github.com/m…
coolbot100s Aug 4, 2025
1f4b43c
fix locale
coolbot100s Aug 5, 2025
ca48107
fix: formatjs
IMB11 Aug 5, 2025
474e482
fix tags warning, and link shorteners and misused discord warnings to…
coolbot100s Aug 5, 2025
29e4c6d
correct vocabulary for resolutions tags warning and sort tags list in…
coolbot100s Aug 5, 2025
de11629
lint fix
coolbot100s Aug 5, 2025
b433935
Merge branch 'main' into cal/dev-124-project-validation
coolbot100s Aug 5, 2025
4b73aee
fix method typo
coolbot100s Aug 5, 2025
2fa712a
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Aug 8, 2025
0c56867
Merge branch 'main' into cal/dev-124-project-validation
IMB11 Aug 9, 2025
78d549f
Add nag for summary formatting.
coolbot100s Aug 11, 2025
f21098a
Check for link shorteners in donation links
coolbot100s Aug 11, 2025
db586e2
add Gallery requirement nag for shaders and most resource packs
coolbot100s Aug 11, 2025
465682f
update index.json
coolbot100s Aug 11, 2025
5052ddb
Merge branch 'main' into cal/dev-124-project-validation
coolbot100s Aug 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
815 changes: 393 additions & 422 deletions apps/frontend/src/components/ui/ProjectMemberHeader.vue

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions apps/frontend/src/locales/en-US/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,78 @@
"profile.user-id": {
"message": "User ID: {id}"
},
"project-member-header.accept": {
"message": "Accept"
},
"project-member-header.action": {
"message": "Action"
},
"project-member-header.decline": {
"message": "Decline"
},
"project-member-header.error": {
"message": "Error"
},
"project-member-header.error-decline": {
"message": "Failed to decline team invitation"
},
"project-member-header.error-join": {
"message": "Failed to accept team invitation"
},
"project-member-header.invitation-no-role": {
"message": "You've been invited to join this project. Please accept or decline the invitation."
},
"project-member-header.invitation-title": {
"message": "Invitation to join project"
},
"project-member-header.invitation-with-role": {
"message": "You've been invited be a member of this project with the role of '{role}'."
},
"project-member-header.key-title": {
"message": "Status Key"
},
"project-member-header.publishing-checklist": {
"message": "Publishing checklist"
},
"project-member-header.required": {
"message": "Required"
},
"project-member-header.resubmit-for-review": {
"message": "Resubmit for review"
},
"project-member-header.resubmit-for-review-desc": {
"message": "Your project has been {status} by Modrinth's staff. In most cases, you can resubmit for review after addressing the staff's message."
},
"project-member-header.show-key": {
"message": "Toggle key"
},
"project-member-header.submit-checklist-tooltip": {
"message": "You must complete the required steps in the publishing checklist!"
},
"project-member-header.submit-for-review": {
"message": "Submit for review"
},
"project-member-header.submit-for-review-desc": {
"message": "Your project is only viewable by members of the project. It must be reviewed by moderators in order to be published."
},
"project-member-header.success": {
"message": "Success"
},
"project-member-header.success-decline": {
"message": "You have declined the team invitation"
},
"project-member-header.success-join": {
"message": "You have joined the project team"
},
"project-member-header.suggestion": {
"message": "Suggestion"
},
"project-member-header.visit-moderation-page": {
"message": "Visit moderation page"
},
"project-member-header.warning": {
"message": "Warning"
},
"project-type.collection.plural": {
"message": "Collections"
},
Expand Down
18 changes: 17 additions & 1 deletion apps/frontend/src/pages/[type]/[id]/settings/description.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
"
:on-image-upload="onUploadHandler"
/>
<div v-if="descriptionWarning" class="flex items-center gap-1.5 text-orange">
<TriangleAlertIcon class="my-auto" />
{{ descriptionWarning }}
</div>
<div class="input-group markdown-disclaimer">
<button
:disabled="!hasChanges"
Expand All @@ -38,7 +42,8 @@
</template>

<script lang="ts" setup>
import { SaveIcon } from "@modrinth/assets";
import { SaveIcon, TriangleAlertIcon } from "@modrinth/assets";
import { countText, MIN_DESCRIPTION_CHARS } from "@modrinth/moderation";
import { MarkdownEditor } from "@modrinth/ui";
import { type Project, type TeamMember, TeamMemberPermission } from "@modrinth/utils";
import { computed, ref } from "vue";
Expand All @@ -53,6 +58,17 @@ const props = defineProps<{

const description = ref(props.project.body);

const descriptionWarning = computed(() => {
const text = description.value?.trim() || "";
const charCount = countText(text);

if (charCount < MIN_DESCRIPTION_CHARS) {
return `It's recommended to have a description with at least ${MIN_DESCRIPTION_CHARS} readable characters. (${charCount}/${MIN_DESCRIPTION_CHARS})`;
}

return null;
});

const patchRequestPayload = computed(() => {
const payload: {
body?: string;
Expand Down
26 changes: 25 additions & 1 deletion apps/frontend/src/pages/[type]/[id]/settings/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
<label for="project-summary">
<span class="label__title">Summary</span>
</label>
<div v-if="summaryWarning" class="my-2 flex items-center gap-1.5 text-orange">
<TriangleAlertIcon class="my-auto" />
{{ summaryWarning }}
</div>
<div class="textarea-wrapper summary-input">
<textarea
id="project-summary"
Expand Down Expand Up @@ -240,9 +244,18 @@

<script setup>
import { formatProjectStatus, formatProjectType } from "@modrinth/utils";
import { UploadIcon, SaveIcon, TrashIcon, XIcon, IssuesIcon, CheckIcon } from "@modrinth/assets";
import {
UploadIcon,
SaveIcon,
TrashIcon,
XIcon,
IssuesIcon,
CheckIcon,
TriangleAlertIcon,
} from "@modrinth/assets";
import { Multiselect } from "vue-multiselect";
import { ConfirmModal, Avatar } from "@modrinth/ui";
import { MIN_SUMMARY_CHARS } from "@modrinth/moderation";
import FileInput from "~/components/ui/FileInput.vue";

const props = defineProps({
Expand Down Expand Up @@ -300,6 +313,17 @@ const hasDeletePermission = computed(() => {
return (props.currentMember.permissions & DELETE_PROJECT) === DELETE_PROJECT;
});

const summaryWarning = computed(() => {
const text = summary.value?.trim() || "";
const charCount = text.length;

if (charCount < MIN_SUMMARY_CHARS) {
return `It's recommended to have a summary with at least ${MIN_SUMMARY_CHARS} characters. (${charCount}/${MIN_SUMMARY_CHARS})`;
}

return null;
});

const sideTypes = ["required", "optional", "unsupported"];

const patchData = computed(() => {
Expand Down
104 changes: 100 additions & 4 deletions apps/frontend/src/pages/[type]/[id]/settings/links.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,26 @@
id="project-issue-tracker"
title="A place for users to report bugs, issues, and concerns about your project."
>
<span class="label__title">Issue tracker</span>
<span class="label__title">Issue tracker </span>
<span class="label__description">
A place for users to report bugs, issues, and concerns about your project.
</span>
</label>
<TriangleAlertIcon
v-if="isIssuesLinkShortener"
v-tooltip="`Use of link shorteners is prohibited.`"
class="size-6 animate-pulse text-orange"
/>
<TriangleAlertIcon
v-else-if="isIssuesDiscordUrl"
v-tooltip="`Discord invites are not appropriate for this link type.`"
class="size-6 animate-pulse text-orange"
/>
<TriangleAlertIcon
v-else-if="!isIssuesUrlCommon"
v-tooltip="`Link includes a domain which isn't common for this link type.`"
class="size-6 animate-pulse text-orange"
/>
<input
id="project-issue-tracker"
v-model="issuesUrl"
Expand All @@ -26,11 +41,26 @@
id="project-source-code"
title="A page/repository containing the source code for your project"
>
<span class="label__title">Source code</span>
<span class="label__title">Source code </span>
<span class="label__description">
A page/repository containing the source code for your project
</span>
</label>
<TriangleAlertIcon
v-if="isSourceLinkShortener"
v-tooltip="`Use of link shorteners is prohibited.`"
class="size-6 animate-pulse text-orange"
/>
<TriangleAlertIcon
v-else-if="isSourceDiscordUrl"
v-tooltip="`Discord invites are not appropriate for this link type.`"
class="size-6 animate-pulse text-orange"
/>
<TriangleAlertIcon
v-else-if="!isSourceUrlCommon"
v-tooltip="`Link includes a domain which isn't common for this link type.`"
class="size-6 animate-pulse text-orange"
/>
<input
id="project-source-code"
v-model="sourceUrl"
Expand All @@ -50,6 +80,16 @@
A page containing information, documentation, and help for the project.
</span>
</label>
<TriangleAlertIcon
v-if="isWikiLinkShortener"
v-tooltip="`Use of link shorteners is prohibited.`"
class="size-6 animate-pulse text-orange"
/>
<TriangleAlertIcon
v-else-if="isWikiDiscordUrl"
v-tooltip="`Discord invites are not appropriate for this link type.`"
class="size-6 animate-pulse text-orange"
/>
<input
id="project-wiki-page"
v-model="wikiUrl"
Expand All @@ -61,9 +101,19 @@
</div>
<div class="adjacent-input">
<label id="project-discord-invite" title="An invitation link to your Discord server.">
<span class="label__title">Discord invite</span>
<span class="label__title">Discord invite </span>
<span class="label__description"> An invitation link to your Discord server. </span>
</label>
<TriangleAlertIcon
v-if="isDiscordLinkShortener"
v-tooltip="`Use of link shorteners is prohibited.`"
class="size-6 animate-pulse text-orange"
/>
<TriangleAlertIcon
v-else-if="!isDiscordUrlCommon"
v-tooltip="`You're using a link which isn't common for this link type.`"
class="size-6 animate-pulse text-orange"
/>
<input
id="project-discord-invite"
v-model="discordUrl"
Expand Down Expand Up @@ -123,7 +173,13 @@

<script setup>
import { DropdownSelect } from "@modrinth/ui";
import { SaveIcon } from "@modrinth/assets";
import { SaveIcon, TriangleAlertIcon } from "@modrinth/assets";
import {
isCommonUrl,
isDiscordUrl,
isLinkShortener,
commonLinkDomains,
} from "@modrinth/moderation";

const tags = useTags();

Expand Down Expand Up @@ -153,6 +209,46 @@ const sourceUrl = ref(props.project.source_url);
const wikiUrl = ref(props.project.wiki_url);
const discordUrl = ref(props.project.discord_url);

const isIssuesUrlCommon = computed(() => {
if (!issuesUrl.value || issuesUrl.value.trim().length === 0) return true;
return isCommonUrl(issuesUrl.value, commonLinkDomains.issues);
});

const isSourceUrlCommon = computed(() => {
if (!sourceUrl.value || sourceUrl.value.trim().length === 0) return true;
return isCommonUrl(sourceUrl.value, commonLinkDomains.source);
});

const isDiscordUrlCommon = computed(() => {
if (!discordUrl.value || discordUrl.value.trim().length === 0) return true;
return isCommonUrl(discordUrl.value, commonLinkDomains.discord);
});

const isIssuesDiscordUrl = computed(() => {
return isDiscordUrl(issuesUrl.value);
});

const isSourceDiscordUrl = computed(() => {
return isDiscordUrl(sourceUrl.value);
});

const isWikiDiscordUrl = computed(() => {
return isDiscordUrl(wikiUrl.value);
});

const isIssuesLinkShortener = computed(() => {
return isLinkShortener(issuesUrl.value);
});
const isSourceLinkShortener = computed(() => {
return isLinkShortener(sourceUrl.value);
});
const isWikiLinkShortener = computed(() => {
return isLinkShortener(wikiUrl.value);
});
const isDiscordLinkShortener = computed(() => {
return isLinkShortener(discordUrl.value);
});

const rawDonationLinks = JSON.parse(JSON.stringify(props.project.donation_urls));
rawDonationLinks.push({
id: null,
Expand Down
Loading