Page MenuHomePhabricator

D12463.id41452.diff
No OneTemporary

D12463.id41452.diff

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
@@ -4,13 +4,13 @@
import _pickBy from 'lodash/fp/pickBy.js';
import { permissionLookup } from 'lib/permissions/thread-permissions.js';
+import { generateNotifUserInfoPromise } from 'lib/push/send-utils.js';
import {
rawMessageInfoFromMessageData,
shimUnsupportedRawMessageInfos,
stripLocalIDs,
} from 'lib/shared/message-utils.js';
import { pushTypes } from 'lib/shared/messages/message-spec.js';
-import type { PushType } from 'lib/shared/messages/message-spec.js';
import { messageSpecs } from 'lib/shared/messages/message-specs.js';
import { messageTypes } from 'lib/types/message-types-enum.js';
import {
@@ -432,71 +432,27 @@
continue;
}
- const generateNotifUserInfoPromise = async (pushType: PushType) => {
- const promises: Array<
- Promise<?{
- +messageInfo: RawMessageInfo,
- +messageData: MessageData,
- }>,
- > = [];
-
- for (const threadID of preUserPushInfo.notFocusedThreadIDs) {
- const messageIndices = threadsToMessageIndices.get(threadID);
- invariant(
- messageIndices,
- `indices should exist for thread ${threadID}`,
- );
- promises.push(
- ...messageIndices.map(async messageIndex => {
- const messageInfo = messageInfos[messageIndex];
- const { type } = messageInfo;
- if (messageInfo.creatorID === userID) {
- // We never send a user notifs about their own activity
- return undefined;
- }
- const { generatesNotifs } = messageSpecs[type];
- const messageData = messageDatas[messageIndex];
- if (!generatesNotifs) {
- return undefined;
- }
- const doesGenerateNotif = await generatesNotifs(
- messageInfo,
- messageData,
- {
- notifTargetUserID: userID,
- userNotMemberOfSubthreads,
- fetchMessageInfoByID: (messageID: string) =>
- fetchMessageInfoByID(viewer, messageID),
- },
- );
- return doesGenerateNotif === pushType
- ? { messageInfo, messageData }
- : undefined;
- }),
- );
- }
-
- const messagesToNotify = await Promise.all(promises);
- const filteredMessagesToNotify = messagesToNotify.filter(Boolean);
-
- if (filteredMessagesToNotify.length === 0) {
- return undefined;
- }
-
- return {
- devices: userDevices,
- messageInfos: filteredMessagesToNotify.map(
- ({ messageInfo }) => messageInfo,
- ),
- messageDatas: filteredMessagesToNotify.map(
- ({ messageData }) => messageData,
- ),
- };
- };
-
- const userPushInfoPromise = generateNotifUserInfoPromise(pushTypes.NOTIF);
+ const userPushInfoPromise = generateNotifUserInfoPromise(
+ pushTypes.NOTIF,
+ userDevices,
+ messageInfos,
+ messageDatas,
+ threadsToMessageIndices,
+ preUserPushInfo.notFocusedThreadIDs,
+ userNotMemberOfSubthreads,
+ (messageID: string) => fetchMessageInfoByID(viewer, messageID),
+ userID,
+ );
const userRescindInfoPromise = generateNotifUserInfoPromise(
pushTypes.RESCIND,
+ userDevices,
+ messageInfos,
+ messageDatas,
+ threadsToMessageIndices,
+ preUserPushInfo.notFocusedThreadIDs,
+ userNotMemberOfSubthreads,
+ (messageID: string) => fetchMessageInfoByID(viewer, messageID),
+ userID,
);
userPushInfoPromises[userID] = userPushInfoPromise;
diff --git a/lib/push/send-utils.js b/lib/push/send-utils.js
new file mode 100644
--- /dev/null
+++ b/lib/push/send-utils.js
@@ -0,0 +1,252 @@
+// @flow
+
+import invariant from 'invariant';
+import _pickBy from 'lodash/fp/pickBy.js';
+import uuidv4 from 'uuid/v4.js';
+
+import { hasPermission } from '../permissions/minimally-encoded-thread-permissions.js';
+import { rawMessageInfoFromMessageData } from '../shared/message-utils.js';
+import { type PushType, pushTypes } from '../shared/messages/message-spec.js';
+import { messageSpecs } from '../shared/messages/message-specs.js';
+import { isMemberActive } from '../shared/thread-utils.js';
+import type { AuxUserInfos } from '../types/aux-user-types.js';
+import type { PlatformDetails } from '../types/device-types.js';
+import {
+ identityDeviceTypeToPlatform,
+ type IdentityPlatformDetails,
+} from '../types/identity-service-types.js';
+import {
+ type MessageData,
+ type RawMessageInfo,
+ messageDataLocalID,
+} from '../types/message-types.js';
+import type { RawThreadInfos } from '../types/thread-types.js';
+import { promiseAll } from '../utils/promises.js';
+
+type PushUserInfo = {
+ +devices: $ReadOnlyArray<{
+ +platformDetails: PlatformDetails,
+ +deliveryID: string,
+ +cryptoID: string,
+ }>,
+ +messageInfos: RawMessageInfo[],
+ +messageDatas: MessageData[],
+};
+
+type PushInfo = { +[userID: string]: PushUserInfo };
+
+type PushUserThreadInfo = {
+ +devices: $ReadOnlyArray<{
+ +platformDetails: PlatformDetails,
+ +deliveryID: string,
+ +cryptoID: string,
+ }>,
+ +threadIDs: Set<string>,
+};
+
+function identityPlatformDetailsToPlatformDetails(
+ identityPlatformDetails: IdentityPlatformDetails,
+): PlatformDetails {
+ const { deviceType, ...rest } = identityPlatformDetails;
+ return {
+ ...rest,
+ platform: identityDeviceTypeToPlatform[deviceType],
+ };
+}
+
+async function generateNotifUserInfoPromise<T>(
+ pushType: PushType,
+ devices: T,
+ newMessageInfos: $ReadOnlyArray<RawMessageInfo>,
+ messageDatas: $ReadOnlyArray<MessageData>,
+ threadsToMessageIndices: $ReadOnlyMap<string, number[]>,
+ threadIDs: $ReadOnlySet<string>,
+ userNotMemberOfSubthreads: Set<string>,
+ fetchMessageInfoByID: (messageID: string) => Promise<any>,
+ userID: string,
+): Promise<?{
+ devices: T,
+ messageDatas: Array<MessageData>,
+ messageInfos: Array<RawMessageInfo>,
+}> {
+ 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}`);
+
+ promises.push(
+ ...messageIndices.map(async messageIndex => {
+ const messageInfo = newMessageInfos[messageIndex];
+ if (messageInfo.creatorID === userID) {
+ return undefined;
+ }
+
+ const { type } = messageInfo;
+ const { generatesNotifs } = messageSpecs[type];
+
+ if (!generatesNotifs) {
+ return undefined;
+ }
+
+ const messageData = messageDatas[messageIndex];
+ const doesGenerateNotif = await generatesNotifs(
+ messageInfo,
+ messageData,
+ {
+ notifTargetUserID: userID,
+ userNotMemberOfSubthreads,
+ fetchMessageInfoByID,
+ },
+ );
+
+ return doesGenerateNotif === pushType
+ ? { messageInfo, messageData }
+ : undefined;
+ }),
+ );
+ }
+
+ const messagesToNotify = await Promise.all(promises);
+ const filteredMessagesToNotify = messagesToNotify.filter(Boolean);
+
+ if (filteredMessagesToNotify.length === 0) {
+ return undefined;
+ }
+
+ return {
+ devices,
+ messageInfos: filteredMessagesToNotify.map(
+ ({ messageInfo }) => messageInfo,
+ ),
+ messageDatas: filteredMessagesToNotify.map(
+ ({ messageData }) => messageData,
+ ),
+ };
+}
+
+async function getPushUserInfo(
+ messageInfos: { +[id: string]: RawMessageInfo },
+ rawThreadInfos: RawThreadInfos,
+ auxUserInfos: AuxUserInfos,
+ messageDatas: $ReadOnlyArray<MessageData>,
+): Promise<{
+ +pushInfos: ?PushInfo,
+ +rescindInfos: ?PushInfo,
+}> {
+ if (messageDatas.length === 0) {
+ return { pushInfos: null, rescindInfos: null };
+ }
+
+ const threadsToMessageIndices: Map<string, number[]> = new Map();
+ const newMessageInfos: RawMessageInfo[] = [];
+
+ let nextNewMessageIndex = 0;
+ for (let i = 0; i < messageDatas.length; i++) {
+ const messageData = messageDatas[i];
+ const threadID = messageData.threadID;
+
+ let messageIndices = threadsToMessageIndices.get(threadID);
+ if (!messageIndices) {
+ messageIndices = [];
+ threadsToMessageIndices.set(threadID, messageIndices);
+ }
+
+ const newMessageIndex = nextNewMessageIndex++;
+ messageIndices.push(newMessageIndex);
+
+ const messageID = messageDataLocalID(messageData) ?? uuidv4();
+ const rawMessageInfo = rawMessageInfoFromMessageData(
+ messageData,
+ messageID,
+ );
+ newMessageInfos.push(rawMessageInfo);
+ }
+
+ const pushUserThreadInfos: { [userID: string]: PushUserThreadInfo } = {};
+ for (const threadID of threadsToMessageIndices.keys()) {
+ const threadInfo = rawThreadInfos[threadID];
+ for (const memberInfo of threadInfo.members) {
+ if (
+ !isMemberActive(memberInfo) ||
+ !hasPermission(memberInfo.permissions, 'visible')
+ ) {
+ continue;
+ }
+
+ if (pushUserThreadInfos[memberInfo.id]) {
+ pushUserThreadInfos[memberInfo.id].threadIDs.add(threadID);
+ continue;
+ }
+
+ const devicesPlatformDetails =
+ auxUserInfos[memberInfo.id].devicesPlatformDetails;
+
+ if (!devicesPlatformDetails) {
+ continue;
+ }
+
+ const devices = Object.entries(devicesPlatformDetails).map(
+ ([deviceID, identityPlatformDetails]) => ({
+ platformDetails: identityPlatformDetailsToPlatformDetails(
+ identityPlatformDetails,
+ ),
+ deliveryID: deviceID,
+ cryptoID: deviceID,
+ }),
+ );
+
+ pushUserThreadInfos[memberInfo.id] = {
+ devices,
+ threadIDs: new Set([threadID]),
+ };
+ }
+ }
+
+ const userPushInfoPromises: { [string]: Promise<?PushUserInfo> } = {};
+ const userRescindInfoPromises: { [string]: Promise<?PushUserInfo> } = {};
+
+ for (const userID in pushUserThreadInfos) {
+ const pushUserThreadInfo = pushUserThreadInfos[userID];
+
+ userPushInfoPromises[userID] = generateNotifUserInfoPromise(
+ pushTypes.NOTIF,
+ pushUserThreadInfo.devices,
+ newMessageInfos,
+ messageDatas,
+ threadsToMessageIndices,
+ pushUserThreadInfo.threadIDs,
+ new Set(),
+ (messageID: string) => (async () => messageInfos[messageID])(),
+ userID,
+ );
+ userRescindInfoPromises[userID] = generateNotifUserInfoPromise(
+ pushTypes.RESCIND,
+ pushUserThreadInfo.devices,
+ newMessageInfos,
+ messageDatas,
+ threadsToMessageIndices,
+ pushUserThreadInfo.threadIDs,
+ new Set(),
+ (messageID: string) => (async () => messageInfos[messageID])(),
+ userID,
+ );
+ }
+
+ const [pushInfo, rescindInfo] = await Promise.all([
+ promiseAll(userPushInfoPromises),
+ promiseAll(userRescindInfoPromises),
+ ]);
+
+ return {
+ pushInfos: _pickBy(Boolean)(pushInfo),
+ rescindInfos: _pickBy(Boolean)(rescindInfo),
+ };
+}
+
+export { getPushUserInfo, generateNotifUserInfoPromise };
diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -219,6 +219,11 @@
);
}
+function isMemberActive(memberInfo: MemberInfoWithPermissions): boolean {
+ const role = memberInfo.role;
+ return role !== null && role !== undefined;
+}
+
function threadIsInHome(threadInfo: ?(RawThreadInfo | ThreadInfo)): boolean {
return !!(threadInfo && threadInfo.currentUser.subscription.home);
}
@@ -1711,4 +1716,5 @@
assertAllThreadInfosAreLegacy,
useOnScreenEntryEditableThreadInfos,
extractMentionedMembers,
+ isMemberActive,
};
diff --git a/lib/types/identity-service-types.js b/lib/types/identity-service-types.js
--- a/lib/types/identity-service-types.js
+++ b/lib/types/identity-service-types.js
@@ -9,6 +9,7 @@
type SignedPrekeys,
type OneTimeKeysResultValues,
} from './crypto-types.js';
+import { type Platform } from './device-types.js';
import {
type OlmSessionInitializationInfo,
olmSessionInitializationInfoValidator,
@@ -327,6 +328,17 @@
});
export type IdentityDeviceType = $Values<typeof identityDeviceTypes>;
+
+export const identityDeviceTypeToPlatform: {
+ +[identityDeviceType: IdentityDeviceType]: Platform,
+} = {
+ [identityDeviceTypes.WEB]: 'web',
+ [identityDeviceTypes.ANDROID]: 'android',
+ [identityDeviceTypes.IOS]: 'ios',
+ [identityDeviceTypes.WINDOWS]: 'windows',
+ [identityDeviceTypes.MAC_OS]: 'macos',
+};
+
export const identityDeviceTypeValidator: TEnums = t.enums.of(
values(identityDeviceTypes),
);

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 29, 11:43 AM (7 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2732024
Default Alt Text
D12463.id41452.diff (12 KB)

Event Timeline