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 @@ -35,7 +35,7 @@ export type CommonNativeNotifInputData = $ReadOnly<{ +senderDeviceDescriptor: SenderDeviceDescriptor, +notifTexts: ResolvedNotifTexts, - +newRawMessageInfos: RawMessageInfo[], + +newRawMessageInfos: $ReadOnlyArray, +threadID: string, +collapseKey: ?string, +badgeOnly: boolean, @@ -55,10 +55,10 @@ platformDetails: tPlatformDetails, }); -export type AndroidNotifInputData = { +export type AndroidNotifInputData = $ReadOnly<{ ...CommonNativeNotifInputData, +notifID: string, -}; +}>; export const androidNotifInputDataValidator: TInterface = tShape({ 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 @@ -30,10 +30,10 @@ export const apnMaxNotificationPayloadByteSize = 4096; -export type APNsNotifInputData = { +export type APNsNotifInputData = $ReadOnly<{ ...CommonNativeNotifInputData, +uniqueID: string, -}; +}>; export const apnsNotifInputDataValidator: TInterface = tShape({ @@ -413,13 +413,19 @@ })); } -type APNsBadgeOnlyNotificationInputData = { +type SenderDescriptorWithPlatformDetails = { +senderDeviceDescriptor: SenderDeviceDescriptor, - +badge?: number, - +threadID?: string, +platformDetails: PlatformDetails, }; +type APNsBadgeOnlyNotificationInputData = $ReadOnly< + | { + ...SenderDescriptorWithPlatformDetails, + +badge: string, + } + | { ...SenderDescriptorWithPlatformDetails, +threadID: string }, +>; + async function createAPNsBadgeOnlyNotification( encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, inputData: APNsBadgeOnlyNotificationInputData, 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 @@ -1,16 +1,19 @@ // @flow +import invariant from 'invariant'; import * as React from 'react'; import uuid from 'uuid'; import { preparePushNotifs, + prepareOwnDevicesPushNotifs, type PerUserTargetedNotifications, } from './send-utils.js'; import { ENSCacheContext } from '../components/ens-cache-provider.react.js'; import { NeynarClientContext } from '../components/neynar-client-provider.react.js'; import { usePeerOlmSessionsCreatorContext } from '../components/peer-olm-session-creator-provider.react.js'; import { thickRawThreadInfosSelector } from '../selectors/thread-selectors.js'; +import { IdentityClientContext } from '../shared/identity-client-context.js'; import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js'; import type { TargetedAPNsNotification, @@ -27,7 +30,6 @@ TunnelbrokerWNSNotif, } from '../types/tunnelbroker/notif-types.js'; import { getConfig } from '../utils/config.js'; -import { getContentSigningKey } from '../utils/crypto-utils.js'; import { getMessageForException } from '../utils/errors.js'; import { useSelector } from '../utils/redux-utils.js'; @@ -98,6 +100,9 @@ function useSendPushNotifs(): ( notifCreationData: ?NotificationsCreationData, ) => Promise { + const client = React.useContext(IdentityClientContext); + invariant(client, 'Identity context should be set'); + const { getAuthMetadata } = client; const rawMessageInfos = useSelector(state => state.messageStore.messages); const thickRawThreadInfos = useSelector(thickRawThreadInfosSelector); const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos); @@ -114,9 +119,17 @@ if (!notifCreationData) { return; } - const deviceID = await getContentSigningKey(); + const { deviceID, userID: senderUserID } = await getAuthMetadata(); + if (!deviceID || !senderUserID) { + return; + } + const senderDeviceDescriptor = { senderDeviceID: deviceID }; - const { messageDatas } = notifCreationData; + const senderInfo = { + senderUserID, + senderDeviceDescriptor, + }; + const { messageDatas, rescindData, badgeUpdateData } = notifCreationData; const pushNotifsPreparationInput = { encryptedNotifUtilsAPI, @@ -131,17 +144,36 @@ getFCNames, }; - const preparedPushNotifs = await preparePushNotifs( - pushNotifsPreparationInput, - ); + const ownDevicesPushNotifsPreparationInput = { + encryptedNotifUtilsAPI, + senderInfo, + olmSessionCreator, + auxUserInfos, + rescindData, + badgeUpdateData, + }; + + const [preparedPushNotifs, preparedOwnDevicesPushNotifs] = + await Promise.all([ + preparePushNotifs(pushNotifsPreparationInput), + prepareOwnDevicesPushNotifs(ownDevicesPushNotifsPreparationInput), + ]); - if (!preparedPushNotifs) { + if (!preparedPushNotifs && !prepareOwnDevicesPushNotifs) { return; } + let allPreparedPushNotifs = preparedPushNotifs; + if (preparedOwnDevicesPushNotifs && senderUserID) { + allPreparedPushNotifs = { + ...allPreparedPushNotifs, + [senderUserID]: preparedOwnDevicesPushNotifs, + }; + } + const sendPromises = []; - for (const userID in preparedPushNotifs) { - for (const notif of preparedPushNotifs[userID]) { + for (const userID in allPreparedPushNotifs) { + for (const notif of allPreparedPushNotifs[userID]) { if (notif.targetedNotification.notification.encryptionFailed) { continue; } @@ -186,6 +218,7 @@ await Promise.all(sendPromises); }, [ + getAuthMetadata, sendNotif, encryptedNotifUtilsAPI, olmSessionCreator, 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 @@ -3,8 +3,16 @@ import _pickBy from 'lodash/fp/pickBy.js'; import uuidv4 from 'uuid/v4.js'; -import { createAndroidVisualNotification } from './android-notif-creators.js'; -import { createAPNsVisualNotification } from './apns-notif-creators.js'; +import { + createAndroidVisualNotification, + createAndroidBadgeOnlyNotification, + createAndroidNotificationRescind, +} from './android-notif-creators.js'; +import { + createAPNsVisualNotification, + createAPNsBadgeOnlyNotification, + createAPNsNotificationRescind, +} from './apns-notif-creators.js'; import { stringToVersionKey, getDevicesByPlatform, @@ -103,12 +111,12 @@ messageInfos: { +[id: string]: RawMessageInfo }, thickRawThreadInfos: ThickRawThreadInfos, auxUserInfos: AuxUserInfos, - messageDatas: $ReadOnlyArray, + messageDatas: ?$ReadOnlyArray, ): Promise<{ +pushInfos: ?PushInfo, +rescindInfos: ?PushInfo, }> { - if (messageDatas.length === 0) { + if (!messageDatas || messageDatas.length === 0) { return { pushInfos: null, rescindInfos: null }; } @@ -250,6 +258,48 @@ }; } +type SenderInfo = { + +senderUserID: string, + +senderDeviceDescriptor: SenderDeviceDescriptor, +}; + +type OwnDevicesPushInfo = { + +devices: $ReadOnlyArray, +}; + +function getOwnDevicesPushInfo( + senderInfo: SenderInfo, + auxUserInfos: AuxUserInfos, +): ?OwnDevicesPushInfo { + const { + senderUserID, + senderDeviceDescriptor: { senderDeviceID }, + } = senderInfo; + + if (!senderDeviceID) { + return null; + } + + const senderDevicesWithPlatformDetails = + auxUserInfos[senderUserID].devicesPlatformDetails; + + if (!senderDevicesWithPlatformDetails) { + return null; + } + + const devices = Object.entries(senderDevicesWithPlatformDetails) + .filter(([deviceID]) => deviceID !== senderDeviceID) + .map(([deviceID, identityPlatformDetails]) => ({ + platformDetails: identityPlatformDetailsToPlatformDetails( + identityPlatformDetails, + ), + deliveryID: deviceID, + cryptoID: deviceID, + })); + + return { devices }; +} + function pushInfoToCollapsableNotifInfo(pushInfo: PushInfo): { +usersToCollapseKeysToInfo: { [string]: { [string]: CollapsableNotifInfo }, @@ -393,6 +443,88 @@ return { notifTexts, newRawMessageInfos, badgeOnly }; } +type BuildNotifsForPlatformInput< + P: Platform, + U, + T, + I: { +platformDetails: PlatformDetails, ... }, +> = { + +platform: P, + +encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, + +notifCreatorCallback: ( + encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, + input: I, + devices: $ReadOnlyArray, + ) => Promise<$ReadOnlyArray>, + +notifCreatorInputBase: U, + +transformInputBase: (inputBase: U, platformDetails: PlatformDetails) => I, + +versionToDevices: $ReadOnlyMap< + string, + $ReadOnlyArray, + >, +}; + +async function buildNotifsForPlatform< + P: Platform, + U, + T, + I: { +platformDetails: PlatformDetails, ... }, +>( + input: BuildNotifsForPlatformInput, +): Promise< + $ReadOnlyArray<{ + +platform: P, + +targetedNotification: T, + }>, +> { + const { + encryptedNotifUtilsAPI, + versionToDevices, + notifCreatorCallback, + notifCreatorInputBase, + platform, + transformInputBase, + } = input; + + const promises: Array< + Promise< + $ReadOnlyArray<{ + +platform: P, + +targetedNotification: T, + }>, + >, + > = []; + + for (const [versionKey, devices] of versionToDevices) { + const { codeVersion, stateVersion, majorDesktopVersion } = + stringToVersionKey(versionKey); + + const platformDetails = { + platform, + codeVersion, + stateVersion, + majorDesktopVersion, + }; + + const inputData = transformInputBase( + notifCreatorInputBase, + platformDetails, + ); + + promises.push( + (async () => { + return ( + await notifCreatorCallback(encryptedNotifUtilsAPI, inputData, devices) + ).map(targetedNotification => ({ + platform, + targetedNotification, + })); + })(), + ); + } + + return (await Promise.all(promises)).flat(); +} type BuildNotifsForUserDevicesInputData = { +encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, @@ -450,196 +582,263 @@ const iosVersionToDevices = devicesByPlatform.get('ios'); if (iosVersionToDevices) { - for (const [versionKey, devices] of iosVersionToDevices) { - const { codeVersion, stateVersion } = stringToVersionKey(versionKey); - const platformDetails = { + promises.push( + buildNotifsForPlatform({ platform: 'ios', - codeVersion, - stateVersion, - }; - const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos( - newRawMessageInfos, - platformDetails, - ); - - promises.push( - (async () => { - return ( - await createAPNsVisualNotification( - encryptedNotifUtilsAPI, - { - senderDeviceDescriptor, - notifTexts, - newRawMessageInfos: shimmedNewRawMessageInfos, - threadID, - collapseKey: undefined, - badgeOnly, - unreadCount: undefined, - platformDetails, - uniqueID: uuidv4(), - }, - devices, - ) - ).map(targetedNotification => ({ - platform: 'ios', - targetedNotification, - })); - })(), - ); - } + encryptedNotifUtilsAPI, + notifCreatorCallback: createAPNsVisualNotification, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + newRawMessageInfos: shimUnsupportedRawMessageInfos( + newRawMessageInfos, + platformDetails, + ), + platformDetails, + }), + notifCreatorInputBase: { + senderDeviceDescriptor, + notifTexts, + threadID, + collapseKey: undefined, + badgeOnly, + uniqueID: uuidv4(), + }, + versionToDevices: iosVersionToDevices, + }), + ); } const androidVersionToDevices = devicesByPlatform.get('android'); if (androidVersionToDevices) { - for (const [versionKey, devices] of androidVersionToDevices) { - const { codeVersion, stateVersion } = stringToVersionKey(versionKey); - const platformDetails = { + promises.push( + buildNotifsForPlatform({ platform: 'android', - codeVersion, - stateVersion, - }; - const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos( - newRawMessageInfos, - platformDetails, - ); - - promises.push( - (async () => { - return ( - await createAndroidVisualNotification( - encryptedNotifUtilsAPI, - { - senderDeviceDescriptor, - notifTexts, - newRawMessageInfos: shimmedNewRawMessageInfos, - threadID, - collapseKey: undefined, - badgeOnly, - unreadCount: undefined, - platformDetails, - notifID: uuidv4(), - }, - devices, - ) - ).map(targetedNotification => ({ - platform: 'android', - targetedNotification, - })); - })(), - ); - } + encryptedNotifUtilsAPI, + notifCreatorCallback: createAndroidVisualNotification, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + newRawMessageInfos: shimUnsupportedRawMessageInfos( + newRawMessageInfos, + platformDetails, + ), + platformDetails, + }), + notifCreatorInputBase: { + senderDeviceDescriptor, + notifTexts, + threadID, + collapseKey: undefined, + badgeOnly, + notifID: uuidv4(), + }, + versionToDevices: androidVersionToDevices, + }), + ); } const macosVersionToDevices = devicesByPlatform.get('macos'); if (macosVersionToDevices) { - for (const [versionKey, devices] of macosVersionToDevices) { - const { codeVersion, stateVersion, majorDesktopVersion } = - stringToVersionKey(versionKey); - const platformDetails = { + promises.push( + buildNotifsForPlatform({ platform: 'macos', - codeVersion, - stateVersion, - majorDesktopVersion, - }; - const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos( - newRawMessageInfos, - platformDetails, - ); - - promises.push( - (async () => { - return ( - await createAPNsVisualNotification( - encryptedNotifUtilsAPI, - { - senderDeviceDescriptor, - notifTexts, - newRawMessageInfos: shimmedNewRawMessageInfos, - threadID, - collapseKey: undefined, - badgeOnly, - unreadCount: undefined, - platformDetails, - uniqueID: uuidv4(), - }, - devices, - ) - ).map(targetedNotification => ({ - platform: 'macos', - targetedNotification, - })); - })(), - ); - } + encryptedNotifUtilsAPI, + notifCreatorCallback: createAPNsVisualNotification, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + newRawMessageInfos: shimUnsupportedRawMessageInfos( + newRawMessageInfos, + platformDetails, + ), + platformDetails, + }), + notifCreatorInputBase: { + senderDeviceDescriptor, + notifTexts, + threadID, + collapseKey: undefined, + badgeOnly, + uniqueID: uuidv4(), + }, + versionToDevices: macosVersionToDevices, + }), + ); } const windowsVersionToDevices = devicesByPlatform.get('windows'); if (windowsVersionToDevices) { - for (const [versionKey, devices] of windowsVersionToDevices) { - const { codeVersion, stateVersion, majorDesktopVersion } = - stringToVersionKey(versionKey); - const platformDetails = { + promises.push( + buildNotifsForPlatform({ platform: 'windows', - codeVersion, - stateVersion, - majorDesktopVersion, - }; - - promises.push( - (async () => { - return ( - await createWNSNotification( - encryptedNotifUtilsAPI, - { - notifTexts, - threadID, - senderDeviceDescriptor, - platformDetails, - }, - devices, - ) - ).map(targetedNotification => ({ - platform: 'windows', - targetedNotification, - })); - })(), - ); - } + encryptedNotifUtilsAPI, + notifCreatorCallback: createWNSNotification, + notifCreatorInputBase: { + senderDeviceDescriptor, + notifTexts, + threadID, + }, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + platformDetails, + }), + versionToDevices: windowsVersionToDevices, + }), + ); } const webVersionToDevices = devicesByPlatform.get('web'); if (webVersionToDevices) { - for (const [versionKey, devices] of webVersionToDevices) { - const { codeVersion, stateVersion } = stringToVersionKey(versionKey); - const platformDetails = { + promises.push( + buildNotifsForPlatform({ platform: 'web', - codeVersion, - stateVersion, - }; + encryptedNotifUtilsAPI, + notifCreatorCallback: createWebNotification, + notifCreatorInputBase: { + senderDeviceDescriptor, + notifTexts, + threadID, + id: uuidv4(), + }, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + platformDetails, + }), + versionToDevices: webVersionToDevices, + }), + ); + } - promises.push( - (async () => { - return ( - await createWebNotification( - encryptedNotifUtilsAPI, - { - notifTexts, - threadID, - senderDeviceDescriptor, - platformDetails, - id: uuidv4(), - }, - devices, - ) - ).map(targetedNotification => ({ - platform: 'web', - targetedNotification, - })); - })(), - ); - } + return (await Promise.all(promises)).flat(); +} + +async function buildRescindsForOwnDevices( + encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, + senderDeviceDescriptor: SenderDeviceDescriptor, + devicesByPlatform: $ReadOnlyMap< + Platform, + $ReadOnlyMap>, + >, + rescindData: { +threadID: string }, +): Promise<$ReadOnlyArray> { + const { threadID } = rescindData; + const promises: Array< + Promise<$ReadOnlyArray>, + > = []; + + const iosVersionToDevices = devicesByPlatform.get('ios'); + if (iosVersionToDevices) { + promises.push( + buildNotifsForPlatform({ + platform: 'ios', + encryptedNotifUtilsAPI, + notifCreatorCallback: createAPNsNotificationRescind, + notifCreatorInputBase: { + senderDeviceDescriptor, + threadID, + }, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + platformDetails, + }), + versionToDevices: iosVersionToDevices, + }), + ); + } + + const androidVersionToDevices = devicesByPlatform.get('android'); + if (androidVersionToDevices) { + promises.push( + buildNotifsForPlatform({ + platform: 'android', + encryptedNotifUtilsAPI, + notifCreatorCallback: createAndroidNotificationRescind, + notifCreatorInputBase: { + senderDeviceDescriptor, + threadID, + }, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + platformDetails, + }), + versionToDevices: androidVersionToDevices, + }), + ); + } + return (await Promise.all(promises)).flat(); +} + +async function buildBadgeUpdatesForOwnDevices( + encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, + senderDeviceDescriptor: SenderDeviceDescriptor, + devicesByPlatform: $ReadOnlyMap< + Platform, + $ReadOnlyMap>, + >, + badgeUpdateData: { +threadID: string }, +): Promise<$ReadOnlyArray> { + const { threadID } = badgeUpdateData; + const promises: Array< + Promise<$ReadOnlyArray>, + > = []; + + const iosVersionToDevices = devicesByPlatform.get('ios'); + if (iosVersionToDevices) { + promises.push( + buildNotifsForPlatform({ + platform: 'ios', + encryptedNotifUtilsAPI, + notifCreatorCallback: createAPNsBadgeOnlyNotification, + notifCreatorInputBase: { + senderDeviceDescriptor, + threadID, + }, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + platformDetails, + }), + versionToDevices: iosVersionToDevices, + }), + ); + } + + const androidVersionToDevices = devicesByPlatform.get('android'); + if (androidVersionToDevices) { + promises.push( + buildNotifsForPlatform({ + platform: 'android', + encryptedNotifUtilsAPI, + notifCreatorCallback: createAndroidBadgeOnlyNotification, + notifCreatorInputBase: { + senderDeviceDescriptor, + threadID, + }, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + platformDetails, + }), + versionToDevices: androidVersionToDevices, + }), + ); } + const macosVersionToDevices = devicesByPlatform.get('macos'); + if (macosVersionToDevices) { + promises.push( + buildNotifsForPlatform({ + platform: 'macos', + encryptedNotifUtilsAPI, + notifCreatorCallback: createAPNsBadgeOnlyNotification, + notifCreatorInputBase: { + senderDeviceDescriptor, + threadID, + }, + transformInputBase: (inputBase, platformDetails) => ({ + ...inputBase, + platformDetails, + }), + versionToDevices: macosVersionToDevices, + }), + ); + } return (await Promise.all(promises)).flat(); } @@ -741,6 +940,43 @@ return promiseAll(perUserBuildNotifsResultPromises); } +async function createOlmSessionWithDevices( + deviceIDsToUserIDs: { + +[string]: string, + }, + olmSessionCreator: (userID: string, deviceID: string) => Promise, +): Promise { + const { + initializeCryptoAccount, + isNotificationsSessionInitializedWithDevices, + } = getConfig().olmAPI; + await initializeCryptoAccount(); + + const deviceIDsToSessionPresence = + await isNotificationsSessionInitializedWithDevices( + Object.keys(deviceIDsToUserIDs), + ); + + const olmSessionCreationPromises = []; + for (const deviceID in deviceIDsToSessionPresence) { + if (deviceIDsToSessionPresence[deviceID]) { + continue; + } + olmSessionCreationPromises.push( + olmSessionCreator(deviceIDsToUserIDs[deviceID], deviceID), + ); + } + + try { + await Promise.allSettled(olmSessionCreationPromises); + } catch (e) { + // session creation may fail for some devices + // but we should still pursue notification + // delivery for others + console.log(e); + } +} + type PreparePushNotifsInputData = { +encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, +senderDeviceDescriptor: SenderDeviceDescriptor, @@ -748,7 +984,7 @@ +messageInfos: { +[id: string]: RawMessageInfo }, +thickRawThreadInfos: ThickRawThreadInfos, +auxUserInfos: AuxUserInfos, - +messageDatas: $ReadOnlyArray, + +messageDatas: ?$ReadOnlyArray, +userInfos: UserInfos, +getENSNames: ?GetENSNames, +getFCNames: ?GetFCNames, @@ -781,12 +1017,6 @@ return null; } - const { - initializeCryptoAccount, - isNotificationsSessionInitializedWithDevices, - } = getConfig().olmAPI; - await initializeCryptoAccount(); - const deviceIDsToUserIDs: { [string]: string } = {}; for (const userID in pushInfos) { for (const device of pushInfos[userID].devices) { @@ -794,29 +1024,7 @@ } } - const deviceIDsToSessionPresence = - await isNotificationsSessionInitializedWithDevices( - Object.keys(deviceIDsToUserIDs), - ); - - const olmSessionCreationPromises = []; - for (const deviceID in deviceIDsToSessionPresence) { - if (deviceIDsToSessionPresence[deviceID]) { - continue; - } - olmSessionCreationPromises.push( - olmSessionCreator(deviceIDsToUserIDs[deviceID], deviceID), - ); - } - - try { - await Promise.allSettled(olmSessionCreationPromises); - } catch (e) { - // session creation may fail for some devices - // but we should still pursue notification - // delivery for others - console.log(e); - } + await createOlmSessionWithDevices(deviceIDsToUserIDs, olmSessionCreator); return await buildNotifsFromPushInfo({ encryptedNotifUtilsAPI, @@ -829,8 +1037,65 @@ }); } +type PrepareOwnDevicesPushNotifsInputData = { + +encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI, + +senderInfo: SenderInfo, + +olmSessionCreator: (userID: string, deviceID: string) => Promise, + +auxUserInfos: AuxUserInfos, + +rescindData?: { threadID: string }, + +badgeUpdateData?: { threadID: string }, +}; + +async function prepareOwnDevicesPushNotifs( + inputData: PrepareOwnDevicesPushNotifsInputData, +): Promise> { + const { + encryptedNotifUtilsAPI, + senderInfo, + olmSessionCreator, + auxUserInfos, + rescindData, + badgeUpdateData, + } = inputData; + + const ownDevicesPushInfo = getOwnDevicesPushInfo(senderInfo, auxUserInfos); + + if (!ownDevicesPushInfo) { + return null; + } + + const { senderUserID, senderDeviceDescriptor } = senderInfo; + const deviceIDsToUserIDs: { [string]: string } = {}; + + for (const device of ownDevicesPushInfo.devices) { + deviceIDsToUserIDs[device.cryptoID] = senderUserID; + } + + await createOlmSessionWithDevices(deviceIDsToUserIDs, olmSessionCreator); + const devicesByPlatform = getDevicesByPlatform(ownDevicesPushInfo.devices); + + if (rescindData) { + return await buildRescindsForOwnDevices( + encryptedNotifUtilsAPI, + senderDeviceDescriptor, + devicesByPlatform, + rescindData, + ); + } else if (badgeUpdateData) { + return await buildBadgeUpdatesForOwnDevices( + encryptedNotifUtilsAPI, + senderDeviceDescriptor, + devicesByPlatform, + badgeUpdateData, + ); + } else { + return null; + } +} + export { preparePushNotifs, + prepareOwnDevicesPushNotifs, generateNotifUserInfoPromise, pushInfoToCollapsableNotifInfo, mergeUserToCollapsableInfo, diff --git a/lib/shared/dm-ops/change-thread-read-status-spec.js b/lib/shared/dm-ops/change-thread-read-status-spec.js --- a/lib/shared/dm-ops/change-thread-read-status-spec.js +++ b/lib/shared/dm-ops/change-thread-read-status-spec.js @@ -11,6 +11,15 @@ const changeThreadReadStatusSpec: DMOperationSpec = Object.freeze({ + notificationsCreationData: async ( + dmOperation: DMChangeThreadReadStatusOperation, + ) => { + const { threadID, unread } = dmOperation; + if (unread) { + return { badgeUpdateData: { threadID } }; + } + return { rescindData: { threadID } }; + }, processDMOperation: async ( dmOperation: DMChangeThreadReadStatusOperation, ) => { diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js --- a/lib/types/notif-types.js +++ b/lib/types/notif-types.js @@ -28,9 +28,14 @@ prefix: t.maybe(t.String), }); -export type NotificationsCreationData = { - +messageDatas: $ReadOnlyArray, -}; +export type NotificationsCreationData = + | { + +messageDatas: $ReadOnlyArray, + } + | { + +rescindData: { threadID: string }, + } + | { +badgeUpdateData: { threadID: string } }; export type SenderDeviceDescriptor = | { +keyserverID: string }