diff --git a/src/screens/Messages/ConversationSettings/Member.tsx b/src/screens/Messages/ConversationSettings/Member.tsx
index 9786e69f55..ab39f11741 100644
--- a/src/screens/Messages/ConversationSettings/Member.tsx
+++ b/src/screens/Messages/ConversationSettings/Member.tsx
@@ -7,18 +7,22 @@ import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-disp
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'
@@ -45,6 +49,14 @@ export function Member({
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 = () => {
@@ -101,6 +113,9 @@ export function Member({
)}`
: l`Added by invite link`
+ // Surface a prominent remove button to the owner for blocked members.
+ const showRemoveButton = isOwner && !isSelf && !!isBlockedOrBlocking(profile)
+
return (
@@ -131,13 +146,29 @@ export function Member({
web(a.pt_2xs),
]}>
{joinedReason}
+ {showRemoveButton && (
+ <>
+ {' • '}
+ Blocked
+ >
+ )}
)}
- {isSelf || isFollowing || isBlockedOrBlocking(profile) ? null : (
+ {showRemoveButton ? (
+
+ ) : isSelf || isFollowing || isBlockedOrBlocking(profile) ? null : (
+ removeMembers({members: [profile.did]})}
+ />
)
}
diff --git a/src/screens/Messages/ConversationSettings/index.tsx b/src/screens/Messages/ConversationSettings/index.tsx
index 2ae09b5130..cd182762be 100644
--- a/src/screens/Messages/ConversationSettings/index.tsx
+++ b/src/screens/Messages/ConversationSettings/index.tsx
@@ -10,6 +10,7 @@ import {useNavigation} from '@react-navigation/native'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
+import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
import {
type CommonNavigatorParams,
type NativeStackScreenProps,
@@ -213,6 +214,12 @@ function GroupSettings({
const bIsSelf = b.did === currentAccount?.did
if (aIsOwner !== bIsOwner) return aIsOwner ? -1 : 1
if (aIsSelf !== bIsSelf) return aIsSelf ? -1 : 1
+ // Surface blocked members to the owner so they can be removed.
+ if (isOwner) {
+ const aBlocked = !!isBlockedOrBlocking(a)
+ const bBlocked = !!isBlockedOrBlocking(b)
+ if (aBlocked !== bBlocked) return aBlocked ? -1 : 1
+ }
return 0
})
diff --git a/src/screens/Messages/ConversationSettings/prompts.tsx b/src/screens/Messages/ConversationSettings/prompts.tsx
index 2eb293bdef..0c3ba2e8ca 100644
--- a/src/screens/Messages/ConversationSettings/prompts.tsx
+++ b/src/screens/Messages/ConversationSettings/prompts.tsx
@@ -151,6 +151,30 @@ export function LeaveAndLockChatPrompt({
)
}
+export function RemoveMemberPrompt({
+ control,
+ displayName,
+ onConfirm,
+}: {
+ control: Dialog.DialogOuterProps['control']
+ displayName: string
+ onConfirm: () => void
+}) {
+ const {t: l} = useLingui()
+
+ return (
+
+ )
+}
+
export function BlockMemberPrompt({
control,
onConfirm,