Compare commits

..

1 Commits

Author SHA1 Message Date
Ammar Ahmed
fdb8289805 mobile: show diff in note history 2024-05-13 09:39:39 +05:00
155 changed files with 3057 additions and 6633 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "3.0.7",
"version": "3.0.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "3.0.7",
"version": "3.0.4",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
@@ -29,7 +29,6 @@
"electron": "^29.3.1",
"electron-builder": "^24.13.3",
"esbuild": "^0.20.0",
"kysely": "^0.27.3",
"node-gyp-build": "^4.8.0",
"prebuildify": "^6.0.1",
"tree-kill": "^1.2.2",
@@ -113,6 +112,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",
@@ -133,6 +133,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",
@@ -146,6 +147,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"
}
@@ -154,6 +156,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"
}
@@ -162,6 +165,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"
}
@@ -817,6 +821,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"
},
@@ -828,6 +833,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"
},
@@ -872,6 +878,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",
@@ -900,12 +907,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": "*"
}
@@ -919,7 +928,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/plist": {
"version": "3.0.2",
@@ -935,6 +945,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": "*"
}
@@ -964,6 +975,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": "*"
@@ -1108,80 +1120,6 @@
"node": ">=12.0.0"
}
},
"node_modules/archiver": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
"dev": true,
"peer": true,
"dependencies": {
"archiver-utils": "^2.1.0",
"async": "^3.2.4",
"buffer-crc32": "^0.2.1",
"readable-stream": "^3.6.0",
"readdir-glob": "^1.1.2",
"tar-stream": "^2.2.0",
"zip-stream": "^4.1.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/archiver-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
"dev": true,
"peer": true,
"dependencies": {
"glob": "^7.1.4",
"graceful-fs": "^4.2.0",
"lazystream": "^1.0.0",
"lodash.defaults": "^4.2.0",
"lodash.difference": "^4.5.0",
"lodash.flatten": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.union": "^4.6.0",
"normalize-path": "^3.0.0",
"readable-stream": "^2.0.0"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/archiver-utils/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"peer": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/archiver-utils/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"peer": true
},
"node_modules/archiver-utils/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"peer": true,
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -1321,6 +1259,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": {
@@ -1371,6 +1310,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": "*"
}
@@ -1446,6 +1386,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"
}
@@ -1454,6 +1395,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",
@@ -1573,6 +1515,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"
},
@@ -1626,22 +1569,6 @@
"node": ">=0.10.0"
}
},
"node_modules/compress-commons": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
"dev": true,
"peer": true,
"dependencies": {
"buffer-crc32": "^0.2.13",
"crc32-stream": "^4.0.2",
"normalize-path": "^3.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1708,7 +1635,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"devOptional": true
"optional": true
},
"node_modules/crc": {
"version": "3.8.0",
@@ -1719,33 +1646,6 @@
"buffer": "^5.1.0"
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"dev": true,
"peer": true,
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/crc32-stream": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
"dev": true,
"peer": true,
"dependencies": {
"crc-32": "^1.2.0",
"readable-stream": "^3.4.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1838,6 +1738,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"
}
@@ -1846,6 +1747,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",
@@ -1879,6 +1781,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": {
@@ -2008,6 +1911,7 @@
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-29.3.1.tgz",
"integrity": "sha512-auge1/6RVqgUd6TgIq88wKdUCJi2cjESi3jy7d+6X4JzvBGprKBqMJ8JSSFpu/Px1YJrFUKAxfy6SC+TQf1uLw==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@electron/get": "^2.0.0",
@@ -2047,19 +1951,6 @@
"node": ">=14.0.0"
}
},
"node_modules/electron-builder-squirrel-windows": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz",
"integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==",
"dev": true,
"peer": true,
"dependencies": {
"app-builder-lib": "24.13.3",
"archiver": "^5.3.1",
"builder-util": "24.13.1",
"fs-extra": "^10.1.0"
}
},
"node_modules/electron-builder/node_modules/builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
@@ -2133,6 +2024,7 @@
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -2154,6 +2046,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"
}
@@ -2168,6 +2061,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": {
@@ -2220,6 +2114,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"
@@ -2240,6 +2135,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",
@@ -2280,6 +2176,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"
}
@@ -2422,6 +2319,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true,
"optional": true
},
"node_modules/get-caller-file": {
@@ -2436,6 +2334,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",
@@ -2451,6 +2350,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"
},
@@ -2524,6 +2424,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",
@@ -2541,6 +2442,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"
@@ -2556,6 +2458,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",
@@ -2585,6 +2488,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"optional": true,
"dependencies": {
"function-bind": "^1.1.1"
@@ -2606,6 +2510,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"
@@ -2618,6 +2523,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"
@@ -2630,6 +2536,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"
@@ -2653,7 +2560,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",
@@ -2673,6 +2581,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"
@@ -2839,13 +2748,6 @@
"node": ">=0.12.0"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true,
"peer": true
},
"node_modules/isbinaryfile": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz",
@@ -2941,7 +2843,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-schema-traverse": {
"version": "0.4.1",
@@ -2953,6 +2856,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": {
@@ -2982,125 +2886,37 @@
"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"
}
},
"node_modules/kysely": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.3.tgz",
"integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==",
"dev": true,
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/lazy-val": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q=="
},
"node_modules/lazystream": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
"dev": true,
"peer": true,
"dependencies": {
"readable-stream": "^2.0.5"
},
"engines": {
"node": ">= 0.6.3"
}
},
"node_modules/lazystream/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"peer": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/lazystream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"peer": true
},
"node_modules/lazystream/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"peer": true,
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"dev": true,
"peer": true
},
"node_modules/lodash.difference": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
"dev": true,
"peer": true
},
"node_modules/lodash.escaperegexp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
"integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="
},
"node_modules/lodash.flatten": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
"dev": true,
"peer": true
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"peer": true
},
"node_modules/lodash.union": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
"dev": true,
"peer": true
},
"node_modules/lowercase-keys": {
"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"
}
@@ -3120,6 +2936,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"
@@ -3165,6 +2982,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"
}
@@ -3290,6 +3108,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"
},
@@ -3313,6 +3132,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"
@@ -3330,6 +3150,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"
}
@@ -3392,7 +3213,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",
@@ -3469,17 +3291,11 @@
"prebuildify": "bin.js"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true,
"peer": true
},
"node_modules/progress": {
"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"
}
@@ -3519,6 +3335,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"
},
@@ -3585,16 +3402,6 @@
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/readdir-glob": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
"dev": true,
"peer": true,
"dependencies": {
"minimatch": "^5.1.0"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -3618,12 +3425,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"
},
@@ -3644,6 +3453,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",
@@ -3723,12 +3533,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"
@@ -3744,6 +3556,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"
@@ -3896,6 +3709,7 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"dev": true,
"optional": true
},
"node_modules/stat-mode": {
@@ -3995,6 +3809,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"
},
@@ -4196,7 +4011,8 @@
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/universalify": {
"version": "2.0.0",
@@ -4345,48 +4161,12 @@
"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"
}
},
"node_modules/zip-stream": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
"dev": true,
"peer": true,
"dependencies": {
"archiver-utils": "^3.0.4",
"compress-commons": "^4.1.2",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/zip-stream/node_modules/archiver-utils": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
"dev": true,
"peer": true,
"dependencies": {
"glob": "^7.2.3",
"graceful-fs": "^4.2.0",
"lazystream": "^1.0.0",
"lodash.defaults": "^4.2.0",
"lodash.difference": "^4.5.0",
"lodash.flatten": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.union": "^4.6.0",
"normalize-path": "^3.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/zod": {
"version": "3.21.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.0.7",
"version": "3.0.4",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
@@ -30,7 +30,6 @@
"electron": "^29.3.1",
"electron-builder": "^24.13.3",
"esbuild": "^0.20.0",
"kysely": "^0.27.3",
"node-gyp-build": "^4.8.0",
"prebuildify": "^6.0.1",
"tree-kill": "^1.2.2",

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { initTRPC } from "@trpc/server";
import { z } from "zod";
import { app, dialog, nativeTheme, Notification, shell } from "electron";
import { dialog, nativeTheme, Notification, shell } from "electron";
import { AutoLaunch } from "../utils/autolaunch";
import { config, DesktopIntegration } from "../utils/config";
import { bringToFront } from "../utils/bring-to-front";
@@ -152,10 +152,6 @@ export const osIntegrationRouter = t.router({
await rm(input);
}),
restart: t.procedure.query(() => {
app.relaunch();
app.exit();
}),
showNotification: t.procedure
.input(NotificationOptions)
.query(({ input }) => {
@@ -195,10 +191,7 @@ export const osIntegrationRouter = t.router({
({ input: { theme, windowControlsIconColor, backgroundColor } }) => {
if (windowControlsIconColor) {
config.windowControlsIconColor = windowControlsIconColor;
if (
process.platform === "win32" &&
!config.desktopSettings.nativeTitlebar
)
if (process.platform === "win32")
globalThis.window?.setTitleBarOverlay({
symbolColor: windowControlsIconColor
});

View File

@@ -1,143 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type { Database, Statement } from "better-sqlite3-multiple-ciphers";
import type { QueryResult } from "kysely";
type SQLiteCompatibleType =
| number
| string
| Uint8Array
| Array<number>
| bigint
| null;
export class SQLite {
sqlite?: Database;
initialized = false;
preparedStatements: Map<string, Statement<unknown[]>> = new Map();
retryCounter: Record<string, number> = {};
constructor() {
console.log("new sqlite worker");
}
async open(filePath: string) {
if (this.sqlite) {
console.error("Database is already initialized");
return;
}
this.sqlite = require("better-sqlite3-multiple-ciphers")(
filePath
).unsafeMode(true);
}
/**
* Wrapper function for preparing SQL statements with caching
* to avoid unnecessary computations.
*/
async prepare(sql: string): Promise<Statement | undefined> {
if (!this.sqlite) throw new Error("Database is not initialized.");
try {
const cached = this.preparedStatements.get(sql);
if (cached !== undefined) return cached;
const prepared = this.sqlite.prepare(sql);
if (!prepared) return;
this.preparedStatements.set(sql, prepared);
// reset retry count on success
this.retryCounter[sql] = 0;
return prepared;
} catch (ex) {
console.error(ex);
// statement prepare process can be flaky so retry at least 5 times
// before giving up.
if (this.retryCounter[sql] < 5) {
this.retryCounter[sql] = (this.retryCounter[sql] || 0) + 1;
console.warn("Failed to prepare statement. Retrying:", sql);
return this.prepare(sql);
} else this.retryCounter[sql] = 0;
if (ex instanceof Error) ex.message += ` (query: ${sql})`;
throw ex;
}
}
async exec<R>(
sql: string,
parameters: SQLiteCompatibleType[] = []
): Promise<QueryResult<R>> {
const prepared = await this.prepare(sql);
if (!prepared) return { rows: [] };
try {
if (prepared.reader) {
return {
rows: prepared.all(parameters) as R[]
};
} else {
const { changes, lastInsertRowid } = prepared.run(parameters);
const numAffectedRows =
changes !== undefined && changes !== null && !isNaN(changes)
? BigInt(changes)
: undefined;
return {
numAffectedRows,
insertId:
lastInsertRowid !== undefined && lastInsertRowid !== null
? typeof lastInsertRowid === "bigint"
? lastInsertRowid
: BigInt(lastInsertRowid)
: undefined,
rows: [] as R[]
};
}
} catch (e) {
if (e instanceof Error) e.message += ` (query: ${sql})`;
throw e;
}
}
async run<R>(
sql: string,
parameters?: SQLiteCompatibleType[]
): Promise<QueryResult<R>> {
if (!this.sqlite) throw new Error("No database is not opened.");
return await this.exec(sql, parameters);
}
async close() {
if (!this.sqlite) return;
this.preparedStatements.clear();
this.sqlite.close();
this.sqlite = undefined;
}
async delete(filePath: string) {
await this.close();
await require("fs/promises").rm(filePath, {
force: true,
maxRetries: 5,
retryDelay: 500
});
}
}

View File

@@ -21,4 +21,3 @@ export * from "./constants";
export type { AppRouter } from "./api";
export { type UpdateInfo } from "builder-util-runtime";
export { type DesktopIntegration } from "./utils/config";
export { SQLite } from "./api/sqlite-kysely.js";

View File

@@ -84,27 +84,22 @@ async function createWindow() {
format: process.platform === "win32" ? "ico" : "png"
}),
...(config.desktopSettings.nativeTitlebar
? {}
: {
titleBarStyle: "hidden",
frame: process.platform === "win32" || process.platform === "darwin",
titleBarOverlay: {
height: 37,
color: "#00000000",
symbolColor: config.windowControlsIconColor
},
trafficLightPosition: {
x: 16,
y: 12
}
}),
titleBarStyle: "hidden",
frame: process.platform === "win32" || process.platform === "darwin",
titleBarOverlay: {
height: 37,
color: "#00000000",
symbolColor: config.windowControlsIconColor
},
trafficLightPosition: {
x: 16,
y: 12
},
webPreferences: {
zoomFactor: config.zoomFactor,
nodeIntegration: true,
contextIsolation: false,
nodeIntegrationInWorker: true,
spellcheck: config.isSpellCheckerEnabled,
preload: __dirname + "/preload.js"
}

View File

@@ -23,11 +23,13 @@ import { type RendererGlobalElectronTRPC } from "electron-trpc/src/types";
import type { NNCrypto } from "@notesnook/crypto";
import { ipcRenderer } from "electron";
import { platform } from "os";
import sqlite3, { Database } from "better-sqlite3-multiple-ciphers";
declare global {
var os: () => "mas" | ReturnType<typeof platform>;
var electronTRPC: RendererGlobalElectronTRPC;
var NativeNNCrypto: (new () => NNCrypto) | undefined;
var createSQLite3Database: (filename: string) => Database;
}
process.once("loaded", async () => {
@@ -41,4 +43,5 @@ process.once("loaded", async () => {
});
globalThis.NativeNNCrypto = require("@notesnook/crypto").NNCrypto;
globalThis.createSQLite3Database = (filename) => sqlite3(filename);
globalThis.os = () => (MAC_APP_STORE ? "mas" : platform());

View File

@@ -25,8 +25,7 @@ export const DesktopIntegration = z.object({
autoStart: z.boolean().optional(),
startMinimized: z.boolean().optional(),
minimizeToSystemTray: z.boolean().optional(),
closeToSystemTray: z.boolean().optional(),
nativeTitlebar: z.boolean().optional()
closeToSystemTray: z.boolean().optional()
});
export type DesktopIntegration = z.infer<typeof DesktopIntegration>;
@@ -36,8 +35,7 @@ export const config = {
autoStart: false,
startMinimized: false,
minimizeToSystemTray: false,
closeToSystemTray: false,
nativeTitlebar: false
closeToSystemTray: false
},
privacyMode: false,
isSpellCheckerEnabled: true,

View File

@@ -16,16 +16,15 @@ 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 "react-native-gesture-handler";
import {
THEME_COMPATIBILITY_VERSION,
useThemeEngineStore
} from "@notesnook/theme";
import React, { useEffect } from "react";
import { I18nManager, View } from "react-native";
import "react-native-gesture-handler";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { initializeLogger } from "./common/database/logger";
import AppLockedOverlay from "./components/app-lock-overlay";
import { withErrorBoundry } from "./components/exception-handler";
import GlobalSafeAreaProvider from "./components/globalsafearea";
@@ -45,28 +44,23 @@ I18nManager.swapLeftAndRightInRTL(false);
const App = () => {
const init = useAppEvents();
useEffect(() => {
initializeLogger()
.catch((e) => {
console.log(e);
})
.finally(() => {
const { appLockEnabled, appLockMode } = SettingsService.get();
if (appLockEnabled || appLockMode !== "none") {
useUserStore.getState().lockApp(true);
}
const { appLockEnabled, appLockMode } = SettingsService.get();
if (appLockEnabled || appLockMode !== "none") {
useUserStore.getState().lockApp(true);
}
//@ts-ignore
globalThis["IS_MAIN_APP_RUNNING"] = true;
init();
setTimeout(async () => {
SettingsService.onFirstLaunch();
await Notifications.get();
if (SettingsService.get().notifNotes) {
Notifications.pinQuickNote(true);
}
TipManager.init();
}, 100);
//@ts-ignore
globalThis["IS_MAIN_APP_RUNNING"] = true;
init();
setTimeout(async () => {
SettingsService.onFirstLaunch();
await Notifications.get();
if (SettingsService.get().notifNotes) {
Notifications.pinQuickNote(true);
}
TipManager.init();
}, 100);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@@ -25,7 +25,6 @@ import { MMKVLoader, ProcessingModes } from "react-native-mmkv-storage";
import { generateSecureRandom } from "react-native-securerandom";
import { DatabaseLogger } from ".";
import { MMKV } from "./mmkv";
import { ToastManager } from "../../services/event-manager";
// Database key cipher is persisted across different user sessions hence it has
// it's independent storage which we will never clear. This is only used when application has
@@ -44,8 +43,9 @@ const IOS_KEYCHAIN_ACCESS_GROUP = "group.org.streetwriters.notesnook";
const IOS_KEYCHAIN_SERVICE_NAME = "org.streetwriters.notesnook";
const KEYCHAIN_SERVER_DBKEY = "notesnook:db";
const NOTESNOOK_APPLOCK_KEY_SALT = "kBwr1Kre86ebOZ8ThLu2OA";
const NOTESNOOK_DB_KEY_SALT = "SNuzOcEK3amoqL0WvPeKqw";
const NOTESNOOK_APPLOCK_KEY_SALT = "3dqclWbOYllfk9kk";
const NOTESNOOK_DB_KEY_SALT = "2rcgSprDmRvZ1AAa";
const NOTESNOOK_USER_KEY_SALT = "7qO4qeoM6PbsAJ0Q";
const DB_KEY_CIPHER = "databaseKeyCipher";
const USER_KEY_CIPHER = "userKeyCipher";
@@ -117,7 +117,9 @@ export async function setAppLockVerificationCipher(appLockPassword) {
NOTESNOOK_APPLOCK_KEY_SALT
);
const encrypted = await encrypt(appLockCredentials, generatePassword());
CipherStorage.setMap(APPLOCK_CIPHER, encrypted);
DatabaseLogger.info("setAppLockVerificationCipher");
} catch (e) {
DatabaseLogger.error(e);
@@ -133,8 +135,10 @@ export async function validateAppLockPassword(appLockPassword) {
try {
const appLockCipher = CipherStorage.getMap(APPLOCK_CIPHER);
if (!appLockCipher) return true;
const key = await Sodium.deriveKey(appLockPassword, appLockCipher.salt);
const decrypted = await decrypt(key, appLockCipher);
const decrypted = await decrypt(
await Sodium.deriveKey(appLockPassword, NOTESNOOK_APPLOCK_KEY_SALT),
appLockCipher
);
DatabaseLogger.info(
`validateAppLockPassword: ${typeof decrypted === "string"}`
@@ -210,7 +214,7 @@ export async function getDatabaseKey(appLockPassword) {
const userKeyCipher = await encrypt(
{
key: DB_KEY,
salt: NOTESNOOK_DB_KEY_SALT
salt: NOTESNOOK_USER_KEY_SALT
},
userKeyCredentials.password
);
@@ -223,7 +227,6 @@ export async function getDatabaseKey(appLockPassword) {
return DB_KEY;
} catch (e) {
ToastManager.error(e, "Error getting database key");
console.log(e, "error");
DatabaseLogger.error(e);
return null;
@@ -236,7 +239,7 @@ export async function deriveCryptoKey(data) {
const userKeyCipher = await encrypt(
{
key: await getDatabaseKey(),
salt: NOTESNOOK_DB_KEY_SALT
salt: NOTESNOOK_USER_KEY_SALT
},
credentials.key
);
@@ -261,12 +264,13 @@ export async function getCryptoKey(_name) {
const key = await decrypt(
{
key: await getDatabaseKey(),
salt: keyCipher.salt
key: await getDatabaseKey()
},
keyCipher
);
DatabaseLogger.info("User key decrypted: ", !!key);
return key;
} catch (e) {
console.log("getCryptoKey", e);

View File

@@ -85,10 +85,4 @@ export async function setupDatabase(password) {
}
export const db = database;
let DatabaseLogger = dbLogger.scope(Platform.OS);
const setLogger = () => {
DatabaseLogger = dbLogger.scope(Platform.OS);
};
export { DatabaseLogger, setLogger };
export const DatabaseLogger = dbLogger;

View File

@@ -16,27 +16,14 @@ 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 { MMKVLoader } from "react-native-mmkv-storage";
import { initialize } from "@notesnook/core/dist/logger";
import { SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from "kysely";
import { Platform } from "react-native";
import { setLogger } from ".";
import { RNSqliteDriver } from "./sqlite.kysely";
import { KV } from "./storage";
const initializeLogger = async () => {
await initialize({
dialect: (name) => ({
createDriver: () => {
return new RNSqliteDriver({ async: true, dbName: name });
},
createAdapter: () => new SqliteAdapter(),
createIntrospector: (db) => new SqliteIntrospector(db),
createQueryCompiler: () => new SqliteQueryCompiler()
}),
tempStore: "memory",
journalMode: Platform.OS === "ios" ? "DELETE" : "WAL"
});
setLogger();
};
const LoggerStorage = new MMKVLoader()
.withInstanceID("notesnook_logs")
.initialize();
export { initializeLogger };
initialize(new KV(LoggerStorage));
export { LoggerStorage };

View File

@@ -21,6 +21,7 @@ import React, { useCallback, useEffect, useRef } from "react";
import { AppStateStatus, Platform, TextInput, View } from "react-native";
//@ts-ignore
import { useThemeColors } from "@notesnook/theme";
import { enabled } from "react-native-privacy-snapshot";
import { DatabaseLogger } from "../../common/database";
import {
decrypt,
@@ -33,13 +34,11 @@ import {
import { MMKV } from "../../common/database/mmkv";
import { useAppState } from "../../hooks/use-app-state";
import BiometricService from "../../services/biometrics";
import { ToastManager } from "../../services/event-manager";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store";
import { NotesnookModule } from "../../utils/notesnook-module";
import { SIZE } from "../../utils/size";
import { Toast } from "../toast";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Input from "../ui/input";
@@ -120,6 +119,7 @@ const AppLockedOverlay = () => {
);
if (unlocked) {
lockApp(false);
enabled(false);
password.current = undefined;
}
biometricUnlockAwaitingUserInput.current = false;
@@ -146,13 +146,8 @@ const AppLockedOverlay = () => {
}
lockApp(false);
enabled(false);
password.current = undefined;
} else {
ToastManager.show({
heading: `Invalid ${keyboardType === "numeric" ? "pin" : "password"}`,
type: "error",
context: "local"
});
}
} catch (e) {
console.error(e);
@@ -160,6 +155,10 @@ const AppLockedOverlay = () => {
};
useEffect(() => {
if (appState === "active") {
enabled(false);
}
const prevState = lastAppState.current;
lastAppState.current = appState;
@@ -185,8 +184,17 @@ const AppLockedOverlay = () => {
DatabaseLogger.info("Biometric unlock request");
onUnlockAppRequested();
}
enabled(false);
} else {
SettingsService.appEnteredBackground();
if (
SettingsService.get().privacyScreen ||
SettingsService.getProperty("appLockEnabled")
) {
enabled(true);
}
}
}, [appState, onUnlockAppRequested, appLocked]);
@@ -201,7 +209,6 @@ const AppLockedOverlay = () => {
justifyContent: "center"
}}
>
<Toast context="local" />
<View
style={{
flex: 1,

View File

@@ -355,7 +355,6 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
AttachmentDialog.present = (note?: Note) => {
presentSheet({
component: () => <AttachmentDialog note={note} />,
keyboardHandlerDisabled: true
component: () => <AttachmentDialog note={note} />
});
};

View File

@@ -17,16 +17,12 @@ 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 { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { DDS } from "../../services/device-detection";
import { eSendEvent } from "../../services/event-manager";
import Sync from "../../services/sync";
import { useSettingStore } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store";
import { eUserLoggedIn } from "../../utils/events";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import SheetProvider from "../sheet-provider";
@@ -38,6 +34,11 @@ import Paragraph from "../ui/typography/paragraph";
import { hideAuth } from "./common";
import { ForgotPassword } from "./forgot-password";
import { useLogin } from "./use-login";
import { useSettingStore } from "../../stores/use-setting-store";
import { eUserLoggedIn } from "../../utils/events";
import { useUserStore } from "../../stores/use-user-store";
import Sync from "../../services/sync";
import { Notice } from "../ui/notice";
const LoginSteps = {
emailAuth: 1,
@@ -183,11 +184,7 @@ export const Login = ({ changeMode }) => {
defaultValue={email.current}
editable={step === LoginSteps.emailAuth && !loading}
onSubmit={() => {
if (step === LoginSteps.emailAuth) {
login();
} else {
passwordInputRef.current?.focus();
}
passwordInputRef.current?.focus();
}}
/>
@@ -246,7 +243,6 @@ export const Login = ({ changeMode }) => {
width: 250,
borderRadius: 100
}}
height={50}
fontSize={SIZE.md}
type="accent"
title={!loading ? "Continue" : null}

View File

@@ -112,10 +112,6 @@ export const SessionExpired = () => {
if (!res) throw new Error("no token found");
if (db.tokenManager._isTokenExpired(res))
throw new Error("token expired");
const key = await db.user.getEncryptionKey();
if (!key) throw new Error("No encryption key found.");
Sync.run("global", false, "full", async (complete) => {
if (!complete) {
let user = await db.user.getUser();
@@ -131,6 +127,7 @@ export const SessionExpired = () => {
setVisible(false);
});
} catch (e) {
console.log(e);
let user = await db.user.getUser();
if (!user) return;
email.current = user.email;

View File

@@ -37,7 +37,6 @@ import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useCallback } from "react";
import { ScrollView } from "react-native-actions-sheet";
const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
const { colors } = useThemeColors();
@@ -144,10 +143,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
}, [currentMethod.method, mfaInfo.token, seconds, sending, start]);
return (
<ScrollView
keyboardShouldPersistTaps="handled"
keyboardDismissMode="interactive"
>
<View>
<View
style={{
alignItems: "center",
@@ -218,7 +214,6 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
code.current = value;
//onNext();
}}
onSubmitEditing={onNext}
caretHidden
inputStyle={{
fontSize: SIZE.lg,
@@ -230,7 +225,6 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
keyboardType={
currentMethod.method === "recoveryCode" ? "default" : "numeric"
}
enablesReturnKeyAutomatically
containerStyle={{
height: 60,
borderWidth: 0,
@@ -303,7 +297,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
</>
)}
</View>
</ScrollView>
</View>
);
};

View File

@@ -44,7 +44,6 @@ type DialogInfo = {
// eslint-disable-next-line @typescript-eslint/ban-types
context: "global" | "local" | (string & {});
secureTextEntry?: boolean;
keyboardType?: string;
};
export function presentDialog(data: Partial<DialogInfo>): void {

View File

@@ -174,7 +174,6 @@ export const Dialog = ({ context = "global" }) => {
onSubmit={onPressPositive}
returnKeyLabel="Done"
returnKeyType="done"
keyboardType={dialogInfo.keyboardType || "default"}
placeholder={dialogInfo.inputPlaceholder}
/>
</View>

View File

@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import { TextInput, View } from "react-native";
import { db } from "../../../common/database";
import {
clearAppLockVerificationCipher,
setAppLockVerificationCipher,
@@ -45,7 +44,6 @@ import BaseDialog from "../../dialog/base-dialog";
import DialogButtons from "../../dialog/dialog-buttons";
import DialogHeader from "../../dialog/dialog-header";
import { Toast } from "../../toast";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
@@ -68,7 +66,6 @@ export const AppLockPassword = () => {
confirmPassword?: string;
}>({});
const [secureTextEntry, setSecureTextEntry] = useState(true);
const [accountPass, setAccountPass] = useState(false);
useEffect(() => {
const subs = [
@@ -76,7 +73,6 @@ export const AppLockPassword = () => {
eOpenAppLockPasswordDialog,
(mode: "create" | "change" | "remove") => {
setMode(mode);
setAccountPass(false);
setVisible(true);
}
),
@@ -130,9 +126,7 @@ export const AppLockPassword = () => {
mode === "change"
? `Change app lock ${keyboardType}`
: mode === "remove"
? `Enter ${
accountPass ? "account password" : `app lock ${keyboardType}`
} to remove ${keyboardType}`
? `Remove app lock ${keyboardType}`
: `Set up a custom app lock ${keyboardType} to unlock the app`
}
icon="shield"
@@ -175,39 +169,33 @@ export const AppLockPassword = () => {
confirmPasswordInputRef.current?.focus();
}}
defaultValue={values.current.password}
keyboardType={
keyboardType === "pin" && !accountPass ? "number-pad" : "default"
}
keyboardType={keyboardType === "pin" ? "number-pad" : "default"}
autoComplete="password"
returnKeyLabel={mode !== "remove" ? "Next" : "Remove"}
returnKeyType={mode !== "remove" ? "next" : "done"}
secureTextEntry={secureTextEntry}
buttonLeft={
accountPass ? null : (
<IconButton
name={keyboardType === "password" ? "numeric" : "keyboard"}
onPress={() => {
setKeyboardType(
keyboardType === "password" ? "pin" : "password"
);
setSecureTextEntry(false);
setImmediate(() => {
setSecureTextEntry(true);
});
}}
style={{
width: 25,
height: 25,
marginRight: 5
}}
size={SIZE.lg}
/>
)
<IconButton
name={keyboardType === "password" ? "numeric" : "keyboard"}
onPress={() => {
setKeyboardType(
keyboardType === "password" ? "pin" : "password"
);
setSecureTextEntry(false);
setImmediate(() => {
setSecureTextEntry(true);
});
}}
style={{
width: 25,
height: 25,
marginRight: 5
}}
size={SIZE.lg}
/>
}
placeholder={
accountPass
? "Account password"
: mode === "change"
mode === "change"
? `New ${keyboardType}`
: `${keyboardType === "pin" ? "Pin" : "Password"}`
}
@@ -234,36 +222,6 @@ export const AppLockPassword = () => {
placeholder={`Confirm ${keyboardType}`}
/>
) : null}
{mode === "remove" ? (
<>
<Button
icon={
accountPass ? "checkbox-marked" : "checkbox-blank-outline"
}
onPress={() => {
setSecureTextEntry(false);
setAccountPass(!accountPass);
setTimeout(() => {
setSecureTextEntry(true);
});
}}
iconSize={SIZE.lg}
type="plain"
iconColor={
accountPass ? colors.primary.accent : colors.primary.icon
}
title="Use account password instead"
style={{
width: "100%",
alignSelf: "flex-start",
justifyContent: "flex-start",
paddingHorizontal: 0,
height: 30
}}
/>
</>
) : null}
</View>
<DialogButtons
@@ -291,8 +249,7 @@ export const AppLockPassword = () => {
);
return;
}
const password = values.current.password;
setAppLockVerificationCipher(password);
await setAppLockVerificationCipher(values.current.password);
SettingsService.setProperty("appLockHasPasswordSecurity", true);
} else if (mode === "change") {
if (
@@ -320,6 +277,7 @@ export const AppLockPassword = () => {
);
return;
}
const isCurrentPasswordCorrect = await validateAppLockPassword(
values.current.currentPassword
);
@@ -335,10 +293,8 @@ export const AppLockPassword = () => {
return;
}
const password = values.current.password;
await clearAppLockVerificationCipher();
SettingsService.setProperty("appLockHasPasswordSecurity", true);
await setAppLockVerificationCipher(password);
await setAppLockVerificationCipher(values.current.password);
} else if (mode === "remove") {
if (!values.current.password) {
ToastManager.error(
@@ -349,18 +305,14 @@ export const AppLockPassword = () => {
return;
}
const isCurrentPasswordCorrect = accountPass
? await db.user.verifyPassword(values.current.password)
: await validateAppLockPassword(values.current.password);
const isCurrentPasswordCorrect = await validateAppLockPassword(
values.current.password
);
if (!isCurrentPasswordCorrect) {
ToastManager.error(
new Error(
accountPass
? "Account password incorrect"
: `${
keyboardType === "pin" ? "Pin" : "Password"
} incorrect`
`${keyboardType === "pin" ? "Pin" : "Password"} incorrect`
),
undefined,
"local"
@@ -390,9 +342,7 @@ export const AppLockPassword = () => {
close();
}}
positiveTitle={
mode === "remove" ? "Remove" : mode === "change" ? "Change" : "Save"
}
positiveTitle="Save"
negativeTitle="Cancel"
positiveType="transparent"
loading={false}

View File

@@ -49,8 +49,7 @@ export const Card = ({ color }: { color?: string }) => {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingHorizontal: 0,
width: "100%"
paddingHorizontal: 0
}}
>
<View
@@ -92,6 +91,7 @@ export const Card = ({ color }: { color?: string }) => {
<View
style={{
marginLeft: 10,
flexShrink: 1,
marginRight: 10
}}
>

View File

@@ -25,8 +25,7 @@ import {
DimensionValue,
TextStyle,
View,
ViewStyle,
useWindowDimensions
ViewStyle
} from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { useUserStore } from "../../../stores/use-user-store";
@@ -93,9 +92,6 @@ export const Button = ({
});
const textColor = buttonType?.text ? buttonType.text : text;
const { fontScale } = useWindowDimensions();
const growFactor = 1 + (fontScale - 1) / 10;
const Component = bold ? Heading : Paragraph;
return (
@@ -121,11 +117,8 @@ export const Button = ({
customOpacity={buttonType?.opacity}
customAlpha={buttonType?.alpha}
style={{
height: typeof height === "number" ? height * growFactor : height,
width:
typeof width === "number"
? width * growFactor
: (width as DimensionValue) || undefined,
height: height,
width: (width as DimensionValue) || undefined,
paddingHorizontal: 12,
borderRadius: 5,
alignSelf: "center",

View File

@@ -21,12 +21,11 @@ import { VariantsWithStaticColors, useThemeColors } from "@notesnook/theme";
import React, { RefObject, useCallback } from "react";
import {
ColorValue,
PressableStateCallbackType,
Pressable as RNPressable,
PressableProps as RNPressableProps,
PressableStateCallbackType,
View,
ViewStyle,
useWindowDimensions
ViewStyle
} from "react-native";
import {
RGB_Linear_Shade,
@@ -257,8 +256,6 @@ export const Pressable = ({
? 1
: colorOpacity;
const alpha = customAlpha ? customAlpha : isDark ? 0.03 : -0.03;
const { fontScale } = useWindowDimensions();
const growFactor = 1 + (fontScale - 1) / 8;
const getStyle = useCallback(
({ pressed }: PressableStateCallbackType): ViewStyle | ViewStyle[] => [
@@ -279,13 +276,7 @@ export const Pressable = ({
: borderColor || "transparent",
borderWidth: borderWidth
},
style,
{
height:
typeof style.height === "number"
? style.height * growFactor
: style.height
}
style
],
[
alpha,
@@ -297,8 +288,7 @@ export const Pressable = ({
borderSelectedColor,
borderColor,
borderWidth,
style,
growFactor
style
]
);

View File

@@ -22,14 +22,20 @@ import { Platform } from "react-native";
import { Subscription } from "react-native-iap";
import PremiumService from "../services/premium";
import { db } from "../common/database";
import { Product } from "@notesnook/core/dist/api/pricing";
const skuInfos: { [name: string]: Product | undefined } = {};
type PurchaseInfo = {
country: string;
countryCode: string;
sku: string;
discount: number;
};
const skuInfos: { [name: string]: PurchaseInfo | undefined } = {};
export const usePricing = (period: "monthly" | "yearly") => {
const [current, setCurrent] = useState<{
period: string;
info?: Product;
info?: PurchaseInfo;
product?: Subscription;
}>();
@@ -48,7 +54,6 @@ export const usePricing = (period: "monthly" | "yearly") => {
period
));
skuInfos[period] = skuInfo;
const products = (await PremiumService.getProducts()) as Subscription[];
let product = products.find((p) => p.productId === skuInfo?.sku);
if (!product)

View File

@@ -185,8 +185,7 @@ const camera = async (options: PickerOptions) => {
cropping: false,
multiple: true,
maxFiles: 10,
writeTempFile: true,
compressImageQuality: 1
writeTempFile: true
})
.then((response) => {
handleImageResponse(
@@ -216,8 +215,7 @@ const gallery = async (options: PickerOptions) => {
mediaType: "photo",
maxFiles: 10,
cropping: false,
multiple: true,
compressImageQuality: 1
multiple: true
})
.then((response) =>
handleImageResponse(
@@ -268,6 +266,7 @@ const handleImageResponse = async (
response: Image[],
options: PickerOptions
) => {
console.log(response, "result-file-picker");
const result = await AttachImage.present(response, options.context);
if (!result) return;
const compress = result.compress;
@@ -303,15 +302,10 @@ const handleImageResponse = async (
type: "url"
});
let fileName = image.sourceURL
const fileName = image.sourceURL
? basename(image.sourceURL)
: image.filename || "image";
fileName =
image.mime === "image/jpeg"
? fileName.replace(/HEIC|HEIF/, "jpeg")
: fileName;
console.log("attaching image...", fileName);
console.log("attaching file...");

View File

@@ -351,18 +351,9 @@ export const useEditor = (
if (!unlocked)
throw new Error("Could not save note, vault is locked");
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (typeof noteData.title === "string") {
await db.notes.add({
title: noteData.title,
id: noteData.id
});
}
noteData.contentId = note?.contentId;
if (data) {
await db.vault?.save(noteData as any);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await db.vault?.save(noteData as any);
clearTimeout(saveTimer);
}
@@ -705,13 +696,14 @@ export const useEditor = (
);
}
console.log("readonly state changed...", note.readonly);
useTabStore.getState().updateTab(tabId, {
readonly: note.readonly
});
}
if (data.type === "tiptap" && note && !isLocal) {
if (lastContentChangeTime.current[noteId] >= data.dateModified) {
if (lastContentChangeTime.current[noteId] >= data.dateEdited) {
return;
}
@@ -736,7 +728,7 @@ export const useEditor = (
}
} else {
const _nextContent = data.data;
if (_nextContent === currentContents.current[note.id]?.data) {
if (_nextContent === currentContents.current?.data) {
return;
}
lastContentChangeTime.current[note.id] = note.dateEdited;

View File

@@ -122,8 +122,7 @@ export default function DebugLogs() {
colors.primary.paragraph,
colors.error.paragraph,
colors.static.black,
colors.static.orange,
colors.primary.border
colors.static.orange
]
);

View File

@@ -90,26 +90,13 @@ export async function verifyUserWithApplock() {
positiveText: "Verify",
secureTextEntry: true,
negativeText: "Cancel",
keyboardType: keyboardType,
positivePress: async (value) => {
try {
const verified = await validateAppLockPassword(value);
if (!verified) {
ToastManager.show({
heading: `Invalid ${
keyboardType === "numeric" ? "pin" : "password"
}`,
type: "error",
context: "local"
});
return false;
}
resolve(verified);
} catch (e) {
resolve(false);
return false;
}
return true;
}
});
} else {

View File

@@ -70,7 +70,6 @@ import { useDragState } from "./editor/state";
import { verifyUser, verifyUserWithApplock } from "./functions";
import { SettingSection } from "./types";
import { getTimeLeft } from "./user-section";
import ScreenGuardModule from "react-native-screenguard";
type User = any;
@@ -334,14 +333,12 @@ export const settingsGroups: SettingSection[] = [
{
id: "logout",
name: "Log out",
description:
"Logging out will clear all data stored on THIS DEVICE.",
description: "Clear all your data and reset the app.",
icon: "logout",
modifer: () => {
presentDialog({
title: "Logout",
paragraph:
"Logging out will clear all data stored on THIS DEVICE. Make sure you have synced all your changes before logging out.",
paragraph: "Clear all your data and reset the app.",
positiveText: "Logout",
positivePress: async () => {
try {
@@ -851,18 +848,9 @@ export const settingsGroups: SettingSection[] = [
"Hide app contents when you switch to other apps. This will also disable screenshot taking in the app.",
modifer: () => {
const settings = SettingsService.get();
if (Platform.OS === "ios") {
enabled(!settings.privacyScreen);
if (settings.privacyScreen) {
ScreenGuardModule.unregister();
} else {
ScreenGuardModule.register({
backgroundColor: "#000000"
});
}
} else {
NotesnookModule.setSecureMode(!settings.privacyScreen);
}
Platform.OS === "android"
? NotesnookModule.setSecureMode(!settings.privacyScreen)
: enabled(true);
SettingsService.set({ privacyScreen: !settings.privacyScreen });
},
@@ -883,9 +871,6 @@ export const settingsGroups: SettingSection[] = [
type: "switch",
property: "appLockEnabled",
onChange: () => {
SettingsService.set({
privacyScreen: true
});
SettingsService.setPrivacyScreen(SettingsService.get());
},
onVerify: async () => {
@@ -988,7 +973,7 @@ export const settingsGroups: SettingSection[] = [
SettingsService.getProperty("applockKeyboardType") === "numeric"
? "pin"
: "password"
}, app lock will be disabled if no other security method is enabled.`,
}, app lock will fallback to using account password to unlock the app`,
hidden: () => {
return !SettingsService.getProperty("appLockHasPasswordSecurity");
},

View File

@@ -59,14 +59,14 @@ const recoveryKeyMessage = {
() => {
eSendEvent(eOpenRecoveryKeyDialog);
},
false,
true,
async () => {
SettingsService.set({
recoveryKeySaved: true
});
clearMessage();
},
"Cancel"
"I have saved my key already"
);
},
data: {},

View File

@@ -29,7 +29,6 @@ import { NotesnookModule } from "../utils/notesnook-module";
import { scale, updateSize } from "../utils/size";
import { DatabaseLogger } from "../common/database";
import { useUserStore } from "../stores/use-user-store";
import ScreenGuardModule from "react-native-screenguard";
function reset() {
const settings = get();
if (settings.reminder !== "off" && settings.reminder !== "useroff") {
@@ -77,17 +76,6 @@ function migrateAppLock() {
DatabaseLogger.debug("App lock Migrated");
}
function migrateSettings(settings: SettingStore["settings"]) {
const version = settings.settingsVersion;
if (!version) {
settings.settingsVersion = 1;
settings.privacyScreen = settings.appLockEnabled
? true
: settings.privacyScreen;
MMKV.setString("appSettings", JSON.stringify(settings));
}
}
function init() {
scale.fontScale = 1;
const settingsJson = MMKV.getString("appSettings");
@@ -95,17 +83,14 @@ function init() {
if (!settingsJson) {
MMKV.setString("appSettings", JSON.stringify(settings));
} else {
const settingsParsed = JSON.parse(settingsJson);
migrateSettings(settingsParsed);
settings = {
...settings,
...settingsParsed
...JSON.parse(settingsJson)
};
}
if (settings.fontScale) {
scale.fontScale = settings.fontScale;
}
setTimeout(() => setPrivacyScreen(settings), 1);
updateSize();
useSettingStore.getState().setSettings({ ...settings });
@@ -113,19 +98,17 @@ function init() {
}
function setPrivacyScreen(settings: SettingStore["settings"]) {
if (settings.privacyScreen) {
if (settings.privacyScreen || settings.appLockEnabled) {
if (Platform.OS === "android") {
NotesnookModule.setSecureMode(true);
} else {
enabled(true);
ScreenGuardModule.register({ backgroundColor: "#000000" });
}
} else {
if (Platform.OS === "android") {
NotesnookModule.setSecureMode(false);
} else {
enabled(false);
ScreenGuardModule.unregister();
}
}
}

View File

@@ -81,7 +81,6 @@ export type Settings = {
biometricsAuthEnabled?: boolean;
backgroundSync?: boolean;
applockKeyboardType: "numeric" | "default";
settingsVersion?: number;
};
type DimensionsType = {
@@ -170,8 +169,7 @@ export const defaultSettings: SettingStore["settings"] = {
markdownShortcuts: true,
biometricsAuthEnabled: false,
appLockHasPasswordSecurity: false,
backgroundSync: true,
settingsVersion: 0
backgroundSync: true
};
export const useSettingStore = create<SettingStore>((set, get) => ({

View File

@@ -111,7 +111,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 3017
versionCode 3012
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -37,10 +37,4 @@ allprojects {
maven { url 'https://www.jitpack.io' }
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
jvmTarget = "11"
}
}
}
}

View File

@@ -1,4 +1,3 @@
- Bring back undo/redo buttons in editor
- Bug fixes and performance improvements
Thank you for using Notesnook!

View File

@@ -21,9 +21,9 @@
6517B7C32B6838EB0079FF37 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */; };
6529A13E279BC4C70048D4A8 /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6529A13D279BC4C70048D4A8 /* BootSplash.storyboard */; };
656835812BB29A9800144BAB /* OpenSans-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 656835802BB29A8300144BAB /* OpenSans-Italic.ttf */; };
656DD2AB2B1891DF00A362EA /* (null) in Resources */ = {isa = PBXBuildFile; };
656DD2AC2B1891DF00A362EA /* (null) in Resources */ = {isa = PBXBuildFile; };
656DD2AD2B1891DF00A362EA /* (null) in Resources */ = {isa = PBXBuildFile; };
656DD2AB2B1891DF00A362EA /* BuildFile in Resources */ = {isa = PBXBuildFile; };
656DD2AC2B1891DF00A362EA /* BuildFile in Resources */ = {isa = PBXBuildFile; };
656DD2AD2B1891DF00A362EA /* BuildFile in Resources */ = {isa = PBXBuildFile; };
6593E4A3281C345400492C50 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6593E4A2281C345400492C50 /* AppDelegate.mm */; };
659BE46725E11A5100E05671 /* notesnook-text.png in Resources */ = {isa = PBXBuildFile; fileRef = 659BE46625E11A5100E05671 /* notesnook-text.png */; };
65AA857925E6DDEC00772A01 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65AA857825E6DDEC00772A01 /* WidgetKit.framework */; };
@@ -586,9 +586,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
656DD2AB2B1891DF00A362EA /* (null) in Resources */,
656DD2AC2B1891DF00A362EA /* (null) in Resources */,
656DD2AD2B1891DF00A362EA /* (null) in Resources */,
656DD2AB2B1891DF00A362EA /* BuildFile in Resources */,
656DD2AC2B1891DF00A362EA /* BuildFile in Resources */,
656DD2AD2B1891DF00A362EA /* BuildFile in Resources */,
65C400DF2A80B6B600AA3DF5 /* MaterialCommunityIcons.ttf in Resources */,
65C149872A61151B005C40F1 /* extension.bundle in Resources */,
65B5014725A672B200E2D264 /* MainInterface.storyboard in Resources */,
@@ -1015,7 +1015,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2106;
CURRENT_PROJECT_VERSION = 2101;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1084,12 +1084,12 @@
"${PODS_ROOT}/Headers/Public/#{s.name}/**",
);
INFOPLIST_FILE = Notesnook/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.9;
MARKETING_VERSION = 3.0.4;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1120,7 +1120,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2106;
CURRENT_PROJECT_VERSION = 2101;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1189,12 +1189,12 @@
"${PODS_ROOT}/Headers/Public/#{s.name}/**",
);
INFOPLIST_FILE = Notesnook/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.9;
MARKETING_VERSION = 3.0.4;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1353,7 +1353,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2106;
CURRENT_PROJECT_VERSION = 2101;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1365,7 +1365,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.9;
MARKETING_VERSION = 3.0.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1396,7 +1396,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2106;
CURRENT_PROJECT_VERSION = 2101;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1409,7 +1409,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.9;
MARKETING_VERSION = 3.0.4;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1439,7 +1439,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2106;
CURRENT_PROJECT_VERSION = 2101;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1513,7 +1513,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.9;
MARKETING_VERSION = 3.0.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1544,7 +1544,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2106;
CURRENT_PROJECT_VERSION = 2101;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1619,7 +1619,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.9;
MARKETING_VERSION = 3.0.4;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -343,9 +343,6 @@ PODS:
- React-Core
- react-native-safe-area-context (4.9.0):
- React-Core
- react-native-screenguard (1.0.0):
- React-Core
- SDWebImage (~> 5.11.1)
- react-native-share-extension (3.0.0):
- React
- react-native-sodium (1.5.4):
@@ -551,9 +548,6 @@ PODS:
- RNZipArchive/Core (6.0.9):
- React-Core
- SSZipArchive (~> 2.2)
- SDWebImage (5.11.1):
- SDWebImage/Core (= 5.11.1)
- SDWebImage/Core (5.11.1)
- SexyTooltip (1.2.5):
- pop (~> 1.0)
- SocketRocket (0.6.0)
@@ -612,7 +606,6 @@ DEPENDENCIES:
- react-native-pdf (from `../../node_modules/react-native-pdf`)
- react-native-quick-sqlite (from `../../node_modules/react-native-quick-sqlite`)
- react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`)
- react-native-screenguard (from `../../node_modules/react-native-screenguard`)
- "react-native-share-extension (from `../../node_modules/@ammarahmed/react-native-share-extension`)"
- "react-native-sodium (from `../../node_modules/@ammarahmed/react-native-sodium`)"
- react-native-theme-switch-animation (from `../../node_modules/react-native-theme-switch-animation`)
@@ -671,7 +664,6 @@ SPEC REPOS:
- MMKV
- MMKVCore
- pop
- SDWebImage
- SocketRocket
- SSZipArchive
- SwiftyRSA
@@ -762,8 +754,6 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native-quick-sqlite"
react-native-safe-area-context:
:path: "../../node_modules/react-native-safe-area-context"
react-native-screenguard:
:path: "../../node_modules/react-native-screenguard"
react-native-share-extension:
:path: "../../node_modules/@ammarahmed/react-native-share-extension"
react-native-sodium:
@@ -915,7 +905,6 @@ SPEC CHECKSUMS:
react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa
react-native-quick-sqlite: e0e23b749382a85e4b57146f753de737a6c3a9e1
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
react-native-screenguard: 8b36a3df84c76cd2b82c477f71c26fa1c8cc14a0
react-native-share-extension: faed334b1ddf165f1e576fcabd3dc1c9e748bfa9
react-native-sodium: 955bb0dc3ea05f8ea06d5e96cb89d1be7b5d7681
react-native-theme-switch-animation: 220f883f7be290e79f2ab022093ed1a7a5929e6d
@@ -960,7 +949,6 @@ SPEC CHECKSUMS:
RNSVG: d7d7bc8229af3842c9cfc3a723c815a52cdd1105
RNTooltips: 5424d4bf0b3d441104127943b1115cc7f0616b1f
RNZipArchive: 68a0c6db4b1c103f846f1559622050df254a3ade
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
SexyTooltip: 5c9b4dec52bfb317938cb0488efd9da3717bb6fd
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef

View File

@@ -66,8 +66,7 @@
"react-native-theme-switch-animation": "^0.6.0",
"@ammarahmed/react-native-background-fetch": "^4.2.2",
"react-native-image-crop-picker": "^0.40.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-screenguard": "^1.0.0"
"react-native-url-polyfill": "^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",

View File

@@ -17,12 +17,6 @@ config.dependencies['react-native-vector-icons'] = {
},
}
config.dependencies['react-native-screenguard'] = {
platforms: {
android: null,
},
}
if (isGithubRelease) {
config.dependencies["react-native-iap"] = {
platforms: {

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.0.9",
"version": "3.0.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.0.9",
"version": "3.0.3",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -7163,7 +7163,7 @@
},
"../../packages/editor-mobile/node_modules/@types/prop-types": {
"version": "15.7.11",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"../../packages/editor-mobile/node_modules/@types/q": {
@@ -7183,7 +7183,7 @@
},
"../../packages/editor-mobile/node_modules/@types/react": {
"version": "18.2.39",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -7214,7 +7214,7 @@
},
"../../packages/editor-mobile/node_modules/@types/scheduler": {
"version": "0.16.8",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"../../packages/editor-mobile/node_modules/@types/semver": {
@@ -12072,7 +12072,7 @@
},
"../../packages/editor-mobile/node_modules/immer": {
"version": "9.0.21",
"devOptional": true,
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -22461,6 +22461,7 @@
},
"../../packages/editor/node_modules/js-tokens": {
"version": "4.0.0",
"dev": true,
"license": "MIT"
},
"../../packages/editor/node_modules/json-parse-even-better-errors": {
@@ -22516,6 +22517,7 @@
},
"../../packages/editor/node_modules/loose-envify": {
"version": "1.4.0",
"dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
@@ -23037,6 +23039,7 @@
},
"../../packages/editor/node_modules/react": {
"version": "18.2.0",
"dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -23055,6 +23058,7 @@
},
"../../packages/editor/node_modules/react-dom": {
"version": "18.2.0",
"dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
@@ -23174,6 +23178,7 @@
},
"../../packages/editor/node_modules/scheduler": {
"version": "0.23.0",
"dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -28487,7 +28492,6 @@
"react-native-reanimated": "3.3.0",
"react-native-safe-area-context": "^4.3.1",
"react-native-scoped-storage": "^1.9.5",
"react-native-screenguard": "^1.0.0",
"react-native-screens": "^3.13.1",
"react-native-securerandom": "^1.0.1",
"react-native-share": "^7.2.0",
@@ -30892,6 +30896,7 @@
},
"node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -30905,6 +30910,7 @@
},
"node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31051,6 +31057,7 @@
},
"node_modules/@babel/plugin-proposal-private-property-in-object": {
"version": "7.21.0-placeholder-for-preset-env.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -31061,6 +31068,7 @@
},
"node_modules/@babel/plugin-proposal-unicode-property-regex": {
"version": "7.18.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
@@ -31106,6 +31114,7 @@
},
"node_modules/@babel/plugin-syntax-class-static-block": {
"version": "7.14.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
@@ -31142,6 +31151,7 @@
},
"node_modules/@babel/plugin-syntax-export-namespace-from": {
"version": "7.8.3",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.3"
@@ -31165,6 +31175,7 @@
},
"node_modules/@babel/plugin-syntax-import-assertions": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -31178,6 +31189,7 @@
},
"node_modules/@babel/plugin-syntax-import-attributes": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -31191,6 +31203,7 @@
},
"node_modules/@babel/plugin-syntax-import-meta": {
"version": "7.10.4",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
@@ -31201,6 +31214,7 @@
},
"node_modules/@babel/plugin-syntax-json-strings": {
"version": "7.8.3",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
@@ -31224,6 +31238,7 @@
},
"node_modules/@babel/plugin-syntax-logical-assignment-operators": {
"version": "7.10.4",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
@@ -31284,6 +31299,7 @@
},
"node_modules/@babel/plugin-syntax-private-property-in-object": {
"version": "7.14.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
@@ -31297,6 +31313,7 @@
},
"node_modules/@babel/plugin-syntax-top-level-await": {
"version": "7.14.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
@@ -31323,6 +31340,7 @@
},
"node_modules/@babel/plugin-syntax-unicode-sets-regex": {
"version": "7.18.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
@@ -31350,6 +31368,7 @@
},
"node_modules/@babel/plugin-transform-async-generator-functions": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-environment-visitor": "^7.22.5",
@@ -31407,6 +31426,7 @@
},
"node_modules/@babel/plugin-transform-class-properties": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.22.5",
@@ -31421,6 +31441,7 @@
},
"node_modules/@babel/plugin-transform-class-static-block": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.22.5",
@@ -31484,6 +31505,7 @@
},
"node_modules/@babel/plugin-transform-dotall-regex": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
@@ -31498,6 +31520,7 @@
},
"node_modules/@babel/plugin-transform-duplicate-keys": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -31511,6 +31534,7 @@
},
"node_modules/@babel/plugin-transform-dynamic-import": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31539,6 +31563,7 @@
},
"node_modules/@babel/plugin-transform-export-namespace-from": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31595,6 +31620,7 @@
},
"node_modules/@babel/plugin-transform-json-strings": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31622,6 +31648,7 @@
},
"node_modules/@babel/plugin-transform-logical-assignment-operators": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31649,6 +31676,7 @@
},
"node_modules/@babel/plugin-transform-modules-amd": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.22.5",
@@ -31678,6 +31706,7 @@
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-hoist-variables": "^7.22.5",
@@ -31694,6 +31723,7 @@
},
"node_modules/@babel/plugin-transform-modules-umd": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.22.5",
@@ -31722,6 +31752,7 @@
},
"node_modules/@babel/plugin-transform-new-target": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -31735,6 +31766,7 @@
},
"node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31749,6 +31781,7 @@
},
"node_modules/@babel/plugin-transform-numeric-separator": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31776,6 +31809,7 @@
},
"node_modules/@babel/plugin-transform-object-rest-spread": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.22.5",
@@ -31807,6 +31841,7 @@
},
"node_modules/@babel/plugin-transform-optional-catch-binding": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31821,6 +31856,7 @@
},
"node_modules/@babel/plugin-transform-optional-chaining": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
@@ -31849,6 +31885,7 @@
},
"node_modules/@babel/plugin-transform-private-methods": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.22.5",
@@ -31863,6 +31900,7 @@
},
"node_modules/@babel/plugin-transform-private-property-in-object": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.22.5",
@@ -31962,6 +32000,7 @@
},
"node_modules/@babel/plugin-transform-reserved-words": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -32053,6 +32092,7 @@
},
"node_modules/@babel/plugin-transform-typeof-symbol": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -32082,6 +32122,7 @@
},
"node_modules/@babel/plugin-transform-unicode-escapes": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
@@ -32095,6 +32136,7 @@
},
"node_modules/@babel/plugin-transform-unicode-property-regex": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
@@ -32123,6 +32165,7 @@
},
"node_modules/@babel/plugin-transform-unicode-sets-regex": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
@@ -32137,6 +32180,7 @@
},
"node_modules/@babel/preset-env": {
"version": "7.22.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.22.5",
@@ -32229,6 +32273,7 @@
},
"node_modules/@babel/preset-env/node_modules/semver": {
"version": "6.3.0",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -32251,6 +32296,7 @@
},
"node_modules/@babel/preset-modules": {
"version": "0.1.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -34900,6 +34946,7 @@
},
"node_modules/@types/eslint": {
"version": "8.40.2",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*",
@@ -34908,6 +34955,7 @@
},
"node_modules/@types/eslint-scope": {
"version": "3.7.4",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint": "*",
@@ -34916,6 +34964,7 @@
},
"node_modules/@types/estree": {
"version": "1.0.1",
"dev": true,
"license": "MIT"
},
"node_modules/@types/graceful-fs": {
@@ -35364,6 +35413,7 @@
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6",
@@ -35372,18 +35422,22 @@
},
"node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.6",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.11.6",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.6",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
@@ -35393,10 +35447,12 @@
},
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.6",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
@@ -35407,6 +35463,7 @@
},
"node_modules/@webassemblyjs/ieee754": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
@@ -35414,6 +35471,7 @@
},
"node_modules/@webassemblyjs/leb128": {
"version": "1.11.6",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@xtuc/long": "4.2.2"
@@ -35421,10 +35479,12 @@
},
"node_modules/@webassemblyjs/utf8": {
"version": "1.11.6",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
@@ -35439,6 +35499,7 @@
},
"node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
@@ -35450,6 +35511,7 @@
},
"node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
@@ -35460,6 +35522,7 @@
},
"node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
@@ -35472,6 +35535,7 @@
},
"node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.6",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
@@ -35529,10 +35593,12 @@
},
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/@yarnpkg/lockfile": {
@@ -35588,6 +35654,7 @@
},
"node_modules/acorn-import-assertions": {
"version": "1.9.0",
"dev": true,
"license": "MIT",
"peerDependencies": {
"acorn": "^8"
@@ -36692,6 +36759,7 @@
},
"node_modules/chrome-trace-event": {
"version": "1.0.3",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0"
@@ -37792,6 +37860,7 @@
},
"node_modules/enhanced-resolve": {
"version": "5.15.0",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -37904,6 +37973,7 @@
},
"node_modules/es-module-lexer": {
"version": "1.3.0",
"dev": true,
"license": "MIT"
},
"node_modules/es-set-tostringtag": {
@@ -38234,6 +38304,7 @@
},
"node_modules/eslint-scope": {
"version": "5.1.1",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
@@ -38245,6 +38316,7 @@
},
"node_modules/eslint-scope/node_modules/estraverse": {
"version": "4.3.0",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -38430,6 +38502,7 @@
},
"node_modules/esrecurse": {
"version": "4.3.0",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
@@ -38440,6 +38513,7 @@
},
"node_modules/estraverse": {
"version": "5.3.0",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -38447,6 +38521,7 @@
},
"node_modules/esutils": {
"version": "2.0.3",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@@ -39503,6 +39578,7 @@
},
"node_modules/glob-to-regexp": {
"version": "0.4.1",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/global": {
@@ -41746,6 +41822,7 @@
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
@@ -42013,6 +42090,7 @@
},
"node_modules/loader-runner": {
"version": "4.3.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.11.5"
@@ -44664,6 +44742,7 @@
},
"node_modules/randombytes": {
"version": "2.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
@@ -45254,18 +45333,6 @@
"react-native": "*"
}
},
"node_modules/react-native-screenguard": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/react-native-screenguard/-/react-native-screenguard-1.0.0.tgz",
"integrity": "sha512-fqtoJI8TxszKqrntnHW48EMb4gNVA9VCAg5UG1FcIXEyv2W70kWoMxRRFoWuCRYlBWSAWqiXO+R6Xfk/0um0cw==",
"engines": {
"node": ">= 16.0.0"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-screens": {
"version": "3.21.1",
"license": "MIT",
@@ -45447,6 +45514,7 @@
},
"node_modules/react-test-renderer": {
"version": "18.2.0",
"dev": true,
"license": "MIT",
"dependencies": {
"react-is": "^18.2.0",
@@ -45459,10 +45527,12 @@
},
"node_modules/react-test-renderer/node_modules/react-is": {
"version": "18.2.0",
"dev": true,
"license": "MIT"
},
"node_modules/react-test-renderer/node_modules/scheduler": {
"version": "0.23.0",
"dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -46298,6 +46368,7 @@
},
"node_modules/serialize-javascript": {
"version": "6.0.1",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
@@ -47353,6 +47424,7 @@
},
"node_modules/terser-webpack-plugin": {
"version": "5.3.9",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
@@ -47385,6 +47457,7 @@
},
"node_modules/terser-webpack-plugin/node_modules/jest-worker": {
"version": "27.5.1",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*",
@@ -47397,6 +47470,7 @@
},
"node_modules/terser-webpack-plugin/node_modules/supports-color": {
"version": "8.1.1",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
@@ -48141,6 +48215,7 @@
},
"node_modules/watchpack": {
"version": "2.4.0",
"dev": true,
"license": "MIT",
"dependencies": {
"glob-to-regexp": "^0.4.1",
@@ -48163,6 +48238,7 @@
},
"node_modules/webpack": {
"version": "5.88.2",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
@@ -48277,6 +48353,7 @@
},
"node_modules/webpack-sources": {
"version": "3.2.3",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.13.0"

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.0.9",
"version": "3.0.4",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -10,19 +10,3 @@ index 5de0845..1b158d8 100644
if (includeBase64) {
image.putString("data", getBase64StringFromFile(compressedImagePath));
diff --git a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
index 9f20973..5e14da8 100644
--- a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
+++ b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
@@ -595,8 +595,10 @@ - (void)qb_imagePickerController:
NSString *mimeType = [self determineMimeTypeFromImageData:imageData];
Boolean isKnownMimeType = [mimeType length] > 0;
+ Boolean isHeicOrHeif = [mimeType isEqualToString:@"image/heic"] || [mimeType isEqualToString:@"image/heif"];
+
ImageResult *imageResult = [[ImageResult alloc] init];
- if (isLossless && useOriginalWidth && useOriginalHeight && isKnownMimeType && !forceJpg) {
+ if (isLossless && useOriginalWidth && useOriginalHeight && isKnownMimeType && !forceJpg && !isHeicOrHeif) {
// Use original, unmodified image
imageResult.data = imageData;
imageResult.width = @(imgT.size.width);

View File

@@ -1,54 +0,0 @@
diff --git a/node_modules/react-native-screenguard/ios/ScreenGuard.mm b/node_modules/react-native-screenguard/ios/ScreenGuard.mm
index 9fa7d33..d2e6d68 100644
--- a/node_modules/react-native-screenguard/ios/ScreenGuard.mm
+++ b/node_modules/react-native-screenguard/ios/ScreenGuard.mm
@@ -11,7 +11,7 @@
@implementation ScreenGuard
RCT_EXPORT_MODULE(ScreenGuard)
-bool hasListeners;
+bool hasListeners_;
UITextField *textField;
UIImageView *imageView;
@@ -22,11 +22,11 @@ @implementation ScreenGuard
}
- (void)startObserving {
- hasListeners = YES;
+ hasListeners_ = YES;
}
- (void)stopObserving {
- hasListeners = NO;
+ hasListeners_ = NO;
}
- (void)secureViewWithBackgroundColor: (NSString *)color {
@@ -335,7 +335,7 @@ - (UIImage *)convertViewToImage:(UIView *)view {
queue:mainQueue
usingBlock:^(NSNotification *notification) {
- if (hasListeners && getScreenShotPath) {
+ if (hasListeners_ && getScreenShotPath) {
UIViewController *presentedViewController = RCTPresentedViewController();
UIImage *image = [self convertViewToImage:presentedViewController.view.superview];
@@ -359,7 +359,7 @@ - (UIImage *)convertViewToImage:(UIView *)view {
result = @{@"path": filePath, @"name": fileName, @"type": @"PNG"};
}
[self emit:SCREENSHOT_EVT body: result];
- } else if (hasListeners) {
+ } else if (hasListeners_) {
[self emit:SCREENSHOT_EVT body: nil];
}
}];
@@ -376,7 +376,7 @@ - (UIImage *)convertViewToImage:(UIView *)view {
queue:mainQueue
usingBlock:^(NSNotification *notification) {
- if (hasListeners) {
+ if (hasListeners_) {
[self emit:SCREEN_RECORDING_EVT body:nil];
}
}];

View File

@@ -311,7 +311,8 @@ const ShareView = () => {
type: "tiptap"
},
id: note.id,
sessionId: Date.now()
sessionId: Date.now(),
title: noteTitle.current
};
} else {
noteData = { ...note };
@@ -486,42 +487,25 @@ const ShareView = () => {
gap: 10
}}
>
{appendNoteId ? (
<Heading
style={{
flexShrink: 1,
flexGrow: 1,
fontFamily: "OpenSans-SemiBold",
fontSize: SIZE.lg,
paddingBottom: 0,
paddingTop: 0
}}
>
Save note
</Heading>
) : (
<TextInput
placeholder="Enter note title"
ref={inputRef}
style={{
flexShrink: 1,
flexGrow: 1,
fontFamily: "OpenSans-SemiBold",
fontSize: SIZE.lg,
paddingBottom: 0,
paddingTop: 0,
color: colors.primary.heading
}}
onChangeText={(value) => {
noteTitle.current = value;
}}
defaultValue={noteTitle.current}
blurOnSubmit={false}
onSubmitEditing={() => {
editorRef.current.focus();
}}
/>
)}
<TextInput
placeholder="Enter note title"
ref={inputRef}
style={{
flexShrink: 1,
flexGrow: 1,
fontFamily: "OpenSans-SemiBold",
fontSize: SIZE.lg,
paddingBottom: 0,
paddingTop: 0
}}
onChangeText={(value) => {
noteTitle.current = value;
}}
blurOnSubmit={false}
onSubmitEditing={() => {
editorRef.current.focus();
}}
/>
<Button
title="Done"
type="accent"

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/theme-builder",
"version": "1.2.0",
"version": "1.1.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/theme-builder",
"version": "1.2.0",
"version": "1.1.1",
"license": "GPL-3.0-or-later",
"dependencies": {
"@emotion/react": "11.11.1",
@@ -18,18 +18,17 @@
"@theme-ui/components": "^0.14.7",
"@theme-ui/core": "^0.14.7",
"@trpc/client": "^10.38.3",
"@types/react-dom": "^18.3.0",
"clipboard-polyfill": "^4.0.0",
"file-saver": "^2.0.5",
"katex": "^0.16.2",
"react": "18.2.0",
"react-dom": "^18.2.0"
"react-dom": "18.2.0"
},
"devDependencies": {
"@babel/core": "^7.22.5",
"@types/babel__core": "^7.20.1",
"@types/file-saver": "^2.0.5",
"@types/react": "^18.2.39",
"@types/react": "^18.2.17",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"buffer": "^6.0.3",
@@ -979,7 +978,7 @@
},
"../web": {
"name": "@notesnook/web",
"version": "3.0.6",
"version": "3.0.4",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
@@ -2027,15 +2026,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,
@@ -2108,30 +2098,25 @@
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"dev": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.2.39",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz",
"integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
"version": "18.3.0",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
"integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
"dev": true
},
"node_modules/@types/styled-system": {
"version": "5.1.16",

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/theme-builder",
"description": "Your private note taking space",
"version": "1.2.0",
"version": "1.1.1",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",
@@ -17,18 +17,18 @@
"@theme-ui/components": "^0.14.7",
"@theme-ui/core": "^0.14.7",
"@trpc/client": "^10.38.3",
"@types/react-dom": "^18.3.0",
"clipboard-polyfill": "^4.0.0",
"file-saver": "^2.0.5",
"katex": "^0.16.2",
"react": "18.2.0",
"react-dom": "^18.2.0"
"react-dom": "18.2.0"
},
"devDependencies": {
"@babel/core": "^7.22.5",
"@types/babel__core": "^7.20.1",
"@types/file-saver": "^2.0.5",
"@types/react": "^18.2.39",
"@types/react": "^18.2.17",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"buffer": "^6.0.3",

View File

@@ -2,10 +2,19 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<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="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" />
<!--
manifest.json provides metadata used when your web app is installed on a
@@ -40,26 +49,13 @@
/>
<meta name="twitter:card" content="summary_large_image" />
<title>Notesnook Theme Builder</title>
<style id="theme-colors">
#splash {
display: none !important;
}
</style>
<script type="module">
import { themeToCSS } from "@notesnook/theme";
<script nonce="7WIq8hRwApoXhctoGZZthMLYQLRNiprTwcPi6Azdf">
const colorScheme = JSON.parse(
window.localStorage.getItem("colorScheme") || '"light"'
);
const root = document.querySelector("html");
if (root) root.setAttribute("data-theme", colorScheme);
const theme = window.localStorage.getItem(`theme:${colorScheme}`);
if (theme) {
const css = themeToCSS(JSON.parse(theme));
const stylesheet = document.getElementById("theme-colors");
if (stylesheet) stylesheet.innerHTML = css;
}
</script>
<script type="module" src="/index.tsx"></script>
<style>
@@ -67,6 +63,21 @@
overscroll-behavior: none;
}
html[data-theme="dark"] {
--bg: #0f0f0f;
--fg: #fff;
--three-bars-bg: #494949;
--gradient-stop-color: #111111;
--n-letter-color: #e1e1e1;
}
html[data-theme="light"] {
--bg: #fff;
--three-bars-bg: #bebebe;
--gradient-stop-color: #fff9f9;
--n-letter-color: #000;
}
html[data-theme="light"] .react-loading-skeleton {
--base-color: var(--background-secondary);
--highlight-color: #var(--background-secondary);
@@ -77,19 +88,6 @@
--highlight-color: var(--background-secondary);
}
#splash {
background-color: var(--background);
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#splash svg {
transform: scale(1);
animation: pulse 2s infinite;
@@ -97,7 +95,7 @@
@keyframes pulse {
0% {
transform: scale(0.9);
transform: scale(0.95);
}
70% {
@@ -105,7 +103,7 @@
}
100% {
transform: scale(0.9);
transform: scale(0.95);
}
}
@@ -130,9 +128,20 @@
overflow: hidden;
}
#root {
display: flex;
flex-direction: column;
@keyframes fadeUp {
0% {
transform: translateY(500px);
opacity: 0;
}
80% {
transform: translateY(0px);
opacity: 0.7;
}
100% {
opacity: 1;
}
}
/* svg {
@@ -162,40 +171,85 @@
</style>
</head>
<body class="theme-scope-base">
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<!-- <script src="https://cdn.jsdelivr.net/npm/highlightjs@9.16.2/highlight.pack.min.js"></script>
-->
<div id="root"></div>
<svg
xmlns="http://www.w3.org/2000/svg"
style="height: 0; width: 0; z-index: -1; position: absolute"
<div
id="splash"
style="
background-color: var(--bg);
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
"
>
<symbol id="full-logo" viewBox="0 0 1000 1000">
<rect
width="1000"
height="1000"
rx="30%"
ry="30%"
style="fill: var(--paragraph)"
/>
<g transform="translate(275,176.95254)">
<svg viewBox="0 0 339 339" style="width: 150px">
<defs />
<defs>
<linearGradient
xlink:href="#a"
id="b"
x1="188.61227"
x2="193.54405"
y1="165.2058"
y2="216.81519"
gradientTransform="rotate(5 4448 -4204) scale(2.93671)"
gradientUnits="userSpaceOnUse"
/>
<linearGradient id="a">
<stop offset="0" />
<stop
offset="1"
stop-color="var(--gradient-stop-color)"
stop-opacity="0"
/>
</linearGradient>
<linearGradient
id="c"
x1="167.8"
x2="270.6"
y1="76.9"
y2="64.2"
gradientTransform="rotate(5 465 -2050) scale(1.50082)"
gradientUnits="userSpaceOnUse"
xlink:href="#a"
/>
</defs>
<g transform="translate(0 42)">
<path fill="url(#b)" d="M160 205l154 42-141 44-155-42z" />
<path fill="url(#c)" d="M160-35v240l154 42 1-253z" />
<path
style="fill: var(--background)"
d="M437.985 493.919c-17.255 50.411-51.835 93.065-97.588 120.372-45.752 27.307-99.71 37.496-152.269 28.753-52.56-8.743-100.313999-35.85-134.763-76.498C18.916 525.898.006 474.347 0 421.064v-110.25l79.121001 33.053v77.152c-.007 20.779 4.434 41.318 13.024 60.237C100.735 500.176 113.276 517.037 128.925 530.707c2.97 2.587 6.053 5.107 9.236 7.459 23.357 17.388 51.402 27.352 80.494 28.597.99 0 1.946.079 2.925.101.979.023 2.25 0 3.375 0h3.375c1.125 0 1.935 0 2.925-.101 29.081-1.248 57.116-11.203 80.471-28.575 3.173-2.351 6.255-4.86 9.237-7.447 20.556-17.98 35.656-41.366 43.582-67.5zM450 225v196.065c0 2.531 0 5.074-.158 7.605l-78.963-33.019V225c-.013-37.684-14.607-73.901-40.725-101.066-26.119-27.164003-61.735-43.168003-99.389-44.660003-37.655-1.492-74.426 11.644-102.611 36.657003-28.185999 25.013-45.599999 59.962-48.593999 97.526-.281 3.803-.439 7.662-.439 11.543v48.712L0 240.637V0h225c59.674 0 116.903 23.705 159.099 65.901C426.295 108.097 450 165.326 450 225Z"
fill="none"
stroke-width="1.2"
d="M160 205V-35m0 240L18 249m142-44l154 41"
/>
<path
fill="var(--n-letter-color)"
d="M84 109l35 54V98l21-7v91l-27 9-35-54v65l-21 6v-91z"
/>
<rect
width="86.1"
height="12.6"
x="185"
y="97"
fill="var(--three-bars-bg)"
ry="2.3"
transform="skewY(15) scale(.9669 1)"
/>
<path
fill="var(--three-bars-bg)"
d="M181 169l99 26 2 3v8c0 1-1 2-2 1l-99-26-2-3v-7c0-2 1-2 2-2zm0-47l99 27 2 2v8l-2 2-99-27c-1 0-2-1-2-3v-7l2-2z"
/>
</g>
</symbol>
<symbol id="themed-logo" viewBox="0 0 450 646.09491">
<path
style="fill: var(--paragraph)"
d="M437.985 493.919c-17.255 50.411-51.835 93.065-97.588 120.372-45.752 27.307-99.71 37.496-152.269 28.753-52.56-8.743-100.313999-35.85-134.763-76.498C18.916 525.898.006 474.347 0 421.064v-110.25l79.121001 33.053v77.152c-.007 20.779 4.434 41.318 13.024 60.237C100.735 500.176 113.276 517.037 128.925 530.707c2.97 2.587 6.053 5.107 9.236 7.459 23.357 17.388 51.402 27.352 80.494 28.597.99 0 1.946.079 2.925.101.979.023 2.25 0 3.375 0h3.375c1.125 0 1.935 0 2.925-.101 29.081-1.248 57.116-11.203 80.471-28.575 3.173-2.351 6.255-4.86 9.237-7.447 20.556-17.98 35.656-41.366 43.582-67.5zM450 225v196.065c0 2.531 0 5.074-.158 7.605l-78.963-33.019V225c-.013-37.684-14.607-73.901-40.725-101.066-26.119-27.164003-61.735-43.168003-99.389-44.660003-37.655-1.492-74.426 11.644-102.611 36.657003-28.185999 25.013-45.599999 59.962-48.593999 97.526-.281 3.803-.439 7.662-.439 11.543v48.712L0 240.637V0h225c59.674 0 116.903 23.705 159.099 65.901C426.295 108.097 450 165.326 450 225Z"
/>
</symbol>
</svg>
<div id="splash" class="hidden">
<svg style="height: 120px">
<use href="#themed-logo" />
</svg>
</div>
<div id="dialogContainer"></div>

View File

@@ -18,59 +18,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { init } from "@notesnook/web/src/bootstrap";
import { createRoot } from "react-dom/client";
import {
ErrorBoundary,
ErrorComponent
} from "@notesnook/web/src/components/error-boundary";
import { render } from "react-dom";
import { BaseThemeProvider } from "@notesnook/web/src/components/theme-provider";
import { App } from "./app";
renderApp();
async function renderApp() {
const rootElement = document.getElementById("root");
if (!rootElement) return;
const root = createRoot(rootElement);
try {
const { component, props } = await init();
const { component, props } = await init();
const { useKeyStore } = await import(
"@notesnook/web/src/interfaces/key-store"
);
await useKeyStore.getState().init();
const { default: Component } = await component();
const { default: AppLock } = await import(
"@notesnook/web/src/views/app-lock"
);
root.render(
<ErrorBoundary>
<BaseThemeProvider
onRender={() => document.getElementById("splash")?.remove()}
sx={{
display: "flex",
"#app": { flex: 1, height: "unset" },
"& > :first-child:not(#menu-wrapper)": { flex: 1 },
height: "100%"
}}
>
<AppLock>
<Component route={props?.route || "login:email"} />
</AppLock>
<App />
</BaseThemeProvider>
</ErrorBoundary>
);
} catch (e) {
root.render(
<>
<ErrorComponent
error={e}
resetErrorBoundary={() => window.location.reload()}
/>
</>
);
}
const { default: Component } = await component();
render(
<BaseThemeProvider
sx={{
display: "flex",
"#app": { flex: 1, height: "unset" },
"& > :first-child:not(#menu-wrapper)": { flex: 1 },
height: "100%"
}}
>
<Component route={props?.route || "login:email"} />
<App />
</BaseThemeProvider>,
document.getElementById("root"),
() => {
document.getElementById("splash")?.remove();
}
);
}

View File

@@ -259,15 +259,14 @@ test("unlock a note for editing", async ({ page }) => {
await page.waitForTimeout(150);
await page.reload();
await notes.waitForList();
await page.waitForTimeout(500);
const newContent = `${NOTE.content}${content}`;
const editedNote = await notes.findNote({
title: NOTE.title,
content: newContent
});
if (!editedNote) throw new Error("Could not find note.");
await editedNote.openLockedNote(PASSWORD);
await editedNote?.openLockedNote(PASSWORD);
await notes.editor.waitForLoading();
expect(await notes.editor.getContent("text")).toContain(newContent);
});

View File

@@ -14,7 +14,7 @@
<link rel="stylesheet" href="https://app.notesnook.com/assets/editor-styles.css?d=1690887574068">
<link rel="stylesheet" href="https://app.notesnook.com/assets/editor-styles.css?d=0">
<style>

View File

@@ -14,7 +14,7 @@
<link rel="stylesheet" href="https://app.notesnook.com/assets/editor-styles.css?d=1690887574068">
<link rel="stylesheet" href="https://app.notesnook.com/assets/editor-styles.css?d=0">
<style>

View File

@@ -14,7 +14,7 @@
<link rel="stylesheet" href="https://app.notesnook.com/assets/editor-styles.css?d=1690887574068">
<link rel="stylesheet" href="https://app.notesnook.com/assets/editor-styles.css?d=0">
<style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/web",
"description": "Your private note taking space",
"version": "3.0.7",
"version": "3.0.4",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",
@@ -89,8 +89,7 @@
"devDependencies": {
"@babel/core": "^7.22.5",
"@playwright/test": "^1.43.1",
"@swc/core": "^1.5.24",
"@swc/plugin-react-remove-properties": "^2.0.4",
"@swc/core": "1.3.61",
"@trpc/server": "10.38.3",
"@types/babel__core": "^7.20.1",
"@types/event-source-polyfill": "^1.0.1",
@@ -105,8 +104,8 @@
"@types/react-scroll-sync": "^0.9.0",
"@types/tinycolor2": "^1.4.3",
"@types/wicg-file-system-access": "^2020.9.6",
"@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.19",
"@vitejs/plugin-react-swc": "3.3.2",
"autoprefixer": "^10.4.14",
"better-sqlite3-multiple-ciphers": "^9.4.0",
"buffer": "^6.0.3",
"chalk": "^4.1.0",
@@ -117,13 +116,14 @@
"ip": "^1.1.8",
"lorem-ipsum": "^2.0.4",
"otplib": "^12.0.1",
"rollup": "^4.18.0",
"rollup-plugin-visualizer": "^5.12.0",
"vite": "^5.2.12",
"vite-plugin-env-compatible": "^2.0.1",
"vite-plugin-pwa": "^0.20.0",
"vite-plugin-svgr": "^4.2.0",
"vitest": "^1.6.0",
"rollup": "^3.29.4",
"rollup-plugin-visualizer": "^5.9.2",
"swc-plugin-react-remove-properties": "^0.1.4",
"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.34.6",
"workbox-core": "^7.0.0",
"workbox-expiration": "^7.0.0",
"workbox-precaching": "^7.0.0",

View File

@@ -22,8 +22,10 @@ import { Box, Flex } from "@theme-ui/components";
import { ScopedThemeProvider } from "./components/theme-provider";
import useMobile from "./hooks/use-mobile";
import useTablet from "./hooks/use-tablet";
import useDatabase from "./hooks/use-database";
import { useStore } from "./stores/app-store";
import { Toaster } from "react-hot-toast";
import { ViewLoader } from "./components/loaders/view-loader";
import NavigationMenu from "./components/navigation-menu";
import StatusBar from "./components/status-bar";
import { EditorLoader } from "./components/loaders/editor-loader";
@@ -36,13 +38,12 @@ import {
PanelResizeHandle,
ImperativePanelHandle
} from "react-resizable-panels";
import GlobalMenuWrapper from "./components/global-menu-wrapper";
new WebExtensionRelay();
// const GlobalMenuWrapper = React.lazy(
// () => import("./components/global-menu-wrapper")
// );
const GlobalMenuWrapper = React.lazy(
() => import("./components/global-menu-wrapper")
);
const AppEffects = React.lazy(() => import("./app-effects"));
const MobileAppEffects = React.lazy(() => import("./app-effects.mobile"));
const HashRouter = React.lazy(() => import("./components/hash-router"));
@@ -50,24 +51,26 @@ const HashRouter = React.lazy(() => import("./components/hash-router"));
function App() {
const isMobile = useMobile();
const [show, setShow] = useState(true);
const [isAppLoaded] = useDatabase();
const isFocusMode = useStore((store) => store.isFocusMode);
return (
<>
<Suspense fallback={<div style={{ display: "none" }} />}>
<div id="menu-wrapper">
<GlobalMenuWrapper />
</div>
<AppEffects setShow={setShow} />
{isMobile && (
<MobileAppEffects
sliderId="slider"
overlayId="overlay"
setShow={setShow}
/>
)}
</Suspense>
{isAppLoaded && (
<Suspense fallback={<div style={{ display: "none" }} />}>
<div id="menu-wrapper">
<GlobalMenuWrapper />
</div>
<AppEffects setShow={setShow} />
{isMobile && (
<MobileAppEffects
sliderId="slider"
overlayId="overlay"
setShow={setShow}
/>
)}
</Suspense>
)}
<Flex
id="app"
bg="background"
@@ -75,9 +78,13 @@ function App() {
sx={{ overflow: "hidden", flexDirection: "column", height: "100%" }}
>
{isMobile ? (
<MobileAppContents />
<MobileAppContents isAppLoaded={isAppLoaded} />
) : (
<DesktopAppContents setShow={setShow} show={show} />
<DesktopAppContents
isAppLoaded={isAppLoaded}
setShow={setShow}
show={show}
/>
)}
<Toaster containerClassName="toasts-container" />
</Flex>
@@ -114,10 +121,15 @@ function SuspenseLoader<TComponent extends React.JSXElementConstructor<any>>({
}
type DesktopAppContentsProps = {
isAppLoaded: boolean;
show: boolean;
setShow: (show: boolean) => void;
};
function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
function DesktopAppContents({
isAppLoaded,
show,
setShow
}: DesktopAppContentsProps) {
const isFocusMode = useStore((store) => store.isFocusMode);
const isTablet = useTablet();
const [isNarrow, setIsNarrow] = useState(isTablet || false);
@@ -182,7 +194,7 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
borderRight: "1px solid var(--separator)"
}}
>
<CachedRouter />
{isAppLoaded && <CachedRouter />}
</ScopedThemeProvider>
</Panel>
<PanelResizeHandle className="panel-resize-handle" />
@@ -196,7 +208,7 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
bg: "background"
}}
>
{<HashRouter />}
{isAppLoaded && <HashRouter />}
</Flex>
</Panel>
</PanelGroup>
@@ -206,7 +218,7 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
);
}
function MobileAppContents() {
function MobileAppContents({ isAppLoaded }: { isAppLoaded: boolean }) {
return (
<FlexScrollContainer
id="slider"
@@ -245,7 +257,11 @@ function MobileAppContents() {
width: "100vw"
}}
>
<CachedRouter />
<SuspenseLoader
condition={isAppLoaded}
component={CachedRouter}
fallback={<ViewLoader />}
/>
<Box
id="overlay"
sx={{
@@ -274,7 +290,7 @@ function MobileAppContents() {
<SuspenseLoader
fallback={<EditorLoader />}
component={HashRouter}
condition={true}
condition={isAppLoaded}
/>
</Flex>
</FlexScrollContainer>

View File

@@ -18,10 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "./polyfills";
import "@notesnook/core/dist/types";
import { getCurrentHash, getCurrentPath, makeURL } from "./navigation";
import Config from "./utils/config";
// import { initializeLogger, logger } from "./utils/logger";
import { initializeLogger, logger } from "./utils/logger";
import type { AuthProps } from "./views/auth";
import { initializeFeatureChecks } from "./utils/feature-check";
@@ -101,7 +102,7 @@ const sessionExpiryExceptions: Routes[] = [
function getRoute(): RouteWithPath<AuthProps> | RouteWithPath {
const path = getCurrentPath() as Routes;
// logger.info(`Getting route for path: ${path}`);
logger.info(`Getting route for path: ${path}`);
const signup = redirectToRegistration(path);
const sessionExpired = isSessionExpired(path);
@@ -128,7 +129,7 @@ function redirectToRegistration(path: Routes): RouteWithPath<AuthProps> | null {
function isSessionExpired(path: Routes): RouteWithPath<AuthProps> | null {
const isSessionExpired = Config.get("sessionExpired", false);
if (isSessionExpired && !sessionExpiryExceptions.includes(path)) {
// logger.info(`User session has expired. Routing to /sessionexpired`);
logger.info(`User session has expired. Routing to /sessionexpired`);
window.history.replaceState(
{},
@@ -142,10 +143,7 @@ function isSessionExpired(path: Routes): RouteWithPath<AuthProps> | null {
export async function init() {
await initializeFeatureChecks();
await await import("./utils/logger").then(({ initializeLogger }) =>
initializeLogger()
);
await initializeLogger();
const { path, route } = getRoute();
return { ...route, path };

View File

@@ -25,8 +25,7 @@ import { database } from "@notesnook/common";
import { createDialect } from "./sqlite";
import { isFeatureSupported } from "../utils/feature-check";
import { generatePassword } from "../utils/password-generator";
import { deriveKey, useKeyStore } from "../interfaces/key-store";
import { logManager } from "@notesnook/core/dist/logger";
import { deriveKey } from "../interfaces/key-store";
const db = database;
async function initializeDatabase(persistence: DatabasePersistence) {
@@ -34,6 +33,7 @@ async function initializeDatabase(persistence: DatabasePersistence) {
const { FileStorage } = await import("../interfaces/fs");
const { Compressor } = await import("../utils/compressor");
const { useKeyStore } = await import("../interfaces/key-store");
let databaseKey = await useKeyStore.getState().getValue("databaseKey");
if (!databaseKey) {
@@ -58,8 +58,7 @@ async function initializeDatabase(persistence: DatabasePersistence) {
database.setup({
sqliteOptions: {
dialect: (name, init) =>
createDialect(persistence === "memory" ? ":memory:" : name, true, init),
dialect: createDialect,
...(IS_DESKTOP_APP || isFeatureSupported("opfs")
? { journalMode: "WAL", lockingMode: "exclusive" }
: {
@@ -98,15 +97,10 @@ async function initializeDatabase(persistence: DatabasePersistence) {
// });
// }
console.log("loading db");
await db.init();
console.log("db loaded");
window.addEventListener("beforeunload", async () => {
if (IS_DESKTOP_APP) {
await db.sql().destroy();
await logManager?.close();
}
window.addEventListener("beforeunload", () => {
if (IS_DESKTOP_APP) db.sql().destroy();
});
logger.measure("Database initialization");

View File

@@ -213,7 +213,7 @@ export function showLogoutConfirmation() {
return confirm({
title: `Logout?`,
message:
"Logging out will clear all data stored on THIS DEVICE. Make sure you have synced all your changes before logging out.",
"Logging out will delete all local data and reset the app. Make sure you have synced your data before logging out.",
positiveButtonText: "Yes",
negativeButtonText: "No"
});

View File

@@ -100,26 +100,17 @@ export async function exportNotes(
};
}
});
if (result instanceof Error) {
confirm({
title: `Export failed`,
message: result.stack || result.message,
positiveButtonText: "Okay"
});
return false;
} else {
confirm({
title: `Exported ${result.count} notes`,
message:
result.errors.length > 0
? `Export completed with ${result.errors.length} errors:
confirm({
title: `Exported ${result.count} notes`,
message:
result.errors.length > 0
? `Export completed with ${result.errors.length} errors:
${result.errors.map((e, i) => `${i + 1}. ${e.message}`).join("\n")}`
: "Export completed with 0 errors.",
positiveButtonText: "Okay"
});
return true;
}
: "Export completed with 0 errors.",
positiveButtonText: "Okay"
});
return true;
}
const FORMAT_TO_EXT = {

View File

@@ -77,19 +77,12 @@ export async function introduceFeatures() {
export const DEFAULT_CONTEXT = { colors: [], tags: [], notebook: {} };
export async function createBackup(
options: {
rescueMode?: boolean;
noVerify?: boolean;
} = {}
) {
const { rescueMode, noVerify } = options;
export async function createBackup(rescueMode = false) {
const { isLoggedIn } = useUserStore.getState();
const { encryptBackups, toggleEncryptBackups } = useSettingStore.getState();
if (!isLoggedIn && encryptBackups) toggleEncryptBackups();
const verified =
rescueMode || encryptBackups || noVerify || (await verifyAccount());
const verified = rescueMode || encryptBackups || (await verifyAccount());
if (!verified) {
showToast("error", "Could not create a backup: user verification failed.");
return false;

View File

@@ -18,10 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import hotkeys from "hotkeys-js";
import { navigate } from "../navigation";
// import { store as themestore } from "../stores/theme-store";
import { GlobalKeyboard } from "../utils/keyboard";
import { useEditorStore } from "../stores/editor-store";
import { useStore as useSearchStore } from "../stores/search-store";
import { useEditorManager } from "../components/editor/manager";
const KEYMAP = [
// {
@@ -58,24 +57,11 @@ const KEYMAP = [
keys: ["command+f", "ctrl+f"],
description: "Search all notes",
global: false,
action: (e: KeyboardEvent) => {
const isInEditor =
e.target instanceof HTMLElement &&
!!e.target?.closest(".editor-container");
if (isInEditor) {
const activeSession = useEditorStore.getState().getActiveSession();
if (activeSession?.type === "readonly") {
e.preventDefault();
const editor = useEditorManager
.getState()
.getEditor(activeSession.id);
editor?.editor?.startSearch();
}
return;
}
action: (e) => {
if (e.target?.classList.contains("ProseMirror")) return;
e.preventDefault();
useSearchStore.setState({ isSearching: true, searchType: "notes" });
navigate("/search/notes");
}
}
// {
@@ -151,11 +137,7 @@ export function registerKeyMap() {
KEYMAP.forEach((key) => {
hotkeys(
key.keys.join(","),
{
element: key.global
? (GlobalKeyboard as unknown as HTMLElement)
: document.body
},
{ element: key.global ? GlobalKeyboard : window },
key.action
);
});

View File

@@ -177,7 +177,7 @@ function isIgnored(key: keyof typeof NoticesData) {
let openedToast: { hide: () => void } | null = null;
async function saveBackup() {
if (IS_DESKTOP_APP) {
await createBackup({ noVerify: true });
await createBackup();
} else if (isUserPremium() && !IS_TESTING) {
if (openedToast !== null) return;
openedToast = showToast(

View File

@@ -18,93 +18,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {
SqliteDriver as KSqliteDriver,
SqliteDialectConfig,
Dialect,
SqliteAdapter,
SqliteIntrospector,
SqliteQueryCompiler,
DatabaseConnection,
QueryResult,
CompiledQuery,
Driver
SqliteQueryCompiler
} from "kysely";
import { desktop } from "../desktop-bridge";
import Worker from "./sqlite.worker.desktop.ts?worker";
import type { SQLiteWorker } from "./sqlite.worker.desktop";
import { wrap, Remote } from "comlink";
import { Mutex } from "async-mutex";
class SqliteDriver implements Driver {
connection?: DatabaseConnection;
private connectionMutex = new Mutex();
worker: Remote<SQLiteWorker> = wrap<SQLiteWorker>(new Worker());
constructor(private readonly config: { name: string }) {}
async init(): Promise<void> {
const path = await desktop!.integration.resolvePath.query({
filePath: `userData/${this.config.name}.sql`
});
await this.worker.open(path);
this.connection = new SqliteWorkerConnection(this.worker);
class SqliteDriver extends KSqliteDriver {
constructor(private readonly config: SqliteDialectConfig & { name: string }) {
super(config);
}
async acquireConnection(): Promise<DatabaseConnection> {
if (!this.connection) throw new Error("Driver not initialized.");
// SQLite only has one single connection. We use a mutex here to wait
// until the single connection has been released.
await this.connectionMutex.waitForUnlock();
await this.connectionMutex.acquire();
return this.connection;
}
async beginTransaction(connection: DatabaseConnection): Promise<void> {
await connection.executeQuery(CompiledQuery.raw("begin"));
}
async commitTransaction(connection: DatabaseConnection): Promise<void> {
await connection.executeQuery(CompiledQuery.raw("commit"));
}
async rollbackTransaction(connection: DatabaseConnection): Promise<void> {
await connection.executeQuery(CompiledQuery.raw("rollback"));
}
async releaseConnection(): Promise<void> {
this.connectionMutex.release();
}
async destroy(): Promise<void> {
await this.worker.close();
}
async delete() {
const path = await desktop!.integration.resolvePath.query({
filePath: `userData/${this.config.name}.sql`
});
await this.worker.delete(path);
await desktop?.integration.deleteFile.query(path);
await this.destroy();
}
}
class SqliteWorkerConnection implements DatabaseConnection {
constructor(private readonly worker: Remote<SQLiteWorker>) {}
streamQuery<R>(): AsyncIterableIterator<QueryResult<R>> {
throw new Error("wasqlite driver doesn't support streaming");
}
async executeQuery<R>(
compiledQuery: CompiledQuery<unknown>
): Promise<QueryResult<R>> {
const { parameters, sql } = compiledQuery;
return this.worker.run(sql, parameters as any) as unknown as QueryResult<R>;
}
}
export const createDialect = (name: string, _encrypted: boolean): Dialect => {
export const createDialect = (name: string): Dialect => {
return {
createDriver: () =>
new SqliteDriver({
name
name,
database: async () => {
const path = await desktop!.integration.resolvePath.query({
filePath: `userData/${name}.sql`
});
return window.createSQLite3Database(path).unsafeMode(true);
}
}),
createAdapter: () => new SqliteAdapter(),
createIntrospector: (db) => new SqliteIntrospector(db),

View File

@@ -34,7 +34,6 @@ declare module "kysely" {
export const createDialect = (
name: string,
encrypted: boolean,
init?: () => Promise<void>
): Dialect => {
return {
@@ -42,7 +41,6 @@ export const createDialect = (
new WaSqliteWorkerDriver({
async: !isFeatureSupported("opfs"),
dbName: name,
encrypted,
init
}),
createAdapter: () => new SqliteAdapter(),

View File

@@ -83,7 +83,7 @@ export class SharedService<T extends object> extends EventTarget {
}
activate(
portProviderFunc: () => Promise<{ port: MessagePort; onclose: () => void }>,
portProviderFunc: () => MessagePort | Promise<MessagePort>,
onClientConnected: () => Promise<void>
) {
if (this.#onDeactivate) return;
@@ -97,7 +97,7 @@ export class SharedService<T extends object> extends EventTarget {
navigator.locks
.request(LOCK_NAME, { signal: this.#onDeactivate.signal }, async () => {
// Get the port to request client ports.
const { port, onclose } = await portProviderFunc();
const port = await portProviderFunc();
port.start();
// Listen for client requests. A separate BroadcastChannel
@@ -159,7 +159,6 @@ export class SharedService<T extends object> extends EventTarget {
// Release the lock only on user abort or context destruction.
return new Promise((_, reject) => {
this.#onDeactivate?.signal.addEventListener("abort", () => {
onclose();
broadcastChannel.close();
reject(this.#onDeactivate?.signal.reason);
});

View File

@@ -1,25 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { SQLite } from "@notesnook/desktop";
import { expose } from "comlink";
export type SQLiteWorker = typeof SQLite.prototype;
const db = new SQLite();
expose(db);

View File

@@ -32,230 +32,219 @@ type PreparedStatement = {
columns: string[];
};
class _SQLiteWorker {
sqlite!: SQLiteAPI;
db: number | undefined = undefined;
vfs: IDBBatchAtomicVFS | AccessHandlePoolVFS | null = null;
initialized = false;
preparedStatements: Map<string, PreparedStatement> = new Map();
retryCounter: Record<string, number> = {};
constructor(
private readonly dbName: string,
private readonly encrypted: boolean
) {
console.log("new sqlite worker", dbName, encrypted);
let sqlite: SQLiteAPI;
let db: number | undefined = undefined;
let vfs: IDBBatchAtomicVFS | AccessHandlePoolVFS | null = null;
let initialized = false;
const preparedStatements: Map<string, PreparedStatement> = new Map();
const retryCounter: Record<string, number> = {};
console.log("new sqlite worker");
async function open(dbName: string, async: boolean, url?: string) {
if (db) {
console.error("Database is already initialized", db);
return;
}
async open(async: boolean, url?: string) {
if (this.db) {
console.error("Database is already initialized", this.db);
return;
}
const option = url ? { locateFile: () => url } : {};
const sqliteModule = async
? await import("./wa-sqlite-async").then(
({ default: SQLiteAsyncESMFactory }) => SQLiteAsyncESMFactory(option)
)
: await import("./wa-sqlite").then(({ default: SQLiteSyncESMFactory }) =>
SQLiteSyncESMFactory(option)
);
this.sqlite = Factory(sqliteModule);
this.vfs = await this.getVFS(this.dbName, async);
this.sqlite.vfs_register(this.vfs, false);
this.db = await this.sqlite.open_v2(
this.dbName,
undefined,
`multipleciphers-${this.vfs.name}`
);
}
/**
* Wrapper function for preparing SQL statements with caching
* to avoid unnecessary computations.
*/
async prepare(sql: string): Promise<PreparedStatement | undefined> {
if (!this.db) throw new Error("Database is not initialized.");
try {
const cached = this.preparedStatements.get(sql);
if (cached !== undefined) return cached;
const str = this.sqlite.str_new(this.db, sql);
const prepared = await this.sqlite.prepare_v2(
this.db,
this.sqlite.str_value(str)
const option = url ? { locateFile: () => url } : {};
const sqliteModule = async
? await import("./wa-sqlite-async").then(
({ default: SQLiteAsyncESMFactory }) => SQLiteAsyncESMFactory(option)
)
: await import("./wa-sqlite").then(({ default: SQLiteSyncESMFactory }) =>
SQLiteSyncESMFactory(option)
);
if (!prepared) return;
sqlite = Factory(sqliteModule);
vfs = await getVFS(dbName, async);
const statement: PreparedStatement = {
stmt: prepared.stmt,
columns: this.sqlite.column_names(prepared.stmt)
};
this.preparedStatements.set(sql, statement);
sqlite.vfs_register(vfs, false);
db = await sqlite.open_v2(dbName, undefined, `multipleciphers-${vfs.name}`);
}
this.sqlite.str_finish(str);
/**
* Wrapper function for preparing SQL statements with caching
* to avoid unnecessary computations.
*/
async function prepare(sql: string) {
if (!db) throw new Error("Database is not initialized.");
try {
const cached = preparedStatements.get(sql);
if (cached !== undefined) return cached;
// reset retry count on success
this.retryCounter[sql] = 0;
return statement;
} catch (ex) {
console.error(ex);
const str = sqlite.str_new(db, sql);
const prepared = await sqlite.prepare_v2(db, sqlite.str_value(str));
if (!prepared) return;
// statement prepare process can be flaky so retry at least 5 times
// before giving up.
if (this.retryCounter[sql] < 5) {
this.retryCounter[sql] = (this.retryCounter[sql] || 0) + 1;
console.warn("Failed to prepare statement. Retrying:", sql);
return this.prepare(sql);
} else this.retryCounter[sql] = 0;
if (ex instanceof Error || ex instanceof SQLiteError)
ex.message += ` (query: ${sql})`;
throw ex;
}
}
async exec(sql: string, mode: RunMode, parameters?: SQLiteCompatibleType[]) {
const prepared = await this.prepare(sql);
if (!prepared) return [];
try {
if (parameters) this.sqlite.bind_collection(prepared.stmt, parameters);
// fast path for exec statements
if (mode === "exec") {
while ((await this.sqlite.step(prepared.stmt)) === SQLITE_ROW);
return [];
}
const rows: Record<string, SQLiteCompatibleType>[] = [];
while ((await this.sqlite.step(prepared.stmt)) === SQLITE_ROW) {
const row = this.sqlite.row(prepared.stmt);
const acc: Record<string, SQLiteCompatibleType> = {};
row.forEach((v, i) => (acc[prepared.columns[i]] = v));
rows.push(acc);
}
return rows;
} catch (e) {
if (e instanceof Error || e instanceof SQLiteError)
e.message += ` (query: ${sql})`;
throw e;
} finally {
await this.sqlite
.reset(prepared.stmt)
// we must clear/destruct the prepared statement if it can't be reset
.catch(() =>
this.sqlite
.finalize(prepared.stmt)
// ignore error (we will just prepare a new statement)
.catch(console.error)
.finally(() => this.preparedStatements.delete(sql))
);
}
}
async run<R>(
mode: RunMode,
sql: string,
parameters?: SQLiteCompatibleType[]
): Promise<QueryResult<R>> {
if (this.encrypted && !sql.startsWith("PRAGMA key")) {
await this.waitForDatabase();
}
if (!this.db) throw new Error("No database is not opened.");
const rows = (await this.exec(sql, mode, parameters)) as R[];
if (mode === "query") return { rows };
// initialize the database after it has been successfully decrypted.
// all queries prior to that must wait otherwise we get the
// "file is not a database" error
if (this.encrypted && sql.startsWith("PRAGMA key")) await this.initialize();
return {
insertId: BigInt(this.sqlite.last_insert_rowid(this.db)),
numAffectedRows: BigInt(this.sqlite.changes(this.db)),
rows: mode === "raw" ? rows : []
const statement: PreparedStatement = {
stmt: prepared.stmt,
columns: sqlite.column_names(prepared.stmt)
};
}
preparedStatements.set(sql, statement);
async close() {
if (!this.db) return;
sqlite.str_finish(str);
for (const [_, prepared] of this.preparedStatements) {
await this.sqlite.finalize(prepared.stmt);
}
this.preparedStatements.clear();
await this.sqlite.close(this.db);
await this.vfs?.close();
// reset retry count on success
retryCounter[sql] = 0;
return statement;
} catch (ex) {
console.error(ex);
this.db = undefined;
this.initialized = false;
}
// statement prepare process can be flaky so retry at least 5 times
// before giving up.
if (retryCounter[sql] < 5) {
retryCounter[sql] = (retryCounter[sql] || 0) + 1;
console.warn("Failed to prepare statement. Retrying:", sql);
return prepare(sql);
} else retryCounter[sql] = 0;
async export(dbName: string, async: boolean) {
const vfs = await this.getVFS(dbName, async);
const stream = new ReadableStream(new DatabaseSource(vfs, dbName));
return transfer(stream, [stream]);
}
async delete(dbName: string, async: boolean) {
await this.close();
if (this.vfs) await this.vfs.delete();
else await (await this.getVFS(dbName, async)).delete();
}
async getVFS(dbName: string, async: boolean) {
const vfs = async
? await import("./IDBBatchAtomicVFS").then(
({ IDBBatchAtomicVFS }) =>
new IDBBatchAtomicVFS(dbName, { durability: "strict" })
)
: await import("./AccessHandlePoolVFS").then(
({ AccessHandlePoolVFS }) => new AccessHandlePoolVFS(dbName)
);
if ("isReady" in vfs) await vfs.isReady;
return vfs;
}
async initialize() {
self.dispatchEvent(
new MessageEvent("message", {
data: { type: "databaseInitialized", dbName: this.dbName }
})
);
console.log("Database initialized", this.db);
this.initialized = true;
}
async waitForDatabase() {
// if the database hasn't yet been initialized.
if (!this.initialized) {
console.log("Waiting for database to be initialized...", this.db);
return await new Promise<boolean>((resolve) =>
self.addEventListener("message", (ev) => {
if (
ev.data.type === "databaseInitialized" &&
ev.data.dbName === this.dbName
)
resolve(true);
})
);
}
return true;
if (ex instanceof Error || ex instanceof SQLiteError)
ex.message += ` (query: ${sql})`;
throw ex;
}
}
export type SQLiteWorker = typeof _SQLiteWorker.prototype;
async function run(
sql: string,
mode: RunMode,
parameters?: SQLiteCompatibleType[]
) {
const prepared = await prepare(sql);
if (!prepared) return [];
try {
if (parameters) sqlite.bind_collection(prepared.stmt, parameters);
// fast path for exec statements
if (mode === "exec") {
while ((await sqlite.step(prepared.stmt)) === SQLITE_ROW);
return [];
}
const rows: Record<string, SQLiteCompatibleType>[] = [];
while ((await sqlite.step(prepared.stmt)) === SQLITE_ROW) {
const row = sqlite.row(prepared.stmt);
const acc: Record<string, SQLiteCompatibleType> = {};
row.forEach((v, i) => (acc[prepared.columns[i]] = v));
rows.push(acc);
}
return rows;
} catch (e) {
if (e instanceof Error || e instanceof SQLiteError)
e.message += ` (query: ${sql})`;
throw e;
} finally {
await sqlite
.reset(prepared.stmt)
// we must clear/destruct the prepared statement if it can't be reset
.catch(() =>
sqlite
.finalize(prepared.stmt)
// ignore error (we will just prepare a new statement)
.catch(console.error)
.finally(() => preparedStatements.delete(sql))
);
}
}
async function exec<R>(
mode: RunMode,
sql: string,
parameters?: SQLiteCompatibleType[]
): Promise<QueryResult<R>> {
if (!sql.startsWith("PRAGMA key")) {
await waitForDatabase();
}
if (!db) throw new Error("No database is not opened.");
const rows = (await run(sql, mode, parameters)) as R[];
if (mode === "query") return { rows };
// initialize the database after it has been successfully decrypted.
// all queries prior to that must wait otherwise we get the
// "file is not a database" error
if (sql.startsWith("PRAGMA key")) await initialize();
return {
insertId: BigInt(sqlite.last_insert_rowid(db)),
numAffectedRows: BigInt(sqlite.changes(db)),
rows: mode === "raw" ? rows : []
};
}
async function close() {
if (!db) return;
for (const [_, prepared] of preparedStatements) {
await sqlite.finalize(prepared.stmt);
}
preparedStatements.clear();
await sqlite.close(db);
await vfs?.close();
db = undefined;
initialized = false;
}
async function exportDatabase(dbName: string, async: boolean) {
const vfs = await getVFS(dbName, async);
const stream = new ReadableStream(new DatabaseSource(vfs, dbName));
return transfer(stream, [stream]);
}
async function deleteDatabase(dbName: string, async: boolean) {
await close();
if (vfs) await vfs.delete();
else await (await getVFS(dbName, async)).delete();
}
async function getVFS(dbName: string, async: boolean) {
const vfs = async
? await import("./IDBBatchAtomicVFS").then(
({ IDBBatchAtomicVFS }) =>
new IDBBatchAtomicVFS(dbName, { durability: "strict" })
)
: await import("./AccessHandlePoolVFS").then(
({ AccessHandlePoolVFS }) => new AccessHandlePoolVFS(dbName)
);
if ("isReady" in vfs) await vfs.isReady;
return vfs;
}
async function initialize() {
self.dispatchEvent(
new MessageEvent("message", { data: { type: "databaseInitialized" } })
);
console.log("Database initialized", db);
initialized = true;
}
const worker = {
close,
open,
run: exec,
export: exportDatabase,
delete: deleteDatabase
};
export type SQLiteWorker = typeof worker;
addEventListener("message", async (event) => {
if (!event.data.type) {
const worker = new _SQLiteWorker(event.data.dbName, event.data.encrypted);
await worker.open(event.data.async, event.data.uri);
await worker.open(event.data.dbName, event.data.async, event.data.uri);
const providerPort = createSharedServicePort(worker);
postMessage(null, [providerPort]);
self.addEventListener("beforeunload", () => worker.close());
}
});
async function waitForDatabase() {
// if the database hasn't yet been initialized.
if (!initialized) {
console.log("Waiting for database to be initialized...", db);
return await new Promise<boolean>((resolve) =>
self.addEventListener("message", (ev) => {
if (ev.data.type === "databaseInitialized") resolve(true);
})
);
}
return true;
}

View File

@@ -26,12 +26,7 @@ import SQLiteAsyncURI from "./wa-sqlite-async.wasm?url";
import { Mutex } from "async-mutex";
import { SharedService } from "./shared-service";
type Config = {
dbName: string;
async: boolean;
encrypted: boolean;
init?: () => Promise<void>;
};
type Config = { dbName: string; async: boolean; init?: () => Promise<void> };
const servicePool = new Map<
string,
@@ -60,6 +55,7 @@ export class WaSqliteWorkerDriver implements Driver {
if (closed) {
console.log("Already activated. Reinitializing...");
await service.proxy.open(
this.config.dbName,
this.config.async,
this.config.async ? SQLiteAsyncURI : SQLiteSyncURI
);
@@ -76,24 +72,19 @@ export class WaSqliteWorkerDriver implements Driver {
service.activate(
() =>
new Promise<{ port: MessagePort; onclose: () => void }>((resolve) => {
new Promise<MessagePort>((resolve) => {
console.log("initializing worker");
this.needsInitialization = true;
const worker = new Worker();
worker.addEventListener(
"message",
(event) =>
resolve({
port: event.ports[0],
onclose: () => worker.terminate()
}),
(event) => resolve(event.ports[0]),
{ once: true }
);
worker.postMessage({
dbName: this.config.dbName,
async: this.config.async,
encrypted: this.config.encrypted,
uri: this.config.async ? SQLiteAsyncURI : SQLiteSyncURI
});
}),

View File

@@ -55,7 +55,7 @@ type TaskProgress = {
type ProgressReportCallback = (progress: TaskProgress) => void;
export class TaskManager {
static async startTask<T>(task: TaskDefinition<T>): Promise<T | Error> {
static async startTask<T>(task: TaskDefinition<T>): Promise<T> {
switch (task.type) {
case "status": {
const statusTask = task;
@@ -74,7 +74,7 @@ export class TaskManager {
return result;
}
case "modal": {
return await showProgressDialog<T | Error>({
return await showProgressDialog<T>({
title: task.title,
subtitle: task.subtitle,
action: task.action

View File

@@ -307,7 +307,7 @@ function CalltoAction({ action, variant, sx, dismissAnnouncement }) {
break;
}
case "backup": {
await createBackup();
await createBackup(true);
break;
}
default: {

View File

@@ -26,7 +26,7 @@ type ContentToggle = {
onToggle: () => void;
label: string;
dateEdited: number;
resolveConflict: (saveCopy: boolean) => void;
resolveConflict: (options: { saveCopy: boolean }) => void;
readonly: boolean;
sx: FlexProps["sx"];
};
@@ -50,7 +50,7 @@ function ContentToggle(props: ContentToggle) {
<Button
variant="accent"
mr={2}
onClick={() => resolveConflict(true)}
onClick={() => resolveConflict({ saveCopy: true })}
p={1}
px={2}
>
@@ -61,7 +61,7 @@ function ContentToggle(props: ContentToggle) {
variant={isOtherSelected ? "error" : "accent"}
onClick={() => {
if (isOtherSelected) {
resolveConflict(false);
resolveConflict({ saveCopy: false });
} else {
onToggle();
}

View File

@@ -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 { useCallback, useLayoutEffect, useRef, useState } from "react";
import { useLayoutEffect, useRef, useState } from "react";
import { Flex, Text, Button } from "@theme-ui/components";
import { Copy, Restore } from "../icons";
import ContentToggle from "./content-toggle";
@@ -45,24 +45,6 @@ function DiffViewer(props: DiffViewerProps) {
);
const root = useRef<HTMLDivElement>(null);
const onResolveContent = useCallback(
(saveCopy: boolean) => {
const toKeep =
selectedContent === 1 ? session.content?.conflicted : session.content;
const toCopy =
selectedContent === 1 ? session.content : session.content?.conflicted;
if (!toKeep) return;
resolveConflict({
note: session.note,
toKeep,
toCopy: saveCopy ? toCopy : undefined,
toKeepDateEdited: toKeep.dateEdited,
dateResolved: conflictedContent!.dateModified
});
},
[conflictedContent, selectedContent, session.content, session.note]
);
useLayoutEffect(() => {
setConflictedContent((c) => {
if (c?.dateEdited === session.content?.conflicted?.dateEdited) return c;
@@ -203,7 +185,15 @@ function DiffViewer(props: DiffViewerProps) {
isSelected={selectedContent === 0}
isOtherSelected={selectedContent === 1}
onToggle={() => setSelectedContent((s) => (s === 0 ? -1 : 0))}
resolveConflict={onResolveContent}
resolveConflict={({ saveCopy }) => {
resolveConflict({
note: session.note,
toKeep: content.data,
toCopy: saveCopy ? conflictedContent : undefined,
toKeepDateEdited: content.dateEdited,
dateResolved: conflictedContent.dateModified
});
}}
sx={{
borderStyle: "solid",
borderWidth: 0,
@@ -273,7 +263,15 @@ function DiffViewer(props: DiffViewerProps) {
<>
<ContentToggle
readonly={session.type === "diff"}
resolveConflict={onResolveContent}
resolveConflict={({ saveCopy }) => {
resolveConflict({
note: session.note,
toKeep: conflictedContent.data,
toCopy: saveCopy ? content : undefined,
toKeepDateEdited: conflictedContent.dateEdited,
dateResolved: conflictedContent.dateModified
});
}}
label={
session.type === "diff"
? "Current version"
@@ -328,31 +326,31 @@ async function resolveConflict({
dateResolved
}: {
note: Note;
toKeep: ContentItem;
toKeep: string;
toCopy?: ContentItem;
toKeepDateEdited: number;
dateResolved?: number;
}) {
await db.content.add({
id: note.contentId,
dateResolved,
sessionId: `${Date.now()}`,
...(toKeep.locked
? { data: toKeep.data, locked: true }
: { data: toKeep.data, locked: false })
});
await db.notes.add({
id: note.id,
dateEdited: toKeepDateEdited,
conflicted: false
});
await db.content.add({
id: note.contentId,
data: toKeep,
type: "tiptap",
dateResolved,
sessionId: `${Date.now()}`
});
if (toCopy) {
await createCopy(note, toCopy);
}
await notesStore.refresh();
useEditorStore.getState().openSession(note.id, { force: true });
}
async function createCopy(note: Note, content: ContentItem) {

View File

@@ -34,11 +34,9 @@ import {
Publish,
Published,
Readonly,
Redo,
Search,
TableOfContents,
Trash,
Undo,
Unlock
} from "../icons";
import { ScrollContainer } from "@notesnook/ui";
@@ -81,8 +79,8 @@ export function EditorActionBar() {
const activeSession = useEditorStore((store) =>
store.activeSessionId ? store.getSession(store.activeSessionId) : undefined
);
const editorManager = useEditorManager((store) =>
activeSession?.id ? store.editors[activeSession?.id] : undefined
const editor = useEditorManager((store) =>
activeSession?.id ? store.editors[activeSession?.id]?.editor : undefined
);
const isLoggedIn = useUserStore((store) => store.isLoggedIn);
const monographs = useMonographStore((store) => store.monographs);
@@ -90,18 +88,6 @@ export function EditorActionBar() {
activeSession && db.monographs.isPublished(activeSession.id);
const tools = [
{
title: "Undo",
icon: Undo,
enabled: editorManager?.canUndo,
onClick: () => editorManager?.editor?.undo()
},
{
title: "Redo",
icon: Redo,
enabled: editorManager?.canRedo,
onClick: () => editorManager?.editor?.redo()
},
{
title: isNotePublished ? "Published" : "Publish",
icon: isNotePublished ? Published : Publish,
@@ -166,9 +152,10 @@ export function EditorActionBar() {
activeSession &&
activeSession.type !== "new" &&
activeSession.type !== "locked" &&
activeSession.type !== "readonly" &&
activeSession.type !== "diff" &&
activeSession.type !== "conflicted",
onClick: editorManager?.editor?.startSearch
onClick: editor?.startSearch
},
{
title: "Properties",

View File

@@ -127,8 +127,11 @@ export default function TabsView() {
return (
<>
{!hasNativeTitlebar ? (
<EditorActionBarPortal />
{IS_DESKTOP_APP ? (
ReactDOM.createPortal(
<EditorActionBar />,
document.getElementById("titlebar-portal-container")!
)
) : (
<Flex sx={{ px: 1 }}>
<EditorActionBar />
@@ -150,7 +153,12 @@ export default function TabsView() {
<PanelGroup direction="horizontal" autoSaveId={"editor-panels"}>
<Panel id="editor-panel" className="editor-pane" order={1}>
{sessions.map((session) => (
<Freeze key={session.id} freeze={session.id !== activeSessionId}>
<Freeze
key={session.id}
freeze={
session.needsHydration || session.id !== activeSessionId
}
>
{session.type === "locked" ? (
<UnlockNoteView session={session} />
) : session.type === "conflicted" || session.type === "diff" ? (
@@ -225,8 +233,7 @@ const MemoizedEditorView = React.memo(
EditorView,
(prev, next) =>
prev.session.id === next.session.id &&
prev.session.type === next.session.type &&
prev.session.needsHydration === next.session.needsHydration
prev.session.type === next.session.type
);
function EditorView({
session
@@ -264,7 +271,7 @@ function EditorView({
const isContent =
item.type === "tiptap" && item.noteId === session.note.id;
const isNote = item.type === "note" && item.id === session.note.id;
if (isContent && lastChangedTime.current < item.dateModified) {
if (isContent && lastChangedTime.current < item.dateEdited) {
if (!item.locked) return editor.updateContent(item.data);
const result = await db.vault
@@ -295,12 +302,6 @@ function EditorView({
};
}, [editor]);
useEffect(() => {
if (!session.needsHydration && session.content) {
editor?.updateContent(session.content.data);
}
}, [editor, session.needsHydration]);
return (
<Flex
ref={root}
@@ -458,7 +459,6 @@ export function Editor(props: EditorProps) {
<EditorChrome {...props}>
<Tiptap
id={id}
isHydrating={!!session.needsHydration}
nonce={nonce}
readonly={readonly}
content={content}
@@ -466,8 +466,7 @@ export function Editor(props: EditorProps) {
corsHost: Config.get("corsProxy", "https://cors.notesnook.com")
}}
onLoad={(editor) => {
editor = editor || useEditorManager.getState().getEditor(id)?.editor;
if (editor) restoreSelection(editor, id);
restoreSelection(editor, id);
restoreScrollPosition(session);
}}
onSelectionChange={({ from, to }) =>
@@ -774,10 +773,8 @@ function restoreScrollPosition(session: EditorSession) {
}
function restoreSelection(editor: IEditor, id: string) {
setTimeout(() => {
editor.focus({
position: Config.get(`${id}:selection`, { from: 0, to: 0 })
});
editor.focus({
position: Config.get(`${id}:selection`, { from: 0, to: 0 })
});
}
@@ -814,7 +811,7 @@ function UnlockNoteView(props: UnlockNoteViewProps) {
throw new Error("note with this id does not exist.");
useEditorStore.getState().addSession({
type: session.note.readonly ? "readonly" : "default",
type: "default",
locked: true,
id: session.id,
note: session.note,
@@ -829,9 +826,3 @@ function UnlockNoteView(props: UnlockNoteViewProps) {
</div>
);
}
function EditorActionBarPortal() {
const container = document.getElementById("titlebar-portal-container");
if (!container) return null;
return ReactDOM.createPortal(<EditorActionBar />, container);
}

View File

@@ -67,7 +67,7 @@ export type OnChangeHandler = (
type TipTapProps = {
id: string;
editorContainer: () => HTMLElement | undefined;
onLoad?: (editor?: IEditor) => void;
onLoad?: (editor: IEditor) => void;
onChange?: OnChangeHandler;
onContentChange?: () => void;
onSelectionChange?: (range: { from: number; to: number }) => void;
@@ -344,6 +344,7 @@ function TipTap(props: TipTapProps) {
};
}, []);
if (readonly) return null;
return (
<>
<ScopedThemeProvider
@@ -371,11 +372,8 @@ function TipTap(props: TipTapProps) {
function TiptapWrapper(
props: PropsWithChildren<
Omit<TipTapProps, "editorContainer" | "theme" | "fontSize" | "fontFamily">
> & {
isHydrating?: boolean;
}
>
) {
const { onLoad, isHydrating } = props;
const theme = useThemeStore((store) =>
store.colorScheme === "dark" ? store.darkTheme : store.lightTheme
);
@@ -400,40 +398,27 @@ function TiptapWrapper(
theme.scopes.base.primary.paragraph;
}, [theme]);
useEffect(() => {
if (!isHydrating) {
onLoad?.();
containerRef.current
?.querySelector(".editor-loading-container")
?.classList.add("hidden");
}
}, [isHydrating]);
return (
<Flex
ref={containerRef}
sx={{
flex: 1,
flexDirection: "column",
".tiptap.ProseMirror": { pb: 150 },
".editor-container": { opacity: isHydrating ? 0 : 1 },
".editor-loading-container.hidden": { display: "none" }
".tiptap.ProseMirror": { pb: 150 }
}}
>
<TipTap
{...props}
onLoad={(editor) => {
if (!isHydrating) {
onLoad?.(editor);
containerRef.current
?.querySelector(".editor-loading-container")
?.classList.add("hidden");
}
props.onLoad?.(editor);
containerRef.current
?.querySelector(".editor-loading-container")
?.remove();
}}
editorContainer={() => {
if (editorContainerRef.current) return editorContainerRef.current;
const editorContainer = document.createElement("div");
editorContainer.classList.add("selectable", "editor-container");
editorContainer.classList.add("selectable");
editorContainer.style.flex = "1";
editorContainer.style.cursor = "text";
editorContainer.style.color =
@@ -442,7 +427,6 @@ function TiptapWrapper(
editorContainer.style.fontSize = `${editorConfig.fontSize}px`;
editorContainer.style.fontFamily =
getFontById(editorConfig.fontFamily)?.font || "sans-serif";
editorContainer.tabIndex = -1;
editorContainerRef.current = editorContainer;
return editorContainer;
}}

View File

@@ -17,42 +17,15 @@ 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 { PropsWithChildren, useEffect } from "react";
import { PropsWithChildren } from "react";
import { ErrorText } from "../error-text";
import { BaseThemeProvider } from "../theme-provider";
import { Button, Flex, Text } from "@theme-ui/components";
import {
ErrorBoundary as RErrorBoundary,
FallbackProps,
useErrorBoundary
FallbackProps
} from "react-error-boundary";
import { useKeyStore } from "../../interfaces/key-store";
export function GlobalErrorHandler(props: PropsWithChildren) {
const { showBoundary } = useErrorBoundary();
useEffect(() => {
function handleError(e: ErrorEvent) {
const error = new Error(e.message);
error.stack = `${e.filename}:${e.lineno}:${e.colno}`;
showBoundary(e.error || error);
}
function handleUnhandledRejection(e: PromiseRejectionEvent) {
showBoundary(e.reason);
}
window.addEventListener("unhandledrejection", handleUnhandledRejection);
window.addEventListener("error", handleError);
return () => {
window.removeEventListener(
"unhandledrejection",
handleUnhandledRejection
);
window.removeEventListener("error", handleError);
};
}, [showBoundary]);
return <>{props.children}</>;
}
import { createDialect } from "../../common/sqlite";
export function ErrorBoundary(props: PropsWithChildren) {
return (
@@ -189,19 +162,19 @@ function getErrorHelp(props: FallbackProps) {
if (
errorText.includes("file is not a database") ||
errorText.includes("unsupported file format") ||
errorText.includes("database disk image is malformed") ||
errorText.includes("null function or function signature mismatch") ||
errorText.includes("malformed database schema") ||
/table ".+?" already exists/.test(errorText) ||
errorText.includes("corrupted migrations:")
errorText.includes("malformed database schema")
) {
return {
explanation: `This error usually means the database file is either corrupt or it could not be decrypted.`,
action:
"This error can only be fixed by wiping & reseting the database. Beware that this will wipe all your data inside the database with no way to recover it later on. This WILL NOT change/affect/delete/wipe your data on the server but ONLY on this device.",
"This error can only be fixed by wiping & reseting the database. Beware that this will wipe all your data inside the database with no way to recover it later on.",
fix: async () => {
const { createDialect } = await import("../../common/sqlite");
const { useKeyStore } = await import("../../interfaces/key-store");
await useKeyStore.getState().clear();
const dialect = createDialect("notesnook", true);
const dialect = createDialect("notesnook");
const driver = dialect.createDriver();
await driver.delete();
resetErrorBoundary();
@@ -211,27 +184,17 @@ function getErrorHelp(props: FallbackProps) {
return {
explanation: `This error means the at rest encryption key could not be decrypted. This can be due to data corruption or implementation change.`,
action:
"This error can only be fixed by wiping & reseting the Key Store and the database. This WILL NOT change/affect/delete/wipe your data on the server but ONLY on this device.",
"This error can only be fixed by wiping & reseting the Key Store and the database.",
fix: async () => {
const { createDialect } = await import("../../common/sqlite");
const { useKeyStore } = await import("../../interfaces/key-store");
await useKeyStore.getState().clear();
const dialect = createDialect("notesnook", true);
const dialect = createDialect("notesnook");
const driver = dialect.createDriver();
await driver.delete();
resetErrorBoundary();
}
};
} else if (errorText.includes("database disk image is malformed")) {
return {
explanation: `This error usually means the search index is corrupted.`,
action:
"This error can be fixed by rebuilding the search index. This action won't result in any kind of data loss.",
fix: async () => {
const { db } = await import("../../common/db");
await db.lookup.rebuild();
resetErrorBoundary();
}
};
}
}

View File

@@ -340,9 +340,9 @@ export class Lightbox extends React.Component<LightboxProps> {
overflow: "hidden",
alignItems: "center",
justifyContent: "flex-end",
height: !hasNativeTitlebar ? TITLE_BAR_HEIGHT : "auto",
height: IS_DESKTOP_APP ? TITLE_BAR_HEIGHT : "auto",
pr:
!hasNativeTitlebar && getPlatform() !== "darwin"
IS_DESKTOP_APP && getPlatform() !== "darwin"
? "calc(100vw - env(titlebar-area-width))"
: 0
}}

View File

@@ -27,6 +27,28 @@ const Lines = [1, 2].map(() => getRandomArbitrary(40, 90));
export const ListLoader = memo(function ListLoader() {
return (
<>
<Flex
sx={{ py: 1, alignItems: "center", justifyContent: "center", px: 1 }}
>
<Box sx={{ height: 38 }}>
<Skeleton enableAnimation={false} width={38} height={38} circle />
</Box>
<Flex
sx={{
flex: 1,
ml: 1,
flexDirection: "column",
justifyContent: "center"
}}
>
<Box sx={{ height: 14 }}>
<Skeleton enableAnimation={false} inline height={14} />
</Box>
<Box sx={{ mt: 1, height: 10 }}>
<Skeleton enableAnimation={false} inline height={10} />
</Box>
</Flex>
</Flex>
{Lines.map((width) => (
<Box key={width} sx={{ py: 2, px: 1 }}>
<Skeleton

View File

@@ -71,7 +71,6 @@ import { usePersistentState } from "../../hooks/use-persistent-state";
import { MenuItem } from "@notesnook/ui";
import { Notebook, Tag } from "@notesnook/core";
import { handleDrop } from "../../common/drop-handler";
import { Menu } from "../../hooks/use-menu";
type Route = {
id: string;
@@ -173,24 +172,6 @@ function NavigationMenu(props: NavigationMenuProps) {
const getSidebarItems = useCallback(async () => {
return [
{
key: "reset-sidebar",
type: "button",
title: "Reset sidebar",
onClick: () => {
db.settings
.setSideBarHiddenItems("routes", [])
.then(() => db.settings.setSideBarHiddenItems("colors", []))
.then(() => db.settings.setSideBarOrder("colors", []))
.then(() => db.settings.setSideBarOrder("routes", []))
.then(() => db.settings.setSideBarOrder("shortcuts", []))
.then(() => {
setHiddenRoutes([]);
setHiddenColors([]);
});
}
},
{ type: "separator", key: "sep" },
...toMenuItems(
orderItems(routes, db.settings.getSideBarOrder("routes")),
hiddenRoutes,
@@ -199,7 +180,7 @@ function NavigationMenu(props: NavigationMenuProps) {
.setSideBarHiddenItems("routes", ids)
.then(() => setHiddenRoutes(ids))
),
{ type: "separator", key: "sep", isHidden: colors.length <= 0 },
{ type: "separator", key: "sep" },
...toMenuItems(
orderItems(colors, db.settings.getSideBarOrder("colors")),
hiddenColors,
@@ -244,10 +225,6 @@ function NavigationMenu(props: NavigationMenuProps) {
justifyContent: "space-between"
}}
px={0}
onContextMenu={async (e) => {
e.preventDefault();
Menu.openMenu(await getSidebarItems());
}}
>
<FlexScrollContainer
style={{

View File

@@ -181,11 +181,7 @@ function NavigationItem(
) : !isTablet && count !== undefined ? (
<Text
variant="subBody"
sx={{
mr: 1,
px: "3px",
borderRadius: "default"
}}
sx={{ mr: 1, bg: "hover", px: "3px", borderRadius: "default" }}
>
{count > 100 ? "100+" : count}
</Text>

View File

@@ -96,7 +96,6 @@ import { Context } from "../list-container/types";
import { SchemeColors } from "@notesnook/theme";
import { writeToClipboard } from "../../utils/clipboard";
import Vault from "../../common/vault";
import { isUserPremium } from "../../hooks/use-is-user-premium";
type NoteProps = NoteResolvedData & {
item: NoteType;
@@ -336,7 +335,6 @@ const menuItems: (
ids?: string[],
context?: { color?: Color; locked?: boolean }
) => MenuItem[] = (note, ids = [], context) => {
const isPro = isUserPremium();
// const isSynced = db.notes.note(note.id)?.synced();
return [
@@ -373,7 +371,6 @@ const menuItems: (
//isDisabled: !isSynced,
title: "Lock",
isChecked: context?.locked,
isDisabled: !isPro,
icon: Lock.path,
onClick: async () => {
const { unlock, lock } = store.get();
@@ -383,7 +380,8 @@ const menuItems: (
} else if (await unlock(note.id)) {
showToast("success", "Note unlocked successfully!");
}
}
},
isPro: true
},
{
type: "button",
@@ -460,12 +458,11 @@ const menuItems: (
title: format.title,
tooltip: `Export as ${format.title} - ${format.subtitle}`,
icon: format.icon.path,
isDisabled:
(format.type !== "txt" && !isPro) ||
(format.type === "pdf" && ids.length > 1),
isDisabled: format.type === "pdf" && ids.length > 1,
// ? "Multiple notes cannot be exported as PDF."
// : false,
multiSelect: true,
isPro: format.type !== "txt",
onClick: async () => {
if (ids.length === 1) {
return await exportNote(note, {
@@ -480,7 +477,8 @@ const menuItems: (
}
}))
},
multiSelect: true
multiSelect: true,
isPro: true
},
{
type: "button",
@@ -526,6 +524,8 @@ const menuItems: (
type: "button",
key: "duplicate",
title: "Duplicate",
//!isSynced ||
isDisabled: context?.locked,
icon: Duplicate.path,
onClick: () => store.get().duplicate(...ids),
multiSelect: true
@@ -652,8 +652,7 @@ function notebooksMenuItems(ids: string[]): MenuItem[] {
}
});
if (notebookShortcuts.size > 0 || notebooks.size > 0)
menuItems.push({ key: "sep3", type: "separator" });
menuItems.push({ key: "sep3", type: "separator" });
if (notebookShortcuts.size > 0) {
notebookShortcuts.forEach((notebook) => {

View File

@@ -31,7 +31,6 @@ function Notice() {
if (!notices) return null;
return notices.slice().sort((a, b) => a.priority - b.priority)[0];
}, [notices]);
if (!notice) return null;
const NoticeData = NoticesData[notice.type];
return (

View File

@@ -239,10 +239,6 @@ export function PdfPreview(props: PdfPreviewProps) {
onZoom={(e) => {
if (hash) setPDFConfig(hash, { scale: e.scale });
}}
transformGetDocumentParams={(options) => {
(options as any).isEvalSupported = false;
return options;
}}
// onDocumentAskPassword={(e) => {
// e.verifyPassword("failed");
// }}

View File

@@ -57,7 +57,7 @@ function Placeholder(props: PlaceholderProps) {
</Flex>
<Text variant="subBody" sx={{ fontSize: "body", mt: 1 }}>
{toTitleCase(syncStatus.type || "sync")}ing {syncStatus.progress}{" "}
{toTitleCase(syncStatus.type || "syncing")}ing {syncStatus.progress}{" "}
items
</Text>
</Flex>

View File

@@ -23,9 +23,9 @@ import { Cross, Check, Loading } from "../../components/icons";
import { useStore as useUserStore } from "../../stores/user-store";
import { useStore as useThemeStore } from "../../stores/theme-store";
import { useTheme } from "@emotion/react";
import Rocket from "../../assets/rocket.svg?react";
import WorkAnywhere from "../../assets/workanywhere.svg?react";
import WorkLate from "../../assets/worklate.svg?react";
import { ReactComponent as Rocket } from "../../assets/rocket.svg";
import { ReactComponent as WorkAnywhere } from "../../assets/workanywhere.svg";
import { ReactComponent as WorkLate } from "../../assets/worklate.svg";
import Field from "../../components/field";
import { hardNavigate } from "../../navigation";
import { Features } from "./features";

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Text, Flex, Button } from "@theme-ui/components";
import { Loading } from "../../components/icons";
import Nomad from "../../assets/nomad.svg?react";
import { ReactComponent as Nomad } from "../../assets/nomad.svg";
import { Period, Plan } from "./types";
import { PLAN_METADATA, usePlans } from "./plans";
import { useEffect } from "react";

View File

@@ -89,15 +89,32 @@ const features: Record<FeatureKeys, Feature> = {
)
}
]
: IS_DESKTOP_APP
? [
: [
{
title: "Use native titlebar",
title: "Bi-directional note linking",
subtitle: "You can now link any 2 notes for easier referencing."
},
{
title: "Tabs",
subtitle:
"You can now disable the custom titlebar from Settings > Desktop integration and use your operating system's native title bar."
"Open multiple notes side by side for faster multi-tasking."
},
{
title: "Nested notebooks",
subtitle:
"Create subnotebooks inside notebooks upto unlimited depth."
},
{
title: "At rest encryption",
subtitle:
"All your data will now be stored encrypted on your device to keep you safe even if your device gets compromised."
},
{
title: "App lock",
subtitle:
"App lock is now here on desktop/web apps! Put a lock on the app, and keep it safe from intruders."
}
]
: [],
],
cta: {
title: "Got it",
icon: Checkmark,

View File

@@ -46,8 +46,8 @@ import { phone } from "phone";
import { db } from "../../common/db";
import FileSaver from "file-saver";
import { writeText } from "clipboard-polyfill";
import MFA from "../../assets/mfa.svg?react";
import Fallback2FA from "../../assets/fallback2fa.svg?react";
import { ReactComponent as MFA } from "../../assets/mfa.svg";
import { ReactComponent as Fallback2FA } from "../../assets/fallback2fa.svg";
import {
Authenticator,
StepComponent,

View File

@@ -27,11 +27,11 @@ import {
Github,
Loading
} from "../components/icons";
import E2E from "../assets/e2e.svg?react";
import Note from "../assets/note2.svg?react";
import Nomad from "../assets/nomad.svg?react";
import WorkAnywhere from "../assets/workanywhere.svg?react";
import Friends from "../assets/cause.svg?react";
import { ReactComponent as E2E } from "../assets/e2e.svg";
import { ReactComponent as Note } from "../assets/note2.svg";
import { ReactComponent as Nomad } from "../assets/nomad.svg";
import { ReactComponent as WorkAnywhere } from "../assets/workanywhere.svg";
import { ReactComponent as Friends } from "../assets/cause.svg";
import LightUI from "../assets/light1.png";
import DarkUI from "../assets/dark1.png";
import GooglePlay from "../assets/play.png";

View File

@@ -19,8 +19,6 @@ 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 { showToast } from "../../utils/toast";
import { desktop } from "../../common/desktop-bridge";
export const DesktopIntegrationSettings: SettingsGroup[] = [
{
@@ -128,42 +126,6 @@ export const DesktopIntegrationSettings: SettingsGroup[] = [
})
}
]
},
{
key: "use-native-titlebar",
title: "Use native titlebar",
description:
"Use native OS titlebar instead of replacing it with a custom one. Requires app restart for changes to take effect.",
onStateChange: (listener) =>
useSettingStore.subscribe(
(s) => s.desktopIntegrationSettings,
listener
),
components: [
{
type: "toggle",
isToggled: () =>
!!useSettingStore.getState().desktopIntegrationSettings
?.nativeTitlebar,
toggle: () => {
useSettingStore.getState().setDesktopIntegration({
nativeTitlebar:
!useSettingStore.getState().desktopIntegrationSettings
?.nativeTitlebar
});
showToast(
"success",
"Restart the app for changes to take effect.",
[
{
text: "Restart now",
onClick: () => desktop?.integration.restart.query()
}
]
);
}
}
]
}
]
}

View File

@@ -244,102 +244,98 @@ function SettingsSideBar(props: SettingsSideBarProps) {
}}
data-test-id="settings-navigation-menu"
>
<ScopedThemeProvider scope="navigationMenu" injectCssVars={false}>
<Flex
<Flex
sx={{
flexDirection: "column",
display: "flex",
overflow: "hidden"
}}
>
<Input
id="search"
name="search"
placeholder="Search"
data-test-id="settings-search"
sx={{
flexDirection: "column",
display: "flex",
overflow: "hidden"
m: 2,
mb: 2,
width: "auto",
bg: "background",
py: "7px"
}}
>
<Input
id="search"
name="search"
placeholder="Search"
data-test-id="settings-search"
sx={{
m: 2,
mb: 2,
width: "auto",
bg: "background",
py: "7px"
}}
onChange={(e) => {
const query = e.target.value.toLowerCase().trim();
if (!query)
return onNavigate(
SettingsGroups.filter((g) => g.section === route)
);
onChange={(e) => {
const query = e.target.value.toLowerCase().trim();
if (!query)
return onNavigate(
SettingsGroups.filter((g) => g.section === route)
);
const groups: SettingsGroup[] = [];
for (const group of SettingsGroups) {
const isTitleMatch =
typeof group.header === "string" &&
group.header.toLowerCase().includes(query);
const isSectionMatch = group.section.includes(query);
const groups: SettingsGroup[] = [];
for (const group of SettingsGroups) {
const isTitleMatch =
typeof group.header === "string" &&
group.header.toLowerCase().includes(query);
const isSectionMatch = group.section.includes(query);
if (isTitleMatch || isSectionMatch) {
groups.push(group);
continue;
}
const settings = group.settings.filter((setting) => {
const description =
typeof setting.description === "function"
? setting.description()
: setting.description;
return [
description || "",
setting.keywords?.join(" ") || "",
setting.title
]
.join(" ")
?.toLowerCase()
.includes(query);
});
if (!settings.length) continue;
groups.push({ ...group, settings });
if (isTitleMatch || isSectionMatch) {
groups.push(group);
continue;
}
onNavigate(groups);
}}
/>
{sectionGroups.map((group) => (
<Flex key={group.key} sx={{ flexDirection: "column", mb: 2 }}>
<Text
variant={"subBody"}
sx={{
fontWeight: "bold",
color: "paragraph",
mx: 3,
mb: 1
}}
>
{group.title}
</Text>
{group.sections.map(
(section) =>
(!section.isHidden || !section.isHidden()) && (
<NavigationItem
key={section.key}
icon={section.icon}
title={section.title}
selected={section.key === route}
onClick={() => {
onNavigate(
SettingsGroups.filter(
(g) => g.section === section.key
)
);
setRoute(section.key);
}}
/>
)
)}
</Flex>
))}
</Flex>
</ScopedThemeProvider>
const settings = group.settings.filter((setting) => {
const description =
typeof setting.description === "function"
? setting.description()
: setting.description;
return [
description || "",
setting.keywords?.join(" ") || "",
setting.title
]
.join(" ")
?.toLowerCase()
.includes(query);
});
if (!settings.length) continue;
groups.push({ ...group, settings });
}
onNavigate(groups);
}}
/>
{sectionGroups.map((group) => (
<Flex key={group.key} sx={{ flexDirection: "column", mb: 2 }}>
<Text
variant={"subBody"}
sx={{
fontWeight: "bold",
color: "paragraph",
mx: 3,
mb: 1
}}
>
{group.title}
</Text>
{group.sections.map(
(section) =>
(!section.isHidden || !section.isHidden()) && (
<NavigationItem
key={section.key}
icon={section.icon}
title={section.title}
selected={section.key === route}
onClick={() => {
onNavigate(
SettingsGroups.filter((g) => g.section === section.key)
);
setRoute(section.key);
}}
/>
)
)}
</Flex>
))}
</Flex>
</FlexScrollContainer>
);
}

View File

@@ -135,8 +135,7 @@ export const ProfileSettings: SettingsGroup[] = [
{
key: "logout",
title: "Logout",
description:
"Logging out will clear all data stored on THIS DEVICE. Make sure you have synced all your changes before logging out.",
description: "Logging out will clear all data on this device.",
keywords: [],
components: [
{

View File

@@ -32,7 +32,6 @@ declare global {
var IS_BETA: boolean;
var APP_TITLE: string;
var IS_THEME_BUILDER: boolean;
var hasNativeTitlebar: boolean;
interface AuthenticationExtensionsClientInputs {
prf?: {

View File

@@ -17,12 +17,50 @@ 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 { useEffect, useState } from "react";
import { initializeDatabase } from "../common/db";
import { useErrorBoundary } from "react-error-boundary";
import "../utils/analytics";
import "../app.css";
// if (import.meta.env.PROD) {
// console.log = () => {};
// }
const memory = {
isDatabaseLoaded: false
};
export default function useDatabase(persistence: "db" | "memory" = "db") {
const [isAppLoaded, setIsAppLoaded] = useState(memory.isDatabaseLoaded);
const { showBoundary } = useErrorBoundary();
useEffect(() => {
loadDatabase(persistence)
.then(() => setIsAppLoaded(true))
.catch((e) => showBoundary(e));
function handleError(e: ErrorEvent) {
const error = new Error(e.message);
error.stack = `${e.filename}:${e.lineno}:${e.colno}`;
showBoundary(e.error || error);
}
function handleUnhandledRejection(e: PromiseRejectionEvent) {
showBoundary(e.reason);
}
window.addEventListener("unhandledrejection", handleUnhandledRejection);
window.addEventListener("error", handleError);
return () => {
window.removeEventListener(
"unhandledrejection",
handleUnhandledRejection
);
window.removeEventListener("error", handleError);
};
}, [persistence]);
return [isAppLoaded];
}
export async function loadDatabase(persistence: "db" | "memory" = "db") {
if (memory.isDatabaseLoaded) return;

View File

@@ -50,9 +50,6 @@ export function useWindowControls() {
isMaximized,
isFullscreen,
hasNativeWindowControls:
!IS_DESKTOP_APP ||
hasNativeTitlebar ||
getPlatform() === "darwin" ||
getPlatform() === "win32"
!IS_DESKTOP_APP || getPlatform() === "darwin" || getPlatform() === "win32"
};
}

View File

@@ -38,8 +38,28 @@
<meta name="twitter:card" content="summary_large_image" />
<title>Notesnook</title>
<style id="theme-colors"></style>
<script type="module" src="./index.ts"></script>
<style id="theme-colors">
#splash {
display: none !important;
}
</style>
<script type="module">
import { themeToCSS } from "@notesnook/theme";
const colorScheme = JSON.parse(
window.localStorage.getItem("colorScheme") || '"light"'
);
const root = document.querySelector("html");
if (root) root.setAttribute("data-theme", colorScheme);
const theme = window.localStorage.getItem(`theme:${colorScheme}`);
if (theme) {
const css = themeToCSS(JSON.parse(theme));
const stylesheet = document.getElementById("theme-colors");
if (stylesheet) stylesheet.innerHTML = css;
}
</script>
<script type="module" src="/index.tsx"></script>
<style>
html {
overscroll-behavior: none;

View File

@@ -1,68 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "./app.css";
import { AppEventManager, AppEvents } from "./common/app-events";
import { register } from "./service-worker-registration";
import { getServiceWorkerVersion } from "./utils/version";
import { register as registerStreamSaver } from "./utils/stream-saver/mitm";
import { ThemeDark, ThemeLight, themeToCSS } from "@notesnook/theme";
import Config from "./utils/config";
const colorScheme = JSON.parse(
window.localStorage.getItem("colorScheme") || '"light"'
);
const root = document.querySelector("html");
if (root) root.setAttribute("data-theme", colorScheme);
const theme =
colorScheme === "dark"
? Config.get("theme:dark", ThemeDark)
: Config.get("theme:light", ThemeLight);
const stylesheet = document.getElementById("theme-colors");
if (theme) {
const css = themeToCSS(theme);
if (stylesheet) stylesheet.innerHTML = css;
} else stylesheet?.remove();
if (!IS_DESKTOP_APP && !IS_TESTING) {
// logger.info("Initializing service worker...");
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
register({
onUpdate: async (registration: ServiceWorkerRegistration) => {
if (!registration.waiting) return;
const { formatted } = await getServiceWorkerVersion(registration.waiting);
AppEventManager.publish(AppEvents.updateDownloadCompleted, {
version: formatted
});
},
onSuccess() {
registerStreamSaver();
}
});
// window.addEventListener("beforeinstallprompt", () => showInstallNotice());
}
import("./root").then(({ startApp }) => {
startApp();
});

103
apps/web/src/index.tsx Normal file
View File

@@ -0,0 +1,103 @@
/*
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 { createRoot } from "react-dom/client";
import { Routes, init } from "./bootstrap";
import { logger } from "./utils/logger";
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";
import { ErrorBoundary, ErrorComponent } from "./components/error-boundary";
import { TitleBar } from "./components/title-bar";
renderApp();
async function renderApp() {
const rootElement = document.getElementById("root");
if (!rootElement) return;
const root = createRoot(rootElement);
try {
const { component, props, path } = await init();
const { useKeyStore } = await import("./interfaces/key-store");
await useKeyStore.getState().init();
if (serviceWorkerWhitelist.includes(path)) await initializeServiceWorker();
const { default: Component } = await component();
const { default: AppLock } = await import("./views/app-lock");
root.render(
<>
{IS_DESKTOP_APP ? <TitleBar /> : null}
<ErrorBoundary>
<BaseThemeProvider
onRender={() => document.getElementById("splash")?.remove()}
sx={{ bg: "background", flex: 1, overflow: "hidden" }}
>
<AppLock>
<Component route={props?.route || "login:email"} />
</AppLock>
</BaseThemeProvider>
</ErrorBoundary>
</>
);
} catch (e) {
root.render(
<>
{IS_DESKTOP_APP ? <TitleBar /> : null}
<ErrorComponent
error={e}
resetErrorBoundary={() => window.location.reload()}
/>
</>
);
}
}
const serviceWorkerWhitelist: Routes[] = ["default"];
async function initializeServiceWorker() {
if (!IS_DESKTOP_APP && !IS_TESTING) {
logger.info("Initializing service worker...");
const serviceWorker = await import("./service-worker-registration");
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.register({
onUpdate: async (registration: ServiceWorkerRegistration) => {
if (!registration.waiting) return;
const { formatted } = await getServiceWorkerVersion(
registration.waiting
);
AppEventManager.publish(AppEvents.updateDownloadCompleted, {
version: formatted
});
},
onSuccess() {
register();
}
});
// window.addEventListener("beforeinstallprompt", () => showInstallNotice());
}
}
if (import.meta.hot) import.meta.hot.accept();

View File

@@ -123,8 +123,8 @@ const decoder = new TextDecoder();
*/
class KeyStore extends BaseStore<KeyStore> {
#secretStore!: IKVStore;
#metadataStore!: IKVStore;
#secretStore: IKVStore;
#metadataStore: IKVStore;
#keyId = "key";
#wrappingKeyId = "wrappingKey";
#key?: CryptoKey;
@@ -134,25 +134,25 @@ class KeyStore extends BaseStore<KeyStore> {
isLocked = false;
constructor(
private readonly dbName: string,
dbName: string,
setState: SetState<KeyStore>,
get: GetState<KeyStore>
) {
super(setState, get);
this.#metadataStore =
isFeatureSupported("indexedDB") && isFeatureSupported("clonableCryptoKey")
? new IndexedDBKVStore(`${dbName}-metadata`, "metadata")
: new MemoryKVStore();
this.#secretStore =
isFeatureSupported("indexedDB") && isFeatureSupported("clonableCryptoKey")
? new IndexedDBKVStore(`${dbName}-secrets`, "secrets")
: new MemoryKVStore();
}
activeCredentials = () => this.get().credentials.filter((c) => c.active);
init = async () => {
this.#metadataStore =
isFeatureSupported("indexedDB") && isFeatureSupported("clonableCryptoKey")
? new IndexedDBKVStore(`${this.dbName}-metadata`, "metadata")
: new MemoryKVStore();
this.#secretStore =
isFeatureSupported("indexedDB") && isFeatureSupported("clonableCryptoKey")
? new IndexedDBKVStore(`${this.dbName}-secrets`, "secrets")
: new MemoryKVStore();
const credentials = await this.getCredentials();
const secrets = Object.fromEntries(
await this.#secretStore.entries<EncryptedData>()

View File

@@ -20,8 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useState } from "react";
import EventManager from "@notesnook/core/dist/utils/event-manager";
import Config from "../utils/config";
import type { HashRoute } from "./hash-routes";
import type { ReplaceParametersInPath } from "./types";
import { HashRoute } from "./hash-routes";
import { ReplaceParametersInPath } from "./types";
export function navigate(
url: string,

Some files were not shown because too many files have changed in this diff Show More