Link prior moderation actions to case decisions

This commit is contained in:
2026-10-17 19:42:05 -04:00
parent ef96a2c308
commit aca728b557
13 changed files with 948 additions and 17 deletions
@@ -7,9 +7,11 @@
type ModerationCoverage,
type ModerationGame,
type ModerationScope,
type ModerationCase
type ModerationCase,
type ModerationCaseAction
} from '$lib/moderation.js';
import { moderationGameName } from '$ui/moderation.js';
import { moderationActionName, moderationGameName } from '$ui/moderation.js';
import { timeFormat } from '$ui/stores/time-zone.js';
import { serverTitle } from '$ui/server-labels.js';
import { confirmAction } from '$ui/confirm.js';
import Dialog from '../primitives/Dialog.svelte';
@@ -26,6 +28,7 @@
admin,
seed = {},
assignees = [],
linkedActions = [],
onsubmitted
}: {
open?: boolean;
@@ -43,6 +46,7 @@
coverage: ModerationCoverage[];
admin: boolean;
assignees?: { id: string; username: string }[];
linkedActions?: ModerationCaseAction[];
seed?: {
game?: ModerationGame;
guid?: string;
@@ -81,6 +85,21 @@
const reasonBytes = $derived(new TextEncoder().encode(reason.trim()).length);
const gameText = $derived(mode === 'restriction' || mode === 'action');
let resolution = $state('resolved');
let actionReference = $state('');
const referenceActions = $derived(
linkedActions.filter((item) =>
resolution === 'warning_issued'
? item.type === 'action' && item.kind === 'warn' && item.state === 'confirmed'
: resolution === 'restriction_applied' && item.type === 'restriction'
)
);
const referencedAction = $derived(
referenceActions.find((item) => `${item.type}:${item.id}` === actionReference)
);
$effect(() => {
void resolution;
actionReference = '';
});
let busy = $state(false);
let error = $state('');
let uncertain = $state(false);
@@ -203,7 +222,10 @@
id: seed.id,
revision: seed.revision,
status: resolution,
reason: reason.trim()
reason: reason.trim(),
...(referencedAction
? { actionReference: { type: referencedAction.type, id: referencedAction.id } }
: {})
};
}
@@ -468,6 +490,29 @@
separately, and send warnings through Live action. Choosing an outcome does not
change enforcement or send a warning.
</p>{/if}
{#if mode === 'resolve' && ['warning_issued', 'restriction_applied'].includes(resolution)}
<Field
label="Supporting action"
hint="Optional. Reference an existing linked action as the basis for this outcome."
>
<Select
bind:value={actionReference}
options={[
{ value: '', label: 'No action reference' },
...referenceActions.map((item) => ({
value: `${item.type}:${item.id}`,
label: `${moderationActionName(item.kind)} · ${$timeFormat.dateTime(item.createdAt)} · ${item.reason}`
}))
]}
/>
</Field>
{#if !referenceActions.length}<p class="hint">
{resolution === 'warning_issued'
? 'No confirmed warnings are linked to this case.'
: 'No restrictions are linked to this case.'} Use “Link existing action” on
the case to add a prior action.
</p>{/if}
{/if}
{#if mode === 'reopen'}<p class="hint">
Reopening returns the case to Open and preserves its evidence, restrictions, and
earlier decisions.