Files
modrinth/apps/frontend/src/components/ui/project-settings/disclosures/DisclosureToggleCard.vue
T
755825b09a project disclosures frontend (#6955)
* 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>
2026-08-13 09:01:18 -07:00

147 lines
4.3 KiB
Vue

<script setup lang="ts">
import { CircleSlashIcon, LockIcon, LockOpenIcon } from '@modrinth/assets'
import {
Button,
ButtonGroup,
defineMessages,
IntlFormatted,
normalizeChildren,
SettingsInlineWarning,
SettingsToggleCard,
} from '@modrinth/ui'
import type { Component } from 'vue'
import { computed } from 'vue'
import DisclosureUpdatedBy from './DisclosureUpdatedBy.vue'
import type { DisclosureCardMetaProps, DisclosureLockStatus } from './types'
defineOptions({ inheritAttrs: false })
const props = defineProps<
DisclosureCardMetaProps & {
title: string
icon?: Component
description?: string
}
>()
const emit = defineEmits<{
setLockStatus: [status: DisclosureLockStatus]
}>()
const enabled = defineModel<boolean>({ required: true })
const resolvedLockStatus = computed(() => props.lockStatus ?? 'unlocked')
const LOCK_STATUSES = [
{ status: 'unlocked', label: 'Unlocked' },
{ status: 'cannot_disable', label: 'Cannot disable' },
{ status: 'fully_locked', label: 'Fully locked' },
] as const
const messages = defineMessages({
cannotDisableWarning: {
id: 'project.settings.disclosures.lock-status.cannot-disable.warning',
defaultMessage:
'Please <contact-support-link>contact support</contact-support-link> if this disclosure needs to be removed.',
},
fullyLockedWarning: {
id: 'project.settings.disclosures.lock-status.fully-locked.warning',
defaultMessage:
'Please <contact-support-link>contact support</contact-support-link> if this disclosure needs to be edited or removed.',
},
disabledLockedWarning: {
id: 'project.settings.disclosures.lock-status.disabled.warning',
defaultMessage:
'This disclosure has been locked by the moderators. Please <contact-support-link>contact support</contact-support-link> if you believe this is in error.',
},
})
const lockWarningMessage = computed(() => {
if (resolvedLockStatus.value === 'cannot_disable') {
return enabled.value ? messages.cannotDisableWarning : null
}
if (resolvedLockStatus.value === 'fully_locked') {
return enabled.value ? messages.fullyLockedWarning : messages.disabledLockedWarning
}
return null
})
const showLockWarning = computed(() => !!lockWarningMessage.value)
const showModeratorLockControls = computed(() => !!props.showLockControls)
const showFooter = computed(
() => !!props.updatedAt || showModeratorLockControls.value || showLockWarning.value,
)
function setLockStatus(status: DisclosureLockStatus) {
if (resolvedLockStatus.value === status) return
emit('setLockStatus', status)
}
</script>
<template>
<SettingsToggleCard
v-model="enabled"
:disabled="disabled"
:toggle-disabled="toggleDisabled"
:icon="icon"
:title="title"
:description="description"
>
<slot />
<template v-if="$slots.expanded" #expanded>
<slot name="expanded" />
</template>
<template v-if="showFooter" #footer>
<div class="flex flex-col gap-3">
<SettingsInlineWarning v-if="showLockWarning && lockWarningMessage">
<IntlFormatted :message-id="lockWarningMessage">
<template #contact-support-link="{ children }">
<a
class="smart-clickable:allow-pointer-events text-orange underline hover:brightness-110"
href="https://support.modrinth.com"
target="_blank"
rel="noopener noreferrer"
>
<component :is="() => normalizeChildren(children)" />
</a>
</template>
</IntlFormatted>
</SettingsInlineWarning>
<div class="flex flex-wrap items-center justify-between gap-3">
<DisclosureUpdatedBy
v-if="updatedAt"
:updated-at="updatedAt"
:updated-by="updatedBy"
:set-by-moderator="setByModerator"
/>
<span v-else />
<ButtonGroup v-if="showModeratorLockControls" class="ml-auto" label="Lock status">
<Button
v-for="{ status, label } in LOCK_STATUSES"
:key="status"
:color="
status === 'cannot_disable'
? 'orange'
: status === 'fully_locked'
? 'red'
: undefined
"
:type="status !== 'unlocked' ? 'colored-text' : undefined"
:disabled="disabled || resolvedLockStatus === status"
@click="setLockStatus(status)"
>
<LockOpenIcon v-if="status === 'unlocked'" />
<CircleSlashIcon v-else-if="status === 'cannot_disable'" />
<LockIcon v-else />
{{ label }}
</Button>
</ButtonGroup>
</div>
</div>
</template>
</SettingsToggleCard>
</template>