Browse Source

feat: persist notification with onclick in Chrome

tophf 2 years ago
parent
commit
79a5fb4f2a

+ 6 - 2
src/background/utils/db.js

@@ -382,8 +382,12 @@ function reportBadScripts(ids) {
 
 export function notifyToOpenScripts(title, text, ids) {
   // FF doesn't show notifications of type:'list' so we'll use `text` everywhere
-  commands.Notification({ title, text }, undefined, isClick => {
-    if (isClick) ids.forEach(id => commands.OpenEditor(id));
+  commands.Notification({
+    title,
+    text,
+    onclick() {
+      ids.forEach(id => commands.OpenEditor(id));
+    },
   });
 }
 

+ 8 - 4
src/background/utils/notifications.js

@@ -1,18 +1,22 @@
 import { i18n, defaultImage, sendTabCmd, trueJoin } from '@/common';
+import ua from '@/common/ua';
 import { addPublicCommands } from './message';
 
 const openers = {};
 
 addPublicCommands({
   /** @return {Promise<string>} */
-  async Notification({ image, text, title }, src, bgCallback) {
+  async Notification({ image, text, title, onclick }, src) {
     const notificationId = await browser.notifications.create({
       type: 'basic',
       title: [title, IS_FIREFOX && i18n('extName')]::trueJoin(' - '), // Chrome already shows the name
       message: text,
       iconUrl: image || defaultImage,
+      ...!IS_FIREFOX && {
+        requireInteraction: !!onclick,
+      },
     });
-    const op = bgCallback || src && [src.tab.id, src.frameId];
+    const op = isFunction(onclick) ? onclick : src && [src.tab.id, src.frameId];
     if (op) openers[notificationId] = op;
     return notificationId;
   },
@@ -32,8 +36,8 @@ browser.notifications.onClosed.addListener((id) => {
 
 function notifyOpener(id, isClick) {
   const op = openers[id];
-  if (isFunction(op)) {
-    op(isClick);
+  if (isClick && isFunction(op)) {
+    op();
   } else if (op) {
     sendTabCmd(op[0], isClick ? 'NotificationClick' : 'NotificationClose', id, {
       frameId: op[1],

+ 3 - 5
src/injected/content/notifications.js

@@ -2,6 +2,7 @@ import bridge, { addBackgroundHandlers, addHandlers } from './bridge';
 import { sendCmd } from './util';
 
 const notifications = createNullObj();
+const relay = (msg, n) => n && bridge.post(msg, n.id, n.realm) && n;
 
 addHandlers({
   async Notification(options, realm) {
@@ -20,13 +21,10 @@ addHandlers({
 
 addBackgroundHandlers({
   NotificationClick(nid) {
-    const n = notifications[nid];
-    if (n) bridge.post('NotificationClicked', n.id, n.realm);
+    relay('NotificationClicked', notifications[nid]);
   },
   NotificationClose(nid) {
-    const n = notifications[nid];
-    if (n) {
-      bridge.post('NotificationClosed', n.id, n.realm);
+    if (relay('NotificationClosed', notifications[nid])) {
       delete notifications[nid];
     }
   },

+ 2 - 17
src/injected/web/gm-api.js

@@ -3,7 +3,7 @@ import bridge from './bridge';
 import store from './store';
 import { onTabCreate } from './tabs';
 import { onRequestCreate, onRequestInitError } from './requests';
-import { onNotificationCreate } from './notifications';
+import { createNotification } from './notifications';
 import { decodeValue, dumpValue, loadValues, changeHooks } from './gm-values';
 import { jsonDump } from './util';
 
@@ -167,22 +167,7 @@ export const GM_API = {
       options.url = url;
       return onTabCreate(options);
     },
-    GM_notification(text, title, image, onclick) {
-      const options = isObject(text) ? text : {
-        __proto__: null,
-        text,
-        title,
-        image,
-        onclick,
-      };
-      if (!options.text) {
-        throw new SafeError('GM_notification: `text` is required!');
-      }
-      const id = onNotificationCreate(options);
-      return {
-        remove: () => bridge.send('RemoveNotification', id),
-      };
-    },
+    GM_notification: createNotification,
     GM_setClipboard(data, type) {
       bridge.post('SetClipboard', { data, type });
     },

+ 24 - 11
src/injected/web/notifications.js

@@ -2,7 +2,6 @@ import bridge, { addHandlers } from './bridge';
 
 let lastId = 0;
 const notifications = createNullObj();
-
 addHandlers({
   NotificationClicked(id) {
     notifications[id]?.onclick?.();
@@ -16,14 +15,28 @@ addHandlers({
   },
 });
 
-export function onNotificationCreate(options) {
-  lastId += 1;
-  notifications[lastId] = options;
-  bridge.post('Notification', {
-    id: lastId,
-    text: options.text,
-    title: options.title,
-    image: options.image,
-  });
-  return lastId;
+export function createNotification(text, title, image, onclick) {
+  let options;
+  if (isObject(text)) {
+    options = nullObjFrom(text);
+    text = options.text;
+  } else {
+    options = createNullObj();
+    options.text = text;
+    options.title = title;
+    options.image = image;
+    options.onclick = onclick;
+  }
+  if (!text) throw new SafeError('Notification `text` is required!');
+  const id = ++lastId;
+  const msg = createNullObj();
+  for (const k in options) {
+    msg[k] = isFunction(options[k]) ? true : options[k];
+  }
+  msg.id = id;
+  notifications[id] = options;
+  bridge.post('Notification', msg);
+  return {
+    remove: () => bridge.send('RemoveNotification', id),
+  };
 }