mirror of
https://github.com/kopia/kopia.git
synced 2026-04-23 23:48:51 -04:00
feat(kopiaui): support for desktop notifications (#4352)
This commit is contained in:
@@ -3,6 +3,7 @@ import pkg from "electron-updater";
|
||||
const autoUpdater = pkg.autoUpdater;
|
||||
import { iconsPath, publicPath, selectByOS } from './utils.js';
|
||||
import { toggleLaunchAtStartup, willLaunchAtStartup, refreshWillLaunchAtStartup } from './auto-launch.js';
|
||||
import { setNotificationLevel, getNotificationLevel } from './notifications.js';
|
||||
import { serverForRepo } from './server.js';
|
||||
import { loadConfigs, allConfigs, deleteConfigIfDisconnected, addNewConfig, configDir, isFirstRun, isPortableConfig } from './config.js';
|
||||
|
||||
@@ -223,6 +224,7 @@ ipcMain.handle('browse-dir', async (_event, path) => {
|
||||
|
||||
ipcMain.on('server-status-updated', updateTrayContextMenu);
|
||||
ipcMain.on('launch-at-startup-updated', updateTrayContextMenu);
|
||||
ipcMain.on('notification-config-updated', updateTrayContextMenu);
|
||||
|
||||
let updateAvailableInfo = null;
|
||||
let updateDownloadStatusInfo = "";
|
||||
@@ -466,8 +468,41 @@ app.on('ready', () => {
|
||||
}
|
||||
})
|
||||
|
||||
function showRepoNotification(e) {
|
||||
const nl = getNotificationLevel();
|
||||
if (nl === 0) {
|
||||
// notifications disabled
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.severity < 10 && nl === 1) {
|
||||
// non-important notifications disabled.
|
||||
return;
|
||||
}
|
||||
|
||||
let urgency = "normal";
|
||||
|
||||
if (e.severity < 0) {
|
||||
urgency = "low";
|
||||
} else if (e.severity >= 10) { // warnings and errors
|
||||
urgency = "critical";
|
||||
} else {
|
||||
urgency = "normal";
|
||||
}
|
||||
|
||||
const notification = new Notification({
|
||||
title: e.notification.subject,
|
||||
body: e.notification.body,
|
||||
urgency: urgency
|
||||
});
|
||||
|
||||
notification.on('click', () => showRepoWindow(e.repositoryID));
|
||||
notification.show();
|
||||
}
|
||||
|
||||
ipcMain.addListener('config-list-updated-event', () => updateTrayContextMenu());
|
||||
ipcMain.addListener('status-updated-event', () => updateTrayContextMenu());
|
||||
ipcMain.addListener('repo-notification-event', showRepoNotification);
|
||||
|
||||
function addAnotherRepository() {
|
||||
const repoID = addNewConfig();
|
||||
@@ -530,6 +565,8 @@ function updateTrayContextMenu() {
|
||||
autoUpdateMenuItems.push({ label: "KopiaUI is up-to-date: " + app.getVersion(), enabled: false });
|
||||
}
|
||||
|
||||
const nl = getNotificationLevel();
|
||||
|
||||
let template = defaultReposTemplates.concat(additionalReposTemplates).concat([
|
||||
{ type: 'separator' },
|
||||
{ label: 'Connect To Another Repository...', click: addAnotherRepository },
|
||||
@@ -538,6 +575,11 @@ function updateTrayContextMenu() {
|
||||
]).concat(autoUpdateMenuItems).concat([
|
||||
{ type: 'separator' },
|
||||
{ label: 'Launch At Startup', type: 'checkbox', click: toggleLaunchAtStartup, checked: willLaunchAtStartup() },
|
||||
{ label: 'Notifications', type: 'submenu', submenu: [
|
||||
{ label: 'Enabled', type: 'radio', click: () => setNotificationLevel(2), checked: nl === 2 },
|
||||
{ label: 'Warnings And Errors', type: 'radio', click: () => setNotificationLevel(1), checked: nl === 1 },
|
||||
{ label: 'Disabled', type: 'radio', click: () => setNotificationLevel(0), checked: nl === 0 },
|
||||
] },
|
||||
{ label: 'Quit', role: 'quit' },
|
||||
]);
|
||||
|
||||
|
||||
35
app/public/notifications.js
Normal file
35
app/public/notifications.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { configDir } from './config.js';
|
||||
|
||||
const path = await import('path');
|
||||
const fs = await import('fs');
|
||||
|
||||
const LevelDisabled = 0;
|
||||
const LevelDefault = 1;
|
||||
const LevelAll = 2;
|
||||
|
||||
let level = -1;
|
||||
|
||||
export function getNotificationLevel() {
|
||||
if (level === -1) {
|
||||
try {
|
||||
const cfg = fs.readFileSync(path.join(configDir(), 'notifications.json'));
|
||||
return JSON.parse(cfg).level;
|
||||
} catch (e) {
|
||||
level = LevelDefault;
|
||||
}
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
export function setNotificationLevel(l) {
|
||||
level = l;
|
||||
if (level < LevelDisabled || level > LevelAll) {
|
||||
level = LevelDefault;
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(configDir(), 'notifications.json'), JSON.stringify({ level: l }));
|
||||
|
||||
ipcMain.emit('notification-config-updated');
|
||||
}
|
||||
@@ -41,9 +41,11 @@ function newServerForRepo(repoID) {
|
||||
'--random-server-control-password',
|
||||
'--tls-generate-cert',
|
||||
'--async-repo-connect',
|
||||
'--error-notifications=always',
|
||||
'--kopiaui-notifications', // will print notification JSON to stderr
|
||||
'--shutdown-on-stdin', // shutdown the server when parent dies
|
||||
'--address=127.0.0.1:0');
|
||||
|
||||
|
||||
|
||||
args.push("--config-file", path.resolve(configDir(), repoID + ".config"));
|
||||
if (isPortableConfig()) {
|
||||
@@ -67,7 +69,7 @@ function newServerForRepo(repoID) {
|
||||
|
||||
const statusUpdated = this.raiseStatusUpdatedEvent.bind(this);
|
||||
|
||||
const pollInterval = 3000;
|
||||
const pollInterval = 3000;
|
||||
|
||||
function pollOnce() {
|
||||
if (!runningServerAddress || !runningServerCertificate || !runningServerPassword || !runningServerControlPassword) {
|
||||
@@ -80,13 +82,13 @@ function newServerForRepo(repoID) {
|
||||
port: parseInt(new URL(runningServerAddress).port),
|
||||
method: "GET",
|
||||
path: "/api/v1/control/status",
|
||||
timeout: pollInterval,
|
||||
timeout: pollInterval,
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + Buffer.from("server-control" + ':' + runningServerControlPassword).toString('base64')
|
||||
}
|
||||
}
|
||||
}, (resp) => {
|
||||
if (resp.statusCode === 200) {
|
||||
resp.on('data', x => {
|
||||
resp.on('data', x => {
|
||||
try {
|
||||
const newDetails = JSON.parse(x);
|
||||
if (JSON.stringify(newDetails) != JSON.stringify(runningServerStatusDetails)) {
|
||||
@@ -101,7 +103,7 @@ function newServerForRepo(repoID) {
|
||||
log.warn('error fetching status', resp.statusMessage);
|
||||
}
|
||||
});
|
||||
req.on('error', (e)=>{
|
||||
req.on('error', (e) => {
|
||||
log.info('error fetching status', e);
|
||||
});
|
||||
req.end();
|
||||
@@ -159,6 +161,14 @@ function newServerForRepo(repoID) {
|
||||
runningServerAddress = value;
|
||||
this.raiseStatusUpdatedEvent();
|
||||
break;
|
||||
|
||||
case "NOTIFICATION":
|
||||
try {
|
||||
this.raiseNotificationEvent(JSON.parse(value));
|
||||
} catch (e) {
|
||||
log.warn('unable to parse notification JSON', e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +241,15 @@ function newServerForRepo(repoID) {
|
||||
|
||||
ipcMain.emit('status-updated-event', args);
|
||||
},
|
||||
|
||||
raiseNotificationEvent(notification) {
|
||||
const args = {
|
||||
repoID: repoID,
|
||||
notification: notification,
|
||||
};
|
||||
|
||||
ipcMain.emit('repo-notification-event', args);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -243,13 +262,13 @@ ipcMain.on('status-fetch', (event, args) => {
|
||||
})
|
||||
|
||||
export function serverForRepo(repoID) {
|
||||
let s = servers[repoID];
|
||||
if (s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s = newServerForRepo(repoID);
|
||||
servers[repoID] = s;
|
||||
let s = servers[repoID];
|
||||
if (s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s = newServerForRepo(repoID);
|
||||
servers[repoID] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user