import {View} from 'react-native'
import {moderateProfile} from '@bsky.app/sdk/moderation'
import {Trans, useLingui} from '@lingui/react/macro'
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {logger} from '#/logger'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useRemoveFromGroupChat} from '#/state/queries/messages/remove-from-group'
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
import {useRequireAuth, useSession} from '#/state/session'
import {atoms as a, native, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {
type ConvoWithDetails,
type GroupConvoMember,
} from '#/components/dms/util'
import {createStaticClick, SimpleInlineLinkText} from '#/components/Link'
import * as ProfileCard from '#/components/ProfileCard'
import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {MemberMenu} from './MemberMenu'
import {RemoveMemberPrompt} from './prompts'
import {StatusBadge} from './StatusBadge'
import {SubtleHoverWrapper} from './SubtleHoverWrapper'
const outerStyles = [a.px_xl, a.py_sm, a.flex_row, a.align_center, a.gap_sm]
export function Member({
convo,
profile: profileUnshadowed,
status,
isOwner,
}: {
convo: ConvoWithDetails
profile: GroupConvoMember
status: 'owner' | 'standard'
isOwner: boolean
}) {
const t = useTheme()
const {t: l} = useLingui()
const profile = useProfileShadow(profileUnshadowed)
const {currentAccount} = useSession()
const moderationOpts = useModerationOpts()
const [queueFollow] = useProfileFollowMutationQueue(profile, 'GroupChat')
const requireAuth = useRequireAuth()
const removeMemberPrompt = Prompt.usePromptControl()
const {mutate: removeMembers} = useRemoveFromGroupChat(convo.view.id, {
onError: e => {
logger.error('Failed to remove group chat member', {message: e})
Toast.show(l`Failed to remove group chat member`, {type: 'error'})
},
})
const isFollowing = !!profile.viewer?.following
const handleFollow = () => {
requireAuth(async () => {
try {
await queueFollow()
Toast.show(l`Following ${displayName}`)
} catch (err) {
const e = err as Error
if (e?.name !== 'AbortError') {
logger.error('Failed to follow', {message: String(e)})
Toast.show(l`There was an issue! ${e.toString()}`, {
type: 'error',
})
}
}
})
}
if (!moderationOpts) {
return
}
const moderation = moderateProfile(profile, moderationOpts)
const isDeletedAccount = profile.handle === 'missing.invalid'
const displayName = isDeletedAccount
? l`Deleted Account`
: createSanitizedDisplayName(profile, true, moderation.ui('displayName'))
const isProfileOwner = profile.did === convo.primaryMember?.did
const isSelf = currentAccount?.did === profile.did
let statusBadge: React.ReactNode | null = null
if (isSelf) {
if (status === 'owner') {
statusBadge =
}
} else {
statusBadge = (
)
}
const joinedReason = profile.kind?.addedBy
? l`Added by ${createSanitizedDisplayName(
profile.kind.addedBy,
true,
moderateProfile(profile.kind.addedBy, moderationOpts).ui('displayName'),
)}`
: l`Added by invite link`
// Surface a prominent remove button to the owner for blocked members.
const showRemoveButton = isOwner && !isSelf && !!isBlockedOrBlocking(profile)
return (
{!isProfileOwner && (
{joinedReason}
)}
{showRemoveButton ? (
) : isSelf || isFollowing || isBlockedOrBlocking(profile) ? null : (
Follow
)}
{statusBadge}
{/* Mounted outside the showRemoveButton conditional: confirming the
prompt optimistically drops this row, so gating the prompt on the
button would unmount it mid-close and race the dismiss animation. */}
removeMembers({members: [profile.did]})}
/>
)
}
export function MemberPlaceholder() {
return (
)
}