mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 10:09:26 +02:00
Compare commits
70 Commits
fix/select
...
2.6.10-and
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b9e2c8526 | ||
|
|
0dc2d14736 | ||
|
|
fc5dada701 | ||
|
|
f34060c4e0 | ||
|
|
006a239680 | ||
|
|
c6ed4bcab1 | ||
|
|
b0fe967087 | ||
|
|
3f32673b0a | ||
|
|
0525cecaee | ||
|
|
7d84a9582b | ||
|
|
289acd8ab6 | ||
|
|
bb6d9f604b | ||
|
|
c20a8232cd | ||
|
|
c886212e89 | ||
|
|
799ae111ae | ||
|
|
2a990979a0 | ||
|
|
2d1071117b | ||
|
|
aefce1ee67 | ||
|
|
64d10f5050 | ||
|
|
95b9edca62 | ||
|
|
06c8422cbe | ||
|
|
177d8b5112 | ||
|
|
39afa98fce | ||
|
|
5bc1ad4ba5 | ||
|
|
0b1223976c | ||
|
|
3873758fe5 | ||
|
|
3c876cb1d8 | ||
|
|
0a1a16c197 | ||
|
|
3c56325fa6 | ||
|
|
e5db3353ae | ||
|
|
3416ba4eaa | ||
|
|
cdb69bf553 | ||
|
|
1aff6d0d55 | ||
|
|
67b7bdcea3 | ||
|
|
9b6a6de415 | ||
|
|
8325246a56 | ||
|
|
3d11fb5e3f | ||
|
|
4627a58a52 | ||
|
|
e557047af1 | ||
|
|
8b4cea7339 | ||
|
|
ba5e35ec5c | ||
|
|
f53817e94d | ||
|
|
846efc5351 | ||
|
|
0149d00d64 | ||
|
|
275f9730f1 | ||
|
|
43bc954989 | ||
|
|
c88bc722e1 | ||
|
|
ec6fc6839e | ||
|
|
8383c93292 | ||
|
|
37d8d85935 | ||
|
|
95db7c6329 | ||
|
|
636783335f | ||
|
|
f88431ded4 | ||
|
|
91b0de0580 | ||
|
|
a5704ad75c | ||
|
|
ba8c09a7d9 | ||
|
|
dbe4015655 | ||
|
|
76237ad7dd | ||
|
|
dbfb1168fb | ||
|
|
830d28fbe5 | ||
|
|
c6def2a8fe | ||
|
|
ebc95fcea7 | ||
|
|
f78cfe59e8 | ||
|
|
a2cbc93d34 | ||
|
|
4728f14e8c | ||
|
|
e94b431d58 | ||
|
|
0ac5e2b995 | ||
|
|
365764e3e3 | ||
|
|
8d2b5aa3de | ||
|
|
9a031a4a43 |
94
apps/desktop/package-lock.json
generated
94
apps/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.9",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -24,7 +24,7 @@
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "25.8.1",
|
||||
"electron": "25.9.3",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
@@ -216,6 +216,7 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz",
|
||||
"integrity": "sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"env-paths": "^2.2.0",
|
||||
@@ -236,6 +237,7 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^4.0.0",
|
||||
@@ -249,6 +251,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"dev": true,
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
@@ -257,6 +260,7 @@
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -265,6 +269,7 @@
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
@@ -798,6 +803,7 @@
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -809,6 +815,7 @@
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||
"integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"defer-to-connect": "^2.0.0"
|
||||
},
|
||||
@@ -853,6 +860,7 @@
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
|
||||
"integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/http-cache-semantics": "*",
|
||||
"@types/keyv": "^3.1.4",
|
||||
@@ -881,12 +889,14 @@
|
||||
"node_modules/@types/http-cache-semantics": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
|
||||
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
|
||||
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/keyv": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
|
||||
"integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -900,7 +910,8 @@
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.1.tgz",
|
||||
"integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA=="
|
||||
"integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.1",
|
||||
@@ -922,6 +933,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
|
||||
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -951,6 +963,7 @@
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
|
||||
"integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
@@ -1191,6 +1204,7 @@
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
|
||||
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
@@ -1242,6 +1256,7 @@
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@@ -1316,6 +1331,7 @@
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
|
||||
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.6.0"
|
||||
}
|
||||
@@ -1324,6 +1340,7 @@
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
|
||||
"integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"clone-response": "^1.0.2",
|
||||
"get-stream": "^5.1.0",
|
||||
@@ -1443,6 +1460,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
|
||||
"integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-response": "^1.0.0"
|
||||
},
|
||||
@@ -1586,6 +1604,7 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
@@ -1600,6 +1619,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -1611,6 +1631,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
|
||||
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@@ -1619,6 +1640,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
|
||||
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
@@ -1644,6 +1666,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
|
||||
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/dir-compare": {
|
||||
@@ -1751,9 +1774,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "25.8.1",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.8.1.tgz",
|
||||
"integrity": "sha512-GtcP1nMrROZfFg0+mhyj1hamrHvukfF6of2B/pcWxmWkd5FVY1NJib0tlhiorFZRzQN5Z+APLPr7aMolt7i2AQ==",
|
||||
"version": "25.9.3",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.3.tgz",
|
||||
"integrity": "sha512-dacaHg/PuwVcFRgPDCM5j7UDzqGJWOsbBRdS5wPKLNS/ejPeccIjuNUT1cqcrpvCJKAFW8swHWg9kdizNSEDHQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
@@ -1920,6 +1944,7 @@
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
@@ -1928,6 +1953,7 @@
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -1951,6 +1977,7 @@
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
|
||||
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
@@ -2002,6 +2029,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -2027,6 +2055,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"get-stream": "^5.1.0",
|
||||
@@ -2067,6 +2096,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
|
||||
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pend": "~1.2.0"
|
||||
}
|
||||
@@ -2183,7 +2213,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
@@ -2197,6 +2227,7 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
@@ -2212,6 +2243,7 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0"
|
||||
},
|
||||
@@ -2280,6 +2312,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
|
||||
"integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"boolean": "^3.0.1",
|
||||
@@ -2297,6 +2330,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
|
||||
"integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"define-properties": "^1.1.3"
|
||||
@@ -2312,6 +2346,7 @@
|
||||
"version": "11.8.6",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
|
||||
"integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": "^4.0.0",
|
||||
"@szmarczak/http-timer": "^4.0.5",
|
||||
@@ -2341,7 +2376,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
@@ -2362,6 +2397,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.1"
|
||||
@@ -2374,6 +2410,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2386,6 +2423,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2409,7 +2447,8 @@
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
@@ -2429,6 +2468,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
|
||||
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"quick-lru": "^5.1.1",
|
||||
"resolve-alpn": "^1.0.0"
|
||||
@@ -2691,7 +2731,8 @@
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
@@ -2709,6 +2750,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/json5": {
|
||||
@@ -2738,6 +2780,7 @@
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
|
||||
"integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
@@ -2773,6 +2816,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -2792,6 +2836,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
||||
"integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^4.0.0"
|
||||
@@ -2837,6 +2882,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -2959,6 +3005,7 @@
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -2970,6 +3017,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2979,6 +3027,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -2987,6 +3036,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -3066,7 +3116,8 @@
|
||||
"node_modules/pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
|
||||
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@@ -3105,6 +3156,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -3126,6 +3178,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
@@ -3144,6 +3197,7 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -3338,12 +3392,14 @@
|
||||
"node_modules/resolve-alpn": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
|
||||
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/responselike": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
|
||||
"integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lowercase-keys": "^2.0.0"
|
||||
},
|
||||
@@ -3379,6 +3435,7 @@
|
||||
"version": "2.15.4",
|
||||
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
|
||||
"integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"boolean": "^3.0.1",
|
||||
@@ -3458,12 +3515,14 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
|
||||
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/serialize-error": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
|
||||
"integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.13.1"
|
||||
@@ -3479,6 +3538,7 @@
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
|
||||
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -3599,6 +3659,7 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/stat-mode": {
|
||||
@@ -3671,6 +3732,7 @@
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
|
||||
"integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.1.0"
|
||||
},
|
||||
@@ -3940,7 +4002,8 @@
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "15.1.1",
|
||||
@@ -3993,6 +4056,7 @@
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-crc32": "~0.2.3",
|
||||
"fd-slicer": "~1.1.0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.9",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
@@ -25,7 +25,7 @@
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "25.8.1",
|
||||
"electron": "25.9.3",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
|
||||
@@ -29,6 +29,8 @@ import { dirname } from "path";
|
||||
import { resolvePath } from "../utils/resolve-path";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
import { AssetManager } from "../utils/asset-manager";
|
||||
import { isFlatpak } from "../utils";
|
||||
import { setupDesktopIntegration } from "../utils/desktop-integration";
|
||||
|
||||
const t = initTRPC.create();
|
||||
|
||||
@@ -44,6 +46,8 @@ const NotificationOptions = z.object({
|
||||
});
|
||||
|
||||
export const osIntegrationRouter = t.router({
|
||||
isFlatpak: t.procedure.query(() => isFlatpak()),
|
||||
|
||||
zoomFactor: t.procedure.query(() => config.zoomFactor),
|
||||
setZoomFactor: t.procedure.input(z.number()).mutation(({ input: factor }) => {
|
||||
globalThis.window?.webContents.setZoomFactor(factor);
|
||||
@@ -79,6 +83,7 @@ export const osIntegrationRouter = t.router({
|
||||
AutoLaunch.disable();
|
||||
}
|
||||
config.desktopSettings = settings;
|
||||
setupDesktopIntegration(settings);
|
||||
}),
|
||||
|
||||
selectDirectory: t.procedure
|
||||
|
||||
@@ -24,7 +24,6 @@ import { configureAutoUpdater } from "./utils/autoupdater";
|
||||
import { getBackgroundColor, getTheme, setTheme } from "./utils/theme";
|
||||
import { setupMenu } from "./utils/menu";
|
||||
import { WindowState } from "./utils/window-state";
|
||||
import { AutoLaunch } from "./utils/autolaunch";
|
||||
import { setupJumplist } from "./utils/jumplist";
|
||||
import { setupTray } from "./utils/tray";
|
||||
import { CLIOptions, parseArguments } from "./cli";
|
||||
@@ -35,6 +34,7 @@ import { config } from "./utils/config";
|
||||
import path from "path";
|
||||
import { bringToFront } from "./utils/bring-to-front";
|
||||
import { bridge } from "./api/bridge";
|
||||
import { setupDesktopIntegration } from "./utils/desktop-integration";
|
||||
|
||||
// only run a single instance
|
||||
if (!MAC_APP_STORE && !app.requestSingleInstanceLock()) {
|
||||
@@ -77,7 +77,7 @@ async function createWindow() {
|
||||
darkTheme: getTheme() === "dark",
|
||||
backgroundColor: getBackgroundColor(),
|
||||
opacity: 0,
|
||||
autoHideMenuBar: true,
|
||||
autoHideMenuBar: false,
|
||||
icon: AssetManager.appIcon({
|
||||
size: 512,
|
||||
format: process.platform === "win32" ? "ico" : "png"
|
||||
@@ -93,6 +93,7 @@ async function createWindow() {
|
||||
|
||||
createIPCHandler({ router, windows: [mainWindow] });
|
||||
globalThis.window = mainWindow;
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
mainWindowState.manage(mainWindow);
|
||||
|
||||
if (cliOptions.hidden && !config.desktopSettings.minimizeToSystemTray)
|
||||
@@ -106,7 +107,7 @@ async function createWindow() {
|
||||
}
|
||||
|
||||
await AssetManager.loadIcons();
|
||||
setupDesktopIntegration();
|
||||
setupDesktopIntegration(config.desktopSettings);
|
||||
|
||||
mainWindow.webContents.session.setSpellCheckerDictionaryDownloadURL(
|
||||
"http://dictionaries.notesnook.com/"
|
||||
@@ -176,49 +177,3 @@ function createURL(options: CLIOptions, path = "/") {
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
function setupDesktopIntegration() {
|
||||
const desktopIntegration = config.desktopSettings;
|
||||
|
||||
if (
|
||||
desktopIntegration.closeToSystemTray ||
|
||||
desktopIntegration.minimizeToSystemTray
|
||||
) {
|
||||
setupTray();
|
||||
}
|
||||
|
||||
// when close to system tray is enabled, it becomes nigh impossible
|
||||
// to "quit" the app. This is necessary in order to fix that.
|
||||
if (desktopIntegration.closeToSystemTray) {
|
||||
app.on("before-quit", () => app.exit(0));
|
||||
}
|
||||
|
||||
globalThis.window?.on("close", (e) => {
|
||||
if (config.desktopSettings.closeToSystemTray) {
|
||||
e.preventDefault();
|
||||
if (process.platform == "darwin") {
|
||||
// on macOS window cannot be minimized/hidden if it is already fullscreen
|
||||
// so we just close it.
|
||||
if (globalThis.window?.isFullScreen()) app.exit(0);
|
||||
else app.hide();
|
||||
} else {
|
||||
globalThis.window?.minimize();
|
||||
globalThis.window?.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
globalThis.window?.on("minimize", () => {
|
||||
if (config.desktopSettings.minimizeToSystemTray) {
|
||||
if (process.platform == "darwin") {
|
||||
app.hide();
|
||||
} else {
|
||||
globalThis.window?.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (desktopIntegration.autoStart) {
|
||||
AutoLaunch.enable(!!desktopIntegration.startMinimized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ const LINUX_AUTOSTART_DIRECTORY_PATH = path.join(
|
||||
"autostart"
|
||||
);
|
||||
|
||||
const STARTUP_ARGS = ["--hidden"];
|
||||
|
||||
export class AutoLaunch {
|
||||
static enable(hidden: boolean) {
|
||||
if (process.platform === "linux") {
|
||||
@@ -47,10 +49,14 @@ export class AutoLaunch {
|
||||
LINUX_DESKTOP_ENTRY(hidden)
|
||||
);
|
||||
} else {
|
||||
const loginItemSettings = app.getLoginItemSettings({
|
||||
args: STARTUP_ARGS
|
||||
});
|
||||
if (loginItemSettings.openAtLogin) return;
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: true,
|
||||
openAsHidden: hidden,
|
||||
args: ["--hidden"]
|
||||
args: STARTUP_ARGS
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
74
apps/desktop/src/utils/desktop-integration.ts
Normal file
74
apps/desktop/src/utils/desktop-integration.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
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 { DesktopIntegration, config } from "./config";
|
||||
import { setupTray, destroyTray } from "./tray";
|
||||
import { AutoLaunch } from "./autolaunch";
|
||||
|
||||
export function setupDesktopIntegration(
|
||||
desktopIntegration: DesktopIntegration
|
||||
) {
|
||||
if (
|
||||
desktopIntegration.closeToSystemTray ||
|
||||
desktopIntegration.minimizeToSystemTray
|
||||
) {
|
||||
setupTray();
|
||||
} else {
|
||||
destroyTray();
|
||||
}
|
||||
|
||||
// when close to system tray is enabled, it becomes nigh impossible
|
||||
// to "quit" the app. This is necessary in order to fix that.
|
||||
app.on("before-quit", () =>
|
||||
desktopIntegration.closeToSystemTray ? app.exit(0) : null
|
||||
);
|
||||
|
||||
globalThis.window?.on("close", (e) => {
|
||||
if (config.desktopSettings.closeToSystemTray) {
|
||||
e.preventDefault();
|
||||
if (process.platform == "darwin") {
|
||||
// on macOS window cannot be minimized/hidden if it is already fullscreen
|
||||
// so we just close it.
|
||||
if (globalThis.window?.isFullScreen()) app.exit(0);
|
||||
else app.hide();
|
||||
} else {
|
||||
try {
|
||||
globalThis.window?.minimize();
|
||||
globalThis.window?.hide();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
globalThis.window?.on("minimize", () => {
|
||||
if (config.desktopSettings.minimizeToSystemTray) {
|
||||
if (process.platform == "darwin") {
|
||||
app.hide();
|
||||
} else {
|
||||
globalThis.window?.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (desktopIntegration.autoStart) {
|
||||
AutoLaunch.enable(!!desktopIntegration.startMinimized);
|
||||
}
|
||||
}
|
||||
@@ -96,16 +96,22 @@ export async function downloadFile(filename, data, cancelToken) {
|
||||
}
|
||||
|
||||
export async function getUploadedFileSize(hash) {
|
||||
const url = `${hosts.API_HOST}/s3?name=${hash}`;
|
||||
const token = await db.user.tokenManager.getAccessToken();
|
||||
try {
|
||||
const url = `${hosts.API_HOST}/s3?name=${hash}`;
|
||||
const token = await db.user.tokenManager.getAccessToken();
|
||||
|
||||
const attachmentInfo = await fetch(url, {
|
||||
method: "HEAD",
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
const attachmentInfo = await fetch(url, {
|
||||
method: "HEAD",
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
const contentLength = parseInt(attachmentInfo.headers?.get("content-length"));
|
||||
return isNaN(contentLength) ? 0 : contentLength;
|
||||
const contentLength = parseInt(
|
||||
attachmentInfo.headers?.get("content-length")
|
||||
);
|
||||
return isNaN(contentLength) ? 0 : contentLength;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkAttachment(hash) {
|
||||
|
||||
@@ -43,6 +43,9 @@ const SelectionWrapper = ({
|
||||
|
||||
const onLongPress = () => {
|
||||
if (!useSelectionStore.getState().selectionMode) {
|
||||
useSelectionStore.setState({
|
||||
selectedItemsList: []
|
||||
});
|
||||
useSelectionStore.getState().setSelectionMode(true);
|
||||
}
|
||||
useSelectionStore.getState().setSelectedItem(item);
|
||||
|
||||
@@ -165,7 +165,12 @@ export const Component = ({ close, promo }) => {
|
||||
}}
|
||||
size={SIZE.md}
|
||||
>
|
||||
({pricing?.product?.localizedPrice} / mo)
|
||||
(
|
||||
{Platform.OS === "android"
|
||||
? pricing.product?.subscriptionOfferDetails[0].pricingPhases
|
||||
.pricingPhaseList?.[0].formattedPrice
|
||||
: pricing.product?.localizedPrice}{" "}
|
||||
/ mo)
|
||||
</Paragraph>
|
||||
)}
|
||||
|
||||
|
||||
@@ -18,13 +18,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { Platform, View } from "react-native";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import RNIap from "react-native-iap";
|
||||
|
||||
export const PricingItem = ({ product, onPress, compact }) => {
|
||||
export const PricingItem = ({
|
||||
product,
|
||||
onPress,
|
||||
compact
|
||||
}: {
|
||||
product: {
|
||||
type: "yearly" | "monthly";
|
||||
data?: RNIap.Subscription;
|
||||
info: string;
|
||||
offerType?: "yearly" | "monthly";
|
||||
};
|
||||
onPress?: () => void;
|
||||
compact?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<PressableButton
|
||||
onPress={onPress}
|
||||
@@ -54,7 +68,14 @@ export const PricingItem = ({ product, onPress, compact }) => {
|
||||
|
||||
<View>
|
||||
<Paragraph size={SIZE.sm}>
|
||||
<Heading size={SIZE.lg - 2}>{product?.data?.localizedPrice}/</Heading>
|
||||
<Heading size={SIZE.lg - 2}>
|
||||
{Platform.OS === "android"
|
||||
? (product.data as RNIap.SubscriptionAndroid | undefined)
|
||||
?.subscriptionOfferDetails[0].pricingPhases
|
||||
.pricingPhaseList?.[0]?.formattedPrice
|
||||
: (product.data as RNIap.SubscriptionIOS | undefined)
|
||||
?.localizedPrice}
|
||||
</Heading>
|
||||
{product?.type === "yearly" || product?.offerType === "yearly"
|
||||
? "/year"
|
||||
: "/month"}
|
||||
@@ -47,11 +47,15 @@ import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { Walkthrough } from "../walkthroughs";
|
||||
import { PricingItem } from "./pricing-item";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
|
||||
const promoCyclesMonthly = {
|
||||
1: "first month",
|
||||
2: "first 2 months",
|
||||
3: "first 3 months"
|
||||
3: "first 3 months",
|
||||
4: "first 4 months",
|
||||
5: "first 5 months",
|
||||
6: "first 3 months"
|
||||
};
|
||||
|
||||
const promoCyclesYearly = {
|
||||
@@ -65,10 +69,24 @@ export const PricingPlans = ({
|
||||
marginTop,
|
||||
heading = true,
|
||||
compact = false
|
||||
}: {
|
||||
promo?: {
|
||||
promoCode: string;
|
||||
};
|
||||
marginTop?: any;
|
||||
heading?: boolean;
|
||||
compact?: boolean;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const user = useUserStore((state) => state.user);
|
||||
const [product, setProduct] = useState(null);
|
||||
const [product, setProduct] = useState<{
|
||||
type: string;
|
||||
offerType: "monthly" | "yearly";
|
||||
data: RNIap.Subscription;
|
||||
cycleText: string;
|
||||
info: string;
|
||||
}>();
|
||||
|
||||
const [buying, setBuying] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const userCanRequestTrial =
|
||||
@@ -90,27 +108,40 @@ export const PricingPlans = ({
|
||||
}
|
||||
}, [promo?.promoCode]);
|
||||
|
||||
const getPromo = async (code) => {
|
||||
const getPromo = async (code: string) => {
|
||||
try {
|
||||
let productId;
|
||||
let skuId: string;
|
||||
if (code.startsWith("com.streetwriters.notesnook")) {
|
||||
productId = code;
|
||||
skuId = code;
|
||||
} else {
|
||||
productId = await db.offers.getCode(code.split(":")[0], Platform.OS);
|
||||
skuId = await db.offers?.getCode(code.split(":")[0], Platform.OS);
|
||||
}
|
||||
|
||||
let products = await PremiumService.getProducts();
|
||||
let product = products.find((p) => p.productId === productId);
|
||||
const products = await PremiumService.getProducts();
|
||||
const product = products.find((p) => p.productId === skuId);
|
||||
if (!product) return false;
|
||||
let isMonthly = product.productId.indexOf(".mo") > -1;
|
||||
let cycleText = isMonthly
|
||||
const isMonthly = product.productId.indexOf(".mo") > -1;
|
||||
|
||||
const cycleText = isMonthly
|
||||
? promoCyclesMonthly[
|
||||
product.introductoryPriceCyclesAndroid ||
|
||||
product.introductoryPriceNumberOfPeriodsIOS
|
||||
(Platform.OS === "android"
|
||||
? (product as RNIap.SubscriptionAndroid)
|
||||
.subscriptionOfferDetails[0]?.pricingPhases
|
||||
.pricingPhaseList?.[0].billingCycleCount
|
||||
: parseInt(
|
||||
(product as RNIap.SubscriptionIOS)
|
||||
.introductoryPriceNumberOfPeriodsIOS as string
|
||||
)) as keyof typeof promoCyclesMonthly
|
||||
]
|
||||
: promoCyclesYearly[
|
||||
product.introductoryPriceCyclesAndroid ||
|
||||
product.introductoryPriceNumberOfPeriodsIOS
|
||||
(Platform.OS === "android"
|
||||
? (product as RNIap.SubscriptionAndroid)
|
||||
.subscriptionOfferDetails[0]?.pricingPhases
|
||||
.pricingPhaseList?.[0].billingCycleCount
|
||||
: parseInt(
|
||||
(product as RNIap.SubscriptionIOS)
|
||||
.introductoryPriceNumberOfPeriodsIOS as string
|
||||
)) as keyof typeof promoCyclesYearly
|
||||
];
|
||||
|
||||
setProduct({
|
||||
@@ -118,7 +149,7 @@ export const PricingPlans = ({
|
||||
offerType: isMonthly ? "monthly" : "yearly",
|
||||
data: product,
|
||||
cycleText: cycleText,
|
||||
info: "Pay monthly, cancel anytime"
|
||||
info: `Pay ${isMonthly ? "monthly" : "yearly"}, cancel anytime`
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -131,22 +162,37 @@ export const PricingPlans = ({
|
||||
getSkus();
|
||||
}, [getSkus]);
|
||||
|
||||
const buySubscription = async (product) => {
|
||||
if (buying) return;
|
||||
const buySubscription = async (product: RNIap.Subscription) => {
|
||||
if (buying || !product) return;
|
||||
setBuying(true);
|
||||
try {
|
||||
if (!user) {
|
||||
setBuying(false);
|
||||
return;
|
||||
}
|
||||
await RNIap.requestSubscription(
|
||||
product?.productId,
|
||||
false,
|
||||
null,
|
||||
-1,
|
||||
user.id,
|
||||
user.id
|
||||
);
|
||||
useSettingStore.getState().setAppDidEnterBackgroundForAction(true);
|
||||
const androidOfferToken =
|
||||
Platform.OS === "android"
|
||||
? (product as RNIap.SubscriptionAndroid).subscriptionOfferDetails[0]
|
||||
.offerToken
|
||||
: null;
|
||||
|
||||
await RNIap.requestSubscription({
|
||||
sku: product?.productId,
|
||||
obfuscatedAccountIdAndroid: user.id,
|
||||
obfuscatedProfileIdAndroid: user.id,
|
||||
appAccountToken: user.id,
|
||||
andDangerouslyFinishTransactionAutomaticallyIOS: false,
|
||||
subscriptionOffers: androidOfferToken
|
||||
? [
|
||||
{
|
||||
offerToken: androidOfferToken,
|
||||
sku: product?.productId
|
||||
}
|
||||
]
|
||||
: undefined
|
||||
});
|
||||
useSettingStore.getState().setAppDidEnterBackgroundForAction(false);
|
||||
setBuying(false);
|
||||
eSendEvent(eCloseSheet);
|
||||
eSendEvent(eClosePremiumDialog);
|
||||
@@ -198,7 +244,14 @@ export const PricingPlans = ({
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
>
|
||||
{PremiumService.getMontlySub().localizedPrice} / mo
|
||||
{(Platform.OS === "android"
|
||||
? (monthlyPlan?.product as RNIap.SubscriptionAndroid | undefined)
|
||||
?.subscriptionOfferDetails[0]?.pricingPhases
|
||||
.pricingPhaseList?.[0]?.formattedPrice
|
||||
: (monthlyPlan?.product as RNIap.SubscriptionIOS | undefined)
|
||||
?.localizedPrice) ||
|
||||
(PremiumService.getMontlySub() as any)?.localizedPrice}
|
||||
/ mo
|
||||
</Paragraph>
|
||||
<Button
|
||||
onPress={() => {
|
||||
@@ -218,7 +271,7 @@ export const PricingPlans = ({
|
||||
<Button
|
||||
onPress={async () => {
|
||||
try {
|
||||
await db.user.activateTrial();
|
||||
await db.user?.activateTrial();
|
||||
eSendEvent(eClosePremiumDialog);
|
||||
eSendEvent(eCloseSheet);
|
||||
await sleep(300);
|
||||
@@ -247,7 +300,11 @@ export const PricingPlans = ({
|
||||
}}
|
||||
size={SIZE.lg - 4}
|
||||
>
|
||||
{product.data.introductoryPrice}
|
||||
{Platform.OS === "android"
|
||||
? (product.data as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails[0].pricingPhases
|
||||
.pricingPhaseList?.[0].formattedPrice
|
||||
: (product.data as RNIap.SubscriptionIOS)?.introductoryPrice}
|
||||
<Paragraph
|
||||
style={{
|
||||
textDecorationLine: "line-through",
|
||||
@@ -255,7 +312,11 @@ export const PricingPlans = ({
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
>
|
||||
({product.data.localizedPrice})
|
||||
{Platform.OS === "android"
|
||||
? (product.data as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails[1]?.pricingPhases
|
||||
.pricingPhaseList?.[1]?.formattedPrice
|
||||
: (product.data as RNIap.SubscriptionIOS)?.localizedPrice}
|
||||
</Paragraph>{" "}
|
||||
for {product.cycleText}
|
||||
</Heading>
|
||||
@@ -263,9 +324,9 @@ export const PricingPlans = ({
|
||||
|
||||
{user && !product ? (
|
||||
<>
|
||||
{heading || monthlyPlan?.info?.discount > 0 ? (
|
||||
{heading || (monthlyPlan?.info?.discount || 0) > 0 ? (
|
||||
<>
|
||||
{monthlyPlan && monthlyPlan?.info?.discount > 0 ? (
|
||||
{monthlyPlan && (monthlyPlan?.info?.discount || 0) > 0 ? (
|
||||
<View
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
@@ -300,7 +361,10 @@ export const PricingPlans = ({
|
||||
}}
|
||||
>
|
||||
<PricingItem
|
||||
onPress={() => buySubscription(monthlyPlan?.product)}
|
||||
onPress={() => {
|
||||
if (!monthlyPlan?.product) return;
|
||||
buySubscription(monthlyPlan?.product);
|
||||
}}
|
||||
compact={compact}
|
||||
product={{
|
||||
type: "monthly",
|
||||
@@ -319,7 +383,10 @@ export const PricingPlans = ({
|
||||
)}
|
||||
|
||||
<PricingItem
|
||||
onPress={() => buySubscription(yearlyPlan?.product)}
|
||||
onPress={() => {
|
||||
if (!yearlyPlan?.product) return;
|
||||
buySubscription(yearlyPlan?.product);
|
||||
}}
|
||||
compact={compact}
|
||||
product={{
|
||||
type: "yearly",
|
||||
@@ -346,7 +413,7 @@ export const PricingPlans = ({
|
||||
eSendEvent(eCloseSimpleDialog);
|
||||
setBuying(true);
|
||||
try {
|
||||
if (!(await getPromo(value)))
|
||||
if (!(await getPromo(value as string)))
|
||||
throw new Error("Error applying promo code");
|
||||
ToastEvent.show({
|
||||
heading: "Discount applied!",
|
||||
@@ -358,7 +425,7 @@ export const PricingPlans = ({
|
||||
setBuying(false);
|
||||
ToastEvent.show({
|
||||
heading: "Promo code invalid or expired",
|
||||
message: e.message,
|
||||
message: (e as Error).message,
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
@@ -427,7 +494,10 @@ export const PricingPlans = ({
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => buySubscription(product.data)}
|
||||
onPress={() => {
|
||||
if (!product?.data) return;
|
||||
buySubscription(product.data);
|
||||
}}
|
||||
height={40}
|
||||
width="50%"
|
||||
type="accent"
|
||||
@@ -436,7 +506,7 @@ export const PricingPlans = ({
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
setProduct(null);
|
||||
setProduct(undefined);
|
||||
}}
|
||||
style={{
|
||||
marginTop: 5
|
||||
@@ -523,7 +593,7 @@ export const PricingPlans = ({
|
||||
<Paragraph
|
||||
size={SIZE.xs}
|
||||
onPress={() => {
|
||||
openLinkInBrowser("https://notesnook.com/tos", colors)
|
||||
openLinkInBrowser("https://notesnook.com/tos")
|
||||
.catch(() => {})
|
||||
.then(() => {});
|
||||
}}
|
||||
@@ -538,7 +608,7 @@ export const PricingPlans = ({
|
||||
<Paragraph
|
||||
size={SIZE.xs}
|
||||
onPress={() => {
|
||||
openLinkInBrowser("https://notesnook.com/privacy", colors)
|
||||
openLinkInBrowser("https://notesnook.com/privacy")
|
||||
.catch(() => {})
|
||||
.then(() => {});
|
||||
}}
|
||||
@@ -229,12 +229,25 @@ async function saveEditorState() {
|
||||
MMKV.setString("appState", state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RNIap.Purchase} subscription
|
||||
*/
|
||||
const onSuccessfulSubscription = async (subscription) => {
|
||||
console.log(
|
||||
"Subscription success!",
|
||||
subscription.transactionId,
|
||||
subscription.obfuscatedAccountIdAndroid,
|
||||
subscription.obfuscatedProfileIdAndroid
|
||||
);
|
||||
await PremiumService.subscriptions.set(subscription);
|
||||
await PremiumService.subscriptions.verify(subscription);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RNIap.PurchaseError} error
|
||||
*/
|
||||
const onSubscriptionError = async (error) => {
|
||||
ToastEvent.show({
|
||||
heading: "Failed to subscribe",
|
||||
@@ -490,9 +503,9 @@ export const useAppEvents = () => {
|
||||
);
|
||||
|
||||
const subscribeToIAPListeners = useCallback(async () => {
|
||||
await RNIap.initConnection()
|
||||
.catch(() => null)
|
||||
.then(async () => {
|
||||
RNIap.flushFailedPurchasesCachedAsPendingAndroid()
|
||||
.catch(() => {})
|
||||
.then(() => {
|
||||
refValues.current.subsriptionSuccessListener =
|
||||
RNIap.purchaseUpdatedListener(onSuccessfulSubscription);
|
||||
refValues.current.subsriptionErrorListener =
|
||||
|
||||
@@ -115,7 +115,7 @@ const Editor = React.memo(
|
||||
} else {
|
||||
editor.commands.updateImage({
|
||||
hash: hash,
|
||||
src: src
|
||||
dataurl: src
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -211,9 +211,20 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
|
||||
);
|
||||
};
|
||||
|
||||
insertImage = async (image: ImageAttributes) => {
|
||||
insertImage = async (
|
||||
image: Omit<ImageAttributes, "bloburl"> & {
|
||||
dataurl: string;
|
||||
}
|
||||
) => {
|
||||
await this.doAsync(
|
||||
`editor && editor.commands.insertImage(${JSON.stringify(image)})`
|
||||
`const image = toBlobURL("${image.dataurl}", "${image.hash}");
|
||||
editor && editor.commands.insertImage({
|
||||
...${JSON.stringify({
|
||||
...image,
|
||||
dataurl: undefined
|
||||
})},
|
||||
bloburl: image
|
||||
})`
|
||||
);
|
||||
};
|
||||
|
||||
@@ -225,11 +236,20 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
|
||||
);
|
||||
};
|
||||
|
||||
updateImage = async ({ src, hash }: Partial<ImageAttributes>) => {
|
||||
updateImage = async ({
|
||||
hash,
|
||||
dataurl
|
||||
}: Partial<Omit<ImageAttributes, "bloburl">> & {
|
||||
dataurl: string;
|
||||
}) => {
|
||||
await this.doAsync(
|
||||
`editor && editor.commands.updateImage(${JSON.stringify({
|
||||
`const image = toBlobURL("${dataurl}", "${hash}");
|
||||
editor && editor.commands.updateImage(${JSON.stringify({
|
||||
hash
|
||||
})},${JSON.stringify({ dataurl: src, hash, preventUpdate: true })})`
|
||||
})}, {
|
||||
...${JSON.stringify({ hash, preventUpdate: true })},
|
||||
bloburl: image
|
||||
})`
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import { FILE_SIZE_LIMIT, IMAGE_SIZE_LIMIT } from "../../../utils/constants";
|
||||
import { eCloseSheet } from "../../../utils/events";
|
||||
import { editorController, editorState } from "./utils";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import filesystem from "../../../common/filesystem";
|
||||
|
||||
const showEncryptionSheet = (file) => {
|
||||
presentSheet({
|
||||
@@ -230,6 +231,16 @@ const handleImageResponse = async (response, options) => {
|
||||
});
|
||||
|
||||
let fileName = image.originalFileName || image.fileName;
|
||||
|
||||
editorController.current?.commands.insertImage({
|
||||
hash: hash,
|
||||
mime: image.type,
|
||||
title: fileName,
|
||||
dataurl: b64,
|
||||
size: image.fileSize,
|
||||
filename: fileName
|
||||
});
|
||||
|
||||
if (!(await attachFile(uri, hash, image.type, fileName, options))) return;
|
||||
const isPng = /(png)/g.test(image.type);
|
||||
const isJpeg = /(jpeg|jpg)/g.test(image.type);
|
||||
@@ -243,15 +254,6 @@ const handleImageResponse = async (response, options) => {
|
||||
}
|
||||
|
||||
if (Platform.OS === "ios") await RNFetchBlob.fs.unlink(uri);
|
||||
|
||||
editorController.current?.commands.insertImage({
|
||||
hash: hash,
|
||||
mime: image.type,
|
||||
title: fileName,
|
||||
dataurl: b64,
|
||||
size: image.fileSize,
|
||||
filename: fileName
|
||||
});
|
||||
};
|
||||
|
||||
export async function attachFile(uri, hash, type, filename, options) {
|
||||
@@ -269,6 +271,10 @@ export async function attachFile(uri, hash, type, filename, options) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!options.reupload && exists) {
|
||||
options.reupload = (await filesystem.getUploadedFileSize(hash)) <= 0;
|
||||
}
|
||||
|
||||
if (!exists || options?.reupload) {
|
||||
let key = await db.attachments.generateKey();
|
||||
encryptionInfo = await Sodium.encryptFile(key, {
|
||||
|
||||
@@ -88,8 +88,8 @@ export const settingsGroups: SettingSection[] = [
|
||||
return isBasic || !user.subscription?.type
|
||||
? "Subscribe to Pro"
|
||||
: isTrial
|
||||
? "Your free trial has started"
|
||||
: "Subscription details";
|
||||
? "Your free trial has started"
|
||||
: "Subscription details";
|
||||
},
|
||||
type: "component",
|
||||
component: "subscription",
|
||||
@@ -111,16 +111,16 @@ export const settingsGroups: SettingSection[] = [
|
||||
return user.subscription?.type === 2
|
||||
? "You signed up on " + startDate
|
||||
: user.subscription?.type === 1
|
||||
? "Your free trial will end on " + expiryDate
|
||||
: user.subscription?.type === 6
|
||||
? subscriptionDaysLeft.time < -3
|
||||
? "Your subscription has ended"
|
||||
: "Your account will be downgraded to Basic in 3 days"
|
||||
: user.subscription?.type === 7
|
||||
? `Your subscription will end on ${expiryDate}.`
|
||||
: user.subscription?.type === 5
|
||||
? `Your subscription will renew on ${expiryDate}.`
|
||||
: "Never hesitate to choose privacy";
|
||||
? "Your free trial will end on " + expiryDate
|
||||
: user.subscription?.type === 6
|
||||
? subscriptionDaysLeft.time < -3
|
||||
? "Your subscription has ended"
|
||||
: "Your account will be downgraded to Basic in 3 days"
|
||||
: user.subscription?.type === 7
|
||||
? `Your subscription will end on ${expiryDate}.`
|
||||
: user.subscription?.type === 5
|
||||
? `Your subscription will renew on ${expiryDate}.`
|
||||
: "Never hesitate to choose privacy";
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -892,16 +892,6 @@ export const settingsGroups: SettingSection[] = [
|
||||
name: "Restore backup",
|
||||
description: "Restore backup from phone storage.",
|
||||
modifer: () => {
|
||||
const user = useUserStore.getState().user;
|
||||
if (!user || !user?.email) {
|
||||
ToastEvent.show({
|
||||
heading: "Login required",
|
||||
message: "Please log in to your account to restore backup",
|
||||
type: "error",
|
||||
context: "global"
|
||||
});
|
||||
return;
|
||||
}
|
||||
eSendEvent(eOpenRestoreDialog);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -88,6 +88,13 @@ export const Subscription = () => {
|
||||
}
|
||||
};
|
||||
|
||||
function getPrice() {
|
||||
return Platform.OS === "android"
|
||||
? monthlyPlan?.product?.subscriptionOfferDetails[0].pricingPhases
|
||||
.pricingPhaseList?.[0]?.formattedPrice
|
||||
: monthlyPlan?.product?.localizedPrice;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{isNotPro ? (
|
||||
@@ -114,12 +121,8 @@ export const Subscription = () => {
|
||||
Config.GITHUB_RELEASE !== "true"
|
||||
? "Resubscribe from Google Playstore"
|
||||
: user.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_EXPIRED
|
||||
? `Resubscribe to Pro (${
|
||||
monthlyPlan?.product?.localizedPrice || "$4.49"
|
||||
} / mo)`
|
||||
: `Get Pro (${
|
||||
monthlyPlan?.product?.localizedPrice || "$4.49"
|
||||
} / mo)`
|
||||
? `Resubscribe to Pro (${getPrice() || "$4.49"} / mo)`
|
||||
: `Get Pro (${getPrice() || "$4.49"} / mo)`
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -39,6 +39,10 @@ import { eSendEvent, presentSheet, ToastEvent } from "./event-manager";
|
||||
|
||||
import SettingsService from "./settings";
|
||||
let premiumStatus = 0;
|
||||
|
||||
/**
|
||||
* @type {RNIap.Subscription[]}
|
||||
*/
|
||||
let products = [];
|
||||
let user = null;
|
||||
|
||||
@@ -68,7 +72,9 @@ async function setPremiumStatus() {
|
||||
}
|
||||
try {
|
||||
await RNIap.initConnection();
|
||||
products = await RNIap.getSubscriptions(itemSkus);
|
||||
products = await RNIap.getSubscriptions({
|
||||
skus: itemSkus
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("subscriptions: ", e);
|
||||
}
|
||||
@@ -223,12 +229,21 @@ const showVerifyEmailDialog = () => {
|
||||
};
|
||||
|
||||
const subscriptions = {
|
||||
/**
|
||||
*
|
||||
* @returns {RNIap.Purchase} subscription
|
||||
*/
|
||||
get: async () => {
|
||||
if (Platform.OS === "android") return;
|
||||
let _subscriptions = MMKV.getString("subscriptionsIOS");
|
||||
if (!_subscriptions) return [];
|
||||
return JSON.parse(_subscriptions);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RNIap.Purchase} subscription
|
||||
* @returns
|
||||
*/
|
||||
set: async (subscription) => {
|
||||
if (Platform.OS === "android") return;
|
||||
let _subscriptions = MMKV.getString("subscriptionsIOS");
|
||||
@@ -263,6 +278,10 @@ const subscriptions = {
|
||||
MMKV.setString("subscriptionsIOS", JSON.stringify(_subscriptions));
|
||||
}
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RNIap.Purchase} subscription
|
||||
*/
|
||||
verify: async (subscription) => {
|
||||
if (Platform.OS === "android") return;
|
||||
|
||||
|
||||
@@ -67,15 +67,10 @@ export const deleteItems = async (item, context) => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
item &&
|
||||
item.id &&
|
||||
useSelectionStore.getState().selectedItemsList.length === 0
|
||||
) {
|
||||
useSelectionStore.getState().setSelectedItem(item);
|
||||
}
|
||||
|
||||
const { selectedItemsList } = useSelectionStore.getState();
|
||||
const selectedItemsList = item
|
||||
? [item]
|
||||
: useSelectionStore.getState().selectedItemsList;
|
||||
|
||||
let notes = selectedItemsList.filter((i) => i.type === "note");
|
||||
let notebooks = selectedItemsList.filter((i) => i.type === "notebook");
|
||||
@@ -179,7 +174,9 @@ export const deleteItems = async (item, context) => {
|
||||
});
|
||||
}
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().clearSelection();
|
||||
if (!item) {
|
||||
useSelectionStore.getState().clearSelection();
|
||||
}
|
||||
useMenuStore.getState().setMenuPins();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
SearchService.updateAndSearch();
|
||||
|
||||
@@ -111,7 +111,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2065
|
||||
versionCode 2070
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -8,6 +8,10 @@ import com.ammarahmed.mmkv.MMKV;
|
||||
|
||||
public class OnClearFromRecentService extends Service {
|
||||
|
||||
static {
|
||||
System.loadLibrary("rnmmkv");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
@@ -15,7 +19,6 @@ public class OnClearFromRecentService extends Service {
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@@ -31,8 +34,10 @@ public class OnClearFromRecentService extends Service {
|
||||
MMKV mmkv = MMKV.mmkvWithID("default",MMKV.SINGLE_PROCESS_MODE);
|
||||
mmkv.removeValueForKey("appState");
|
||||
stopSelf();
|
||||
} catch (Exception e) {
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
//System.exit(0);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ buildscript {
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 33
|
||||
targetSdkVersion = 33
|
||||
ext.kotlinVersion = '1.6.10'
|
||||
ext.kotlinVersion = '1.8.0'
|
||||
androidXAnnotation = "1.1.0"
|
||||
androidXCoreVersion = "1.7.0"
|
||||
androidXCore = "1.7.0"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
- Fixed HTML & PDF exports
|
||||
- Improved task lists in editor
|
||||
- Improved loading images
|
||||
- Allow restoring backups when logged out
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -997,7 +997,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1071,7 +1071,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1102,7 +1102,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1176,7 +1176,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1335,7 +1335,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1347,7 +1347,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1378,7 +1378,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1391,7 +1391,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1421,7 +1421,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1495,7 +1495,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1526,7 +1526,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1601,7 +1601,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -477,7 +477,7 @@ PODS:
|
||||
- React-Core
|
||||
- RNGestureHandler (2.12.0):
|
||||
- React-Core
|
||||
- RNIap (7.5.6):
|
||||
- RNIap (12.11.0):
|
||||
- React-Core
|
||||
- RNKeychain (4.0.5):
|
||||
- React
|
||||
@@ -911,7 +911,7 @@ SPEC CHECKSUMS:
|
||||
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
|
||||
RNFlashList: 399bf6a0db68f594ad2c86aaff3ea39564f39f8a
|
||||
RNGestureHandler: dec4645026e7401a0899f2846d864403478ff6a5
|
||||
RNIap: d248609d1b8937e63bd904e865c318e9b1457eff
|
||||
RNIap: fc9af04ee706894a80c9d8f979bae930b0dee191
|
||||
RNKeychain: 840f8e6f13be0576202aefcdffd26a4f54bfe7b5
|
||||
RNNotifee: 2ae3c18196e6f307fa62ae5c8e5305dea03ff147
|
||||
RNPrivacySnapshot: 8eaf571478a353f2e5184f5c803164f22428b023
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"react-native-get-random-values": "^1.7.0",
|
||||
"react-native-gzip": "1.1.0",
|
||||
"react-native-html-to-pdf-lite": "^0.9.1",
|
||||
"react-native-iap": "7.5.6",
|
||||
"react-native-iap": "12.11.0",
|
||||
"react-native-image-picker": "4.1.2",
|
||||
"react-native-in-app-review": "4.3.3",
|
||||
"react-native-keychain": "4.0.5",
|
||||
|
||||
892
apps/mobile/package-lock.json
generated
892
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.10",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -41,6 +41,7 @@
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0"
|
||||
"react-native": "0.72.0",
|
||||
"react-native-iap": "12.11.0"
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,9 @@
|
||||
"@notesnook/theme": ["../../packages/theme"],
|
||||
"@types/react": ["./node_modules/@types/react"]
|
||||
},
|
||||
"incremental": true
|
||||
"incremental": true,
|
||||
"maxNodeModuleJsDepth": 5,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"exclude": ["native"]
|
||||
}
|
||||
|
||||
21
apps/theme-builder/package-lock.json
generated
21
apps/theme-builder/package-lock.json
generated
@@ -1377,7 +1377,7 @@
|
||||
},
|
||||
"../web": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.6.5",
|
||||
"version": "2.6.8",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@aws-sdk/util-base64-browser": "^3.208.0",
|
||||
@@ -1446,6 +1446,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
"@playwright/test": "^1.36.2",
|
||||
"@swc/core": "1.3.61",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@types/babel__core": "^7.20.1",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
@@ -1456,7 +1457,8 @@
|
||||
"@types/react-dom": "17.0.2",
|
||||
"@types/react-modal": "3.13.1",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"@types/wicg-file-system-access": "^2020.9.6",
|
||||
"@vitejs/plugin-react-swc": "3.3.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
@@ -1470,14 +1472,14 @@
|
||||
"otplib": "^12.0.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"rollup": "^3.27.0",
|
||||
"rollup": "^3.29.4",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"swc-plugin-react-remove-properties": "^0.1.4",
|
||||
"vite": "^4.3.9",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-env-compatible": "^1.1.1",
|
||||
"vite-plugin-pwa": "^0.16.3",
|
||||
"vite-plugin-svgr": "^3.2.0",
|
||||
"vitest": "^0.32.0",
|
||||
"vitest": "^0.34.6",
|
||||
"workbox-core": "^7.0.0",
|
||||
"workbox-expiration": "^7.0.0",
|
||||
"workbox-precaching": "^7.0.0",
|
||||
@@ -2406,15 +2408,6 @@
|
||||
"@trpc/server": "10.38.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/server": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.38.3.tgz",
|
||||
"integrity": "sha512-9s8/kwo2IDB5hwB2SKZZrfevRhdb1f9fdXtIYd3lbQuf2jQaC/LyQuHaIQjDQoUx9updBfsHXcFFPiCP1DL6pg==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.1",
|
||||
"dev": true,
|
||||
|
||||
38767
apps/web/package-lock.json
generated
38767
apps/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.9",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
@@ -74,6 +74,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
"@playwright/test": "^1.36.2",
|
||||
"@swc/core": "1.3.61",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@types/babel__core": "^7.20.1",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
@@ -84,7 +85,8 @@
|
||||
"@types/react-dom": "17.0.2",
|
||||
"@types/react-modal": "3.13.1",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"@types/wicg-file-system-access": "^2020.9.6",
|
||||
"@vitejs/plugin-react-swc": "3.3.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
@@ -98,14 +100,14 @@
|
||||
"otplib": "^12.0.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"rollup": "^3.27.0",
|
||||
"rollup": "^3.29.4",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"swc-plugin-react-remove-properties": "^0.1.4",
|
||||
"vite": "^4.3.9",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-env-compatible": "^1.1.1",
|
||||
"vite-plugin-pwa": "^0.16.3",
|
||||
"vite-plugin-svgr": "^3.2.0",
|
||||
"vitest": "^0.32.0",
|
||||
"vitest": "^0.34.6",
|
||||
"workbox-core": "^7.0.0",
|
||||
"workbox-expiration": "^7.0.0",
|
||||
"workbox-precaching": "^7.0.0",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 9.2 KiB |
@@ -129,7 +129,6 @@ export default function AppEffects({ setShow }: AppEffectsProps) {
|
||||
) {
|
||||
const [key, status] = getProcessingStatusFromType(type);
|
||||
|
||||
console.log("handleDownloadUploadProgresss", key, status, current, total);
|
||||
if (current === total) {
|
||||
removeStatus(key);
|
||||
} else {
|
||||
|
||||
@@ -24,6 +24,7 @@ import Config from "./utils/config";
|
||||
|
||||
import { initalizeLogger, logger } from "./utils/logger";
|
||||
import { AuthProps } from "./views/auth";
|
||||
import { initializeFeatureChecks } from "./utils/feature-check";
|
||||
|
||||
type Route<TProps = null> = {
|
||||
component: () => Promise<{
|
||||
@@ -142,6 +143,8 @@ function isSessionExpired(path: Routes): RouteWithPath<AuthProps> | null {
|
||||
|
||||
export async function init() {
|
||||
await initalizeLogger();
|
||||
await initializeFeatureChecks();
|
||||
|
||||
const { path, route } = getRoute();
|
||||
return { ...route, path };
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ 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 FS from "../interfaces/fs";
|
||||
import { lazify } from "../utils/lazify";
|
||||
import { db } from "./db";
|
||||
|
||||
async function download(hash: string) {
|
||||
@@ -42,13 +42,15 @@ export async function saveAttachment(hash: string) {
|
||||
if (!response) return;
|
||||
|
||||
const { attachment, key } = response;
|
||||
await FS.saveFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.metadata.filename,
|
||||
type: attachment.metadata.type,
|
||||
isUploaded: !!attachment.dateUploaded
|
||||
});
|
||||
await lazify(import("../interfaces/fs"), ({ default: FS }) =>
|
||||
FS.saveFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.metadata.filename,
|
||||
type: attachment.metadata.type,
|
||||
isUploaded: !!attachment.dateUploaded
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
type OutputTypeToReturnType = {
|
||||
@@ -67,13 +69,15 @@ export async function downloadAttachment<
|
||||
if (type === "base64" || type === "text")
|
||||
return (await db.attachments?.read(hash, type)) as TOutputType;
|
||||
|
||||
const blob = await FS.decryptFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.metadata.filename,
|
||||
type: attachment.metadata.type,
|
||||
isUploaded: !!attachment.dateUploaded
|
||||
});
|
||||
const blob = await lazify(import("../interfaces/fs"), ({ default: FS }) =>
|
||||
FS.decryptFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.metadata.filename,
|
||||
type: attachment.metadata.type,
|
||||
isUploaded: !!attachment.dateUploaded
|
||||
})
|
||||
);
|
||||
|
||||
if (!blob) return;
|
||||
return blob as TOutputType;
|
||||
@@ -84,7 +88,9 @@ export async function checkAttachment(hash: string) {
|
||||
if (!attachment) return { failed: "Attachment not found." };
|
||||
|
||||
try {
|
||||
const size = await FS.getUploadedFileSize(hash);
|
||||
const size = await lazify(import("../interfaces/fs"), ({ default: FS }) =>
|
||||
FS.getUploadedFileSize(hash)
|
||||
);
|
||||
if (size <= 0) return { failed: "File length is 0." };
|
||||
} catch (e) {
|
||||
return { failed: e instanceof Error ? e.message : "Unknown error." };
|
||||
|
||||
@@ -21,6 +21,7 @@ import { EventSourcePolyfill as EventSource } from "event-source-polyfill";
|
||||
import { DatabasePersistence, NNStorage } from "../interfaces/storage";
|
||||
import { logger } from "../utils/logger";
|
||||
import type Database from "@notesnook/core/dist/api";
|
||||
import { showMigrationDialog } from "./dialog-controller";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
@@ -70,7 +71,6 @@ async function initializeDatabase(persistence: DatabasePersistence) {
|
||||
logger.measure("Database initialization");
|
||||
|
||||
if (db.migrations?.required()) {
|
||||
const { showMigrationDialog } = await import("./dialog-controller");
|
||||
await showMigrationDialog();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,12 +41,21 @@ export async function exportToPDF(
|
||||
iframe.style.width = "0";
|
||||
iframe.style.height = "0";
|
||||
iframe.style.border = "0";
|
||||
iframe.loading = "eager";
|
||||
|
||||
iframe.onload = () => {
|
||||
iframe.onload = async () => {
|
||||
if (!iframe.contentWindow) return;
|
||||
if (iframe.contentDocument) iframe.contentDocument.title = title;
|
||||
iframe.contentWindow.onbeforeunload = () => closePrint(false);
|
||||
iframe.contentWindow.onafterprint = () => closePrint(true);
|
||||
|
||||
if (
|
||||
iframe.contentDocument &&
|
||||
!!iframe.contentDocument.querySelector(".math-block,.math-inline")
|
||||
) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
iframe.contentWindow.print();
|
||||
};
|
||||
|
||||
@@ -119,7 +128,11 @@ export async function exportNotes(
|
||||
return await exportToPDF(note.title, exported);
|
||||
}
|
||||
|
||||
files.push({ filename: note.title, content: exported });
|
||||
files.push({
|
||||
filename: note.title,
|
||||
content: exported,
|
||||
date: note.dateEdited
|
||||
});
|
||||
}
|
||||
|
||||
if (!files.length) return false;
|
||||
|
||||
@@ -290,7 +290,6 @@ const AttachmentMenuItems: (
|
||||
key: "download",
|
||||
title: status?.type === "download" ? "Cancel download" : "Download",
|
||||
icon: Download.path,
|
||||
isDisabled: !attachment.dateUploaded,
|
||||
onClick: async () => {
|
||||
const isDownloading = status?.type === "download";
|
||||
if (isDownloading) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import IconTag from "../icon-tag";
|
||||
import { db } from "../../common/db";
|
||||
import { useMenuTrigger } from "../../hooks/use-menu";
|
||||
import { MenuItem } from "@notesnook/ui";
|
||||
import { navigate } from "../../navigation";
|
||||
|
||||
type HeaderProps = { readonly: boolean };
|
||||
function Header(props: HeaderProps) {
|
||||
@@ -39,7 +40,7 @@ function Header(props: HeaderProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{!readonly && id && (
|
||||
{id && (
|
||||
<Flex
|
||||
sx={{ lineHeight: 2.5, alignItems: "center", flexWrap: "wrap" }}
|
||||
data-test-id="tags"
|
||||
@@ -50,24 +51,34 @@ function Header(props: HeaderProps) {
|
||||
key={tag}
|
||||
text={db.tags?.alias(tag)}
|
||||
icon={Tag}
|
||||
title={`Click to remove`}
|
||||
onClick={() => setTag(tag)}
|
||||
title={tag}
|
||||
onClick={() => {
|
||||
const tagItem = db.tags?.tag(tag);
|
||||
if (!tagItem) {
|
||||
setTag(tag);
|
||||
return;
|
||||
}
|
||||
navigate(`/tags/${tagItem.id}`);
|
||||
}}
|
||||
onDismiss={readonly ? undefined : () => setTag(tag)}
|
||||
styles={{ container: { mr: 1 }, text: { fontSize: "body" } }}
|
||||
/>
|
||||
))}
|
||||
<Autosuggest
|
||||
sessionId={id}
|
||||
filter={(query) =>
|
||||
db.lookup?.tags(filterableTags, query).slice(0, 10) || []
|
||||
}
|
||||
onAdd={(value) => setTag(value)}
|
||||
onSelect={(item) => setTag(item.title)}
|
||||
onRemove={() => {
|
||||
if (tags.length <= 0) return;
|
||||
setTag(tags[tags.length - 1]);
|
||||
}}
|
||||
defaultItems={filterableTags?.slice(0, 10) || []}
|
||||
/>
|
||||
{!readonly && (
|
||||
<Autosuggest
|
||||
sessionId={id}
|
||||
filter={(query) =>
|
||||
db.lookup?.tags(filterableTags, query).slice(0, 10) || []
|
||||
}
|
||||
onAdd={(value) => setTag(value)}
|
||||
onSelect={(item) => setTag(item.title)}
|
||||
onRemove={() => {
|
||||
if (tags.length <= 0) return;
|
||||
setTag(tags[tags.length - 1]);
|
||||
}}
|
||||
defaultItems={filterableTags?.slice(0, 10) || []}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
</>
|
||||
@@ -86,6 +97,7 @@ type AutosuggestProps = {
|
||||
export function Autosuggest(props: AutosuggestProps) {
|
||||
const { sessionId, filter, onRemove, onSelect, onAdd, defaultItems } = props;
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const arrowDown = useRef<boolean>();
|
||||
const filteredItems = useRef<any[]>([]);
|
||||
const { openMenu, closeMenu, isOpen } = useMenuTrigger();
|
||||
const clearInput = useCallback(() => {
|
||||
@@ -178,8 +190,13 @@ export function Autosuggest(props: AutosuggestProps) {
|
||||
data-test-id="editor-tag-input"
|
||||
onFocus={() => {
|
||||
const text = getInputValue();
|
||||
console.log(defaultItems);
|
||||
if (!text) onOpenMenu(defaultItems.slice());
|
||||
else onOpenMenu([]);
|
||||
}}
|
||||
onClick={() => {
|
||||
const text = getInputValue();
|
||||
if (!text) onOpenMenu(defaultItems.slice());
|
||||
else onOpenMenu([]);
|
||||
}}
|
||||
onChange={(e) => {
|
||||
const { value } = e.target;
|
||||
@@ -191,22 +208,17 @@ export function Autosuggest(props: AutosuggestProps) {
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
const text = getInputValue();
|
||||
if (
|
||||
e.key === "Enter" &&
|
||||
!!text &&
|
||||
isOpen &&
|
||||
filteredItems.current.length <= 0
|
||||
) {
|
||||
if (e.key === "Enter" && !!text && isOpen && !arrowDown.current) {
|
||||
onAction("add", text);
|
||||
} else if (e.key === "Enter" && !!text && isOpen) {
|
||||
onAction("select", filteredItems.current[0]);
|
||||
} else if (!text && e.key === "Backspace") {
|
||||
onRemove();
|
||||
closeMenu();
|
||||
} else if (e.key === "Escape") {
|
||||
arrowDown.current = false;
|
||||
closeMenu();
|
||||
e.stopPropagation();
|
||||
} else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||
arrowDown.current = true;
|
||||
if (e.key === "ArrowDown" && !text) onOpenMenu(defaultItems.slice());
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
@@ -23,7 +23,6 @@ import { db } from "../../common/db";
|
||||
import { showBuyDialog } from "../../common/dialog-controller";
|
||||
import { TaskManager } from "../../common/task-manager";
|
||||
import { isUserPremium } from "../../hooks/use-is-user-premium";
|
||||
import fs from "../../interfaces/fs";
|
||||
import { showToast } from "../../utils/toast";
|
||||
import { showFilePicker } from "../../utils/file-picker";
|
||||
|
||||
@@ -151,25 +150,29 @@ async function addAttachment(
|
||||
dataurl: string | undefined,
|
||||
options: AddAttachmentOptions = {}
|
||||
): Promise<Attachment> {
|
||||
const { expectedFileHash, forceWrite, showProgress = true } = options;
|
||||
const { default: FS } = await import("../../interfaces/fs");
|
||||
const { expectedFileHash, showProgress = true } = options;
|
||||
let forceWrite = options.forceWrite;
|
||||
|
||||
const action = async () => {
|
||||
const reader: ReadableStreamReader<Uint8Array> = (
|
||||
file.stream() as unknown as ReadableStream<Uint8Array>
|
||||
).getReader();
|
||||
|
||||
const { hash, type: hashType } = await fs.hashStream(reader);
|
||||
const reader = file.stream().getReader();
|
||||
const { hash, type: hashType } = await FS.hashStream(reader);
|
||||
reader.releaseLock();
|
||||
|
||||
if (expectedFileHash && hash !== expectedFileHash)
|
||||
throw new Error(
|
||||
`Please select the same file for reuploading. Expected hash ${expectedFileHash} but got ${hash}.`
|
||||
);
|
||||
|
||||
const exists = db.attachments?.exists(hash);
|
||||
if (!forceWrite && exists) {
|
||||
forceWrite = (await FS.getUploadedFileSize(hash)) <= 0;
|
||||
}
|
||||
|
||||
if (forceWrite || !exists) {
|
||||
const key: SerializedKey = await getEncryptionKey();
|
||||
|
||||
const output = await fs.writeEncryptedFile(file, key, hash);
|
||||
const output = await FS.writeEncryptedFile(file, key, hash);
|
||||
if (!output) throw new Error("Could not encrypt file.");
|
||||
|
||||
if (forceWrite && exists) await db.attachments?.reset(hash);
|
||||
|
||||
@@ -57,6 +57,7 @@ import { store as editorstore } from "../../stores/editor-store";
|
||||
import { ScopedThemeProvider } from "../theme-provider";
|
||||
import { writeText } from "clipboard-polyfill";
|
||||
import { useStore as useThemeStore } from "../../stores/theme-store";
|
||||
import { toBlobURL } from "@notesnook/editor/dist/utils/downloader";
|
||||
|
||||
type OnChangeHandler = (
|
||||
id: string | undefined,
|
||||
@@ -446,7 +447,7 @@ function toIEditor(editor: Editor): IEditor {
|
||||
if (file.dataurl) {
|
||||
editor.current?.commands.insertImage({
|
||||
...file,
|
||||
dataurl: file.dataurl
|
||||
bloburl: toBlobURL(file.dataurl, file.hash)
|
||||
});
|
||||
} else editor.current?.commands.insertAttachment(file);
|
||||
},
|
||||
@@ -455,7 +456,7 @@ function toIEditor(editor: Editor): IEditor {
|
||||
loadImage: (hash, dataurl) =>
|
||||
editor.current?.commands.updateImage(
|
||||
{ hash },
|
||||
{ hash, dataurl, preventUpdate: true }
|
||||
{ hash, bloburl: toBlobURL(dataurl, hash), preventUpdate: true }
|
||||
),
|
||||
sendAttachmentProgress: (hash, type, progress) =>
|
||||
editor.current?.commands.setAttachmentProgress({
|
||||
|
||||
@@ -26,6 +26,8 @@ import useTablet from "../../hooks/use-tablet";
|
||||
import { useEditorConfig } from "./context";
|
||||
import { getFontById } from "@notesnook/editor";
|
||||
import { AppEventManager, AppEvents } from "../../common/app-events";
|
||||
import { replaceDateTime } from "@notesnook/editor/dist/extensions/date-time";
|
||||
import { useStore as useSettingsStore } from "../../stores/setting-store";
|
||||
|
||||
type TitleBoxProps = {
|
||||
readonly: boolean;
|
||||
@@ -38,6 +40,9 @@ function TitleBox(props: TitleBoxProps) {
|
||||
const isMobile = useMobile();
|
||||
const isTablet = useTablet();
|
||||
const { editorConfig } = useEditorConfig();
|
||||
const dateFormat = useSettingsStore((store) => store.dateFormat);
|
||||
const timeFormat = useSettingsStore((store) => store.timeFormat);
|
||||
|
||||
const fontFamily = useMemo(
|
||||
() => getFontById(editorConfig.fontFamily)?.font || "heading",
|
||||
[editorConfig.fontFamily]
|
||||
@@ -111,6 +116,11 @@ function TitleBox(props: TitleBoxProps) {
|
||||
}}
|
||||
onChange={(e) => {
|
||||
const { sessionId, id } = store.get().session;
|
||||
e.target.value = replaceDateTime(
|
||||
e.target.value,
|
||||
dateFormat,
|
||||
timeFormat
|
||||
);
|
||||
debouncedOnTitleChange(sessionId, id, e.target.value);
|
||||
updateFontSize(e.target.value.length);
|
||||
}}
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { Flex, Text } from "@theme-ui/components";
|
||||
import { ThemeUICSSObject } from "@theme-ui/core";
|
||||
import { Icon } from "../icons";
|
||||
import { Close, Icon } from "../icons";
|
||||
|
||||
type IconTagProps = {
|
||||
text: string;
|
||||
@@ -33,10 +33,20 @@ type IconTagProps = {
|
||||
};
|
||||
testId?: string;
|
||||
highlight?: boolean;
|
||||
onDismiss?: () => void;
|
||||
};
|
||||
|
||||
function IconTag(props: IconTagProps) {
|
||||
const { icon: Icon, text, title, onClick, styles, testId, highlight } = props;
|
||||
const {
|
||||
icon: Icon,
|
||||
text,
|
||||
title,
|
||||
onClick,
|
||||
onDismiss,
|
||||
styles,
|
||||
testId,
|
||||
highlight
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -92,6 +102,22 @@ function IconTag(props: IconTagProps) {
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
{onDismiss && (
|
||||
<Close
|
||||
size={12}
|
||||
title="Remove"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onDismiss();
|
||||
}}
|
||||
sx={{
|
||||
ml: 1,
|
||||
":hover": { bg: "background-error" },
|
||||
":hover path": { fill: "var(--icon-error) !important" }
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,10 +110,11 @@ function Properties(props) {
|
||||
"reminder"
|
||||
);
|
||||
const allNotebooks = useMemo(
|
||||
() => [
|
||||
...notebooks.map((ref) => db.notebooks.notebook(ref.id)?.data),
|
||||
...db.relations.to({ id: sessionId, type: "note" }, "notebook")
|
||||
],
|
||||
() =>
|
||||
[
|
||||
...notebooks.map((ref) => db.notebooks.notebook(ref.id)?.data),
|
||||
...db.relations.to({ id: sessionId, type: "note" }, "notebook")
|
||||
].filter(Boolean),
|
||||
[sessionId, notebooks]
|
||||
);
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ function StatusBar() {
|
||||
const isLoggedIn = useUserStore((state) => state.isLoggedIn);
|
||||
const statuses = useStatus();
|
||||
const updateStatus = useAutoUpdater();
|
||||
const isFocusMode = useAppStore((state) => state.isFocusMode);
|
||||
|
||||
return (
|
||||
<ScopedThemeProvider
|
||||
@@ -63,124 +64,130 @@ function StatusBar() {
|
||||
}}
|
||||
px={2}
|
||||
>
|
||||
<Flex>
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
{isFocusMode ? (
|
||||
<Flex />
|
||||
) : (
|
||||
<Flex>
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<Button
|
||||
onClick={() =>
|
||||
user?.isEmailConfirmed
|
||||
? navigate("/settings")
|
||||
: hashNavigate("/email/verify")
|
||||
}
|
||||
variant="statusitem"
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
<Circle
|
||||
size={7}
|
||||
color={
|
||||
user?.isEmailConfirmed
|
||||
? "var(--icon-success)"
|
||||
: "var(--icon-error)"
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
className="selectable"
|
||||
variant="subBody"
|
||||
ml={1}
|
||||
sx={{ color: "paragraph" }}
|
||||
>
|
||||
{user?.email}
|
||||
{user?.isEmailConfirmed ? "" : " (not verified)"}
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<SyncStatus />
|
||||
</>
|
||||
) : isLoggedIn === false ? (
|
||||
<Button
|
||||
onClick={() =>
|
||||
user?.isEmailConfirmed
|
||||
? navigate("/settings")
|
||||
: hashNavigate("/email/verify")
|
||||
}
|
||||
variant="statusitem"
|
||||
onClick={() => hardNavigate("/login")}
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
data-test-id="not-logged-in"
|
||||
>
|
||||
<Circle
|
||||
size={7}
|
||||
color={
|
||||
user?.isEmailConfirmed
|
||||
? "var(--icon-success)"
|
||||
: "var(--icon-error)"
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
className="selectable"
|
||||
variant="subBody"
|
||||
ml={1}
|
||||
sx={{ color: "paragraph" }}
|
||||
>
|
||||
{user?.email}
|
||||
{user?.isEmailConfirmed ? "" : " (not verified)"}
|
||||
<Circle size={7} color="var(--icon-error)" />
|
||||
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
|
||||
Not logged in
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<SyncStatus />
|
||||
</>
|
||||
) : isLoggedIn === false ? (
|
||||
) : null}
|
||||
<Button
|
||||
variant="statusitem"
|
||||
onClick={() => hardNavigate("/login")}
|
||||
onClick={() => showIssueDialog()}
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
data-test-id="not-logged-in"
|
||||
title="Facing an issue? Click here to create a bug report."
|
||||
>
|
||||
<Circle size={7} color="var(--icon-error)" />
|
||||
<Issue size={12} />
|
||||
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
|
||||
Not logged in
|
||||
Report an issue
|
||||
</Text>
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
variant="statusitem"
|
||||
onClick={() => showIssueDialog()}
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
title="Facing an issue? Click here to create a bug report."
|
||||
>
|
||||
<Issue size={12} />
|
||||
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
|
||||
Report an issue
|
||||
</Text>
|
||||
</Button>
|
||||
{statuses?.map((status) => {
|
||||
const { key, icon: Icon } = status;
|
||||
return (
|
||||
<Flex
|
||||
key={key}
|
||||
ml={1}
|
||||
sx={{ alignItems: "center", justifyContent: "center" }}
|
||||
{statuses?.map((status) => {
|
||||
const { key, icon: Icon } = status;
|
||||
return (
|
||||
<Flex
|
||||
key={key}
|
||||
ml={1}
|
||||
sx={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
{Icon ? <Icon size={12} /> : <Loading size={12} />}
|
||||
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
|
||||
{statusToString(status)}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
|
||||
{updateStatus && updateStatus.type !== "updated" && (
|
||||
<Button
|
||||
variant="statusitem"
|
||||
onClick={async () => {
|
||||
if (updateStatus.type === "available") {
|
||||
await showUpdateAvailableNotice(updateStatus);
|
||||
} else if (updateStatus.type === "completed") {
|
||||
installUpdate();
|
||||
} else {
|
||||
checkForUpdate();
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
ml: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
{Icon ? <Icon size={12} /> : <Loading size={12} />}
|
||||
<Update
|
||||
rotate={
|
||||
updateStatus.type !== "completed" &&
|
||||
updateStatus.type !== "available"
|
||||
}
|
||||
color={
|
||||
updateStatus.type === "available" ? "accent" : "paragraph"
|
||||
}
|
||||
size={12}
|
||||
/>
|
||||
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
|
||||
{statusToString(status)}
|
||||
{statusToInfoText(updateStatus)}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
|
||||
{updateStatus && updateStatus.type !== "updated" && (
|
||||
<Button
|
||||
variant="statusitem"
|
||||
onClick={async () => {
|
||||
if (updateStatus.type === "available") {
|
||||
await showUpdateAvailableNotice(updateStatus);
|
||||
} else if (updateStatus.type === "completed") {
|
||||
installUpdate();
|
||||
} else {
|
||||
checkForUpdate();
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
ml: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
<Update
|
||||
rotate={
|
||||
updateStatus.type !== "completed" &&
|
||||
updateStatus.type !== "available"
|
||||
}
|
||||
color={updateStatus.type === "available" ? "accent" : "paragraph"}
|
||||
size={12}
|
||||
/>
|
||||
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
|
||||
{statusToInfoText(updateStatus)}
|
||||
</Text>
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
<EditorFooter />
|
||||
</ScopedThemeProvider>
|
||||
);
|
||||
|
||||
@@ -59,6 +59,16 @@ export function BaseThemeProvider(
|
||||
|
||||
useEffect(() => {
|
||||
useThemeEngineStore.getState().setTheme(theme);
|
||||
|
||||
const themeColorElement = document.head.querySelector(
|
||||
"meta[name='theme-color']"
|
||||
);
|
||||
if (themeColorElement) {
|
||||
themeColorElement.setAttribute(
|
||||
"content",
|
||||
theme.scopes.base.primary.background
|
||||
);
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -89,7 +89,17 @@ const features: Record<FeatureKeys, Feature> = {
|
||||
)
|
||||
}
|
||||
]
|
||||
: [],
|
||||
: [
|
||||
{
|
||||
title: "Date time shortcuts in note title",
|
||||
subtitle:
|
||||
"You can now use date time shortcuts (/now, /date & /time) in note & task list titles."
|
||||
},
|
||||
{
|
||||
title: "Restore backups without an account",
|
||||
subtitle: "You can now restore backups without logging in."
|
||||
}
|
||||
],
|
||||
cta: {
|
||||
title: "Got it",
|
||||
icon: Checkmark,
|
||||
|
||||
@@ -105,7 +105,11 @@ function PasswordDialog(props) {
|
||||
<Label
|
||||
key={check.key}
|
||||
mt={2}
|
||||
sx={{ fontSize: "title", alignItems: "center" }}
|
||||
sx={{
|
||||
fontSize: "body",
|
||||
fontFamily: "body",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Checkbox id={check.key} name={check.key} />
|
||||
{check.title}
|
||||
|
||||
@@ -62,7 +62,6 @@ export const BackupExportSettings: SettingsGroup[] = [
|
||||
key: "restore-backup",
|
||||
title: "Restore backup",
|
||||
description: "Restore a backup file from your disk drive.",
|
||||
isHidden: () => !useUserStore.getState().isLoggedIn && !IS_TESTING,
|
||||
components: [
|
||||
{
|
||||
type: "button",
|
||||
|
||||
@@ -136,9 +136,10 @@ export const BehaviourSettings: SettingsGroup[] = [
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "updates",
|
||||
key: "desktop-app",
|
||||
section: "behaviour",
|
||||
header: "Updates",
|
||||
header: "Desktop app",
|
||||
isHidden: () => !IS_DESKTOP_APP,
|
||||
settings: [
|
||||
{
|
||||
key: "auto-updates",
|
||||
@@ -147,6 +148,7 @@ export const BehaviourSettings: SettingsGroup[] = [
|
||||
"Automatically download & install updates in the background without prompting first.",
|
||||
onStateChange: (listener) =>
|
||||
useSettingStore.subscribe((s) => s.autoUpdates, listener),
|
||||
isHidden: () => useSettingStore.getState().isFlatpak,
|
||||
components: [
|
||||
{
|
||||
type: "toggle",
|
||||
|
||||
@@ -72,6 +72,24 @@ export const AboutSettings: SettingsGroup[] = [
|
||||
];
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "source-code",
|
||||
title: "Source code",
|
||||
description: "All the source code for Notesnook is available & open for everyone on GitHub.",
|
||||
components: [
|
||||
{
|
||||
type: "button",
|
||||
action: () => {
|
||||
window.open(
|
||||
"https://github.com/streetwriters/notesnook",
|
||||
"_blank"
|
||||
);
|
||||
},
|
||||
title: "View source code",
|
||||
variant: "secondary"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "roadmap",
|
||||
title: "Roadmap",
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { SettingsGroup } from "./types";
|
||||
import { useStore as useSettingStore } from "../../stores/setting-store";
|
||||
import { useStore as useUserStore } from "../../stores/user-store";
|
||||
import { getPlatform, isDesktop } from "../../utils/platform";
|
||||
import { getPlatform } from "../../utils/platform";
|
||||
import { db } from "../../common/db";
|
||||
import { showPromptDialog } from "../../common/dialog-controller";
|
||||
import Config from "../../utils/config";
|
||||
@@ -74,6 +74,20 @@ What data is collected & when?`,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "hide-note-title",
|
||||
title: "Hide note title",
|
||||
description: "Prevent note title from appearing in tab/window title.",
|
||||
onStateChange: (listener) =>
|
||||
useSettingStore.subscribe((s) => s.hideNoteTitle, listener),
|
||||
components: [
|
||||
{
|
||||
type: "toggle",
|
||||
isToggled: () => useSettingStore.getState().hideNoteTitle,
|
||||
toggle: () => useSettingStore.getState().toggleHideTitle()
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "privacy-mode",
|
||||
title: "Privacy mode",
|
||||
|
||||
@@ -58,6 +58,8 @@ export const VaultSettings: SettingsGroup[] = [
|
||||
title: "Change vault password",
|
||||
description: "Set a new password for your vault",
|
||||
isHidden: () => !useAppStore.getState().isVaultCreated,
|
||||
onStateChange: (listener) =>
|
||||
useAppStore.subscribe((s) => s.isVaultCreated, listener),
|
||||
components: [
|
||||
{
|
||||
type: "button",
|
||||
@@ -72,6 +74,8 @@ export const VaultSettings: SettingsGroup[] = [
|
||||
title: "Clear vault",
|
||||
description: "Unlock all locked notes and clear vault.",
|
||||
isHidden: () => !useAppStore.getState().isVaultCreated,
|
||||
onStateChange: (listener) =>
|
||||
useAppStore.subscribe((s) => s.isVaultCreated, listener),
|
||||
components: [
|
||||
{
|
||||
type: "button",
|
||||
@@ -91,6 +95,8 @@ export const VaultSettings: SettingsGroup[] = [
|
||||
title: "Delete vault",
|
||||
description: "Delete vault including all locked notes.",
|
||||
isHidden: () => !useAppStore.getState().isVaultCreated,
|
||||
onStateChange: (listener) =>
|
||||
useAppStore.subscribe((s) => s.isVaultCreated, listener),
|
||||
components: [
|
||||
{
|
||||
type: "button",
|
||||
|
||||
@@ -3,16 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAHUUExURUxpcf////////////////////////////////////////////////////////////////////////////////////39/f39/f////////////////////////////////z8/O3s7P7+/vTz8wAAAPz7++/v7/Py8u/u7vDv7+7t7dfX1/79/ezr66Kiovv6+vf29vX09OTj4w0NDfv7++Ti4vr6+vT09Ovq6vn5+f39/fb19dnX1/b29tvb29/f3xMTE/j4+Pn4+O7s7MnJyerq6uno6M/Pz8jIyN3d3dnZ2d7e3kZGRvj39/38/Pf39+zs7PDw8OPi4ubl5cvLy+rp6czMzPLy8vPz887Ozujo6MrKyuDf3+fn59PT0+bm5uXl5c3NzfX19evr6wsLC+/t7ePj4/Ty8u3r6/Lx8eXk5OPh4dDQ0Nza2tjX19vZ2fHw8Nva2tLR0djW1tbW1tra2tLS0uHh4evp6eLi4tXV1djY2L+/vysrK7q6utHR0aGhoa6urrm5uQICAigoKCEhIZCQkFxcXBgYGFRUVBISEm1tbQ4ODklJSbu7uyAgID09PZqamkVFRXh4eLGxsTg4OOTk5FFRUQEBAQoKCnx8fBcXF7/+rP8AAAAfdFJOUwBOqO2h6KT9Mtyr5+w3Str+6TTZOjXb31M24O8BOd0PapSsAAABxElEQVQ4y2NgYOBgYWWXxwLYBdmEGICAjx9Z1NJJVQfB42QE6hdG0RaoaaCqhKSCl4EF1VxNVAXyTAzcaArUUBXwMLBjKDBBViDAII+uwBZFgTy6AjU1awIKNKz1aa0g1VBfB58CDS08CuIb40AKzLAqsJKXN2tXacChwKS+rTtB3tJHRU9ewxmbFZ6tKiqTrSAKdIO1LDEUaPeoqMzsUwAp0NIt8Uj0ijJXQFVgnDJVZcK0XrACXcXY0sjoHM8AfyUkBbOn9KvMmgRSYFEW4hUVqJagHeaShqTAwX7ijJQusAkW5VXRHu6eruFqaigKkqarqAAVKFbYxWiYq1XGedijuMHBWz6xo7m2Rl7RwtvdJaw6NjzCFl2BVVNdshJQgbqBsqtdpIu7nasjXIFXS2cyRL2CW6i6JTCgFCLitUPs4QrM7cDmKZi7FRYVm6o5QeLbDGICLNEqJAVk59rk2djY5Me4FThawQOKFSzrrxmaZWpqBAamQUGmwRaakNTNzMAm7+ekrKmlqOjsrIgAGcAA1ci0NVPiYhAV8zVURgbp6nBg7isuwsDAKKfkp4AEVOHASklKApS9JZmY5bECaS5ZGQYA3vqpy6NoYh0AAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII="
|
||||
/>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<!-- <link rel="manifest" href="/site.webmanifest" /> -->
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#01c352" />
|
||||
<meta name="msapplication-TileColor" content="#01c352" />
|
||||
<meta name="theme-color" content="#01c352" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
@@ -24,6 +24,7 @@ import { loadDatabase } from "./hooks/use-database";
|
||||
import { AppEventManager, AppEvents } from "./common/app-events";
|
||||
import { BaseThemeProvider } from "./components/theme-provider";
|
||||
import { register } from "./utils/stream-saver/mitm";
|
||||
import { getServiceWorkerVersion } from "./utils/version";
|
||||
|
||||
renderApp();
|
||||
|
||||
@@ -60,7 +61,6 @@ async function initializeServiceWorker() {
|
||||
serviceWorker.register({
|
||||
onUpdate: async (registration: ServiceWorkerRegistration) => {
|
||||
if (!registration.waiting) return;
|
||||
const { getServiceWorkerVersion } = await import("./utils/version");
|
||||
const { formatted } = await getServiceWorkerVersion(
|
||||
registration.waiting
|
||||
);
|
||||
|
||||
161
apps/web/src/interfaces/file-store.ts
Normal file
161
apps/web/src/interfaces/file-store.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
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 { IFileStorage } from "@notesnook/streamable-fs/dist/src/interfaces";
|
||||
import { File } from "@notesnook/streamable-fs/dist/src/types";
|
||||
import { IndexedDBKVStore } from "./key-value";
|
||||
import OriginPrivateFileStoreWorker from "./opfs.worker?worker";
|
||||
import { OriginPrivateFileStoreWorkerType } from "./opfs.worker";
|
||||
import { transfer, wrap } from "comlink";
|
||||
|
||||
export class IndexedDBFileStore implements IFileStorage {
|
||||
storage: IndexedDBKVStore;
|
||||
constructor(name: string) {
|
||||
this.storage = new IndexedDBKVStore(name, name);
|
||||
}
|
||||
|
||||
clear(): Promise<void> {
|
||||
return this.storage.clear();
|
||||
}
|
||||
setMetadata(filename: string, metadata: File): Promise<void> {
|
||||
return this.storage.set(filename, metadata);
|
||||
}
|
||||
getMetadata(filename: string): Promise<File | undefined> {
|
||||
return this.storage.get(filename);
|
||||
}
|
||||
deleteMetadata(filename: string): Promise<void> {
|
||||
return this.storage.delete(filename);
|
||||
}
|
||||
writeChunk(chunkName: string, data: Uint8Array): Promise<void> {
|
||||
return this.storage.set(chunkName, data);
|
||||
}
|
||||
deleteChunk(chunkName: string): Promise<void> {
|
||||
return this.storage.delete(chunkName);
|
||||
}
|
||||
readChunk(chunkName: string): Promise<Uint8Array | undefined> {
|
||||
return this.storage.get(chunkName);
|
||||
}
|
||||
}
|
||||
|
||||
export class CacheStorageFileStore implements IFileStorage {
|
||||
storage: IndexedDBKVStore;
|
||||
constructor(private readonly name: string) {
|
||||
this.storage = new IndexedDBKVStore(name, name);
|
||||
console.log("USING CACHE FILE STORE!");
|
||||
}
|
||||
|
||||
private getCache() {
|
||||
return window.caches.open(this.name);
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
const cache = await this.getCache();
|
||||
for (const req of await cache.keys()) {
|
||||
await cache.delete(req);
|
||||
}
|
||||
return this.storage.clear();
|
||||
}
|
||||
|
||||
setMetadata(filename: string, metadata: File): Promise<void> {
|
||||
return this.storage.set(filename, metadata);
|
||||
}
|
||||
|
||||
getMetadata(filename: string): Promise<File | undefined> {
|
||||
return this.storage.get(filename);
|
||||
}
|
||||
|
||||
deleteMetadata(filename: string): Promise<void> {
|
||||
return this.storage.delete(filename);
|
||||
}
|
||||
|
||||
async writeChunk(chunkName: string, data: Uint8Array): Promise<void> {
|
||||
const cache = await this.getCache();
|
||||
await cache.put(
|
||||
this.toURL(chunkName),
|
||||
new Response(data, {
|
||||
headers: new Headers({
|
||||
"Content-Length": data.length.toString(),
|
||||
"Content-Type": "application/encrypted-octet-stream"
|
||||
})
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async deleteChunk(chunkName: string): Promise<void> {
|
||||
const cache = await this.getCache();
|
||||
await cache.delete(this.toURL(chunkName));
|
||||
}
|
||||
|
||||
async readChunk(chunkName: string): Promise<Uint8Array | undefined> {
|
||||
const cache = await this.getCache();
|
||||
const response = await cache.match(this.toURL(chunkName));
|
||||
return response ? new Uint8Array(await response.arrayBuffer()) : undefined;
|
||||
}
|
||||
|
||||
private toURL(chunkName: string) {
|
||||
return `/${chunkName}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class OriginPrivateFileSystem implements IFileStorage {
|
||||
private readonly worker = wrap<OriginPrivateFileStoreWorkerType>(
|
||||
new OriginPrivateFileStoreWorker()
|
||||
);
|
||||
private created = false;
|
||||
constructor(private readonly name: string) {
|
||||
console.log("using origin private file store");
|
||||
}
|
||||
private async create() {
|
||||
if (this.created) return;
|
||||
await this.worker.create(this.name, this.name);
|
||||
this.created = true;
|
||||
}
|
||||
async clear(): Promise<void> {
|
||||
await this.create();
|
||||
await this.worker.clear(this.name);
|
||||
}
|
||||
async setMetadata(filename: string, metadata: File): Promise<void> {
|
||||
await this.create();
|
||||
await this.worker.setMetadata(this.name, filename, metadata);
|
||||
}
|
||||
async getMetadata(filename: string): Promise<File | undefined> {
|
||||
await this.create();
|
||||
return this.worker.getMetadata(this.name, filename);
|
||||
}
|
||||
async deleteMetadata(filename: string): Promise<void> {
|
||||
await this.create();
|
||||
return this.worker.deleteMetadata(this.name, filename);
|
||||
}
|
||||
async writeChunk(chunkName: string, data: Uint8Array): Promise<void> {
|
||||
await this.create();
|
||||
return this.worker.writeChunk(
|
||||
this.name,
|
||||
chunkName,
|
||||
transfer(data.buffer, [data.buffer])
|
||||
);
|
||||
}
|
||||
async deleteChunk(chunkName: string): Promise<void> {
|
||||
await this.create();
|
||||
return this.worker.deleteChunk(this.name, chunkName);
|
||||
}
|
||||
async readChunk(chunkName: string): Promise<Uint8Array | undefined> {
|
||||
await this.create();
|
||||
return this.worker.readChunk(this.name, chunkName);
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,12 @@ import { Cipher, DataFormat, SerializedKey } from "@notesnook/crypto";
|
||||
import { IDataType } from "hash-wasm/dist/lib/util";
|
||||
import { IndexedDBKVStore } from "./key-value";
|
||||
import FileHandle from "@notesnook/streamable-fs/dist/src/filehandle";
|
||||
import {
|
||||
CacheStorageFileStore,
|
||||
IndexedDBFileStore,
|
||||
OriginPrivateFileSystem
|
||||
} from "./file-store";
|
||||
import { isFeatureSupported } from "../utils/feature-check";
|
||||
|
||||
const ABYTES = 17;
|
||||
const CHUNK_SIZE = 512 * 1024;
|
||||
@@ -45,7 +51,13 @@ const UPLOAD_PART_REQUIRED_CHUNKS = Math.ceil(
|
||||
(5 * 1024 * 1024) / ENCRYPTED_CHUNK_SIZE
|
||||
);
|
||||
const MINIMUM_MULTIPART_FILE_SIZE = 25 * 1024 * 1024;
|
||||
const streamablefs = new StreamableFS("streamable-fs");
|
||||
const streamablefs = new StreamableFS(
|
||||
isFeatureSupported("opfs")
|
||||
? new OriginPrivateFileSystem("streamable-fs")
|
||||
: isFeatureSupported("cache")
|
||||
? new CacheStorageFileStore("streamable-fs")
|
||||
: new IndexedDBFileStore("streamable-fs")
|
||||
);
|
||||
|
||||
async function writeEncryptedFile(
|
||||
file: File,
|
||||
@@ -69,7 +81,12 @@ async function writeEncryptedFile(
|
||||
const { iv, stream } = await NNCrypto.createEncryptionStream(key);
|
||||
await file
|
||||
.stream()
|
||||
.pipeThrough(new ChunkedStream(CHUNK_SIZE))
|
||||
.pipeThrough(
|
||||
new ChunkedStream(
|
||||
CHUNK_SIZE,
|
||||
isFeatureSupported("opfs") ? "copy" : "nocopy"
|
||||
)
|
||||
)
|
||||
.pipeThrough(new IntoChunks(file.size))
|
||||
.pipeThrough(stream)
|
||||
.pipeThrough(
|
||||
@@ -500,11 +517,17 @@ async function downloadFile(filename: string, requestOptions: RequestOptions) {
|
||||
);
|
||||
})
|
||||
)
|
||||
.pipeThrough(new ChunkedStream(chunkSize + ABYTES))
|
||||
.pipeThrough(
|
||||
new ChunkedStream(
|
||||
chunkSize + ABYTES,
|
||||
isFeatureSupported("opfs") ? "copy" : "nocopy"
|
||||
)
|
||||
)
|
||||
.pipeTo(fileHandle.writeable);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showError(toS3Error(e), "Could not download file");
|
||||
reportProgress(undefined, { type: "download", hash: filename });
|
||||
return false;
|
||||
@@ -542,8 +565,6 @@ export async function decryptFile(
|
||||
}
|
||||
|
||||
async function saveFile(filename: string, fileMetadata: FileMetadata) {
|
||||
if (!fileMetadata) return false;
|
||||
|
||||
const { name, type, isUploaded } = fileMetadata;
|
||||
|
||||
const decrypted = await decryptFile(filename, fileMetadata);
|
||||
|
||||
@@ -21,4 +21,7 @@ import { INNCrypto } from "@notesnook/crypto";
|
||||
import CryptoWorker from "./nncrypto.worker?worker";
|
||||
import { wrap } from "comlink";
|
||||
|
||||
export const NNCrypto = wrap<INNCrypto>(new CryptoWorker()) as INNCrypto;
|
||||
export const NNCrypto =
|
||||
IS_DESKTOP_APP && window.NativeNNCrypto
|
||||
? new window.NativeNNCrypto()
|
||||
: (wrap<INNCrypto>(new CryptoWorker()) as INNCrypto);
|
||||
|
||||
119
apps/web/src/interfaces/opfs.worker.ts
Normal file
119
apps/web/src/interfaces/opfs.worker.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
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 { IFileStorage } from "@notesnook/streamable-fs/dist/src/interfaces";
|
||||
import { File } from "@notesnook/streamable-fs/dist/src/types";
|
||||
import { IndexedDBKVStore } from "./key-value";
|
||||
import { expose, transfer } from "comlink";
|
||||
|
||||
class OriginPrivateFileStore implements IFileStorage {
|
||||
private storage: IndexedDBKVStore;
|
||||
constructor(
|
||||
name: string,
|
||||
private readonly directory: FileSystemDirectoryHandle
|
||||
) {
|
||||
this.storage = new IndexedDBKVStore(name, name);
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
for await (const [name] of this.directory) {
|
||||
await this.directory.removeEntry(name, { recursive: true });
|
||||
}
|
||||
await this.storage.clear();
|
||||
}
|
||||
setMetadata(filename: string, metadata: File): Promise<void> {
|
||||
return this.storage.set(filename, metadata);
|
||||
}
|
||||
getMetadata(filename: string): Promise<File | undefined> {
|
||||
return this.storage.get(filename);
|
||||
}
|
||||
deleteMetadata(filename: string): Promise<void> {
|
||||
return this.storage.delete(filename);
|
||||
}
|
||||
async writeChunk(chunkName: string, data: Uint8Array): Promise<void> {
|
||||
const file = await this.directory.getFileHandle(chunkName, {
|
||||
create: true
|
||||
});
|
||||
const syncHandle = await file.createSyncAccessHandle();
|
||||
syncHandle.write(data);
|
||||
syncHandle.close();
|
||||
}
|
||||
async deleteChunk(chunkName: string) {
|
||||
try {
|
||||
await this.directory.removeEntry(chunkName);
|
||||
} catch (e) {
|
||||
console.error("Failed to delete chunk", e);
|
||||
}
|
||||
}
|
||||
async readChunk(chunkName: string): Promise<Uint8Array | undefined> {
|
||||
try {
|
||||
const file = await this.directory.getFileHandle(chunkName);
|
||||
const syncHandle = await file.createSyncAccessHandle();
|
||||
const buffer = new Uint8Array(syncHandle.getSize());
|
||||
syncHandle.read(buffer);
|
||||
syncHandle.close();
|
||||
return buffer;
|
||||
} catch (e) {
|
||||
console.error("Failed to read chunk", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fileStores: Map<string, OriginPrivateFileStore> = new Map();
|
||||
const workerModule = {
|
||||
async create(name: string, directoryName: string) {
|
||||
const root = await navigator.storage.getDirectory();
|
||||
const directoryHandle = await root.getDirectoryHandle(directoryName, {
|
||||
create: true
|
||||
});
|
||||
fileStores.set(
|
||||
directoryName,
|
||||
new OriginPrivateFileStore(name, directoryHandle)
|
||||
);
|
||||
},
|
||||
clear(directoryName: string) {
|
||||
return fileStores.get(directoryName)?.clear();
|
||||
},
|
||||
setMetadata(directoryName: string, filename: string, metadata: File) {
|
||||
return fileStores.get(directoryName)?.setMetadata(filename, metadata);
|
||||
},
|
||||
getMetadata(directoryName: string, filename: string) {
|
||||
return fileStores.get(directoryName)?.getMetadata(filename);
|
||||
},
|
||||
deleteMetadata(directoryName: string, filename: string) {
|
||||
return fileStores.get(directoryName)?.deleteMetadata(filename);
|
||||
},
|
||||
writeChunk(directoryName: string, chunkName: string, data: ArrayBuffer) {
|
||||
return fileStores
|
||||
.get(directoryName)
|
||||
?.writeChunk(chunkName, new Uint8Array(data));
|
||||
},
|
||||
deleteChunk(directoryName: string, chunkName: string) {
|
||||
return fileStores.get(directoryName)?.deleteChunk(chunkName);
|
||||
},
|
||||
async readChunk(directoryName: string, chunkName: string) {
|
||||
const chunk = await fileStores.get(directoryName)?.readChunk(chunkName);
|
||||
return chunk ? transfer(chunk, [chunk.buffer]) : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
expose(workerModule);
|
||||
|
||||
export type OriginPrivateFileStoreWorkerType = typeof workerModule;
|
||||
@@ -22,6 +22,7 @@ import { store as noteStore } from "./note-store";
|
||||
import { store as attachmentStore } from "./attachment-store";
|
||||
import { store as appStore } from "./app-store";
|
||||
import { store as tagStore } from "./tag-store";
|
||||
import { store as settingStore } from "./setting-store";
|
||||
import { db } from "../common/db";
|
||||
import BaseStore from ".";
|
||||
import { EV, EVENTS } from "@notesnook/core/dist/common";
|
||||
@@ -151,7 +152,7 @@ class EditorStore extends BaseStore {
|
||||
if (!note) return;
|
||||
|
||||
noteStore.setSelectedNote(note.id);
|
||||
setDocumentTitle(note.title);
|
||||
setDocumentTitle(settingStore.get().hideNoteTitle ? undefined : note.title);
|
||||
|
||||
if (note.locked)
|
||||
return hashNavigate(`/notes/${noteId}/unlock`, { replace: true });
|
||||
@@ -247,7 +248,9 @@ class EditorStore extends BaseStore {
|
||||
state.session.dateEdited = note.dateEdited;
|
||||
state.session.attachmentsLength = attachments.length;
|
||||
});
|
||||
setDocumentTitle(note.title);
|
||||
setDocumentTitle(
|
||||
settingStore.get().hideNoteTitle ? undefined : note.title
|
||||
);
|
||||
|
||||
this.setSaveState(1);
|
||||
} catch (err) {
|
||||
|
||||
@@ -23,7 +23,9 @@ import { desktop } from "../common/desktop-bridge";
|
||||
import createStore from "../common/store";
|
||||
import Config from "../utils/config";
|
||||
import BaseStore from "./index";
|
||||
import { store as editorStore } from "./editor-store";
|
||||
import { isTelemetryEnabled, setTelemetry } from "../utils/telemetry";
|
||||
import { setDocumentTitle } from "../utils/dom";
|
||||
|
||||
/**
|
||||
* @extends {BaseStore<SettingStore>}
|
||||
@@ -40,6 +42,7 @@ class SettingStore extends BaseStore {
|
||||
|
||||
zoomFactor = 1.0;
|
||||
privacyMode = false;
|
||||
hideNoteTitle = Config.get("hideNoteTitle", false);
|
||||
telemetry = isTelemetryEnabled();
|
||||
/** @type {string} */
|
||||
dateFormat = null;
|
||||
@@ -54,6 +57,7 @@ class SettingStore extends BaseStore {
|
||||
*/
|
||||
desktopIntegrationSettings = undefined;
|
||||
autoUpdates = true;
|
||||
isFlatpak = false;
|
||||
|
||||
refresh = async () => {
|
||||
this.set({
|
||||
@@ -61,6 +65,7 @@ class SettingStore extends BaseStore {
|
||||
timeFormat: db.settings.getTimeFormat(),
|
||||
titleFormat: db.settings.getTitleFormat(),
|
||||
trashCleanupInterval: db.settings.getTrashCleanupInterval(),
|
||||
isFlatpak: await desktop?.integration.isFlatpak.query(),
|
||||
desktopIntegrationSettings:
|
||||
await desktop?.integration.desktopIntegration.query(),
|
||||
privacyMode: await desktop?.integration.privacyMode.query(),
|
||||
@@ -163,6 +168,15 @@ class SettingStore extends BaseStore {
|
||||
await desktop?.integration.setPrivacyMode.mutate({ enabled: !privacyMode });
|
||||
};
|
||||
|
||||
toggleHideTitle = async () => {
|
||||
const { hideNoteTitle } = this.get();
|
||||
this.set({ hideNoteTitle: !hideNoteTitle });
|
||||
Config.set("hideNoteTitle", !hideNoteTitle);
|
||||
setDocumentTitle(
|
||||
!hideNoteTitle ? undefined : editorStore.get().session.title
|
||||
);
|
||||
};
|
||||
|
||||
toggleAutoUpdates = async () => {
|
||||
const autoUpdates = this.get().autoUpdates;
|
||||
this.set({ autoUpdates: !autoUpdates });
|
||||
|
||||
@@ -112,7 +112,7 @@ class UserStore extends BaseStore {
|
||||
});
|
||||
};
|
||||
|
||||
login = async (form, skipInit = false) => {
|
||||
login = async (form, skipInit = false, sessionExpired = false) => {
|
||||
this.set((state) => (state.isLoggingIn = true));
|
||||
const { email, password, code, method } = form;
|
||||
|
||||
@@ -120,7 +120,12 @@ class UserStore extends BaseStore {
|
||||
if (code) {
|
||||
return await db.user.authenticateMultiFactorCode(code, method);
|
||||
} else if (password) {
|
||||
await db.user.authenticatePassword(email, password, null);
|
||||
await db.user.authenticatePassword(
|
||||
email,
|
||||
password,
|
||||
null,
|
||||
sessionExpired
|
||||
);
|
||||
Config.set("encryptBackups", true);
|
||||
|
||||
if (skipInit) return true;
|
||||
|
||||
60
apps/web/src/utils/feature-check.ts
Normal file
60
apps/web/src/utils/feature-check.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
const FEATURE_CHECKS = {
|
||||
opfs: false,
|
||||
cache: false
|
||||
};
|
||||
|
||||
async function isOPFSSupported() {
|
||||
const hasGetDirectory =
|
||||
"getDirectory" in window.navigator.storage &&
|
||||
typeof window.navigator.storage.getDirectory === "function";
|
||||
return (
|
||||
hasGetDirectory &&
|
||||
window.navigator.storage
|
||||
.getDirectory()
|
||||
.then(() => (FEATURE_CHECKS.opfs = true))
|
||||
.catch(() => (FEATURE_CHECKS.opfs = false))
|
||||
);
|
||||
}
|
||||
|
||||
async function isCacheSupported() {
|
||||
const hasCacheStorage =
|
||||
"CacheStorage" in window &&
|
||||
"caches" in window &&
|
||||
window.caches instanceof CacheStorage;
|
||||
return (
|
||||
hasCacheStorage &&
|
||||
window.caches
|
||||
.has("something")
|
||||
.then((f) => (FEATURE_CHECKS.cache = true))
|
||||
.catch((a) => (FEATURE_CHECKS.cache = false))
|
||||
);
|
||||
}
|
||||
|
||||
export async function initializeFeatureChecks() {
|
||||
await Promise.allSettled([isOPFSSupported(), isCacheSupported()]);
|
||||
}
|
||||
|
||||
function isFeatureSupported(key: keyof typeof FEATURE_CHECKS) {
|
||||
return FEATURE_CHECKS[key];
|
||||
}
|
||||
|
||||
export { isFeatureSupported };
|
||||
@@ -18,8 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { db } from "../common/db";
|
||||
import FS from "../interfaces/fs";
|
||||
|
||||
import type { Note, Notebook } from "@notesnook-importer/core";
|
||||
import {
|
||||
ATTACHMENTS_DIRECTORY_NAME,
|
||||
@@ -30,6 +28,7 @@ import { Reader, Entry } from "./zip-reader";
|
||||
import { path } from "@notesnook-importer/core/dist/src/utils/path";
|
||||
|
||||
export async function* importFiles(zipFiles: File[]) {
|
||||
const { default: FS } = await import("../interfaces/fs");
|
||||
for (const zip of zipFiles) {
|
||||
let count = 0;
|
||||
let filesRead = 0;
|
||||
|
||||
26
apps/web/src/utils/lazify.ts
Normal file
26
apps/web/src/utils/lazify.ts
Normal 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/>.
|
||||
*/
|
||||
|
||||
export async function lazify<T, R>(
|
||||
loader: Promise<T>,
|
||||
action: (module: T) => R | Promise<R>
|
||||
): Promise<R> {
|
||||
const module = await loader;
|
||||
return await action(module);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import { expose } from "comlink";
|
||||
|
||||
const module = {
|
||||
async waitForInternet() {
|
||||
let retries = 10;
|
||||
let retries = 3;
|
||||
while (retries-- > 0) {
|
||||
try {
|
||||
const response = await fetch("https://api.notesnook.com/health");
|
||||
|
||||
@@ -129,5 +129,5 @@ export function isMac() {
|
||||
}
|
||||
|
||||
export function isMacStoreApp() {
|
||||
return window.os && window.os() === "mas";
|
||||
return window.os ? window.os() === "mas" : false;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ 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 { decryptFile } from "../../interfaces/fs";
|
||||
import { db } from "../../common/db";
|
||||
import { lazify } from "../lazify";
|
||||
import { ZipFile } from "./zip-stream";
|
||||
|
||||
export const METADATA_FILENAME = "metadata.json";
|
||||
@@ -29,12 +29,13 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
|
||||
signal?: AbortSignal,
|
||||
onProgress?: (current: number) => void
|
||||
) {
|
||||
let index = 0;
|
||||
const counters: Record<string, number> = {};
|
||||
if (signal)
|
||||
signal.onabort = async () => {
|
||||
await db.fs?.cancel(GROUP_ID, "download");
|
||||
};
|
||||
|
||||
let index = 0;
|
||||
super({
|
||||
start() {},
|
||||
async pull(controller) {
|
||||
@@ -54,18 +55,22 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
|
||||
);
|
||||
|
||||
const key = await db.attachments?.decryptKey(attachment.key);
|
||||
const file = await decryptFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.metadata.filename,
|
||||
type: attachment.metadata.type,
|
||||
isUploaded: !!attachment.dateUploaded
|
||||
});
|
||||
const file = await lazify(
|
||||
import("../../interfaces/fs"),
|
||||
({ decryptFile }) =>
|
||||
decryptFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.metadata.filename,
|
||||
type: attachment.metadata.type,
|
||||
isUploaded: !!attachment.dateUploaded
|
||||
})
|
||||
);
|
||||
|
||||
if (file) {
|
||||
const filePath = attachment.metadata.filename;
|
||||
const filePath: string = attachment.metadata.filename;
|
||||
controller.enqueue({
|
||||
path: filePath,
|
||||
path: makeUniqueFilename(filePath, counters),
|
||||
data: new Uint8Array(await file.arrayBuffer())
|
||||
});
|
||||
} else {
|
||||
@@ -79,3 +84,17 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function makeUniqueFilename(
|
||||
filePath: string,
|
||||
counters: Record<string, number>
|
||||
) {
|
||||
filePath = filePath.toLowerCase();
|
||||
counters[filePath] = (counters[filePath] || 0) + 1;
|
||||
if (counters[filePath] === 1) return filePath;
|
||||
|
||||
const parts = filePath.split(".");
|
||||
return `${parts.slice(0, -1).join(".")}-${counters[filePath]}.${
|
||||
parts[parts.length - 1]
|
||||
}`;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { Chunk } from "@notesnook/crypto";
|
||||
|
||||
export class ChunkedStream extends TransformStream<Uint8Array, Uint8Array> {
|
||||
constructor(chunkSize: number) {
|
||||
constructor(chunkSize: number, mode: "nocopy" | "copy") {
|
||||
let backBuffer: Uint8Array | null = null;
|
||||
super({
|
||||
start() {},
|
||||
@@ -38,7 +38,13 @@ export class ChunkedStream extends TransformStream<Uint8Array, Uint8Array> {
|
||||
const start = backBuffer.length - remainingBytes;
|
||||
const end = start + chunkSize;
|
||||
|
||||
controller.enqueue(backBuffer.subarray(start, end));
|
||||
// TODO: find a way to support sending the chunked
|
||||
// buffer to web workers without copying.
|
||||
controller.enqueue(
|
||||
mode === "copy"
|
||||
? new Uint8Array(backBuffer.buffer.slice(start, end))
|
||||
: backBuffer.subarray(start, end)
|
||||
);
|
||||
remainingBytes -= chunkSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,23 +17,25 @@ 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 { Unzipped } from "fflate";
|
||||
import { AsyncZippable, zip as zipAsync } from "fflate";
|
||||
import { sanitizeFilename } from "@notesnook/common";
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
type File = { filename: string; content: string };
|
||||
type File = { filename: string; content: string; date: number };
|
||||
async function zip(files: File[], format: string): Promise<Uint8Array> {
|
||||
const obj: Unzipped = Object.create(null);
|
||||
const obj: AsyncZippable = Object.create(null);
|
||||
files.forEach((file) => {
|
||||
const name = sanitizeFilename(file.filename, { replacement: "-" });
|
||||
let counter = 0;
|
||||
while (obj[makeFilename(name, format, counter)]) ++counter;
|
||||
|
||||
obj[makeFilename(name, format, counter)] = textEncoder.encode(file.content);
|
||||
obj[makeFilename(name, format, counter)] = [
|
||||
textEncoder.encode(file.content),
|
||||
{ mtime: file.date }
|
||||
];
|
||||
});
|
||||
const { zip } = await import("fflate");
|
||||
return new Promise((resolve, reject) =>
|
||||
zip(obj, (err, data) => (err ? reject(err) : resolve(data)))
|
||||
zipAsync(obj, (err, data) => (err ? reject(err) : resolve(data)))
|
||||
);
|
||||
}
|
||||
export { zip };
|
||||
|
||||
@@ -278,10 +278,14 @@ function LoginPassword(props: BaseAuthComponentProps<"login:password">) {
|
||||
subtitle: "Please wait while you are authenticated."
|
||||
}}
|
||||
onSubmit={async (form) => {
|
||||
await userstore.login({
|
||||
password: form.password,
|
||||
email: formData.email
|
||||
});
|
||||
await userstore.login(
|
||||
{
|
||||
password: form.password,
|
||||
email: formData.email
|
||||
},
|
||||
false,
|
||||
Config.get("sessionExpired", false)
|
||||
);
|
||||
Config.set("sessionExpired", false);
|
||||
openURL("/");
|
||||
}}
|
||||
|
||||
@@ -329,23 +329,14 @@ function RecoveryMethods(props: BaseRecoveryComponentProps<"methods">) {
|
||||
|
||||
function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
|
||||
const { navigate } = props;
|
||||
const [progress, setProgress] = useState("0");
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
db.eventManager.subscribe(
|
||||
EVENTS.syncProgress,
|
||||
({
|
||||
type,
|
||||
total,
|
||||
current
|
||||
}: {
|
||||
type: string;
|
||||
total: number;
|
||||
current: number;
|
||||
}) => {
|
||||
if (total === current) return;
|
||||
({ type, current }: { type: string; current: number }) => {
|
||||
if (type === "download") {
|
||||
setProgress(((current / total) * 100).toFixed());
|
||||
setProgress(current);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -358,11 +349,11 @@ function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
|
||||
title="Recover your account"
|
||||
subtitle={"Use a data recovery key to reset your account password."}
|
||||
loading={{
|
||||
title: `Downloading your data (${progress}%)`,
|
||||
title: `Downloading your data (${progress})`,
|
||||
subtitle: "Please wait while your data is downloaded & decrypted."
|
||||
}}
|
||||
onSubmit={async (form) => {
|
||||
setProgress("0");
|
||||
setProgress(0);
|
||||
|
||||
const user = await db.user?.getUser();
|
||||
if (!user) throw new Error("User not authenticated");
|
||||
@@ -479,14 +470,13 @@ function BackupData(props: BaseRecoveryComponentProps<"backup">) {
|
||||
|
||||
function NewPassword(props: BaseRecoveryComponentProps<"new">) {
|
||||
const { navigate, formData } = props;
|
||||
const [progress, setProgress] = useState("0");
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
db.eventManager.subscribe(
|
||||
EVENTS.syncProgress,
|
||||
({ total, current }: { total: number; current: number }) => {
|
||||
if (total === current) return;
|
||||
setProgress(((current / total) * 100).toFixed());
|
||||
({ current }: { current: number }) => {
|
||||
setProgress(current);
|
||||
}
|
||||
);
|
||||
}, []);
|
||||
@@ -500,11 +490,11 @@ function NewPassword(props: BaseRecoveryComponentProps<"new">) {
|
||||
"Notesnook is E2E encrypted — your password never leaves this device."
|
||||
}
|
||||
loading={{
|
||||
title: `Resetting account password (${progress}%)`,
|
||||
title: `Resetting account password (${progress})`,
|
||||
subtitle: "Please wait while we reset your account password."
|
||||
}}
|
||||
onSubmit={async (form) => {
|
||||
setProgress("0");
|
||||
setProgress(0);
|
||||
|
||||
if (form.password !== form.confirmPassword)
|
||||
throw new Error("Passwords do not match.");
|
||||
|
||||
8
extensions/web-clipper/package-lock.json
generated
8
extensions/web-clipper/package-lock.json
generated
@@ -89,7 +89,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.27.1",
|
||||
"slugify": "^1.6.5"
|
||||
"slugify": "^1.6.5",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
},
|
||||
"../../packages/theme": {
|
||||
@@ -27672,7 +27674,9 @@
|
||||
"css-what": "6.1.0",
|
||||
"hyperapp": "^2.0.22",
|
||||
"slugify": "^1.6.5",
|
||||
"specificity": "^0.4.1"
|
||||
"specificity": "^0.4.1",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
},
|
||||
"@notesnook/theme": {
|
||||
|
||||
@@ -55,8 +55,16 @@ function attachMessagePort() {
|
||||
message.area === "full-page" &&
|
||||
(message.mode === "complete" || message.mode === "screenshot");
|
||||
return {
|
||||
height: sizeable ? document.body.clientHeight : 0,
|
||||
width: sizeable ? document.body.clientWidth : 0,
|
||||
height: sizeable
|
||||
? document.body.clientHeight ||
|
||||
document.firstElementChild?.scrollHeight ||
|
||||
1080
|
||||
: 0,
|
||||
width: sizeable
|
||||
? document.body.clientWidth ||
|
||||
document.firstElementChild?.scrollWidth ||
|
||||
1920
|
||||
: 0,
|
||||
data: await clip(request)
|
||||
};
|
||||
}
|
||||
|
||||
7
fastlane/metadata/android/en-US/changelogs/10334.txt
Normal file
7
fastlane/metadata/android/en-US/changelogs/10334.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
- Fix 24 hour reminder time in date picker
|
||||
- Fix pin input hide behind keyboard on session expiry
|
||||
- Disallow links in inline code
|
||||
- Fix default notebook not set in widget
|
||||
- Bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
3
fastlane/metadata/android/en-US/changelogs/10344.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/10344.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
7
fastlane/metadata/android/en-US/changelogs/10349.txt
Normal file
7
fastlane/metadata/android/en-US/changelogs/10349.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
- Fixed HTML & PDF exports
|
||||
- Improved task lists in editor
|
||||
- Improved loading images
|
||||
- Allow restoring backups when logged out
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
7
fastlane/metadata/android/en-US/changelogs/10354.txt
Normal file
7
fastlane/metadata/android/en-US/changelogs/10354.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
- Fixed HTML & PDF exports
|
||||
- Improved task lists in editor
|
||||
- Improved loading images
|
||||
- Allow restoring backups when logged out
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
15
packages/clipper/package-lock.json
generated
15
packages/clipper/package-lock.json
generated
@@ -1673,22 +1673,19 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
|
||||
"integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@webpack-cli/info": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
|
||||
"integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@webpack-cli/serve": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
|
||||
"integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
@@ -1712,8 +1709,7 @@
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
|
||||
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
@@ -1731,8 +1727,7 @@
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.21.10",
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"scripts": {
|
||||
"build": "tsc && yarn webpack -c webpack.config.js",
|
||||
"test": "playwright test",
|
||||
"postinstall": "patch-package"
|
||||
"postinstall": "patch-package",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/streetwriters/notesnook/issues"
|
||||
|
||||
@@ -87,23 +87,26 @@ export async function inlineStylesheets(options?: FetchOptions) {
|
||||
}
|
||||
|
||||
async function resolveImports(options?: FetchOptions) {
|
||||
let index = 0;
|
||||
for (const sheet of document.styleSheets) {
|
||||
const rulesToDelete = [];
|
||||
if (skipStyleSheet(sheet)) continue;
|
||||
|
||||
for (const rule of sheet.cssRules) {
|
||||
for (let i = 0; i < sheet.cssRules.length; ++i) {
|
||||
const rule = sheet.cssRules.item(i);
|
||||
if (!rule) continue;
|
||||
|
||||
if (rule.type === CSSRule.IMPORT_RULE) {
|
||||
const href = (rule as CSSImportRule).href;
|
||||
const result = await downloadStylesheet(href, options);
|
||||
if (result) {
|
||||
if (sheet.ownerNode) sheet.ownerNode.before(result);
|
||||
else document.head.appendChild(result);
|
||||
|
||||
sheet.deleteRule(index);
|
||||
rulesToDelete.push(i);
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
for (const ruleIndex of rulesToDelete) sheet.deleteRule(ruleIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ const languagesList = await langen(
|
||||
path.join(ROOT_DIR, "src", "utils", "templates", "html", "languages")
|
||||
);
|
||||
const languageIndex = `function hasRequire() {
|
||||
return typeof require === "function" && !IS_DESKTOP_APP;
|
||||
return (
|
||||
typeof require === "function" &&
|
||||
(typeof IS_DESKTOP_APP === "undefined" || !IS_DESKTOP_APP)
|
||||
);
|
||||
}
|
||||
|
||||
export async function loadLanguage(language) {
|
||||
|
||||
@@ -372,9 +372,7 @@ export default class Attachments extends Collection {
|
||||
|
||||
get pending() {
|
||||
return this.all.filter(
|
||||
(attachment) =>
|
||||
(attachment.dateUploaded <= 0 || !attachment.dateUploaded) &&
|
||||
attachment.noteIds.length > 0
|
||||
(attachment) => attachment.dateUploaded <= 0 || !attachment.dateUploaded
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import Migrator from "./migrator.js";
|
||||
import { toChunks } from "../utils/array.js";
|
||||
import { migrateItem } from "../migrations.js";
|
||||
import Indexer from "./indexer.js";
|
||||
import setManipulator from "../utils/set.js";
|
||||
|
||||
const COLORS = [
|
||||
"red",
|
||||
@@ -247,9 +248,10 @@ export default class Backup {
|
||||
const { data, version = 0 } = backup;
|
||||
|
||||
const toAdd = {};
|
||||
for (const item of Array.isArray(data) ? data : Object.values(data)) {
|
||||
for (let item of Array.isArray(data) ? data : Object.values(data)) {
|
||||
// we do not want to restore deleted items
|
||||
if (!item || (!item.type && item.deleted)) continue;
|
||||
if (!item || (!item.type && item.deleted) || typeof item !== "object")
|
||||
continue;
|
||||
// in v5.6 of the database, we did not set note history session's type
|
||||
if (!item.type && item.sessionContentId) item.type = "notehistory";
|
||||
|
||||
@@ -265,6 +267,23 @@ export default class Backup {
|
||||
? "color"
|
||||
: item.itemType || item.type;
|
||||
|
||||
// items should sync immediately after getting restored
|
||||
item.dateModified = Date.now();
|
||||
item.synced = false;
|
||||
|
||||
if (itemType === "attachment" && item.metadata && item.metadata.hash) {
|
||||
const attachment = this._db.attachments.attachment(item.metadata.hash);
|
||||
if (attachment) {
|
||||
item = {
|
||||
...attachment,
|
||||
noteIds: setManipulator.union(attachment.noteIds, item.noteIds)
|
||||
};
|
||||
} else {
|
||||
item.dateUploaded = undefined;
|
||||
item.failed = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const collectionKey = itemTypeToCollectionKey[itemType];
|
||||
if (collectionKey) {
|
||||
toAdd[collectionKey] = toAdd[collectionKey] || [];
|
||||
|
||||
@@ -57,7 +57,7 @@ const KEY_SELECTORS = {
|
||||
abc: (item) => getFirstCharacter(item.alias || item.title),
|
||||
month: (item, groupBy, dateNow) => {
|
||||
dateNow.setTime(item[groupBy]);
|
||||
return MONTHS_FULL[dateNow.getMonth()];
|
||||
return `${MONTHS_FULL[dateNow.getMonth()]} ${dateNow.getFullYear()}`;
|
||||
},
|
||||
week: (item, groupBy) => getWeekGroupFromTimestamp(item[groupBy]),
|
||||
year: (item, groupBy, dateNow) => {
|
||||
|
||||
@@ -41,7 +41,6 @@ async function preprocessHTML(templateData) {
|
||||
const { content } = templateData;
|
||||
|
||||
let html = content.replaceAll(/<p([^>]*)><\/p>/gm, "<p$1><br/></p>");
|
||||
console.log(html);
|
||||
for (const attribute in replaceableAttributes) {
|
||||
html = html.replaceAll(attribute, replaceableAttributes[attribute]);
|
||||
}
|
||||
@@ -60,7 +59,6 @@ async function preprocessHTML(templateData) {
|
||||
: await import("katex/contrib/mhchem/mhchem.js");
|
||||
for (const mathBlock of mathBlocks) {
|
||||
const text = mathBlock.textContent;
|
||||
console.log(text);
|
||||
mathBlock.innerHTML = katex.renderToString(text, {
|
||||
displayMode: true,
|
||||
throwOnError: false
|
||||
@@ -114,6 +112,9 @@ async function preprocessHTML(templateData) {
|
||||
export default { buildHTML };
|
||||
|
||||
function hasRequire() {
|
||||
// eslint-disable-next-line no-undef
|
||||
return typeof require === "function" && !IS_DESKTOP_APP;
|
||||
return (
|
||||
typeof require === "function" &&
|
||||
// eslint-disable-next-line no-undef
|
||||
(typeof IS_DESKTOP_APP === "undefined" || !IS_DESKTOP_APP)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ const template = (data) => `<!DOCTYPE html>
|
||||
color: #202124;
|
||||
font-family: "Open Sans", "Noto Sans", Frutiger, Calibri, Myriad, Arial, Ubuntu, Helvetica, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
|
||||
.math-block {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
|
||||
@@ -52,7 +52,7 @@ export function formatTitle(
|
||||
.replace(NEWLINE_STRIP_REGEX, " ")
|
||||
.replace(DATE_REGEX, date)
|
||||
.replace(TIME_REGEX, time)
|
||||
.replace(HEADLINE_REGEX, headline)
|
||||
.replace(HEADLINE_REGEX, headline || "")
|
||||
.replace(TIMESTAMP_REGEX, timestamp)
|
||||
.replace(COUNT_REGEX, totalNotes + 1);
|
||||
}
|
||||
|
||||
173
packages/editor-mobile/package-lock.json
generated
173
packages/editor-mobile/package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"@mdi/react": "^1.6.0",
|
||||
"@notesnook/editor": "file:../editor",
|
||||
"@notesnook/theme": "file:../theme",
|
||||
"buffer": "^6.0.3",
|
||||
"framer-motion": "^6.5.1",
|
||||
"mdi-react": "9.1.0",
|
||||
"react": "^17.0.2",
|
||||
@@ -50,28 +51,28 @@
|
||||
"@social-embed/lib": "^0.0.2-next.1",
|
||||
"@theme-ui/components": ">=0.14.0",
|
||||
"@theme-ui/core": ">=0.14.0",
|
||||
"@tiptap/core": "2.1.7",
|
||||
"@tiptap/extension-character-count": "2.1.7",
|
||||
"@tiptap/extension-color": "2.1.7",
|
||||
"@tiptap/extension-font-family": "2.1.7",
|
||||
"@tiptap/extension-history": "2.1.7",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.7",
|
||||
"@tiptap/extension-link": "2.1.7",
|
||||
"@tiptap/extension-list-keymap": "2.1.7",
|
||||
"@tiptap/extension-placeholder": "2.1.7",
|
||||
"@tiptap/extension-subscript": "2.1.7",
|
||||
"@tiptap/extension-superscript": "2.1.7",
|
||||
"@tiptap/extension-table": "2.1.7",
|
||||
"@tiptap/extension-table-cell": "2.1.7",
|
||||
"@tiptap/extension-table-header": "2.1.7",
|
||||
"@tiptap/extension-table-row": "2.1.7",
|
||||
"@tiptap/extension-task-item": "2.1.7",
|
||||
"@tiptap/extension-task-list": "2.1.7",
|
||||
"@tiptap/extension-text-align": "2.1.7",
|
||||
"@tiptap/extension-text-style": "2.1.7",
|
||||
"@tiptap/extension-underline": "2.1.7",
|
||||
"@tiptap/pm": "2.1.7",
|
||||
"@tiptap/starter-kit": "2.1.7",
|
||||
"@tiptap/core": "2.1.12",
|
||||
"@tiptap/extension-character-count": "2.1.12",
|
||||
"@tiptap/extension-color": "2.1.12",
|
||||
"@tiptap/extension-font-family": "2.1.12",
|
||||
"@tiptap/extension-history": "2.1.12",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.12",
|
||||
"@tiptap/extension-link": "2.1.12",
|
||||
"@tiptap/extension-list-keymap": "2.1.12",
|
||||
"@tiptap/extension-placeholder": "2.1.12",
|
||||
"@tiptap/extension-subscript": "2.1.12",
|
||||
"@tiptap/extension-superscript": "2.1.12",
|
||||
"@tiptap/extension-table": "2.1.12",
|
||||
"@tiptap/extension-table-cell": "2.1.12",
|
||||
"@tiptap/extension-table-header": "2.1.12",
|
||||
"@tiptap/extension-table-row": "2.1.12",
|
||||
"@tiptap/extension-task-item": "2.1.12",
|
||||
"@tiptap/extension-task-list": "2.1.12",
|
||||
"@tiptap/extension-text-align": "2.1.12",
|
||||
"@tiptap/extension-text-style": "2.1.12",
|
||||
"@tiptap/extension-underline": "2.1.12",
|
||||
"@tiptap/pm": "2.1.12",
|
||||
"@tiptap/starter-kit": "2.1.12",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"detect-indent": "^7.0.0",
|
||||
"entities": "^4.5.0",
|
||||
@@ -79,7 +80,7 @@
|
||||
"nanoid": "^4.0.1",
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.2",
|
||||
"prosemirror-view": "^1.31.7",
|
||||
"prosemirror-view": "1.32.2",
|
||||
"re-resizable": "^6.9.9",
|
||||
"react-colorful": "^5.5.1",
|
||||
"react-modal": "3.13.1",
|
||||
@@ -5931,6 +5932,25 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/batch": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||
@@ -6119,6 +6139,29 @@
|
||||
"node-int64": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@@ -10120,6 +10163,25 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
@@ -21638,28 +21700,28 @@
|
||||
"@social-embed/lib": "^0.0.2-next.1",
|
||||
"@theme-ui/components": ">=0.14.0",
|
||||
"@theme-ui/core": ">=0.14.0",
|
||||
"@tiptap/core": "2.1.7",
|
||||
"@tiptap/extension-character-count": "2.1.7",
|
||||
"@tiptap/extension-color": "2.1.7",
|
||||
"@tiptap/extension-font-family": "2.1.7",
|
||||
"@tiptap/extension-history": "2.1.7",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.7",
|
||||
"@tiptap/extension-link": "2.1.7",
|
||||
"@tiptap/extension-list-keymap": "2.1.7",
|
||||
"@tiptap/extension-placeholder": "2.1.7",
|
||||
"@tiptap/extension-subscript": "2.1.7",
|
||||
"@tiptap/extension-superscript": "2.1.7",
|
||||
"@tiptap/extension-table": "2.1.7",
|
||||
"@tiptap/extension-table-cell": "2.1.7",
|
||||
"@tiptap/extension-table-header": "2.1.7",
|
||||
"@tiptap/extension-table-row": "2.1.7",
|
||||
"@tiptap/extension-task-item": "2.1.7",
|
||||
"@tiptap/extension-task-list": "2.1.7",
|
||||
"@tiptap/extension-text-align": "2.1.7",
|
||||
"@tiptap/extension-text-style": "2.1.7",
|
||||
"@tiptap/extension-underline": "2.1.7",
|
||||
"@tiptap/pm": "2.1.7",
|
||||
"@tiptap/starter-kit": "2.1.7",
|
||||
"@tiptap/core": "2.1.12",
|
||||
"@tiptap/extension-character-count": "2.1.12",
|
||||
"@tiptap/extension-color": "2.1.12",
|
||||
"@tiptap/extension-font-family": "2.1.12",
|
||||
"@tiptap/extension-history": "2.1.12",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.12",
|
||||
"@tiptap/extension-link": "2.1.12",
|
||||
"@tiptap/extension-list-keymap": "2.1.12",
|
||||
"@tiptap/extension-placeholder": "2.1.12",
|
||||
"@tiptap/extension-subscript": "2.1.12",
|
||||
"@tiptap/extension-superscript": "2.1.12",
|
||||
"@tiptap/extension-table": "2.1.12",
|
||||
"@tiptap/extension-table-cell": "2.1.12",
|
||||
"@tiptap/extension-table-header": "2.1.12",
|
||||
"@tiptap/extension-table-row": "2.1.12",
|
||||
"@tiptap/extension-task-item": "2.1.12",
|
||||
"@tiptap/extension-task-list": "2.1.12",
|
||||
"@tiptap/extension-text-align": "2.1.12",
|
||||
"@tiptap/extension-text-style": "2.1.12",
|
||||
"@tiptap/extension-underline": "2.1.12",
|
||||
"@tiptap/pm": "2.1.12",
|
||||
"@tiptap/starter-kit": "2.1.12",
|
||||
"@types/katex": "^0.14.0",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/react": "17.0.2",
|
||||
@@ -21678,7 +21740,7 @@
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.2",
|
||||
"prosemirror-test-builder": "^1.1.0",
|
||||
"prosemirror-view": "^1.31.7",
|
||||
"prosemirror-view": "1.32.2",
|
||||
"re-resizable": "^6.9.9",
|
||||
"react": "17.0.2",
|
||||
"react-colorful": "^5.5.1",
|
||||
@@ -23401,6 +23463,11 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
},
|
||||
"batch": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||
@@ -23553,6 +23620,15 @@
|
||||
"node-int64": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@@ -26552,6 +26628,11 @@
|
||||
"harmony-reflect": "^1.4.6"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"@mdi/react": "^1.6.0",
|
||||
"@notesnook/editor": "file:../editor",
|
||||
"@notesnook/theme": "file:../theme",
|
||||
"buffer": "^6.0.3",
|
||||
"framer-motion": "^6.5.1",
|
||||
"mdi-react": "9.1.0",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -42,6 +42,9 @@ import Header from "./header";
|
||||
import StatusBar from "./statusbar";
|
||||
import Tags from "./tags";
|
||||
import Title from "./title";
|
||||
import { toBlobURL } from "@notesnook/editor/dist/utils/downloader";
|
||||
|
||||
globalThis.toBlobURL = toBlobURL;
|
||||
|
||||
const Tiptap = ({ settings }: { settings: Settings }) => {
|
||||
const [tick, setTick] = useState(0);
|
||||
@@ -208,6 +211,8 @@ const Tiptap = ({ settings }: { settings: Settings }) => {
|
||||
controller={controllerRef}
|
||||
title={controller.title}
|
||||
fontFamily={settings.fontFamily}
|
||||
dateFormat={settings.dateFormat}
|
||||
timeFormat={settings.timeFormat}
|
||||
/>
|
||||
<StatusBar container={containerRef} />
|
||||
</>
|
||||
|
||||
@@ -21,18 +21,23 @@ import { getFontById } from "@notesnook/editor";
|
||||
import React, { RefObject, useCallback, useEffect, useRef } from "react";
|
||||
import { EditorController } from "../hooks/useEditorController";
|
||||
import styles from "./styles.module.css";
|
||||
import { replaceDateTime } from "@notesnook/editor/dist/extensions/date-time";
|
||||
function Title({
|
||||
controller,
|
||||
title,
|
||||
titlePlaceholder,
|
||||
readonly,
|
||||
fontFamily
|
||||
fontFamily,
|
||||
dateFormat,
|
||||
timeFormat
|
||||
}: {
|
||||
controller: RefObject<EditorController>;
|
||||
title: string;
|
||||
titlePlaceholder: string;
|
||||
readonly: boolean;
|
||||
fontFamily: string;
|
||||
dateFormat: string;
|
||||
timeFormat: string;
|
||||
}) {
|
||||
const titleRef = useRef<HTMLTextAreaElement>(null);
|
||||
const titleSizeDiv = useRef<HTMLDivElement>(null);
|
||||
@@ -128,6 +133,11 @@ function Title({
|
||||
onChange={(event) => {
|
||||
resizeTextarea();
|
||||
if (!emitUpdate.current) return;
|
||||
event.target.value = replaceDateTime(
|
||||
event.target.value,
|
||||
dateFormat,
|
||||
timeFormat as "12-hour" | "24-hour"
|
||||
);
|
||||
controller.current?.titleChange(event.target.value);
|
||||
}}
|
||||
placeholder={titlePlaceholder}
|
||||
|
||||
@@ -16,7 +16,7 @@ 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/>.
|
||||
*/
|
||||
|
||||
global.Buffer = require("buffer").Buffer;
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
|
||||
@@ -62,6 +62,7 @@ declare global {
|
||||
var readonly: boolean;
|
||||
var noToolbar: boolean;
|
||||
var noHeader: boolean;
|
||||
function toBlobURL(dataurl: string, id?: string): string | undefined;
|
||||
/**
|
||||
* Id of current session
|
||||
*/
|
||||
|
||||
646
packages/editor/package-lock.json
generated
646
packages/editor/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,28 +12,28 @@
|
||||
"@social-embed/lib": "^0.0.2-next.1",
|
||||
"@theme-ui/components": ">=0.14.0",
|
||||
"@theme-ui/core": ">=0.14.0",
|
||||
"@tiptap/core": "2.1.11",
|
||||
"@tiptap/extension-character-count": "2.1.11",
|
||||
"@tiptap/extension-color": "2.1.11",
|
||||
"@tiptap/extension-font-family": "2.1.11",
|
||||
"@tiptap/extension-history": "2.1.11",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.11",
|
||||
"@tiptap/extension-link": "2.1.11",
|
||||
"@tiptap/extension-list-keymap": "2.1.11",
|
||||
"@tiptap/extension-placeholder": "2.1.11",
|
||||
"@tiptap/extension-subscript": "2.1.11",
|
||||
"@tiptap/extension-superscript": "2.1.11",
|
||||
"@tiptap/extension-table": "2.1.11",
|
||||
"@tiptap/extension-table-cell": "2.1.11",
|
||||
"@tiptap/extension-table-header": "2.1.11",
|
||||
"@tiptap/extension-table-row": "2.1.11",
|
||||
"@tiptap/extension-task-item": "2.1.11",
|
||||
"@tiptap/extension-task-list": "2.1.11",
|
||||
"@tiptap/extension-text-align": "2.1.11",
|
||||
"@tiptap/extension-text-style": "2.1.11",
|
||||
"@tiptap/extension-underline": "2.1.11",
|
||||
"@tiptap/pm": "2.1.11",
|
||||
"@tiptap/starter-kit": "2.1.11",
|
||||
"@tiptap/core": "2.1.12",
|
||||
"@tiptap/extension-character-count": "2.1.12",
|
||||
"@tiptap/extension-color": "2.1.12",
|
||||
"@tiptap/extension-font-family": "2.1.12",
|
||||
"@tiptap/extension-history": "2.1.12",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.12",
|
||||
"@tiptap/extension-link": "2.1.12",
|
||||
"@tiptap/extension-list-keymap": "2.1.12",
|
||||
"@tiptap/extension-placeholder": "2.1.12",
|
||||
"@tiptap/extension-subscript": "2.1.12",
|
||||
"@tiptap/extension-superscript": "2.1.12",
|
||||
"@tiptap/extension-table": "2.1.12",
|
||||
"@tiptap/extension-table-cell": "2.1.12",
|
||||
"@tiptap/extension-table-header": "2.1.12",
|
||||
"@tiptap/extension-table-row": "2.1.12",
|
||||
"@tiptap/extension-task-item": "2.1.12",
|
||||
"@tiptap/extension-task-list": "2.1.12",
|
||||
"@tiptap/extension-text-align": "2.1.12",
|
||||
"@tiptap/extension-text-style": "2.1.12",
|
||||
"@tiptap/extension-underline": "2.1.12",
|
||||
"@tiptap/pm": "2.1.12",
|
||||
"@tiptap/starter-kit": "2.1.12",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"detect-indent": "^7.0.0",
|
||||
"entities": "^4.5.0",
|
||||
@@ -41,7 +41,7 @@
|
||||
"nanoid": "^4.0.1",
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.2",
|
||||
"prosemirror-view": "^1.31.8",
|
||||
"prosemirror-view": "1.32.2",
|
||||
"re-resizable": "^6.9.9",
|
||||
"react-colorful": "^5.5.1",
|
||||
"react-modal": "3.13.1",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/node_modules/@tiptap/extension-link/dist/index.cjs b/node_modules/@tiptap/extension-link/dist/index.cjs
|
||||
index ecedb96..6c69974 100644
|
||||
index dad166b..9e27178 100644
|
||||
--- a/node_modules/@tiptap/extension-link/dist/index.cjs
|
||||
+++ b/node_modules/@tiptap/extension-link/dist/index.cjs
|
||||
@@ -103,6 +103,7 @@ function clickHandler(options) {
|
||||
@@ -11,7 +11,7 @@ index ecedb96..6c69974 100644
|
||||
window.open(href, target);
|
||||
}
|
||||
diff --git a/node_modules/@tiptap/extension-link/dist/index.js b/node_modules/@tiptap/extension-link/dist/index.js
|
||||
index 06aedaf..9c3543c 100644
|
||||
index 68433f3..7133f48 100644
|
||||
--- a/node_modules/@tiptap/extension-link/dist/index.js
|
||||
+++ b/node_modules/@tiptap/extension-link/dist/index.js
|
||||
@@ -99,6 +99,7 @@ function clickHandler(options) {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user