Add validation to threadgate records, check for max hidden replies (#9178)

This commit is contained in:
Eric Bailey
2025-10-10 10:03:47 -05:00
committed by GitHub
parent e1ee85622d
commit 5bf1141b55
2 changed files with 64 additions and 5 deletions
@@ -46,7 +46,12 @@ import {
useProfileBlockMutationQueue,
useProfileMuteMutationQueue,
} from '#/state/queries/profile'
import {useToggleReplyVisibilityMutation} from '#/state/queries/threadgate'
import {
InvalidInteractionSettingsError,
MAX_HIDDEN_REPLIES,
MaxHiddenRepliesError,
useToggleReplyVisibilityMutation,
} from '#/state/queries/threadgate'
import {useRequireAuth, useSession} from '#/state/session'
import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies'
import * as Toast from '#/view/com/util/Toast'
@@ -339,10 +344,30 @@ let PostMenuItems = ({
: _(msg({message: 'Reply visibility updated', context: 'toast'})),
)
} catch (e: any) {
Toast.show(
_(msg({message: 'Updating reply visibility failed', context: 'toast'})),
)
logger.error(`Failed to ${action} reply`, {safeMessage: e.message})
if (e instanceof MaxHiddenRepliesError) {
Toast.show(
_(
msg({
message: `You can hide a maximum of ${MAX_HIDDEN_REPLIES} replies.`,
context: 'toast',
}),
),
)
} else if (e instanceof InvalidInteractionSettingsError) {
Toast.show(
_(msg({message: 'Invalid interaction settings.', context: 'toast'})),
)
} else {
Toast.show(
_(
msg({
message: 'Updating reply visibility failed',
context: 'toast',
}),
),
)
logger.error(`Failed to ${action} reply`, {safeMessage: e.message})
}
}
}
+34
View File
@@ -25,6 +25,11 @@ import * as bsky from '#/types/bsky'
export * from '#/state/queries/threadgate/types'
export * from '#/state/queries/threadgate/util'
/**
* Must match the threadgate lexicon record definition.
*/
export const MAX_HIDDEN_REPLIES = 300
export const threadgateRecordQueryKeyRoot = 'threadgate-record'
export const createThreadgateRecordQueryKey = (uri: string) => [
threadgateRecordQueryKeyRoot,
@@ -205,6 +210,7 @@ export async function upsertThreadgate(
})
const next = await callback(prev)
if (!next) return
validateThreadgateRecordOrThrow(next)
await writeThreadgateRecord({
agent,
postUri,
@@ -358,3 +364,31 @@ export function useToggleReplyVisibilityMutation() {
},
})
}
export class MaxHiddenRepliesError extends Error {
constructor(message?: string) {
super(message || 'Maximum number of hidden replies reached')
this.name = 'MaxHiddenRepliesError'
}
}
export class InvalidInteractionSettingsError extends Error {
constructor(message?: string) {
super(message || 'Invalid interaction settings')
this.name = 'InvalidInteractionSettingsError'
}
}
export function validateThreadgateRecordOrThrow(
record: AppBskyFeedThreadgate.Record,
) {
const result = AppBskyFeedThreadgate.validateRecord(record)
if (result.success) {
if ((result.value.hiddenReplies?.length ?? 0) > MAX_HIDDEN_REPLIES) {
throw new MaxHiddenRepliesError()
}
} else {
throw new InvalidInteractionSettingsError()
}
}