Changeset View
Changeset View
Standalone View
Standalone View
keyserver/src/push/send.js
Show All 16 Lines | import { | ||||
shimUnsupportedRawMessageInfos, | shimUnsupportedRawMessageInfos, | ||||
} from 'lib/shared/message-utils.js'; | } from 'lib/shared/message-utils.js'; | ||||
import { messageSpecs } from 'lib/shared/messages/message-specs.js'; | import { messageSpecs } from 'lib/shared/messages/message-specs.js'; | ||||
import { notifTextsForMessageInfo } from 'lib/shared/notif-utils.js'; | import { notifTextsForMessageInfo } from 'lib/shared/notif-utils.js'; | ||||
import { | import { | ||||
rawThreadInfoFromServerThreadInfo, | rawThreadInfoFromServerThreadInfo, | ||||
threadInfoFromRawThreadInfo, | threadInfoFromRawThreadInfo, | ||||
} from 'lib/shared/thread-utils.js'; | } from 'lib/shared/thread-utils.js'; | ||||
import type { Platform } from 'lib/types/device-types.js'; | import type { Platform, PlatformDetails } from 'lib/types/device-types.js'; | ||||
import { | import { | ||||
type RawMessageInfo, | type RawMessageInfo, | ||||
type MessageInfo, | type MessageInfo, | ||||
messageTypes, | messageTypes, | ||||
} from 'lib/types/message-types.js'; | } from 'lib/types/message-types.js'; | ||||
import type { ServerThreadInfo, ThreadInfo } from 'lib/types/thread-types.js'; | import type { ServerThreadInfo, ThreadInfo } from 'lib/types/thread-types.js'; | ||||
import { updateTypes } from 'lib/types/update-types.js'; | import { updateTypes } from 'lib/types/update-types.js'; | ||||
import { promiseAll } from 'lib/utils/promises.js'; | import { promiseAll } from 'lib/utils/promises.js'; | ||||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | for (const notifInfo of usersToCollapsableNotifInfo[userID]) { | ||||
if (iosVersionsToTokens) { | if (iosVersionsToTokens) { | ||||
for (const [codeVer, deviceTokens] of iosVersionsToTokens) { | for (const [codeVer, deviceTokens] of iosVersionsToTokens) { | ||||
const codeVersion = parseInt(codeVer, 10); // only for Flow | const codeVersion = parseInt(codeVer, 10); // only for Flow | ||||
const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos( | const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos( | ||||
newRawMessageInfos, | newRawMessageInfos, | ||||
{ platform: 'ios', codeVersion }, | { platform: 'ios', codeVersion }, | ||||
); | ); | ||||
const deliveryPromise = (async () => { | const deliveryPromise = (async () => { | ||||
const notification = await prepareIOSNotification( | const notification = await prepareAPNsNotification( | ||||
allMessageInfos, | allMessageInfos, | ||||
shimmedNewRawMessageInfos, | shimmedNewRawMessageInfos, | ||||
threadInfo, | threadInfo, | ||||
notifInfo.collapseKey, | notifInfo.collapseKey, | ||||
badgeOnly, | badgeOnly, | ||||
unreadCounts[userID], | unreadCounts[userID], | ||||
codeVersion, | { platform: 'ios', codeVersion }, | ||||
); | ); | ||||
return await sendIOSNotification(notification, [...deviceTokens], { | return await sendIOSNotification(notification, [...deviceTokens], { | ||||
...notificationInfo, | ...notificationInfo, | ||||
codeVersion, | codeVersion, | ||||
}); | }); | ||||
})(); | })(); | ||||
deliveryPromises.push(deliveryPromise); | deliveryPromises.push(deliveryPromise); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 300 Lines • ▼ Show 20 Lines | if (!innerMostSet) { | ||||
innerMostSet = new Set(); | innerMostSet = new Set(); | ||||
innerMap.set(codeVersion, innerMostSet); | innerMap.set(codeVersion, innerMostSet); | ||||
} | } | ||||
innerMostSet.add(device.deviceToken); | innerMostSet.add(device.deviceToken); | ||||
} | } | ||||
return byPlatform; | return byPlatform; | ||||
} | } | ||||
async function prepareIOSNotification( | async function prepareAPNsNotification( | ||||
allMessageInfos: MessageInfo[], | allMessageInfos: MessageInfo[], | ||||
newRawMessageInfos: RawMessageInfo[], | newRawMessageInfos: RawMessageInfo[], | ||||
threadInfo: ThreadInfo, | threadInfo: ThreadInfo, | ||||
collapseKey: ?string, | collapseKey: ?string, | ||||
badgeOnly: boolean, | badgeOnly: boolean, | ||||
unreadCount: number, | unreadCount: number, | ||||
codeVersion: number, | platformDetails: PlatformDetails, | ||||
): Promise<apn.Notification> { | ): Promise<apn.Notification> { | ||||
const uniqueID = uuidv4(); | const uniqueID = uuidv4(); | ||||
const notification = new apn.Notification(); | const notification = new apn.Notification(); | ||||
notification.topic = getAPNsNotificationTopic(codeVersion); | notification.topic = getAPNsNotificationTopic(platformDetails); | ||||
const { merged, ...rest } = await notifTextsForMessageInfo( | const { merged, ...rest } = await notifTextsForMessageInfo( | ||||
allMessageInfos, | allMessageInfos, | ||||
threadInfo, | threadInfo, | ||||
getENSNames, | getENSNames, | ||||
); | ); | ||||
if (!badgeOnly) { | if (!badgeOnly) { | ||||
notification.body = merged; | notification.body = merged; | ||||
notification.sound = 'default'; | notification.sound = 'default'; | ||||
} | } | ||||
notification.payload = { | notification.payload = { | ||||
...notification.payload, | ...notification.payload, | ||||
...rest, | ...rest, | ||||
}; | }; | ||||
notification.badge = unreadCount; | notification.badge = unreadCount; | ||||
notification.threadId = threadInfo.id; | notification.threadId = threadInfo.id; | ||||
notification.id = uniqueID; | notification.id = uniqueID; | ||||
notification.pushType = 'alert'; | notification.pushType = 'alert'; | ||||
notification.payload.id = uniqueID; | notification.payload.id = uniqueID; | ||||
notification.payload.threadID = threadInfo.id; | notification.payload.threadID = threadInfo.id; | ||||
if (codeVersion > 1000) { | if (platformDetails.codeVersion && platformDetails.codeVersion > 1000) { | ||||
notification.mutableContent = true; | notification.mutableContent = true; | ||||
} | } | ||||
if (collapseKey) { | if (collapseKey) { | ||||
notification.collapseId = collapseKey; | notification.collapseId = collapseKey; | ||||
} | } | ||||
const messageInfos = JSON.stringify(newRawMessageInfos); | const messageInfos = JSON.stringify(newRawMessageInfos); | ||||
// We make a copy before checking notification's length, because calling | // We make a copy before checking notification's length, because calling | ||||
// length compiles the notification and makes it immutable. Further | // length compiles the notification and makes it immutable. Further | ||||
// changes to its properties won't be reflected in the final plaintext | // changes to its properties won't be reflected in the final plaintext | ||||
// data that is sent. | // data that is sent. | ||||
const copyWithMessageInfos = _cloneDeep(notification); | const copyWithMessageInfos = _cloneDeep(notification); | ||||
copyWithMessageInfos.payload = { | copyWithMessageInfos.payload = { | ||||
...copyWithMessageInfos.payload, | ...copyWithMessageInfos.payload, | ||||
messageInfos, | messageInfos, | ||||
}; | }; | ||||
if (copyWithMessageInfos.length() <= apnMaxNotificationPayloadByteSize) { | if (copyWithMessageInfos.length() <= apnMaxNotificationPayloadByteSize) { | ||||
notification.payload.messageInfos = messageInfos; | notification.payload.messageInfos = messageInfos; | ||||
return notification; | return notification; | ||||
} | } | ||||
const notificationCopy = _cloneDeep(notification); | const notificationCopy = _cloneDeep(notification); | ||||
if (notificationCopy.length() > apnMaxNotificationPayloadByteSize) { | if (notificationCopy.length() > apnMaxNotificationPayloadByteSize) { | ||||
console.warn( | console.warn( | ||||
`iOS notification ${uniqueID} exceeds size limit, even with messageInfos omitted`, | `${platformDetails.platform} notification ${uniqueID} ` + | ||||
`exceeds size limit, even with messageInfos omitted`, | |||||
); | ); | ||||
} | } | ||||
return notification; | return notification; | ||||
} | } | ||||
async function prepareAndroidNotification( | async function prepareAndroidNotification( | ||||
allMessageInfos: MessageInfo[], | allMessageInfos: MessageInfo[], | ||||
newRawMessageInfos: RawMessageInfo[], | newRawMessageInfos: RawMessageInfo[], | ||||
▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | type IOSResult = { | ||||
invalidTokens?: $ReadOnlyArray<string>, | invalidTokens?: $ReadOnlyArray<string>, | ||||
}; | }; | ||||
async function sendIOSNotification( | async function sendIOSNotification( | ||||
notification: apn.Notification, | notification: apn.Notification, | ||||
deviceTokens: $ReadOnlyArray<string>, | deviceTokens: $ReadOnlyArray<string>, | ||||
notificationInfo: NotificationInfo, | notificationInfo: NotificationInfo, | ||||
): Promise<IOSResult> { | ): Promise<IOSResult> { | ||||
const { source, codeVersion } = notificationInfo; | const { source, codeVersion } = notificationInfo; | ||||
const response = await apnPush({ notification, deviceTokens, codeVersion }); | const response = await apnPush({ | ||||
notification, | |||||
deviceTokens, | |||||
platformDetails: { platform: 'ios', codeVersion }, | |||||
}); | |||||
const delivery: IOSDelivery = { | const delivery: IOSDelivery = { | ||||
source, | source, | ||||
deviceType: 'ios', | deviceType: 'ios', | ||||
iosID: notification.id, | iosID: notification.id, | ||||
deviceTokens, | deviceTokens, | ||||
codeVersion, | codeVersion, | ||||
}; | }; | ||||
if (response.errors) { | if (response.errors) { | ||||
▲ Show 20 Lines • Show All 194 Lines • ▼ Show 20 Lines | ) { | ||||
const deliveryPromises = []; | const deliveryPromises = []; | ||||
const iosVersionsToTokens = byPlatform.get('ios'); | const iosVersionsToTokens = byPlatform.get('ios'); | ||||
if (iosVersionsToTokens) { | if (iosVersionsToTokens) { | ||||
for (const [codeVer, deviceTokens] of iosVersionsToTokens) { | for (const [codeVer, deviceTokens] of iosVersionsToTokens) { | ||||
const codeVersion = parseInt(codeVer, 10); // only for Flow | const codeVersion = parseInt(codeVer, 10); // only for Flow | ||||
const notification = new apn.Notification(); | const notification = new apn.Notification(); | ||||
notification.topic = getAPNsNotificationTopic(codeVersion); | notification.topic = getAPNsNotificationTopic({ | ||||
platform: 'ios', | |||||
codeVersion, | |||||
}); | |||||
notification.badge = unreadCount; | notification.badge = unreadCount; | ||||
notification.pushType = 'alert'; | notification.pushType = 'alert'; | ||||
deliveryPromises.push( | deliveryPromises.push( | ||||
sendIOSNotification(notification, [...deviceTokens], { | sendIOSNotification(notification, [...deviceTokens], { | ||||
source, | source, | ||||
dbID, | dbID, | ||||
userID, | userID, | ||||
codeVersion, | codeVersion, | ||||
Show All 30 Lines |