diff --git a/ts/components/conversation/SessionRightPanel.tsx b/ts/components/conversation/SessionRightPanel.tsx index 8076f5c4d..2869a6200 100644 --- a/ts/components/conversation/SessionRightPanel.tsx +++ b/ts/components/conversation/SessionRightPanel.tsx @@ -7,6 +7,8 @@ import useInterval from 'react-use/lib/useInterval'; import styled from 'styled-components'; import { Data } from '../../data/data'; import { + ConversationInteractionStatus, + ConversationInteractionType, setDisappearingMessagesByConvoId, showAddModeratorsByConvoId, showInviteContactByConvoId, @@ -27,6 +29,7 @@ import { useSelectedIsKickedFromGroup, useSelectedIsLeft, useSelectedIsPublic, + useSelectedLastMessage, useSelectedSubscriberCount, useSelectedWeAreAdmin, } from '../../state/selectors/selectedConversation'; @@ -220,6 +223,7 @@ export const SessionRightPanelWithDetails = () => { const isGroup = useSelectedIsGroup(); const isPublic = useSelectedIsPublic(); const weAreAdmin = useSelectedWeAreAdmin(); + const lastMessage = useSelectedLastMessage(); useEffect(() => { let isRunning = true; @@ -263,6 +267,9 @@ export const SessionRightPanelWithDetails = () => { const hasDisappearingMessages = !isPublic && !commonNoShow; const leaveGroupString = isPublic ? window.i18n('leaveCommunity') + : lastMessage?.interactionType === ConversationInteractionType.Leave && + lastMessage?.interactionStatus === ConversationInteractionStatus.Error + ? window.i18n('deleteConversation') : isKickedFromGroup ? window.i18n('youGotKickedFromGroup') : left diff --git a/ts/components/menu/Menu.tsx b/ts/components/menu/Menu.tsx index 3c0fae9bd..f4448d4ee 100644 --- a/ts/components/menu/Menu.tsx +++ b/ts/components/menu/Menu.tsx @@ -15,9 +15,12 @@ import { useIsPrivate, useIsPrivateAndFriend, useIsPublic, + useLastMessage, useWeAreAdmin, } from '../../hooks/useParamSelector'; import { + ConversationInteractionStatus, + ConversationInteractionType, approveConvoAndSendResponse, blockConvoById, clearNickNameByConvoId, @@ -134,6 +137,7 @@ export const LeaveGroupOrCommunityMenuItem = () => { const isKickedFromGroup = useIsKickedFromGroup(convoId); const isPrivate = useIsPrivate(convoId); const isPublic = useIsPublic(convoId); + const lastMessage = useLastMessage(convoId); if (!isKickedFromGroup && !isLeft && !isPrivate) { return ( @@ -142,7 +146,12 @@ export const LeaveGroupOrCommunityMenuItem = () => { showLeaveGroupByConvoId(convoId, username); }} > - {isPublic ? window.i18n('leaveCommunity') : window.i18n('leaveGroup')} + {isPublic + ? window.i18n('leaveCommunity') + : lastMessage?.interactionType === ConversationInteractionType.Leave && + lastMessage?.interactionStatus === ConversationInteractionStatus.Error + ? window.i18n('deleteConversation') + : window.i18n('leaveGroup')} ); } diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index e4e65e224..7823ec815 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -292,7 +292,45 @@ export function showLeavePrivateConversationbyConvoId( ); } -export function showLeaveGroupByConvoId(conversationId: string, name: string | undefined) { +async function leaveGroupOrCommunityByConvoId( + conversationId: string, + isPublic: boolean, + forceDeleteLocal: boolean, + onClickClose?: () => void +) { + try { + await updateConversationInteractionState({ + conversationId, + type: ConversationInteractionType.Leave, + status: ConversationInteractionStatus.Start, + }); + + if (onClickClose) { + onClickClose(); + } + + if (isPublic) { + await getConversationController().deleteCommunity(conversationId, { + fromSyncMessage: false, + }); + } else { + await getConversationController().deleteClosedGroup(conversationId, { + fromSyncMessage: false, + sendLeaveMessage: true, + forceDeleteLocal, + }); + } + await clearConversationInteractionState({ conversationId }); + } catch (err) { + window.log.warn(`showLeaveGroupByConvoId error: ${err}`); + await saveConversationInteractionErrorAsMessage({ + conversationId, + interactionType: ConversationInteractionType.Leave, + }); + } +} + +export async function showLeaveGroupByConvoId(conversationId: string, name: string | undefined) { const conversation = getConversationController().get(conversationId); if (!conversation.isGroup()) { @@ -304,6 +342,17 @@ export function showLeaveGroupByConvoId(conversationId: string, name: string | u const admins = conversation.get('groupAdmins') || []; const isAdmin = admins.includes(UserUtils.getOurPubKeyStrFromCache()); const showOnlyGroupAdminWarning = isClosedGroup && isAdmin && admins.length === 1; + const lastMessageInteractionType = conversation.get('lastMessageInteractionType'); + const lastMessageInteractionStatus = conversation.get('lastMessageInteractionStatus'); + + if ( + !isPublic && + lastMessageInteractionType === ConversationInteractionType.Leave && + lastMessageInteractionStatus === ConversationInteractionStatus.Error + ) { + await leaveGroupOrCommunityByConvoId(conversationId, isPublic, true); + return; + } // if this is a community, or we legacy group are not admin, we can just show a confirmation dialog @@ -312,31 +361,7 @@ export function showLeaveGroupByConvoId(conversationId: string, name: string | u }; const onClickOk = async () => { - try { - await updateConversationInteractionState({ - conversationId, - type: ConversationInteractionType.Leave, - status: ConversationInteractionStatus.Start, - }); - onClickClose(); - if (isPublic) { - await getConversationController().deleteCommunity(conversation.id, { - fromSyncMessage: false, - }); - } else { - await getConversationController().deleteClosedGroup(conversation.id, { - fromSyncMessage: false, - sendLeaveMessage: true, - }); - } - await clearConversationInteractionState({ conversationId }); - } catch (err) { - window.log.warn(`showLeaveGroupByConvoId error: ${err}`); - await saveConversationInteractionErrorAsMessage({ - conversationId, - interactionType: ConversationInteractionType.Leave, - }); - } + await leaveGroupOrCommunityByConvoId(conversationId, isPublic, false, onClickClose); }; if (showOnlyGroupAdminWarning) { diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 93e944b99..1d7a12289 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -229,17 +229,22 @@ export class ConversationController { public async deleteClosedGroup( groupId: string, - options: DeleteOptions & { sendLeaveMessage: boolean } + options: DeleteOptions & { sendLeaveMessage: boolean; forceDeleteLocal?: boolean } ) { const conversation = await this.deleteConvoInitialChecks(groupId, 'LegacyGroup'); if (!conversation || !conversation.isClosedGroup()) { return; } - window.log.info(`deleteClosedGroup: ${groupId}, sendLeaveMessage?:${options.sendLeaveMessage}`); + getSwarmPollingInstance().removePubkey(groupId); // we don't need to keep polling anymore. - if (options.sendLeaveMessage) { - await leaveClosedGroup(groupId, options.fromSyncMessage); + if (!options.forceDeleteLocal) { + window.log.info( + `deleteClosedGroup: ${groupId}, sendLeaveMessage?:${options.sendLeaveMessage}` + ); + if (options.sendLeaveMessage) { + await leaveClosedGroup(groupId, options.fromSyncMessage); + } } // if we were kicked or sent our left message, we have nothing to do more with that group. diff --git a/ts/state/selectors/selectedConversation.ts b/ts/state/selectors/selectedConversation.ts index 759ab8cbf..f30791be4 100644 --- a/ts/state/selectors/selectedConversation.ts +++ b/ts/state/selectors/selectedConversation.ts @@ -257,3 +257,7 @@ export function useSelectedNicknameOrProfileNameOrShortenedPubkey() { export function useSelectedWeAreAdmin() { return useSelector((state: StateType) => getSelectedConversation(state)?.weAreAdmin || false); } + +export function useSelectedLastMessage() { + return useSelector((state: StateType) => getSelectedConversation(state)?.lastMessage); +}