diff --git a/keyserver/src/creators/message-creator.js b/keyserver/src/creators/message-creator.js --- a/keyserver/src/creators/message-creator.js +++ b/keyserver/src/creators/message-creator.js @@ -5,7 +5,10 @@ import { permissionLookup } from 'lib/permissions/thread-permissions.js'; import { type Device, type PushUserInfo } from 'lib/push/send-utils.js'; -import { generateNotifUserInfoPromise } from 'lib/push/utils.js'; +import { + fetchMessageNotifyType, + generateNotifUserInfo, +} from 'lib/push/utils.js'; import { rawMessageInfoFromMessageData, shimUnsupportedRawMessageInfos, @@ -425,6 +428,17 @@ messageInfosPerUser[userID] = userMessageInfos; } + const { userNotMemberOfSubthreads } = preUserPushInfo; + const messagesPromise = fetchMessageNotifyType({ + newMessageInfos: messageInfos, + messageDatas, + threadsToMessageIndices, + userNotMemberOfSubthreads, + fetchMessageInfoByID: (messageID: string) => + fetchMessageInfoByID(viewer, messageID), + userID, + }); + latestMessagesPerUser.set( userID, determineLatestMessagesPerThread( @@ -435,36 +449,29 @@ ), ); - const { userNotMemberOfSubthreads } = preUserPushInfo; const userDevices = [...preUserPushInfo.devices.values()]; if (userDevices.length === 0) { continue; } - const userPushInfoPromise = generateNotifUserInfoPromise({ - messageNotifyType: messageNotifyTypes.NOTIF_AND_SET_UNREAD, - devices: userDevices, - newMessageInfos: messageInfos, - messageDatas, - threadsToMessageIndices, - threadIDs: [...preUserPushInfo.notFocusedThreadIDs], - userNotMemberOfSubthreads, - fetchMessageInfoByID: (messageID: string) => - fetchMessageInfoByID(viewer, messageID), - userID, - }); - const userRescindInfoPromise = generateNotifUserInfoPromise({ - messageNotifyType: messageNotifyTypes.RESCIND, - devices: userDevices, - newMessageInfos: messageInfos, - messageDatas, - threadsToMessageIndices, - threadIDs: [...preUserPushInfo.notFocusedThreadIDs], - userNotMemberOfSubthreads, - fetchMessageInfoByID: (messageID: string) => - fetchMessageInfoByID(viewer, messageID), - userID, - }); + const userPushInfoPromise = (async () => { + const messages = await messagesPromise; + return generateNotifUserInfo({ + messageNotifyType: messageNotifyTypes.NOTIF_AND_SET_UNREAD, + messages, + devices: userDevices, + threadIDs: [...preUserPushInfo.notFocusedThreadIDs], + }); + })(); + const userRescindInfoPromise = (async () => { + const messages = await messagesPromise; + return generateNotifUserInfo({ + messageNotifyType: messageNotifyTypes.RESCIND, + messages, + devices: userDevices, + threadIDs: [...preUserPushInfo.notFocusedThreadIDs], + }); + })(); userPushInfoPromises[userID] = userPushInfoPromise; userRescindInfoPromises[userID] = userRescindInfoPromise; diff --git a/lib/push/send-utils.js b/lib/push/send-utils.js --- a/lib/push/send-utils.js +++ b/lib/push/send-utils.js @@ -17,7 +17,8 @@ import { stringToVersionKey, getDevicesByPlatform, - generateNotifUserInfoPromise, + fetchMessageNotifyType, + generateNotifUserInfo, userAllowsNotif, } from './utils.js'; import { createWebNotification } from './web-notif-creators.js'; @@ -216,18 +217,23 @@ for (const userID in pushUserThreadInfos) { const pushUserThreadInfo = pushUserThreadInfos[userID]; + const messagesPromise = fetchMessageNotifyType({ + newMessageInfos, + messageDatas, + threadsToMessageIndices, + userNotMemberOfSubthreads: new Set(), + fetchMessageInfoByID: (messageID: string) => + (async () => messageInfos[messageID])(), + userID, + }); + userPushInfoPromises[userID] = (async () => { - const pushInfosWithoutSubscriptions = await generateNotifUserInfoPromise({ + const messages = await messagesPromise; + const pushInfosWithoutSubscriptions = generateNotifUserInfo({ messageNotifyType: messageNotifyTypes.NOTIF_AND_SET_UNREAD, + messages, devices: pushUserThreadInfo.devices, - newMessageInfos, - messageDatas, - threadsToMessageIndices, threadIDs: Object.keys(pushUserThreadInfo.threadsWithSubscriptions), - userNotMemberOfSubthreads: new Set(), - fetchMessageInfoByID: (messageID: string) => - (async () => messageInfos[messageID])(), - userID, }); if (!pushInfosWithoutSubscriptions) { return null; @@ -239,17 +245,12 @@ })(); userRescindInfoPromises[userID] = (async () => { - const pushInfosWithoutSubscriptions = await generateNotifUserInfoPromise({ + const messages = await messagesPromise; + const pushInfosWithoutSubscriptions = generateNotifUserInfo({ messageNotifyType: messageNotifyTypes.RESCIND, + messages, devices: pushUserThreadInfo.devices, - newMessageInfos, - messageDatas, - threadsToMessageIndices, threadIDs: Object.keys(pushUserThreadInfo.threadsWithSubscriptions), - userNotMemberOfSubthreads: new Set(), - fetchMessageInfoByID: (messageID: string) => - (async () => messageInfos[messageID])(), - userID, }); if (!pushInfosWithoutSubscriptions) { return null; @@ -1325,7 +1326,6 @@ export { preparePushNotifs, prepareOwnDevicesPushNotifs, - generateNotifUserInfoPromise, pushInfoToCollapsableNotifInfo, mergeUserToCollapsableInfo, }; diff --git a/lib/push/utils.js b/lib/push/utils.js --- a/lib/push/utils.js +++ b/lib/push/utils.js @@ -5,7 +5,10 @@ import type { Device, ThreadSubscriptionWithRole } from './send-utils.js'; import { oldValidUsernameRegex } from '../shared/account-utils.js'; import { isUserMentioned } from '../shared/mention-utils.js'; -import { type MessageNotifyType } from '../shared/messages/message-spec.js'; +import { + type MessageNotifyType, + messageNotifyTypes, +} from '../shared/messages/message-spec.js'; import { messageSpecs } from '../shared/messages/message-specs.js'; import type { Platform } from '../types/device-types.js'; import { messageTypes } from '../types/message-types-enum.js'; @@ -89,96 +92,125 @@ return byPlatform; } -type GenerateNotifUserInfoPromiseInputData = { - +messageNotifyType: MessageNotifyType, - +devices: $ReadOnlyArray<Device>, +type FetchMessageNotifyTypeInputData = { +newMessageInfos: $ReadOnlyArray<RawMessageInfo>, +messageDatas: $ReadOnlyArray<MessageData>, +threadsToMessageIndices: $ReadOnlyMap<string, number[]>, - +threadIDs: $ReadOnlyArray<string>, +userNotMemberOfSubthreads: Set<string>, +fetchMessageInfoByID: (messageID: string) => Promise<any>, +userID: string, }; - -async function generateNotifUserInfoPromise( - inputData: GenerateNotifUserInfoPromiseInputData, -): Promise<?{ - devices: $ReadOnlyArray<Device>, - messageDatas: Array<MessageData>, - messageInfos: Array<RawMessageInfo>, -}> { +type FetchMessageNotifyTypeReturnInstance = { + +messageNotifyType: MessageNotifyType, + +messageInfo: RawMessageInfo, + +messageData: MessageData, +}; +async function fetchMessageNotifyType( + inputData: FetchMessageNotifyTypeInputData, +): Promise<Map<string, Array<FetchMessageNotifyTypeReturnInstance>>> { const { - messageNotifyType, - devices, newMessageInfos, messageDatas, threadsToMessageIndices, - threadIDs, userNotMemberOfSubthreads, - userID, fetchMessageInfoByID, + userID, } = inputData; - const promises: Array< - Promise<?{ - +messageInfo: RawMessageInfo, - +messageData: MessageData, - }>, - > = []; - - for (const threadID of threadIDs) { - const messageIndices = threadsToMessageIndices.get(threadID); - invariant(messageIndices, `indices should exist for thread ${threadID}`); - + const promises: Array<Promise<?FetchMessageNotifyTypeReturnInstance>> = []; + for (const [, messageIndices] of threadsToMessageIndices) { promises.push( ...messageIndices.map(async messageIndex => { const messageInfo = newMessageInfos[messageIndex]; if (messageInfo.creatorID === userID) { + // We don't need to notify the message author about their message return undefined; } + const messageData = messageDatas[messageIndex]; + const { type } = messageInfo; const { getMessageNotifyType } = messageSpecs[type]; - if (!getMessageNotifyType) { - return undefined; + let messageNotifyType = messageNotifyTypes.SET_UNREAD; + if (getMessageNotifyType) { + messageNotifyType = await getMessageNotifyType( + messageInfo, + messageData, + { + notifTargetUserID: userID, + userNotMemberOfSubthreads, + fetchMessageInfoByID, + }, + ); } - const messageData = messageDatas[messageIndex]; - const thisMessageNotifyType = await getMessageNotifyType( + return { + messageNotifyType, messageInfo, messageData, - { - notifTargetUserID: userID, - userNotMemberOfSubthreads, - fetchMessageInfoByID, - }, - ); - - return thisMessageNotifyType === messageNotifyType - ? { messageInfo, messageData } - : undefined; + }; }), ); } + const results = await Promise.all(promises); - const messagesToNotify = await Promise.all(promises); - const filteredMessagesToNotify = messagesToNotify.filter(Boolean); + const returnMap = new Map< + string, + Array<FetchMessageNotifyTypeReturnInstance>, + >(); + for (const result of results) { + if (!result) { + continue; + } + let resultsForThread = returnMap.get(result.messageInfo.threadID); + if (!resultsForThread) { + resultsForThread = []; + returnMap.set(result.messageInfo.threadID, resultsForThread); + } + resultsForThread.push(result); + } + return returnMap; +} + +type GenerateNotifUserInfoInputData = { + +messageNotifyType: MessageNotifyType, + +messages: $ReadOnlyMap< + string, + $ReadOnlyArray<FetchMessageNotifyTypeReturnInstance>, + >, + +devices: $ReadOnlyArray<Device>, + +threadIDs: $ReadOnlyArray<string>, +}; +function generateNotifUserInfo(inputData: GenerateNotifUserInfoInputData): ?{ + devices: $ReadOnlyArray<Device>, + messageInfos: Array<RawMessageInfo>, + messageDatas: Array<MessageData>, +} { + const { messageNotifyType, messages, devices, threadIDs } = inputData; + + const messageInfos: Array<RawMessageInfo> = []; + const messageDatas: Array<MessageData> = []; + + for (const threadID of threadIDs) { + const threadMessages = messages.get(threadID); + if (!threadMessages) { + continue; + } + for (const message of threadMessages) { + if (message.messageNotifyType !== messageNotifyType) { + continue; + } + messageInfos.push(message.messageInfo); + messageDatas.push(message.messageData); + } + } - if (filteredMessagesToNotify.length === 0) { + if (messageInfos.length === 0) { return undefined; } - return { - devices, - messageInfos: filteredMessagesToNotify.map( - ({ messageInfo }) => messageInfo, - ), - messageDatas: filteredMessagesToNotify.map( - ({ messageData }) => messageData, - ), - }; + return { devices, messageInfos, messageDatas }; } export type UserAllowsNotifInputData = { @@ -238,6 +270,7 @@ stringToVersionKey, versionKeyToString, getDevicesByPlatform, - generateNotifUserInfoPromise, + fetchMessageNotifyType, + generateNotifUserInfo, userAllowsNotif, };