From 6997d220a02f34f34d311eef71f3eeef3c5f478d Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Mon, 8 Jun 2026 04:57:08 -0700 Subject: [PATCH] Add Leave chat button to settings for chat owners (#10764) Co-authored-by: Samuel Newman --- .../Messages/ConversationSettings/index.tsx | 92 ++++++++++++------- .../Messages/ConversationSettings/prompts.tsx | 24 +++++ .../queries/messages/lock-conversation.ts | 16 +++- 3 files changed, 96 insertions(+), 36 deletions(-) diff --git a/src/screens/Messages/ConversationSettings/index.tsx b/src/screens/Messages/ConversationSettings/index.tsx index fbc4ffcc63..2ae09b5130 100644 --- a/src/screens/Messages/ConversationSettings/index.tsx +++ b/src/screens/Messages/ConversationSettings/index.tsx @@ -58,7 +58,12 @@ import {InviteLinkDialog} from '../components/InviteLinkDialog' import {AddMembersLink} from './AddMembersLink' import {Member, MemberPlaceholder} from './Member' import {MembersAndRequests} from './MembersAndRequests' -import {EditNamePrompt, LeaveChatPrompt, LockChatPrompt} from './prompts' +import { + EditNamePrompt, + LeaveAndLockChatPrompt, + LeaveChatPrompt, + LockChatPrompt, +} from './prompts' type Item = | {type: 'MEMBERS_AND_REQUESTS'; key: string} @@ -374,33 +379,49 @@ function SettingsHeader({ }, ) - const {mutate: lockConvo, isPending: isLocking} = useLockConvo( - convo.view.id, - { - onSuccess: data => { - if (!ChatBskyConvoDefs.isGroupConvo(data.convo.kind)) return - if (data.convo.kind.lockStatus === 'locked') { - Toast.show(l({message: 'Group chat locked', context: 'toast'})) - } else { - Toast.show(l({message: 'Group chat unlocked', context: 'toast'})) - } - }, - onError: (e, {lock}) => { - if (lock) { - logger.error('Failed to lock group chat', {message: e}) - Toast.show(l`Failed to lock group chat`, {type: 'error'}) - } else { - logger.error('Failed to unlock group chat', {message: e}) - Toast.show(l`Failed to unlock group chat`, {type: 'error'}) - } - }, + const { + mutate: lockConvo, + mutateAsync: lockConvoAsync, + isPending: isLocking, + } = useLockConvo(convo.view.id, { + onSuccess: (data, {silent}) => { + if (!ChatBskyConvoDefs.isGroupConvo(data.convo.kind)) return + if (silent) return + if (data.convo.kind.lockStatus === 'locked') { + Toast.show(l({message: 'Group chat locked', context: 'toast'})) + } else { + Toast.show(l({message: 'Group chat unlocked', context: 'toast'})) + } }, - ) + onError: (e, {lock}) => { + if (lock) { + logger.error('Failed to lock group chat', {message: e}) + Toast.show(l`Failed to lock group chat`, {type: 'error'}) + } else { + logger.error('Failed to unlock group chat', {message: e}) + Toast.show(l`Failed to unlock group chat`, {type: 'error'}) + } + }, + }) + + const leaveAndLockConvo = async () => { + try { + if (lockStatus === 'unlocked') { + await lockConvoAsync({lock: true, silent: true}) + } + } catch { + // Handled by onError in useLockConvo + return + } + // Owners can only leave a locked chat + leaveConvo() + } const inviteLinkDialog = Dialog.useDialogControl() const editNamePrompt = Prompt.usePromptControl() const lockChatPrompt = Prompt.usePromptControl() const leaveChatPrompt = Prompt.usePromptControl() + const leaveAndLockChatPrompt = Prompt.usePromptControl() const reportControl = Prompt.usePromptControl() const deleteControl = Prompt.usePromptControl() @@ -452,6 +473,7 @@ function SettingsHeader({ a.justify_center, a.gap_2xl, a.pt_2xl, + a.flex_wrap, ]}> ) : null} - {!isOwner ? ( - - ) : null} + + { + void leaveAndLockConvo() + }} + /> {reportSubjectDid ? ( <> void +}) { + const {t: l} = useLingui() + + return ( + + ) +} + export function BlockMemberPrompt({ control, onConfirm, diff --git a/src/state/queries/messages/lock-conversation.ts b/src/state/queries/messages/lock-conversation.ts index b10db13462..122c0633b4 100644 --- a/src/state/queries/messages/lock-conversation.ts +++ b/src/state/queries/messages/lock-conversation.ts @@ -14,15 +14,21 @@ export function useLockConvo( onSuccess, onError, }: { - onSuccess?: (data: ChatBskyConvoLockConvo.OutputSchema) => void - onError?: (error: Error, variables: {lock: boolean}) => void + onSuccess?: ( + data: ChatBskyConvoLockConvo.OutputSchema, + variables: {lock: boolean; silent?: boolean}, + ) => void + onError?: ( + error: Error, + variables: {lock: boolean; silent?: boolean}, + ) => void }, ) { const queryClient = useQueryClient() const agent = useAgent() return useMutation({ - mutationFn: async ({lock}: {lock: boolean}) => { + mutationFn: async ({lock}: {lock: boolean; silent?: boolean}) => { if (!convoId) throw new Error('No convoId provided') if (lock) { const {data} = await agent.chat.bsky.convo.lockConvo( @@ -51,8 +57,8 @@ export function useLockConvo( } }) }, - onSuccess: data => { - onSuccess?.(data) + onSuccess: (data, variables) => { + onSuccess?.(data, variables) }, onError: (e, variables, context) => { if (convoId && context) {