Add Leave chat button to settings for chat owners (#10764)

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
DS Boyce
2026-06-08 04:57:08 -07:00
committed by GitHub
parent e60e31733a
commit 6997d220a0
3 changed files with 96 additions and 36 deletions
@@ -58,7 +58,12 @@ import {InviteLinkDialog} from '../components/InviteLinkDialog'
import {AddMembersLink} from './AddMembersLink' import {AddMembersLink} from './AddMembersLink'
import {Member, MemberPlaceholder} from './Member' import {Member, MemberPlaceholder} from './Member'
import {MembersAndRequests} from './MembersAndRequests' import {MembersAndRequests} from './MembersAndRequests'
import {EditNamePrompt, LeaveChatPrompt, LockChatPrompt} from './prompts' import {
EditNamePrompt,
LeaveAndLockChatPrompt,
LeaveChatPrompt,
LockChatPrompt,
} from './prompts'
type Item = type Item =
| {type: 'MEMBERS_AND_REQUESTS'; key: string} | {type: 'MEMBERS_AND_REQUESTS'; key: string}
@@ -374,33 +379,49 @@ function SettingsHeader({
}, },
) )
const {mutate: lockConvo, isPending: isLocking} = useLockConvo( const {
convo.view.id, mutate: lockConvo,
{ mutateAsync: lockConvoAsync,
onSuccess: data => { isPending: isLocking,
if (!ChatBskyConvoDefs.isGroupConvo(data.convo.kind)) return } = useLockConvo(convo.view.id, {
if (data.convo.kind.lockStatus === 'locked') { onSuccess: (data, {silent}) => {
Toast.show(l({message: 'Group chat locked', context: 'toast'})) if (!ChatBskyConvoDefs.isGroupConvo(data.convo.kind)) return
} else { if (silent) return
Toast.show(l({message: 'Group chat unlocked', context: 'toast'})) if (data.convo.kind.lockStatus === 'locked') {
} Toast.show(l({message: 'Group chat locked', context: 'toast'}))
}, } else {
onError: (e, {lock}) => { Toast.show(l({message: 'Group chat unlocked', context: 'toast'}))
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'})
}
},
}, },
) 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 inviteLinkDialog = Dialog.useDialogControl()
const editNamePrompt = Prompt.usePromptControl() const editNamePrompt = Prompt.usePromptControl()
const lockChatPrompt = Prompt.usePromptControl() const lockChatPrompt = Prompt.usePromptControl()
const leaveChatPrompt = Prompt.usePromptControl() const leaveChatPrompt = Prompt.usePromptControl()
const leaveAndLockChatPrompt = Prompt.usePromptControl()
const reportControl = Prompt.usePromptControl() const reportControl = Prompt.usePromptControl()
const deleteControl = Prompt.usePromptControl() const deleteControl = Prompt.usePromptControl()
@@ -452,6 +473,7 @@ function SettingsHeader({
a.justify_center, a.justify_center,
a.gap_2xl, a.gap_2xl,
a.pt_2xl, a.pt_2xl,
a.flex_wrap,
]}> ]}>
<SettingsButton <SettingsButton
color={convo.view.muted ? 'negative_subtle' : 'secondary'} color={convo.view.muted ? 'negative_subtle' : 'secondary'}
@@ -517,15 +539,15 @@ function SettingsHeader({
onPress={reportControl.open} onPress={reportControl.open}
/> />
) : null} ) : null}
{!isOwner ? ( <SettingsButton
<SettingsButton disabled={!isReady || isLeaving || (isOwner && isLocking)}
disabled={!isReady || isLeaving} icon={ArrowBoxLeftIcon}
icon={ArrowBoxLeftIcon} label={l`Leave this group chat`}
label={l`Leave this group chat`} text={l`Leave`}
text={l`Leave`} onPress={
onPress={leaveChatPrompt.open} isOwner ? leaveAndLockChatPrompt.open : leaveChatPrompt.open
/> }
) : null} />
</View> </View>
</View> </View>
<EditNamePrompt <EditNamePrompt
@@ -552,6 +574,13 @@ function SettingsHeader({
groupName={groupName} groupName={groupName}
onConfirm={leaveConvo} onConfirm={leaveConvo}
/> />
<LeaveAndLockChatPrompt
control={leaveAndLockChatPrompt}
groupName={groupName}
onConfirm={() => {
void leaveAndLockConvo()
}}
/>
{reportSubjectDid ? ( {reportSubjectDid ? (
<> <>
<ReportConversationDialog <ReportConversationDialog
@@ -610,6 +639,7 @@ function SettingsButton({
</Button> </Button>
<Text <Text
numberOfLines={1} numberOfLines={1}
maxFontSizeMultiplier={2.5}
style={[ style={[
a.text_xs, a.text_xs,
a.font_medium, a.font_medium,
@@ -127,6 +127,30 @@ export function LeaveChatPrompt({
) )
} }
export function LeaveAndLockChatPrompt({
control,
groupName,
onConfirm,
}: {
control: Dialog.DialogOuterProps['control']
groupName: string
onConfirm: () => void
}) {
const {t: l} = useLingui()
return (
<Prompt.Basic
control={control}
title={l`Are you sure you want to leave ${groupName}?`}
description={l`Leaving this chat will lock it permanently and you wont be able to rejoin.`}
confirmButtonCta={l`Leave group chat`}
confirmButtonColor="negative"
cancelButtonCta={l`Cancel`}
onConfirm={onConfirm}
/>
)
}
export function BlockMemberPrompt({ export function BlockMemberPrompt({
control, control,
onConfirm, onConfirm,
@@ -14,15 +14,21 @@ export function useLockConvo(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyConvoLockConvo.OutputSchema) => void onSuccess?: (
onError?: (error: Error, variables: {lock: boolean}) => void data: ChatBskyConvoLockConvo.OutputSchema,
variables: {lock: boolean; silent?: boolean},
) => void
onError?: (
error: Error,
variables: {lock: boolean; silent?: boolean},
) => void
}, },
) { ) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const agent = useAgent()
return useMutation({ return useMutation({
mutationFn: async ({lock}: {lock: boolean}) => { mutationFn: async ({lock}: {lock: boolean; silent?: boolean}) => {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
if (lock) { if (lock) {
const {data} = await agent.chat.bsky.convo.lockConvo( const {data} = await agent.chat.bsky.convo.lockConvo(
@@ -51,8 +57,8 @@ export function useLockConvo(
} }
}) })
}, },
onSuccess: data => { onSuccess: (data, variables) => {
onSuccess?.(data) onSuccess?.(data, variables)
}, },
onError: (e, variables, context) => { onError: (e, variables, context) => {
if (convoId && context) { if (convoId && context) {