diff --git a/keyserver/src/push/rescind.js b/keyserver/src/push/rescind.js --- a/keyserver/src/push/rescind.js +++ b/keyserver/src/push/rescind.js @@ -368,7 +368,7 @@ devices: $ReadOnlyArray, ): Promise<$ReadOnlyArray> { threadID = await validateOutput(platformDetails, tID, threadID); - return await createAndroidNotificationRescind( + const { targetedNotifications } = await createAndroidNotificationRescind( encryptedNotifUtilsAPI, { senderDeviceDescriptor: { keyserverID }, @@ -379,6 +379,7 @@ }, devices, ); + return targetedNotifications; } export { rescindPushNotifs }; 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 @@ -1093,11 +1093,12 @@ inputData, ); - return createAndroidVisualNotification( + const { targetedNotifications } = await createAndroidVisualNotification( encryptedNotifUtilsAPI, convertedData, devices, ); + return targetedNotifications; } async function prepareWebNotification( @@ -1110,7 +1111,13 @@ inputData, ); - return createWebNotification(encryptedNotifUtilsAPI, convertedData, devices); + const { targetedNotifications } = await createWebNotification( + encryptedNotifUtilsAPI, + convertedData, + devices, + ); + + return targetedNotifications; } async function prepareWNSNotification( @@ -1122,7 +1129,12 @@ wnsNotifInputDataValidator, inputData, ); - return createWNSNotification(encryptedNotifUtilsAPI, convertedData, devices); + const { targetedNotifications } = await createWNSNotification( + encryptedNotifUtilsAPI, + convertedData, + devices, + ); + return targetedNotifications; } type NotificationInfo = @@ -1518,7 +1530,7 @@ for (const [versionKey, deviceInfos] of androidVersionsToTokens) { const { codeVersion, stateVersion } = stringToVersionKey(versionKey); const preparePromise: Promise = (async () => { - const targetedNotifications: $ReadOnlyArray = + const { targetedNotifications } = await createAndroidBadgeOnlyNotification( encryptedNotifUtilsAPI, { diff --git a/lib/push/android-notif-creators.js b/lib/push/android-notif-creators.js --- a/lib/push/android-notif-creators.js +++ b/lib/push/android-notif-creators.js @@ -6,6 +6,8 @@ import { prepareEncryptedAndroidVisualNotifications, prepareEncryptedAndroidSilentNotifications, + prepareLargeNotifData, + type LargeNotifData, } from './crypto.js'; import { hasMinCodeVersion } from '../shared/version-utils.js'; import type { PlatformDetails } from '../types/device-types.js'; @@ -67,7 +69,13 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: AndroidNotifInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { + largeNotifToEncryptionResultPromises?: { + [string]: Promise, + }, +): Promise<{ + +targetedNotifications: $ReadOnlyArray, + +largeNotifData?: LargeNotifData, +}> { const { senderDeviceDescriptor, notifTexts, @@ -146,11 +154,13 @@ ? copyWithMessageInfos : notification; - return devices.map(({ deliveryID }) => ({ - priority, - notification: notificationToSend, - deliveryID, - })); + return { + targetedNotifications: devices.map(({ deliveryID }) => ({ + priority, + notification: notificationToSend, + deliveryID, + })), + }; } const notificationsSizeValidator = (notif: AndroidVisualNotification) => { @@ -176,25 +186,61 @@ .map(({ cryptoID, deliveryID }) => ({ cryptoID, deliveryID })); if (devicesWithExcessiveSizeNoHolders.length === 0) { - return notifsWithMessageInfos.map( - ({ notification: notif, deliveryID, encryptionOrder }) => ({ - priority, - notification: notif, - deliveryID, - encryptionOrder, - }), - ); + return { + targetedNotifications: notifsWithMessageInfos.map( + ({ notification: notif, deliveryID, encryptionOrder }) => ({ + priority, + notification: notif, + deliveryID, + encryptionOrder, + }), + ), + }; } const canQueryBlobService = hasMinCodeVersion(platformDetails, { native: 331, }); - let blobHash, blobHolders, encryptionKey, blobUploadError; - if (canQueryBlobService) { + let blobHash, + blobHolders, + encryptionKey, + blobUploadError, + encryptedCopyWithMessageInfos; + const copyWithMessageInfosDataBlob = JSON.stringify( + copyWithMessageInfos.data, + ); + if ( + canQueryBlobService && + largeNotifToEncryptionResultPromises && + largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob] + ) { + const largeNotifData = + await largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob]; + blobHash = largeNotifData.blobHash; + encryptionKey = largeNotifData.encryptionKey; + blobHolders = largeNotifData.blobHolders; + encryptedCopyWithMessageInfos = + largeNotifData.encryptedCopyWithMessageInfos; + } else if (canQueryBlobService && largeNotifToEncryptionResultPromises) { + largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob] = + prepareLargeNotifData( + copyWithMessageInfosDataBlob, + devicesWithExcessiveSizeNoHolders.length, + encryptedNotifUtilsAPI, + ); + + const largeNotifData = + await largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob]; + blobHash = largeNotifData.blobHash; + encryptionKey = largeNotifData.encryptionKey; + blobHolders = largeNotifData.blobHolders; + encryptedCopyWithMessageInfos = + largeNotifData.encryptedCopyWithMessageInfos; + } else if (canQueryBlobService) { ({ blobHash, blobHolders, encryptionKey, blobUploadError } = await encryptedNotifUtilsAPI.uploadLargeNotifPayload( - JSON.stringify(copyWithMessageInfos.data), + copyWithMessageInfosDataBlob, devicesWithExcessiveSizeNoHolders.length, )); } @@ -251,10 +297,29 @@ }), ); - return [ + const targetedNotifications = [ ...targetedNotifsWithMessageInfos, ...targetedNotifsWithoutMessageInfos, ]; + + if ( + !encryptedCopyWithMessageInfos || + !blobHash || + !blobHolders || + !encryptionKey + ) { + return { targetedNotifications }; + } + + return { + targetedNotifications, + largeNotifData: { + blobHash, + blobHolders, + encryptionKey, + encryptedCopyWithMessageInfos, + }, + }; } type AndroidNotificationRescindInputData = { +senderDeviceDescriptor: SenderDeviceDescriptor, @@ -268,7 +333,9 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: AndroidNotificationRescindInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { +): Promise<{ + +targetedNotifications: $ReadOnlyArray, +}> { const { senderDeviceDescriptor, platformDetails, @@ -298,11 +365,13 @@ const shouldBeEncrypted = hasMinCodeVersion(platformDetails, { native: 233 }); if (!shouldBeEncrypted) { - return devices.map(({ deliveryID }) => ({ - notification, - deliveryID, - priority: 'normal', - })); + return { + targetedNotifications: devices.map(({ deliveryID }) => ({ + notification, + deliveryID, + priority: 'normal', + })), + }; } const notifications = await prepareEncryptedAndroidSilentNotifications( @@ -312,11 +381,15 @@ notification, ); - return notifications.map(({ deliveryID, notification: notif }) => ({ - deliveryID, - notification: notif, - priority: 'normal', - })); + return { + targetedNotifications: notifications.map( + ({ deliveryID, notification: notif }) => ({ + deliveryID, + notification: notif, + priority: 'normal', + }), + ), + }; } type SenderDescriptorWithPlatformDetails = { @@ -336,7 +409,9 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: AndroidBadgeOnlyNotificationInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { +): Promise<{ + +targetedNotifications: $ReadOnlyArray, +}> { const { senderDeviceDescriptor, platformDetails, badge, threadID } = inputData; @@ -361,11 +436,13 @@ const shouldBeEncrypted = hasMinCodeVersion(platformDetails, { native: 222 }); if (!shouldBeEncrypted) { - return devices.map(({ deliveryID }) => ({ - notification, - deliveryID, - priority: 'normal', - })); + return { + targetedNotifications: devices.map(({ deliveryID }) => ({ + notification, + deliveryID, + priority: 'normal', + })), + }; } const notifications = await prepareEncryptedAndroidSilentNotifications( @@ -375,14 +452,16 @@ notification, ); - return notifications.map( - ({ notification: notif, deliveryID, encryptionOrder }) => ({ - priority: 'normal', - notification: notif, - deliveryID, - encryptionOrder, - }), - ); + return { + targetedNotifications: notifications.map( + ({ notification: notif, deliveryID, encryptionOrder }) => ({ + priority: 'normal', + notification: notif, + deliveryID, + encryptionOrder, + }), + ), + }; } export { diff --git a/lib/push/apns-notif-creators.js b/lib/push/apns-notif-creators.js --- a/lib/push/apns-notif-creators.js +++ b/lib/push/apns-notif-creators.js @@ -10,6 +10,8 @@ import { prepareEncryptedAPNsVisualNotifications, prepareEncryptedAPNsSilentNotifications, + prepareLargeNotifData, + type LargeNotifData, } from './crypto.js'; import { getAPNsNotificationTopic } from '../shared/notif-utils.js'; import { hasMinCodeVersion } from '../shared/version-utils.js'; @@ -42,7 +44,13 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: APNsNotifInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { + largeNotifToEncryptionResultPromises?: { + [string]: Promise, + }, +): Promise<{ + +targetedNotifications: $ReadOnlyArray, + +largeNotifData?: LargeNotifData, +}> { const { senderDeviceDescriptor, notifTexts, @@ -166,10 +174,13 @@ const notificationToSend = notificationSizeValidator(copyWithMessageInfos) ? copyWithMessageInfos : notification; - return devices.map(({ deliveryID }) => ({ + const targetedNotifications = devices.map(({ deliveryID }) => ({ notification: notificationToSend, deliveryID, })); + return { + targetedNotifications, + }; } // The `messageInfos` field in notification payload is @@ -183,12 +194,13 @@ notification, platformDetails.codeVersion, ); - return macOSNotifsWithoutMessageInfos.map( + const targetedNotifications = macOSNotifsWithoutMessageInfos.map( ({ notification: notif, deliveryID }) => ({ notification: notif, deliveryID, }), ); + return { targetedNotifications }; } const notifsWithMessageInfos = await prepareEncryptedAPNsVisualNotifications( @@ -208,7 +220,7 @@ })); if (devicesWithExcessiveSizeNoHolders.length === 0) { - return notifsWithMessageInfos.map( + const targetedNotifications = notifsWithMessageInfos.map( ({ notification: notif, deliveryID, @@ -221,17 +233,55 @@ encryptionOrder, }), ); + + return { + targetedNotifications, + }; } const canQueryBlobService = hasMinCodeVersion(platformDetails, { native: 331, }); - let blobHash, blobHolders, encryptionKey, blobUploadError; - if (canQueryBlobService) { + let blobHash, + blobHolders, + encryptionKey, + blobUploadError, + encryptedCopyWithMessageInfos; + + const copyWithMessageInfosBlob = serializeAPNsNotif(copyWithMessageInfos); + + if ( + canQueryBlobService && + largeNotifToEncryptionResultPromises && + largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob] + ) { + const largeNotifData = + await largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob]; + blobHash = largeNotifData.blobHash; + encryptionKey = largeNotifData.encryptionKey; + blobHolders = largeNotifData.blobHolders; + encryptedCopyWithMessageInfos = + largeNotifData.encryptedCopyWithMessageInfos; + } else if (canQueryBlobService && largeNotifToEncryptionResultPromises) { + largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob] = + prepareLargeNotifData( + copyWithMessageInfosBlob, + devicesWithExcessiveSizeNoHolders.length, + encryptedNotifUtilsAPI, + ); + + const largeNotifData = + await largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob]; + blobHash = largeNotifData.blobHash; + encryptionKey = largeNotifData.encryptionKey; + blobHolders = largeNotifData.blobHolders; + encryptedCopyWithMessageInfos = + largeNotifData.encryptedCopyWithMessageInfos; + } else if (canQueryBlobService) { ({ blobHash, blobHolders, encryptionKey, blobUploadError } = await encryptedNotifUtilsAPI.uploadLargeNotifPayload( - serializeAPNsNotif(copyWithMessageInfos), + copyWithMessageInfosBlob, devicesWithExcessiveSizeNoHolders.length, )); } @@ -300,11 +350,27 @@ encryptionOrder, }), ); - - return [ + const targetedNotifications = [ ...targetedNotifsWithMessageInfos, ...targetedNotifsWithoutMessageInfos, ]; + if ( + !encryptedCopyWithMessageInfos || + !blobHolders || + !blobHash || + !encryptionKey + ) { + return { targetedNotifications }; + } + return { + targetedNotifications, + largeNotifData: { + blobHash, + blobHolders, + encryptionKey, + encryptedCopyWithMessageInfos, + }, + }; } type APNsNotificationRescindInputData = { @@ -319,7 +385,9 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: APNsNotificationRescindInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { +): Promise<{ + +targetedNotifications: $ReadOnlyArray, +}> { const { badge, rescindID, @@ -384,10 +452,12 @@ const shouldBeEncrypted = hasMinCodeVersion(platformDetails, { native: 233 }); if (!shouldBeEncrypted) { - return devices.map(({ deliveryID }) => ({ - notification, - deliveryID, - })); + return { + targetedNotifications: devices.map(({ deliveryID }) => ({ + notification, + deliveryID, + })), + }; } const notifications = await prepareEncryptedAPNsSilentNotifications( @@ -398,10 +468,14 @@ platformDetails.codeVersion, ); - return notifications.map(({ deliveryID, notification: notif }) => ({ - deliveryID, - notification: notif, - })); + return { + targetedNotifications: notifications.map( + ({ deliveryID, notification: notif }) => ({ + deliveryID, + notification: notif, + }), + ), + }; } type SenderDescriptorWithPlatformDetails = { @@ -421,7 +495,10 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: APNsBadgeOnlyNotificationInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { +): Promise<{ + +targetedNotifications: $ReadOnlyArray, + +largeNotifData?: LargeNotifData, +}> { const { senderDeviceDescriptor, platformDetails, threadID, badge } = inputData; @@ -467,10 +544,12 @@ } if (!shouldBeEncrypted) { - return devices.map(({ deliveryID }) => ({ - deliveryID, - notification, - })); + return { + targetedNotifications: devices.map(({ deliveryID }) => ({ + deliveryID, + notification, + })), + }; } const notifications = await prepareEncryptedAPNsSilentNotifications( @@ -481,10 +560,14 @@ platformDetails.codeVersion, ); - return notifications.map(({ deliveryID, notification: notif }) => ({ - deliveryID, - notification: notif, - })); + return { + targetedNotifications: notifications.map( + ({ deliveryID, notification: notif }) => ({ + deliveryID, + notification: notif, + }), + ), + }; } export { diff --git a/lib/push/crypto.js b/lib/push/crypto.js --- a/lib/push/crypto.js +++ b/lib/push/crypto.js @@ -1,6 +1,7 @@ // @flow import invariant from 'invariant'; +import uuid from 'uuid'; import type { PlainTextWebNotification, @@ -610,6 +611,37 @@ return Promise.all(notificationPromises); } +export type LargeNotifData = { + +blobHolders: $ReadOnlyArray, + +blobHash: string, + +encryptionKey: string, + +encryptedCopyWithMessageInfos: Uint8Array, +}; + +async function prepareLargeNotifData( + copyWithMessageInfos: string, + numberOfDevices: number, + encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, +): Promise { + const encryptionKey = await encryptedNotifUtilsAPI.generateAESKey(); + + const blobHolders = Array.from({ length: numberOfDevices }, () => uuid.v4()); + const encryptedCopyWithMessageInfos = + await encryptedNotifUtilsAPI.encryptWithAESKey( + encryptionKey, + copyWithMessageInfos, + ); + const blobHash = await encryptedNotifUtilsAPI.getBlobHash( + encryptedCopyWithMessageInfos, + ); + return { + blobHolders, + blobHash, + encryptedCopyWithMessageInfos, + encryptionKey, + }; +} + export { prepareEncryptedAPNsVisualNotifications, prepareEncryptedAPNsSilentNotifications, @@ -617,4 +649,5 @@ prepareEncryptedAndroidSilentNotifications, prepareEncryptedWebNotifications, prepareEncryptedWNSNotifications, + prepareLargeNotifData, }; diff --git a/lib/push/send-hooks.react.js b/lib/push/send-hooks.react.js --- a/lib/push/send-hooks.react.js +++ b/lib/push/send-hooks.react.js @@ -164,17 +164,22 @@ return; } - let allPreparedPushNotifs = preparedPushNotifs; + let allPreparedPushNotifs: ?PerUserTargetedNotifications = + preparedPushNotifs; + if (preparedOwnDevicesPushNotifs && senderUserID) { allPreparedPushNotifs = { ...allPreparedPushNotifs, - [senderUserID]: preparedOwnDevicesPushNotifs, + [senderUserID]: { + targetedNotifications: preparedOwnDevicesPushNotifs, + }, }; } const sendPromises = []; for (const userID in allPreparedPushNotifs) { - for (const notif of allPreparedPushNotifs[userID]) { + for (const notif of allPreparedPushNotifs[userID] + .targetedNotifications) { if (notif.targetedNotification.notification.encryptionFailed) { continue; } 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 @@ -13,6 +13,7 @@ createAPNsBadgeOnlyNotification, createAPNsNotificationRescind, } from './apns-notif-creators.js'; +import type { LargeNotifData } from './crypto.js'; import { stringToVersionKey, getDevicesByPlatform, @@ -468,7 +469,13 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, input: NotifCreatorInput, devices: $ReadOnlyArray, - ) => Promise<$ReadOnlyArray>, + largeNotifToEncryptionResultPromises?: { + [string]: Promise, + }, + ) => Promise<{ + +targetedNotifications: $ReadOnlyArray, + +largeNotifData?: LargeNotifData, + }>, +notifCreatorInputBase: NotifCreatorinputBase, +transformInputBase: ( inputBase: NotifCreatorinputBase, @@ -492,12 +499,16 @@ TargetedNotificationType, NotifCreatorInput, >, -): Promise< - $ReadOnlyArray<{ + largeNotifToEncryptionResultPromises?: { + [string]: Promise, + }, +): Promise<{ + +targetedNotificationsWithPlatform: $ReadOnlyArray<{ +platform: PlatformType, +targetedNotification: TargetedNotificationType, }>, -> { + +largeNotifDataArray?: $ReadOnlyArray, +}> { const { encryptedNotifUtilsAPI, versionToDevices, @@ -508,12 +519,13 @@ } = input; const promises: Array< - Promise< - $ReadOnlyArray<{ + Promise<{ + +targetedNotificationsWithPlatform: $ReadOnlyArray<{ +platform: PlatformType, +targetedNotification: TargetedNotificationType, }>, - >, + +largeNotifData?: LargeNotifData, + }>, > = []; for (const [versionKey, devices] of versionToDevices) { @@ -534,17 +546,40 @@ promises.push( (async () => { - return ( - await notifCreatorCallback(encryptedNotifUtilsAPI, inputData, devices) - ).map(targetedNotification => ({ - platform, - targetedNotification, - })); + const { targetedNotifications, largeNotifData } = + await notifCreatorCallback( + encryptedNotifUtilsAPI, + inputData, + devices, + largeNotifToEncryptionResultPromises, + ); + const targetedNotificationsWithPlatform = targetedNotifications.map( + targetedNotification => ({ + platform, + targetedNotification, + }), + ); + return { targetedNotificationsWithPlatform, largeNotifData }; })(), ); } - return (await Promise.all(promises)).flat(); + const results = await Promise.all(promises); + const targetedNotifsWithPlatform = results + .map( + ({ targetedNotificationsWithPlatform }) => + targetedNotificationsWithPlatform, + ) + .flat(); + + const largeNotifDataArray = results + .map(({ largeNotifData }) => largeNotifData) + .filter(Boolean); + + return { + largeNotifDataArray, + targetedNotificationsWithPlatform: targetedNotifsWithPlatform, + }; } type BuildNotifsForUserDevicesInputData = { @@ -565,7 +600,13 @@ async function buildNotifsForUserDevices( inputData: BuildNotifsForUserDevicesInputData, -): Promise> { + largeNotifToEncryptionResultPromises: { + [string]: Promise, + }, +): Promise, + +largeNotifDataArray?: $ReadOnlyArray, +}> { const { encryptedNotifUtilsAPI, senderDeviceDescriptor, @@ -598,62 +639,71 @@ const [{ threadID }] = newRawMessageInfos; const promises: Array< - Promise<$ReadOnlyArray>, + Promise<{ + +targetedNotificationsWithPlatform: $ReadOnlyArray, + +largeNotifDataArray?: $ReadOnlyArray, + }>, > = []; const iosVersionToDevices = devicesByPlatform.get('ios'); if (iosVersionToDevices) { promises.push( - buildNotifsForPlatform({ - platform: 'ios', - encryptedNotifUtilsAPI, - notifCreatorCallback: createAPNsVisualNotification, - transformInputBase: (inputBase, platformDetails) => ({ - ...inputBase, - newRawMessageInfos: shimUnsupportedRawMessageInfos( - newRawMessageInfos, + buildNotifsForPlatform( + { + platform: 'ios', + encryptedNotifUtilsAPI, + notifCreatorCallback: createAPNsVisualNotification, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + newRawMessageInfos: shimUnsupportedRawMessageInfos( + newRawMessageInfos, + platformDetails, + ), platformDetails, - ), - platformDetails, - }), - notifCreatorInputBase: { - senderDeviceDescriptor, - notifTexts, - threadID, - collapseKey: undefined, - badgeOnly, - uniqueID: uuidv4(), + }), + notifCreatorInputBase: { + senderDeviceDescriptor, + notifTexts, + threadID, + collapseKey: undefined, + badgeOnly, + uniqueID: uuidv4(), + }, + versionToDevices: iosVersionToDevices, }, - versionToDevices: iosVersionToDevices, - }), + largeNotifToEncryptionResultPromises, + ), ); } const androidVersionToDevices = devicesByPlatform.get('android'); if (androidVersionToDevices) { promises.push( - buildNotifsForPlatform({ - platform: 'android', - encryptedNotifUtilsAPI, - notifCreatorCallback: createAndroidVisualNotification, - transformInputBase: (inputBase, platformDetails) => ({ - ...inputBase, - newRawMessageInfos: shimUnsupportedRawMessageInfos( - newRawMessageInfos, + buildNotifsForPlatform( + { + platform: 'android', + encryptedNotifUtilsAPI, + notifCreatorCallback: createAndroidVisualNotification, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + newRawMessageInfos: shimUnsupportedRawMessageInfos( + newRawMessageInfos, + platformDetails, + ), platformDetails, - ), - platformDetails, - }), - notifCreatorInputBase: { - senderDeviceDescriptor, - notifTexts, - threadID, - collapseKey: undefined, - badgeOnly, - notifID: uuidv4(), + }), + notifCreatorInputBase: { + senderDeviceDescriptor, + notifTexts, + threadID, + collapseKey: undefined, + badgeOnly, + notifID: uuidv4(), + }, + versionToDevices: androidVersionToDevices, }, - versionToDevices: androidVersionToDevices, - }), + largeNotifToEncryptionResultPromises, + ), ); } @@ -728,7 +778,23 @@ ); } - return (await Promise.all(promises)).flat(); + const results = await Promise.all(promises); + const targetedNotifsWithPlatform = results + .map( + ({ targetedNotificationsWithPlatform }) => + targetedNotificationsWithPlatform, + ) + .flat(); + + const largeNotifDataArray = results + .map(({ largeNotifDataArray: array }) => array) + .filter(Boolean) + .flat(); + + return { + largeNotifDataArray, + targetedNotificationsWithPlatform: targetedNotifsWithPlatform, + }; } async function buildRescindsForOwnDevices( @@ -742,7 +808,10 @@ ): Promise<$ReadOnlyArray> { const { threadID } = rescindData; const promises: Array< - Promise<$ReadOnlyArray>, + Promise<{ + +targetedNotificationsWithPlatform: $ReadOnlyArray, + ... + }>, > = []; const iosVersionToDevices = devicesByPlatform.get('ios'); @@ -784,7 +853,15 @@ }), ); } - return (await Promise.all(promises)).flat(); + + const results = await Promise.all(promises); + const targetedNotifsWithPlatform = results + .map( + ({ targetedNotificationsWithPlatform }) => + targetedNotificationsWithPlatform, + ) + .flat(); + return targetedNotifsWithPlatform; } async function buildBadgeUpdatesForOwnDevices( @@ -798,7 +875,10 @@ ): Promise<$ReadOnlyArray> { const { threadID } = badgeUpdateData; const promises: Array< - Promise<$ReadOnlyArray>, + Promise<{ + +targetedNotificationsWithPlatform: $ReadOnlyArray, + ... + }>, > = []; const iosVersionToDevices = devicesByPlatform.get('ios'); @@ -860,11 +940,22 @@ }), ); } - return (await Promise.all(promises)).flat(); + + const results = await Promise.all(promises); + const targetedNotifsWithPlatform = results + .map( + ({ targetedNotificationsWithPlatform }) => + targetedNotificationsWithPlatform, + ) + .flat(); + return targetedNotifsWithPlatform; } export type PerUserTargetedNotifications = { - +[userID: string]: $ReadOnlyArray, + +[userID: string]: { + +targetedNotifications: $ReadOnlyArray, + +largeNotifDataArray?: $ReadOnlyArray, + }, }; type BuildNotifsFromPushInfoInputData = { @@ -906,7 +997,10 @@ } const perUserBuildNotifsResultPromises: { - [userID: string]: Promise<$ReadOnlyArray>, + [userID: string]: Promise<{ + +targetedNotifications: $ReadOnlyArray, + +largeNotifDataArray?: $ReadOnlyArray, + }>, } = {}; const { usersToCollapsableNotifInfo, usersToCollapseKeysToInfo } = @@ -916,6 +1010,10 @@ usersToCollapsableNotifInfo, ); + const largeNotifToEncryptionResultPromises: { + [string]: Promise, + } = {}; + for (const userID in mergedUsersToCollapsableInfo) { const threadInfos = Object.fromEntries( [...threadIDs].map(threadID => [ @@ -935,26 +1033,45 @@ // We always pass one element array here // because coalescing is not supported for // notifications generated on the client - buildNotifsForUserDevices({ - encryptedNotifUtilsAPI, - senderDeviceDescriptor, - rawMessageInfos: notifInfo.newMessageInfos, - userID, - threadInfos, - subscriptions: pushInfo[userID].subscriptions, - userInfos, - getENSNames, - getFCNames, - devicesByPlatform, - }), + buildNotifsForUserDevices( + { + encryptedNotifUtilsAPI, + senderDeviceDescriptor, + rawMessageInfos: notifInfo.newMessageInfos, + userID, + threadInfos, + subscriptions: pushInfo[userID].subscriptions, + userInfos, + getENSNames, + getFCNames, + devicesByPlatform, + }, + largeNotifToEncryptionResultPromises, + ), ); } perUserBuildNotifsResultPromises[userID] = (async () => { - const singleNotificationResults = await Promise.all( - singleNotificationPromises, - ); - return singleNotificationResults.filter(Boolean).flat(); + const singleNotificationResults = ( + await Promise.all(singleNotificationPromises) + ).filter(Boolean); + + const targetedNotifsWithPlatform = singleNotificationResults + .map( + ({ targetedNotificationsWithPlatform }) => + targetedNotificationsWithPlatform, + ) + .flat(); + + const largeNotifDataArray = singleNotificationResults + .map(({ largeNotifDataArray: array }) => array) + .filter(Boolean) + .flat(); + console.log(largeNotifDataArray); + return { + targetedNotifications: targetedNotifsWithPlatform, + largeNotifDataArray, + }; })(); } diff --git a/lib/push/web-notif-creators.js b/lib/push/web-notif-creators.js --- a/lib/push/web-notif-creators.js +++ b/lib/push/web-notif-creators.js @@ -39,7 +39,9 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: WebNotifInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { +): Promise<{ + +targetedNotifications: $ReadOnlyArray, +}> { const { id, notifTexts, threadID, unreadCount, senderDeviceDescriptor } = inputData; @@ -56,15 +58,20 @@ }); if (!shouldBeEncrypted) { - return devices.map(({ deliveryID }) => ({ deliveryID, notification })); + return { + targetedNotifications: devices.map(({ deliveryID }) => ({ + deliveryID, + notification, + })), + }; } - - return prepareEncryptedWebNotifications( + const targetedNotifications = await prepareEncryptedWebNotifications( encryptedNotifUtilsAPI, senderDeviceDescriptor, devices, notification, ); + return { targetedNotifications }; } export { createWebNotification }; diff --git a/lib/push/wns-notif-creators.js b/lib/push/wns-notif-creators.js --- a/lib/push/wns-notif-creators.js +++ b/lib/push/wns-notif-creators.js @@ -39,7 +39,9 @@ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: WNSNotifInputData, devices: $ReadOnlyArray, -): Promise<$ReadOnlyArray> { +): Promise<{ + +targetedNotifications: $ReadOnlyArray, +}> { const { notifTexts, threadID, unreadCount, senderDeviceDescriptor } = inputData; const { merged, ...rest } = notifTexts; @@ -61,17 +63,20 @@ }); if (!shouldBeEncrypted) { - return devices.map(({ deliveryID }) => ({ - deliveryID, - notification, - })); + return { + targetedNotifications: devices.map(({ deliveryID }) => ({ + deliveryID, + notification, + })), + }; } - return await prepareEncryptedWNSNotifications( + const targetedNotifications = await prepareEncryptedWNSNotifications( encryptedNotifUtilsAPI, senderDeviceDescriptor, devices, notification, ); + return { targetedNotifications }; } export { createWNSNotification };