feat: add automated community moderation
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import PendingForm from '$lib/components/PendingForm.svelte';
|
||||
|
||||
let {
|
||||
contentType,
|
||||
contentId,
|
||||
label = 'REPORT',
|
||||
result
|
||||
}: {
|
||||
contentType: 'comment' | 'profile_bio' | 'guide' | 'avatar';
|
||||
contentId: string;
|
||||
label?: string;
|
||||
result?: Record<string, unknown> | null;
|
||||
} = $props();
|
||||
|
||||
const categories = [
|
||||
{ value: 'spam', label: 'SPAM OR PROMOTION' },
|
||||
{ value: 'harassment', label: 'HARASSMENT OR BULLYING' },
|
||||
{ value: 'hate', label: 'HATEFUL CONTENT' },
|
||||
{ value: 'sexual', label: 'SEXUAL CONTENT' },
|
||||
{ value: 'violence', label: 'VIOLENCE OR THREATS' },
|
||||
{ value: 'self_harm', label: 'SELF-HARM' },
|
||||
{ value: 'privacy', label: 'PRIVATE INFORMATION' },
|
||||
{ value: 'other', label: 'OTHER POLICY CONCERN' }
|
||||
];
|
||||
const matchesResult = $derived(
|
||||
String(result?.userReportContentId ?? '') === contentId
|
||||
&& String(result?.userReportContentType ?? '') === contentType
|
||||
);
|
||||
</script>
|
||||
|
||||
<details class="content-report">
|
||||
<summary>{label}</summary>
|
||||
<div class="content-report-panel">
|
||||
<strong>REQUEST OVERSIGHT REVIEW</strong>
|
||||
<p>Send this content to Community Oversight. A report does not remove it automatically.</p>
|
||||
<PendingForm action="?/reportContent" toast={false}>
|
||||
{#snippet children(pending)}
|
||||
<input type="hidden" name="contentType" value={contentType}/>
|
||||
<input type="hidden" name="contentId" value={contentId}/>
|
||||
<label class="field">CONCERN
|
||||
<select name="category" required>
|
||||
{#each categories as category}<option value={category.value}>{category.label}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">DETAILS — OPTIONAL
|
||||
<textarea name="details" maxlength="500" rows="3" placeholder="Give Oversight useful context without repeating sensitive information."></textarea>
|
||||
</label>
|
||||
{#if matchesResult && result?.userReportError}<p class="error">{String(result.userReportError)}</p>{/if}
|
||||
{#if matchesResult && result?.userReportSaved}<p class="success">Report sent to Community Oversight.</p>{/if}
|
||||
<div class="content-report-actions">
|
||||
<button class="primary" disabled={pending} aria-busy={pending}>{pending ? 'SENDING…' : 'SEND REPORT →'}</button>
|
||||
</div>
|
||||
{/snippet}
|
||||
</PendingForm>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<style>
|
||||
.content-report{position:relative}.content-report summary{list-style:none;min-height:32px;display:inline-flex;align-items:center;padding:0 7px;border:1px solid var(--shd-border);color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.7px;cursor:pointer}.content-report summary::-webkit-details-marker{display:none}.content-report summary:hover,.content-report summary:focus-visible{border-color:var(--shd-danger);color:var(--shd-danger)}.content-report[open] summary{border-color:var(--shd-orange);color:var(--shd-orange)}.content-report-panel{position:absolute;z-index:8;right:0;width:min(380px,calc(100vw - 32px));display:grid;gap:8px;padding:10px;border:1px solid var(--shd-border-strong);background:var(--shd-surface);box-shadow:0 18px 46px #000a}.content-report-panel>strong{color:var(--shd-orange);font-size:var(--shd-type-telemetry);letter-spacing:.8px}.content-report-panel>p{margin:0;color:var(--shd-text-muted);font-size:var(--shd-type-supporting)}.content-report-panel :global(form fieldset){display:grid;gap:8px}.content-report-panel :global(.field){margin:0}.content-report-panel textarea{min-height:80px}.content-report-actions{display:flex;justify-content:flex-end}.content-report-actions button{min-height:40px}
|
||||
@media(max-width:650px){.content-report-panel{position:fixed;right:16px;bottom:16px;left:16px;width:auto}.content-report summary{min-height:44px}.content-report-actions button{width:100%;min-height:44px}}
|
||||
@media(prefers-reduced-motion:reduce){.content-report summary{transition:none}}
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
<aside class="moderation-disclosure" aria-label="Automated safety review">
|
||||
<span aria-hidden="true">i</span>
|
||||
<p>
|
||||
This submission may receive automated safety review.
|
||||
<a href="/terms#privacy">TERMS & PRIVACY →</a>
|
||||
</p>
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
.moderation-disclosure{display:flex;align-items:flex-start;gap:8px;margin:8px 0;padding:8px 9px;border:1px solid var(--shd-border-soft);background:var(--shd-surface);color:var(--shd-text-muted)}
|
||||
.moderation-disclosure>span{flex:0 0 20px;display:grid;place-items:center;width:20px;height:20px;border:1px solid var(--shd-border-strong);color:var(--shd-orange);font-size:var(--shd-type-telemetry);font-weight:900}
|
||||
.moderation-disclosure p{margin:0;font-size:var(--shd-type-telemetry);line-height:1.5}
|
||||
.moderation-disclosure a{display:inline-block;margin-left:4px;color:var(--shd-orange);font-weight:900;letter-spacing:.4px}
|
||||
.moderation-disclosure a:hover{text-decoration:underline}
|
||||
.moderation-disclosure a:focus-visible{outline:2px solid var(--shd-orange);outline-offset:3px}
|
||||
@media(max-width:650px){.moderation-disclosure{padding:10px}.moderation-disclosure a{display:block;min-height:44px;margin:4px 0 0;padding-top:8px}}
|
||||
</style>
|
||||
Reference in New Issue
Block a user