Add ability to remove members from a group clip clop (#10287)

This commit is contained in:
DS Boyce
2026-04-23 12:30:03 -07:00
committed by Eric Bailey
parent c49e5a566c
commit 5039d80a7e
4 changed files with 125 additions and 9 deletions
+24 -6
View File
@@ -21,12 +21,13 @@ import {ConvoProvider, useConvo} from '#/state/messages/convo'
import {ConvoStatus} from '#/state/messages/convo/types'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useAddGroupMembers} from '#/state/queries/messages/add-group-members'
import {useEditGroupName} from '#/state/queries/messages/edit-group-name'
import {useEditGroupChatName} from '#/state/queries/messages/edit-group-chat-name'
import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability'
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import {useListJoinRequestsQuery} from '#/state/queries/messages/list-join-requests'
import {useMuteConvo} from '#/state/queries/messages/mute-conversation'
import {useRemoveFromGroupChat} from '#/state/queries/messages/remove-from-group'
import {useProfileBlockMutationQueue} from '#/state/queries/profile'
import {useSession} from '#/state/session'
import {List} from '#/view/com/util/List'
@@ -189,6 +190,7 @@ function SettingsInner({convoId}: {convoId: string}) {
case 'CHAT_MEMBER':
return (
<Member
convo={convo}
profile={item.profile}
status={item.status}
isOwner={isOwner}
@@ -391,10 +393,12 @@ function AddMembersLink({
}
function Member({
convo,
profile,
status,
isOwner,
}: {
convo: ConvoWithDetails | null
profile: Shadow<bsky.profile.AnyProfileView>
status: 'owner' | 'standard' | 'invited'
isOwner: boolean
@@ -429,9 +433,14 @@ function Member({
break
}
} else {
statusBadge = (
<MemberMenu profile={profile} type={status} isOwner={isOwner} />
)
statusBadge = convo ? (
<MemberMenu
convo={convo}
profile={profile}
type={status}
isOwner={isOwner}
/>
) : null
}
return (
@@ -537,10 +546,12 @@ function StatusButton({
}
function MemberMenu({
convo,
profile,
type,
isOwner,
}: {
convo: ConvoWithDetails
profile: Shadow<bsky.profile.AnyProfileView>
type: 'owner' | 'standard' | 'invited'
isOwner: boolean
@@ -564,6 +575,13 @@ function MemberMenu({
Toast.show(l`Failed to create conversation`)
},
})
const convoId = convo?.view.id
const {mutate: removeMembers} = useRemoveFromGroupChat(convoId, {
onError: e => {
logger.error('Failed to remove group chat member', {message: e})
Toast.show(l`Failed to remove group chat member`)
},
})
const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile)
const messageMember = () => {
@@ -714,7 +732,7 @@ function MemberMenu({
{isOwner ? (
<Menu.Item
label={l`Remove ${displayName} from this group chat`}
onPress={() => {}}>
onPress={() => removeMembers({members: [profile.did]})}>
<Menu.ItemText>
<Trans>Remove from chat</Trans>
</Menu.ItemText>
@@ -759,7 +777,7 @@ function SettingsHeader({
const [isLocked, setIsLocked] = useState(false)
const {mutate: editGroupName} = useEditGroupName(convo.view.id, {
const {mutate: editGroupName} = useEditGroupChatName(convo.view.id, {
onError: e => {
setNewGroupName(groupName)
logger.error('Failed to edit group chat name', {message: e})
+2 -2
View File
@@ -116,11 +116,11 @@ export function ConvoProvider({
switch (event.type) {
case 'invalidate-block-state': {
for (const did of event.accountDids) {
queryClient.invalidateQueries({
void queryClient.invalidateQueries({
queryKey: createProfileQueryKey(did),
})
}
queryClient.invalidateQueries({
void queryClient.invalidateQueries({
queryKey: [ListConvosQueryKeyRoot],
})
}
@@ -15,7 +15,7 @@ import {useAgent} from '#/state/session'
import {RQKEY as CONVO_KEY} from './conversation'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
export function useEditGroupName(
export function useEditGroupChatName(
convoId: string | undefined,
{
onSuccess,
@@ -0,0 +1,98 @@
import {
type ChatBskyConvoDefs,
type ChatBskyConvoListConvos,
type ChatBskyGroupRemoveMembers,
} from '@atproto/api'
import {
type InfiniteData,
useMutation,
useQueryClient,
} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {useAgent} from '#/state/session'
import {RQKEY as CONVO_KEY} from './conversation'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
export function useRemoveFromGroupChat(
convoId: string | undefined,
{
onSuccess,
onError,
}: {
onSuccess?: (data: ChatBskyGroupRemoveMembers.OutputSchema) => void
onError?: (error: Error) => void
},
) {
const queryClient = useQueryClient()
const agent = useAgent()
return useMutation({
mutationFn: async ({members}: {members: string[]}) => {
if (!convoId) throw new Error('No convoId provided')
const {data} = await agent.chat.bsky.group.removeMembers(
{convoId, members},
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
)
return data
},
onMutate: ({members}) => {
if (!convoId) return
const prevConvo = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>(
CONVO_KEY(convoId),
)
const prevListEntries = queryClient.getQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema>
>({queryKey: [CONVO_LIST_KEY]})
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>(
CONVO_KEY(convoId),
prev => {
if (!prev) return
return {
...prev,
members: prev.members.filter(m => !members.includes(m.did)),
}
},
)
queryClient.setQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema>
>({queryKey: [CONVO_LIST_KEY]}, prev => {
if (!prev?.pages) return
return {
...prev,
pages: prev.pages.map(page => ({
...page,
convos: page.convos.map(convo => {
if (convo.id !== convoId) return convo
return {
...convo,
members: convo.members.filter(m => !members.includes(m.did)),
}
}),
})),
}
})
return {prevConvo, prevListEntries}
},
onSuccess: data => {
onSuccess?.(data)
},
onError: (e, _variables, context) => {
logger.error(e)
if (context?.prevConvo && convoId) {
queryClient.setQueryData(CONVO_KEY(convoId), context.prevConvo)
}
if (context?.prevListEntries) {
for (const [key, data] of context.prevListEntries) {
queryClient.setQueryData(key, data)
}
}
onError?.(e)
},
})
}