fix: clean up delete/leave group&communities button/menuitems

pull/3281/head
Audric Ackermann 10 months ago
parent 3ee69437b0
commit c3cfd37950
No known key found for this signature in database

@ -46,7 +46,6 @@ import { Reactions } from '../../../../util/reactions';
import { SessionContextMenuContainer } from '../../../SessionContextMenuContainer'; import { SessionContextMenuContainer } from '../../../SessionContextMenuContainer';
import { SessionEmojiPanel, StyledEmojiPanel } from '../../SessionEmojiPanel'; import { SessionEmojiPanel, StyledEmojiPanel } from '../../SessionEmojiPanel';
import { MessageReactBar } from './MessageReactBar'; import { MessageReactBar } from './MessageReactBar';
import { showCopyAccountIdAction } from '../../../menu/items/CopyAccountId';
import { CopyAccountIdMenuItem } from '../../../menu/items/CopyAccountId/CopyAccountIdMenuItem'; import { CopyAccountIdMenuItem } from '../../../menu/items/CopyAccountId/CopyAccountIdMenuItem';
import { Localizer } from '../../../basic/Localizer'; import { Localizer } from '../../../basic/Localizer';
import { ItemWithDataTestId } from '../../../menu/items/MenuItemWithDataTestId'; import { ItemWithDataTestId } from '../../../menu/items/MenuItemWithDataTestId';
@ -54,6 +53,7 @@ import { getMenuAnimation } from '../../../menu/MenuAnimation';
import { WithMessageId } from '../../../../session/types/with'; import { WithMessageId } from '../../../../session/types/with';
import { DeleteItem } from '../../../menu/items/DeleteMessage/DeleteMessageMenuItem'; import { DeleteItem } from '../../../menu/items/DeleteMessage/DeleteMessageMenuItem';
import { RetryItem } from '../../../menu/items/RetrySend/RetrySendMenuItem'; import { RetryItem } from '../../../menu/items/RetrySend/RetrySendMenuItem';
import { showCopyAccountIdAction } from '../../../menu/items/CopyAccountId/guard';
export type MessageContextMenuSelectorProps = Pick< export type MessageContextMenuSelectorProps = Pick<
MessageRenderingProps, MessageRenderingProps,

@ -1,7 +1,7 @@
import { compact, flatten, isEqual } from 'lodash'; import { compact, flatten, isEqual } from 'lodash';
import { SessionDataTestId, useEffect, useState } from 'react'; import { SessionDataTestId, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import useInterval from 'react-use/lib/useInterval'; import useInterval from 'react-use/lib/useInterval';
import styled from 'styled-components'; import styled from 'styled-components';
import { Data } from '../../../../data/data'; import { Data } from '../../../../data/data';
@ -10,6 +10,10 @@ import { SessionIconButton } from '../../../icon';
import { import {
useConversationUsername, useConversationUsername,
useDisappearingMessageSettingText, useDisappearingMessageSettingText,
useIsClosedGroup,
useIsKickedFromGroup,
useIsPublic,
useLastMessageIsLeaveError,
} from '../../../../hooks/useParamSelector'; } from '../../../../hooks/useParamSelector';
import { useIsRightPanelShowing } from '../../../../hooks/useUI'; import { useIsRightPanelShowing } from '../../../../hooks/useUI';
import { import {
@ -36,7 +40,6 @@ import {
useSelectedIsGroupV2, useSelectedIsGroupV2,
useSelectedIsKickedFromGroup, useSelectedIsKickedFromGroup,
useSelectedIsPublic, useSelectedIsPublic,
useSelectedLastMessage,
useSelectedSubscriberCount, useSelectedSubscriberCount,
useSelectedWeAreAdmin, useSelectedWeAreAdmin,
} from '../../../../state/selectors/selectedConversation'; } from '../../../../state/selectors/selectedConversation';
@ -49,11 +52,13 @@ import { PanelButtonGroup, PanelIconButton } from '../../../buttons';
import { MediaItemType } from '../../../lightbox/LightboxGallery'; import { MediaItemType } from '../../../lightbox/LightboxGallery';
import { MediaGallery } from '../../media-gallery/MediaGallery'; import { MediaGallery } from '../../media-gallery/MediaGallery';
import { Header, StyledScrollContainer } from './components'; import { Header, StyledScrollContainer } from './components';
import {
ConversationInteractionStatus,
ConversationInteractionType,
} from '../../../../interactions/types';
import { Localizer } from '../../../basic/Localizer'; import { Localizer } from '../../../basic/Localizer';
import {
showDeleteGroupItem,
showLeaveGroupItem,
} from '../../../menu/items/LeaveAndDeleteGroup/guard';
import { getIsMessageRequestOverlayShown } from '../../../../state/selectors/section';
import { showLeaveCommunityItem } from '../../../menu/items/LeaveCommunity/guard';
async function getMediaGalleryProps(conversationId: string): Promise<{ async function getMediaGalleryProps(conversationId: string): Promise<{
documents: Array<MediaItemType>; documents: Array<MediaItemType>;
@ -199,12 +204,94 @@ const StyledName = styled.h4`
font-size: var(--font-size-md); font-size: var(--font-size-md);
`; `;
const LeaveCommunityPanelButton = () => {
const selectedConvoKey = useSelectedConversationKey();
const selectedUsername = useConversationUsername(selectedConvoKey) || selectedConvoKey;
const isPublic = useIsPublic(selectedConvoKey);
const showItem = showLeaveCommunityItem({ isPublic });
if (!selectedConvoKey || !showItem) {
return null;
}
return (
<PanelIconButton
text={window.i18n('communityLeave')}
dataTestId="leave-group-button"
onClick={() => void showLeaveGroupByConvoId(selectedConvoKey, selectedUsername)}
color={'var(--danger-color)'}
iconType={'delete'}
/>
);
};
const DeleteGroupPanelButton = () => {
const convoId = useSelectedConversationKey();
const isGroup = useIsClosedGroup(convoId);
const isMessageRequestShown = useSelector(getIsMessageRequestOverlayShown);
const isKickedFromGroup = useIsKickedFromGroup(convoId) || false;
const lastMessageIsLeaveError = useLastMessageIsLeaveError(convoId);
const selectedUsername = useConversationUsername(convoId) || convoId;
const showItem = showDeleteGroupItem({
isGroup,
isKickedFromGroup,
isMessageRequestShown,
lastMessageIsLeaveError,
});
if (!showItem || !convoId) {
return null;
}
const token = PubKey.is03Pubkey(convoId) ? 'groupDelete' : 'conversationsDelete';
return (
<PanelIconButton
text={window.i18n(token)}
dataTestId="leave-group-button"
onClick={() => void showLeaveGroupByConvoId(convoId, selectedUsername)}
color={'var(--danger-color)'}
iconType={'delete'}
/>
);
};
const LeaveGroupPanelButton = () => {
const selectedConvoKey = useSelectedConversationKey();
const isGroup = useIsClosedGroup(selectedConvoKey);
const username = useConversationUsername(selectedConvoKey) || selectedConvoKey;
const isMessageRequestShown = useSelector(getIsMessageRequestOverlayShown);
const isKickedFromGroup = useIsKickedFromGroup(selectedConvoKey) || false;
const lastMessageIsLeaveError = useLastMessageIsLeaveError(selectedConvoKey);
const showItem = showLeaveGroupItem({
isGroup,
isKickedFromGroup,
isMessageRequestShown,
lastMessageIsLeaveError,
});
if (!selectedConvoKey || !showItem) {
return null;
}
return (
<PanelIconButton
text={window.i18n('groupLeave')}
dataTestId="leave-group-button"
onClick={() => void showLeaveGroupByConvoId(selectedConvoKey, username)}
color={'var(--danger-color)'}
iconType={'delete'}
/>
);
};
export const OverlayRightPanelSettings = () => { export const OverlayRightPanelSettings = () => {
const [documents, setDocuments] = useState<Array<MediaItemType>>([]); const [documents, setDocuments] = useState<Array<MediaItemType>>([]);
const [media, setMedia] = useState<Array<MediaItemType>>([]); const [media, setMedia] = useState<Array<MediaItemType>>([]);
const selectedConvoKey = useSelectedConversationKey(); const selectedConvoKey = useSelectedConversationKey();
const selectedUsername = useConversationUsername(selectedConvoKey) || selectedConvoKey;
const isShowing = useIsRightPanelShowing(); const isShowing = useIsRightPanelShowing();
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -219,7 +306,6 @@ export const OverlayRightPanelSettings = () => {
const disappearingMessagesSubtitle = useDisappearingMessageSettingText({ const disappearingMessagesSubtitle = useDisappearingMessageSettingText({
convoId: selectedConvoKey, convoId: selectedConvoKey,
}); });
const lastMessage = useSelectedLastMessage();
useEffect(() => { useEffect(() => {
let isCancelled = false; let isCancelled = false;
@ -269,23 +355,11 @@ export const OverlayRightPanelSettings = () => {
const commonNoShow = isKickedFromGroup || isBlocked || !isActive; const commonNoShow = isKickedFromGroup || isBlocked || !isActive;
const hasDisappearingMessages = !isPublic && !commonNoShow; const hasDisappearingMessages = !isPublic && !commonNoShow;
const leaveGroupString = isPublic
? window.i18n('communityLeave')
: lastMessage?.interactionType === ConversationInteractionType.Leave &&
lastMessage?.interactionStatus === ConversationInteractionStatus.Error
? window.i18n('conversationsDelete')
: isKickedFromGroup
? window.i18n('groupDelete')
: window.i18n('groupLeave');
const showUpdateGroupNameButton = isGroup && weAreAdmin && !commonNoShow; // legacy groups non-admin cannot change groupname anymore const showUpdateGroupNameButton = isGroup && weAreAdmin && !commonNoShow; // legacy groups non-admin cannot change groupname anymore
const showAddRemoveModeratorsButton = weAreAdmin && !commonNoShow && isPublic; const showAddRemoveModeratorsButton = weAreAdmin && !commonNoShow && isPublic;
const showUpdateGroupMembersButton = !isPublic && isGroup && !commonNoShow; const showUpdateGroupMembersButton = !isPublic && isGroup && !commonNoShow;
const deleteConvoAction = () => {
void showLeaveGroupByConvoId(selectedConvoKey, selectedUsername);
};
return ( return (
<StyledScrollContainer> <StyledScrollContainer>
<Flex container={true} flexDirection={'column'} alignItems={'center'}> <Flex container={true} flexDirection={'column'} alignItems={'center'}>
@ -399,14 +473,11 @@ export const OverlayRightPanelSettings = () => {
<MediaGallery documents={documents} media={media} /> <MediaGallery documents={documents} media={media} />
{isGroup && ( {isGroup && (
<PanelIconButton <>
text={leaveGroupString} <LeaveGroupPanelButton />
dataTestId="leave-group-button" <DeleteGroupPanelButton />
disabled={isKickedFromGroup} <LeaveCommunityPanelButton />
onClick={() => void deleteConvoAction()} </>
color={'var(--danger-color)'}
iconType={'delete'}
/>
)} )}
</PanelButtonGroup> </PanelButtonGroup>
<SpacerLG /> <SpacerLG />

@ -51,7 +51,7 @@ import { Message } from '../../../message/message-item/Message';
import { AttachmentInfo, MessageInfo } from './components'; import { AttachmentInfo, MessageInfo } from './components';
import { AttachmentCarousel } from './components/AttachmentCarousel'; import { AttachmentCarousel } from './components/AttachmentCarousel';
import { ToastUtils } from '../../../../../session/utils'; import { ToastUtils } from '../../../../../session/utils';
import { showCopyAccountIdAction } from '../../../../menu/items/CopyAccountId'; import { showCopyAccountIdAction } from '../../../../menu/items/CopyAccountId/guard';
// NOTE we override the default max-widths when in the detail isDetailView // NOTE we override the default max-widths when in the detail isDetailView
const StyledMessageBody = styled.div` const StyledMessageBody = styled.div`

@ -21,7 +21,6 @@ import {
DeleteMessagesMenuItem, DeleteMessagesMenuItem,
DeletePrivateConversationMenuItem, DeletePrivateConversationMenuItem,
InviteContactMenuItem, InviteContactMenuItem,
LeaveGroupOrCommunityMenuItem,
MarkAllReadMenuItem, MarkAllReadMenuItem,
MarkConversationUnreadMenuItem, MarkConversationUnreadMenuItem,
NotificationForConvoMenuItem, NotificationForConvoMenuItem,
@ -32,6 +31,9 @@ import { CopyCommunityUrlMenuItem } from './items/CopyCommunityUrl/CopyCommunity
import { CopyAccountIdMenuItem } from './items/CopyAccountId/CopyAccountIdMenuItem'; import { CopyAccountIdMenuItem } from './items/CopyAccountId/CopyAccountIdMenuItem';
import { ItemWithDataTestId } from './items/MenuItemWithDataTestId'; import { ItemWithDataTestId } from './items/MenuItemWithDataTestId';
import { getMenuAnimation } from './MenuAnimation'; import { getMenuAnimation } from './MenuAnimation';
import { LeaveCommunityMenuItem } from './items/LeaveCommunity/LeaveCommunityMenuItem';
import { LeaveGroupMenuItem } from './items/LeaveAndDeleteGroup/LeaveGroupMenuItem';
import { DeleteGroupMenuItem } from './items/LeaveAndDeleteGroup/DeleteGroupMenuItem';
export type PropsContextConversationItem = { export type PropsContextConversationItem = {
triggerId: string; triggerId: string;
@ -57,7 +59,6 @@ const ConversationListItemContextMenu = (props: PropsContextConversationItem) =>
{/* Generic actions */} {/* Generic actions */}
<PinConversationMenuItem /> <PinConversationMenuItem />
<NotificationForConvoMenuItem /> <NotificationForConvoMenuItem />
<BlockMenuItem /> <BlockMenuItem />
<CopyCommunityUrlMenuItem convoId={convoIdFromContext} /> <CopyCommunityUrlMenuItem convoId={convoIdFromContext} />
<CopyAccountIdMenuItem pubkey={convoIdFromContext} /> <CopyAccountIdMenuItem pubkey={convoIdFromContext} />
@ -73,7 +74,9 @@ const ConversationListItemContextMenu = (props: PropsContextConversationItem) =>
<InviteContactMenuItem /> <InviteContactMenuItem />
<DeleteMessagesMenuItem /> <DeleteMessagesMenuItem />
<DeletePrivateConversationMenuItem /> <DeletePrivateConversationMenuItem />
<LeaveGroupOrCommunityMenuItem /> <LeaveCommunityMenuItem />
<LeaveGroupMenuItem />
<DeleteGroupMenuItem />
<ShowUserDetailsMenuItem /> <ShowUserDetailsMenuItem />
</Menu> </Menu>
</SessionContextMenuContainer> </SessionContextMenuContainer>

@ -15,7 +15,6 @@ import {
useIsPrivate, useIsPrivate,
useIsPrivateAndFriend, useIsPrivateAndFriend,
useIsPublic, useIsPublic,
useLastMessage,
useNicknameOrProfileNameOrShortenedPubkey, useNicknameOrProfileNameOrShortenedPubkey,
useNotificationSetting, useNotificationSetting,
useWeAreAdmin, useWeAreAdmin,
@ -31,8 +30,7 @@ import {
showAddModeratorsByConvoId, showAddModeratorsByConvoId,
showBanUserByConvoId, showBanUserByConvoId,
showInviteContactByConvoId, showInviteContactByConvoId,
showLeaveGroupByConvoId, showDeletePrivateConversationByConvoId,
showLeavePrivateConversationByConvoId,
showRemoveModeratorsByConvoId, showRemoveModeratorsByConvoId,
showUnbanUserByConvoId, showUnbanUserByConvoId,
showUpdateGroupNameByConvoId, showUpdateGroupNameByConvoId,
@ -58,10 +56,6 @@ import { useSelectedConversationKey } from '../../state/selectors/selectedConver
import type { LocalizerToken } from '../../types/localizer'; import type { LocalizerToken } from '../../types/localizer';
import { SessionButtonColor } from '../basic/SessionButton'; import { SessionButtonColor } from '../basic/SessionButton';
import { ItemWithDataTestId } from './items/MenuItemWithDataTestId'; import { ItemWithDataTestId } from './items/MenuItemWithDataTestId';
import {
ConversationInteractionStatus,
ConversationInteractionType,
} from '../../interactions/types';
import { useLibGroupDestroyed } from '../../state/selectors/userGroups'; import { useLibGroupDestroyed } from '../../state/selectors/userGroups';
import { NetworkTime } from '../../util/NetworkTime'; import { NetworkTime } from '../../util/NetworkTime';
@ -155,34 +149,6 @@ export const DeletePrivateContactMenuItem = () => {
return null; return null;
}; };
export const LeaveGroupOrCommunityMenuItem = () => {
const convoId = useConvoIdFromContext();
const username = useConversationUsername(convoId) || convoId;
const isPrivate = useIsPrivate(convoId);
const isPublic = useIsPublic(convoId);
const lastMessage = useLastMessage(convoId);
const isMessageRequestShown = useSelector(getIsMessageRequestOverlayShown);
if (!isPrivate && !isMessageRequestShown) {
return (
<ItemWithDataTestId
onClick={() => {
void showLeaveGroupByConvoId(convoId, username);
}}
>
{isPublic
? window.i18n('communityLeave')
: lastMessage?.interactionType === ConversationInteractionType.Leave &&
lastMessage?.interactionStatus === ConversationInteractionStatus.Error
? window.i18n('conversationsDelete')
: window.i18n('groupLeave')}
</ItemWithDataTestId>
);
}
return null;
};
export const ShowUserDetailsMenuItem = () => { export const ShowUserDetailsMenuItem = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const convoId = useConvoIdFromContext(); const convoId = useConvoIdFromContext();
@ -427,7 +393,7 @@ export const DeletePrivateConversationMenuItem = () => {
return ( return (
<ItemWithDataTestId <ItemWithDataTestId
onClick={() => { onClick={() => {
showLeavePrivateConversationByConvoId(convoId); showDeletePrivateConversationByConvoId(convoId);
}} }}
> >
{isMe ? window.i18n('noteToSelfHide') : window.i18n('conversationsDelete')} {isMe ? window.i18n('noteToSelfHide') : window.i18n('conversationsDelete')}

@ -1,8 +1,8 @@
import { useIsPrivate } from '../../../../hooks/useParamSelector'; import { useIsPrivate } from '../../../../hooks/useParamSelector';
import { copyPublicKeyByConvoId } from '../../../../interactions/conversationInteractions'; import { copyPublicKeyByConvoId } from '../../../../interactions/conversationInteractions';
import { Localizer } from '../../../basic/Localizer'; import { Localizer } from '../../../basic/Localizer';
import { showCopyAccountIdAction } from '.';
import { ItemWithDataTestId } from '../MenuItemWithDataTestId'; import { ItemWithDataTestId } from '../MenuItemWithDataTestId';
import { showCopyAccountIdAction } from './guard';
/** /**
* Can be used to copy the conversation AccountID or the message's author sender'id. * Can be used to copy the conversation AccountID or the message's author sender'id.
@ -11,8 +11,6 @@ import { ItemWithDataTestId } from '../MenuItemWithDataTestId';
export const CopyAccountIdMenuItem = ({ pubkey }: { pubkey: string }): JSX.Element | null => { export const CopyAccountIdMenuItem = ({ pubkey }: { pubkey: string }): JSX.Element | null => {
const isPrivate = useIsPrivate(pubkey); const isPrivate = useIsPrivate(pubkey);
// we want to show the copyId for communities only
if (showCopyAccountIdAction({ isPrivate, pubkey })) { if (showCopyAccountIdAction({ isPrivate, pubkey })) {
return ( return (
<ItemWithDataTestId <ItemWithDataTestId

@ -1,5 +1,8 @@
import { PubKey } from '../../../../session/types'; import { PubKey } from '../../../../session/types';
/**
* We want to show the copyId for private and not blinded chats only
*/
export function showCopyAccountIdAction({ export function showCopyAccountIdAction({
isPrivate, isPrivate,
pubkey, pubkey,

@ -1,8 +1,8 @@
import { showCopyCommunityUrlMenuItem } from '.';
import { useIsPublic } from '../../../../hooks/useParamSelector'; import { useIsPublic } from '../../../../hooks/useParamSelector';
import { copyPublicKeyByConvoId } from '../../../../interactions/conversationInteractions'; import { copyPublicKeyByConvoId } from '../../../../interactions/conversationInteractions';
import { Localizer } from '../../../basic/Localizer'; import { Localizer } from '../../../basic/Localizer';
import { ItemWithDataTestId } from '../MenuItemWithDataTestId'; import { ItemWithDataTestId } from '../MenuItemWithDataTestId';
import { showCopyCommunityUrlMenuItem } from './guard';
export const CopyCommunityUrlMenuItem = ({ convoId }: { convoId: string }): JSX.Element | null => { export const CopyCommunityUrlMenuItem = ({ convoId }: { convoId: string }): JSX.Element | null => {
const isPublic = useIsPublic(convoId); const isPublic = useIsPublic(convoId);

@ -0,0 +1,46 @@
import { useSelector } from 'react-redux';
import { useConvoIdFromContext } from '../../../../contexts/ConvoIdContext';
import {
useConversationUsername,
useIsKickedFromGroup,
useIsClosedGroup,
useLastMessageIsLeaveError,
} from '../../../../hooks/useParamSelector';
import { showLeaveGroupByConvoId } from '../../../../interactions/conversationInteractions';
import { PubKey } from '../../../../session/types';
import { getIsMessageRequestOverlayShown } from '../../../../state/selectors/section';
import { ItemWithDataTestId } from '../MenuItemWithDataTestId';
import { showDeleteGroupItem } from './guard';
import { Localizer } from '../../../basic/Localizer';
export const DeleteGroupMenuItem = () => {
const convoId = useConvoIdFromContext();
const username = useConversationUsername(convoId) || convoId;
const isGroup = useIsClosedGroup(convoId);
const isMessageRequestShown = useSelector(getIsMessageRequestOverlayShown);
const isKickedFromGroup = useIsKickedFromGroup(convoId) || false;
const lastMessageIsLeaveError = useLastMessageIsLeaveError(convoId);
const showLeave = showDeleteGroupItem({
isGroup,
isKickedFromGroup,
isMessageRequestShown,
lastMessageIsLeaveError,
});
if (!showLeave) {
return null;
}
const token = PubKey.is03Pubkey(convoId) ? 'groupDelete' : 'conversationsDelete';
return (
<ItemWithDataTestId
onClick={() => {
void showLeaveGroupByConvoId(convoId, username);
}}
>
<Localizer token={token} />
</ItemWithDataTestId>
);
};

@ -0,0 +1,43 @@
import { useSelector } from 'react-redux';
import { useConvoIdFromContext } from '../../../../contexts/ConvoIdContext';
import {
useConversationUsername,
useIsKickedFromGroup,
useIsClosedGroup,
useLastMessageIsLeaveError,
} from '../../../../hooks/useParamSelector';
import { showLeaveGroupByConvoId } from '../../../../interactions/conversationInteractions';
import { getIsMessageRequestOverlayShown } from '../../../../state/selectors/section';
import { ItemWithDataTestId } from '../MenuItemWithDataTestId';
import { showLeaveGroupItem } from './guard';
import { Localizer } from '../../../basic/Localizer';
export const LeaveGroupMenuItem = () => {
const convoId = useConvoIdFromContext();
const isGroup = useIsClosedGroup(convoId);
const username = useConversationUsername(convoId) || convoId;
const isMessageRequestShown = useSelector(getIsMessageRequestOverlayShown);
const isKickedFromGroup = useIsKickedFromGroup(convoId) || false;
const lastMessageIsLeaveError = useLastMessageIsLeaveError(convoId);
const showLeave = showLeaveGroupItem({
isGroup,
isMessageRequestShown,
isKickedFromGroup,
lastMessageIsLeaveError,
});
if (!showLeave) {
return null;
}
return (
<ItemWithDataTestId
onClick={() => {
void showLeaveGroupByConvoId(convoId, username);
}}
>
<Localizer token="groupLeave" />
</ItemWithDataTestId>
);
};

@ -0,0 +1,42 @@
function sharedEnabled({
isGroup,
isMessageRequestShown,
}: Pick<Parameters<typeof showLeaveGroupItem>[0], 'isGroup' | 'isMessageRequestShown'>) {
return isGroup && !isMessageRequestShown;
}
export function showLeaveGroupItem({
isGroup,
isKickedFromGroup,
isMessageRequestShown,
lastMessageIsLeaveError,
}: {
isGroup: boolean;
isMessageRequestShown: boolean;
lastMessageIsLeaveError: boolean;
isKickedFromGroup: boolean;
}) {
// we can't try to leave the group if we were kicked from it, or if we've already tried to (lastMessageIsLeaveError is true)
return (
sharedEnabled({ isGroup, isMessageRequestShown }) &&
!isKickedFromGroup &&
!lastMessageIsLeaveError
);
}
export function showDeleteGroupItem({
isGroup,
isKickedFromGroup,
isMessageRequestShown,
lastMessageIsLeaveError,
}: {
isGroup: boolean;
isMessageRequestShown: boolean;
lastMessageIsLeaveError: boolean;
isKickedFromGroup: boolean;
}) {
return (
sharedEnabled({ isGroup, isMessageRequestShown }) &&
(isKickedFromGroup || lastMessageIsLeaveError)
);
}

@ -0,0 +1,26 @@
import { useConvoIdFromContext } from '../../../../contexts/ConvoIdContext';
import { useConversationUsername, useIsPublic } from '../../../../hooks/useParamSelector';
import { showLeaveGroupByConvoId } from '../../../../interactions/conversationInteractions';
import { Localizer } from '../../../basic/Localizer';
import { ItemWithDataTestId } from '../MenuItemWithDataTestId';
import { showLeaveCommunityItem } from './guard';
export const LeaveCommunityMenuItem = () => {
const convoId = useConvoIdFromContext();
const username = useConversationUsername(convoId) || convoId;
const isPublic = useIsPublic(convoId);
if (!showLeaveCommunityItem({ isPublic })) {
return null;
}
return (
<ItemWithDataTestId
onClick={() => {
void showLeaveGroupByConvoId(convoId, username);
}}
>
<Localizer token="communityLeave" />
</ItemWithDataTestId>
);
};

@ -0,0 +1,3 @@
export const showLeaveCommunityItem = ({ isPublic }: { isPublic: boolean }) => {
return isPublic;
};

@ -26,6 +26,7 @@ import {
useLibGroupInvitePending, useLibGroupInvitePending,
useLibGroupKicked, useLibGroupKicked,
} from '../state/selectors/userGroups'; } from '../state/selectors/userGroups';
import { ConversationInteractionStatus, ConversationInteractionType } from '../interactions/types';
export function useAvatarPath(convoId: string | undefined) { export function useAvatarPath(convoId: string | undefined) {
const convoProps = useConversationPropsById(convoId); const convoProps = useConversationPropsById(convoId);
@ -536,3 +537,12 @@ export function useLastMessage(convoId?: string) {
return convoProps.lastMessage; return convoProps.lastMessage;
} }
export function useLastMessageIsLeaveError(convoId?: string) {
const lastMessage = useLastMessage(convoId);
return (
lastMessage?.interactionType === ConversationInteractionType.Leave &&
lastMessage?.interactionStatus === ConversationInteractionStatus.Error
);
}

@ -340,7 +340,7 @@ export async function showUpdateGroupMembersByConvoId(conversationId: string) {
window.inboxStore?.dispatch(updateGroupMembersModal({ conversationId })); window.inboxStore?.dispatch(updateGroupMembersModal({ conversationId }));
} }
export function showLeavePrivateConversationByConvoId(conversationId: string) { export function showDeletePrivateConversationByConvoId(conversationId: string) {
const conversation = ConvoHub.use().get(conversationId); const conversation = ConvoHub.use().get(conversationId);
const isMe = conversation.isMe(); const isMe = conversation.isMe();
@ -367,7 +367,7 @@ export function showLeavePrivateConversationByConvoId(conversationId: string) {
}); });
await clearConversationInteractionState({ conversationId }); await clearConversationInteractionState({ conversationId });
} catch (err) { } catch (err) {
window.log.warn(`showLeavePrivateConversationByConvoId error: ${err}`); window.log.warn(`showDeletePrivateConversationByConvoId error: ${err}`);
await saveConversationInteractionErrorAsMessage({ await saveConversationInteractionErrorAsMessage({
conversationId, conversationId,
interactionType: isMe interactionType: isMe
@ -452,6 +452,21 @@ async function leaveGroupOrCommunityByConvoId({
} }
} }
/**
* Returns true if we the convo is a 03 group and if we can try to send a leave message.
*/
async function hasLeavingDetails(convoId: string) {
if (!PubKey.is03Pubkey(convoId)) {
return true;
}
const group = await UserGroupsWrapperActions.getGroup(convoId);
// we need the authData or the secretKey to be able to attempt to leave,
// otherwise we won't be able to even try
return group && (!isEmpty(group.authData) || !isEmpty(group.secretKey));
}
export async function showLeaveGroupByConvoId(conversationId: string, name: string | undefined) { export async function showLeaveGroupByConvoId(conversationId: string, name: string | undefined) {
const conversation = ConvoHub.use().get(conversationId); const conversation = ConvoHub.use().get(conversationId);
@ -465,15 +480,19 @@ export async function showLeaveGroupByConvoId(conversationId: string, name: stri
const isAdmin = admins.includes(UserUtils.getOurPubKeyStrFromCache()); const isAdmin = admins.includes(UserUtils.getOurPubKeyStrFromCache());
const showOnlyGroupAdminWarning = isClosedGroup && isAdmin; const showOnlyGroupAdminWarning = isClosedGroup && isAdmin;
const weAreLastAdmin = const weAreLastAdmin =
(PubKey.is05Pubkey(conversationId) && isAdmin && admins.length === 1) || (PubKey.is05Pubkey(conversationId) || PubKey.is03Pubkey(conversationId)) &&
(PubKey.is03Pubkey(conversationId) && isAdmin && admins.length === 1); isAdmin &&
admins.length === 1;
const lastMessageInteractionType = conversation.get('lastMessageInteractionType'); const lastMessageInteractionType = conversation.get('lastMessageInteractionType');
const lastMessageInteractionStatus = conversation.get('lastMessageInteractionStatus'); const lastMessageInteractionStatus = conversation.get('lastMessageInteractionStatus');
const canTryToLeave = await hasLeavingDetails(conversationId);
if ( if (
!isPublic && !isPublic &&
lastMessageInteractionType === ConversationInteractionType.Leave && ((lastMessageInteractionType === ConversationInteractionType.Leave &&
lastMessageInteractionStatus === ConversationInteractionStatus.Error lastMessageInteractionStatus === ConversationInteractionStatus.Error) ||
!canTryToLeave) // if we don't have any key to send our leave message, no need to try
) { ) {
await leaveGroupOrCommunityByConvoId({ conversationId, isPublic, sendLeaveMessage: false }); await leaveGroupOrCommunityByConvoId({ conversationId, isPublic, sendLeaveMessage: false });
return; return;

Loading…
Cancel
Save