diff --git a/ts/components/conversation/MessageRequestButtons.tsx b/ts/components/conversation/MessageRequestButtons.tsx index 9e684985f..6b91f5728 100644 --- a/ts/components/conversation/MessageRequestButtons.tsx +++ b/ts/components/conversation/MessageRequestButtons.tsx @@ -2,14 +2,9 @@ import React from 'react'; import styled from 'styled-components'; import { useIsIncomingRequest } from '../../hooks/useParamSelector'; import { - approveConvoAndSendResponse, declineConversationWithConfirm, + handleAcceptConversationRequest, } from '../../interactions/conversationInteractions'; -import { GroupV2Receiver } from '../../receiver/groupv2/handleGroupV2Message'; -import { getSwarmPollingInstance } from '../../session/apis/snode_api/swarmPolling'; -import { ConvoHub } from '../../session/conversations'; -import { PubKey } from '../../session/types'; -import { sleepFor } from '../../session/utils/Promise'; import { useSelectedConversationIdOrigin, useSelectedConversationKey, @@ -17,7 +12,6 @@ import { useSelectedIsPrivateFriend, } from '../../state/selectors/selectedConversation'; import { useLibGroupInvitePending } from '../../state/selectors/userGroups'; -import { UserGroupsWrapperActions } from '../../webworker/workers/browser/libsession_worker_interface'; import { SessionButton, SessionButtonColor } from '../basic/SessionButton'; import { InvitedToGroupControlMessage, MessageRequestExplanation } from './SubtleNotification'; @@ -77,34 +71,6 @@ const handleDeclineAndBlockConversationRequest = ( }); }; -const handleAcceptConversationRequest = async (convoId: string) => { - const convo = ConvoHub.use().get(convoId); - if (!convo) { - return; - } - await convo.setDidApproveMe(true, false); - await convo.setIsApproved(true, false); - await convo.commit(); - if (convo.isPrivate()) { - await convo.addOutgoingApprovalMessage(Date.now()); - await approveConvoAndSendResponse(convoId); - } else if (PubKey.is03Pubkey(convoId)) { - const found = await UserGroupsWrapperActions.getGroup(convoId); - if (!found) { - window.log.warn('cannot approve a non existing group in usergroup'); - return; - } - // this updates the wrapper and refresh the redux slice - await UserGroupsWrapperActions.setGroup({ ...found, invitePending: false }); - getSwarmPollingInstance().addGroupId(convoId, async () => { - // we need to do a first poll to fetch the keys etc before we can send our invite response - // this is pretty hacky, but also an admin seeing a message from that user in the group will mark it as not pending anymore - await sleepFor(2000); - await GroupV2Receiver.sendInviteResponseToGroup({ groupPk: convoId }); - }); - } -}; - export const ConversationMessageRequestButtons = () => { const selectedConvoId = useSelectedConversationKey(); const isIncomingRequest = useIsIncomingRequest(selectedConvoId); @@ -131,7 +97,7 @@ export const ConversationMessageRequestButtons = () => { { - await handleAcceptConversationRequest(selectedConvoId); + await handleAcceptConversationRequest({ convoId: selectedConvoId, sendResponse: true }); }} text={window.i18n('accept')} dataTestId="accept-message-request" diff --git a/ts/components/menu/ConversationListItemContextMenu.tsx b/ts/components/menu/ConversationListItemContextMenu.tsx index 349562e57..a5d2007de 100644 --- a/ts/components/menu/ConversationListItemContextMenu.tsx +++ b/ts/components/menu/ConversationListItemContextMenu.tsx @@ -4,9 +4,10 @@ import { Item, Menu } from 'react-contexify'; import { useSelector } from 'react-redux'; import { useIsPinned, useIsPrivate, useIsPrivateAndFriend } from '../../hooks/useParamSelector'; import { ConvoHub } from '../../session/conversations'; +import { isSearching } from '../../state/selectors/search'; import { getIsMessageSection } from '../../state/selectors/section'; -import { useConvoIdFromContext } from '../leftpane/conversation-list-item/ConvoIdContext'; import { SessionContextMenuContainer } from '../SessionContextMenuContainer'; +import { useConvoIdFromContext } from '../leftpane/conversation-list-item/ConvoIdContext'; import { AcceptMsgRequestMenuItem, BanMenuItem, @@ -17,16 +18,15 @@ import { DeclineAndBlockMsgRequestMenuItem, DeclineMsgRequestMenuItem, DeleteMessagesMenuItem, + DeletePrivateConversationMenuItem, InviteContactMenuItem, LeaveGroupOrCommunityMenuItem, MarkAllReadMenuItem, MarkConversationUnreadMenuItem, + NotificationForConvoMenuItem, ShowUserDetailsMenuItem, UnbanMenuItem, - DeletePrivateConversationMenuItem, - NotificationForConvoMenuItem, } from './Menu'; -import { isSearching } from '../../state/selectors/search'; export type PropsContextConversationItem = { triggerId: string; diff --git a/ts/components/menu/Menu.tsx b/ts/components/menu/Menu.tsx index 43d885ced..cc935600f 100644 --- a/ts/components/menu/Menu.tsx +++ b/ts/components/menu/Menu.tsx @@ -23,12 +23,12 @@ import { import { ConversationInteractionStatus, ConversationInteractionType, - approveConvoAndSendResponse, blockConvoById, clearNickNameByConvoId, copyPublicKeyByConvoId, declineConversationWithConfirm, deleteAllMessagesByConvoIdWithConfirmation, + handleAcceptConversationRequest, markAllReadByConvoId, setNotificationForConvoId, showAddModeratorsByConvoId, @@ -441,17 +441,17 @@ export const DeletePrivateConversationMenuItem = () => { export const AcceptMsgRequestMenuItem = () => { const convoId = useConvoIdFromContext(); const isRequest = useIsIncomingRequest(convoId); - const convo = ConvoHub.use().get(convoId); const isPrivate = useIsPrivate(convoId); - if (isRequest && isPrivate) { + if (isRequest && (isPrivate || PubKey.is03Pubkey(convoId))) { return ( { - await convo.setDidApproveMe(true); - await convo.addOutgoingApprovalMessage(Date.now()); - await approveConvoAndSendResponse(convoId); + await handleAcceptConversationRequest({ + convoId, + sendResponse: true, + }); }} > {window.i18n('accept')} diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index 59cfa1c20..e80a7124f 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -10,15 +10,19 @@ import { SessionButtonColor } from '../components/basic/SessionButton'; import { getCallMediaPermissionsSettings } from '../components/settings/SessionSettings'; import { Data } from '../data/data'; import { SettingsKey } from '../data/settings-key'; +import { GroupV2Receiver } from '../receiver/groupv2/handleGroupV2Message'; import { uploadFileToFsWithOnionV4 } from '../session/apis/file_server_api/FileServerApi'; import { OpenGroupUtils } from '../session/apis/open_group_api/utils'; +import { getSwarmPollingInstance } from '../session/apis/snode_api'; import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime'; import { ConvoHub } from '../session/conversations'; import { getSodiumRenderer } from '../session/crypto'; import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsManager'; import { DisappearingMessageConversationModeType } from '../session/disappearing_messages/types'; +import { ed25519Str } from '../session/onions/onionPath'; import { PubKey } from '../session/types'; import { perfEnd, perfStart } from '../session/utils/Performance'; +import { sleepFor } from '../session/utils/Promise'; import { fromHexToArray, toHex } from '../session/utils/String'; import { UserSync } from '../session/utils/job_runners/jobs/UserSyncJob'; import { SessionUtilContact } from '../session/utils/libsession/libsession_utils_contacts'; @@ -109,21 +113,53 @@ export async function unblockConvoById(conversationId: string) { await conversation.commit(); } -/** - * marks the conversation's approval fields, sends messageRequestResponse - */ -export const approveConvoAndSendResponse = async (conversationId: string) => { - const convoToApprove = ConvoHub.use().get(conversationId); - - if (!convoToApprove) { - window?.log?.info('Conversation is already approved.'); - return; +export const handleAcceptConversationRequest = async ({ + convoId, + sendResponse, +}: { + convoId: string; + sendResponse: boolean; +}) => { + const convo = ConvoHub.use().get(convoId); + if (!convo) { + return null; } + await convo.setDidApproveMe(true, false); + await convo.setIsApproved(true, false); + await convo.commit(); - await convoToApprove.setIsApproved(true, false); - - await convoToApprove.commit(); - await convoToApprove.sendMessageRequestResponse(); + if (convo.isPrivate()) { + await convo.addOutgoingApprovalMessage(Date.now()); + if (sendResponse) { + await convo.sendMessageRequestResponse(); + } + return null; + } + if (PubKey.is03Pubkey(convoId)) { + const found = await UserGroupsWrapperActions.getGroup(convoId); + if (!found) { + window.log.warn('cannot approve a non existing group in usergroup'); + return null; + } + // this updates the wrapper and refresh the redux slice + await UserGroupsWrapperActions.setGroup({ ...found, invitePending: false }); + const acceptedPromise = new Promise(resolve => { + getSwarmPollingInstance().addGroupId(convoId, async () => { + // we need to do a first poll to fetch the keys etc before we can send our invite response + // this is pretty hacky, but also an admin seeing a message from that user in the group will mark it as not pending anymore + await sleepFor(2000); + if (sendResponse) { + await GroupV2Receiver.sendInviteResponseToGroup({ groupPk: convoId }); + } + window.log.info( + `handleAcceptConversationRequest: first poll for group ${ed25519Str(convoId)} happened, we should have encryption keys now` + ); + return resolve(true); + }); + }); + await acceptedPromise; + } + return null; }; export async function declineConversationWithoutConfirm({ diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 6acb8f743..4db8bf55e 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -127,6 +127,7 @@ import { getSubscriberCountOutsideRedux, } from '../state/selectors/sogsRoomInfo'; // decide it it makes sense to move this to a redux slice? +import { handleAcceptConversationRequest } from '../interactions/conversationInteractions'; import { DisappearingMessages } from '../session/disappearing_messages'; import { DisappearingMessageConversationModeType } from '../session/disappearing_messages/types'; import { GroupUpdateInfoChangeMessage } from '../session/messages/outgoing/controlMessage/group_v2/to_group/GroupUpdateInfoChangeMessage'; @@ -2034,7 +2035,8 @@ export class ConversationModel extends Backbone.Model { lokiProfile: UserUtils.getOurProfile(), }; - const shouldApprove = !this.isApproved() && this.isPrivate(); + const shouldApprove = !this.isApproved() && (this.isPrivate() || this.isClosedGroupV2()); + const incomingMessageCount = await Data.getMessageCountByType( this.id, MessageDirection.incoming @@ -2048,6 +2050,10 @@ export class ConversationModel extends Backbone.Model { } if (shouldApprove) { + await handleAcceptConversationRequest({ + convoId: this.id, + sendResponse: !message, + }); await this.setIsApproved(true); if (hasIncomingMessages) { // have to manually add approval for local client here as DB conditional approval check in config msg handling will prevent this from running diff --git a/ts/session/utils/calling/CallManager.ts b/ts/session/utils/calling/CallManager.ts index b9ca6b369..bccf2b122 100644 --- a/ts/session/utils/calling/CallManager.ts +++ b/ts/session/utils/calling/CallManager.ts @@ -24,7 +24,7 @@ import { PubKey } from '../../types'; import { getMessageQueue } from '../..'; import { getCallMediaPermissionsSettings } from '../../../components/settings/SessionSettings'; import { Data } from '../../../data/data'; -import { approveConvoAndSendResponse } from '../../../interactions/conversationInteractions'; +import { handleAcceptConversationRequest } from '../../../interactions/conversationInteractions'; import { READ_MESSAGE_STATE } from '../../../models/conversationAttributes'; import { PnServer } from '../../apis/push_notification_api'; import { GetNetworkTime } from '../../apis/snode_api/getNetworkTime'; @@ -533,7 +533,7 @@ export async function USER_callRecipient(recipient: string) { weAreCallerOnCurrentCall = true; // initiating a call is analogous to sending a message request - await approveConvoAndSendResponse(recipient); + await handleAcceptConversationRequest({ convoId: recipient, sendResponse: false }); // Note: we do the sending of the preoffer manually as the sendTo1o1NonDurably rely on having a message saved to the db for MessageSentSuccess // which is not the case for a pre offer message (the message only exists in memory) @@ -932,8 +932,10 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) { await buildAnswerAndSendIt(fromSender, msgIdentifier); // consider the conversation completely approved - await callerConvo.setDidApproveMe(true); - await approveConvoAndSendResponse(fromSender); + await handleAcceptConversationRequest({ + convoId: fromSender, + sendResponse: true, + }); } export async function rejectCallAlreadyAnotherCall(fromSender: string, forcedUUID: string) {