[Chat] Prevent reactions when unavailable (#10615)

This commit is contained in:
Samuel Newman
2026-05-26 19:55:30 +03:00
committed by GitHub
parent 308b2e05f1
commit 1113686e30
5 changed files with 125 additions and 33 deletions
+60 -2
View File
@@ -1,7 +1,15 @@
import {type $Typed, ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api'
import {
type $Typed,
ChatBskyActorDefs,
ChatBskyConvoDefs,
moderateProfile,
type ModerationOpts,
} from '@atproto/api'
import {EMOJI_REACTION_LIMIT} from '#/lib/constants'
import {logger} from '#/logger'
import {type Shadow} from '#/state/cache/profile-shadow'
import {type ConvoState, ConvoStatus} from '#/state/messages/convo/types'
import * as bsky from '#/types/bsky'
export const MESSAGE_GAP_THRESHOLD_MS = 60 * 60 * 1000
@@ -31,8 +39,9 @@ export function canBeAddedToGroup(profile: bsky.profile.AnyProfileView) {
case 'all':
return true
case 'following':
case undefined:
return Boolean(profile.viewer?.followedBy)
case undefined:
return canBeMessaged(profile)
default:
return false
}
@@ -73,6 +82,55 @@ export function hasReachedReactionLimit(
return myReactions.length >= EMOJI_REACTION_LIMIT
}
/**
* Whether the active conversation accepts emoji reactions. Reactions are
* unavailable when:
* - the convo is in the disabled state
* - a group convo is locked or permanently locked
* - 1-1: the other user is blocked or is blocking us
* - group: we are blocking the primary member (the owner)
*/
export function canReact({
convoState,
primaryMember,
moderationOpts,
}: {
convoState: ConvoState
primaryMember: Shadow<bsky.profile.AnyProfileView> | undefined
moderationOpts: ModerationOpts | undefined
}): boolean {
if (convoState.status === ConvoStatus.Disabled) {
return false
}
if (!convoState.convo) {
return true
}
if (convoState.convo.kind === 'group') {
const {lockStatus} = convoState.convo.details
if (lockStatus === 'locked' || lockStatus === 'locked-permanently') {
return false
}
}
if (primaryMember && moderationOpts) {
const moderation = moderateProfile(primaryMember, moderationOpts)
if (convoState.convo.kind === 'direct') {
// Either direction (blocking or blocked-by) hides reactions in 1-1s
if (moderation.blocked) return false
} else {
// In groups, only "we are blocking" the owner hides reactions
const isBlockingPrimary = moderation
.ui('profileView')
.alerts.some(alert => alert.type === 'blocking')
if (isBlockingPrimary) return false
}
}
return true
}
export type GroupConvoMember = ChatBskyActorDefs.ProfileViewBasic & {
// can be missing if account deleted
kind?: $Typed<ChatBskyActorDefs.GroupConvoMember>