9aa6b49daf
Point the moderation primitives - the label definitions, the cause/UI/decision types and the label-info hooks - at `@bsky.app/sdk/moderation` instead of `@atproto/api`. The two implementations are mutually unassignable: the sdk's subject types are the generated `#/lexicons` views, so their `did`/`uri`/`cid` fields are branded (`DidString`, `AtUriString`) where the `@atproto/api` views type the same fields as plain `string`. That rules out a partial swap - a decision made by one implementation cannot be read by the other - so the whole moderation layer moves together. `lib/moderation/subjects.ts` is the transitional seam for the brand mismatch. Every `moderate*` entry point is re-exported through a wrapper that widens its subject parameter to accept a view from either world, because many read paths still emit `@atproto/api` views while the moderation runtime only ever reads `.did`, `.labels` and `.viewer` - none of which the brand affects. It is deleted in the `@atproto/api` removal pass.
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import {type ComAtprotoLabelDefs} from '@atproto/api'
|
|
import {
|
|
type InterpretedLabelValueDefinition,
|
|
interpretLabelValueDefinition,
|
|
LABELS,
|
|
} from '@bsky.app/sdk/moderation'
|
|
import {useLingui} from '@lingui/react'
|
|
import * as bcp47Match from 'bcp-47-match'
|
|
|
|
import {
|
|
type GlobalLabelStrings,
|
|
useGlobalLabelStrings,
|
|
} from '#/lib/moderation/useGlobalLabelStrings'
|
|
import {useLabelDefinitions} from '#/state/preferences'
|
|
import {type app} from '#/lexicons'
|
|
|
|
export interface LabelInfo {
|
|
label: ComAtprotoLabelDefs.Label
|
|
def: InterpretedLabelValueDefinition
|
|
strings: ComAtprotoLabelDefs.LabelValueDefinitionStrings
|
|
labeler: app.bsky.labeler.defs.LabelerViewDetailed | undefined
|
|
}
|
|
|
|
export function useLabelInfo(label: ComAtprotoLabelDefs.Label): LabelInfo {
|
|
const {i18n} = useLingui()
|
|
const {labelDefs, labelers} = useLabelDefinitions()
|
|
const globalLabelStrings = useGlobalLabelStrings()
|
|
const def = getDefinition(labelDefs, label)
|
|
return {
|
|
label,
|
|
def,
|
|
strings: getLabelStrings(i18n.locale, globalLabelStrings, def),
|
|
labeler: labelers.find(labeler => label.src === labeler.creator.did),
|
|
}
|
|
}
|
|
|
|
export function getDefinition(
|
|
labelDefs: Record<string, InterpretedLabelValueDefinition[]>,
|
|
label: ComAtprotoLabelDefs.Label,
|
|
): InterpretedLabelValueDefinition {
|
|
// check local definitions
|
|
const customDef =
|
|
!label.val.startsWith('!') &&
|
|
labelDefs[label.src]?.find(
|
|
def => def.identifier === label.val && def.definedBy === label.src,
|
|
)
|
|
if (customDef) {
|
|
return customDef
|
|
}
|
|
|
|
// check global definitions
|
|
const globalDef = LABELS[label.val as keyof typeof LABELS]
|
|
if (globalDef) {
|
|
return globalDef
|
|
}
|
|
|
|
// fallback to a noop definition
|
|
return interpretLabelValueDefinition(
|
|
{
|
|
identifier: label.val,
|
|
severity: 'none',
|
|
blurs: 'none',
|
|
defaultSetting: 'ignore',
|
|
locales: [],
|
|
},
|
|
label.src,
|
|
)
|
|
}
|
|
|
|
export function getLabelStrings(
|
|
locale: string,
|
|
globalLabelStrings: GlobalLabelStrings,
|
|
def: InterpretedLabelValueDefinition,
|
|
): ComAtprotoLabelDefs.LabelValueDefinitionStrings {
|
|
if (!def.definedBy) {
|
|
// global definition, look up strings
|
|
if (def.identifier in globalLabelStrings) {
|
|
return globalLabelStrings[
|
|
def.identifier
|
|
] as ComAtprotoLabelDefs.LabelValueDefinitionStrings
|
|
}
|
|
} else {
|
|
// try to find locale match in the definition's strings
|
|
const localeMatch = def.locales.find(
|
|
strings => bcp47Match.basicFilter(locale, strings.lang).length > 0,
|
|
)
|
|
if (localeMatch) {
|
|
return localeMatch
|
|
}
|
|
// fall back to the zero item if no match
|
|
if (def.locales[0]) {
|
|
return def.locales[0]
|
|
}
|
|
}
|
|
return {
|
|
lang: locale,
|
|
name: def.identifier,
|
|
description: `Labeled "${def.identifier}"`,
|
|
}
|
|
}
|