diff --git a/apps/web/desktop/events.js b/apps/web/desktop/events.js index 469e37bdf..b036ee6bc 100644 --- a/apps/web/desktop/events.js +++ b/apps/web/desktop/events.js @@ -23,5 +23,6 @@ export const EVENTS = { updateDownloadProgress: "updateDownloadProgress", updateDownloadCompleted: "updateDownloadCompleted", updateNotAvailable: "updateNotAvailable", - themeChanged: "themeChanged" + themeChanged: "themeChanged", + notificationClicked: "notificationClicked" }; diff --git a/apps/web/desktop/ipc/actions/bringToFront.js b/apps/web/desktop/ipc/actions/bringToFront.js new file mode 100644 index 000000000..02ae593ce --- /dev/null +++ b/apps/web/desktop/ipc/actions/bringToFront.js @@ -0,0 +1,24 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +export default () => { + if (!global.win) return; + global.win.show(); + global.win.moveTop(); +}; diff --git a/apps/web/desktop/ipc/actions/index.js b/apps/web/desktop/ipc/actions/index.js index 27714208d..9309fe8ad 100644 --- a/apps/web/desktop/ipc/actions/index.js +++ b/apps/web/desktop/ipc/actions/index.js @@ -25,6 +25,8 @@ import open from "./open"; import saveFile from "./saveFile"; import setZoomFactor from "./setZoomFactor"; import setPrivacyMode from "./setPrivacyMode"; +import showNotification from "./showNotification"; +import bringToFront from "./bringToFront"; const actions = { changeAppTheme, @@ -34,7 +36,9 @@ const actions = { open, saveFile, setZoomFactor, - setPrivacyMode + setPrivacyMode, + showNotification, + bringToFront }; export function getAction(actionName) { diff --git a/apps/web/desktop/ipc/actions/showNotification.js b/apps/web/desktop/ipc/actions/showNotification.js new file mode 100644 index 000000000..1e23636eb --- /dev/null +++ b/apps/web/desktop/ipc/actions/showNotification.js @@ -0,0 +1,42 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +import { Notification, shell } from "electron"; +import { join } from "path"; +import { EVENTS } from "../../events"; +import { sendMessageToRenderer } from "../utils"; +import { platform } from "os"; + +export default (args) => { + if (!global.win) return; + const notification = new Notification({ + ...args, + icon: join( + __dirname, + platform() === "win32" ? "app.ico" : "favicon-72x72.png" + ) + }); + notification.show(); + if (args.urgency === "critical") { + shell.beep(); + } + + notification.addListener("click", () => { + sendMessageToRenderer(EVENTS.notificationClicked, { tag: args.tag }); + }); +}; diff --git a/apps/web/src/commands/bring-to-front.js b/apps/web/src/commands/bring-to-front.js new file mode 100644 index 000000000..5ffbfe6f4 --- /dev/null +++ b/apps/web/src/commands/bring-to-front.js @@ -0,0 +1,24 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { invokeCommand } from "./index"; + +export default function bringToFront() { + invokeCommand("bringToFront"); +} diff --git a/apps/web/src/commands/index.js b/apps/web/src/commands/index.js index 5ca3b87ba..38913031d 100644 --- a/apps/web/src/commands/index.js +++ b/apps/web/src/commands/index.js @@ -32,7 +32,7 @@ export function invokeCommand(type, payload = {}) { if (isDesktop()) { window.api.receive("fromMain", (args) => { console.log(args); - const { type } = args; - AppEventManager.publish(type, args); + const { type, ...other } = args; + AppEventManager.publish(type, other); }); } diff --git a/apps/web/src/commands/show-notification.js b/apps/web/src/commands/show-notification.js new file mode 100644 index 000000000..3e0ac384a --- /dev/null +++ b/apps/web/src/commands/show-notification.js @@ -0,0 +1,40 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { EVENTS } from "@notesnook/desktop/events"; +import { AppEventManager } from "../common/app-events"; +import { invokeCommand } from "./index"; + +/** + * + * @param {import("electron").NotificationConstructorOptions & { tag: string }} options + * @param {() => void} onClicked + */ +export default function showNotification(options, onClicked) { + invokeCommand("showNotification", options); + const { unsubscribe } = AppEventManager.subscribe( + EVENTS.notificationClicked, + ({ tag }) => { + if (tag === options.tag) { + onClicked(); + unsubscribe(); + } + } + ); +} diff --git a/apps/web/src/stores/reminder-store.js b/apps/web/src/stores/reminder-store.js index d176a2178..52731aa27 100644 --- a/apps/web/src/stores/reminder-store.js +++ b/apps/web/src/stores/reminder-store.js @@ -26,7 +26,9 @@ import { showReminderPreviewDialog } from "../common/dialog-controller"; import dayjs from "dayjs"; import Config from "../utils/config"; import { store as notestore } from "./note-store"; -import { isTesting } from "../utils/platform"; +import { isDesktop, isTesting } from "../utils/platform"; +import showNotification from "../commands/show-notification"; +import bringToFront from "../commands/bring-to-front"; class ReminderStore extends BaseStore { reminders = []; @@ -81,6 +83,13 @@ async function resetReminders(reminders) { } } +/** + * + * @param {string} id + * @param {import("@notesnook/core/collections/reminders").Reminder} reminder + * @param {string} cron + * @returns + */ function scheduleReminder(id, reminder, cron) { return TaskScheduler.register(`reminder:${id}`, cron, () => { if (!Config.get("reminderNotifications", true)) return; @@ -90,18 +99,42 @@ function scheduleReminder(id, reminder, cron) { return; } - const notification = new Notification(reminder.title, { - body: reminder.description, - vibrate: reminder.priority === "vibrate", - silent: reminder.priority === "silent", - tag: id, - renotify: true, - requireInteraction: true - }); + if (isDesktop()) { + showNotification( + { + title: reminder.title, + body: reminder.description, + silent: reminder.priority === "silent", + timeoutType: reminder.priority === "urgent" ? "never" : "default", + urgency: + reminder.priority === "urgent" + ? "critical" + : reminder.priority === "vibrate" + ? "normal" + : "low", + focusOnClick: true, + tag: id + }, + () => { + bringToFront(); + showReminderPreviewDialog(reminder); + } + ); + } else { + const notification = new Notification(reminder.title, { + body: reminder.description, + vibrate: reminder.priority === "vibrate", + silent: reminder.priority === "silent", + tag: id, + renotify: true, + requireInteraction: true + }); - notification.onclick = function () { - showReminderPreviewDialog(reminder); - }; + notification.onclick = function () { + window.focus(); + showReminderPreviewDialog(reminder); + }; + } store.refresh(false); });