From 3224fec04b4dd79dd22ebb1ccf38ccea3b8c6f13 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 11 Jan 2024 10:49:55 +1100 Subject: [PATCH] feat: marking pending member as invited when he sends a message to group --- ts/receiver/dataMessage.ts | 1 + ts/receiver/groupv2/handleGroupV2Message.ts | 12 ++- ts/receiver/queuedJob.ts | 94 ++++++++++++++------- ts/state/selectors/groups.ts | 8 ++ 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/ts/receiver/dataMessage.ts b/ts/receiver/dataMessage.ts index f7fd0c4de..51c4cf249 100644 --- a/ts/receiver/dataMessage.ts +++ b/ts/receiver/dataMessage.ts @@ -181,6 +181,7 @@ export async function handleSwarmDataMessage({ } // we handle legacy group updates from our other devices in handleLegacyClosedGroupControlMessage() if (cleanDataMessage.closedGroupControlMessage) { + // TODO DEPRECATED await handleLegacyClosedGroupControlMessage( envelope, cleanDataMessage.closedGroupControlMessage as SignalService.DataMessage.ClosedGroupControlMessage, diff --git a/ts/receiver/groupv2/handleGroupV2Message.ts b/ts/receiver/groupv2/handleGroupV2Message.ts index 28c85b7b9..af3d7a8f3 100644 --- a/ts/receiver/groupv2/handleGroupV2Message.ts +++ b/ts/receiver/groupv2/handleGroupV2Message.ts @@ -38,7 +38,9 @@ type GroupInviteDetails = { } & WithEnvelopeTimestamp & WithAuthor; -type GroupUpdateGeneric = { change: T } & WithEnvelopeTimestamp & WithGroupPubkey & WithAuthor; +type GroupUpdateGeneric = { change: Omit } & WithEnvelopeTimestamp & + WithGroupPubkey & + WithAuthor; type GroupUpdateDetails = { updateMessage: SignalService.GroupUpdateMessage; @@ -381,7 +383,7 @@ async function handleGroupUpdateInviteResponseMessage({ groupPk, change, author, -}: GroupUpdateGeneric) { +}: Omit, 'envelopeTimestamp'>) { // no sig verify for this type of message const convo = ConvoHub.use().get(groupPk); if (!convo) { @@ -536,4 +538,8 @@ async function handleGroupUpdateMessage( window.log.warn('received group update of unknown type. Discarding...'); } -export const GroupV2Receiver = { handleGroupUpdateMessage, sendInviteResponseToGroup }; +export const GroupV2Receiver = { + handleGroupUpdateMessage, + sendInviteResponseToGroup, + handleGroupUpdateInviteResponseMessage, +}; diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index dcbebce60..fc128b4bb 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -1,4 +1,4 @@ -import _, { isEmpty, isNumber } from 'lodash'; +import _, { isEmpty, isNumber, toNumber } from 'lodash'; import { queueAttachmentDownloads } from './attachments'; import { Data } from '../data/data'; @@ -16,9 +16,11 @@ import { PubKey } from '../session/types'; import { UserUtils } from '../session/utils'; import { PropsForMessageWithoutConvoProps, lookupQuote } from '../state/ducks/conversations'; import { showMessageRequestBannerOutsideRedux } from '../state/ducks/userConfig'; +import { getMemberInvitePendingOutsideRedux } from '../state/selectors/groups'; import { getHideMessageRequestBannerOutsideRedux } from '../state/selectors/userConfig'; import { GoogleChrome } from '../util'; import { LinkPreviews } from '../util/linkPreviews'; +import { GroupV2Receiver } from './groupv2/handleGroupV2Message'; function contentTypeSupported(type: string): boolean { const Chrome = GoogleChrome; @@ -212,6 +214,66 @@ export function toRegularMessage(rawDataMessage: SignalService.DataMessage): Reg }; } +async function toggleMsgRequestBannerIfNeeded( + conversation: ConversationModel, + message: MessageModel, + source: string +) { + if (!conversation.isPrivate() || !message.isIncoming()) { + return; + } + + const incomingMessageCount = await Data.getMessageCountByType( + conversation.id, + MessageDirection.incoming + ); + const isFirstRequestMessage = incomingMessageCount < 2; + if ( + conversation.isIncomingRequest() && + isFirstRequestMessage && + getHideMessageRequestBannerOutsideRedux() + ) { + showMessageRequestBannerOutsideRedux(); + } + + // For edge case when messaging a client that's unable to explicitly send request approvals + if (conversation.isOutgoingRequest()) { + // Conversation was not approved before so a sync is needed + await conversation.addIncomingApprovalMessage(toNumber(message.get('sent_at')) - 1, source); + } + // should only occur after isOutgoing request as it relies on didApproveMe being false. + await conversation.setDidApproveMe(true); +} + +async function handleMessageFromPendingMember( + conversation: ConversationModel, + message: MessageModel, + source: string +) { + const convoId = conversation.id; + if ( + !conversation.isClosedGroupV2() || + !message.isIncoming() || + !conversation.weAreAdminUnblinded() || // this checks on libsession of that group if we are an admin + !conversation.getGroupMembers().includes(source) || // this check that the sender of that message is indeed a member of the group + !PubKey.is03Pubkey(convoId) || + !PubKey.is05Pubkey(source) + ) { + return; + } + + const isMemberInvitePending = getMemberInvitePendingOutsideRedux(source, convoId); + if (!isMemberInvitePending) { + return; // nothing else to do + } + // we are an admin and we received a message from a member whose invite is `pending`. Update that member state now and push a change. + await GroupV2Receiver.handleGroupUpdateInviteResponseMessage({ + groupPk: convoId, + author: source, + change: { isApproved: true }, + }); +} + async function handleRegularMessage( conversation: ConversationModel, sendingDeviceConversation: ConversationModel, @@ -220,7 +282,6 @@ async function handleRegularMessage( source: string, messageHash: string ): Promise { - const type = message.get('type'); // this does not trigger a UI update nor write to the db await copyFromQuotedMessage(message, rawDataMessage.quote); @@ -251,33 +312,8 @@ async function handleRegularMessage( await sendingDeviceConversation.updateBlocksSogsMsgReqsTimestamp(updateBlockTimestamp, false); } - if (type === 'incoming') { - if (conversation.isPrivate()) { - const incomingMessageCount = await Data.getMessageCountByType( - conversation.id, - MessageDirection.incoming - ); - const isFirstRequestMessage = incomingMessageCount < 2; - if ( - conversation.isIncomingRequest() && - isFirstRequestMessage && - getHideMessageRequestBannerOutsideRedux() - ) { - showMessageRequestBannerOutsideRedux(); - } - - // For edge case when messaging a client that's unable to explicitly send request approvals - if (conversation.isOutgoingRequest()) { - // Conversation was not approved before so a sync is needed - await conversation.addIncomingApprovalMessage( - _.toNumber(message.get('sent_at')) - 1, - source - ); - } - // should only occur after isOutgoing request as it relies on didApproveMe being false. - await conversation.setDidApproveMe(true); - } - } + await toggleMsgRequestBannerIfNeeded(conversation, message, source); + await handleMessageFromPendingMember(conversation, message, source); const conversationActiveAt = conversation.getActiveAt(); if ( diff --git a/ts/state/selectors/groups.ts b/ts/state/selectors/groups.ts index 1903aef7c..4cea26c4b 100644 --- a/ts/state/selectors/groups.ts +++ b/ts/state/selectors/groups.ts @@ -110,6 +110,14 @@ export function getLibGroupAdminsOutsideRedux(convoId: string): Array { return state ? getLibAdminsPubkeys(state, convoId) : []; } +export function getMemberInvitePendingOutsideRedux( + member: PubkeyType, + convoId: GroupPubkeyType +): boolean { + const state = window.inboxStore?.getState(); + return state ? getMemberInvitePending(state, member, convoId) : false; +} + export function useIsCreatingGroupFromUIPending() { return useSelector(getIsCreatingGroupFromUI); }