diff --git a/apps/desktop/package-lock.json b/apps/desktop/package-lock.json
index cb94b613a..eced7e797 100644
--- a/apps/desktop/package-lock.json
+++ b/apps/desktop/package-lock.json
@@ -8,19 +8,20 @@
"name": "@notesnook/desktop",
"version": "2.5.2",
"dependencies": {
- "@trpc/client": "^10.18.0",
- "@trpc/server": "^10.18.0",
+ "@trpc/client": "^10.29.1",
+ "@trpc/server": "^10.29.1",
"diary": "^0.3.1",
- "electron-trpc": "^0.4.2",
+ "electron-trpc": "^0.5.0",
"electron-updater": "^5.3.0",
"icojs": "^0.17.1",
+ "typed-emitter": "^2.1.0",
"yargs": "^17.6.2",
"zod": "^3.21.4"
},
"devDependencies": {
- "@electron/rebuild": "^3.2.10",
+ "@electron/rebuild": "^3.2.13",
"@types/node": "^18.15.0",
- "electron": "^25.1.0",
+ "electron": "^24.4.0",
"electron-builder": "^23.6.0",
"electron-builder-notarize": "^1.5.1",
"electron-reloader": "^1.2.3",
@@ -173,6 +174,7 @@
},
"node_modules/@electron/get": {
"version": "2.0.2",
+ "dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
@@ -191,9 +193,10 @@
}
},
"node_modules/@electron/rebuild": {
- "version": "3.2.10",
+ "version": "3.2.13",
+ "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.2.13.tgz",
+ "integrity": "sha512-DH9Ol4JCnHDYVOD0fKWq+Qqbn/0WU1O6QR0mIpMXEVU4YFM4PlaqNC9K36mGShNBxxGFotZCMDrB1wl/iHM12g==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@malept/cross-spawn-promise": "^2.0.0",
"chalk": "^4.0.0",
@@ -201,7 +204,6 @@
"detect-libc": "^2.0.1",
"fs-extra": "^10.0.0",
"got": "^11.7.0",
- "lzma-native": "^8.0.5",
"node-abi": "^3.0.0",
"node-api-version": "^0.1.4",
"node-gyp": "^9.0.0",
@@ -211,7 +213,7 @@
"yargs": "^17.0.1"
},
"bin": {
- "electron-rebuild": "lib/src/cli.js"
+ "electron-rebuild": "lib/cli.js"
},
"engines": {
"node": ">=12.13.0"
@@ -799,6 +801,7 @@
},
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -809,6 +812,7 @@
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
+ "dev": true,
"license": "MIT",
"dependencies": {
"defer-to-connect": "^2.0.0"
@@ -830,18 +834,27 @@
}
},
"node_modules/@trpc/client": {
- "version": "10.18.0",
- "license": "MIT",
+ "version": "10.29.1",
+ "resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.29.1.tgz",
+ "integrity": "sha512-+9Tifg6dtKsYLsqOW0wizqc3iILAkXxn16pyYAeMDPlulPEqNvnI85GDJ0zJOJLIkQnQefkRbtCmtDxLNtV9Eg==",
+ "funding": [
+ "https://trpc.io/sponsor"
+ ],
"peerDependencies": {
- "@trpc/server": "10.18.0"
+ "@trpc/server": "10.29.1"
}
},
"node_modules/@trpc/server": {
- "version": "10.18.0",
- "license": "MIT"
+ "version": "10.29.1",
+ "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.29.1.tgz",
+ "integrity": "sha512-kNXgMh5ya+awuz2tB4eIyVrRs7nVtqGXwSGabzH3l5ZLWz7rbKJquOJ7h6bjvIfWUpaFG62HJNWxxGUtXCRgRw==",
+ "funding": [
+ "https://trpc.io/sponsor"
+ ]
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/http-cache-semantics": "*",
@@ -878,10 +891,12 @@
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.1",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/keyv": {
"version": "3.1.4",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
@@ -900,6 +915,7 @@
},
"node_modules/@types/node": {
"version": "18.15.11",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/normalize-package-data": {
@@ -909,6 +925,7 @@
},
"node_modules/@types/responselike": {
"version": "1.0.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
@@ -933,6 +950,7 @@
},
"node_modules/@types/yauzl": {
"version": "2.10.0",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1273,6 +1291,7 @@
},
"node_modules/boolean": {
"version": "3.2.0",
+ "dev": true,
"license": "MIT",
"optional": true
},
@@ -1335,6 +1354,7 @@
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
+ "dev": true,
"license": "MIT",
"engines": {
"node": "*"
@@ -1512,6 +1532,7 @@
},
"node_modules/cacheable-lookup": {
"version": "5.0.4",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10.6.0"
@@ -1519,6 +1540,7 @@
},
"node_modules/cacheable-request": {
"version": "7.0.2",
+ "dev": true,
"license": "MIT",
"dependencies": {
"clone-response": "^1.0.2",
@@ -1638,6 +1660,7 @@
},
"node_modules/clone-response": {
"version": "1.0.3",
+ "dev": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^1.0.0"
@@ -1777,6 +1800,7 @@
},
"node_modules/decompress-response": {
"version": "6.0.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
@@ -1790,6 +1814,7 @@
},
"node_modules/decompress-response/node_modules/mimic-response": {
"version": "3.1.0",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -1811,6 +1836,7 @@
},
"node_modules/defer-to-connect": {
"version": "2.0.1",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -1818,6 +1844,7 @@
},
"node_modules/define-properties": {
"version": "1.2.0",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1862,6 +1889,7 @@
},
"node_modules/detect-node": {
"version": "2.1.0",
+ "dev": true,
"license": "MIT",
"optional": true
},
@@ -1981,9 +2009,10 @@
}
},
"node_modules/electron": {
- "version": "25.1.0",
- "resolved": "https://registry.npmjs.org/electron/-/electron-25.1.0.tgz",
- "integrity": "sha512-VKk4G/0euO7ysMKQKHXmI4d3/qR4uHsAtVFXK2WfQUVxBmc160OAm2R6PN9/EXmgXEioKQBtbc2/lvWyYpDbuA==",
+ "version": "24.5.0",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-24.5.0.tgz",
+ "integrity": "sha512-9Xo2EFZHWeuw1otm9mcJYKCNC64fPRpgp+ZJWMJ9RtvsnSgcuitkM4esZv4gIsqhWk5yiKApYHqinIUyu82O0Q==",
+ "dev": true,
"hasInstallScript": true,
"dependencies": {
"@electron/get": "^2.0.0",
@@ -2253,8 +2282,12 @@
}
},
"node_modules/electron-trpc": {
- "version": "0.4.2",
- "license": "MIT",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/electron-trpc/-/electron-trpc-0.5.0.tgz",
+ "integrity": "sha512-xvnvOpuI0IiecMGS9VOS5/3kLj6IUpjngKhv5VqXU9vP2LzE140G8gqaziX0Tl7MaiYmJZbad/imis9i49n/4A==",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
"peerDependencies": {
"@trpc/client": ">10.0.0",
"@trpc/server": ">10.0.0",
@@ -2333,6 +2366,7 @@
},
"node_modules/end-of-stream": {
"version": "1.4.4",
+ "dev": true,
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
@@ -2340,6 +2374,7 @@
},
"node_modules/env-paths": {
"version": "2.2.1",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -2360,6 +2395,7 @@
},
"node_modules/es6-error": {
"version": "4.1.1",
+ "dev": true,
"license": "MIT",
"optional": true
},
@@ -2409,6 +2445,7 @@
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
+ "dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -2432,6 +2469,7 @@
},
"node_modules/extract-zip": {
"version": "2.0.1",
+ "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"debug": "^4.1.1",
@@ -2460,6 +2498,7 @@
},
"node_modules/fd-slicer": {
"version": "1.1.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"pend": "~1.2.0"
@@ -2543,6 +2582,7 @@
},
"node_modules/fs-extra": {
"version": "8.1.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
@@ -2571,7 +2611,7 @@
},
"node_modules/function-bind": {
"version": "1.1.1",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/gauge": {
@@ -2601,6 +2641,7 @@
},
"node_modules/get-intrinsic": {
"version": "1.2.0",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2614,6 +2655,7 @@
},
"node_modules/get-stream": {
"version": "5.2.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"pump": "^3.0.0"
@@ -2657,6 +2699,7 @@
},
"node_modules/global-agent": {
"version": "3.0.0",
+ "dev": true,
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
@@ -2673,6 +2716,7 @@
},
"node_modules/global-agent/node_modules/semver": {
"version": "7.3.8",
+ "dev": true,
"license": "ISC",
"optional": true,
"dependencies": {
@@ -2687,6 +2731,7 @@
},
"node_modules/globalthis": {
"version": "1.0.3",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2701,6 +2746,7 @@
},
"node_modules/got": {
"version": "11.8.6",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@sindresorhus/is": "^4.0.0",
@@ -2733,7 +2779,7 @@
},
"node_modules/has": {
"version": "1.0.3",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.1"
@@ -2752,6 +2798,7 @@
},
"node_modules/has-property-descriptors": {
"version": "1.0.0",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2763,6 +2810,7 @@
},
"node_modules/has-symbols": {
"version": "1.0.3",
+ "dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -2790,6 +2838,7 @@
},
"node_modules/http-cache-semantics": {
"version": "4.1.0",
+ "dev": true,
"license": "BSD-2-Clause"
},
"node_modules/http-proxy-agent": {
@@ -2807,6 +2856,7 @@
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
+ "dev": true,
"license": "MIT",
"dependencies": {
"quick-lru": "^5.1.1",
@@ -3094,6 +3144,7 @@
},
"node_modules/json-buffer": {
"version": "3.0.1",
+ "dev": true,
"license": "MIT"
},
"node_modules/json-parse-even-better-errors": {
@@ -3108,6 +3159,7 @@
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
+ "dev": true,
"license": "ISC",
"optional": true
},
@@ -3124,6 +3176,7 @@
},
"node_modules/jsonfile": {
"version": "4.0.0",
+ "dev": true,
"license": "MIT",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
@@ -3131,6 +3184,7 @@
},
"node_modules/keyv": {
"version": "4.5.2",
+ "dev": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
@@ -3197,6 +3251,7 @@
},
"node_modules/lowercase-keys": {
"version": "2.0.0",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -3212,23 +3267,6 @@
"node": ">=10"
}
},
- "node_modules/lzma-native": {
- "version": "8.0.6",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "node-addon-api": "^3.1.0",
- "node-gyp-build": "^4.2.1",
- "readable-stream": "^3.6.0"
- },
- "bin": {
- "lzmajs": "bin/lzmajs"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
"node_modules/make-fetch-happen": {
"version": "10.2.1",
"dev": true,
@@ -3265,6 +3303,7 @@
},
"node_modules/matcher": {
"version": "3.0.0",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -3326,6 +3365,7 @@
},
"node_modules/mimic-response": {
"version": "1.0.1",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -3462,9 +3502,10 @@
"license": "MIT"
},
"node_modules/node-abi": {
- "version": "3.33.0",
+ "version": "3.44.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.44.0.tgz",
+ "integrity": "sha512-MYjZTiAETGG28/7fBH1RjuY7vzDwYC5q5U4whCgM4jNEQcC0gAvN339LxXukmL2T2tGpzYTfp+LZ5RN7E5DwEg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"semver": "^7.3.5"
},
@@ -3486,11 +3527,6 @@
"node": ">=10"
}
},
- "node_modules/node-addon-api": {
- "version": "3.2.1",
- "dev": true,
- "license": "MIT"
- },
"node_modules/node-api-version": {
"version": "0.1.4",
"dev": true,
@@ -3536,16 +3572,6 @@
"node": "^12.13 || ^14.13 || >=16"
}
},
- "node_modules/node-gyp-build": {
- "version": "4.6.0",
- "dev": true,
- "license": "MIT",
- "bin": {
- "node-gyp-build": "bin.js",
- "node-gyp-build-optional": "optional.js",
- "node-gyp-build-test": "build-test.js"
- }
- },
"node_modules/node-gyp/node_modules/semver": {
"version": "7.3.8",
"dev": true,
@@ -3608,6 +3634,7 @@
},
"node_modules/normalize-url": {
"version": "6.1.0",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -3632,6 +3659,7 @@
},
"node_modules/object-keys": {
"version": "1.1.1",
+ "dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -3640,6 +3668,7 @@
},
"node_modules/once": {
"version": "1.4.0",
+ "dev": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
@@ -3706,6 +3735,7 @@
},
"node_modules/p-cancelable": {
"version": "2.1.1",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -3980,6 +4010,7 @@
},
"node_modules/pend": {
"version": "1.2.0",
+ "dev": true,
"license": "MIT"
},
"node_modules/picomatch": {
@@ -4014,6 +4045,7 @@
},
"node_modules/progress": {
"version": "2.0.3",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
@@ -4038,6 +4070,7 @@
},
"node_modules/pump": {
"version": "3.0.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
@@ -4054,6 +4087,7 @@
},
"node_modules/quick-lru": {
"version": "5.1.1",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -4229,10 +4263,12 @@
},
"node_modules/resolve-alpn": {
"version": "1.2.1",
+ "dev": true,
"license": "MIT"
},
"node_modules/responselike": {
"version": "2.0.1",
+ "dev": true,
"license": "MIT",
"dependencies": {
"lowercase-keys": "^2.0.0"
@@ -4277,6 +4313,7 @@
},
"node_modules/roarr": {
"version": "2.15.4",
+ "dev": true,
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
@@ -4336,6 +4373,7 @@
},
"node_modules/semver": {
"version": "6.3.0",
+ "dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -4343,11 +4381,13 @@
},
"node_modules/semver-compare": {
"version": "1.0.0",
+ "dev": true,
"license": "MIT",
"optional": true
},
"node_modules/serialize-error": {
"version": "7.0.1",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -4362,6 +4402,7 @@
},
"node_modules/serialize-error/node_modules/type-fest": {
"version": "0.13.1",
+ "dev": true,
"license": "(MIT OR CC0-1.0)",
"optional": true,
"engines": {
@@ -4509,6 +4550,7 @@
},
"node_modules/sprintf-js": {
"version": "1.1.2",
+ "dev": true,
"license": "BSD-3-Clause",
"optional": true
},
@@ -4586,6 +4628,7 @@
},
"node_modules/sumchecker": {
"version": "3.0.1",
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.1.0"
@@ -4783,6 +4826,7 @@
},
"node_modules/universalify": {
"version": "0.1.2",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
@@ -4861,6 +4905,7 @@
},
"node_modules/wrappy": {
"version": "1.0.2",
+ "dev": true,
"license": "ISC"
},
"node_modules/xmlbuilder": {
@@ -4915,6 +4960,7 @@
},
"node_modules/yauzl": {
"version": "2.10.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"buffer-crc32": "~0.2.3",
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 3919b6acf..d438c3aa0 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -11,19 +11,20 @@
"repository": "https://github.com/streetwriters/notesnook",
"dependencies": {
"@notesnook/crypto": "*",
- "@trpc/client": "^10.18.0",
- "@trpc/server": "^10.18.0",
+ "@trpc/client": "^10.29.1",
+ "@trpc/server": "^10.29.1",
"diary": "^0.3.1",
- "electron-trpc": "^0.4.2",
+ "electron-trpc": "^0.5.0",
"electron-updater": "^5.3.0",
"icojs": "^0.17.1",
"yargs": "^17.6.2",
- "zod": "^3.21.4"
+ "zod": "^3.21.4",
+ "typed-emitter": "^2.1.0"
},
"devDependencies": {
- "@electron/rebuild": "^3.2.10",
+ "@electron/rebuild": "^3.2.13",
"@types/node": "^18.15.0",
- "electron": "^25.1.0",
+ "electron": "^24.4.0",
"electron-builder": "^23.6.0",
"electron-builder-notarize": "^1.5.1",
"electron-reloader": "^1.2.3",
@@ -32,12 +33,12 @@
"undici": "^5.22.1"
},
"scripts": {
- "start": "npm run build:electron && electron build/electron.js",
+ "start": "turbowatch scripts/dev.ts",
+ "staging": "zx scripts/staging.mjs",
"build": "tsc",
"build:electron": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --minify --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
"build:mas": "esbuild ./electron.js ./preload.js --minify --external:electron --external:fsevents --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=true --define:RELEASE=true",
- "pack": "rm -rf ./build && cp -r ../build ./ && npm run build && yarn electron-builder --linux AppImage:x64 AppImage:arm64",
- "postinstall": "patch-package"
+ "postinstall": "electron-builder install-app-deps && patch-package"
},
"author": {
"name": "Streetwriters (Private) Limited",
@@ -53,8 +54,8 @@
"!*.chunk.js.map",
"!*.chunk.js.LICENSE.txt",
"build/",
- "!build/screenshots",
- "!node_modules"
+ "!build/screenshots${/*}",
+ "!node_modules${/*}"
],
"afterSign": "electron-builder-notarize",
"afterPack": "./scripts/removeLocales.js",
@@ -185,7 +186,7 @@
],
"directories": {
"buildResources": "assets",
- "output": "./dist/"
+ "output": "./output/"
},
"publish": [
{
diff --git a/apps/desktop/scripts/dev.ts b/apps/desktop/scripts/dev.ts
new file mode 100644
index 000000000..d80d0a55a
--- /dev/null
+++ b/apps/desktop/scripts/dev.ts
@@ -0,0 +1,117 @@
+/*
+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 path from "path";
+import fs from "fs/promises";
+import { watch } from "turbowatch";
+import { $, within } from "zx";
+
+const sodiumNativePrebuildPath = path.join(
+ `node_modules`,
+ `@notesnook`,
+ `crypto`,
+ `node_modules`,
+ `@notesnook`,
+ `sodium`,
+ `node_modules`,
+ `sodium-native`,
+ `prebuilds`
+);
+let isServerRunning = false;
+
+async function main() {
+ const { shutdown } = await watch({
+ project: path.join(__dirname, ".."),
+ cwd: path.join(__dirname, ".."),
+ triggers: [
+ {
+ expression: [
+ "allof",
+ ["not", ["dirname", "node_modules"]],
+ ["match", "*.ts", "basename"]
+ ],
+ name: "dev",
+ // retry: { retries: 0 },
+ onTeardown: async () => {
+ await fs.rm("./build/", { force: true, recursive: true });
+ },
+ onChange: async ({ spawn: $, first, log }) => {
+ if (first) {
+ await fs.rm("./build/", { force: true, recursive: true });
+ }
+
+ await $`npm run build:electron`;
+ await $`tsc`;
+
+ if (first) {
+ await fs.cp(sodiumNativePrebuildPath, "build/prebuilds", {
+ recursive: true,
+ force: true
+ });
+ }
+
+ if (!isServerRunning) {
+ await spawnAndWaitUntil(
+ path.join(__dirname, "..", "..", "web"),
+ "npm run start:desktop",
+ (data) => data.includes("Compiled successfully!")
+ );
+ isServerRunning = true;
+ }
+
+ log("Starting desktop app!");
+
+ await $`npx electron ${path.join("build", "electron.js")}`;
+ }
+ }
+ ]
+ });
+
+ // SIGINT is the signal sent when we press Ctrl+C
+ process.once("SIGINT", () => {
+ void shutdown();
+ });
+}
+
+main();
+
+function spawnAndWaitUntil(
+ cwd: string,
+ cmd: string,
+ predicate: (data: string) => boolean
+) {
+ return new Promise((resolve) => {
+ within(async () => {
+ $.env = process.env;
+ $.quote = (c) => c;
+
+ try {
+ const s = $`cd ${cwd} && ${cmd}`;
+ s.stdout.on("data", (data) => {
+ if (predicate(data)) resolve(undefined); //
+ });
+ await s;
+ } catch (e) {
+ //ignore
+ } finally {
+ isServerRunning = false;
+ }
+ });
+ });
+}
diff --git a/apps/desktop/scripts/staging.mjs b/apps/desktop/scripts/staging.mjs
new file mode 100644
index 000000000..e10129032
--- /dev/null
+++ b/apps/desktop/scripts/staging.mjs
@@ -0,0 +1,65 @@
+/*
+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 path from "path";
+import fs from "fs/promises";
+import { $, argv, os } from "zx";
+
+$.env = process.env;
+
+const sodiumNativePrebuildPath = path.join(
+ `node_modules`,
+ `@notesnook`,
+ `crypto`,
+ `node_modules`,
+ `@notesnook`,
+ `sodium`,
+ `node_modules`,
+ `sodium-native`,
+ `prebuilds`,
+ `${os.platform()}-x64`
+);
+
+await fs.rm("./build/", { force: true, recursive: true });
+
+if (argv.rebuild) await $`cd ../web/ && npm run build:desktop`;
+
+await fs.cp(path.join("..", "web", "build"), "build", {
+ recursive: true,
+ force: true
+});
+
+await $`npm run build:electron`;
+await $`tsc`;
+await $`npm rebuild`;
+
+await fs.cp(
+ sodiumNativePrebuildPath,
+ path.join("build", "prebuilds", `${process.platform}-x64`),
+ {
+ recursive: true,
+ force: true
+ }
+);
+
+if (argv.run) {
+ await $`yarn electron-builder -c.extraMetadata.main=./build/electron.js --linux AppImage:x64`;
+
+ await $`./output/notesnook_linux_x86_64.AppImage`;
+}
diff --git a/apps/desktop/src/api/bridge.ts b/apps/desktop/src/api/bridge.ts
new file mode 100644
index 000000000..cdb6e12c0
--- /dev/null
+++ b/apps/desktop/src/api/bridge.ts
@@ -0,0 +1,58 @@
+/*
+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 { initTRPC } from "@trpc/server";
+import { observable } from "@trpc/server/observable";
+import { EventEmitter } from "events";
+import TypedEventEmitter from "typed-emitter";
+
+export type AppEvents = {
+ onCreateItem(name: "note" | "notebook" | "reminder"): void;
+};
+
+const emitter = new EventEmitter();
+const typedEmitter = emitter as TypedEventEmitter;
+const t = initTRPC.create();
+
+export const bridgeRouter = t.router({
+ onCreateItem: createSubscription("onCreateItem")
+});
+
+export const bridge: AppEvents = new Proxy({} as AppEvents, {
+ get(_t, name) {
+ if (typeof name === "symbol") return;
+ return (...args: unknown[]) => {
+ emitter.emit(name, ...args);
+ };
+ }
+});
+
+function createSubscription(eventName: TName) {
+ return t.procedure.subscription(() => {
+ return observable[0]>((emit) => {
+ const listener: AppEvents[TName] = (...args: any[]) => {
+ emit.next(args[0]);
+ };
+ typedEmitter.addListener(eventName, listener);
+ return () => {
+ typedEmitter.removeListener(eventName, listener);
+ };
+ });
+ });
+}
diff --git a/apps/desktop/src/api/index.ts b/apps/desktop/src/api/index.ts
index 5350d7ade..9c20fdb86 100644
--- a/apps/desktop/src/api/index.ts
+++ b/apps/desktop/src/api/index.ts
@@ -22,6 +22,7 @@ import { compressionRouter } from "./compression";
import { osIntegrationRouter } from "./os-integration";
import { spellCheckerRouter } from "./spell-checker";
import { updaterRouter } from "./updater";
+import { bridgeRouter } from "./bridge";
const t = initTRPC.create();
@@ -29,7 +30,8 @@ export const router = t.router({
compress: compressionRouter,
integration: osIntegrationRouter,
spellChecker: spellCheckerRouter,
- updater: updaterRouter
+ updater: updaterRouter,
+ bridge: bridgeRouter
});
export const api = router.createCaller({});
diff --git a/apps/desktop/src/api/os-integration.ts b/apps/desktop/src/api/os-integration.ts
index ab21d7318..73d5b5e03 100644
--- a/apps/desktop/src/api/os-integration.ts
+++ b/apps/desktop/src/api/os-integration.ts
@@ -19,16 +19,16 @@ along with this program. If not, see .
import { initTRPC } from "@trpc/server";
import { z } from "zod";
-import { dialog, Notification, shell } from "electron";
+import { dialog, nativeTheme, Notification, shell } from "electron";
import { AutoLaunch } from "../utils/autolaunch";
import { config, DesktopIntegration } from "../utils/config";
import { bringToFront } from "../utils/bring-to-front";
-import { setTheme, Theme } from "../utils/theme";
+import { getTheme, setTheme, Theme } from "../utils/theme";
import { mkdirSync, writeFileSync } from "fs";
import { dirname, join } from "path";
import { platform } from "os";
import { resolvePath } from "../utils/resolve-path";
-import { client } from "../rpc/electron";
+import { observable } from "@trpc/server/observable";
const t = initTRPC.create();
@@ -132,7 +132,10 @@ export const osIntegrationRouter = t.router({
shell.beep();
}
- client.onNotificationClicked(input.tag);
+ return new Promise((resolve) => {
+ notification.once("close", () => resolve(undefined));
+ notification.once("click", () => resolve(input.tag));
+ });
}),
openPath: t.procedure
.input(z.object({ type: z.literal("path"), link: z.string() }))
@@ -141,5 +144,17 @@ export const osIntegrationRouter = t.router({
if (type === "path") return shell.openPath(resolvePath(link));
}),
bringToFront: t.procedure.query(() => bringToFront()),
- changeTheme: t.procedure.input(Theme).mutation(({ input }) => setTheme(input))
+ changeTheme: t.procedure
+ .input(Theme)
+ .mutation(({ input }) => setTheme(input)),
+
+ onThemeChanged: t.procedure.subscription(() =>
+ observable<"dark" | "light">((emit) => {
+ nativeTheme.on("updated", () => {
+ if (getTheme() === "system") {
+ emit.next(nativeTheme.shouldUseDarkColors ? "dark" : "light");
+ }
+ });
+ })
+ )
});
diff --git a/apps/desktop/src/api/updater.ts b/apps/desktop/src/api/updater.ts
index 381f8d5ef..50094e334 100644
--- a/apps/desktop/src/api/updater.ts
+++ b/apps/desktop/src/api/updater.ts
@@ -18,7 +18,12 @@ along with this program. If not, see .
*/
import { initTRPC } from "@trpc/server";
+import { observable } from "@trpc/server/observable";
import { CancellationToken, autoUpdater } from "electron-updater";
+import type { AppUpdaterEvents } from "electron-updater/out/AppUpdater";
+
+type UpdateInfo = { version: string };
+type Progress = { percent: number };
const t = initTRPC.create();
@@ -30,5 +35,37 @@ export const updaterRouter = t.router({
}),
check: t.procedure.query(async () => {
await autoUpdater.checkForUpdates();
- })
+ }),
+
+ onChecking: createSubscription("checking-for-update"),
+ onDownloaded: createSubscription<"update-downloaded", UpdateInfo>(
+ "update-downloaded"
+ ),
+ onDownloadProgress: createSubscription<"download-progress", Progress>(
+ "download-progress"
+ ),
+ onNotAvailable: createSubscription<"update-not-available", UpdateInfo>(
+ "update-not-available"
+ ),
+ onAvailable: createSubscription<"update-available", UpdateInfo>(
+ "update-available"
+ ),
+ onError: createSubscription("error")
});
+
+function createSubscription<
+ TName extends keyof AppUpdaterEvents,
+ TReturnType = Parameters[0]
+>(eventName: TName) {
+ return t.procedure.subscription(() => {
+ return observable((emit) => {
+ const listener: AppUpdaterEvents[TName] = (...args: any[]) => {
+ emit.next(args[0]);
+ };
+ autoUpdater.addListener(eventName, listener);
+ return () => {
+ autoUpdater.removeListener(eventName, listener);
+ };
+ });
+ });
+}
diff --git a/apps/desktop/src/index.ts b/apps/desktop/src/index.ts
index fc157e147..30dcf6689 100644
--- a/apps/desktop/src/index.ts
+++ b/apps/desktop/src/index.ts
@@ -19,4 +19,4 @@ along with this program. If not, see .
export * from "./constants";
export type { AppRouter } from "./api";
-export * from "./rpc";
+export { type UpdateInfo } from "builder-util-runtime";
diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts
index 18f3ffd73..329968966 100644
--- a/apps/desktop/src/main.ts
+++ b/apps/desktop/src/main.ts
@@ -18,7 +18,6 @@ along with this program. If not, see .
*/
/* global MAC_APP_STORE, RELEASE */
-import { client } from "./rpc/electron";
import { app, BrowserWindow, nativeTheme, shell } from "electron";
import { isDevelopment } from "./utils";
import { registerProtocol, PROTOCOL_URL } from "./utils/protocol";
@@ -118,10 +117,6 @@ async function createWindow() {
nativeTheme.on("updated", () => {
setupTray();
setupJumplist();
-
- if (getTheme() === "system") {
- client.onThemeChanged(nativeTheme.shouldUseDarkColors ? "dark" : "light");
- }
});
}
@@ -129,7 +124,7 @@ app.commandLine.appendSwitch("lang", "en-US");
app.on("ready", async () => {
console.info("App ready. Opening window.");
- registerProtocol();
+ if (!isDevelopment()) registerProtocol();
await createWindow();
configureAutoUpdater();
});
diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts
index 507708662..620fd6c1e 100644
--- a/apps/desktop/src/preload.ts
+++ b/apps/desktop/src/preload.ts
@@ -22,16 +22,15 @@ import { ELECTRON_TRPC_CHANNEL } from "electron-trpc/main";
import { type RendererGlobalElectronTRPC } from "electron-trpc/src/types";
import { NNCrypto } from "@notesnook/crypto";
import { ipcRenderer } from "electron";
-import { CHANNEL, ITransport } from "./rpc";
declare global {
var os: string;
var electronTRPC: RendererGlobalElectronTRPC;
- var RPCTransport: ITransport;
var NativeNNCrypto: new () => NNCrypto;
}
-
+console.log("HELLO", process);
process.once("loaded", async () => {
+ console.log("HELLO!");
const electronTRPC: RendererGlobalElectronTRPC = {
sendMessage: (operation) =>
ipcRenderer.send(ELECTRON_TRPC_CHANNEL, operation),
@@ -39,20 +38,6 @@ process.once("loaded", async () => {
ipcRenderer.on(ELECTRON_TRPC_CHANNEL, (_event, args) => callback(args))
};
globalThis.electronTRPC = electronTRPC;
-
- globalThis.RPCTransport = {
- send(message) {
- console.log("[browser] sending message", message);
- ipcRenderer.send(CHANNEL, message);
- },
- receive(callback) {
- ipcRenderer.removeAllListeners(CHANNEL);
- ipcRenderer.addListener(CHANNEL, (_event, args) => {
- console.log("[browser] recevied message", args);
- callback(args);
- });
- }
- };
});
globalThis.NativeNNCrypto = NNCrypto;
diff --git a/apps/desktop/src/rpc/electron.ts b/apps/desktop/src/rpc/electron.ts
deleted file mode 100644
index 5c38c2e19..000000000
--- a/apps/desktop/src/rpc/electron.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-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 { ipcMain } from "electron";
-import { CHANNEL, createRPCClient } from ".";
-import { ClientPrototype, ITransport } from "./types";
-
-const transport: ITransport = {
- send(message) {
- console.log("[electron] sending message", message);
- globalThis.window?.webContents.send(CHANNEL, message);
- },
- receive(callback) {
- ipcMain.on(CHANNEL, (_event, message) => {
- console.log("[electron] received message", message);
- callback(message);
- });
- }
-};
-
-export const client = createRPCClient(transport, ClientPrototype);
diff --git a/apps/desktop/src/rpc/index.ts b/apps/desktop/src/rpc/index.ts
deleted file mode 100644
index bbf28edf8..000000000
--- a/apps/desktop/src/rpc/index.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-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 { ITransport } from "./types";
-
-export const CHANNEL = "RPC_COM_CHANNEL";
-type RPCApi = Record any>;
-type WrappedAPI = {
- [P in keyof TApi]: (
- ...args: Parameters
- ) => ReturnType extends void
- ? void
- : ReturnType extends Promise
- ? Promise
- : Promise>;
-};
-
-export function createRPCClient(
- transport: ITransport,
- api: TApi
-): WrappedAPI {
- const wrappedAPI = >{};
- for (const method in api) {
- if (Object.hasOwn(api, method)) {
- wrappedAPI[method] = (...args: any[]) => {
- return new Promise((resolve) => {
- transport.receive((message) => {
- if (message.type === "response" && message.id === method) {
- resolve(message.result);
- }
- });
-
- transport.send({
- type: "message",
- id: method,
- args
- });
- });
- };
- }
- }
- return wrappedAPI;
-}
-
-export function createRPCServer(
- transport: ITransport,
- api: TApi
-) {
- transport.receive(async (message) => {
- if (
- message.type === "message" &&
- message.id &&
- Object.hasOwn(api, message.id)
- ) {
- const result = await api[message.id](...message.args);
- transport.send({ type: "response", id: message.id, result });
- }
- });
-}
-
-export * from "./types";
diff --git a/apps/desktop/src/rpc/types.ts b/apps/desktop/src/rpc/types.ts
deleted file mode 100644
index 30abae563..000000000
--- a/apps/desktop/src/rpc/types.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-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 type {
- UpdateInfo,
- ProgressInfo,
- UpdateDownloadedEvent
-} from "electron-updater";
-import type { Theme } from "../utils/theme";
-
-export const ClientPrototype = {
- onCheckingForUpdate() {},
- onUpdateAvailable(info: UpdateInfo) {},
- onUpdateDownloadProgress(progress: ProgressInfo) {},
- onUpdateDownloadCompleted(info: UpdateDownloadedEvent) {},
- onUpdateNotAvailable(info: UpdateInfo) {},
- onThemeChanged(theme: Theme) {},
- onNotificationClicked(tag: string) {},
- onCreateItem(type: "note" | "notebook" | "reminder") {
- return type;
- }
-};
-
-export type IClient = typeof ClientPrototype;
-export type IClientMethod = keyof IClient;
-
-export type IMessage = {
- type: "message";
- id: string;
- args: unknown[];
-};
-export type IResponse = {
- type: "response";
- id: string;
- result: unknown;
-};
-export type ITransport = {
- send(message: IMessage | IResponse): void;
- receive(callback: (message: IMessage | IResponse) => void): void;
-};
diff --git a/apps/desktop/src/utils/autoupdater.ts b/apps/desktop/src/utils/autoupdater.ts
index db0636e84..357834626 100644
--- a/apps/desktop/src/utils/autoupdater.ts
+++ b/apps/desktop/src/utils/autoupdater.ts
@@ -18,7 +18,6 @@ along with this program. If not, see .
*/
import { autoUpdater } from "electron-updater";
-import { client } from "../rpc/electron";
async function configureAutoUpdater() {
autoUpdater.setFeedURL({
@@ -31,14 +30,6 @@ async function configureAutoUpdater() {
autoUpdater.allowDowngrade = false;
autoUpdater.allowPrerelease = false;
autoUpdater.autoInstallOnAppQuit = true;
- autoUpdater.addListener("checking-for-update", client.onCheckingForUpdate);
- autoUpdater.addListener("update-available", client.onUpdateAvailable);
- autoUpdater.addListener("download-progress", client.onUpdateDownloadProgress);
- autoUpdater.addListener(
- "update-downloaded",
- client.onUpdateDownloadCompleted
- );
- autoUpdater.addListener("update-not-available", client.onUpdateNotAvailable);
}
export { configureAutoUpdater };
diff --git a/apps/desktop/src/utils/jumplist.ts b/apps/desktop/src/utils/jumplist.ts
index 8efea0c77..1fdbeae41 100644
--- a/apps/desktop/src/utils/jumplist.ts
+++ b/apps/desktop/src/utils/jumplist.ts
@@ -18,9 +18,9 @@ along with this program. If not, see .
*/
import { app, Menu } from "electron";
-import { client } from "../rpc/electron";
import { AssetManager } from "./asset-manager";
import { bringToFront } from "./bring-to-front";
+import { bridge } from "../api/bridge";
export function setupJumplist() {
if (process.platform === "win32") {
@@ -75,7 +75,7 @@ function setDockMenuOnMacOs() {
type: "normal",
click: () => {
bringToFront();
- client.onCreateItem("note");
+ bridge.onCreateItem("note");
}
},
{
@@ -83,7 +83,7 @@ function setDockMenuOnMacOs() {
type: "normal",
click: () => {
bringToFront();
- client.onCreateItem("notebook");
+ bridge.onCreateItem("notebook");
}
},
{
@@ -91,7 +91,7 @@ function setDockMenuOnMacOs() {
type: "normal",
click: () => {
bringToFront();
- client.onCreateItem("reminder");
+ bridge.onCreateItem("reminder");
}
}
]);
diff --git a/apps/desktop/src/utils/protocol.ts b/apps/desktop/src/utils/protocol.ts
index 7c6504cc2..4360559fb 100644
--- a/apps/desktop/src/utils/protocol.ts
+++ b/apps/desktop/src/utils/protocol.ts
@@ -17,14 +17,16 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { protocol, net } from "electron";
+import { protocol, ProtocolRequest } from "electron";
import { isDevelopment } from "./index";
import { createReadStream, statSync } from "fs";
import { extname, join, normalize } from "path";
import { URL } from "url";
+import { Readable } from "stream";
const BASE_PATH = isDevelopment() ? "../public" : "";
const HOSTNAME = `app.notesnook.com`;
+const FILE_NOT_FOUND = -6;
const SCHEME = "https";
const extensionToMimeType: Record = {
html: "text/html",
@@ -36,36 +38,98 @@ const extensionToMimeType: Record = {
};
function registerProtocol() {
- protocol.handle(SCHEME, (request) => {
- const url = new URL(request.url);
- if (shouldInterceptRequest(url)) {
- console.info("Intercepting request:", request);
+ const protocolInterceptionResult = protocol.interceptStreamProtocol(
+ SCHEME,
+ async (request, callback) => {
+ const url = new URL(request.url);
+ if (shouldInterceptRequest(url)) {
+ console.info("Intercepting request:", request.url);
- const loadIndex = !extname(url.pathname);
- const absoluteFilePath = normalize(
- `${__dirname}${
- loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}`
- }`
- );
- const filePath = getPath(absoluteFilePath);
- if (!filePath) {
- console.error("Local asset file not found at", filePath);
- return new Response(undefined, {
- status: 404,
- statusText: "FILE_NOT_FOUND"
+ const loadIndex = !extname(url.pathname);
+ const absoluteFilePath = normalize(
+ `${__dirname}${
+ loadIndex
+ ? `${BASE_PATH}/index.html`
+ : `${BASE_PATH}/${url.pathname}`
+ }`
+ );
+ const filePath = getPath(absoluteFilePath);
+ if (!filePath) {
+ console.error("Local asset file not found at", filePath);
+ callback({ error: FILE_NOT_FOUND });
+ return;
+ }
+ const fileExtension = extname(filePath).replace(".", "");
+
+ const data = createReadStream(filePath);
+ callback({
+ data,
+ mimeType: extensionToMimeType[fileExtension]
+ });
+ } else {
+ let response: Response;
+ try {
+ const body = await getBody(request);
+ response = await fetch(request.url, {
+ ...request,
+ body,
+ headers: {
+ ...request.headers
+ // origin: `${PROTOCOL}://${HOSTNAME}/`
+ },
+ referrer: request.referrer,
+ redirect: "manual"
+ });
+ } catch (e) {
+ console.error(e);
+ console.error(`Error sending request to `, request.url, "Error: ", e);
+ callback({ statusCode: 400 });
+ return;
+ }
+ callback({
+ statusCode: response.status,
+ data: response.body ? Readable.fromWeb(response.body) : undefined,
+ headers: Object.fromEntries(response.headers.entries()),
+ mimeType: response.headers.get("Content-Type") || undefined
});
}
- const fileExtension = extname(filePath).replace(".", "");
- const data = createReadStream(filePath);
- const headers = new Headers();
- headers.set("Content-Type", extensionToMimeType[fileExtension]);
- return new Response(data, { headers });
- } else {
- return net.fetch(request);
}
- });
+ );
- console.info(`${SCHEME} protocol inteception "successful"`);
+ console.info(
+ `${SCHEME} protocol inteception ${
+ protocolInterceptionResult ? "successful" : "failed"
+ }.`
+ );
+
+ // protocol.handle(SCHEME, (request) => {
+ // const url = new URL(request.url);
+ // if (shouldInterceptRequest(url)) {
+ // console.info("Intercepting request:", request.url);
+ // const loadIndex = !extname(url.pathname);
+ // const absoluteFilePath = normalize(
+ // `${__dirname}${
+ // loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}`
+ // }`
+ // );
+ // const filePath = getPath(absoluteFilePath);
+ // if (!filePath) {
+ // console.error("Local asset file not found at", filePath);
+ // return new Response(undefined, {
+ // status: 404,
+ // statusText: "FILE_NOT_FOUND"
+ // });
+ // }
+ // const fileExtension = extname(filePath).replace(".", "");
+ // const data = createReadStream(filePath);
+ // return new Response(data, {
+ // headers: { "Content-Type": extensionToMimeType[fileExtension] }
+ // });
+ // } else {
+ // return net.fetch(request);
+ // }
+ // });
+ // console.info(`${SCHEME} protocol inteception "successful"`);
}
const bypassedRoutes: string[] = [];
@@ -77,6 +141,24 @@ function shouldInterceptRequest(url: URL) {
const PROTOCOL_URL = `${SCHEME}://${HOSTNAME}/`;
export { registerProtocol, PROTOCOL_URL };
+async function getBody(request: ProtocolRequest) {
+ const session = globalThis?.window?.webContents?.session;
+
+ const blobParts = [];
+ if (!request.uploadData || !request.uploadData.length) return null;
+ for (const data of request.uploadData) {
+ if (data.bytes) {
+ blobParts.push(new Uint8Array(data.bytes));
+ } else if (session && data.blobUUID) {
+ const buffer = await session.getBlobData(data.blobUUID);
+ if (!buffer) continue;
+ blobParts.push(new Uint8Array(buffer));
+ }
+ }
+ const blob = new Blob(blobParts);
+ return await blob.arrayBuffer();
+}
+
function getPath(filePath: string): string | undefined {
try {
const result = statSync(filePath);
diff --git a/apps/desktop/src/utils/tray.ts b/apps/desktop/src/utils/tray.ts
index 931b16541..022d85228 100644
--- a/apps/desktop/src/utils/tray.ts
+++ b/apps/desktop/src/utils/tray.ts
@@ -21,7 +21,7 @@ import { app, Menu, Tray } from "electron";
import { AssetManager } from "./asset-manager";
import { isFlatpak } from "./index";
import { bringToFront } from "./bring-to-front";
-import { client } from "../rpc/electron";
+import { bridge } from "../api/bridge";
let tray: Tray | undefined = undefined;
export function destroyTray() {
@@ -58,7 +58,7 @@ export function setupTray() {
: AssetManager.icon("note-add", { size: trayIconSize }),
click: () => {
bringToFront();
- client.onCreateItem("note");
+ bridge.onCreateItem("note");
}
},
{
@@ -69,7 +69,7 @@ export function setupTray() {
: AssetManager.icon("notebook-add", { size: trayIconSize }),
click: () => {
bringToFront();
- client.onCreateItem("notebook");
+ bridge.onCreateItem("notebook");
}
},
{ type: "separator" },
diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json
index ea116130f..aeb5c56f7 100644
--- a/apps/web/package-lock.json
+++ b/apps/web/package-lock.json
@@ -22,11 +22,10 @@
"@react-pdf-viewer/core": "^3.12.0",
"@react-pdf-viewer/toolbar": "^3.12.0",
"@tanstack/react-query": "^4.28.0",
- "@tanstack/react-virtual": "^3.0.0-beta.18",
"@theme-ui/components": "^0.14.7",
"@theme-ui/core": "^0.14.7",
- "@trpc/client": "^10.18.0",
- "@trpc/react-query": "^10.18.0",
+ "@trpc/client": "^10.29.1",
+ "@trpc/react-query": "^10.29.1",
"allotment": "^1.12.1",
"async-mutex": "^0.3.2",
"axios": "^1.3.4",
@@ -34,7 +33,7 @@
"comlink": "^4.3.1",
"cronosjs": "^1.7.1",
"dayjs": "^1.10.4",
- "electron-trpc": "^0.4.2",
+ "electron-trpc": "^0.5.0",
"event-source-polyfill": "^1.0.25",
"fflate": "^0.7.4",
"file-saver": "^2.0.5",
@@ -68,6 +67,7 @@
},
"devDependencies": {
"@playwright/test": "^1.26.0",
+ "@trpc/server": "^10.29.1",
"@types/file-saver": "^2.0.5",
"@types/hookrouter": "^2.2.5",
"@types/marked": "^4.0.7",
@@ -81,6 +81,7 @@
"buffer": "^6.0.3",
"chalk": "^4.1.0",
"dotenv": "^10.0.0",
+ "electron-updater": "^5.3.0",
"env-cmd": "^10.1.0",
"file-loader": "^6.2.0",
"find-process": "^1.4.4",
@@ -4699,28 +4700,6 @@
}
}
},
- "node_modules/@tanstack/react-virtual": {
- "version": "3.0.0-beta.48",
- "license": "MIT",
- "dependencies": {
- "@tanstack/virtual-core": "3.0.0-beta.48"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/@tanstack/virtual-core": {
- "version": "3.0.0-beta.48",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
"node_modules/@theme-ui/components": {
"version": "0.14.7",
"license": "MIT",
@@ -4778,23 +4757,40 @@
}
},
"node_modules/@trpc/client": {
- "version": "10.18.0",
- "license": "MIT",
+ "version": "10.29.1",
+ "resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.29.1.tgz",
+ "integrity": "sha512-+9Tifg6dtKsYLsqOW0wizqc3iILAkXxn16pyYAeMDPlulPEqNvnI85GDJ0zJOJLIkQnQefkRbtCmtDxLNtV9Eg==",
+ "funding": [
+ "https://trpc.io/sponsor"
+ ],
"peerDependencies": {
- "@trpc/server": "10.18.0"
+ "@trpc/server": "10.29.1"
}
},
"node_modules/@trpc/react-query": {
- "version": "10.18.0",
- "license": "MIT",
+ "version": "10.29.1",
+ "resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.29.1.tgz",
+ "integrity": "sha512-yWsce8euPSVtn3SeBKXxLmq607/sqyIez7pgMOhMBKehRNdZzrGp3MhjmRwim+IUKLrw71kUgsw7w6uT5FPB0g==",
+ "funding": [
+ "https://trpc.io/sponsor"
+ ],
"peerDependencies": {
"@tanstack/react-query": "^4.18.0",
- "@trpc/client": "10.18.0",
- "@trpc/server": "10.18.0",
+ "@trpc/client": "10.29.1",
+ "@trpc/server": "10.29.1",
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
+ "node_modules/@trpc/server": {
+ "version": "10.29.1",
+ "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.29.1.tgz",
+ "integrity": "sha512-kNXgMh5ya+awuz2tB4eIyVrRs7nVtqGXwSGabzH3l5ZLWz7rbKJquOJ7h6bjvIfWUpaFG62HJNWxxGUtXCRgRw==",
+ "dev": true,
+ "funding": [
+ "https://trpc.io/sponsor"
+ ]
+ },
"node_modules/@trysound/sax": {
"version": "0.2.0",
"license": "ISC",
@@ -6517,6 +6513,19 @@
"version": "1.1.2",
"license": "MIT"
},
+ "node_modules/builder-util-runtime": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.1.1.tgz",
+ "integrity": "sha512-azRhYLEoDvRDR8Dhis4JatELC/jUvYjm4cVSj7n9dauGTOM2eeNn9KS0z6YA6oDsjI1xphjNbY6PZZeHPzzqaw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "sax": "^1.2.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/builtin-modules": {
"version": "3.3.0",
"license": "MIT",
@@ -7985,14 +7994,67 @@
"license": "ISC"
},
"node_modules/electron-trpc": {
- "version": "0.4.2",
- "license": "MIT",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/electron-trpc/-/electron-trpc-0.5.0.tgz",
+ "integrity": "sha512-xvnvOpuI0IiecMGS9VOS5/3kLj6IUpjngKhv5VqXU9vP2LzE140G8gqaziX0Tl7MaiYmJZbad/imis9i49n/4A==",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
"peerDependencies": {
"@trpc/client": ">10.0.0",
"@trpc/server": ">10.0.0",
"electron": ">19.0.0"
}
},
+ "node_modules/electron-updater": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.3.0.tgz",
+ "integrity": "sha512-iKEr7yQBcvnQUPnSDYGSWC9t0eF2YbZWeYYYZzYxdl+HiRejXFENjYMnYjoOm2zxyD6Cr2JTHZhp9pqxiXuCOw==",
+ "dev": true,
+ "dependencies": {
+ "@types/semver": "^7.3.6",
+ "builder-util-runtime": "9.1.1",
+ "fs-extra": "^10.0.0",
+ "js-yaml": "^4.1.0",
+ "lazy-val": "^1.0.5",
+ "lodash.escaperegexp": "^4.1.2",
+ "lodash.isequal": "^4.5.0",
+ "semver": "^7.3.5",
+ "typed-emitter": "^2.1.0"
+ }
+ },
+ "node_modules/electron-updater/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/electron-updater/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/electron-updater/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/emittery": {
"version": "0.8.1",
"license": "MIT",
@@ -11920,6 +11982,12 @@
"language-subtag-registry": "~0.3.2"
}
},
+ "node_modules/lazy-val": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
+ "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
+ "dev": true
+ },
"node_modules/leven": {
"version": "3.1.0",
"license": "MIT",
@@ -12042,6 +12110,12 @@
"version": "4.0.8",
"license": "MIT"
},
+ "node_modules/lodash.escaperegexp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+ "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
+ "dev": true
+ },
"node_modules/lodash.includes": {
"version": "4.3.0",
"license": "MIT"
@@ -15534,6 +15608,23 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/rxjs/node_modules/tslib": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
+ "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==",
+ "dev": true,
+ "optional": true
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"funding": [
@@ -17040,6 +17131,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/typed-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz",
+ "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==",
+ "dev": true,
+ "optionalDependencies": {
+ "rxjs": "*"
+ }
+ },
"node_modules/typedarray-to-buffer": {
"version": "3.1.5",
"license": "MIT",
diff --git a/apps/web/package.json b/apps/web/package.json
index ee7035820..b2cc3e6b7 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -30,11 +30,10 @@
"@react-pdf-viewer/core": "^3.12.0",
"@react-pdf-viewer/toolbar": "^3.12.0",
"@tanstack/react-query": "^4.28.0",
- "@tanstack/react-virtual": "^3.0.0-beta.18",
"@theme-ui/components": "^0.14.7",
"@theme-ui/core": "^0.14.7",
- "@trpc/client": "^10.18.0",
- "@trpc/react-query": "^10.18.0",
+ "@trpc/client": "^10.29.1",
+ "@trpc/react-query": "^10.29.1",
"allotment": "^1.12.1",
"async-mutex": "^0.3.2",
"axios": "^1.3.4",
@@ -42,7 +41,7 @@
"comlink": "^4.3.1",
"cronosjs": "^1.7.1",
"dayjs": "^1.10.4",
- "electron-trpc": "^0.4.2",
+ "electron-trpc": "^0.5.0",
"event-source-polyfill": "^1.0.25",
"fflate": "^0.7.4",
"file-saver": "^2.0.5",
@@ -75,6 +74,7 @@
"zustand": "^3.3.1"
},
"devDependencies": {
+ "@trpc/server": "^10.29.1",
"@playwright/test": "^1.26.0",
"@types/file-saver": "^2.0.5",
"@types/hookrouter": "^2.2.5",
diff --git a/apps/web/src/app-effects.js b/apps/web/src/app-effects.js
index dfd24b523..f312e3176 100644
--- a/apps/web/src/app-effects.js
+++ b/apps/web/src/app-effects.js
@@ -30,7 +30,6 @@ 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";
import { registerKeyMap } from "./common/key-map";
import { isUserPremium } from "./hooks/use-is-user-premium";
import {
@@ -46,6 +45,7 @@ import { updateStatus, removeStatus, getStatus } from "./hooks/use-status";
import { showToast } from "./utils/toast";
import { interruptedOnboarding } from "./components/dialogs/onboarding-dialog";
import { hashNavigate } from "./navigation";
+import { desktop } from "./common/desktop-bridge";
export default function AppEffects({ setShow }) {
const refreshNavItems = useStore((store) => store.refreshNavItems);
@@ -198,19 +198,26 @@ export default function AppEffects({ setShow }) {
}, [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;
- }
- });
+ const { unsubscribe } =
+ desktop?.bridge.onCreateItem.subscribe(undefined, {
+ onData(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 () => {
+ unsubscribe();
+ };
}, []);
return ;
diff --git a/apps/web/src/commands/index.js b/apps/web/src/commands/index.js
deleted file mode 100644
index 2dadfcff7..000000000
--- a/apps/web/src/commands/index.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-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 { AppEventManager } from "../common/app-events";
-import { isDesktop } from "../utils/platform";
-
-export function invokeCommand(type, payload = {}) {
- if (!isDesktop()) return;
-
- // window.api.send("fromRenderer", {
- // type,
- // ...payload
- // });
-}
-
-// if (isDesktop()) {
-// window.api.receive("fromMain", (args) => {
-// console.log(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
deleted file mode 100644
index 56ce63dfe..000000000
--- a/apps/web/src/commands/show-notification.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-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";
-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/common/app-events.js b/apps/web/src/common/app-events.js
index 7f8b208c6..638f6ac42 100644
--- a/apps/web/src/common/app-events.js
+++ b/apps/web/src/common/app-events.js
@@ -23,5 +23,15 @@ export const AppEventManager = new EventManager();
export const AppEvents = {
UPDATE_ATTACHMENT_PROGRESS: "updateAttachmentProgress",
UPDATE_STATUS: "updateStatus",
- REMOVE_STATUS: "removeStatus"
+ REMOVE_STATUS: "removeStatus",
+
+ checkingForUpdate: "checkingForUpdate",
+ updateAvailable: "updateAvailable",
+ updateDownloadProgress: "updateDownloadProgress",
+ updateDownloadCompleted: "updateDownloadCompleted",
+ updateNotAvailable: "updateNotAvailable",
+ updateError: "updateError",
+ themeChanged: "themeChanged",
+ notificationClicked: "notificationClicked",
+ createItem: "createItem"
};
diff --git a/apps/web/src/common/desktop-client.ts b/apps/web/src/common/desktop-bridge/bridge.ts
similarity index 51%
rename from apps/web/src/common/desktop-client.ts
rename to apps/web/src/common/desktop-bridge/bridge.ts
index 316a84f2e..3a3c5367d 100644
--- a/apps/web/src/common/desktop-client.ts
+++ b/apps/web/src/common/desktop-bridge/bridge.ts
@@ -19,31 +19,47 @@ along with this program. If not, see .
import { createTRPCProxyClient } from "@trpc/client";
import { ipcLink } from "electron-trpc/renderer";
-import { AppRouter, createRPCServer, IClient } from "@notesnook/desktop";
-import "@notesnook/desktop/dist/rpc/browser";
+import type { AppRouter } from "@notesnook/desktop";
+import { AppEventManager, AppEvents } from "../app-events";
export const desktop = createTRPCProxyClient({
links: [ipcLink()]
});
-const client: IClient = {
- onCheckingForUpdate: function (): void {
- throw new Error("Function not implemented.");
- },
- onUpdateAvailable() {},
- onUpdateDownloadProgress() {},
- onUpdateDownloadCompleted() {},
- onUpdateNotAvailable() {},
- onThemeChanged: function (theme: "system" | "light" | "dark"): void {
- throw new Error("Function not implemented.");
- },
- onNotificationClicked: function (tag: string): void {
- throw new Error("Function not implemented.");
- },
- onCreateItem: function (type: "note" | "notebook" | "reminder") {
- console.log("GOT", type);
- return type;
- }
-};
+desktop.updater.onChecking.subscribe(
+ undefined,
+ attachListener(AppEvents.checkingForUpdate)
+);
-createRPCServer(window.RPCTransport, client);
+desktop.updater.onAvailable.subscribe(
+ undefined,
+ attachListener(AppEvents.updateAvailable)
+);
+
+desktop.updater.onDownloaded.subscribe(
+ undefined,
+ attachListener(AppEvents.updateDownloadCompleted)
+);
+
+desktop.updater.onDownloadProgress.subscribe(
+ undefined,
+ attachListener(AppEvents.updateDownloadProgress)
+);
+
+desktop.updater.onNotAvailable.subscribe(
+ undefined,
+ attachListener(AppEvents.updateNotAvailable)
+);
+
+desktop.updater.onError.subscribe(
+ undefined,
+ attachListener(AppEvents.updateError)
+);
+
+function attachListener(event: string) {
+ return {
+ onData(...args: any[]) {
+ AppEventManager.publish(event, ...args);
+ }
+ };
+}
diff --git a/apps/desktop/src/rpc/browser.ts b/apps/web/src/common/desktop-bridge/index.ts
similarity index 78%
rename from apps/desktop/src/rpc/browser.ts
rename to apps/web/src/common/desktop-bridge/index.ts
index b8c895197..00101cd97 100644
--- a/apps/desktop/src/rpc/browser.ts
+++ b/apps/web/src/common/desktop-bridge/index.ts
@@ -16,10 +16,10 @@ 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 .
*/
-/* eslint-disable no-var */
-import { ITransport } from "./types";
+import { type desktop as bridge } from "./bridge";
-declare global {
- var RPCTransport: ITransport;
-}
+export const desktop: typeof bridge | undefined =
+ process.env.REACT_APP_PLATFORM === "desktop"
+ ? require("./bridge").desktop
+ : undefined;
diff --git a/apps/web/src/common/index.js b/apps/web/src/common/index.js
index 35a382477..46be4fb33 100644
--- a/apps/web/src/common/index.js
+++ b/apps/web/src/common/index.js
@@ -38,6 +38,7 @@ import { PATHS } from "@notesnook/desktop";
import { TaskManager } from "./task-manager";
import { EVENTS } from "@notesnook/core/common";
import { getFormattedDate } from "../utils/time";
+import { desktop } from "./desktop-bridge";
export const CREATE_BUTTON_MAP = {
notes: {
@@ -103,7 +104,7 @@ export async function createBackup() {
PATHS.backupsDirectory
);
const filePath = `${directory}/${filename}.${ext}`;
- saveFile(filePath, data);
+ await desktop?.integration.saveFile.query(filePath, data);
showToast("success", `Backup saved at ${filePath}.`);
} else {
FileSaver.saveAs(new Blob([Buffer.from(data)]), `${filename}.${ext}`);
diff --git a/apps/web/src/hooks/use-auto-updater.js b/apps/web/src/hooks/use-auto-updater.ts
similarity index 71%
rename from apps/web/src/hooks/use-auto-updater.js
rename to apps/web/src/hooks/use-auto-updater.ts
index 4a9deeae2..f9a2f6175 100644
--- a/apps/web/src/hooks/use-auto-updater.js
+++ b/apps/web/src/hooks/use-auto-updater.ts
@@ -18,21 +18,30 @@ along with this program. If not, see .
*/
import { useEffect, useState } from "react";
-import { AppEventManager } from "../common/app-events";
-import { EVENTS } from "@notesnook/desktop";
import {
showUpdateAvailableNotice,
showUpdateReadyNotice
} from "../common/dialog-controller";
import { isDesktop } from "../utils/platform";
import { checkForUpdate } from "../utils/updater";
+import { AppEventManager, AppEvents } from "../common/app-events";
-var checkingForUpdateTimeout = 0;
+type CompletedUpdateStatus = { type: "completed"; version: string };
+type DownloadingUpdateStatus = { type: "downloading"; progress: number };
+type AvailableUpdateStatus = { type: "available"; version: string };
+type GenericUpdateStatus = { type: "checking" | "updated" };
+type UpdateStatus =
+ | AvailableUpdateStatus
+ | CompletedUpdateStatus
+ | DownloadingUpdateStatus
+ | GenericUpdateStatus;
+
+let checkingForUpdateTimeout = 0;
export function useAutoUpdater() {
- const [status, setStatus] = useState();
+ const [status, setStatus] = useState();
useEffect(() => {
- function changeStatus(status) {
+ function changeStatus(status?: UpdateStatus) {
clearTimeout(checkingForUpdateTimeout);
setStatus(status);
}
@@ -41,10 +50,10 @@ export function useAutoUpdater() {
changeStatus({ type: "checking" });
checkingForUpdateTimeout = setTimeout(() => {
changeStatus({ type: "updated" });
- }, 10000);
+ }, 10000) as unknown as number;
}
- function updateAvailable(info) {
+ function updateAvailable(info: { version: string }) {
changeStatus({
type: "available",
version: info.version
@@ -59,33 +68,33 @@ export function useAutoUpdater() {
else changeStatus();
}
- function updateDownloadCompleted(info) {
+ function updateDownloadCompleted(info: { version: string }) {
changeStatus({ type: "completed", version: info.version });
showUpdateReadyNotice({ version: info.version });
}
- function updateDownloadProgress(progressInfo) {
+ function updateDownloadProgress(progressInfo: { percent: number }) {
changeStatus({ type: "downloading", progress: progressInfo.percent });
}
const checkingForUpdateEvent = AppEventManager.subscribe(
- EVENTS.checkingForUpdate,
+ AppEvents.checkingForUpdate,
checkingForUpdate
);
const updateAvailableEvent = AppEventManager.subscribe(
- EVENTS.updateAvailable,
+ AppEvents.updateAvailable,
updateAvailable
);
const updateNotAvailableEvent = AppEventManager.subscribe(
- EVENTS.updateNotAvailable,
+ AppEvents.updateNotAvailable,
updateNotAvailable
);
const updateCompletedEvent = AppEventManager.subscribe(
- EVENTS.updateDownloadCompleted,
+ AppEvents.updateDownloadCompleted,
updateDownloadCompleted
);
const updateProgressEvent = AppEventManager.subscribe(
- EVENTS.updateDownloadProgress,
+ AppEvents.updateDownloadProgress,
updateDownloadProgress
);
diff --git a/apps/web/src/hooks/use-desktop-integration.ts b/apps/web/src/hooks/use-desktop-integration.ts
index b7dd5a150..a0c04aeac 100644
--- a/apps/web/src/hooks/use-desktop-integration.ts
+++ b/apps/web/src/hooks/use-desktop-integration.ts
@@ -18,7 +18,7 @@ along with this program. If not, see .
*/
import { useCallback, useEffect, useState } from "react";
-import { desktop } from "../common/desktop-client";
+import { desktop } from "../common/desktop-bridge";
export type DesktopIntegrationSettings = {
autoStart: boolean;
@@ -31,7 +31,7 @@ export default function useDesktopIntegration() {
const [settings, changeSettings] = useState();
const setupDesktopIntegration = useCallback(async () => {
- const settings = await desktop.integration.desktopIntegration.query();
+ const settings = await desktop?.integration.desktopIntegration.query();
changeSettings(settings);
return settings;
}, []);
@@ -46,7 +46,7 @@ export default function useDesktopIntegration() {
async (_settings: Partial) => {
if (!settings) return;
- await desktop.integration.setDesktopIntegration.mutate({
+ await desktop?.integration.setDesktopIntegration.mutate({
...settings,
..._settings
});
diff --git a/apps/web/src/hooks/use-privacy-mode.ts b/apps/web/src/hooks/use-privacy-mode.ts
index 26712e73b..0920aff01 100644
--- a/apps/web/src/hooks/use-privacy-mode.ts
+++ b/apps/web/src/hooks/use-privacy-mode.ts
@@ -18,19 +18,19 @@ along with this program. If not, see .
*/
import { useCallback, useEffect, useState } from "react";
-import { desktop } from "../common/desktop-client";
+import { desktop } from "../common/desktop-bridge";
export default function usePrivacyMode() {
const [privacyMode, setPrivacyMode] = useState(false);
useEffect(() => {
(async function () {
- setPrivacyMode(await desktop.integration.privacyMode.query());
+ setPrivacyMode((await desktop?.integration.privacyMode.query()) || false);
})();
}, []);
const set = useCallback(async (privacyMode) => {
- await desktop.integration.setPrivacyMode.mutate(privacyMode);
+ await desktop?.integration.setPrivacyMode.mutate(privacyMode);
setPrivacyMode(privacyMode);
}, []);
diff --git a/apps/web/src/hooks/use-spell-checker.ts b/apps/web/src/hooks/use-spell-checker.ts
index e32ace9ee..a64aec9b8 100644
--- a/apps/web/src/hooks/use-spell-checker.ts
+++ b/apps/web/src/hooks/use-spell-checker.ts
@@ -18,7 +18,7 @@ along with this program. If not, see .
*/
import { useCallback, useEffect, useState } from "react";
-import { desktop } from "../common/desktop-client";
+import { desktop } from "../common/desktop-bridge";
export type Language = { code: string; name: string };
export type SpellCheckerOptions = {
@@ -32,9 +32,10 @@ export default function useSpellChecker() {
const loadSpellChecker = useCallback(async () => {
setSpellChecker({
- enabledLanguages: await desktop.spellChecker.enabledLanguages.query(),
- languages: await desktop.spellChecker.languages.query(),
- enabled: await desktop.spellChecker.isEnabled.query()
+ enabledLanguages:
+ (await desktop?.spellChecker.enabledLanguages.query()) || [],
+ languages: (await desktop?.spellChecker.languages.query()) || [],
+ enabled: (await desktop?.spellChecker.isEnabled.query()) || false
});
}, []);
@@ -46,7 +47,7 @@ export default function useSpellChecker() {
const toggle = useCallback(
async (enabled: boolean) => {
- await desktop.spellChecker.toggle.mutate(enabled);
+ await desktop?.spellChecker.toggle.mutate(enabled);
await loadSpellChecker();
},
[loadSpellChecker]
@@ -54,7 +55,7 @@ export default function useSpellChecker() {
const setLanguages = useCallback(
async (languages: string[]) => {
- await desktop.spellChecker.setLanguages.mutate(languages);
+ await desktop?.spellChecker.setLanguages.mutate(languages);
await loadSpellChecker();
},
[loadSpellChecker]
diff --git a/apps/web/src/hooks/use-system-theme.js b/apps/web/src/hooks/use-system-theme.ts
similarity index 79%
rename from apps/web/src/hooks/use-system-theme.js
rename to apps/web/src/hooks/use-system-theme.ts
index 4760f9b76..86946a361 100644
--- a/apps/web/src/hooks/use-system-theme.js
+++ b/apps/web/src/hooks/use-system-theme.ts
@@ -17,10 +17,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { EVENTS } from "@notesnook/desktop";
import { useEffect, useState } from "react";
-import { AppEventManager } from "../common/app-events";
import useMediaQuery from "./use-media-query";
+import { desktop } from "../common/desktop-bridge";
function useSystemTheme() {
const isDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
@@ -31,12 +30,12 @@ function useSystemTheme() {
}, [isDarkMode]);
useEffect(() => {
- function onThemeChanged({ theme }) {
- setSystemTheme(theme);
- }
- AppEventManager.subscribe(EVENTS.themeChanged, onThemeChanged);
+ const { unsubscribe } =
+ desktop?.integration.onThemeChanged.subscribe(undefined, {
+ onData: setSystemTheme
+ }) || {};
return () => {
- AppEventManager.unsubscribe(EVENTS.themeChanged, onThemeChanged);
+ unsubscribe?.();
};
}, []);
diff --git a/apps/web/src/hooks/use-zoom-factor.ts b/apps/web/src/hooks/use-zoom-factor.ts
index 995ad847d..10d4df5a9 100644
--- a/apps/web/src/hooks/use-zoom-factor.ts
+++ b/apps/web/src/hooks/use-zoom-factor.ts
@@ -18,19 +18,19 @@ along with this program. If not, see .
*/
import { useCallback, useEffect, useState } from "react";
-import { desktop } from "../common/desktop-client";
+import { desktop } from "../common/desktop-bridge";
export default function useZoomFactor() {
const [zoom, setZoom] = useState(1.0);
useEffect(() => {
(async function () {
- setZoom(await desktop.integration.zoomFactor.query());
+ setZoom((await desktop?.integration.zoomFactor.query()) || 1.0);
})();
}, []);
const set = useCallback(async (zoomFactor) => {
- await desktop.integration.setZoomFactor.mutate(zoomFactor);
+ await desktop?.integration.setZoomFactor.mutate(zoomFactor);
setZoom(zoomFactor);
}, []);
diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx
index 31334b84e..5dc2ba080 100644
--- a/apps/web/src/index.tsx
+++ b/apps/web/src/index.tsx
@@ -18,8 +18,7 @@ along with this program. If not, see .
*/
import "@notesnook/core/types";
-import { EVENTS } from "@notesnook/desktop";
-import { AppEventManager } from "./common/app-events";
+import { AppEventManager, AppEvents } from "./common/app-events";
import { render } from "react-dom";
import { getCurrentHash, getCurrentPath, makeURL } from "./navigation";
import Config from "./utils/config";
@@ -30,7 +29,6 @@ import { AuthProps } from "./views/auth";
global.Buffer = Buffer;
initalizeLogger();
-if (process.env.REACT_APP_PLATFORM === "desktop") require("./commands");
type Route = {
component: () => Promise<{
@@ -188,7 +186,7 @@ async function initializeServiceWorker() {
const { formatted } = await getServiceWorkerVersion(
registration.waiting
);
- AppEventManager.publish(EVENTS.updateDownloadCompleted, {
+ AppEventManager.publish(AppEvents.updateDownloadCompleted, {
version: formatted
});
}
diff --git a/apps/web/src/stores/reminder-store.js b/apps/web/src/stores/reminder-store.js
index 06d7bdff2..06d0593a1 100644
--- a/apps/web/src/stores/reminder-store.js
+++ b/apps/web/src/stores/reminder-store.js
@@ -27,7 +27,7 @@ import dayjs from "dayjs";
import Config from "../utils/config";
import { store as notestore } from "./note-store";
import { isDesktop, isTesting } from "../utils/platform";
-import { desktop } from "../common/desktop-client";
+import { desktop } from "../common/desktop-bridge";
class ReminderStore extends BaseStore {
reminders = [];
@@ -99,26 +99,25 @@ function scheduleReminder(id, reminder, cron) {
}
if (isDesktop()) {
- await desktop.integration.showNotification.query(
- {
- 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
- },
- async () => {
- await desktop.integration.bringToFront.query();
- showReminderPreviewDialog(reminder);
- }
- );
+ const tag = await desktop?.integration.showNotification.query({
+ 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
+ });
+
+ if (tag) {
+ await desktop?.integration.bringToFront.query();
+ showReminderPreviewDialog(reminder);
+ }
} else {
const notification = new Notification(reminder.title, {
body: reminder.description,
diff --git a/apps/web/src/utils/compressor.ts b/apps/web/src/utils/compressor.ts
index 488c10a2e..43adfd0da 100644
--- a/apps/web/src/utils/compressor.ts
+++ b/apps/web/src/utils/compressor.ts
@@ -23,7 +23,7 @@ import Worker from "worker-loader?filename=static/workers/compressor.worker.[con
import type { Compressor as CompressorWorker } from "./compressor.worker";
import { wrap, Remote } from "comlink";
import { isDesktop } from "./platform";
-import { desktop } from "../common/desktop-client";
+import { desktop } from "../common/desktop-bridge";
export class Compressor {
private worker!: globalThis.Worker;
@@ -38,13 +38,13 @@ export class Compressor {
async compress(data: string) {
if (isDesktop())
- return await desktop.compress.gzip.query({ data, level: 6 });
+ return await desktop?.compress.gzip.query({ data, level: 6 });
return await this.compressor.gzip({ data, level: 6 });
}
async decompress(data: string) {
- if (isDesktop()) return await desktop.compress.gunzip.query(data);
+ if (isDesktop()) return await desktop?.compress.gunzip.query(data);
return await this.compressor.gunzip({ data });
}
diff --git a/apps/web/src/utils/updater.ts b/apps/web/src/utils/updater.ts
index aa40096ea..198191f56 100644
--- a/apps/web/src/utils/updater.ts
+++ b/apps/web/src/utils/updater.ts
@@ -17,16 +17,15 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { EVENTS } from "@notesnook/desktop";
-import { AppEventManager } from "../common/app-events";
-import { desktop } from "../common/desktop-client";
+import { AppEventManager, AppEvents } from "../common/app-events";
+import { desktop } from "../common/desktop-bridge";
import { isDesktop } from "./platform";
import { appVersion, getServiceWorkerVersion } from "./version";
export async function checkForUpdate() {
- if (isDesktop()) await desktop.updater.check.query();
+ if (isDesktop()) await desktop?.updater.check.query();
else {
- AppEventManager.publish(EVENTS.checkingForUpdate);
+ AppEventManager.publish(AppEvents.checkingForUpdate);
const registrations =
(await navigator.serviceWorker?.getRegistrations()) || [];
@@ -41,19 +40,19 @@ export async function checkForUpdate() {
continue;
}
- AppEventManager.publish(EVENTS.updateDownloadCompleted, {
+ AppEventManager.publish(AppEvents.updateDownloadCompleted, {
version: workerVersion.formatted
});
return;
}
}
- AppEventManager.publish(EVENTS.updateNotAvailable);
+ AppEventManager.publish(AppEvents.updateNotAvailable);
}
}
export async function downloadUpdate() {
- if (isDesktop()) await desktop.updater.download.query();
+ if (isDesktop()) await desktop?.updater.download.query();
else {
console.log("Force updating");
if (!("serviceWorker" in navigator)) return;
@@ -63,7 +62,7 @@ export async function downloadUpdate() {
}
export async function installUpdate() {
- if (isDesktop()) await desktop.updater.install.query();
+ if (isDesktop()) await desktop?.updater.install.query();
else {
const registrations =
(await navigator.serviceWorker?.getRegistrations()) || [];
diff --git a/apps/web/src/views/settings.js b/apps/web/src/views/settings.js
index 2c7f4c12f..71097e797 100644
--- a/apps/web/src/views/settings.js
+++ b/apps/web/src/views/settings.js
@@ -87,7 +87,7 @@ import { writeText } from "clipboard-polyfill";
import { useEditorConfig } from "../components/editor/context";
import { getFonts } from "@notesnook/editor";
import { formatDate } from "@notesnook/core/utils/date";
-import { desktop } from "../common/desktop-client";
+import { desktop } from "../common/desktop-bridge";
function subscriptionStatusToString(user) {
const status = user?.subscription?.type;
@@ -938,7 +938,7 @@ function Settings() {
variant="list"
onClick={async () => {
const location =
- await desktop.integration.selectDirectory.query({
+ await desktop?.integration.selectDirectory.query({
title: "Select where Notesnook should save backups",
defaultPath:
backupStorageLocation || PATHS.backupsDirectory
@@ -1156,7 +1156,7 @@ function Settings() {