Browse Source

use node js main process to show notification

MaysWind 3 years ago
parent
commit
81ff756d23

+ 3 - 0
app/scripts/controllers/settings-ariang.js

@@ -39,6 +39,7 @@
                 config.afterMainWindowClosed = 'minimize-to-tray';
             }
 
+            config.notificationSound = originalConfig.notificationSound;
             config.execCommandOnStartup = originalConfig.execCommandOnStartup;
             config.execCommandArgumentsOnStartup = originalConfig.execCommandArgumentsOnStartup;
 
@@ -228,6 +229,7 @@
 
         $scope.setBrowserNotificationSound = function (value) {
             ariaNgSettingService.setBrowserNotificationSound(value);
+            ariaNgNativeElectronService.setNotificationSound(value);
         };
 
         $scope.setBrowserNotificationFrequency = function (value) {
@@ -380,6 +382,7 @@
             if (settingsObj) {
                 ariaNgCommonService.confirm('Confirm Import', 'Are you sure you want to import all settings?', 'warning', function () {
                     ariaNgSettingService.importAllOptions(settingsObj);
+                    ariaNgNativeElectronService.setNotificationSound(!!settingsObj.browserNotificationSound);
                     $window.location.reload();
                 });
             }

+ 12 - 11
app/scripts/core/root.js

@@ -639,17 +639,18 @@
             });
         });
 
-        aria2TaskService.onTaskCompleted(function (event) {
-            ariaNgNotificationService.notifyTaskComplete(event.task);
-        });
-
-        aria2TaskService.onBtTaskCompleted(function (event) {
-            ariaNgNotificationService.notifyBtTaskComplete(event.task);
-        });
-
-        aria2TaskService.onTaskErrorOccur(function (event) {
-            ariaNgNotificationService.notifyTaskError(event.task);
-        });
+        // AriaNg Native shows notification via main process
+        // aria2TaskService.onTaskCompleted(function (event) {
+        //     ariaNgNotificationService.notifyTaskComplete(event.task);
+        // });
+        //
+        // aria2TaskService.onBtTaskCompleted(function (event) {
+        //     ariaNgNotificationService.notifyBtTaskComplete(event.task);
+        // });
+        //
+        // aria2TaskService.onTaskErrorOccur(function (event) {
+        //     ariaNgNotificationService.notifyTaskError(event.task);
+        // });
 
         $rootScope.$on('$locationChangeStart', function (event) {
             ariaNgCommonService.closeAllDialogs();

+ 17 - 0
app/scripts/services/ariaNgNativeElectronService.js

@@ -121,12 +121,26 @@
                     Exit: ariaNgLocalizationService.getLocalizedText('tray.Exit')
                 });
             },
+            setSystemNotificationTemplate: function () {
+                invokeMainProcessMethod('render-update-system-notification-templates', {
+                    onDownloadComplete: {
+                        title: ariaNgLocalizationService.getLocalizedText('Download Completed')
+                    },
+                    onBtDownloadComplete: {
+                        title: ariaNgLocalizationService.getLocalizedText('BT Download Completed')
+                    },
+                    onDownloadError: {
+                        title: ariaNgLocalizationService.getLocalizedText('Download Error')
+                    },
+                });
+            },
             setTrayToolTip: function (value) {
                 invokeMainProcessMethod('render-update-tray-tip', value);
             },
             setMainWindowLanguage: function () {
                 this.setApplicationMenu();
                 this.setTrayMenu();
+                this.setSystemNotificationTemplate();
             },
             getNativeConfig: function () {
                 var config = invokeMainProcessMethodSync('render-sync-get-native-config');
@@ -148,6 +162,9 @@
             setMinimizedToTray: function (value) {
                 invokeMainProcessMethod('render-set-native-config-minimized-to-tray', value);
             },
+            setNotificationSound: function (value) {
+                invokeMainProcessMethod('render-set-native-config-notification-sound', value);
+            },
             setExecCommandOnStartup: function (value) {
                 invokeMainProcessMethod('render-set-native-config-exec-command-on-startup', value);
             },

+ 41 - 0
main/components/notification.js

@@ -4,6 +4,45 @@ const electron = require('electron');
 
 const localfs = require('../lib/localfs');
 
+const notificationMessageTemplates = {
+    onDownloadComplete: {
+        title: 'Download Completed',
+        text: '${taskname}'
+    },
+    onBtDownloadComplete: {
+        title: 'BT Download Completed',
+        text: '${taskname}'
+    },
+    onDownloadError: {
+        title: 'Download Error',
+        text: '${taskname}'
+    }
+};
+
+let setNotificationMessageTemplates = function (templates) {
+    for (const eventName in notificationMessageTemplates) {
+        if (!notificationMessageTemplates.hasOwnProperty(eventName)) {
+            continue;
+        }
+
+        if (!templates[eventName]) {
+            continue;
+        }
+
+        if (templates[eventName].title) {
+            notificationMessageTemplates[eventName].title = templates[eventName].title;
+        }
+
+        if (templates[eventName].text) {
+            notificationMessageTemplates[eventName].text = templates[eventName].text;
+        }
+    }
+};
+
+let getNotificationMessageTemplate = function (eventName) {
+    return notificationMessageTemplates[eventName];
+};
+
 let showNotification = function (title, body, silent) {
     const notification = new electron.Notification({
         title: title,
@@ -16,5 +55,7 @@ let showNotification = function (title, body, silent) {
 };
 
 module.exports = {
+    setNotificationMessageTemplates: setNotificationMessageTemplates,
+    getNotificationMessageTemplate: getNotificationMessageTemplate,
     showNotification: showNotification
 };

+ 4 - 0
main/config/config.js

@@ -26,6 +26,9 @@ const userSettingsSchema = {
     pos_y: {
         type: 'number'
     },
+    notificationSound: {
+        type: 'boolean'
+    },
     execCommandOnStartup: {
         type: 'string'
     },
@@ -51,6 +54,7 @@ let config = {
     maximized: !!userSettingsStore.get('maximized'),
     defaultPosition: userSettingsStore.get('defaultPosition') || 'last-position',
     minimizedToTray: userSettingsStore.get('minimizedToTray', true),
+    notificationSound: userSettingsStore.get('notificationSound', true),
     execCommandOnStartup: userSettingsStore.get('execCommandOnStartup'),
     execCommandArgumentsOnStartup: userSettingsStore.get('execCommandArgumentsOnStartup'),
     execDetachedCommandOnStartup: userSettingsStore.get('execDetachedCommandOnStartup', false),

+ 10 - 0
main/config/constants.js

@@ -9,6 +9,14 @@ const ariaNgPageLocations = {
     AriaNgSettings: '/settings/ariang'
 };
 
+const ariaNgNativeConstants = {
+    appPrefix: 'AriaNg-Native',
+};
+
+const aria2Constants = {
+    rpcServiceName: 'aria2'
+};
+
 const supportedFileExtensions = {
     '.torrent': 'torrent',
     '.meta4': 'metalink',
@@ -18,5 +26,7 @@ const supportedFileExtensions = {
 module.exports = {
     startupCommandConstants: startupCommandConstants,
     ariaNgPageLocations: ariaNgPageLocations,
+    ariaNgNativeConstants: ariaNgNativeConstants,
+    aria2Constants: aria2Constants,
     supportedFileExtensions: supportedFileExtensions
 }

+ 12 - 0
main/ipc/main-process.js

@@ -11,6 +11,7 @@ const http = require('../lib/http');
 const websocket = require('../lib/websocket');
 const localfs = require('../lib/localfs');
 const bittorrent = require('../lib/bittorrent');
+const taskEvent = require('../task/event');
 
 const shell = electron.shell;
 const dialog = electron.dialog;
@@ -102,10 +103,15 @@ ipcMain.on('render-update-tray-tip', (event, tooltip) => {
     tray.setToolTip(tooltip);
 });
 
+ipcMain.on('render-update-system-notification-templates', (event, template) => {
+    taskEvent.setNotificationMessageTemplates(template);
+});
+
 ipcMain.on('render-sync-get-native-config', (event) => {
     event.returnValue = {
         defaultPosition: config.defaultPosition,
         minimizedToTray: config.minimizedToTray,
+        notificationSound: config.notificationSound,
         execCommandOnStartup: config.execCommandOnStartup,
         execCommandArgumentsOnStartup: config.execCommandArgumentsOnStartup,
         execDetachedCommandOnStartup: config.execDetachedCommandOnStartup
@@ -122,6 +128,11 @@ ipcMain.on('render-set-native-config-minimized-to-tray', (event, value) => {
     config.save('minimizedToTray');
 });
 
+ipcMain.on('render-set-native-config-notification-sound', (event, value) => {
+    config.notificationSound = !!value;
+    config.save('notificationSound');
+});
+
 ipcMain.on('render-set-native-config-exec-command-on-startup', (event, value) => {
     config.execCommandOnStartup = value;
     config.save('execCommandOnStartup');
@@ -176,6 +187,7 @@ ipcMain.on('render-connect-websocket', (event, rpcUrl, options) => {
             }
         },
         function onMessage(context) {
+            taskEvent.nativeProcessTaskMessage(context);
             try {
                 core.mainWindow.webContents.send('on-main-websocket-message', {
                     success: context.success,

+ 17 - 2
main/lib/utils.js

@@ -1,5 +1,7 @@
 'use strict';
 
+const constants = require('../config/constants');
+
 let isObject = function (value) {
     return value !== null && typeof value === 'object';
 }
@@ -33,8 +35,21 @@ let copyObjectTo = function (from, to) {
     return to;
 }
 
+let base64Encode = function (str) {
+    return new Buffer(str).toString('base64');
+};
+
+let generateUniqueId = function () {
+    const sourceId = constants.ariaNgNativeConstants.appPrefix + '_' + Math.round(new Date().getTime() / 1000) + '_' + Math.random();
+    const hashedId = base64Encode(sourceId);
+
+    return hashedId;
+};
+
 module.exports = {
     isObject: isObject,
     isArray: isArray,
-    copyObjectTo: copyObjectTo
-}
+    copyObjectTo: copyObjectTo,
+    generateUniqueId: generateUniqueId
+};
+

+ 20 - 0
main/lib/websocket.js

@@ -3,10 +3,12 @@
 const WebSocket = require('ws');
 
 const ipcRender = require('../ipc/render-proecss');
+const utils = require('../lib/utils');
 
 let wsClient = null;
 let sendQueue = [];
 let pendingReconnect = null;
+let requestIdMap = {};
 
 let fireSendQueue = function () {
     while (sendQueue.length && wsClient && wsClient.readyState === WebSocket.OPEN) {
@@ -151,6 +153,23 @@ let send = function (requestContext) {
     return deferred.promise;
 };
 
+let request = function () {
+    const uniqueId = utils.generateUniqueId();
+    const deferred = {};
+
+    deferred.promise = new Promise(function (resolve, reject) {
+        deferred.resolve = resolve
+        deferred.reject = reject
+    });
+
+    // TODO
+    requestIdMap[uniqueId] = {
+
+    };
+
+    return deferred.promise;
+};
+
 let getReadyState = function () {
     if (!wsClient) {
         return null;
@@ -164,5 +183,6 @@ module.exports = {
     connect: connect,
     reconnect: reconnect,
     send: send,
+    request: request,
     getReadyState: getReadyState
 };

+ 69 - 0
main/task/event.js

@@ -0,0 +1,69 @@
+'use strict';
+
+const constants = require('../config/constants');
+const config = require('../config/config');
+const notification = require('../components/notification');
+const ipcRender = require('../ipc/render-proecss');
+
+const getFinalText = function (textTemplate, taskContext) {
+    let finalText = textTemplate;
+
+    finalText = finalText.replace('${taskname}', taskContext.taskName || '');
+
+    return finalText;
+};
+
+let nativeProcessTaskEvent = function (gid, template) {
+    const taskContext = {
+        taskName: gid
+    };
+
+    const title = getFinalText(template.title, taskContext);
+    const body = getFinalText(template.text, taskContext);
+
+    notification.showNotification(title, body, !config.notificationSound);
+};
+
+let nativeProcessTaskMessage = function (context) {
+    const message = context.message;
+    let content = null;
+
+    try {
+        content = JSON.parse(message);
+    } catch (ex) {
+        ipcRender.notifyRenderProcessLogError('[task/event.nativeProcessTaskMessage] cannot parse message json, message=' + message, ex);
+        return;
+    }
+
+    if (!content || content.id || !content.method) { // Not Event
+        return;
+    }
+
+    if (!content.params || !content.params[0] || !content.params[0].gid) {
+        ipcRender.notifyRenderProcessLogError('[task/event.nativeProcessTaskMessage] content params is invalid', content);
+        return;
+    }
+
+    try {
+        let methodName = content.method;
+
+        if (methodName.indexOf(constants.aria2Constants.rpcServiceName) !== 0) {
+            ipcRender.notifyRenderProcessLogError('[task/event.nativeProcessTaskMessage] event method name is invalid', content);
+            return;
+        }
+
+        methodName = methodName.substring(constants.aria2Constants.rpcServiceName.length + 1);
+
+        const template = notification.getNotificationMessageTemplate(methodName);
+
+        if (template) {
+            nativeProcessTaskEvent(content.params[0].gid, template);
+        }
+    } catch (ex) {
+        ipcRender.notifyRenderProcessLogError('[task/event.nativeProcessTaskMessage] cannot process task event, message=' + message, ex);
+    }
+};
+
+module.exports = {
+    nativeProcessTaskMessage: nativeProcessTaskMessage
+};