[SDK] Route moderation reports through the appview client (#11368)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-13 22:26:18 +03:00
committed by GitHub
parent 34d1c75756
commit d991118215
7 changed files with 127 additions and 87 deletions
@@ -1,22 +1,21 @@
import {
type $Typed,
BSKY_LABELER_DID,
type ChatBskyConvoDefs,
type ComAtprotoModerationCreateReport,
} from '@atproto/api'
import {BSKY_LABELER_DID} from '@atproto/api'
import {type AtUriString, type DidString} from '@atproto/syntax'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useMutation} from '@tanstack/react-query'
import {logger} from '#/logger'
import {useAgent} from '#/state/session'
import {useAppviewClient} from '#/state/session'
import {com} from '#/lexicons'
import {NEW_TO_OLD_REASONS_MAP, REPORT_MOD_TOOL_NAME} from './const'
import {type ReportState} from './state'
import {type ParsedReportSubject} from './types'
type ReportInput = com.atproto.moderation.createReport.$InputBody
export function useSubmitReportMutation() {
const {_} = useLingui()
const agent = useAgent()
const client = useAppviewClient()
return useMutation({
async mutationFn({
@@ -59,13 +58,7 @@ export function useSubmitReportMutation() {
reasonType = backwardsCompatibleReasonType
}
let report:
| ComAtprotoModerationCreateReport.InputSchema
| (Omit<ComAtprotoModerationCreateReport.InputSchema, 'subject'> & {
subject:
| $Typed<ChatBskyConvoDefs.MessageRef>
| $Typed<ChatBskyConvoDefs.ConvoRef>
})
let report: ReportInput
switch (subject.type) {
case 'account': {
@@ -74,7 +67,8 @@ export function useSubmitReportMutation() {
reason: state.details,
subject: {
$type: 'com.atproto.admin.defs#repoRef',
did: subject.did,
// the parsed subject carries an already-resolved did
did: subject.did as DidString,
},
}
break
@@ -89,7 +83,8 @@ export function useSubmitReportMutation() {
reason: state.details,
subject: {
$type: 'com.atproto.repo.strongRef',
uri: subject.uri,
// the parsed subject carries an at-uri read off a view
uri: subject.uri as AtUriString,
cid: subject.cid,
},
}
@@ -99,12 +94,12 @@ export function useSubmitReportMutation() {
report = {
reasonType,
reason: state.details,
subject: {
subject: toOpenSubject({
$type: 'chat.bsky.convo.defs#messageRef',
messageId: subject.message.id,
convoId: subject.convoId,
did: subject.message.sender.did,
},
}),
}
break
}
@@ -112,11 +107,11 @@ export function useSubmitReportMutation() {
report = {
reasonType,
reason: state.details,
subject: {
subject: toOpenSubject({
$type: 'chat.bsky.convo.defs#convoRef',
convoId: subject.convoId,
did: subject.did,
},
}),
}
break
}
@@ -145,13 +140,30 @@ export function useSubmitReportMutation() {
report,
})
} else {
await agent.createModerationReport(report, {
encoding: 'application/json',
headers: {
'atproto-proxy': `${labeler.creator.did}#atproto_labeler`,
},
/*
* Reports go to the labeler the user selected rather than Bluesky's, so
* the proxy target is built per call from that labeler's creator did.
*/
await client.call(com.atproto.moderation.createReport, report, {
service: `${labeler.creator.did as DidString}#atproto_labeler`,
})
}
},
})
}
/**
* Widen a chat convo ref into `createReport`'s subject union.
*
* The lexicon declares only `com.atproto.admin.defs#repoRef` and
* `com.atproto.repo.strongRef`, but leaves the union OPEN, and the chat service
* accepts its own refs there. An open union types its unknown arm as
* `{$type: Unknown$Type}`, which a concrete chat ref does not structurally
* satisfy, so the widening is asserted here once rather than at each call site.
*/
function toOpenSubject(ref: {
$type: string
[key: string]: unknown
}): ReportInput['subject'] {
return ref as unknown as ReportInput['subject']
}