diff --git a/apps/app-frontend/src/locales/en-US/index.json b/apps/app-frontend/src/locales/en-US/index.json index a46dbe60e..515e4e71a 100644 --- a/apps/app-frontend/src/locales/en-US/index.json +++ b/apps/app-frontend/src/locales/en-US/index.json @@ -20,6 +20,9 @@ "app.settings.tabs.resource-management": { "message": "Resource management" }, + "instance.filter.disabled": { + "message": "Disabled projects" + }, "instance.filter.updates-available": { "message": "Updates available" }, diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json index 86fe0f97f..df10535bf 100644 --- a/apps/frontend/src/locales/en-US/index.json +++ b/apps/frontend/src/locales/en-US/index.json @@ -485,6 +485,81 @@ "project.versions.title": { "message": "Versions" }, + "report.already-reported": { + "message": "You've already reported {title}" + }, + "report.already-reported-description": { + "message": "You have an open report for this {item} already. You can add more details to your report if you have more information to add." + }, + "report.back-to-item": { + "message": "Back to {item}" + }, + "report.body.description": { + "message": "Include links and images if possible and relevant. Empty or insufficient reports will be closed and ignored." + }, + "report.body.title": { + "message": "Please provide additional context about your report" + }, + "report.checking": { + "message": "Checking {item}..." + }, + "report.could-not-find": { + "message": "Could not find {item}" + }, + "report.for.violation": { + "message": "Violation of Modrinth Rules or Terms of Use" + }, + "report.for.violation.description": { + "message": "Examples include malicious, spam, offensive, deceptive, misleading, and illegal content." + }, + "report.form-not-for": { + "message": "This form is not for:" + }, + "report.go-to-report": { + "message": "Go to report" + }, + "report.not-for.bug-reports": { + "message": "Bug reports" + }, + "report.not-for.dmca": { + "message": "DMCA takedowns" + }, + "report.not-for.dmca.description": { + "message": "See our Copyright Policy." + }, + "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." + }, + "report.note.malicious.1": { + "message": "Reports for malicious or deceptive content must include substantial evidence of the behavior, such as code samples." + }, + "report.note.malicious.2": { + "message": "Summaries from Microsoft Defender, VirusTotal, or AI malware detection are not sufficient forms of evidence and will not be accepted." + }, + "report.please-report": { + "message": "Please report:" + }, + "report.question.content-id": { + "message": "What is the ID of the {item}?" + }, + "report.question.content-type": { + "message": "What type of content are you reporting?" + }, + "report.question.report-reason": { + "message": "Which of Modrinth's rules is this {item} violating?" + }, + "report.report-content": { + "message": "Report content to moderators" + }, + "report.report-item": { + "message": "Report {title} to moderators" + }, + "report.submit": { + "message": "Submit report" + }, "revenue.transfers.total": { "message": "You have withdrawn {amount} in total." }, diff --git a/apps/frontend/src/pages/[type]/[id].vue b/apps/frontend/src/pages/[type]/[id].vue index a17cdae2d..8b79db4b4 100644 --- a/apps/frontend/src/pages/[type]/[id].vue +++ b/apps/frontend/src/pages/[type]/[id].vue @@ -631,7 +631,7 @@ auth.user ? reportProject(project.id) : navigateTo('/auth/sign-in'), color: 'red', hoverOnly: true, - shown: !currentMember, + shown: !isMember, }, { id: 'copy-id', action: () => copyId() }, ]" @@ -1204,6 +1204,10 @@ const members = computed(() => { return owner ? [owner, ...rest] : rest; }); +const isMember = computed( + () => auth.value.user && allMembers.value.some((x) => x.user.id === auth.value.user.id), +); + const currentMember = computed(() => { let val = auth.value.user ? allMembers.value.find((x) => x.user.id === auth.value.user.id) : null; diff --git a/apps/frontend/src/pages/report.vue b/apps/frontend/src/pages/report.vue index 8c54c9d3d..e44a8a9cd 100644 --- a/apps/frontend/src/pages/report.vue +++ b/apps/frontend/src/pages/report.vue @@ -1,99 +1,256 @@ - diff --git a/packages/ui/src/components/base/MarkdownEditor.vue b/packages/ui/src/components/base/MarkdownEditor.vue index 9c6d57032..59a8746a1 100644 --- a/packages/ui/src/components/base/MarkdownEditor.vue +++ b/packages/ui/src/components/base/MarkdownEditor.vue @@ -300,8 +300,8 @@ import Chips from './Chips.vue' const props = withDefaults( defineProps<{ modelValue: string - disabled: boolean - headingButtons: boolean + disabled?: boolean + headingButtons?: boolean /** * @param file The file to upload * @throws If the file is invalid or the upload fails @@ -948,4 +948,8 @@ function openVideoModal() { pointer-events: none; cursor: not-allowed; } + +:deep(.cm-content) { + overflow: auto; +} diff --git a/packages/ui/src/components/base/RadialHeader.vue b/packages/ui/src/components/base/RadialHeader.vue new file mode 100644 index 000000000..89e80b67b --- /dev/null +++ b/packages/ui/src/components/base/RadialHeader.vue @@ -0,0 +1,49 @@ + + + diff --git a/packages/ui/src/components/base/RadioButtons.vue b/packages/ui/src/components/base/RadioButtons.vue new file mode 100644 index 000000000..fd384c4aa --- /dev/null +++ b/packages/ui/src/components/base/RadioButtons.vue @@ -0,0 +1,48 @@ + + diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 8fe546bc9..bf53d7aac 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -25,6 +25,8 @@ export { default as Pagination } from './base/Pagination.vue' export { default as PopoutMenu } from './base/PopoutMenu.vue' export { default as PreviewSelectButton } from './base/PreviewSelectButton.vue' export { default as ProjectCard } from './base/ProjectCard.vue' +export { default as RadialHeader } from './base/RadialHeader.vue' +export { default as RadioButtons } from './base/RadioButtons.vue' export { default as ScrollablePanel } from './base/ScrollablePanel.vue' export { default as SimpleBadge } from './base/SimpleBadge.vue' export { default as Slider } from './base/Slider.vue' diff --git a/packages/utils/types.ts b/packages/utils/types.ts index 3fe5dbec1..76962e9e5 100644 --- a/packages/utils/types.ts +++ b/packages/utils/types.ts @@ -1,7 +1,7 @@ export const BASE62_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' export type Base62Char = (typeof BASE62_CHARS)[number] -export type ModrinthId = `${Base62Char}`[] +export type ModrinthId = string export type Environment = 'required' | 'optional' | 'unsupported' | 'unknown' @@ -241,3 +241,15 @@ export interface TeamMember { payouts_split: number ordering: number } + +export type Report = { + id: ModrinthId + item_id: ModrinthId + item_type: 'project' | 'version' | 'user' + report_type: string + reporter: ModrinthId + thread_id: ModrinthId + closed: boolean + created: string + body: string +}