mirror of
https://github.com/modrinth/code.git
synced 2026-08-27 18:14:49 +00:00
* begin project disclosures * new project settings header * begin disclosure settings, edit content rules * togglecards * update phrasing of the rules, prepr * more disclosure settings structuring * add functionality toggle * implement functionality with staging api * improve project settings head titles * feat(labrinth): project disclosures model * feat(labrinth): project disclosures database model * feat(labrinth): project disclosures get endpoint * feat(labrinth): censor user ids if set by moderator * feat(labrinth): wrap disclosures in struct * feat(labrinth): edit project disclosures endpoint * style(labrinth): cargo fmt * style(labrinth): fix typo * feat(labrinth): add fields for ai content disclosure * fix(labrinth): field typo * chore(labrinth): update query cache * feat(labrinth): index disclosures in search * refactor(labrinth): use enum instead of bools * feat(labrinth): trigger incremental index on disclosure change * feat(labrinth): change archived status to disclosure * feat(labrinth): use disclosure for archival status * fix(labrinth): error type for deserialization * fix(labrinth): migration timestamp * fix(labrinth): add disclosures to elasticsearch schema * disclosures in search * update photosensitivity warning copy * update archiving, move general project settings to v3 (start redesign there), use project id lookups for stable cache keys, advanced in server search * blue archive banner * display AI use types * improve labels * add modrinth to link * add placeholder missing disclosure report type * add AI metadata checking to block image uploads with notice * update copy * update section 4 of rules * Update rule 6 layout * fix(labrinth): don't remove archival disclosure when changing status via v3 api * feat(labrinth): derive str for ai usages and telemtry consent * feat(labrinth): include ai usages and telemetry consent in disclosure types * Update moderation checklist (#7056) * fix: missing delphi severity (#7054) * Utils nav, new disclosures stage, ai button in rules * add prefix to collect * Finish up new disclosures stage * update r4 messages * new r4 msg, update showcase clarity message * fix: version upload failing to detect mrpack loader (#7063) * fix: mrpack exporting with zip64 (#7064) * fix: action bar max width (#7048) * fix: action bar max width * fix: width * changelog * Rule placeholders with subsections & anchors, update messages. * update new messages to account for new rule and placeholder layout * Add nag for content disclosures --------- Co-authored-by: ThatGravyBoat <gravy@thatgravyboat.tech> Co-authored-by: chyzman <chyzalt@gmail.com> Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> * prepr * refactor(labrinth): move disclosure parsing to document creation * fix(labrinth): prevent removing moderator added archival disclosures * fix(labrinth): dedupe ai usages * fix(labrinth): auth logic on archived disclosure removal * feat(labrinth): add interactions field * style(labrinth): cargo fmt * feat(labrinth): granular disclosure locking * add 5.8, move 5.9, update messages and placeholders accordingly. * move disclosures stage earlier * include consent model info in telemetry disclosure msgs * prepr + update date * typo * qa pass * search suboptions * add empty state * checkboxes in column * persistent advanced filters * blog draft * prepr * support soft deletion * reload instead of optimistically updating * update blog * TOS update + minor verbiage adjustment * 45 day grace --------- Co-authored-by: sychic <47618543+Sychic@users.noreply.github.com> Co-authored-by: coolbot <76798835+coolbot100s@users.noreply.github.com> Co-authored-by: ThatGravyBoat <gravy@thatgravyboat.tech> Co-authored-by: chyzman <chyzalt@gmail.com> Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com>
180 lines
4.9 KiB
Vue
180 lines
4.9 KiB
Vue
<script setup lang="ts">
|
||
import { GitForkIcon, PlusIcon, TrashIcon } from '@modrinth/assets'
|
||
import {
|
||
Button,
|
||
commonMessages,
|
||
defineMessages,
|
||
SettingsFormGroup,
|
||
StyledInput,
|
||
useVIntl,
|
||
} from '@modrinth/ui'
|
||
import { watch } from 'vue'
|
||
|
||
import DisclosureToggleCard from './DisclosureToggleCard.vue'
|
||
import type {
|
||
DerivativeDisclosure,
|
||
DerivativeSource,
|
||
DisclosureCardMetaProps,
|
||
DisclosureLockStatus,
|
||
} from './types'
|
||
|
||
const model = defineModel<DerivativeDisclosure>({ required: true })
|
||
|
||
const props = defineProps<DisclosureCardMetaProps>()
|
||
|
||
const emit = defineEmits<{
|
||
setLockStatus: [status: DisclosureLockStatus]
|
||
}>()
|
||
|
||
const { formatMessage } = useVIntl()
|
||
|
||
const messages = defineMessages({
|
||
title: {
|
||
id: 'project.settings.disclosures.derivative.title',
|
||
defaultMessage: 'Contains derivative content',
|
||
},
|
||
description: {
|
||
id: 'project.settings.disclosures.derivative.description',
|
||
defaultMessage:
|
||
'You must enable this if your project is derivative of another project, such as being a fork or containing a substantial amount of someone else’s work.',
|
||
},
|
||
nameLabel: {
|
||
id: 'project.settings.disclosures.derivative.name-label',
|
||
defaultMessage: 'Name of original work',
|
||
},
|
||
namePlaceholder: {
|
||
id: 'project.settings.disclosures.derivative.name-placeholder',
|
||
defaultMessage: 'Example project',
|
||
},
|
||
linkLabel: {
|
||
id: 'project.settings.disclosures.derivative.link-label',
|
||
defaultMessage: 'Link to original work',
|
||
},
|
||
linkPlaceholder: {
|
||
id: 'project.settings.disclosures.derivative.link-placeholder',
|
||
defaultMessage: 'https://example.com',
|
||
},
|
||
noteLabel: {
|
||
id: 'project.settings.disclosures.derivative.note-label',
|
||
defaultMessage: 'Explain how your project is based on the original work',
|
||
},
|
||
})
|
||
|
||
function emptyDerivativeSource(): DerivativeSource {
|
||
return { label: '' }
|
||
}
|
||
|
||
function updateSource(index: number, patch: Partial<DerivativeSource>) {
|
||
model.value.sources = model.value.sources.map((source, i) =>
|
||
i === index ? { ...source, ...patch } : source,
|
||
)
|
||
}
|
||
|
||
watch(
|
||
() => model.value.enabled,
|
||
(enabled) => {
|
||
if (enabled && model.value.sources.length === 0) {
|
||
model.value.sources = [emptyDerivativeSource()]
|
||
}
|
||
},
|
||
)
|
||
|
||
function addSource() {
|
||
model.value.sources = [...model.value.sources, emptyDerivativeSource()]
|
||
}
|
||
|
||
function removeSource(index: number) {
|
||
if (model.value.sources.length <= 1) return
|
||
model.value.sources = model.value.sources.filter((_, i) => i !== index)
|
||
}
|
||
|
||
function setOptionalField(
|
||
index: number,
|
||
field: 'link' | 'note',
|
||
value: string | number | undefined,
|
||
) {
|
||
updateSource(index, {
|
||
[field]: typeof value === 'string' && value ? value : null,
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<DisclosureToggleCard
|
||
v-bind="props"
|
||
v-model="model.enabled"
|
||
:icon="GitForkIcon"
|
||
:title="formatMessage(messages.title)"
|
||
:description="formatMessage(messages.description)"
|
||
@set-lock-status="emit('setLockStatus', $event)"
|
||
>
|
||
<template #expanded>
|
||
<div
|
||
v-for="(source, index) in model.sources"
|
||
:key="index"
|
||
class="relative flex flex-col gap-4 rounded-2xl border border-solid border-surface-4 bg-surface-3 p-4"
|
||
>
|
||
<div class="absolute right-3 top-3">
|
||
<Button
|
||
v-if="model.sources.length > 1"
|
||
type="quiet"
|
||
color="red"
|
||
:disabled="disabled"
|
||
@click="removeSource(index)"
|
||
>
|
||
<TrashIcon />
|
||
{{ formatMessage(commonMessages.removeButton) }}
|
||
</Button>
|
||
</div>
|
||
<SettingsFormGroup
|
||
:title="formatMessage(messages.nameLabel)"
|
||
:title-for="`derivative-name-${index}`"
|
||
class="max-w-[40rem]"
|
||
>
|
||
<StyledInput
|
||
:id="`derivative-name-${index}`"
|
||
:model-value="source.label"
|
||
:disabled="disabled"
|
||
:placeholder="formatMessage(messages.namePlaceholder)"
|
||
@update:model-value="
|
||
(value) => updateSource(index, { label: typeof value === 'string' ? value : '' })
|
||
"
|
||
/>
|
||
</SettingsFormGroup>
|
||
<SettingsFormGroup
|
||
:title="formatMessage(messages.linkLabel)"
|
||
:title-for="`derivative-link-${index}`"
|
||
class="max-w-[40rem]"
|
||
>
|
||
<StyledInput
|
||
:id="`derivative-link-${index}`"
|
||
:model-value="source.link ?? undefined"
|
||
type="url"
|
||
:disabled="disabled"
|
||
:placeholder="formatMessage(messages.linkPlaceholder)"
|
||
@update:model-value="(value) => setOptionalField(index, 'link', value)"
|
||
/>
|
||
</SettingsFormGroup>
|
||
<SettingsFormGroup
|
||
:title="formatMessage(messages.noteLabel)"
|
||
:title-for="`derivative-note-${index}`"
|
||
class="max-w-[40rem]"
|
||
>
|
||
<StyledInput
|
||
:id="`derivative-note-${index}`"
|
||
:model-value="source.note ?? undefined"
|
||
multiline
|
||
:rows="3"
|
||
:disabled="disabled"
|
||
@update:model-value="(value) => setOptionalField(index, 'note', value)"
|
||
/>
|
||
</SettingsFormGroup>
|
||
</div>
|
||
<Button class="w-fit" :disabled="disabled" @click="addSource">
|
||
<PlusIcon />
|
||
{{ formatMessage(commonMessages.addAnotherButton) }}
|
||
</Button>
|
||
</template>
|
||
</DisclosureToggleCard>
|
||
</template>
|