diff --git a/keyserver/src/fetchers/message-fetchers.js b/keyserver/src/fetchers/message-fetchers.js --- a/keyserver/src/fetchers/message-fetchers.js +++ b/keyserver/src/fetchers/message-fetchers.js @@ -2,16 +2,19 @@ import invariant from 'invariant'; -import type { PushInfo } from 'lib/push/send-utils.js'; import { - sortMessageInfoList, + type PushInfo, + type FetchCollapsableNotifsResult, + pushInfoToCollapsableNotifInfo, + mergeUserToCollapsableInfo, +} from 'lib/push/send-utils.js'; +import { shimUnsupportedRawMessageInfos, isInvalidSidebarSource, isUnableToBeRenderedIndependently, isInvalidPinSource, } from 'lib/shared/message-utils.js'; import { messageSpecs } from 'lib/shared/messages/message-specs.js'; -import { getNotifCollapseKey } from 'lib/shared/notif-utils.js'; import { messageTypes, type MessageType, @@ -54,15 +57,6 @@ localIDFromCreationString, } from '../utils/idempotent.js'; -export type CollapsableNotifInfo = { - collapseKey: ?string, - existingMessageInfos: RawMessageInfo[], - newMessageInfos: RawMessageInfo[], -}; -export type FetchCollapsableNotifsResult = { - [userID: string]: CollapsableNotifInfo[], -}; - const visibleExtractString = `$.${threadPermissions.VISIBLE}.value`; // This function doesn't filter RawMessageInfos based on what messageTypes the @@ -72,39 +66,8 @@ pushInfo: PushInfo, ): Promise { // First, we need to fetch any notifications that should be collapsed - const usersToCollapseKeysToInfo: { - [string]: { [string]: CollapsableNotifInfo }, - } = {}; - const usersToCollapsableNotifInfo: { [string]: Array } = - {}; - for (const userID in pushInfo) { - usersToCollapseKeysToInfo[userID] = {}; - usersToCollapsableNotifInfo[userID] = []; - for (let i = 0; i < pushInfo[userID].messageInfos.length; i++) { - const rawMessageInfo = pushInfo[userID].messageInfos[i]; - const messageData = pushInfo[userID].messageDatas[i]; - const collapseKey = getNotifCollapseKey(rawMessageInfo, messageData); - if (!collapseKey) { - const collapsableNotifInfo: CollapsableNotifInfo = { - collapseKey, - existingMessageInfos: [], - newMessageInfos: [rawMessageInfo], - }; - usersToCollapsableNotifInfo[userID].push(collapsableNotifInfo); - continue; - } - if (!usersToCollapseKeysToInfo[userID][collapseKey]) { - usersToCollapseKeysToInfo[userID][collapseKey] = ({ - collapseKey, - existingMessageInfos: [], - newMessageInfos: [], - }: CollapsableNotifInfo); - } - usersToCollapseKeysToInfo[userID][collapseKey].newMessageInfos.push( - rawMessageInfo, - ); - } - } + const { usersToCollapsableNotifInfo, usersToCollapseKeysToInfo } = + pushInfoToCollapsableNotifInfo(pushInfo); const sqlTuples = []; for (const userID in usersToCollapseKeysToInfo) { @@ -168,19 +131,10 @@ } } - for (const userID in usersToCollapseKeysToInfo) { - const collapseKeysToInfo = usersToCollapseKeysToInfo[userID]; - for (const collapseKey in collapseKeysToInfo) { - const info = collapseKeysToInfo[collapseKey]; - usersToCollapsableNotifInfo[userID].push({ - collapseKey: info.collapseKey, - existingMessageInfos: sortMessageInfoList(info.existingMessageInfos), - newMessageInfos: sortMessageInfoList(info.newMessageInfos), - }); - } - } - - return usersToCollapsableNotifInfo; + return mergeUserToCollapsableInfo( + usersToCollapseKeysToInfo, + usersToCollapsableNotifInfo, + ); } type MessageSQLResultRow = { @@ -1035,6 +989,7 @@ export { fetchCollapsableNotifs, + pushInfoToCollapsableNotifInfo, fetchMessageInfos, fetchMessageInfosSince, getMessageFetchResultFromRedisMessages, diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js --- a/keyserver/src/push/send.js +++ b/keyserver/src/push/send.js @@ -19,7 +19,12 @@ createAndroidBadgeOnlyNotification, } from 'lib/push/android-notif-creators.js'; import { apnMaxNotificationPayloadByteSize } from 'lib/push/apns-notif-creators.js'; -import type { PushUserInfo, PushInfo, Device } from 'lib/push/send-utils.js'; +import type { + PushUserInfo, + PushInfo, + Device, + CollapsableNotifInfo, +} from 'lib/push/send-utils.js'; import { stringToVersionKey, getDevicesByPlatform, @@ -87,7 +92,6 @@ import createIDs from '../creators/id-creator.js'; import { createUpdates } from '../creators/update-creator.js'; import { dbQuery, mergeOrConditions, SQL } from '../database/database.js'; -import type { CollapsableNotifInfo } from '../fetchers/message-fetchers.js'; import { fetchCollapsableNotifs } from '../fetchers/message-fetchers.js'; import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js'; import { fetchUserInfos } from '../fetchers/user-fetchers.js'; 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 @@ -18,10 +18,14 @@ rawMessageInfoFromMessageData, createMessageInfo, shimUnsupportedRawMessageInfos, + sortMessageInfoList, } from '../shared/message-utils.js'; import { pushTypes } from '../shared/messages/message-spec.js'; import { messageSpecs } from '../shared/messages/message-specs.js'; -import { notifTextsForMessageInfo } from '../shared/notif-utils.js'; +import { + notifTextsForMessageInfo, + getNotifCollapseKey, +} from '../shared/notif-utils.js'; import { isMemberActive, threadInfoFromRawThreadInfo, @@ -75,6 +79,16 @@ export type PushInfo = { +[userID: string]: PushUserInfo }; +export type CollapsableNotifInfo = { + collapseKey: ?string, + existingMessageInfos: RawMessageInfo[], + newMessageInfos: RawMessageInfo[], +}; + +export type FetchCollapsableNotifsResult = { + [userID: string]: CollapsableNotifInfo[], +}; + function identityPlatformDetailsToPlatformDetails( identityPlatformDetails: IdentityPlatformDetails, ): PlatformDetails { @@ -236,6 +250,74 @@ }; } +function pushInfoToCollapsableNotifInfo(pushInfo: PushInfo): { + +usersToCollapseKeysToInfo: { + [string]: { [string]: CollapsableNotifInfo }, + }, + +usersToCollapsableNotifInfo: { [string]: Array }, +} { + const usersToCollapseKeysToInfo: { + [string]: { [string]: CollapsableNotifInfo }, + } = {}; + const usersToCollapsableNotifInfo: { [string]: Array } = + {}; + for (const userID in pushInfo) { + usersToCollapseKeysToInfo[userID] = {}; + usersToCollapsableNotifInfo[userID] = []; + for (let i = 0; i < pushInfo[userID].messageInfos.length; i++) { + const rawMessageInfo = pushInfo[userID].messageInfos[i]; + const messageData = pushInfo[userID].messageDatas[i]; + const collapseKey = getNotifCollapseKey(rawMessageInfo, messageData); + if (!collapseKey) { + const collapsableNotifInfo: CollapsableNotifInfo = { + collapseKey, + existingMessageInfos: [], + newMessageInfos: [rawMessageInfo], + }; + usersToCollapsableNotifInfo[userID].push(collapsableNotifInfo); + continue; + } + if (!usersToCollapseKeysToInfo[userID][collapseKey]) { + usersToCollapseKeysToInfo[userID][collapseKey] = ({ + collapseKey, + existingMessageInfos: [], + newMessageInfos: [], + }: CollapsableNotifInfo); + } + usersToCollapseKeysToInfo[userID][collapseKey].newMessageInfos.push( + rawMessageInfo, + ); + } + } + + return { + usersToCollapseKeysToInfo, + usersToCollapsableNotifInfo, + }; +} + +function mergeUserToCollapsableInfo( + usersToCollapseKeysToInfo: { + [string]: { [string]: CollapsableNotifInfo }, + }, + usersToCollapsableNotifInfo: { [string]: Array }, +): { [string]: Array } { + const mergedUsersToCollapsableInfo = { ...usersToCollapsableNotifInfo }; + for (const userID in usersToCollapseKeysToInfo) { + const collapseKeysToInfo = usersToCollapseKeysToInfo[userID]; + for (const collapseKey in collapseKeysToInfo) { + const info = collapseKeysToInfo[collapseKey]; + mergedUsersToCollapsableInfo[userID].push({ + collapseKey: info.collapseKey, + existingMessageInfos: sortMessageInfoList(info.existingMessageInfos), + newMessageInfos: sortMessageInfoList(info.newMessageInfos), + }); + } + } + + return mergedUsersToCollapsableInfo; +} + async function buildNotifText( rawMessageInfos: $ReadOnlyArray, userID: string, @@ -607,7 +689,14 @@ [userID: string]: Promise<$ReadOnlyArray>, } = {}; - for (const userID in pushInfo) { + const { usersToCollapsableNotifInfo, usersToCollapseKeysToInfo } = + pushInfoToCollapsableNotifInfo(pushInfo); + const mergedUsersToCollapsableInfo = mergeUserToCollapsableInfo( + usersToCollapseKeysToInfo, + usersToCollapsableNotifInfo, + ); + + for (const userID in mergedUsersToCollapsableInfo) { const threadInfos = Object.fromEntries( [...threadIDs].map(threadID => [ threadID, @@ -621,7 +710,7 @@ const devicesByPlatform = getDevicesByPlatform(pushInfo[userID].devices); const singleNotificationPromises = []; - for (const rawMessageInfos of pushInfo[userID].messageInfos) { + for (const notifInfo of mergedUsersToCollapsableInfo[userID]) { singleNotificationPromises.push( // We always pass one element array here // because coalescing is not supported for @@ -629,7 +718,7 @@ buildNotifsForUserDevices({ encryptedNotifUtilsAPI, senderDeviceDescriptor, - rawMessageInfos: [rawMessageInfos], + rawMessageInfos: notifInfo.newMessageInfos, userID, threadInfos, subscriptions: pushInfo[userID].subscriptions, @@ -740,4 +829,9 @@ }); } -export { preparePushNotifs, generateNotifUserInfoPromise }; +export { + preparePushNotifs, + generateNotifUserInfoPromise, + pushInfoToCollapsableNotifInfo, + mergeUserToCollapsableInfo, +};