2025-01-09 18:07:02 +02:00
|
|
|
import { Menu, Tray } from "electron";
|
2024-07-18 21:37:45 +03:00
|
|
|
import path from "path";
|
2024-07-18 21:35:17 +03:00
|
|
|
import windowService from "./window.js";
|
|
|
|
|
import optionService from "./options.js";
|
2024-07-19 00:18:35 +03:00
|
|
|
import { fileURLToPath } from "url";
|
2025-02-01 02:08:19 +02:00
|
|
|
import type { KeyboardActionNames } from "./keyboard_actions_interface.js";
|
2025-02-01 02:18:10 +02:00
|
|
|
import date_notes from "./date_notes.js";
|
|
|
|
|
import type BNote from "../becca/entities/bnote.js";
|
2025-02-01 02:29:34 +02:00
|
|
|
import becca from "../becca/becca.js";
|
2025-02-01 02:40:04 +02:00
|
|
|
import becca_service from "../becca/becca_service.js";
|
|
|
|
|
import type BRecentNote from "../becca/entities/brecent_note.js";
|
2025-02-01 11:04:49 +02:00
|
|
|
import { ipcMain, nativeTheme } from "electron/main";
|
2025-02-01 11:20:29 +02:00
|
|
|
import { default as i18next, t } from "i18next";
|
2025-02-01 12:49:01 +02:00
|
|
|
import { isDev, isMac } from "./utils.js";
|
2025-02-01 12:25:12 +02:00
|
|
|
import cls from "./cls.js";
|
2021-11-14 12:36:39 +00:00
|
|
|
|
2024-04-02 23:39:45 +03:00
|
|
|
let tray: Tray;
|
2021-11-14 12:36:39 +00:00
|
|
|
// `mainWindow.isVisible` doesn't work with `mainWindow.show` and `mainWindow.hide` - it returns `false` when the window
|
|
|
|
|
// is minimized
|
|
|
|
|
let isVisible = true;
|
|
|
|
|
|
2025-02-01 10:18:41 +02:00
|
|
|
function getTrayIconPath() {
|
2025-02-01 12:49:01 +02:00
|
|
|
let name: string;
|
|
|
|
|
if (isMac) {
|
2025-02-01 12:53:11 +02:00
|
|
|
name = "icon-blackTemplate";
|
2025-02-01 12:49:01 +02:00
|
|
|
} else if (isDev) {
|
|
|
|
|
name = "icon-purple";
|
|
|
|
|
} else {
|
|
|
|
|
name = "icon-color";
|
|
|
|
|
}
|
2021-11-14 12:36:39 +00:00
|
|
|
|
2025-02-01 12:45:32 +02:00
|
|
|
return path.join(path.dirname(fileURLToPath(import.meta.url)), "../..", "images", "app-icons", "tray", `${name}.png`);
|
2025-02-01 01:54:51 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-01 10:18:41 +02:00
|
|
|
function getIconPath(name: string) {
|
2025-03-02 20:47:57 +01:00
|
|
|
const suffix = !isMac && nativeTheme.shouldUseDarkColors ? "-inverted" : "";
|
2025-02-01 12:57:51 +02:00
|
|
|
return path.join(path.dirname(fileURLToPath(import.meta.url)), "../..", "images", "app-icons", "tray", `${name}Template${suffix}.png`);
|
2025-02-01 10:18:41 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-01 01:54:51 +02:00
|
|
|
function registerVisibilityListener() {
|
2021-11-14 12:36:39 +00:00
|
|
|
const mainWindow = windowService.getMainWindow();
|
2025-01-09 18:07:02 +02:00
|
|
|
if (!mainWindow) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-11-14 12:36:39 +00:00
|
|
|
|
|
|
|
|
// They need to be registered before the tray updater is registered
|
2025-01-09 18:07:02 +02:00
|
|
|
mainWindow.on("show", () => {
|
2021-11-14 12:36:39 +00:00
|
|
|
isVisible = true;
|
2024-04-02 23:39:45 +03:00
|
|
|
updateTrayMenu();
|
2021-11-14 12:36:39 +00:00
|
|
|
});
|
2025-01-09 18:07:02 +02:00
|
|
|
mainWindow.on("hide", () => {
|
2021-11-14 12:36:39 +00:00
|
|
|
isVisible = false;
|
2024-04-02 23:39:45 +03:00
|
|
|
updateTrayMenu();
|
2021-11-14 12:36:39 +00:00
|
|
|
});
|
|
|
|
|
|
2024-04-02 23:39:45 +03:00
|
|
|
mainWindow.on("minimize", updateTrayMenu);
|
|
|
|
|
mainWindow.on("maximize", updateTrayMenu);
|
2025-02-01 12:57:51 +02:00
|
|
|
if (!isMac) {
|
|
|
|
|
// macOS uses template icons which work great on dark & light themes.
|
|
|
|
|
nativeTheme.on("updated", updateTrayMenu);
|
|
|
|
|
}
|
2025-02-01 11:04:49 +02:00
|
|
|
ipcMain.on("reload-tray", updateTrayMenu);
|
2025-02-01 11:20:29 +02:00
|
|
|
i18next.on("languageChanged", updateTrayMenu);
|
2025-02-01 01:54:51 +02:00
|
|
|
}
|
2021-11-14 12:36:39 +00:00
|
|
|
|
2025-02-01 01:54:51 +02:00
|
|
|
function updateTrayMenu() {
|
2021-11-14 12:36:39 +00:00
|
|
|
const mainWindow = windowService.getMainWindow();
|
2025-01-09 18:07:02 +02:00
|
|
|
if (!mainWindow) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-11-14 12:36:39 +00:00
|
|
|
|
2025-02-01 10:46:27 +02:00
|
|
|
function ensureVisible() {
|
|
|
|
|
if (mainWindow) {
|
|
|
|
|
mainWindow.show();
|
|
|
|
|
mainWindow.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-01 02:08:19 +02:00
|
|
|
function triggerKeyboardAction(actionName: KeyboardActionNames) {
|
|
|
|
|
mainWindow?.webContents.send("globalShortcut", actionName);
|
2025-02-01 10:46:27 +02:00
|
|
|
ensureVisible();
|
2025-02-01 02:08:19 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-01 02:40:04 +02:00
|
|
|
function openInSameTab(note: BNote | BRecentNote) {
|
2025-02-01 02:18:10 +02:00
|
|
|
mainWindow?.webContents.send("openInSameTab", note.noteId);
|
2025-02-01 10:46:27 +02:00
|
|
|
ensureVisible();
|
2025-02-01 02:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-01 02:29:34 +02:00
|
|
|
function buildBookmarksMenu() {
|
|
|
|
|
const parentNote = becca.getNoteOrThrow("_lbBookmarks");
|
|
|
|
|
const menuItems: Electron.MenuItemConstructorOptions[] = [];
|
|
|
|
|
|
|
|
|
|
for (const bookmarkNote of parentNote?.children) {
|
|
|
|
|
if (bookmarkNote.isLabelTruthy("bookmarkFolder")) {
|
2025-02-01 10:54:00 +02:00
|
|
|
menuItems.push({
|
|
|
|
|
label: bookmarkNote.title,
|
|
|
|
|
type: "submenu",
|
|
|
|
|
submenu: bookmarkNote.children.map((subitem) => {
|
|
|
|
|
return {
|
|
|
|
|
label: subitem.title,
|
|
|
|
|
type: "normal",
|
|
|
|
|
click: () => openInSameTab(subitem)
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
menuItems.push({
|
|
|
|
|
label: bookmarkNote.title,
|
|
|
|
|
type: "normal",
|
|
|
|
|
click: () => openInSameTab(bookmarkNote)
|
|
|
|
|
});
|
2025-02-01 02:29:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return menuItems;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-01 02:40:04 +02:00
|
|
|
function buildRecentNotesMenu() {
|
|
|
|
|
const recentNotes = becca.getRecentNotesFromQuery(`
|
|
|
|
|
SELECT recent_notes.*
|
|
|
|
|
FROM recent_notes
|
|
|
|
|
JOIN notes USING(noteId)
|
|
|
|
|
WHERE notes.isDeleted = 0
|
|
|
|
|
ORDER BY utcDateCreated DESC
|
|
|
|
|
LIMIT 10
|
|
|
|
|
`);
|
|
|
|
|
const menuItems: Electron.MenuItemConstructorOptions[] = [];
|
2025-02-01 13:23:14 +02:00
|
|
|
const formatter = new Intl.DateTimeFormat(undefined, {
|
2025-02-01 14:00:56 +02:00
|
|
|
dateStyle: "short",
|
2025-02-01 13:23:14 +02:00
|
|
|
timeStyle: "short"
|
|
|
|
|
});
|
2025-02-01 02:40:04 +02:00
|
|
|
|
|
|
|
|
for (const recentNote of recentNotes) {
|
2025-02-01 13:23:14 +02:00
|
|
|
const date = new Date(recentNote.utcDateCreated);
|
|
|
|
|
|
2025-02-01 02:40:04 +02:00
|
|
|
menuItems.push({
|
|
|
|
|
label: becca_service.getNoteTitle(recentNote.noteId),
|
|
|
|
|
type: "normal",
|
2025-02-01 13:23:14 +02:00
|
|
|
sublabel: formatter.format(date),
|
2025-02-01 02:40:04 +02:00
|
|
|
click: () => openInSameTab(recentNote)
|
2025-03-02 20:47:57 +01:00
|
|
|
});
|
2025-02-01 02:40:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return menuItems;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-14 12:36:39 +00:00
|
|
|
const contextMenu = Menu.buildFromTemplate([
|
2025-02-01 02:08:19 +02:00
|
|
|
{
|
2025-02-01 11:16:46 +02:00
|
|
|
label: t("tray.show-windows"),
|
2025-02-01 02:46:33 +02:00
|
|
|
type: "checkbox",
|
|
|
|
|
checked: isVisible,
|
|
|
|
|
click: () => {
|
|
|
|
|
if (isVisible) {
|
|
|
|
|
mainWindow.hide();
|
|
|
|
|
} else {
|
2025-02-01 10:46:27 +02:00
|
|
|
ensureVisible();
|
2025-02-01 02:46:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ type: "separator" },
|
|
|
|
|
{
|
2025-02-01 11:16:46 +02:00
|
|
|
label: t("tray.new-note"),
|
2025-02-01 02:08:19 +02:00
|
|
|
type: "normal",
|
2025-02-01 10:18:41 +02:00
|
|
|
icon: getIconPath("new-note"),
|
2025-02-01 02:08:19 +02:00
|
|
|
click: () => triggerKeyboardAction("createNoteIntoInbox")
|
|
|
|
|
},
|
2025-02-01 02:18:10 +02:00
|
|
|
{
|
2025-02-01 11:16:46 +02:00
|
|
|
label: t("tray.today"),
|
2025-02-01 02:18:10 +02:00
|
|
|
type: "normal",
|
2025-02-01 10:18:41 +02:00
|
|
|
icon: getIconPath("today"),
|
2025-02-01 12:25:12 +02:00
|
|
|
click: cls.wrap(() => openInSameTab(date_notes.getTodayNote()))
|
2025-02-01 02:18:10 +02:00
|
|
|
},
|
2025-02-01 02:29:34 +02:00
|
|
|
{
|
2025-02-01 11:16:46 +02:00
|
|
|
label: t("tray.bookmarks"),
|
2025-02-01 02:29:34 +02:00
|
|
|
type: "submenu",
|
2025-02-01 10:18:41 +02:00
|
|
|
icon: getIconPath("bookmarks"),
|
2025-02-01 02:29:34 +02:00
|
|
|
submenu: buildBookmarksMenu()
|
|
|
|
|
},
|
2025-02-01 02:40:04 +02:00
|
|
|
{
|
2025-02-01 11:16:46 +02:00
|
|
|
label: t("tray.recents"),
|
2025-02-01 02:40:04 +02:00
|
|
|
type: "submenu",
|
2025-02-01 10:18:41 +02:00
|
|
|
icon: getIconPath("recents"),
|
2025-02-01 02:40:04 +02:00
|
|
|
submenu: buildRecentNotesMenu()
|
|
|
|
|
},
|
2025-02-01 02:08:19 +02:00
|
|
|
{ type: "separator" },
|
2021-11-14 12:36:39 +00:00
|
|
|
{
|
2025-02-01 11:16:46 +02:00
|
|
|
label: t("tray.close"),
|
2025-01-09 18:07:02 +02:00
|
|
|
type: "normal",
|
2025-02-01 10:18:41 +02:00
|
|
|
icon: getIconPath("close"),
|
2021-11-14 12:36:39 +00:00
|
|
|
click: () => {
|
|
|
|
|
mainWindow.close();
|
|
|
|
|
}
|
2025-01-09 18:07:02 +02:00
|
|
|
}
|
2021-11-14 12:36:39 +00:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
tray?.setContextMenu(contextMenu);
|
2025-02-01 01:54:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function changeVisibility() {
|
2022-01-10 17:09:20 +01:00
|
|
|
const window = windowService.getMainWindow();
|
2025-01-09 18:07:02 +02:00
|
|
|
if (!window) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-11-14 12:36:39 +00:00
|
|
|
|
|
|
|
|
if (isVisible) {
|
|
|
|
|
window.hide();
|
|
|
|
|
} else {
|
|
|
|
|
window.show();
|
|
|
|
|
window.focus();
|
|
|
|
|
}
|
2025-02-01 01:54:51 +02:00
|
|
|
}
|
2021-11-14 12:36:39 +00:00
|
|
|
|
|
|
|
|
function createTray() {
|
2022-11-18 21:08:32 +01:00
|
|
|
if (optionService.getOptionBool("disableTray")) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-01 10:18:41 +02:00
|
|
|
tray = new Tray(getTrayIconPath());
|
2025-02-01 11:16:46 +02:00
|
|
|
tray.setToolTip(t("tray.tooltip"));
|
2021-11-14 12:36:39 +00:00
|
|
|
// Restore focus
|
2025-01-09 18:07:02 +02:00
|
|
|
tray.on("click", changeVisibility);
|
2021-11-14 12:36:39 +00:00
|
|
|
updateTrayMenu();
|
|
|
|
|
|
|
|
|
|
registerVisibilityListener();
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-18 21:42:44 +03:00
|
|
|
export default {
|
2021-11-14 12:36:39 +00:00
|
|
|
createTray
|
2025-01-09 18:07:02 +02:00
|
|
|
};
|