Page MenuHomePhabricator

D12882.id43517.diff
No OneTemporary

D12882.id43517.diff

diff --git a/lib/handlers/db-ops-handler.react.js b/lib/handlers/db-ops-handler.react.js
--- a/lib/handlers/db-ops-handler.react.js
+++ b/lib/handlers/db-ops-handler.react.js
@@ -3,6 +3,7 @@
import * as React from 'react';
import { opsProcessingFinishedActionType } from '../actions/db-ops-actions.js';
+import { useSendPushNotifs } from '../push/send-hooks.react.js';
import { usePeerToPeerCommunication } from '../tunnelbroker/peer-to-peer-context.js';
import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js';
import type { DBOpsEntry } from '../types/db-ops-types.js';
@@ -22,6 +23,7 @@
const prevQueueFront = React.useRef<?DBOpsEntry>(null);
const { sendMessageToDevice } = useTunnelbroker();
const { processOutboundMessages } = usePeerToPeerCommunication();
+ const sendPushNotifs = useSendPushNotifs();
const dispatch = useDispatch();
@@ -31,10 +33,13 @@
}
prevQueueFront.current = queueFront;
- const { ops, messageSourceMetadata, dmOpID } = queueFront;
+ const { ops, messageSourceMetadata, dmOpID, notificationsCreationData } =
+ queueFront;
+
void (async () => {
if (ops) {
await processDBStoreOperations(ops);
+
if (ops.outboundP2PMessages && ops.outboundP2PMessages.length > 0) {
const messageIDs = ops.outboundP2PMessages.map(
message => message.messageID,
@@ -42,6 +47,10 @@
processOutboundMessages(messageIDs, dmOpID);
}
}
+
+ if (notificationsCreationData) {
+ void sendPushNotifs(notificationsCreationData);
+ }
dispatch({
type: opsProcessingFinishedActionType,
});
@@ -69,6 +78,7 @@
}
})();
}, [
+ sendPushNotifs,
queueFront,
dispatch,
processDBStoreOperations,
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,6 +1,7 @@
// @flow
import * as React from 'react';
+import uuid from 'uuid';
import {
preparePushNotifs,
@@ -10,36 +11,111 @@
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 type { MessageData } from '../types/message-types.js';
+import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js';
import type {
- EncryptedNotifUtilsAPI,
- SenderDeviceDescriptor,
+ TargetedAPNsNotification,
+ TargetedAndroidNotification,
+ TargetedWebNotification,
+ TargetedWNSNotification,
+ NotificationsCreationData,
} from '../types/notif-types.js';
+import { deviceToTunnelbrokerMessageTypes } from '../types/tunnelbroker/messages.js';
+import type {
+ TunnelbrokerAPNsNotif,
+ TunnelbrokerFCMNotif,
+ TunnelbrokerWebPushNotif,
+ 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';
-function usePreparePushNotifs(): (
- encryptedNotifsUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- messageDatas: $ReadOnlyArray<MessageData>,
+function apnsNotifToTunnelbrokerAPNsNotif(
+ targetedNotification: TargetedAPNsNotification,
+): TunnelbrokerAPNsNotif {
+ const {
+ deliveryID: deviceID,
+ notification: { headers, ...payload },
+ } = targetedNotification;
+
+ const newHeaders = {
+ ...headers,
+ 'apns-push-type': 'Alert',
+ };
+
+ return {
+ type: deviceToTunnelbrokerMessageTypes.TUNNELBROKER_APNS_NOTIF,
+ deviceID,
+ headers: JSON.stringify(newHeaders),
+ payload: JSON.stringify(payload),
+ clientMessageID: uuid.v4(),
+ };
+}
+
+function androidNotifToTunnelbrokerFCMNotif(
+ targetedNotification: TargetedAndroidNotification,
+): TunnelbrokerFCMNotif {
+ const {
+ deliveryID: deviceID,
+ notification: { data },
+ priority,
+ } = targetedNotification;
+
+ return {
+ type: deviceToTunnelbrokerMessageTypes.TUNNELBROKER_FCM_NOTIF,
+ deviceID,
+ clientMessageID: uuid.v4(),
+ data: JSON.stringify(data),
+ priority: priority === 'normal' ? 'NORMAL' : 'HIGH',
+ };
+}
+
+function webNotifToTunnelbrokerWebPushNotif(
+ targetedNotification: TargetedWebNotification,
+): TunnelbrokerWebPushNotif {
+ const { deliveryID: deviceID, notification } = targetedNotification;
+ return {
+ type: deviceToTunnelbrokerMessageTypes.TUNNELBROKER_WEB_PUSH_NOTIF,
+ deviceID,
+ clientMessageID: uuid.v4(),
+ payload: JSON.stringify(notification),
+ };
+}
+
+function wnsNotifToTunnelbrokerWNSNofif(
+ targetedNotification: TargetedWNSNotification,
+): TunnelbrokerWNSNotif {
+ const { deliveryID: deviceID, notification } = targetedNotification;
+ return {
+ type: deviceToTunnelbrokerMessageTypes.TUNNELBROKER_WNS_NOTIF,
+ deviceID,
+ clientMessageID: uuid.v4(),
+ payload: JSON.stringify(notification),
+ };
+}
+
+function useSendPushNotifs(): (
+ notifCreationData: NotificationsCreationData,
) => Promise<?PerUserTargetedNotifications> {
const rawMessageInfos = useSelector(state => state.messageStore.messages);
const thickRawThreadInfos = useSelector(thickRawThreadInfosSelector);
const auxUserInfos = useSelector(state => state.auxUserStore.auxUserInfos);
const userInfos = useSelector(state => state.userStore.userInfos);
-
const { getENSNames } = React.useContext(ENSCacheContext);
const getFCNames = React.useContext(NeynarClientContext)?.getFCNames;
-
const { createOlmSessionsWithPeer: olmSessionCreator } =
usePeerOlmSessionsCreatorContext();
+ const { sendNotif } = useTunnelbroker();
+ const { encryptedNotifUtilsAPI } = getConfig();
return React.useCallback(
- (
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- messageDatas: $ReadOnlyArray<MessageData>,
- ) => {
- return preparePushNotifs({
+ async (notifCreationData: NotificationsCreationData) => {
+ const deviceID = await getContentSigningKey();
+ const senderDeviceDescriptor = { senderDeviceID: deviceID };
+ const { messageDatas } = notifCreationData;
+
+ const pushNotifsPreparationInput = {
encryptedNotifUtilsAPI,
senderDeviceDescriptor,
olmSessionCreator,
@@ -50,9 +126,65 @@
userInfos,
getENSNames,
getFCNames,
- });
+ };
+
+ const preparedPushNotifs = await preparePushNotifs(
+ pushNotifsPreparationInput,
+ );
+
+ if (!preparedPushNotifs) {
+ return;
+ }
+
+ const sendPromises = [];
+ for (const userID in preparedPushNotifs) {
+ for (const notif of preparedPushNotifs[userID]) {
+ if (notif.targetedNotification.notification.encryptionFailed) {
+ continue;
+ }
+
+ let tunnelbrokerNotif;
+ if (notif.platform === 'ios' || notif.platform === 'macos') {
+ tunnelbrokerNotif = apnsNotifToTunnelbrokerAPNsNotif(
+ notif.targetedNotification,
+ );
+ } else if (notif.platform === 'android') {
+ tunnelbrokerNotif = androidNotifToTunnelbrokerFCMNotif(
+ notif.targetedNotification,
+ );
+ } else if (notif.platform === 'web') {
+ tunnelbrokerNotif = webNotifToTunnelbrokerWebPushNotif(
+ notif.targetedNotification,
+ );
+ } else if (notif.platform === 'windows') {
+ tunnelbrokerNotif = wnsNotifToTunnelbrokerWNSNofif(
+ notif.targetedNotification,
+ );
+ } else {
+ continue;
+ }
+
+ sendPromises.push(
+ (async () => {
+ try {
+ await sendNotif(tunnelbrokerNotif);
+ } catch (e) {
+ console.log(
+ `Failed to send notification to device: ${
+ tunnelbrokerNotif.deviceID
+ }. Details: ${getMessageForException(e) ?? ''}`,
+ );
+ }
+ })(),
+ );
+ }
+ }
+
+ await Promise.all(sendPromises);
},
[
+ sendNotif,
+ encryptedNotifUtilsAPI,
olmSessionCreator,
rawMessageInfos,
thickRawThreadInfos,
@@ -64,4 +196,4 @@
);
}
-export { usePreparePushNotifs };
+export { useSendPushNotifs };
diff --git a/lib/types/db-ops-types.js b/lib/types/db-ops-types.js
--- a/lib/types/db-ops-types.js
+++ b/lib/types/db-ops-types.js
@@ -1,5 +1,6 @@
// @flow
+import type { NotificationsCreationData } from './notif-types.js';
import type { StoreOperations } from './store-ops-types.js';
export type MessageSourceMetadata = {
@@ -15,6 +16,9 @@
| {
+ops: StoreOperations,
+dmOpID?: string,
+ }
+ | {
+ +notificationsCreationData?: NotificationsCreationData,
};
export type DBOpsStore = {
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
@@ -3,7 +3,7 @@
import type { EncryptResult } from '@commapp/olm';
import t, { type TInterface, type TUnion } from 'tcomb';
-import type { Platform } from './device-types.js';
+import type { MessageData } from './message-types.js';
import type { EntityText, ThreadEntity } from '../utils/entity-text.js';
import { tShape } from '../utils/validation-utils.js';
@@ -28,6 +28,10 @@
prefix: t.maybe(t.String),
});
+export type NotificationsCreationData = {
+ +messageDatas: $ReadOnlyArray<MessageData>,
+};
+
export type SenderDeviceDescriptor =
| { +keyserverID: string }
| { +senderDeviceID: string };
@@ -362,14 +366,14 @@
+blobHolder?: string,
};
-export type TargetedNotificationWithPlatform = {
- +platform: Platform,
- +targetedNotification:
- | TargetedAPNsNotification
- | TargetedWNSNotification
- | TargetedWebNotification
- | TargetedAndroidNotification,
-};
+export type TargetedNotificationWithPlatform =
+ | {
+ +platform: 'ios' | 'macos',
+ +targetedNotification: TargetedAPNsNotification,
+ }
+ | { +platform: 'android', +targetedNotification: TargetedAndroidNotification }
+ | { +platform: 'web', +targetedNotification: TargetedWebNotification }
+ | { +platform: 'windows', +targetedNotification: TargetedWNSNotification };
export type EncryptedNotifUtilsAPI = {
+encryptSerializedNotifPayload: (
diff --git a/lib/types/tunnelbroker/messages.js b/lib/types/tunnelbroker/messages.js
--- a/lib/types/tunnelbroker/messages.js
+++ b/lib/types/tunnelbroker/messages.js
@@ -45,6 +45,8 @@
ANONYMOUS_INITIALIZATION_MESSAGE: 'AnonymousInitializationMessage',
TUNNELBROKER_APNS_NOTIF: 'APNsNotif',
TUNNELBROKER_FCM_NOTIF: 'FCMNotif',
+ TUNNELBROKER_WEB_PUSH_NOTIF: 'WebPushNotif',
+ TUNNELBROKER_WNS_NOTIF: 'WNSNotif',
MESSAGE_TO_DEVICE_REQUEST: 'MessageToDeviceRequest',
MESSAGE_RECEIVE_CONFIRMATION: 'MessageReceiveConfirmation',
MESSAGE_TO_TUNNELBROKER_REQUEST: 'MessageToTunnelbrokerRequest',
diff --git a/lib/utils/__mocks__/config.js b/lib/utils/__mocks__/config.js
--- a/lib/utils/__mocks__/config.js
+++ b/lib/utils/__mocks__/config.js
@@ -45,6 +45,12 @@
getOutboundP2PMessagesByID: jest.fn(),
searchMessages: jest.fn(),
},
+ encryptedNotifUtilsAPI: {
+ encryptSerializedNotifPayload: jest.fn(),
+ uploadLargeNotifPayload: jest.fn(),
+ getEncryptedNotifHash: jest.fn(),
+ getNotifByteSize: jest.fn(),
+ },
});
const hasConfig = (): boolean => true;
diff --git a/lib/utils/config.js b/lib/utils/config.js
--- a/lib/utils/config.js
+++ b/lib/utils/config.js
@@ -8,6 +8,7 @@
import type { RecoveryActionSource } from '../types/account-types.js';
import type { OlmAPI } from '../types/crypto-types.js';
import type { PlatformDetails } from '../types/device-types.js';
+import type { EncryptedNotifUtilsAPI } from '../types/notif-types.js';
import type { SQLiteAPI } from '../types/sqlite-types.js';
import type { DispatchActionPromise } from '../utils/redux-promise-utils.js';
@@ -29,6 +30,7 @@
+authoritativeKeyserverID: string,
+olmAPI: OlmAPI,
+sqliteAPI: SQLiteAPI,
+ +encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
};
let registeredConfig: ?Config = null;
diff --git a/native/config.js b/native/config.js
--- a/native/config.js
+++ b/native/config.js
@@ -8,6 +8,7 @@
import { authoritativeKeyserverID } from './authoritative-keyserver.js';
import { olmAPI } from './crypto/olm-api.js';
import { sqliteAPI } from './database/sqlite-api.js';
+import encryptedNotifUtilsAPI from './push/encrypted-notif-utils-api.js';
import { persistConfig, codeVersion } from './redux/persist.js';
registerConfig({
@@ -22,4 +23,5 @@
authoritativeKeyserverID,
olmAPI,
sqliteAPI,
+ encryptedNotifUtilsAPI,
});
diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -70,6 +70,7 @@
import SettingsSwitcher from './navigation-panels/settings-switcher.react.js';
import Topbar from './navigation-panels/topbar.react.js';
import useBadgeHandler from './push-notif/badge-handler.react.js';
+import encryptedNotifUtilsAPI from './push-notif/encrypted-notif-utils-api.js';
import { PushNotificationsHandler } from './push-notif/push-notifs-handler.js';
import { updateNavInfoActionType } from './redux/action-types.js';
import DisconnectedBar from './redux/disconnected-bar.js';
@@ -124,6 +125,7 @@
authoritativeKeyserverID,
olmAPI,
sqliteAPI,
+ encryptedNotifUtilsAPI,
});
const versionBroadcast = new BroadcastChannel('comm_version');

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 25, 9:36 AM (20 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2579217
Default Alt Text
D12882.id43517.diff (13 KB)

Event Timeline