Page MenuHomePhabricator

D6894.diff
No OneTemporary

D6894.diff

diff --git a/desktop/flow-typed/npm/electron_v22.0.0.js b/desktop/flow-typed/npm/electron_v22.0.0.js
--- a/desktop/flow-typed/npm/electron_v22.0.0.js
+++ b/desktop/flow-typed/npm/electron_v22.0.0.js
@@ -297,6 +297,75 @@
): Menu;
}
+ declare export var pushNotifications: PushNotifications;
+ declare class PushNotifications {
+ registerForAPNSNotifications(): Promise<string>;
+ unregisterForAPNSNotifications(): void;
+
+ on<T: $Keys<PushNotificationsEvents>>(
+ event: T,
+ listener: $ElementType<PushNotificationsEvents, T>,
+ ): void;
+ removeListener<T: $Keys<PushNotificationsEvents>>(
+ event: T,
+ listener: $ElementType<PushNotificationsEvents, T>,
+ ): void;
+ }
+ declare type PushNotificationsEvents = {
+ 'received-apns-notification': (
+ event: Event,
+ userInfo: { +[string]: mixed },
+ ) => void,
+ };
+
+ declare export class Notification {
+ constructor(options?: {
+ +title?: string,
+ +subtitle?: string,
+ +body?: string,
+ +silent?: boolean,
+ +hasReply?: boolean,
+ +timeoutType?: 'default' | 'never',
+ +replyPlaceholder?: string,
+ +sound?: string,
+ +urgency?: 'normal' | 'critical' | 'low',
+ +actions?: NotificationAction[],
+ +closeButtonText?: string,
+ +toastXml?: string,
+ }): void;
+ static isSupported(): boolean;
+ show(): void;
+ close(): void;
+ +title: string;
+ +subtitle: string;
+ +body: string;
+ +replyPlaceholder: string;
+ +sound: string;
+ +closeButtonText: string;
+ +silent: boolean;
+ +hasReply: boolean;
+ +urgency: 'normal' | 'critical' | 'low';
+ +timeoutType: 'default' | 'never';
+ +toastXml: string;
+
+ on<T: $Keys<NotificationEvents>>(
+ event: T,
+ listener: $ElementType<NotificationEvents, T>,
+ ): void;
+ removeListener<T: $Keys<NotificationEvents>>(
+ event: T,
+ listener: $ElementType<NotificationEvents, T>,
+ ): void;
+ }
+ declare type NotificationEvents = {
+ show: (event: Event) => void,
+ click: (event: Event) => void,
+ close: (event: Event) => void,
+ reply: (event: Event, reply: string) => void,
+ action: (event: Event, index: number) => void,
+ failed: (event: Event, error: string) => void,
+ };
+
declare export var shell: Shell;
declare type Shell = {
openExternal(
@@ -402,6 +471,8 @@
ipcMain,
systemPreferences,
autoUpdater,
+ pushNotifications,
+ Notification,
} from 'electron';
}
diff --git a/desktop/src/main.js b/desktop/src/main.js
--- a/desktop/src/main.js
+++ b/desktop/src/main.js
@@ -15,6 +15,10 @@
import { initAutoUpdate } from './auto-update.js';
import { handleSquirrelEvent } from './handle-squirrel-event.js';
+import {
+ listenForNotifications,
+ registerForNotifications,
+} from './push-notifications.js';
const isDev = process.env.ENV === 'dev';
const url = isDev ? 'http://localhost:3000/comm/' : 'https://web.comm.app';
@@ -83,7 +87,8 @@
Menu.setApplicationMenu(menu);
};
-const createMainWindow = () => {
+let mainWindow = null;
+const createMainWindow = (urlPath?: string) => {
const win = new BrowserWindow({
show: false,
width: 1300,
@@ -149,6 +154,7 @@
autoUpdater.on('update-downloaded', updateDownloaded);
win.on('closed', () => {
+ mainWindow = null;
ipcMain.removeListener('clear-history', clearHistory);
ipcMain.removeListener('double-click-top-bar', doubleClickTopBar);
autoUpdater.removeListener('update-downloaded', updateDownloaded);
@@ -165,7 +171,8 @@
win.webContents.insertCSS(css);
})();
- win.loadURL(url);
+ win.loadURL(url + (urlPath ?? ''));
+ mainWindow = win;
return win;
};
@@ -204,10 +211,10 @@
return win;
};
-const show = () => {
+const show = (urlPath?: string) => {
const splash = createSplashWindow();
const error = createErrorWindow();
- const main = createMainWindow();
+ const main = createMainWindow(urlPath);
let loadedSuccessfully = true;
main.webContents.on('did-fail-load', () => {
@@ -235,6 +242,13 @@
}
main.show();
+
+ if (app.isPackaged) {
+ (async () => {
+ const token = await registerForNotifications();
+ main.webContents.send('on-device-token-registered', token);
+ })();
+ }
}
});
};
@@ -252,6 +266,15 @@
} catch (error) {
console.error(error);
}
+ listenForNotifications(threadID => {
+ if (mainWindow) {
+ mainWindow.webContents.send('on-notification-clicked', {
+ threadID,
+ });
+ } else {
+ show(`chat/thread/${threadID}/`);
+ }
+ });
}
ipcMain.on('set-badge', (event, value) => {
diff --git a/desktop/src/push-notifications.js b/desktop/src/push-notifications.js
new file mode 100644
--- /dev/null
+++ b/desktop/src/push-notifications.js
@@ -0,0 +1,49 @@
+// @flow
+
+// eslint-disable-next-line import/extensions
+import { pushNotifications, Notification } from 'electron/main';
+
+async function registerForNotifications(): Promise<?string> {
+ if (process.platform !== 'darwin') {
+ return null;
+ }
+
+ try {
+ const token = await pushNotifications.registerForAPNSNotifications();
+ return token;
+ } catch (err) {
+ console.error(err);
+ }
+
+ return null;
+}
+
+function listenForNotifications(handleClick: (threadID: string) => void) {
+ if (process.platform !== 'darwin') {
+ return;
+ }
+ pushNotifications.on('received-apns-notification', (event, userInfo) => {
+ if (
+ typeof userInfo.title !== 'string' ||
+ typeof userInfo.body !== 'string' ||
+ typeof userInfo.threadID !== 'string'
+ ) {
+ console.error(
+ 'Notification must contain a string title, body and threadID',
+ );
+ return;
+ }
+ const { title, body, threadID } = userInfo;
+
+ const notif = new Notification({
+ title,
+ body,
+ });
+ notif.on('click', () => {
+ handleClick(threadID);
+ });
+ notif.show();
+ });
+}
+
+export { listenForNotifications, registerForNotifications };

File Metadata

Mime Type
text/plain
Expires
Wed, Jan 8, 11:11 AM (1 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2816891
Default Alt Text
D6894.diff (6 KB)

Event Timeline