desktop: improve integration with operating system

This commit is contained in:
Abdullah Atta
2023-01-24 19:31:37 +05:00
committed by Abdullah Atta
parent 4805e54ef3
commit f7a0938c38
19 changed files with 564 additions and 83 deletions

View File

@@ -0,0 +1,66 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { app } from "electron";
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
import path from "path";
const LINUX_DESKTOP_ENTRY = (hidden: boolean) => `[Desktop Entry]
Type=Application
Version=${app.getVersion()}
Name=${app.getName()}
Comment=${app.getName()} startup script
Exec=${process.execPath}${hidden ? " --hidden" : ""}
StartupNotify=false
Terminal=false`;
const LINUX_AUTOSTART_DIRECTORY_PATH = path.join(
app.getPath("home"),
".config",
"autostart"
);
export class AutoLaunch {
static enable(hidden: boolean) {
if (process.platform === "linux") {
mkdirSync(LINUX_AUTOSTART_DIRECTORY_PATH, { recursive: true });
writeFileSync(
path.join(
LINUX_AUTOSTART_DIRECTORY_PATH,
`${app.getName().toLowerCase()}.desktop`
),
LINUX_DESKTOP_ENTRY(hidden)
);
} else {
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: hidden });
}
}
static disable() {
if (process.platform === "linux") {
const desktopFilePath = path.join(
LINUX_AUTOSTART_DIRECTORY_PATH,
`${app.getName().toLowerCase()}.desktop`
);
if (!existsSync(desktopFilePath)) return;
rmSync(desktopFilePath);
} else {
app.setLoginItemSettings({ openAtLogin: false, openAsHidden: false });
}
}
}

View File

@@ -0,0 +1,35 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { JSONStorage } from "../jsonstorage";
function getDesktopIntegration() {
return JSONStorage.get("desktopSettings", {
autoStart: false,
startMinimized: false,
minimizeToSystemTray: false,
closeToSystemTray: false
});
}
function setDesktopIntegration(settings) {
return JSONStorage.set("desktopSettings", settings);
}
export { getDesktopIntegration, setDesktopIntegration };

View File

@@ -19,10 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* global MAC_APP_STORE, RELEASE */
import "isomorphic-fetch";
import { app, BrowserWindow, nativeTheme, shell } from "electron";
import { join } from "path";
import { platform } from "os";
import { isDevelopment } from "./utils";
import { app, BrowserWindow, Menu, nativeTheme, shell, Tray } from "electron";
import { APP_ICON_PATH, isDevelopment } from "./utils";
import { registerProtocol, PROTOCOL_URL } from "./protocol";
import { configureAutoUpdater } from "./autoupdate";
import { getBackgroundColor, getTheme, setTheme } from "./config/theme";
@@ -36,6 +34,10 @@ import "./ipc/index.js";
import getPrivacyMode from "./ipc/calls/getPrivacyMode";
import setPrivacyMode from "./ipc/actions/setPrivacyMode";
import { getIsSpellCheckerEnabled } from "./config/spellChecker";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { getDesktopIntegration } from "./config/desktopIntegration";
import { AutoLaunch } from "./autolaunch";
if (!RELEASE) {
require("electron-reloader")(module);
@@ -51,20 +53,21 @@ if (process.platform === "win32") {
app.setAppUserModelId(app.name);
}
var mainWindowState;
async function createWindow() {
const mainWindowState = new WindowState({});
mainWindowState = new WindowState({});
setTheme(getTheme());
const mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
fullscreen: mainWindowState.isFullScreen,
backgroundColor: getBackgroundColor(),
autoHideMenuBar: true,
icon: join(
__dirname,
platform() === "win32" ? "app.ico" : "favicon-72x72.png"
),
icon: APP_ICON_PATH,
webPreferences: {
zoomFactor: getZoomFactor(),
devTools: true, // isDev,
@@ -76,10 +79,14 @@ async function createWindow() {
preload: __dirname + "/preload.js"
}
});
mainWindow.setAutoHideMenuBar(true);
mainWindowState.manage(mainWindow);
globalThis.window = mainWindow;
setupMenu();
setupJumplist();
setupTray();
setupDesktopIntegration();
if (isDevelopment())
mainWindow.webContents.openDevTools({ mode: "right", activate: true });
@@ -88,10 +95,9 @@ async function createWindow() {
setPrivacyMode({ privacyMode: getPrivacyMode() });
}
const cliOptions = await parseCli();
try {
await mainWindow.loadURL(
isDevelopment() ? "http://localhost:3000" : PROTOCOL_URL
);
await mainWindow.loadURL(`${createURL(cliOptions)}`);
} catch (e) {
logger.error(e);
}
@@ -134,3 +140,179 @@ app.on("activate", () => {
createWindow();
}
});
function createURL(options) {
const url = new URL(isDevelopment() ? "http://localhost:3000" : PROTOCOL_URL);
if (options.note === true) url.hash = "/notes/create/1";
else if (options.notebook === true) url.hash = "/notebooks/create";
else if (options.reminder === true) url.hash = "/reminders/create";
else if (typeof options.note === "string")
url.hash = `/notes/${options.note}/edit`;
else if (typeof options.notebook === "string")
url.hash = `/notebooks/${options.notebook}`;
return url;
}
async function parseCli() {
const result = {
note: false,
notebook: false,
reminder: false
};
await yargs(hideBin(process.argv))
.command("new", "Create a new item", (yargs) => {
return yargs
.command("note", "Create a new note", {}, () => (result.note = true))
.command(
"notebook",
"Create a new notebook",
{},
() => (result.notebook = true)
)
.command(
"reminder",
"Add a new reminder",
{},
() => (result.reminder = true)
);
})
.command("open", "Open a specific item", (yargs) => {
return yargs
.command(
"note",
"Open a note",
{ id: { string: true, description: "Id of the note" } },
(args) => (result.note = args.id)
)
.command(
"notebook",
"Open a notebook",
{ id: { string: true, description: "Id of the notebook" } },
(args) => (result.notebook = args.id)
)
.command(
"topic",
"Open a topic",
{
id: { string: true, description: "Id of the topic" },
notebookId: { string: true, description: "Id of the notebook" }
},
(args) => (result.notebook = `${args.notebookId}/${args.id}`)
);
})
.parse();
return result;
}
function setupJumplist() {
if (process.platform === "win32") {
app.setJumpList([
{ type: "frequent" },
{
type: "tasks",
items: [
{
program: process.execPath,
iconPath: process.execPath,
args: "new note",
description: "Create a new note",
title: "New note",
type: "task"
},
{
program: process.execPath,
iconPath: process.execPath,
args: "new notebook",
description: "Create a new notebook",
title: "New notebook",
type: "task"
},
{
program: process.execPath,
iconPath: process.execPath,
args: "new reminder",
description: "Add a new reminder",
title: "New reminder",
type: "task"
}
]
}
]);
}
}
function setupTray() {
const tray = new Tray(APP_ICON_PATH);
const contextMenu = Menu.buildFromTemplate([
{
label: "Show Notesnook",
type: "normal",
icon: APP_ICON_PATH,
click: showApp
},
{ type: "separator" },
{
label: "New note",
type: "normal",
click: () => {
showApp();
sendMessageToRenderer(EVENTS.createItem, { itemType: "note" });
}
},
{
label: "New notebook",
type: "normal",
click: () => {
showApp();
sendMessageToRenderer(EVENTS.createItem, { itemType: "notebook" });
}
},
{ type: "separator" },
{
label: "Quit Notesnook",
type: "normal",
click: () => {
app.exit(0);
}
}
]);
tray.on("double-click", showApp);
tray.on("click", showApp);
tray.setToolTip("Notesnook");
tray.setContextMenu(contextMenu);
}
function showApp() {
if (globalThis.window.isMinimized()) {
if (mainWindowState.isMaximized) {
globalThis.window.maximize();
} else globalThis.window.restore();
}
globalThis.window.show();
globalThis.window.focus();
globalThis.window.moveTop();
globalThis.window.webContents.focus();
}
function setupDesktopIntegration() {
const desktopIntegration = getDesktopIntegration();
if (desktopIntegration.autoStart) {
AutoLaunch.enable(desktopIntegration.startMinimized);
}
globalThis.window.on("close", (e) => {
if (getDesktopIntegration().closeToSystemTray) {
e.preventDefault();
globalThis.window.minimize();
globalThis.window.hide();
}
});
globalThis.window.on("minimize", () => {
if (getDesktopIntegration().minimizeToSystemTray) globalThis.window.hide();
});
}

View File

@@ -24,5 +24,6 @@ export const EVENTS = {
updateDownloadCompleted: "updateDownloadCompleted",
updateNotAvailable: "updateNotAvailable",
themeChanged: "themeChanged",
notificationClicked: "notificationClicked"
notificationClicked: "notificationClicked",
createItem: "createItem"
};

View File

@@ -29,6 +29,7 @@ import showNotification from "./showNotification";
import bringToFront from "./bringToFront";
import setSpellCheckerLanguages from "./setSpellCheckerLanguages";
import toggleSpellChecker from "./toggleSpellChecker";
import setDesktopIntegration from "./setDesktopIntegration";
const actions = {
changeAppTheme,
@@ -42,7 +43,8 @@ const actions = {
showNotification,
bringToFront,
setSpellCheckerLanguages,
toggleSpellChecker
toggleSpellChecker,
setDesktopIntegration,
};
export function getAction(actionName) {

View File

@@ -0,0 +1,33 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { AutoLaunch } from "../../autolaunch";
import { setDesktopIntegration } from "../../config/desktopIntegration";
export default (args) => {
if (!globalThis.window) return;
if (args.autoStart) {
AutoLaunch.enable(args.startMinimized);
} else {
AutoLaunch.disable();
}
setDesktopIntegration(args);
};

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
import { getDesktopIntegration } from "../../config/desktopIntegration";
export default function () {
return getDesktopIntegration();
}

View File

@@ -22,6 +22,7 @@ import getPrivacyMode from "./getPrivacyMode";
import selectDirectory from "./selectDirectory";
import { gunzip, gzip } from "./gzip";
import getSpellChecker from "./getSpellChecker";
import getDesktopIntegration from "./getDesktopIntegration";
const calls = {
getZoomFactor,
@@ -29,7 +30,8 @@ const calls = {
selectDirectory,
gunzip,
gzip,
getSpellChecker
getSpellChecker,
getDesktopIntegration,
};
export const getCall = function getAction(callName) {

View File

@@ -25,12 +25,12 @@ const directory = app.getPath("userData");
const filename = "config.json";
const filePath = join(directory, filename);
class JSONStorage {
static get(key, def) {
static get<T>(key: string, def?: T): T {
const json = this.readJson();
return json[key] === undefined ? def : json[key];
}
static set(key, value) {
static set(key: string, value: unknown) {
const json = this.readJson();
json[key] = value;
this.writeJson(json);
@@ -40,10 +40,7 @@ class JSONStorage {
this.writeJson({});
}
/**
* @private
*/
static readJson() {
private static readJson() {
try {
const json = readFileSync(filePath, "utf-8");
return JSON.parse(json);
@@ -53,10 +50,7 @@ class JSONStorage {
}
}
/**
* @private
*/
static writeJson(json) {
private static writeJson(json: Record<string, unknown>) {
try {
writeFileSync(filePath, JSON.stringify(json));
} catch (e) {

View File

@@ -10,7 +10,8 @@
"dependencies": {
"diary": "^0.3.1",
"electron-updater": "^5.3.0",
"isomorphic-fetch": "^3.0.0"
"isomorphic-fetch": "^3.0.0",
"yargs": "^17.6.2"
},
"devDependencies": {
"@types/node-fetch": "^2.6.2",
@@ -514,7 +515,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -523,7 +523,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -1073,7 +1072,6 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
@@ -1096,7 +1094,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -1107,8 +1104,7 @@
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/colors": {
"version": "1.0.3",
@@ -1801,8 +1797,7 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
@@ -2209,7 +2204,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -2434,7 +2428,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
@@ -2846,7 +2839,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -3591,7 +3583,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3835,7 +3826,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -3849,7 +3839,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -4140,7 +4129,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -4172,7 +4160,6 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"engines": {
"node": ">=10"
}
@@ -4183,10 +4170,9 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yargs": {
"version": "17.6.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
"integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
"dev": true,
"version": "17.6.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz",
"integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
@@ -4194,7 +4180,7 @@
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
@@ -4204,7 +4190,6 @@
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"engines": {
"node": ">=12"
}
@@ -4624,14 +4609,12 @@
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -5055,7 +5038,6 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
@@ -5075,7 +5057,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -5083,8 +5064,7 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"colors": {
"version": "1.0.3",
@@ -5642,8 +5622,7 @@
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"encodeurl": {
"version": "1.0.2",
@@ -5856,8 +5835,7 @@
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
},
"escape-string-regexp": {
"version": "4.0.0",
@@ -6024,8 +6002,7 @@
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-intrinsic": {
"version": "1.1.2",
@@ -6328,8 +6305,7 @@
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-glob": {
"version": "4.0.1",
@@ -6887,8 +6863,7 @@
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
},
"resolve": {
"version": "1.20.0",
@@ -7091,7 +7066,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -7102,7 +7076,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
@@ -7342,7 +7315,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -7364,8 +7336,7 @@
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
},
"yallist": {
"version": "4.0.0",
@@ -7373,10 +7344,9 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": {
"version": "17.6.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
"integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
"dev": true,
"version": "17.6.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz",
"integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==",
"requires": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
@@ -7384,14 +7354,13 @@
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
"yargs-parser": "^21.1.1"
}
},
"yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
},
"yauzl": {
"version": "2.10.0",

View File

@@ -11,7 +11,8 @@
"dependencies": {
"diary": "^0.3.1",
"electron-updater": "^5.3.0",
"isomorphic-fetch": "^3.0.0"
"isomorphic-fetch": "^3.0.0",
"yargs": "^17.6.2"
},
"devDependencies": {
"@types/node-fetch": "^2.6.2",

View File

@@ -61,6 +61,11 @@ contextBridge.exposeInMainWorld("config", {
return ipcRenderer.invoke("fromRenderer", {
type: "getSpellChecker"
});
},
desktopIntegration: () => {
return ipcRenderer.invoke("fromRenderer", {
type: "getDesktopIntegration"
});
}
});

View File

@@ -21,6 +21,11 @@ import { app } from "electron";
import { join } from "path";
import { statSync } from "fs";
const APP_ICON_PATH = join(
__dirname,
process.platform === "win32" ? "app.ico" : "favicon-72x72.png"
);
function isDevelopment() {
if (typeof electron === "string") {
throw new TypeError("Not running in an Electron environment!");
@@ -47,4 +52,4 @@ function getPath(filePath) {
}
}
export { getPath, isDevelopment };
export { getPath, isDevelopment, APP_ICON_PATH };

View File

@@ -29,6 +29,7 @@ import { introduceFeatures, showUpgradeReminderDialogs } from "./common";
import { AppEventManager, AppEvents } from "./common/app-events";
import { db } from "./common/db";
import { EV, EVENTS } from "@notesnook/core/common";
import { EVENTS as DESKTOP_APP_EVENTS } from "@notesnook/desktop/events";
import { registerKeyMap } from "./common/key-map";
import { isUserPremium } from "./hooks/use-is-user-premium";
import useAnnouncements from "./hooks/use-announcements";
@@ -45,6 +46,7 @@ import { updateStatus, removeStatus, getStatus } from "./hooks/use-status";
import { showToast } from "./utils/toast";
import { interruptedOnboarding } from "./components/dialogs/onboarding-dialog";
import { WebExtensionRelay } from "./utils/web-extension-relay";
import { hashNavigate } from "./navigation";
const relay = new WebExtensionRelay();
@@ -197,6 +199,22 @@ export default function AppEffects({ setShow }) {
setTheme(isSystemThemeDark ? "dark" : "light");
}, [isSystemThemeDark, followSystemTheme, setTheme]);
useEffect(() => {
AppEventManager.subscribe(DESKTOP_APP_EVENTS.createItem, ({ itemType }) => {
switch (itemType) {
case "note":
hashNavigate("/notes/create", { addNonce: true, replace: true });
break;
case "notebook":
hashNavigate("/notebooks/create", { replace: true });
break;
case "reminder":
hashNavigate("/reminders/create", { replace: true });
break;
}
});
}, []);
return <React.Fragment />;
}

View File

@@ -0,0 +1,26 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { invokeCommand } from "./index";
export default function setDesktopIntegration(
settings: DesktopIntegrationSettings
) {
invokeCommand("setDesktopIntegration", settings);
}

View File

@@ -47,19 +47,19 @@ export const CREATE_BUTTON_MAP = {
},
notebooks: {
title: "Create a notebook",
onClick: () => hashNavigate("/notebooks/create")
onClick: () => hashNavigate("/notebooks/create", { replace: true })
},
topics: {
title: "Create a topic",
onClick: () => hashNavigate(`/topics/create`)
onClick: () => hashNavigate(`/topics/create`, { replace: true })
},
tags: {
title: "Create a tag",
onClick: () => hashNavigate(`/tags/create`)
onClick: () => hashNavigate(`/tags/create`, { replace: true })
},
reminders: {
title: "Add a reminder",
onClick: () => hashNavigate(`/reminders/create`)
onClick: () => hashNavigate(`/reminders/create`, { replace: true })
}
};

View File

@@ -23,9 +23,18 @@ type SpellCheckerOptions = {
enabledLanguages: Language[];
enabled: boolean;
};
type DesktopIntegrationSettings = {
autoStart: boolean;
startMinimized: boolean;
minimizeToSystemTray: boolean;
closeToSystemTray: boolean;
};
declare interface Window {
config: {
static spellChecker(): Promise<SpellCheckerOptions>;
static desktopIntegration(): Promise<DesktopIntegrationSettings>;
};
native: {
static gzip({

View File

@@ -0,0 +1,50 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { useCallback, useEffect, useState } from "react";
import setDesktopIntegration from "../commands/set-desktop-integration";
export default function useDesktopIntegration() {
const [settings, changeSettings] = useState<DesktopIntegrationSettings>();
const setupDesktopIntegration = useCallback(async () => {
const settings = await window.config.desktopIntegration();
changeSettings(settings);
return settings;
}, []);
useEffect(() => {
if (!window.config) return;
(async function () {
await setupDesktopIntegration();
})();
}, [setupDesktopIntegration]);
const set = useCallback(
async (_settings: Partial<DesktopIntegrationSettings>) => {
if (!settings) return;
setDesktopIntegration({ ...settings, ..._settings });
await setupDesktopIntegration();
},
[settings, setupDesktopIntegration]
);
return [settings, set] as const;
}

View File

@@ -77,6 +77,7 @@ import { scheduleBackups } from "../common/reminders";
import usePrivacyMode from "../hooks/use-privacy-mode";
import { useTelemetry } from "../hooks/use-telemetry";
import useSpellChecker from "../hooks/use-spell-checker";
import useDesktopIntegration from "../hooks/use-desktop-integration";
function subscriptionStatusToString(user) {
const status = user?.subscription?.type;
@@ -147,6 +148,7 @@ function Settings() {
privacy: false,
developer: false,
notifications: false,
desktop: false,
other: true
});
const isVaultCreated = useAppStore((store) => store.isVaultCreated);
@@ -196,6 +198,8 @@ function Settings() {
const [privacyMode, setPrivacyMode] = usePrivacyMode();
const [showReminderNotifications, setShowReminderNotifications] =
usePersistentState("reminderNotifications", true);
const [desktopIntegration, changeDesktopIntegration] =
useDesktopIntegration();
const [corsProxy, setCorsProxy] = usePersistentState(
"corsProxy",
"https://cors.notesnook.com"
@@ -534,6 +538,61 @@ function Settings() {
</>
)}
{isDesktop() && (
<>
<Header
title="Desktop integration"
isOpen={groups.desktop}
onClick={() => {
setGroups((g) => ({ ...g, desktop: !g.desktop }));
}}
/>
{groups.desktop && (
<>
<Toggle
title="Auto start on system startup"
onToggled={() => {
changeDesktopIntegration({
autoStart: !desktopIntegration.autoStart
});
}}
isToggled={desktopIntegration.autoStart}
/>
{desktopIntegration.autoStart && (
<Toggle
title="Start minimized"
onToggled={() => {
changeDesktopIntegration({
startMinimized: !desktopIntegration.startMinimized
});
}}
isToggled={desktopIntegration.startMinimized}
/>
)}
<Toggle
title="Minimize to system tray"
onToggled={() => {
changeDesktopIntegration({
minimizeToSystemTray:
!desktopIntegration.minimizeToSystemTray
});
}}
isToggled={desktopIntegration.minimizeToSystemTray}
/>
<Toggle
title="Close to system tray"
onToggled={() => {
changeDesktopIntegration({
closeToSystemTray: !desktopIntegration.closeToSystemTray
});
}}
isToggled={desktopIntegration.closeToSystemTray}
/>
</>
)}
</>
)}
<Header
title="Editor settings"
isOpen={groups.editor}