Use profile shadow in AvatarBubbles

This commit is contained in:
Eric Bailey
2026-06-09 16:53:32 -05:00
committed by Samuel Newman
parent 3bfb2f8035
commit bcdff62cd4
3 changed files with 38 additions and 49 deletions
+28 -33
View File
@@ -1,4 +1,4 @@
import {useEffect, useMemo} from 'react' import {useEffect} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import Animated, { import Animated, {
Easing, Easing,
@@ -8,12 +8,10 @@ import Animated, {
withDelay, withDelay,
withTiming, withTiming,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import { import {moderateProfile} from '@atproto/api'
moderateProfile,
type ModerationOpts,
type ModerationUI,
} from '@atproto/api'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -33,7 +31,6 @@ export function AvatarBubbles({
profiles: allProfiles, profiles: allProfiles,
self = false, self = false,
size = 120, size = 120,
moderationOpts,
}: { }: {
animate?: boolean animate?: boolean
profiles: (bsky.profile.AnyProfileView | undefined)[] profiles: (bsky.profile.AnyProfileView | undefined)[]
@@ -45,19 +42,12 @@ export function AvatarBubbles({
*/ */
self?: boolean self?: boolean
size?: number size?: number
moderationOpts?: ModerationOpts
}) { }) {
const {currentAccount} = useSession() const {currentAccount} = useSession()
const profiles = const profiles =
!self && allProfiles.length > 2 !self && allProfiles.length > 2
? allProfiles.filter(p => !p || p.did !== currentAccount?.did) ? allProfiles.filter(p => !p || p.did !== currentAccount?.did)
: allProfiles : allProfiles
const moderations = useMemo(() => {
if (!moderationOpts) return []
return profiles.map(p => {
return p && moderateProfile(p, moderationOpts)
})
}, [profiles, moderationOpts])
const scale = size / 120 const scale = size / 120
const marginOffset = size < 120 ? -2 : 0 const marginOffset = size < 120 ? -2 : 0
@@ -100,45 +90,50 @@ export function AvatarBubbles({
transform: [{scale}], transform: [{scale}],
transformOrigin: 'top left', transformOrigin: 'top left',
}}> }}>
{layouts.map((layout, i) => ( {layouts.map((layout, i) => {
<AvatarBubble const profile = profiles[i]
key={i} if (!profile) return null
profile={profiles[i]} return (
scale={scales[i]} <AvatarBubble
size={layout.size} key={i}
x={layout.x} profile={profile}
y={layout.y} scale={scales[i]}
zIndex={layout.zIndex} size={layout.size}
includeProfileBorder={layout.border} x={layout.x}
moderation={moderations[i]?.ui('avatar')} y={layout.y}
/> zIndex={layout.zIndex}
))} includeProfileBorder={layout.border}
/>
)
})}
</View> </View>
</Animated.View> </Animated.View>
) )
} }
function AvatarBubble({ function AvatarBubble({
profile, profile: profileUnshadowed,
scale, scale,
size, size,
x, x,
y, y,
zIndex, zIndex,
includeProfileBorder, includeProfileBorder,
moderation,
}: { }: {
profile?: bsky.profile.AnyProfileView profile: bsky.profile.AnyProfileView
scale: SharedValue<number> scale: SharedValue<number>
size: number size: number
x: number x: number
y: number y: number
zIndex?: number zIndex?: number
includeProfileBorder?: boolean includeProfileBorder?: boolean
moderation?: ModerationUI
}) { }) {
const t = useTheme() const t = useTheme()
const profile = useProfileShadow(profileUnshadowed)
const moderationOpts = useModerationOpts()
const moderation = moderationOpts
? moderateProfile(profile, moderationOpts)
: undefined
const animatedStyle = useAnimatedStyle(() => ({ const animatedStyle = useAnimatedStyle(() => ({
transform: [{translateX: x}, {translateY: y}, {scale: scale.get()}], transform: [{translateX: x}, {translateY: y}, {scale: scale.get()}],
})) }))
@@ -163,7 +158,7 @@ function AvatarBubble({
type="user" type="user"
hideLiveBadge hideLiveBadge
noBorder noBorder
moderation={moderation} moderation={moderation?.ui('avatar')}
/> />
) : ( ) : (
<AvatarPlaceholder size={size} /> <AvatarPlaceholder size={size} />
+2 -8
View File
@@ -42,7 +42,7 @@ export function MessagesListHeader({convo}: {convo?: ConvoWithDetails | null}) {
convo.kind === 'direct' ? ( convo.kind === 'direct' ? (
<ProfileHeaderReady convo={convo} moderationOpts={moderationOpts} /> <ProfileHeaderReady convo={convo} moderationOpts={moderationOpts} />
) : ( ) : (
<GroupHeaderReady convo={convo} moderationOpts={moderationOpts} /> <GroupHeaderReady convo={convo} />
) )
) : ( ) : (
<> <>
@@ -152,10 +152,8 @@ function ProfileHeaderReady({
function GroupHeaderReady({ function GroupHeaderReady({
convo, convo,
moderationOpts,
}: { }: {
convo: Extract<ConvoWithDetails, {kind: 'group'}> convo: Extract<ConvoWithDetails, {kind: 'group'}>
moderationOpts: ModerationOpts
}) { }) {
const {t: l} = useLingui() const {t: l} = useLingui()
@@ -178,11 +176,7 @@ function GroupHeaderReady({
}, },
} }
}> }>
<AvatarBubbles <AvatarBubbles size={40} profiles={convo.members} />
size={40}
profiles={convo.members}
moderationOpts={moderationOpts}
/>
<View style={[a.flex_row, a.flex_1, a.align_center]}> <View style={[a.flex_row, a.flex_1, a.align_center]}>
<Text <Text
style={[a.text_lg, a.font_semi_bold, a.flex_shrink]} style={[a.text_lg, a.font_semi_bold, a.flex_shrink]}
+8 -8
View File
@@ -132,14 +132,6 @@ export class Convo {
recipients: ChatBskyActorDefs.ProfileViewBasic[] | undefined recipients: ChatBskyActorDefs.ProfileViewBasic[] | undefined
snapshot: ConvoState | undefined snapshot: ConvoState | undefined
mergeProfileShadow(did: string, shadow: Partial<ProfileShadow>) {
const related = this.relatedProfiles.get(did)
if (related) {
this.relatedProfiles.set(did, mergeShadow(related, shadow))
this.commit()
}
}
constructor(params: ConvoParams) { constructor(params: ConvoParams) {
this.id = nanoid(3) this.id = nanoid(3)
this.convoId = params.convoId this.convoId = params.convoId
@@ -1494,4 +1486,12 @@ export class Convo {
throw error throw error
} }
} }
mergeProfileShadow(did: string, shadow: Partial<ProfileShadow>) {
const related = this.relatedProfiles.get(did)
if (related) {
this.relatedProfiles.set(did, mergeShadow(related, shadow))
this.commit()
}
}
} }