mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 11:08:47 +02:00
Compare commits
85 Commits
feat-custo
...
background
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4f2786e55 | ||
|
|
c6612a8e5f | ||
|
|
356142bb22 | ||
|
|
dad1742e80 | ||
|
|
c68ed0dc52 | ||
|
|
978afa0899 | ||
|
|
14d57f200b | ||
|
|
2f4f351fb6 | ||
|
|
1344ba3821 | ||
|
|
21f9930cd0 | ||
|
|
0789f0c075 | ||
|
|
8e1d7e097c | ||
|
|
f3a977c070 | ||
|
|
c6fad05c56 | ||
|
|
72a99ff751 | ||
|
|
1fcc4aa0b4 | ||
|
|
3a97cdeb3d | ||
|
|
7cf6441b9c | ||
|
|
cdd3075e4f | ||
|
|
162d16b2c7 | ||
|
|
8e0559ca85 | ||
|
|
e92e54684e | ||
|
|
9321aa1732 | ||
|
|
5daeda890c | ||
|
|
f6dbf253fa | ||
|
|
acf993f06e | ||
|
|
4b4a388f5f | ||
|
|
3fd216bfd4 | ||
|
|
a2bbde30e6 | ||
|
|
2e4035cd7b | ||
|
|
e705793e0a | ||
|
|
37c0107660 | ||
|
|
5bfcf93713 | ||
|
|
920ff08f16 | ||
|
|
6b85150acb | ||
|
|
0e243fbeb4 | ||
|
|
24a5953e0f | ||
|
|
ec1e868510 | ||
|
|
7450585737 | ||
|
|
9a09332182 | ||
|
|
3992aa5dc0 | ||
|
|
14f9d0ac7f | ||
|
|
f1a615515b | ||
|
|
d4ff1dd46c | ||
|
|
6276bd5db3 | ||
|
|
34736307c3 | ||
|
|
60056f3fab | ||
|
|
f99a00789b | ||
|
|
fcfda03fb2 | ||
|
|
cd4c2934e0 | ||
|
|
396508d4b9 | ||
|
|
ffc03460b9 | ||
|
|
4315503893 | ||
|
|
1c9e42fbfc | ||
|
|
f8f992d11e | ||
|
|
8aa36f632a | ||
|
|
ac89e3851a | ||
|
|
430ad22263 | ||
|
|
c46ba25408 | ||
|
|
ddcddbc8a8 | ||
|
|
24b1ada636 | ||
|
|
98a3b12bb8 | ||
|
|
26dca8b1d8 | ||
|
|
7dc9529981 | ||
|
|
32e36325b3 | ||
|
|
7b554bf781 | ||
|
|
4bcc08d046 | ||
|
|
77bb9726ab | ||
|
|
94f3dc0c52 | ||
|
|
be19ff1171 | ||
|
|
6d52f9a6a3 | ||
|
|
b3c6bedc13 | ||
|
|
94bc61f636 | ||
|
|
68406a2a3a | ||
|
|
8a0bf20e11 | ||
|
|
1055b6f13a | ||
|
|
4994ad77b4 | ||
|
|
e0712ce2f3 | ||
|
|
559801ba80 | ||
|
|
b69dd3b217 | ||
|
|
8328575806 | ||
|
|
30de9cabe0 | ||
|
|
506cb2dd47 | ||
|
|
2f5a3db1c1 | ||
|
|
06f4fee088 |
@@ -15,8 +15,6 @@ apps/mobile/e2e/
|
||||
|
||||
# web
|
||||
apps/web/public/an.js
|
||||
apps/web/src/common/sqlite/wa-sqlite-async.js
|
||||
apps/web/src/common/sqlite/wa-sqlite.js
|
||||
|
||||
# editor
|
||||
packages/editor/styles/
|
||||
|
||||
176
apps/desktop/package-lock.json
generated
176
apps/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -24,8 +24,8 @@
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "25.9.8",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron": "25.9.3",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
"tree-kill": "^1.2.2",
|
||||
@@ -173,11 +173,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/asar": {
|
||||
"version": "3.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.8.tgz",
|
||||
"integrity": "sha512-cmskk5M06ewHMZAplSiF4AlME3IrnnZhKnWbtwKVLRkdJkKyUVjMLhDIiPIx/+6zQWVlKX/LtmK9xDme7540Sg==",
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.4.tgz",
|
||||
"integrity": "sha512-lykfY3TJRRWFeTxccEKdf1I6BLl2Plw81H0bbp4Fc5iEc67foDCa5pjJQULVgo0wF+Dli75f3xVcdb/67FFZ/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chromium-pickle-js": "^0.2.0",
|
||||
"commander": "^5.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"minimatch": "^3.0.4"
|
||||
@@ -868,9 +869,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
|
||||
"integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
@@ -901,9 +902,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "0.7.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
|
||||
"version": "0.7.31",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
|
||||
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
@@ -969,9 +970,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/7zip-bin": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz",
|
||||
"integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz",
|
||||
"integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
@@ -1053,9 +1054,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/app-builder-lib": {
|
||||
"version": "24.9.1",
|
||||
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.9.1.tgz",
|
||||
"integrity": "sha512-Q1nYxZcio4r+W72cnIRVYofEAyjBd3mG47o+zms8HlD51zWtA/YxJb01Jei5F+jkWhge/PTQK+uldsPh6d0/4g==",
|
||||
"version": "24.6.4",
|
||||
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.6.4.tgz",
|
||||
"integrity": "sha512-m9931WXb83teb32N0rKg+ulbn6+Hl8NV5SUpVDOVz9MWOXfhV6AQtTdftf51zJJvCQnQugGtSqoLvgw6mdF/Rg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@develar/schema-utils": "~2.6.5",
|
||||
@@ -1064,15 +1065,15 @@
|
||||
"@electron/universal": "1.4.1",
|
||||
"@malept/flatpak-bundler": "^0.4.0",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"7zip-bin": "~5.2.0",
|
||||
"7zip-bin": "~5.1.1",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"bluebird-lst": "^1.0.9",
|
||||
"builder-util": "24.8.1",
|
||||
"builder-util-runtime": "9.2.3",
|
||||
"builder-util": "24.5.0",
|
||||
"builder-util-runtime": "9.2.1",
|
||||
"chromium-pickle-js": "^0.2.0",
|
||||
"debug": "^4.3.4",
|
||||
"ejs": "^3.1.8",
|
||||
"electron-publish": "24.8.1",
|
||||
"electron-publish": "24.5.0",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"hosted-git-info": "^4.1.0",
|
||||
@@ -1091,19 +1092,6 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/builder-util-runtime": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
|
||||
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
@@ -1128,9 +1116,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
|
||||
"integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/async-exit-hook": {
|
||||
@@ -1292,16 +1280,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/builder-util": {
|
||||
"version": "24.8.1",
|
||||
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.8.1.tgz",
|
||||
"integrity": "sha512-ibmQ4BnnqCnJTNrdmdNlnhF48kfqhNzSeqFMXHLIl+o9/yhn6QfOaVrloZ9YUu3m0k3rexvlT5wcki6LWpjTZw==",
|
||||
"version": "24.5.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.5.0.tgz",
|
||||
"integrity": "sha512-STnBmZN/M5vGcv01u/K8l+H+kplTaq4PAIn3yeuufUKSpcdro0DhJWxPI81k5XcNfC//bjM3+n9nr8F9uV4uAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/debug": "^4.1.6",
|
||||
"7zip-bin": "~5.2.0",
|
||||
"7zip-bin": "~5.1.1",
|
||||
"app-builder-bin": "4.0.0",
|
||||
"bluebird-lst": "^1.0.9",
|
||||
"builder-util-runtime": "9.2.3",
|
||||
"builder-util-runtime": "9.2.1",
|
||||
"chalk": "^4.1.2",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"debug": "^4.3.4",
|
||||
@@ -1327,19 +1315,6 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/builder-util/node_modules/builder-util-runtime": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
|
||||
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
@@ -1438,9 +1413,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
|
||||
"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1727,14 +1702,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-builder": {
|
||||
"version": "24.9.1",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.9.1.tgz",
|
||||
"integrity": "sha512-huC+O6hvHd24Ubj3cy2GMiGLe2xGFKN3klqVMLAdcbB6SWMd1yPSdZvV8W1O01ICzCCRlZDHiv4VrNUgnPUfbQ==",
|
||||
"version": "24.6.4",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.6.4.tgz",
|
||||
"integrity": "sha512-BNcHRc9CWEuI9qt0E655bUBU/j/3wUCYBVKGu1kVpbN5lcUdEJJJeiO0NHK3dgKmra6LUUZlo+mWqc+OCbi0zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "24.9.1",
|
||||
"builder-util": "24.8.1",
|
||||
"builder-util-runtime": "9.2.3",
|
||||
"app-builder-lib": "24.6.4",
|
||||
"builder-util": "24.5.0",
|
||||
"builder-util-runtime": "9.2.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"iconv-lite": "^0.6.2",
|
||||
"js-yaml": "^4.1.0"
|
||||
@@ -1743,19 +1718,6 @@
|
||||
"dmg-license": "^1.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-builder/node_modules/builder-util-runtime": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
|
||||
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-license": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
|
||||
@@ -1812,9 +1774,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "25.9.8",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.8.tgz",
|
||||
"integrity": "sha512-PGgp6PH46QVENHuAHc2NT1Su8Q1qov7qIl2jI5tsDpTibwV2zD8539AeWBQySeBU4dhbj9onIl7+1bXQ0wefBg==",
|
||||
"version": "25.9.3",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.3.tgz",
|
||||
"integrity": "sha512-dacaHg/PuwVcFRgPDCM5j7UDzqGJWOsbBRdS5wPKLNS/ejPeccIjuNUT1cqcrpvCJKAFW8swHWg9kdizNSEDHQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
@@ -1830,16 +1792,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-builder": {
|
||||
"version": "24.9.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.9.1.tgz",
|
||||
"integrity": "sha512-v7BuakDuY6sKMUYM8mfQGrwyjBpZ/ObaqnenU0H+igEL10nc6ht049rsCw2HghRBdEwJxGIBuzs3jbEhNaMDmg==",
|
||||
"version": "24.6.4",
|
||||
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.6.4.tgz",
|
||||
"integrity": "sha512-uNWQoU7pE7qOaIQ6CJHpBi44RJFVG8OHRBIadUxrsDJVwLLo8Nma3K/EEtx5/UyWAQYdcK4nVPYKoRqBb20hbA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "24.9.1",
|
||||
"builder-util": "24.8.1",
|
||||
"builder-util-runtime": "9.2.3",
|
||||
"app-builder-lib": "24.6.4",
|
||||
"builder-util": "24.5.0",
|
||||
"builder-util-runtime": "9.2.1",
|
||||
"chalk": "^4.1.2",
|
||||
"dmg-builder": "24.9.1",
|
||||
"dmg-builder": "24.6.4",
|
||||
"fs-extra": "^10.1.0",
|
||||
"is-ci": "^3.0.0",
|
||||
"lazy-val": "^1.0.5",
|
||||
@@ -1901,19 +1863,6 @@
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/electron-builder/node_modules/builder-util-runtime": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
|
||||
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-notarize": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.2.2.tgz",
|
||||
@@ -1944,33 +1893,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-publish": {
|
||||
"version": "24.8.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.8.1.tgz",
|
||||
"integrity": "sha512-IFNXkdxMVzUdweoLJNXSupXkqnvgbrn3J4vognuOY06LaS/m0xvfFYIf+o1CM8if6DuWYWoQFKPcWZt/FUjZPw==",
|
||||
"version": "24.5.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.5.0.tgz",
|
||||
"integrity": "sha512-zwo70suH15L15B4ZWNDoEg27HIYoPsGJUF7xevLJLSI7JUPC8l2yLBdLGwqueJ5XkDL7ucYyRZzxJVR8ElV9BA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/fs-extra": "^9.0.11",
|
||||
"builder-util": "24.8.1",
|
||||
"builder-util-runtime": "9.2.3",
|
||||
"builder-util": "24.5.0",
|
||||
"builder-util-runtime": "9.2.1",
|
||||
"chalk": "^4.1.2",
|
||||
"fs-extra": "^10.1.0",
|
||||
"lazy-val": "^1.0.5",
|
||||
"mime": "^2.5.2"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-publish/node_modules/builder-util-runtime": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
|
||||
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-trpc": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/electron-trpc/-/electron-trpc-0.5.2.tgz",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
@@ -25,8 +25,8 @@
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "25.9.8",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron": "25.9.3",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
"tree-kill": "^1.2.2",
|
||||
|
||||
@@ -31,16 +31,16 @@ import { RNSqliteDriver } from "./sqlite.kysely";
|
||||
database.host(
|
||||
__DEV__
|
||||
? {
|
||||
// API_HOST: "https://api.notesnook.com",
|
||||
// AUTH_HOST: "https://auth.streetwriters.co",
|
||||
// SSE_HOST: "https://events.streetwriters.co",
|
||||
// SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||
// ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
API_HOST: "http://192.168.43.5:5264",
|
||||
AUTH_HOST: "http://192.168.43.5:8264",
|
||||
SSE_HOST: "http://192.168.43.5:7264",
|
||||
SUBSCRIPTIONS_HOST: "http://192.168.43.5:9264",
|
||||
ISSUES_HOST: "http://192.168.43.5:2624"
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
AUTH_HOST: "https://auth.streetwriters.co",
|
||||
SSE_HOST: "https://events.streetwriters.co",
|
||||
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
// API_HOST: "http://192.168.43.108:5264",
|
||||
// AUTH_HOST: "http://192.168.43.108:8264",
|
||||
// SSE_HOST: "http://192.168.43.108:7264",
|
||||
// SUBSCRIPTIONS_HOST: "http://192.168.43.108:9264",
|
||||
// ISSUES_HOST: "http://192.168.43.108:2624"
|
||||
}
|
||||
: {
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
@@ -59,17 +59,20 @@ database.setup({
|
||||
compress: Gzip.deflate,
|
||||
decompress: Gzip.inflate
|
||||
},
|
||||
batchSize: 100,
|
||||
batchSize: 500,
|
||||
sqliteOptions: {
|
||||
dialect: (name) => ({
|
||||
createDriver: () => {
|
||||
return new RNSqliteDriver({ async: true, dbName: name });
|
||||
},
|
||||
dialect: {
|
||||
createDriver: () =>
|
||||
new RNSqliteDriver({ async: true, dbName: "test.db" }),
|
||||
createAdapter: () => new SqliteAdapter(),
|
||||
createIntrospector: (db) => new SqliteIntrospector(db),
|
||||
createQueryCompiler: () => new SqliteQueryCompiler()
|
||||
}),
|
||||
tempStore: "memory"
|
||||
}
|
||||
// journalMode: "MEMORY",
|
||||
// synchronous: "normal",
|
||||
// pageSize: 8192,
|
||||
// cacheSize: -16000,
|
||||
// lockingMode: "exclusive"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -45,15 +45,15 @@ export class RNSqliteDriver implements Driver {
|
||||
}
|
||||
|
||||
async beginTransaction(connection: DatabaseConnection): Promise<void> {
|
||||
await connection.executeQuery(CompiledQuery.raw("BEGIN TRANSACTION"));
|
||||
await connection.executeQuery(CompiledQuery.raw("begin"));
|
||||
}
|
||||
|
||||
async commitTransaction(connection: DatabaseConnection): Promise<void> {
|
||||
await connection.executeQuery(CompiledQuery.raw("COMMIT"));
|
||||
await connection.executeQuery(CompiledQuery.raw("commit"));
|
||||
}
|
||||
|
||||
async rollbackTransaction(connection: DatabaseConnection): Promise<void> {
|
||||
await connection.executeQuery(CompiledQuery.raw("ROLLBACK"));
|
||||
await connection.executeQuery(CompiledQuery.raw("rollback"));
|
||||
}
|
||||
|
||||
async releaseConnection(): Promise<void> {
|
||||
|
||||
@@ -18,22 +18,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { formatBytes } from "@notesnook/common";
|
||||
import { Attachment, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
|
||||
import { useDBItem } from "../../hooks/use-db-item";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { ProgressCircleComponent } from "../ui/svg/lazy";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import Actions from "./actions";
|
||||
import { Attachment, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useDBItem } from "../../hooks/use-db-item";
|
||||
|
||||
function getFileExtension(filename: string) {
|
||||
const ext = /^.+\.([^.]+)$/.exec(filename);
|
||||
var ext = /^.+\.([^.]+)$/.exec(filename);
|
||||
return ext == null ? "" : ext[1];
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export const AttachmentItem = ({
|
||||
hideWhenNotDownloading,
|
||||
context
|
||||
}: {
|
||||
id: string | number;
|
||||
id: string;
|
||||
attachments?: VirtualizedGrouping<Attachment>;
|
||||
encryption?: boolean;
|
||||
setAttachments: (attachments: any) => void;
|
||||
@@ -54,7 +54,7 @@ export const AttachmentItem = ({
|
||||
hideWhenNotDownloading?: boolean;
|
||||
context?: string;
|
||||
}) => {
|
||||
const [attachment] = useDBItem(id, "attachment", attachments);
|
||||
const [attachment] = useDBItem(id, "attachment", attachments?.item);
|
||||
|
||||
const { colors } = useThemeColors();
|
||||
const [currentProgress, setCurrentProgress] = useAttachmentProgress(
|
||||
@@ -68,7 +68,7 @@ export const AttachmentItem = ({
|
||||
};
|
||||
|
||||
return (hideWhenNotDownloading &&
|
||||
(!currentProgress || !currentProgress.value)) ||
|
||||
(!currentProgress || !(currentProgress as any).value)) ||
|
||||
!attachment ? null : (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.9}
|
||||
@@ -133,8 +133,8 @@ export const AttachmentItem = ({
|
||||
{!hideWhenNotDownloading ? (
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
|
||||
{formatBytes(attachment.size)}{" "}
|
||||
{currentProgress?.type
|
||||
? "(" + currentProgress.type + "ing - tap to cancel)"
|
||||
{(currentProgress as any)?.type
|
||||
? "(" + (currentProgress as any).type + "ing - tap to cancel)"
|
||||
: ""}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
|
||||
@@ -91,7 +91,7 @@ const DownloadAttachments = ({
|
||||
|
||||
const successResults = () => {
|
||||
const results = [];
|
||||
for (const [key, value] of result.entries()) {
|
||||
for (let [key, value] of result.entries()) {
|
||||
if (value.status === 1) results.push(db.attachments.attachment(key));
|
||||
}
|
||||
return results;
|
||||
@@ -99,7 +99,7 @@ const DownloadAttachments = ({
|
||||
|
||||
const failedResults = () => {
|
||||
const results = [];
|
||||
for (const [key, value] of result.entries()) {
|
||||
for (let [key, value] of result.entries()) {
|
||||
if (value.status === 0) results.push(db.attachments.attachment(key));
|
||||
}
|
||||
return results;
|
||||
@@ -207,11 +207,11 @@ const DownloadAttachments = ({
|
||||
</Paragraph>
|
||||
</View>
|
||||
}
|
||||
keyExtractor={(item, index) => "attachment" + index}
|
||||
renderItem={({ index }) => {
|
||||
keyExtractor={(item) => item as string}
|
||||
renderItem={({ item }) => {
|
||||
return (
|
||||
<AttachmentItem
|
||||
id={index}
|
||||
id={item as string}
|
||||
setAttachments={() => {}}
|
||||
pressable={false}
|
||||
hideWhenNotDownloading={true}
|
||||
|
||||
@@ -83,14 +83,14 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
|
||||
}
|
||||
clearTimeout(searchTimer.current);
|
||||
searchTimer.current = setTimeout(async () => {
|
||||
const results = await db.lookup
|
||||
.attachments(attachmentSearchValue.current as string)
|
||||
.sorted();
|
||||
let results = await db.lookup.attachments(
|
||||
attachmentSearchValue.current as string
|
||||
);
|
||||
setAttachments(results);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const renderItem = ({ item }: { item: string | number }) => (
|
||||
const renderItem = ({ item }: { item: string }) => (
|
||||
<AttachmentItem
|
||||
setAttachments={setAttachments}
|
||||
attachments={attachments}
|
||||
@@ -102,15 +102,15 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
|
||||
const onCheck = async () => {
|
||||
if (!attachments) return;
|
||||
setLoading(true);
|
||||
for (const id of attachments.ids) {
|
||||
const attachment = (await attachments.item(id))?.item;
|
||||
for (let id of attachments.ids) {
|
||||
const attachment = await attachments.item(id as string);
|
||||
if (!attachment) continue;
|
||||
|
||||
const result = await filesystem.checkAttachment(attachment.hash);
|
||||
let result = await filesystem.checkAttachment(attachment.hash);
|
||||
if (result.failed) {
|
||||
await db.attachments.markAsFailed(attachment.hash, result.failed);
|
||||
} else {
|
||||
await db.attachments.markAsFailed(attachment.id, undefined);
|
||||
await db.attachments.markAsFailed(id as string, undefined);
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
@@ -306,7 +306,7 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
|
||||
/>
|
||||
}
|
||||
estimatedItemSize={50}
|
||||
data={attachments?.ids}
|
||||
data={attachments?.ids as string[]}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
|
||||
|
||||
@@ -17,16 +17,14 @@ 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, useRef, useState } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { eCloseLoginDialog, eOpenLoginDialog } from "../../utils/events";
|
||||
import { sleep } from "../../utils/time";
|
||||
import BaseDialog from "../dialog/base-dialog";
|
||||
@@ -36,6 +34,7 @@ import { IconButton } from "../ui/icon-button";
|
||||
import { hideAuth, initialAuthMode } from "./common";
|
||||
import { Login } from "./login";
|
||||
import { Signup } from "./signup";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
||||
|
||||
export const AuthMode = {
|
||||
login: 0,
|
||||
@@ -69,9 +68,6 @@ const AuthModal = () => {
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
useUserStore.setState({
|
||||
disableAppLockRequests: false
|
||||
});
|
||||
actionSheetRef.current?.hide();
|
||||
setCurrentAuthMode(AuthMode.login);
|
||||
setVisible(false);
|
||||
@@ -81,11 +77,6 @@ const AuthModal = () => {
|
||||
<BaseDialog
|
||||
overlayOpacity={0}
|
||||
statusBarTranslucent={false}
|
||||
onShow={() => {
|
||||
useUserStore.setState({
|
||||
disableAppLockRequests: true
|
||||
});
|
||||
}}
|
||||
onRequestClose={currentAuthMode !== AuthMode.welcomeSignup && close}
|
||||
visible={true}
|
||||
onClose={close}
|
||||
@@ -95,7 +86,6 @@ const AuthModal = () => {
|
||||
transparent={false}
|
||||
animated={false}
|
||||
centered={false}
|
||||
enableSheetKeyboardHandler
|
||||
>
|
||||
<KeyboardAwareScrollView
|
||||
style={{
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function DelayLayout({
|
||||
}: IDelayLayoutProps) {
|
||||
const { colors } = useThemeColors();
|
||||
const loading = useDelayLayout(
|
||||
!props.delay || props.delay < 300 ? 0 : props.delay
|
||||
!props.delay || props.delay < 300 ? 300 : props.delay
|
||||
);
|
||||
const Placeholder = placeholder[props.type || "default"];
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { AnnouncementDialog } from "../announcements";
|
||||
import AuthModal from "../auth/auth-modal";
|
||||
import { SessionExpired } from "../auth/session-expired";
|
||||
@@ -39,6 +40,7 @@ import RestoreDataSheet from "../sheets/restore-data";
|
||||
|
||||
const DialogProvider = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -55,7 +57,7 @@ const DialogProvider = () => {
|
||||
<VaultDialog colors={colors} />
|
||||
<RateAppSheet />
|
||||
<ImagePreview />
|
||||
<Expiring />
|
||||
{loading ? null : <Expiring />}
|
||||
<AnnouncementDialog />
|
||||
<SessionExpired />
|
||||
<PDFPreview />
|
||||
|
||||
@@ -86,7 +86,6 @@ const BaseDialog = ({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (useUserStore.getState().disableAppLockRequests) return;
|
||||
if (SettingsService.get().appLockMode === "background") {
|
||||
if (appState === "background") {
|
||||
setIntervalVisible(false);
|
||||
|
||||
@@ -18,36 +18,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { View, ViewProps } from "react-native";
|
||||
import { View } from "react-native";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
|
||||
const DialogContainer = ({
|
||||
width,
|
||||
height,
|
||||
style,
|
||||
...restProps
|
||||
}: ViewProps & {
|
||||
width?: any;
|
||||
height?: any;
|
||||
}) => {
|
||||
const DialogContainer = ({ width, height, ...restProps }) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
return (
|
||||
<View
|
||||
{...restProps}
|
||||
style={[
|
||||
{
|
||||
...getElevationStyle(5),
|
||||
width: width || DDS.isTab ? 500 : "85%",
|
||||
maxHeight: height || 450,
|
||||
borderRadius: 10,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12
|
||||
},
|
||||
style
|
||||
]}
|
||||
style={{
|
||||
...getElevationStyle(5),
|
||||
width: width || DDS.isTab ? 500 : "85%",
|
||||
maxHeight: height || 450,
|
||||
borderRadius: 10,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,302 +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 { isThemeColor, useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { FlashList } from "react-native-actions-sheet";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import DialogContainer from "../../dialog/dialog-container";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import { ToastManager } from "../../../services/event-manager";
|
||||
import { Toast } from "../../toast";
|
||||
import { db } from "../../../common/database";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
const arrayOfColors = [
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7"
|
||||
];
|
||||
const HEX_COLOR_REGEX_ALPHA =
|
||||
/^#(?:(?:[\da-fA-F]{3}){1,2}|(?:[\da-fA-F]{4}){1,2})$/;
|
||||
|
||||
const convertToColorObjects = (colors: string[]) => {
|
||||
const colorObjects = [];
|
||||
for (let i = 0; i < colors.length; i += 3) {
|
||||
colorObjects.push({
|
||||
colorOne: colors[i],
|
||||
colorTwo: colors[i + 1],
|
||||
colorThree: colors[i + 2]
|
||||
});
|
||||
}
|
||||
return colorObjects;
|
||||
};
|
||||
|
||||
const ColorPicker = ({
|
||||
visible,
|
||||
setVisible
|
||||
}: {
|
||||
visible: boolean;
|
||||
setVisible: (value: boolean) => void;
|
||||
}) => {
|
||||
const [selectedColor, setSelectedColor] = useState<string>();
|
||||
const { colors } = useThemeColors();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const title = useRef<string>();
|
||||
|
||||
const renderItem = ({
|
||||
item
|
||||
}: {
|
||||
item: { colorOne: string; colorTwo: string; colorThree: string };
|
||||
}) => (
|
||||
<View>
|
||||
{Object.keys(item).map((key) => (
|
||||
<PressableButton
|
||||
key={item[key as keyof typeof item]}
|
||||
type="accent"
|
||||
accentColor={item[key as keyof typeof item]}
|
||||
customStyle={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 10,
|
||||
marginBottom: 10
|
||||
}}
|
||||
onPress={() => {
|
||||
const color = item[key as keyof typeof item];
|
||||
setSelectedColor(color);
|
||||
inputRef.current?.setNativeProps({
|
||||
placeholder: color,
|
||||
text: color
|
||||
});
|
||||
}}
|
||||
>
|
||||
{selectedColor === item[key as keyof typeof item] ? (
|
||||
<Icon name="check" color="white" size={SIZE.lg} />
|
||||
) : null}
|
||||
</PressableButton>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
visible={visible}
|
||||
onRequestClose={() => {
|
||||
setVisible(false);
|
||||
useSettingStore.getState().setSheetKeyboardHandler(true);
|
||||
}}
|
||||
statusBarTranslucent={false}
|
||||
centered
|
||||
>
|
||||
<Toast context="color-picker" />
|
||||
<DialogContainer
|
||||
style={{
|
||||
paddingTop: 0
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
padding: 20
|
||||
}}
|
||||
>
|
||||
<FlashList
|
||||
extraData={selectedColor}
|
||||
horizontal
|
||||
data={convertToColorObjects(arrayOfColors)}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingTop: 10,
|
||||
columnGap: 10,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
fwdRef={inputRef}
|
||||
placeholder="#f0f0f0"
|
||||
marginBottom={0}
|
||||
onChangeText={(value) => {
|
||||
if (HEX_COLOR_REGEX_ALPHA.test(value)) {
|
||||
setSelectedColor(value);
|
||||
inputRef.current?.setNativeProps({
|
||||
placeholder: value,
|
||||
text: value
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<PressableButton
|
||||
type="accent"
|
||||
accentColor={selectedColor || colors.secondary.background}
|
||||
customStyle={{
|
||||
width: 45,
|
||||
height: 45,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Input
|
||||
marginBottom={10}
|
||||
onChangeText={(value) => {
|
||||
title.current = value;
|
||||
}}
|
||||
placeholder={title.current || "Color title"}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Add color"
|
||||
onPress={async () => {
|
||||
if (!selectedColor)
|
||||
return ToastManager.error(
|
||||
new Error("Select a color"),
|
||||
undefined,
|
||||
"color-picker"
|
||||
);
|
||||
if (!title.current)
|
||||
return ToastManager.error(
|
||||
new Error("Enter a title for the color")
|
||||
);
|
||||
const exists = await db.colors.all.find((v) =>
|
||||
v.and([v(`colorCode`, "==", selectedColor)])
|
||||
);
|
||||
if (exists)
|
||||
return ToastManager.error(
|
||||
new Error(`Color #${selectedColor} already exists`)
|
||||
);
|
||||
|
||||
await db.colors.add({
|
||||
title: title.current,
|
||||
colorCode: selectedColor
|
||||
});
|
||||
useRelationStore.getState().update();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
setVisible(false);
|
||||
}}
|
||||
type={selectedColor ? "grayAccent" : "grayBg"}
|
||||
width="100%"
|
||||
/>
|
||||
</View>
|
||||
</DialogContainer>
|
||||
</BaseDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorPicker;
|
||||
@@ -26,7 +26,7 @@ import React, {
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { ActivityIndicator, FlatList, ScrollView, View } from "react-native";
|
||||
import { FlatList, ScrollView, View } from "react-native";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
@@ -51,20 +51,19 @@ const JumpToSectionDialog = () => {
|
||||
const notes = data;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const currentScrollPosition = useRef(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [groups, setGroups] = useState<
|
||||
{
|
||||
index: number;
|
||||
group: GroupHeader;
|
||||
}[]
|
||||
>();
|
||||
const offsets = useRef<number[]>([]);
|
||||
const timeout = useRef<NodeJS.Timeout>();
|
||||
|
||||
const onPress = (item: { index: number; group: GroupHeader }) => {
|
||||
const onPress = (item: GroupHeader) => {
|
||||
const index = notes?.ids?.findIndex((i) => {
|
||||
if (typeof i === "object") {
|
||||
return i.title === item.title && i.type === "header";
|
||||
} else {
|
||||
false;
|
||||
}
|
||||
});
|
||||
scrollRef.current?.current?.scrollToIndex({
|
||||
index: item.index,
|
||||
index: index as number,
|
||||
animated: true
|
||||
});
|
||||
close();
|
||||
@@ -78,7 +77,6 @@ const JumpToSectionDialog = () => {
|
||||
data: VirtualizedGrouping<Item>;
|
||||
ref: RefObject<FlatList>;
|
||||
}) => {
|
||||
setLoading(true);
|
||||
setData(data);
|
||||
scrollRef.current = ref;
|
||||
setVisible(true);
|
||||
@@ -99,45 +97,55 @@ const JumpToSectionDialog = () => {
|
||||
}, [open]);
|
||||
|
||||
const onScroll = (data: { x: number; y: number }) => {
|
||||
currentScrollPosition.current = data.y;
|
||||
const y = data.y;
|
||||
if (timeout) {
|
||||
clearTimeout(timeout.current);
|
||||
timeout.current = undefined;
|
||||
}
|
||||
timeout.current = setTimeout(() => {
|
||||
setCurrentIndex(
|
||||
offsets.current?.findIndex(
|
||||
(o, i) => o <= y && offsets.current[i + 1] > y
|
||||
) || 0
|
||||
);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const loadGroupsAndOffsets = useCallback(() => {
|
||||
notes?.groups?.().then((groups) => {
|
||||
setGroups(groups);
|
||||
offsets.current = [];
|
||||
groups.map((item, index) => {
|
||||
const loadOffsets = useCallback(() => {
|
||||
notes?.ids
|
||||
.filter((i) => typeof i === "object" && i.type === "header")
|
||||
.map((item, index) => {
|
||||
if (typeof item === "string") return;
|
||||
|
||||
let offset = 35 * index;
|
||||
let groupIndex = item.index;
|
||||
let ind = notes.ids.findIndex(
|
||||
(i) =>
|
||||
typeof i === "object" &&
|
||||
i.title === item.title &&
|
||||
i.type === "header"
|
||||
);
|
||||
const messageState = useMessageStore.getState().message;
|
||||
const msgOffset = messageState?.visible ? 60 : 10;
|
||||
|
||||
groupIndex = groupIndex + 1;
|
||||
groupIndex = groupIndex - (index + 1);
|
||||
offset = offset + groupIndex * 100 + msgOffset;
|
||||
ind = ind + 1;
|
||||
ind = ind - (index + 1);
|
||||
offset = offset + ind * 100 + msgOffset;
|
||||
offsets.current.push(offset);
|
||||
});
|
||||
|
||||
const index = offsets.current?.findIndex((o, i) => {
|
||||
return (
|
||||
o <= currentScrollPosition.current + 100 &&
|
||||
offsets.current[i + 1] - 100 > currentScrollPosition.current
|
||||
);
|
||||
});
|
||||
|
||||
setCurrentIndex(index < 0 ? 0 : index);
|
||||
setLoading(false);
|
||||
});
|
||||
}, [notes]);
|
||||
|
||||
useEffect(() => {
|
||||
loadOffsets();
|
||||
}, [loadOffsets, notes]);
|
||||
|
||||
return !visible ? null : (
|
||||
<BaseDialog
|
||||
onShow={() => {
|
||||
loadGroupsAndOffsets();
|
||||
loadOffsets();
|
||||
}}
|
||||
onRequestClose={close}
|
||||
visible={true}
|
||||
@@ -156,42 +164,35 @@ const JumpToSectionDialog = () => {
|
||||
paddingTop: 30
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator
|
||||
size={SIZE.lg}
|
||||
color={colors.primary.accent}
|
||||
<ScrollView
|
||||
style={{
|
||||
maxHeight: "100%"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
marginBottom: 20
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ScrollView
|
||||
style={{
|
||||
maxHeight: "100%"
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
alignSelf: "center",
|
||||
justifyContent: "center",
|
||||
paddingBottom: 20
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
alignSelf: "center",
|
||||
justifyContent: "center",
|
||||
paddingBottom: 20
|
||||
}}
|
||||
>
|
||||
{groups?.map((item, index) => {
|
||||
return (
|
||||
{notes?.ids
|
||||
.filter((i) => typeof i === "object" && i.type === "header")
|
||||
.map((item, index) => {
|
||||
return typeof item === "object" && item.title ? (
|
||||
<PressableButton
|
||||
key={item.group.id}
|
||||
key={item.title}
|
||||
onPress={() => onPress(item)}
|
||||
type={currentIndex === index ? "accent" : "transparent"}
|
||||
type={currentIndex === index ? "selected" : "transparent"}
|
||||
customStyle={{
|
||||
minWidth: "20%",
|
||||
width: null,
|
||||
paddingHorizontal: 12,
|
||||
margin: 5,
|
||||
borderRadius: 100,
|
||||
height: 30,
|
||||
height: 25,
|
||||
marginVertical: 10
|
||||
}}
|
||||
>
|
||||
@@ -199,21 +200,20 @@ const JumpToSectionDialog = () => {
|
||||
size={SIZE.sm}
|
||||
color={
|
||||
currentIndex === index
|
||||
? colors.static.white
|
||||
: colors.primary.paragraph
|
||||
? colors.selected.accent
|
||||
: colors.primary.accent
|
||||
}
|
||||
style={{
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
{item.group.title}
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</PressableButton>
|
||||
);
|
||||
) : null;
|
||||
})}
|
||||
</View>
|
||||
</ScrollView>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
</BaseDialog>
|
||||
);
|
||||
|
||||
@@ -49,8 +49,6 @@ export const Title = ({
|
||||
const isTag = title.startsWith("#");
|
||||
const onScroll = useCallback(
|
||||
(data: { x: number; y: number; id?: string; route: string }) => {
|
||||
if (data.route !== "Notebook") return;
|
||||
|
||||
if (data.route !== renderedInRoute || data.id !== id) return;
|
||||
if (data.y > 150) {
|
||||
if (!visible) return;
|
||||
|
||||
@@ -37,7 +37,10 @@ import { useEditorStore } from "../../../stores/use-editor-store";
|
||||
import useNavigationStore from "../../../stores/use-navigation-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
|
||||
import {
|
||||
NotebooksWithDateEdited,
|
||||
TagsWithDateEdited
|
||||
} from "../../list/list-item.wrapper";
|
||||
import { Properties } from "../../properties";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
@@ -45,10 +48,6 @@ import { ReminderTime } from "../../ui/reminder-time";
|
||||
import { TimeSince } from "../../ui/time-since";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import {
|
||||
NotebooksWithDateEdited,
|
||||
TagsWithDateEdited
|
||||
} from "../../../stores/resolve-items";
|
||||
|
||||
type NoteItemProps = {
|
||||
item: Note | BaseTrashItem<Note>;
|
||||
@@ -72,17 +71,18 @@ const NoteItem = ({
|
||||
reminder,
|
||||
tags,
|
||||
attachmentsCount,
|
||||
noOpen = false
|
||||
noOpen
|
||||
}: NoteItemProps) => {
|
||||
const isEditingNote = useEditorStore(
|
||||
(state) => state.currentEditingNote === item.id
|
||||
const currentEditingNote = useEditorStore(
|
||||
(state) => state.currentEditingNote
|
||||
);
|
||||
const { colors } = useThemeColors();
|
||||
const compactMode = useIsCompactModeEnabled(
|
||||
(item as TrashItem).itemType || item.type
|
||||
);
|
||||
const _update = useRelationStore((state) => state.updater);
|
||||
const primaryColors = isEditingNote ? colors.selected : colors.primary;
|
||||
const primaryColors =
|
||||
currentEditingNote === item.id ? colors.selected : colors.primary;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -156,10 +156,10 @@ const NoteItem = ({
|
||||
<Paragraph
|
||||
numberOfLines={1}
|
||||
color={color?.colorCode || primaryColors.heading}
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
paddingRight: 10
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
@@ -167,10 +167,10 @@ const NoteItem = ({
|
||||
<Heading
|
||||
numberOfLines={1}
|
||||
color={color?.colorCode || primaryColors.heading}
|
||||
size={SIZE.md}
|
||||
style={{
|
||||
paddingRight: 10
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
size={SIZE.md}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
|
||||
@@ -33,13 +33,12 @@ import { useEditorStore } from "../../../stores/use-editor-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { eOnLoadNote, eShowMergeDialog } from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
|
||||
import NotePreview from "../../note-history/preview";
|
||||
import SelectionWrapper from "../selection-wrapper";
|
||||
import {
|
||||
import type {
|
||||
NotebooksWithDateEdited,
|
||||
TagsWithDateEdited
|
||||
} from "../../../stores/resolve-items";
|
||||
} from "../../list/list-item.wrapper";
|
||||
import NotePreview from "../../note-history/preview";
|
||||
import SelectionWrapper from "../selection-wrapper";
|
||||
|
||||
export const openNote = async (
|
||||
item: Note,
|
||||
@@ -52,20 +51,15 @@ export const openNote = async (
|
||||
if (!isTrash) {
|
||||
note = (await db.notes.note(item.id)) as Note;
|
||||
}
|
||||
if (useSelectionStore.getState().selectionMode === item.type) {
|
||||
const {
|
||||
selectedItemsList,
|
||||
selectionMode,
|
||||
clearSelection,
|
||||
setSelectedItem
|
||||
} = useSelectionStore.getState();
|
||||
|
||||
if (selectedItemsList.length > 0 && selectionMode === item.type) {
|
||||
setSelectedItem(note.id);
|
||||
} else {
|
||||
clearSelection();
|
||||
}
|
||||
const { selectedItemsList, selectionMode, clearSelection, setSelectedItem } =
|
||||
useSelectionStore.getState();
|
||||
|
||||
if (selectedItemsList.length > 0 && selectionMode === item.type) {
|
||||
setSelectedItem(note.id);
|
||||
return;
|
||||
} else {
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
if (note.conflicted) {
|
||||
@@ -94,9 +88,7 @@ export const openNote = async (
|
||||
)
|
||||
});
|
||||
} else {
|
||||
if (note?.readonly) {
|
||||
useEditorStore.getState().setReadonly(note?.readonly);
|
||||
}
|
||||
useEditorStore.getState().setReadonly(note?.readonly);
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note
|
||||
});
|
||||
@@ -164,7 +156,6 @@ export const NoteWrapper = React.memo<
|
||||
if (prev.item?.dateModified !== next.item?.dateModified) {
|
||||
return false;
|
||||
}
|
||||
if (prev.item?.id !== next.item?.id) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export const openNotebook = (item: Notebook | BaseTrashItem<Notebook>) => {
|
||||
},
|
||||
onClose: async () => {
|
||||
await db.trash.delete(item.id);
|
||||
useTrashStore.getState().refresh();
|
||||
useTrashStore.getState().setTrash();
|
||||
useSelectionStore.getState().setSelectionMode(undefined);
|
||||
ToastManager.show({
|
||||
heading: "Permanently deleted items",
|
||||
@@ -103,7 +103,6 @@ export const NotebookWrapper = React.memo(
|
||||
if (prev.totalNotes !== next.totalNotes) return false;
|
||||
if (prev.date !== next.date) return false;
|
||||
if (prev.item?.dateModified !== next.item?.dateModified) return false;
|
||||
if (prev.item?.id !== next.item?.id) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,13 +26,13 @@ import { Item } from "@notesnook/core";
|
||||
|
||||
export const Filler = ({ item, color }: { item: Item; color?: string }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const isEditingNote = useEditorStore(
|
||||
(state) => state.currentEditingNote === item.id
|
||||
const currentEditingNote = useEditorStore(
|
||||
(state) => state.currentEditingNote
|
||||
);
|
||||
|
||||
const [selected] = useIsSelected(item);
|
||||
|
||||
return isEditingNote || selected ? (
|
||||
return currentEditingNote === item.id || selected ? (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
@@ -40,11 +40,12 @@ export const Filler = ({ item, color }: { item: Item; color?: string }) => {
|
||||
height: "150%",
|
||||
backgroundColor: colors.selected.background,
|
||||
borderLeftWidth: 5,
|
||||
borderLeftColor: isEditingNote
|
||||
? item.color
|
||||
? colors.static[item.color]
|
||||
: colors.selected.accent
|
||||
: "transparent"
|
||||
borderLeftColor:
|
||||
currentEditingNote === item.id
|
||||
? color
|
||||
? color
|
||||
: colors.selected.accent
|
||||
: "transparent"
|
||||
}}
|
||||
collapsable={false}
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,13 @@ 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 { GroupingKey, Item, VirtualizedGrouping } from "@notesnook/core";
|
||||
import {
|
||||
GroupHeader,
|
||||
GroupingKey,
|
||||
Item,
|
||||
VirtualizedGrouping,
|
||||
isGroupHeader
|
||||
} from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
@@ -33,10 +39,11 @@ import { eSendEvent } from "../../services/event-manager";
|
||||
import Sync from "../../services/sync";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { eOpenJumpToDialog, eScrollEvent } from "../../utils/events";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { Footer } from "../list-items/footer";
|
||||
import { Header } from "../list-items/headers/header";
|
||||
import { SectionHeader } from "../list-items/headers/section-header";
|
||||
import { Empty, PlaceholderData } from "./empty";
|
||||
import { ListItemWrapper } from "./list-item.wrapper";
|
||||
|
||||
@@ -52,7 +59,6 @@ type ListProps = {
|
||||
isRenderedInActionSheet?: boolean;
|
||||
CustomListComponent?: React.JSX.ElementType;
|
||||
placeholder?: PlaceholderData;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export default function List(props: ListProps) {
|
||||
@@ -86,20 +92,35 @@ export default function List(props: ListProps) {
|
||||
};
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({ index }: { index: number }) => {
|
||||
return (
|
||||
<ListItemWrapper
|
||||
index={index}
|
||||
isSheet={props.isRenderedInActionSheet || false}
|
||||
items={props.data}
|
||||
groupOptions={groupOptions}
|
||||
group={groupType as GroupingKey}
|
||||
renderedInRoute={props.renderedInRoute}
|
||||
customAccentColor={props.customAccentColor}
|
||||
dataType={props.dataType}
|
||||
scrollRef={scrollRef}
|
||||
/>
|
||||
);
|
||||
({ item, index }: { item: string | GroupHeader; index: number }) => {
|
||||
if (isGroupHeader(item)) {
|
||||
return (
|
||||
<SectionHeader
|
||||
screen={props.renderedInRoute}
|
||||
item={item}
|
||||
index={index}
|
||||
dataType={props.dataType}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: scrollRef,
|
||||
data: props.data
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ListItemWrapper
|
||||
id={item}
|
||||
index={index}
|
||||
isSheet={props.isRenderedInActionSheet || false}
|
||||
items={props.data}
|
||||
group={groupType as GroupingKey}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
groupOptions,
|
||||
@@ -117,18 +138,16 @@ export default function List(props: ListProps) {
|
||||
if (!event) return;
|
||||
eSendEvent(eScrollEvent, {
|
||||
y: event.nativeEvent.contentOffset.y,
|
||||
route: props.renderedInRoute,
|
||||
id: props.id || props.renderedInRoute
|
||||
screen: props.renderedInRoute
|
||||
});
|
||||
},
|
||||
[props.renderedInRoute, props.id]
|
||||
[props.renderedInRoute]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eSendEvent(eScrollEvent, {
|
||||
y: 0,
|
||||
route: props.renderedInRoute,
|
||||
id: props.id || props.renderedInRoute
|
||||
screen: props.renderedInRoute
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
@@ -162,10 +181,8 @@ export default function List(props: ListProps) {
|
||||
onMomentumScrollEnd={() => {
|
||||
tabBarRef.current?.unlock();
|
||||
}}
|
||||
getItemType={(item: number, index: number) => {
|
||||
return props.data?.type(index);
|
||||
}}
|
||||
estimatedItemSize={isCompactModeEnabled ? 60 : 120}
|
||||
getItemType={(item: any) => (isGroupHeader(item) ? "header" : "item")}
|
||||
estimatedItemSize={isCompactModeEnabled ? 60 : 100}
|
||||
directionalLockEnabled={true}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="interactive"
|
||||
|
||||
@@ -16,53 +16,42 @@ 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 from "react";
|
||||
import { getSortValue } from "@notesnook/core/dist/utils/grouping";
|
||||
import {
|
||||
Color,
|
||||
GroupHeader,
|
||||
GroupOptions,
|
||||
GroupingKey,
|
||||
Item,
|
||||
ItemType,
|
||||
Note,
|
||||
Notebook,
|
||||
VirtualizedGrouping,
|
||||
Color,
|
||||
Reminder,
|
||||
Notebook,
|
||||
Tag,
|
||||
TrashItem,
|
||||
VirtualizedGrouping
|
||||
ItemType,
|
||||
Note
|
||||
} from "@notesnook/core";
|
||||
import { getSortValue } from "@notesnook/core/dist/utils/grouping";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import {
|
||||
NotebooksWithDateEdited,
|
||||
TagsWithDateEdited,
|
||||
isNoteResolvedData,
|
||||
resolveItems
|
||||
} from "../../stores/resolve-items";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { eOpenJumpToDialog } from "../../utils/events";
|
||||
import { SectionHeader } from "../list-items/headers/section-header";
|
||||
import { NoteWrapper } from "../list-items/note/wrapper";
|
||||
import { db } from "../../common/database";
|
||||
import { NotebookWrapper } from "../list-items/notebook/wrapper";
|
||||
import ReminderItem from "../list-items/reminder";
|
||||
import TagItem from "../list-items/tag";
|
||||
|
||||
export type WithDateEdited<T> = { items: T[]; dateEdited: number };
|
||||
export type NotebooksWithDateEdited = WithDateEdited<Notebook>;
|
||||
export type TagsWithDateEdited = WithDateEdited<Tag>;
|
||||
|
||||
type ListItemWrapperProps<TItem = Item> = {
|
||||
group?: GroupingKey;
|
||||
items: VirtualizedGrouping<TItem> | undefined;
|
||||
id: string;
|
||||
isSheet: boolean;
|
||||
index: number;
|
||||
renderedInRoute?: RouteName;
|
||||
customAccentColor?: string;
|
||||
dataType: string;
|
||||
scrollRef: any;
|
||||
groupOptions: GroupOptions;
|
||||
};
|
||||
|
||||
export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
const { items, group, isSheet, index, groupOptions } = props;
|
||||
const { id, items, group, isSheet, index } = props;
|
||||
const [item, setItem] = useState<Item>();
|
||||
const tags = useRef<TagsWithDateEdited>();
|
||||
const notebooks = useRef<NotebooksWithDateEdited>();
|
||||
@@ -70,197 +59,83 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
const color = useRef<Color>();
|
||||
const totalNotes = useRef<number>(0);
|
||||
const attachmentsCount = useRef(0);
|
||||
const [groupHeader, setGroupHeader] = useState<GroupHeader>();
|
||||
const previousIndex = useRef<number>();
|
||||
const refreshTimeout = useRef<NodeJS.Timeout>();
|
||||
const currentItemId = useRef<string>();
|
||||
|
||||
const refreshItem = useCallback((resolvedItem: any) => {
|
||||
if (!resolvedItem || !resolvedItem.data) {
|
||||
tags.current = undefined;
|
||||
notebooks.current = undefined;
|
||||
reminder.current = undefined;
|
||||
color.current = undefined;
|
||||
attachmentsCount.current = 0;
|
||||
totalNotes.current = 0;
|
||||
}
|
||||
|
||||
if (resolvedItem && resolvedItem.item) {
|
||||
const data = resolvedItem.data;
|
||||
if (resolvedItem.item.type === "note" && isNoteResolvedData(data)) {
|
||||
useEffect(() => {
|
||||
(async function () {
|
||||
const { item, data } = (await items?.item(id, resolveItems)) || {};
|
||||
if (!item) return;
|
||||
if (item.type === "note" && isNoteResolvedData(data)) {
|
||||
tags.current = data.tags;
|
||||
notebooks.current = data.notebooks;
|
||||
reminder.current = data.reminder;
|
||||
color.current = data.color;
|
||||
attachmentsCount.current = data.attachmentsCount || 0;
|
||||
} else if (
|
||||
resolvedItem.item.type === "notebook" &&
|
||||
typeof data === "number"
|
||||
) {
|
||||
attachmentsCount.current = data.attachmentsCount;
|
||||
} else if (item.type === "notebook" && typeof data === "number") {
|
||||
totalNotes.current = data;
|
||||
} else if (resolvedItem.item.type === "tag" && typeof data === "number") {
|
||||
} else if (item.type === "tag" && typeof data === "number") {
|
||||
totalNotes.current = data;
|
||||
}
|
||||
currentItemId.current = resolvedItem.item.id;
|
||||
setItem(resolvedItem.item);
|
||||
setGroupHeader(resolvedItem.group);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (previousIndex.current !== index) {
|
||||
previousIndex.current = index;
|
||||
const resolvedItem = items?.cacheItem(index);
|
||||
refreshItem(resolvedItem);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
(async function () {
|
||||
try {
|
||||
clearTimeout(refreshTimeout.current);
|
||||
const idx = index;
|
||||
refreshTimeout.current = setTimeout(async () => {
|
||||
if (idx !== previousIndex.current) return;
|
||||
const resolvedItem = await items?.item(idx, resolveItems);
|
||||
if (idx !== previousIndex.current) return;
|
||||
refreshItem(resolvedItem);
|
||||
}, 100);
|
||||
} catch (e) {
|
||||
console.log("Error", e);
|
||||
}
|
||||
setItem(item);
|
||||
})();
|
||||
}, [index, items, refreshItem]);
|
||||
}, [id, items]);
|
||||
|
||||
if (!item)
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: 120,
|
||||
width: "100%"
|
||||
}}
|
||||
/>
|
||||
);
|
||||
if (!item) return <View style={{ height: 100, width: "100%" }} />;
|
||||
|
||||
const type = ((item as TrashItem).itemType || item.type) as ItemType;
|
||||
switch (type) {
|
||||
case "note": {
|
||||
return (
|
||||
<>
|
||||
{groupHeader && previousIndex.current === index ? (
|
||||
<SectionHeader
|
||||
screen={props.renderedInRoute}
|
||||
item={groupHeader}
|
||||
index={index}
|
||||
dataType={item.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<NoteWrapper
|
||||
item={item as Note}
|
||||
tags={tags.current}
|
||||
color={color.current}
|
||||
notebooks={notebooks.current}
|
||||
reminder={reminder.current}
|
||||
attachmentsCount={attachmentsCount.current}
|
||||
date={getDate(item, group)}
|
||||
isRenderedInActionSheet={isSheet}
|
||||
index={index}
|
||||
/>
|
||||
</>
|
||||
<NoteWrapper
|
||||
item={item as Note}
|
||||
tags={tags.current}
|
||||
color={color.current}
|
||||
notebooks={notebooks.current}
|
||||
reminder={reminder.current}
|
||||
attachmentsCount={attachmentsCount.current}
|
||||
date={getDate(item, group)}
|
||||
isRenderedInActionSheet={isSheet}
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "notebook":
|
||||
return (
|
||||
<>
|
||||
{groupHeader && previousIndex.current === index ? (
|
||||
<SectionHeader
|
||||
screen={props.renderedInRoute}
|
||||
item={groupHeader}
|
||||
index={index}
|
||||
dataType={item.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<NotebookWrapper
|
||||
item={item as Notebook}
|
||||
totalNotes={totalNotes.current}
|
||||
date={getDate(item, group)}
|
||||
index={index}
|
||||
/>
|
||||
</>
|
||||
<NotebookWrapper
|
||||
item={item as Notebook}
|
||||
totalNotes={totalNotes.current}
|
||||
date={getDate(item, group)}
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
|
||||
case "reminder":
|
||||
return (
|
||||
<>
|
||||
{groupHeader && previousIndex.current === index ? (
|
||||
<SectionHeader
|
||||
screen={props.renderedInRoute}
|
||||
item={groupHeader}
|
||||
index={index}
|
||||
dataType={item.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<ReminderItem
|
||||
item={item as Reminder}
|
||||
index={index}
|
||||
isSheet={isSheet}
|
||||
/>
|
||||
</>
|
||||
<ReminderItem item={item as Reminder} index={index} isSheet={isSheet} />
|
||||
);
|
||||
case "tag":
|
||||
return (
|
||||
<>
|
||||
{groupHeader && previousIndex.current === index ? (
|
||||
<SectionHeader
|
||||
screen={props.renderedInRoute}
|
||||
item={groupHeader}
|
||||
index={index}
|
||||
dataType={item.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<TagItem
|
||||
item={item as Tag}
|
||||
index={index}
|
||||
totalNotes={totalNotes.current}
|
||||
/>
|
||||
</>
|
||||
<TagItem
|
||||
item={item as Tag}
|
||||
index={index}
|
||||
totalNotes={totalNotes.current}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function withDateEdited<
|
||||
T extends { dateEdited: number } | { dateModified: number }
|
||||
>(items: T[]): WithDateEdited<T> {
|
||||
let latestDateEdited = 0;
|
||||
items.forEach((item) => {
|
||||
const date = "dateEdited" in item ? item.dateEdited : item.dateModified;
|
||||
if (latestDateEdited < date) latestDateEdited = date;
|
||||
});
|
||||
return { dateEdited: latestDateEdited, items };
|
||||
}
|
||||
|
||||
function getDate(item: Item, groupType?: GroupingKey): number {
|
||||
return (
|
||||
getSortValue(
|
||||
@@ -275,3 +150,120 @@ function getDate(item: Item, groupType?: GroupingKey): number {
|
||||
) || 0
|
||||
);
|
||||
}
|
||||
|
||||
async function resolveItems(ids: string[], items: Record<string, Item>) {
|
||||
const { type } = items[ids[0]];
|
||||
if (type === "note") return resolveNotes(ids);
|
||||
else if (type === "notebook") {
|
||||
const data: Record<string, number> = {};
|
||||
for (const id of ids) data[id] = await db.notebooks.totalNotes(id);
|
||||
return data;
|
||||
} else if (type === "tag") {
|
||||
const data: Record<string, number> = {};
|
||||
for (const id of ids)
|
||||
data[id] = await db.relations.from({ id, type: "tag" }, "note").count();
|
||||
return data;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
type NoteResolvedData = {
|
||||
notebooks?: NotebooksWithDateEdited;
|
||||
reminder?: Reminder;
|
||||
color?: Color;
|
||||
tags?: TagsWithDateEdited;
|
||||
attachmentsCount: number;
|
||||
};
|
||||
async function resolveNotes(ids: string[]) {
|
||||
console.time("relations");
|
||||
const relations = [
|
||||
...(await db.relations
|
||||
.to({ type: "note", ids }, ["notebook", "tag", "color"])
|
||||
.get()),
|
||||
...(await db.relations.from({ type: "note", ids }, "reminder").get())
|
||||
];
|
||||
console.timeEnd("relations");
|
||||
|
||||
const relationIds: {
|
||||
notebooks: Set<string>;
|
||||
colors: Set<string>;
|
||||
tags: Set<string>;
|
||||
reminders: Set<string>;
|
||||
} = {
|
||||
colors: new Set(),
|
||||
notebooks: new Set(),
|
||||
tags: new Set(),
|
||||
reminders: new Set()
|
||||
};
|
||||
|
||||
const grouped: Record<
|
||||
string,
|
||||
{
|
||||
notebooks: string[];
|
||||
color?: string;
|
||||
tags: string[];
|
||||
reminder?: string;
|
||||
}
|
||||
> = {};
|
||||
for (const relation of relations) {
|
||||
const noteId =
|
||||
relation.toType === "relation" ? relation.fromId : relation.toId;
|
||||
const data = grouped[noteId] || {
|
||||
notebooks: [],
|
||||
tags: []
|
||||
};
|
||||
|
||||
if (relation.toType === "relation" && !data.reminder) {
|
||||
data.reminder = relation.fromId;
|
||||
relationIds.reminders.add(relation.fromId);
|
||||
} else if (relation.fromType === "notebook" && data.notebooks.length < 2) {
|
||||
data.notebooks.push(relation.fromId);
|
||||
relationIds.notebooks.add(relation.fromId);
|
||||
} else if (relation.fromType === "tag" && data.tags.length < 3) {
|
||||
data.tags.push(relation.fromId);
|
||||
relationIds.tags.add(relation.fromId);
|
||||
} else if (relation.fromType === "color" && !data.color) {
|
||||
data.color = relation.fromId;
|
||||
relationIds.colors.add(relation.fromId);
|
||||
}
|
||||
grouped[relation.toId] = data;
|
||||
}
|
||||
|
||||
console.time("resolve");
|
||||
const resolved = {
|
||||
notebooks: await db.notebooks.all.records(
|
||||
Array.from(relationIds.notebooks)
|
||||
),
|
||||
tags: await db.tags.all.records(Array.from(relationIds.tags)),
|
||||
colors: await db.colors.all.records(Array.from(relationIds.colors)),
|
||||
reminders: await db.reminders.all.records(Array.from(relationIds.reminders))
|
||||
};
|
||||
console.timeEnd("resolve");
|
||||
|
||||
const data: Record<string, NoteResolvedData> = {};
|
||||
for (const noteId in grouped) {
|
||||
const group = grouped[noteId];
|
||||
data[noteId] = {
|
||||
color: group.color ? resolved.colors[group.color] : undefined,
|
||||
reminder: group.reminder ? resolved.reminders[group.reminder] : undefined,
|
||||
tags: withDateEdited(group.tags.map((id) => resolved.tags[id])),
|
||||
notebooks: withDateEdited(
|
||||
group.notebooks.map((id) => resolved.notebooks[id])
|
||||
),
|
||||
attachmentsCount:
|
||||
(await db.attachments?.ofNote(noteId, "all").ids())?.length || 0
|
||||
};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function isNoteResolvedData(data: unknown): data is NoteResolvedData {
|
||||
return (
|
||||
typeof data === "object" &&
|
||||
!!data &&
|
||||
"notebooks" in data &&
|
||||
"reminder" in data &&
|
||||
"color" in data &&
|
||||
"tags" in data
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,11 +17,9 @@ 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 { getFormattedDate } from "@notesnook/common";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import KeepAwake from "@sayem314/react-native-keep-awake";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { SafeAreaView, Text, View } from "react-native";
|
||||
import { Modal, SafeAreaView, Text, View } from "react-native";
|
||||
import Animated from "react-native-reanimated";
|
||||
import { db } from "../../common/database";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
@@ -35,7 +33,7 @@ import {
|
||||
} from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import Sync from "../../services/sync";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { eOnLoadNote, eShowMergeDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
@@ -47,6 +45,8 @@ import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import Seperator from "../ui/seperator";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
|
||||
const MergeConflicts = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -62,7 +62,7 @@ const MergeConflicts = () => {
|
||||
|
||||
const applyChanges = async () => {
|
||||
let _content = keep;
|
||||
let note = await db.notes.note(_content.noteId);
|
||||
let note = db.notes.note(_content.noteId).data;
|
||||
await db.notes.add({
|
||||
id: note.id,
|
||||
conflicted: false,
|
||||
@@ -100,7 +100,7 @@ const MergeConflicts = () => {
|
||||
};
|
||||
|
||||
const show = async (item) => {
|
||||
let noteContent = await db.content.get(item.contentId);
|
||||
let noteContent = await db.content.raw(item.contentId);
|
||||
content.current = { ...noteContent };
|
||||
if (__DEV__) {
|
||||
if (!noteContent.conflicted) {
|
||||
@@ -320,7 +320,7 @@ const MergeConflicts = () => {
|
||||
readonly
|
||||
editorId=":conflictPrimary"
|
||||
onLoad={async () => {
|
||||
const note = await db.notes.note(content.current?.noteId);
|
||||
const note = db.notes.note(content.current?.noteId)?.data;
|
||||
if (!note) return;
|
||||
await sleep(300);
|
||||
eSendEvent(eOnLoadNote + ":conflictPrimary", {
|
||||
@@ -357,7 +357,7 @@ const MergeConflicts = () => {
|
||||
readonly
|
||||
editorId=":conflictSecondary"
|
||||
onLoad={async () => {
|
||||
const note = await db.notes.note(content.current?.noteId);
|
||||
const note = db.notes.note(content.current?.noteId)?.data;
|
||||
if (!note) return;
|
||||
await sleep(300);
|
||||
eSendEvent(eOnLoadNote + ":conflictSecondary", {
|
||||
|
||||
@@ -84,7 +84,7 @@ export default function NotePreview({ session, content, note }) {
|
||||
context: "local",
|
||||
positivePress: async () => {
|
||||
await db.trash.delete(note.id);
|
||||
useTrashStore.getState().refresh();
|
||||
useTrashStore.getState().setTrash();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastManager.show({
|
||||
heading: "Permanently deleted items",
|
||||
|
||||
@@ -17,10 +17,11 @@ 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 { Color, Note } from "@notesnook/core/dist/types";
|
||||
import { DefaultColors } from "@notesnook/core/dist/collections/colors";
|
||||
import { Color, ItemReference, Note } from "@notesnook/core/dist/types";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { ScrollView, View } from "react-native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { db } from "../../common/database";
|
||||
@@ -31,29 +32,87 @@ import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { refreshNotesPage } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import ColorPicker from "../dialogs/color-picker";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import { FlashList } from "react-native-actions-sheet";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
|
||||
export const ColorTags = ({ item }: { item: Note }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [isLinked, setIsLinked] = useState<boolean>();
|
||||
const setColorNotes = useMenuStore((state) => state.setColorNotes);
|
||||
useEffect(() => {
|
||||
const checkIsLinked = async (color: Color) => {
|
||||
const hasRelation = await db.relations.from(color, "note").has(note.id);
|
||||
return hasRelation;
|
||||
const isTablet = useSettingStore((state) => state.deviceMode) !== "mobile";
|
||||
const updater = useRelationStore((state) => state.updater);
|
||||
|
||||
const getColorInfo = async (colorCode: string) => {
|
||||
const dbColor = await db.colors.all.find((v) =>
|
||||
v.and([v(`colorCode`, "==", colorCode)])
|
||||
);
|
||||
let isLinked = false;
|
||||
|
||||
if (dbColor) {
|
||||
const hasRelation = await db.relations.from(dbColor, "note").has(item.id);
|
||||
if (hasRelation) {
|
||||
isLinked = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
linked: isLinked,
|
||||
item: dbColor
|
||||
};
|
||||
};
|
||||
|
||||
checkIsLinked(item).then((info) => setIsLinked(info));
|
||||
}, [item, note.id]);
|
||||
const ColorItem = ({ name }: { name: keyof typeof DefaultColors }) => {
|
||||
const color = DefaultColors[name];
|
||||
const [colorInfo, setColorInfo] = useState<{
|
||||
linked: boolean;
|
||||
item: Color | undefined;
|
||||
}>();
|
||||
|
||||
const toggleColor = async () => {
|
||||
await db.relations.unlinkOfType("color", [item.id]);
|
||||
useEffect(() => {
|
||||
getColorInfo(color).then((info) => setColorInfo(info));
|
||||
}, [color]);
|
||||
|
||||
if (!isLinked) {
|
||||
await db.relations.add(item, note);
|
||||
return (
|
||||
<PressableButton
|
||||
type="accent"
|
||||
accentColor={color}
|
||||
accentText={colors.static.white}
|
||||
testID={notesnook.ids.dialogs.actionsheet.color(name)}
|
||||
key={color}
|
||||
onPress={() => changeColor(name)}
|
||||
customStyle={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: isTablet ? 10 : undefined
|
||||
}}
|
||||
>
|
||||
{colorInfo?.linked ? (
|
||||
<Icon testID="icon-check" name="check" color="white" size={SIZE.lg} />
|
||||
) : null}
|
||||
</PressableButton>
|
||||
);
|
||||
};
|
||||
|
||||
const changeColor = async (color: string) => {
|
||||
const colorInfo = await getColorInfo(DefaultColors[color]);
|
||||
|
||||
if (colorInfo.item) {
|
||||
if (colorInfo.linked) {
|
||||
await db.relations.unlink(colorInfo.item, item);
|
||||
} else {
|
||||
await db.relations.add(colorInfo.item, item);
|
||||
}
|
||||
} else {
|
||||
const colorId = await db.colors.add({
|
||||
title: color,
|
||||
colorCode: DefaultColors[color]
|
||||
});
|
||||
|
||||
const dbColor = await db.colors.color(colorId);
|
||||
if (dbColor) {
|
||||
await db.relations.add(dbColor as unknown as ItemReference, item);
|
||||
}
|
||||
}
|
||||
|
||||
useRelationStore.getState().update();
|
||||
@@ -63,109 +122,20 @@ const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<PressableButton
|
||||
type="accent"
|
||||
accentColor={item.colorCode}
|
||||
accentText={colors.static.white}
|
||||
testID={notesnook.ids.dialogs.actionsheet.color(item.colorCode)}
|
||||
key={item.id}
|
||||
onPress={toggleColor}
|
||||
customStyle={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
flexGrow: isTablet ? undefined : 1,
|
||||
paddingHorizontal: 12,
|
||||
paddingRight: 0,
|
||||
alignItems: "center",
|
||||
marginRight: 5
|
||||
justifyContent: isTablet ? "center" : "space-between"
|
||||
}}
|
||||
>
|
||||
{isLinked ? (
|
||||
<Icon testID="icon-check" name="check" color="white" size={SIZE.lg} />
|
||||
) : null}
|
||||
</PressableButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColorTags = ({ item }: { item: Note }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const colorNotes = useMenuStore((state) => state.colorNotes);
|
||||
const isTablet = useSettingStore((state) => state.deviceMode) !== "mobile";
|
||||
const updater = useRelationStore((state) => state.updater);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const note = item;
|
||||
|
||||
const renderItem = useCallback(
|
||||
({ item }: { item: Color }) => (
|
||||
<ColorItem note={note} key={item.id} item={item} />
|
||||
),
|
||||
[note]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColorPicker visible={visible} setVisible={setVisible} />
|
||||
<View
|
||||
style={{
|
||||
flexGrow: isTablet ? undefined : 1,
|
||||
paddingRight: 0,
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{!colorNotes || !colorNotes.length ? (
|
||||
<Button
|
||||
onPress={async () => {
|
||||
useSettingStore.getState().setSheetKeyboardHandler(false);
|
||||
setVisible(true);
|
||||
}}
|
||||
buttonType={{
|
||||
text: colors.primary.accent
|
||||
}}
|
||||
title="Add color"
|
||||
type="grayBg"
|
||||
icon="plus"
|
||||
iconPosition="right"
|
||||
height={30}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 8
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<PressableButton
|
||||
customStyle={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 5
|
||||
}}
|
||||
type="grayBg"
|
||||
onPress={() => {
|
||||
useSettingStore.getState().setSheetKeyboardHandler(false);
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
testID="icon-plus"
|
||||
name="plus"
|
||||
color={colors.primary.icon}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
</PressableButton>
|
||||
)}
|
||||
|
||||
<FlashList
|
||||
data={colorNotes}
|
||||
estimatedItemSize={30}
|
||||
horizontal
|
||||
extraData={updater}
|
||||
renderItem={renderItem}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
{Object.keys(DefaultColors).map((name: keyof typeof DefaultColors) => {
|
||||
return <ColorItem key={name} name={name} />;
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -120,15 +120,13 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
<Line bottom={0} />
|
||||
{item.type === "note" ? <Tags close={close} item={item} /> : null}
|
||||
|
||||
{item.type === "note" ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Notebooks note={item} close={close} />
|
||||
</View>
|
||||
) : null}
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Notebooks note={item} close={close} />
|
||||
</View>
|
||||
|
||||
<Items
|
||||
item={item}
|
||||
|
||||
@@ -218,7 +218,8 @@ export const Items = ({ item, buttons, close }) => {
|
||||
<ScrollView
|
||||
horizontal
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 12
|
||||
}}
|
||||
>
|
||||
{topBarItems.map(renderTopBarItem)}
|
||||
|
||||
@@ -17,24 +17,16 @@ 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 { Note } from "@notesnook/core";
|
||||
import { GroupHeader, Note } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { RefObject, useCallback, useEffect } from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Keyboard,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from "react-native";
|
||||
import { Keyboard, TouchableOpacity, View } from "react-native";
|
||||
import { ActionSheetRef, FlashList } from "react-native-actions-sheet";
|
||||
import { db } from "../../../common/database";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { ItemSelection } from "../../../stores/item-selection-store";
|
||||
import {
|
||||
useNotebookStore,
|
||||
useNotebooks
|
||||
} from "../../../stores/use-notebook-store";
|
||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
@@ -47,7 +39,6 @@ import { IconButton } from "../../ui/icon-button";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { NotebookItem } from "./notebook-item";
|
||||
import { useNotebookItemSelectionStore } from "./store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
|
||||
async function updateInitialSelectionState(items: string[]) {
|
||||
const relations = await db.relations
|
||||
@@ -94,7 +85,7 @@ const MoveNoteSheet = ({
|
||||
actionSheetRef: RefObject<ActionSheetRef>;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [notebooks, loading] = useNotebooks();
|
||||
const notebooks = useNotebookStore((state) => state.notebooks);
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const selectedItemsList = useSelectionStore(
|
||||
(state) => state.selectedItemsList
|
||||
@@ -144,9 +135,10 @@ const MoveNoteSheet = ({
|
||||
};
|
||||
|
||||
const renderNotebook = useCallback(
|
||||
({ item, index }: { item: string | number; index: number }) => (
|
||||
<NotebookItem items={notebooks} id={index} index={index} />
|
||||
),
|
||||
({ item, index }: { item: string | GroupHeader; index: number }) =>
|
||||
(item as GroupHeader).type === "header" ? null : (
|
||||
<NotebookItem items={notebooks} id={item as string} index={index} />
|
||||
),
|
||||
[notebooks]
|
||||
);
|
||||
|
||||
@@ -228,11 +220,12 @@ const MoveNoteSheet = ({
|
||||
}}
|
||||
>
|
||||
<FlashList
|
||||
data={notebooks?.ids}
|
||||
data={notebooks?.ids?.filter((id) => typeof id === "string")}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
estimatedItemSize={50}
|
||||
keyExtractor={(item) => item as string}
|
||||
renderItem={renderNotebook}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
@@ -243,16 +236,7 @@ const MoveNoteSheet = ({
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator
|
||||
size={SIZE.lg}
|
||||
color={colors.primary.accent}
|
||||
/>
|
||||
) : (
|
||||
<Paragraph color={colors.primary.icon}>
|
||||
No notebooks
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph color={colors.primary.icon}>No notebooks</Paragraph>
|
||||
</View>
|
||||
}
|
||||
ListFooterComponent={<View style={{ height: 50 }} />}
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { View, useWindowDimensions } from "react-native";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { useTotalNotes } from "../../../hooks/use-db-item";
|
||||
@@ -46,41 +46,31 @@ export const NotebookItem = ({
|
||||
parent,
|
||||
items
|
||||
}: {
|
||||
id: string | number;
|
||||
id: string;
|
||||
currentLevel?: number;
|
||||
index: number;
|
||||
parent?: NotebookParentProp;
|
||||
items?: VirtualizedGrouping<Notebook>;
|
||||
}) => {
|
||||
const { nestedNotebooks, notebook: item } = useNotebook(id, items, true);
|
||||
const expanded = useNotebookExpandedStore((state) =>
|
||||
item?.id ? state.expanded[item?.id] : false
|
||||
);
|
||||
const { totalNotes: totalNotes, getTotalNotes } = useTotalNotes("notebook");
|
||||
const { nestedNotebooks, notebook: item } = useNotebook(id, items);
|
||||
const ids = useMemo(() => (id ? [id] : []), [id]);
|
||||
const { totalNotes: totalNotes } = useTotalNotes(ids, "notebook");
|
||||
const focusedRouteId = useNavigationStore((state) => state.focusedRouteId);
|
||||
const { colors } = useThemeColors("sheet");
|
||||
const selection = useNotebookItemSelectionStore((state) =>
|
||||
item?.id ? state.selection[item?.id] : undefined
|
||||
id ? state.selection[id] : undefined
|
||||
);
|
||||
const isSelected = selection === "selected";
|
||||
const isFocused = focusedRouteId === id;
|
||||
const { fontScale } = useWindowDimensions();
|
||||
|
||||
useEffect(() => {
|
||||
if (item?.id) {
|
||||
getTotalNotes([item?.id]);
|
||||
}
|
||||
}, [getTotalNotes, item?.id]);
|
||||
const expanded = useNotebookExpandedStore((state) => state.expanded[id]);
|
||||
|
||||
const onPress = () => {
|
||||
if (!item) return;
|
||||
const state = useNotebookItemSelectionStore.getState();
|
||||
|
||||
if (isSelected) {
|
||||
state.markAs(
|
||||
item,
|
||||
!state.initialState[item?.id] ? undefined : "deselected"
|
||||
);
|
||||
state.markAs(item, !state.initialState[id] ? undefined : "deselected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,7 +112,7 @@ export const NotebookItem = ({
|
||||
item,
|
||||
!isSelected
|
||||
? "selected"
|
||||
: !state.initialState[item?.id]
|
||||
: !state.initialState[id]
|
||||
? undefined
|
||||
: "deselected"
|
||||
);
|
||||
@@ -151,8 +141,7 @@ export const NotebookItem = ({
|
||||
size={SIZE.xl}
|
||||
color={isSelected ? colors.selected.icon : colors.primary.icon}
|
||||
onPress={() => {
|
||||
if (!item?.id) return;
|
||||
useNotebookExpandedStore.getState().setExpanded(item?.id);
|
||||
useNotebookExpandedStore.getState().setExpanded(id);
|
||||
}}
|
||||
top={0}
|
||||
left={0}
|
||||
@@ -212,9 +201,9 @@ export const NotebookItem = ({
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{item?.id && totalNotes?.(item?.id) ? (
|
||||
{totalNotes?.(id) ? (
|
||||
<Paragraph size={SIZE.sm} color={colors.secondary.paragraph}>
|
||||
{totalNotes(item?.id)}
|
||||
{totalNotes(id)}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
<IconButton
|
||||
@@ -242,8 +231,8 @@ export const NotebookItem = ({
|
||||
? null
|
||||
: nestedNotebooks?.ids.map((id, index) => (
|
||||
<NotebookItem
|
||||
key={item?.id + "_" + index}
|
||||
id={index}
|
||||
key={id as string}
|
||||
id={id as string}
|
||||
index={index}
|
||||
currentLevel={currentLevel + 1}
|
||||
items={nestedNotebooks}
|
||||
|
||||
@@ -115,7 +115,7 @@ const ManageTagsSheet = (props: {
|
||||
});
|
||||
} else {
|
||||
db.tags.all.sorted(db.settings.getGroupOptions("tags")).then((items) => {
|
||||
console.log("items loaded tags", items.ids);
|
||||
console.log("items loaded tags");
|
||||
setTags(items);
|
||||
});
|
||||
}
|
||||
@@ -177,7 +177,7 @@ const ManageTagsSheet = (props: {
|
||||
}
|
||||
|
||||
useRelationStore.getState().update();
|
||||
useTagStore.getState().refresh();
|
||||
useTagStore.getState().setTags();
|
||||
setQuery(undefined);
|
||||
} catch (e) {
|
||||
ToastManager.show({
|
||||
@@ -225,7 +225,7 @@ const ManageTagsSheet = (props: {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
useTagStore.getState().refresh();
|
||||
useTagStore.getState().setTags();
|
||||
useRelationStore.getState().update();
|
||||
refreshTags();
|
||||
setTimeout(() => {
|
||||
@@ -237,10 +237,11 @@ const ManageTagsSheet = (props: {
|
||||
);
|
||||
|
||||
const renderTag = useCallback(
|
||||
({ item, index }: { item: string | number; index: number }) => (
|
||||
({ item }: { item: string; index: number }) => (
|
||||
<TagItem
|
||||
key={item as string}
|
||||
tags={tags as VirtualizedGrouping<Tag>}
|
||||
id={index}
|
||||
id={item as string}
|
||||
onPress={onPress}
|
||||
/>
|
||||
),
|
||||
@@ -302,13 +303,13 @@ const ManageTagsSheet = (props: {
|
||||
) : null}
|
||||
|
||||
<FlatList
|
||||
data={tags?.ids}
|
||||
data={tags?.ids?.filter((id) => typeof id === "string") as string[]}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
keyboardShouldPersistTaps
|
||||
keyboardDismissMode="interactive"
|
||||
keyExtractor={(item) => item + "_tag"}
|
||||
keyExtractor={(item) => item as string}
|
||||
renderItem={renderTag}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
@@ -351,38 +352,29 @@ const TagItem = ({
|
||||
tags,
|
||||
onPress
|
||||
}: {
|
||||
id: string | number;
|
||||
id: string;
|
||||
tags: VirtualizedGrouping<Tag>;
|
||||
onPress: (id: string) => void;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [tag] = useDBItem(id, "tag", tags);
|
||||
const selection = useTagItemSelection((state) =>
|
||||
tag?.id ? state.selection[tag?.id] : false
|
||||
);
|
||||
const selection = useTagItemSelection((state) => state.selection[id]);
|
||||
|
||||
return !tag ? null : (
|
||||
return (
|
||||
<PressableButton
|
||||
key={tag?.id}
|
||||
customStyle={{
|
||||
flexDirection: "row",
|
||||
marginVertical: 5,
|
||||
justifyContent: "flex-start",
|
||||
height: 40
|
||||
}}
|
||||
onPress={() => {
|
||||
if (!tag) return;
|
||||
onPress(tag.id);
|
||||
}}
|
||||
onPress={() => onPress(id)}
|
||||
type="gray"
|
||||
>
|
||||
{!tag ? null : (
|
||||
<Icon
|
||||
size={22}
|
||||
onPress={() => {
|
||||
if (!tag) return;
|
||||
onPress(tag.id);
|
||||
}}
|
||||
onPress={() => onPress(id)}
|
||||
color={
|
||||
selection === "selected" || selection === "intermediate"
|
||||
? colors.selected.icon
|
||||
@@ -414,6 +406,7 @@ const TagItem = ({
|
||||
style={{
|
||||
width: 200,
|
||||
height: 30,
|
||||
// backgroundColor: colors.secondary.background,
|
||||
borderRadius: 5
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -36,7 +36,6 @@ import { IconButton } from "../../ui/icon-button";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
|
||||
export const MoveNotes = ({
|
||||
notebook,
|
||||
@@ -46,13 +45,11 @@ export const MoveNotes = ({
|
||||
fwdRef: RefObject<ActionSheetRef>;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const currentNotebook = notebook;
|
||||
|
||||
const [currentNotebook, setCurrentNotebook] = useState(notebook);
|
||||
const { height } = useWindowDimensions();
|
||||
const [selectedNoteIds, setSelectedNoteIds] = useState<string[]>([]);
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const [existingNoteIds, setExistingNoteIds] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
db.notes?.all.sorted(db.settings.getGroupOptions("notes")).then((notes) => {
|
||||
setNotes(notes);
|
||||
@@ -88,18 +85,17 @@ export const MoveNotes = ({
|
||||
);
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({ index }: { item: string | number; index: number }) => {
|
||||
({ item }: { item: string }) => {
|
||||
return (
|
||||
<SelectableNoteItem
|
||||
id={index}
|
||||
id={item}
|
||||
items={notes}
|
||||
select={select}
|
||||
selected={(id) => selectedNoteIds?.indexOf(id) > -1}
|
||||
exists={(id) => existingNoteIds.indexOf(id) > -1}
|
||||
selected={selectedNoteIds?.indexOf(item) > -1}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[existingNoteIds, notes, select, selectedNoteIds]
|
||||
[notes, select, selectedNoteIds]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -132,7 +128,9 @@ export const MoveNotes = ({
|
||||
</Paragraph>
|
||||
</View>
|
||||
}
|
||||
data={notes?.ids}
|
||||
data={(notes?.ids as string[])?.filter(
|
||||
(id) => existingNoteIds?.indexOf(id) === -1
|
||||
)}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
{selectedNoteIds.length > 0 ? (
|
||||
@@ -159,19 +157,21 @@ const SelectableNoteItem = ({
|
||||
id,
|
||||
items,
|
||||
select,
|
||||
selected,
|
||||
exists
|
||||
selected
|
||||
}: {
|
||||
id: string | number;
|
||||
id: string;
|
||||
items?: VirtualizedGrouping<Note>;
|
||||
select: (id: string) => void;
|
||||
selected?: (id: string) => boolean;
|
||||
exists: (id: string) => boolean;
|
||||
selected?: boolean;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [item] = useDBItem(id, "note", items);
|
||||
const [item, setItem] = useState<Note>();
|
||||
|
||||
return !item || exists(item.id) ? null : (
|
||||
useEffect(() => {
|
||||
items?.item(id).then((item) => setItem(item));
|
||||
}, [id, items]);
|
||||
|
||||
return !item ? null : (
|
||||
<PressableButton
|
||||
testID="listitem.select"
|
||||
onPress={() => {
|
||||
@@ -197,14 +197,10 @@ const SelectableNoteItem = ({
|
||||
select(item?.id);
|
||||
}}
|
||||
name={
|
||||
selected?.(item?.id)
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
selected ? "check-circle-outline" : "checkbox-blank-circle-outline"
|
||||
}
|
||||
type="selected"
|
||||
color={
|
||||
selected?.(item?.id) ? colors.selected.icon : colors.primary.icon
|
||||
}
|
||||
color={selected ? colors.selected.icon : colors.primary.icon}
|
||||
/>
|
||||
|
||||
<View
|
||||
|
||||
@@ -16,7 +16,7 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { GroupHeader, Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, {
|
||||
createContext,
|
||||
@@ -123,11 +123,7 @@ export const NotebookSheet = () => {
|
||||
nestedNotebooks: notebooks,
|
||||
nestedNotebookNotesCount: totalNotes,
|
||||
groupOptions
|
||||
} = useNotebook(
|
||||
currentRoute === "Notebook" ? root : undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
} = useNotebook(currentRoute === "Notebook" ? root : undefined);
|
||||
|
||||
const PLACEHOLDER_DATA = {
|
||||
heading: "Notebooks",
|
||||
@@ -144,16 +140,17 @@ export const NotebookSheet = () => {
|
||||
item,
|
||||
index
|
||||
}: {
|
||||
item: string | number;
|
||||
item: string | GroupHeader;
|
||||
index: number;
|
||||
}) => (
|
||||
<NotebookItem
|
||||
items={notebooks}
|
||||
id={index}
|
||||
index={index}
|
||||
totalNotes={totalNotes}
|
||||
/>
|
||||
);
|
||||
}) =>
|
||||
(item as GroupHeader).type === "header" ? null : (
|
||||
<NotebookItem
|
||||
items={notebooks}
|
||||
id={item as string}
|
||||
index={index}
|
||||
totalNotes={totalNotes}
|
||||
/>
|
||||
);
|
||||
|
||||
const selectionContext = {
|
||||
selection: selection,
|
||||
@@ -180,9 +177,11 @@ export const NotebookSheet = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (canShow) {
|
||||
setImmediate(async () => {
|
||||
setTimeout(async () => {
|
||||
if (!focusedRouteId) return;
|
||||
const nextRoot = await findRootNotebookId(focusedRouteId);
|
||||
setRoot(nextRoot);
|
||||
|
||||
if (nextRoot !== currentItem.current) {
|
||||
console.log(
|
||||
"NotebookSheet.useEffect.canShow",
|
||||
@@ -204,9 +203,8 @@ export const NotebookSheet = () => {
|
||||
ref.current?.show(snapPoint);
|
||||
}
|
||||
console.log("NotebookSheet.useEffect.canShow", focusedRouteId);
|
||||
setRoot(nextRoot);
|
||||
onRequestUpdate();
|
||||
});
|
||||
}, 0);
|
||||
} else {
|
||||
setSelection([]);
|
||||
setEnabled(false);
|
||||
@@ -304,48 +302,28 @@ export const NotebookSheet = () => {
|
||||
}}
|
||||
>
|
||||
{enabled ? (
|
||||
<>
|
||||
<IconButton
|
||||
customStyle={{
|
||||
marginLeft: 10,
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
}}
|
||||
onPress={async () => {
|
||||
await deleteItems(
|
||||
selection.map((notebook) => notebook.id),
|
||||
"notebook"
|
||||
);
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
return;
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
tooltipText="Move to trash"
|
||||
tooltipPosition={1}
|
||||
name="delete"
|
||||
size={22}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
customStyle={{
|
||||
marginLeft: 10,
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
}}
|
||||
onPress={() => {
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
tooltipText="Clear selection"
|
||||
tooltipPosition={1}
|
||||
name="close"
|
||||
size={22}
|
||||
/>
|
||||
</>
|
||||
<IconButton
|
||||
customStyle={{
|
||||
marginLeft: 10,
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
}}
|
||||
onPress={async () => {
|
||||
await deleteItems(
|
||||
selection.map((notebook) => notebook.id),
|
||||
"notebook"
|
||||
);
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
return;
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
tooltipText="Move to trash"
|
||||
tooltipPosition={1}
|
||||
name="delete"
|
||||
size={22}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<IconButton
|
||||
@@ -418,6 +396,7 @@ export const NotebookSheet = () => {
|
||||
progressBackgroundColor={colors.primary.background}
|
||||
/>
|
||||
}
|
||||
keyExtractor={(item) => item as string}
|
||||
renderItem={renderNotebook}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
@@ -447,7 +426,7 @@ const NotebookItem = ({
|
||||
parent,
|
||||
items
|
||||
}: {
|
||||
id: string | number;
|
||||
id: string;
|
||||
totalNotes: (id: string) => number;
|
||||
currentLevel?: number;
|
||||
index: number;
|
||||
@@ -458,7 +437,7 @@ const NotebookItem = ({
|
||||
nestedNotebookNotesCount,
|
||||
nestedNotebooks,
|
||||
notebook: item
|
||||
} = useNotebook(id, items, true);
|
||||
} = useNotebook(id, items);
|
||||
const isFocused = useNavigationStore((state) => state.focusedRouteId === id);
|
||||
const { colors } = useThemeColors("sheet");
|
||||
const selection = useSelection();
|
||||
@@ -466,9 +445,7 @@ const NotebookItem = ({
|
||||
selection.selection.findIndex((selected) => selected.id === item?.id) > -1;
|
||||
|
||||
const { fontScale } = useWindowDimensions();
|
||||
const expanded = useNotebookExpandedStore((state) =>
|
||||
item?.id ? state.expanded[item?.id] : undefined
|
||||
);
|
||||
const expanded = useNotebookExpandedStore((state) => state.expanded[id]);
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -498,7 +475,8 @@ const NotebookItem = ({
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12,
|
||||
paddingLeft: 0,
|
||||
paddingRight: 12,
|
||||
borderRadius: 0
|
||||
}}
|
||||
>
|
||||
@@ -508,33 +486,6 @@ const NotebookItem = ({
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{nestedNotebooks?.ids.length ? (
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
color={isSelected ? colors.selected.icon : colors.primary.icon}
|
||||
onPress={() => {
|
||||
if (!item?.id) return;
|
||||
useNotebookExpandedStore.getState().setExpanded(item?.id);
|
||||
}}
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
right={0}
|
||||
customStyle={{
|
||||
width: 35,
|
||||
height: 35
|
||||
}}
|
||||
name={expanded ? "chevron-down" : "chevron-right"}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selection.enabled ? (
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
@@ -544,9 +495,8 @@ const NotebookItem = ({
|
||||
bottom={0}
|
||||
right={0}
|
||||
customStyle={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
marginRight: 5
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
name={
|
||||
isSelected
|
||||
@@ -556,6 +506,36 @@ const NotebookItem = ({
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{nestedNotebooks?.ids.length ? (
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
color={isSelected ? colors.selected.icon : colors.primary.icon}
|
||||
onPress={() => {
|
||||
useNotebookExpandedStore.getState().setExpanded(id);
|
||||
}}
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
right={0}
|
||||
customStyle={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
name={expanded ? "chevron-down" : "chevron-right"}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{selection?.enabled ? null : (
|
||||
<View
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Paragraph
|
||||
color={
|
||||
isFocused ? colors.selected.paragraph : colors.secondary.paragraph
|
||||
@@ -573,9 +553,9 @@ const NotebookItem = ({
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{item?.id && totalNotes?.(item?.id) ? (
|
||||
{totalNotes?.(id) ? (
|
||||
<Paragraph size={SIZE.sm} color={colors.secondary.paragraph}>
|
||||
{totalNotes(item?.id)}
|
||||
{totalNotes(id)}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
<IconButton
|
||||
@@ -600,11 +580,10 @@ const NotebookItem = ({
|
||||
|
||||
{!expanded
|
||||
? null
|
||||
: item &&
|
||||
nestedNotebooks?.ids.map((id, index) => (
|
||||
: nestedNotebooks?.ids.map((id, index) => (
|
||||
<NotebookItem
|
||||
key={item.id + "_" + index}
|
||||
id={index}
|
||||
key={id as string}
|
||||
id={id as string}
|
||||
index={index}
|
||||
totalNotes={nestedNotebookNotesCount}
|
||||
currentLevel={currentLevel + 1}
|
||||
|
||||
@@ -143,9 +143,7 @@ const Sort = ({ type, screen }) => {
|
||||
/>
|
||||
) : (
|
||||
Object.keys(SORT).map((item) =>
|
||||
(item === "title" &&
|
||||
groupOptions.groupBy !== "none" &&
|
||||
screen !== "TopicSheet") ||
|
||||
(item === "title" && groupOptions.groupBy !== "none") ||
|
||||
((screen !== "Tags" || screen !== "Reminders") &&
|
||||
item === "dateModified") ||
|
||||
((screen === "Tags" || screen === "Reminders") &&
|
||||
@@ -173,7 +171,7 @@ const Sort = ({ type, screen }) => {
|
||||
...groupOptions,
|
||||
sortBy: type === "trash" ? "dateDeleted" : item
|
||||
};
|
||||
if (screen === "TopicSheet") {
|
||||
if (type === "topics") {
|
||||
_groupOptions.groupBy = "none";
|
||||
}
|
||||
await updateGroupOptions(_groupOptions);
|
||||
|
||||
@@ -17,29 +17,26 @@ 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 { Color } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { ColoredNotes } from "../../screens/notes/colored";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { Color } from "@notesnook/core";
|
||||
|
||||
export const ColorSection = React.memo(
|
||||
function ColorSection() {
|
||||
const [colorNotes, loadingColors] = useMenuStore((state) => [
|
||||
state.colorNotes,
|
||||
state.loadingColors
|
||||
]);
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const colorNotes = useMenuStore((state) => state.colorNotes);
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const setColorNotes = useMenuStore((state) => state.setColorNotes);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -48,11 +45,9 @@ export const ColorSection = React.memo(
|
||||
}
|
||||
}, [loading, setColorNotes]);
|
||||
|
||||
return !loadingColors
|
||||
? colorNotes.map((item) => {
|
||||
return <ColorItem key={item.id} item={item} />;
|
||||
})
|
||||
: null;
|
||||
return colorNotes.map((item) => {
|
||||
return <ColorItem key={item.id} item={item} />;
|
||||
});
|
||||
},
|
||||
() => true
|
||||
);
|
||||
@@ -61,10 +56,36 @@ const ColorItem = React.memo(
|
||||
function ColorItem({ item }: { item: Color }) {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
const setColorNotes = useMenuStore((state) => state.setColorNotes);
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item.id
|
||||
const [headerTextState, setHeaderTextState] = useState<{
|
||||
id: string | undefined;
|
||||
}>({
|
||||
id: undefined
|
||||
});
|
||||
const isFocused = headerTextState?.id === item.id;
|
||||
|
||||
const onHeaderStateChange = useCallback(
|
||||
(state: any) => {
|
||||
setTimeout(() => {
|
||||
let id = state.focusedRouteId;
|
||||
if (id === item.id) {
|
||||
setHeaderTextState({ id: state.currentScreen.id });
|
||||
} else {
|
||||
if (headerTextState !== null) {
|
||||
setHeaderTextState({ id: undefined });
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
[headerTextState, item.id]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const remove = useNavigationStore.subscribe(onHeaderStateChange);
|
||||
return () => {
|
||||
remove();
|
||||
};
|
||||
}, [headerTextState, onHeaderStateChange]);
|
||||
|
||||
const onPress = (item: Color) => {
|
||||
ColoredNotes.navigate(item, false);
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ 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, { useCallback } from "react";
|
||||
import { FlatList, View } from "react-native";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
@@ -25,16 +24,18 @@ import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
import { eOpenPremiumDialog } from "../../utils/events";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
import { ColorSection } from "./color-section";
|
||||
import { MenuItem } from "./menu-item";
|
||||
import { TagsSection } from "./pinned-section";
|
||||
import { UserStatus } from "./user-status";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
|
||||
export const SideMenu = React.memo(
|
||||
function SideMenu() {
|
||||
@@ -43,8 +44,7 @@ export const SideMenu = React.memo(
|
||||
const subscriptionType = useUserStore(
|
||||
(state) => state.user?.subscription?.type
|
||||
);
|
||||
const isAppLoading = useSettingStore((state) => state.isAppLoading);
|
||||
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
@@ -96,7 +96,7 @@ export const SideMenu = React.memo(
|
||||
[noTextMode]
|
||||
);
|
||||
|
||||
return !isAppLoading && introCompleted ? (
|
||||
return !loading && introCompleted ? (
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
|
||||
@@ -33,9 +33,10 @@ import Paragraph from "../ui/typography/paragraph";
|
||||
export const MenuItem = React.memo(
|
||||
function MenuItem({ item, index, testID, rightBtn }) {
|
||||
const { colors } = useThemeColors();
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item.name
|
||||
const [headerTextState, setHeaderTextState] = useState(
|
||||
useNavigationStore.getState().focusedRouteId
|
||||
);
|
||||
let isFocused = headerTextState?.id === item.name;
|
||||
const primaryColors = isFocused ? colors.selected : colors.primary;
|
||||
|
||||
const _onPress = () => {
|
||||
@@ -43,10 +44,7 @@ export const MenuItem = React.memo(
|
||||
item.func();
|
||||
} else {
|
||||
if (useNavigationStore.getState().currentRoute !== item.name) {
|
||||
Navigation.navigate(item.name, {
|
||||
canGoBack: false,
|
||||
beta: item.isBeta
|
||||
});
|
||||
Navigation.push(item.name, { canGoBack: false, beta: item.isBeta });
|
||||
}
|
||||
}
|
||||
if (item.close) {
|
||||
@@ -56,6 +54,29 @@ export const MenuItem = React.memo(
|
||||
}
|
||||
};
|
||||
|
||||
const onHeaderStateChange = useCallback(
|
||||
(state) => {
|
||||
setTimeout(() => {
|
||||
let id = state.focusedRouteId;
|
||||
if (id === item.name) {
|
||||
setHeaderTextState({ id: state.focusedRouteId });
|
||||
} else {
|
||||
if (headerTextState !== null) {
|
||||
setHeaderTextState(null);
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
[headerTextState, item.name]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let unsub = useNavigationStore.subscribe(onHeaderStateChange);
|
||||
return () => {
|
||||
unsub();
|
||||
};
|
||||
}, [headerTextState, onHeaderStateChange]);
|
||||
|
||||
return (
|
||||
<PressableButton
|
||||
testID={testID}
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { Notebook, Tag } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { FlatList, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
@@ -28,7 +28,7 @@ import { TaggedNotes } from "../../screens/notes/tagged";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import { Properties } from "../properties";
|
||||
import { Button } from "../ui/button";
|
||||
@@ -41,8 +41,8 @@ import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
export const TagsSection = React.memo(
|
||||
function TagsSection() {
|
||||
const [menuPins] = useMenuStore((state) => [state.menuPins]);
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const menuPins = useMenuStore((state) => state.menuPins);
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const setMenuPins = useMenuStore((state) => state.setMenuPins);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -109,13 +109,44 @@ export const PinItem = React.memo(
|
||||
const setMenuPins = useMenuStore((state) => state.setMenuPins);
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === item.id
|
||||
);
|
||||
const primaryColors = isFocused ? colors.selected : colors.primary;
|
||||
const color = isFocused ? colors.selected.accent : colors.primary.icon;
|
||||
const [headerTextState, setHeaderTextState] = useState<{
|
||||
id?: string;
|
||||
}>({});
|
||||
const primaryColors =
|
||||
headerTextState?.id === item.id ? colors.selected : colors.primary;
|
||||
|
||||
const isFocused = headerTextState?.id === item.id;
|
||||
const color =
|
||||
headerTextState?.id === item.id
|
||||
? colors.selected.accent
|
||||
: colors.primary.icon;
|
||||
const fwdRef = useRef();
|
||||
|
||||
const onHeaderStateChange = useCallback(
|
||||
(state: any) => {
|
||||
setTimeout(() => {
|
||||
const id = state.focusedRouteId;
|
||||
if (id === item.id) {
|
||||
setHeaderTextState({
|
||||
id
|
||||
});
|
||||
} else {
|
||||
if (headerTextState !== null) {
|
||||
setHeaderTextState({});
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
[headerTextState, item.id]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const remove = useNavigationStore.subscribe(onHeaderStateChange);
|
||||
return () => {
|
||||
remove();
|
||||
};
|
||||
}, [headerTextState, onHeaderStateChange]);
|
||||
|
||||
const icons = {
|
||||
topic: "bookmark",
|
||||
notebook: "book-outline",
|
||||
|
||||
@@ -90,7 +90,6 @@ const SheetWrapper = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (useUserStore.getState().disableAppLockRequests) return;
|
||||
if (SettingsService.get().appLockMode === "background") {
|
||||
if (appState === "background") {
|
||||
const ref = fwdRef || localRef;
|
||||
|
||||
@@ -192,7 +192,7 @@ export const useActions = ({
|
||||
});
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
useTagStore.getState().refresh();
|
||||
useTagStore.getState().setTags();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useRelationStore.getState().update();
|
||||
@@ -223,7 +223,7 @@ export const useActions = ({
|
||||
await db.tags.remove(item.id);
|
||||
}
|
||||
setImmediate(() => {
|
||||
useTagStore.getState().refresh();
|
||||
useTagStore.getState().setTags();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useRelationStore.getState().update();
|
||||
});
|
||||
|
||||
@@ -78,10 +78,11 @@ import {
|
||||
import PremiumService from "../services/premium";
|
||||
import SettingsService from "../services/settings";
|
||||
import Sync from "../services/sync";
|
||||
import { initAfterSync } from "../stores";
|
||||
import { initAfterSync, initialize } from "../stores";
|
||||
import { useAttachmentStore } from "../stores/use-attachment-store";
|
||||
import { useEditorStore } from "../stores/use-editor-store";
|
||||
import { useMessageStore } from "../stores/use-message-store";
|
||||
import { useNoteStore } from "../stores/use-notes-store";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { SyncStatus, useUserStore } from "../stores/use-user-store";
|
||||
import { updateStatusBarColor } from "../utils/colors";
|
||||
@@ -277,7 +278,7 @@ const onSubscriptionError = async (error: RNIap.PurchaseError) => {
|
||||
const SodiumEventEmitter = new NativeEventEmitter(NativeModules.Sodium);
|
||||
|
||||
export const useAppEvents = () => {
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const [setLastSynced, setUser, appLocked, syncing] = useUserStore((state) => [
|
||||
state.setLastSynced,
|
||||
state.setUser,
|
||||
@@ -627,7 +628,8 @@ export const useAppEvents = () => {
|
||||
if (!db.isInitialized) {
|
||||
await db.init();
|
||||
}
|
||||
useSettingStore.getState().setAppLoading(false);
|
||||
await initialize();
|
||||
useNoteStore.getState().setLoading(false);
|
||||
},
|
||||
disableClosing: true
|
||||
});
|
||||
@@ -654,7 +656,10 @@ export const useAppEvents = () => {
|
||||
}
|
||||
}
|
||||
if (IsDatabaseMigrationRequired()) return;
|
||||
useSettingStore.getState().setAppLoading(false);
|
||||
await initialize();
|
||||
setImmediate(() => {
|
||||
useNoteStore.getState().setLoading(false);
|
||||
});
|
||||
Walkthrough.init();
|
||||
}, [IsDatabaseMigrationRequired]);
|
||||
|
||||
@@ -665,7 +670,7 @@ export const useAppEvents = () => {
|
||||
if (
|
||||
!state.appLocked &&
|
||||
db.isInitialized &&
|
||||
useSettingStore.getState().isAppLoading
|
||||
useNoteStore.getState().loading
|
||||
) {
|
||||
initializeDatabase();
|
||||
sub();
|
||||
|
||||
@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useAttachmentStore } from "../stores/use-attachment-store";
|
||||
import { Attachment } from "@notesnook/core";
|
||||
|
||||
type AttachmentProgress = {
|
||||
type: string;
|
||||
@@ -28,7 +27,7 @@ type AttachmentProgress = {
|
||||
};
|
||||
|
||||
export const useAttachmentProgress = (
|
||||
attachment?: Attachment,
|
||||
attachment: any,
|
||||
encryption?: boolean
|
||||
): [
|
||||
AttachmentProgress | undefined,
|
||||
@@ -46,9 +45,7 @@ export const useAttachmentProgress = (
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const attachmentProgress = !attachment
|
||||
? null
|
||||
: progress?.[attachment?.hash];
|
||||
const attachmentProgress = progress?.[attachment?.metadata?.hash];
|
||||
if (attachmentProgress) {
|
||||
const type = attachmentProgress.type;
|
||||
const loaded =
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
Tag,
|
||||
VirtualizedGrouping
|
||||
} from "@notesnook/core";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { db } from "../common/database";
|
||||
import {
|
||||
eSendEvent,
|
||||
@@ -45,36 +45,22 @@ type ItemTypeKey = {
|
||||
shortcut: Shortcut;
|
||||
};
|
||||
|
||||
function isValidIdOrIndex(idOrIndex?: string | number) {
|
||||
return typeof idOrIndex === "number" || typeof idOrIndex === "string";
|
||||
}
|
||||
|
||||
export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
idOrIndex?: string | number,
|
||||
id?: string,
|
||||
type?: T,
|
||||
items?: VirtualizedGrouping<ItemTypeKey[T]>
|
||||
): [ItemTypeKey[T] | undefined, () => void] => {
|
||||
const [item, setItem] = useState<ItemTypeKey[T]>();
|
||||
const itemIdRef = useRef<string>();
|
||||
const prevIdOrIndexRef = useRef<string | number>();
|
||||
|
||||
if (prevIdOrIndexRef.current !== idOrIndex) {
|
||||
itemIdRef.current = undefined;
|
||||
prevIdOrIndexRef.current = idOrIndex;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const onUpdateItem = (itemId?: string) => {
|
||||
if (typeof itemId === "string" && itemId !== itemIdRef.current) return;
|
||||
if (typeof itemId === "string" && itemId !== id) return;
|
||||
if (!id) return;
|
||||
console.log("useDBItem.onUpdateItem", id, type);
|
||||
|
||||
if (!isValidIdOrIndex(idOrIndex)) return;
|
||||
|
||||
console.log("useDBItem.onUpdateItem", idOrIndex, type);
|
||||
|
||||
if (items && typeof idOrIndex === "number") {
|
||||
items.item(idOrIndex).then((item) => {
|
||||
setItem(item.item);
|
||||
itemIdRef.current = item.item.id;
|
||||
if (items) {
|
||||
items.item(id).then((item) => {
|
||||
setItem(item);
|
||||
});
|
||||
} else {
|
||||
if (!(db as any)[type + "s"][type]) {
|
||||
@@ -83,14 +69,9 @@ export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
`db.${type}s.${type}(id: string)`
|
||||
);
|
||||
} else {
|
||||
console.log("get notebook");
|
||||
|
||||
(db as any)[type + "s"]
|
||||
?.[type]?.(idOrIndex as string)
|
||||
.then((item: ItemTypeKey[T]) => {
|
||||
setItem(item);
|
||||
itemIdRef.current = item.id;
|
||||
});
|
||||
?.[type]?.(id)
|
||||
.then((item: ItemTypeKey[T]) => setItem(item));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -99,42 +80,46 @@ export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
return () => {
|
||||
eUnSubscribeEvent(eDBItemUpdate, onUpdateItem);
|
||||
};
|
||||
}, [idOrIndex, type, items]);
|
||||
}, [id, type, items]);
|
||||
|
||||
return [
|
||||
isValidIdOrIndex(idOrIndex) ? (item as ItemTypeKey[T]) : undefined,
|
||||
id ? (item as ItemTypeKey[T]) : undefined,
|
||||
() => {
|
||||
if (idOrIndex) {
|
||||
eSendEvent(eDBItemUpdate, itemIdRef.current || idOrIndex);
|
||||
if (id) {
|
||||
eSendEvent(eDBItemUpdate, id);
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
export const useTotalNotes = (type: "notebook" | "tag" | "color") => {
|
||||
export const useTotalNotes = (
|
||||
ids: string[],
|
||||
type: "notebook" | "tag" | "color"
|
||||
) => {
|
||||
const [totalNotesById, setTotalNotesById] = useState<{
|
||||
[id: string]: number;
|
||||
}>({});
|
||||
|
||||
const getTotalNotes = React.useCallback(
|
||||
(ids: string[]) => {
|
||||
if (!ids || !ids.length || !type) return;
|
||||
db.relations
|
||||
.from({ type: type, ids: ids as string[] }, ["note"])
|
||||
.get()
|
||||
.then((relations) => {
|
||||
const totalNotesById: any = {};
|
||||
for (const id of ids) {
|
||||
totalNotesById[id] = relations.filter(
|
||||
(relation) => relation.fromId === id && relation.toType === "note"
|
||||
)?.length;
|
||||
}
|
||||
setTotalNotesById(totalNotesById);
|
||||
});
|
||||
console.log("useTotalNotes.getTotalNotes");
|
||||
},
|
||||
[type]
|
||||
);
|
||||
const getTotalNotes = React.useCallback(() => {
|
||||
if (!ids || !ids.length || !type) return;
|
||||
db.relations
|
||||
.from({ type: "notebook", ids: ids as string[] }, ["notebook", "note"])
|
||||
.get()
|
||||
.then((relations) => {
|
||||
const totalNotesById: any = {};
|
||||
for (const id of ids) {
|
||||
totalNotesById[id] = relations.filter(
|
||||
(relation) => relation.fromId === id && relation.toType === "note"
|
||||
)?.length;
|
||||
}
|
||||
setTotalNotesById(totalNotesById);
|
||||
});
|
||||
console.log("useTotalNotes.getTotalNotes");
|
||||
}, [ids, type]);
|
||||
|
||||
useEffect(() => {
|
||||
getTotalNotes();
|
||||
}, [ids, type, getTotalNotes]);
|
||||
|
||||
return {
|
||||
totalNotes: (id: string) => {
|
||||
|
||||
@@ -17,78 +17,74 @@ 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 { Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { db } from "../common/database";
|
||||
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
|
||||
import { eGroupOptionsUpdated, eOnNotebookUpdated } from "../utils/events";
|
||||
import { useDBItem, useTotalNotes } from "./use-db-item";
|
||||
|
||||
export const useNotebook = (
|
||||
id?: string | number,
|
||||
items?: VirtualizedGrouping<Notebook>,
|
||||
nestedNotebooks?: boolean
|
||||
id?: string,
|
||||
items?: VirtualizedGrouping<Notebook>
|
||||
) => {
|
||||
const [item, refresh] = useDBItem(id, "notebook", items);
|
||||
const groupOptions = db.settings.getGroupOptions("notebooks");
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings.getGroupOptions("notebooks")
|
||||
);
|
||||
const [notebooks, setNotebooks] = useState<VirtualizedGrouping<Notebook>>();
|
||||
const { totalNotes: nestedNotebookNotesCount, getTotalNotes } =
|
||||
useTotalNotes("notebook");
|
||||
const { totalNotes: nestedNotebookNotesCount } = useTotalNotes(
|
||||
notebooks?.ids as string[],
|
||||
"notebook"
|
||||
);
|
||||
|
||||
const onRequestUpdate = React.useCallback(() => {
|
||||
if (!item?.id) return;
|
||||
console.log("useNotebook.onRequestUpdate", item?.id, Date.now());
|
||||
|
||||
const selector = db.relations.from(
|
||||
{
|
||||
type: "notebook",
|
||||
id: item.id
|
||||
},
|
||||
"notebook"
|
||||
).selector;
|
||||
|
||||
selector.ids().then((notebookIds) => {
|
||||
getTotalNotes(notebookIds);
|
||||
});
|
||||
|
||||
selector
|
||||
.sorted(db.settings.getGroupOptions("notebooks"))
|
||||
if (!id) return;
|
||||
console.log("useNotebook.onRequestUpdate", id, Date.now());
|
||||
db.relations
|
||||
.from(
|
||||
{
|
||||
type: "notebook",
|
||||
id: id
|
||||
},
|
||||
"notebook"
|
||||
)
|
||||
.selector.sorted(db.settings.getGroupOptions("notebooks"))
|
||||
.then((notebooks) => {
|
||||
setNotebooks(notebooks);
|
||||
});
|
||||
}, [getTotalNotes, item?.id]);
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (nestedNotebooks) {
|
||||
console.log("useNotebook.useEffect.onRequestUpdate");
|
||||
console.log("useNotebook.useEffect.onRequestUpdate");
|
||||
onRequestUpdate();
|
||||
}, [onRequestUpdate]);
|
||||
|
||||
const onUpdate = useCallback(
|
||||
(type: string) => {
|
||||
if (type !== "notebooks") return;
|
||||
setGroupOptions({ ...(db.settings.getGroupOptions("notebooks") as any) });
|
||||
onRequestUpdate();
|
||||
}
|
||||
}, [item?.id, onRequestUpdate, nestedNotebooks]);
|
||||
console.log("useNotebook.onUpdate", id, Date.now());
|
||||
},
|
||||
[id, onRequestUpdate]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const onNotebookUpdate = (id?: string) => {
|
||||
if (typeof id === "string" && id !== id) return;
|
||||
setImmediate(() => {
|
||||
if (nestedNotebooks) {
|
||||
onRequestUpdate();
|
||||
}
|
||||
onRequestUpdate();
|
||||
refresh();
|
||||
});
|
||||
};
|
||||
|
||||
const onUpdate = (type: string) => {
|
||||
console.log("event", type);
|
||||
if (type !== "notebooks") return;
|
||||
onRequestUpdate();
|
||||
console.log("useNotebook.onUpdate", item?.id, Date.now());
|
||||
};
|
||||
|
||||
eSubscribeEvent(eGroupOptionsUpdated, onUpdate);
|
||||
eSubscribeEvent(eOnNotebookUpdated, onNotebookUpdate);
|
||||
return () => {
|
||||
eUnSubscribeEvent(eGroupOptionsUpdated, onUpdate);
|
||||
eUnSubscribeEvent(eOnNotebookUpdated, onNotebookUpdate);
|
||||
};
|
||||
}, [onRequestUpdate, item?.id, refresh, nestedNotebooks]);
|
||||
}, [onUpdate, onRequestUpdate, id, refresh]);
|
||||
|
||||
return {
|
||||
notebook: item,
|
||||
|
||||
@@ -132,7 +132,7 @@ const Tabs = React.memo(_Tabs, () => true);
|
||||
|
||||
const _NavigationStack = () => {
|
||||
const clearSelection = useSelectionStore((state) => state.clearSelection);
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const onStateChange = React.useCallback(() => {
|
||||
if (useSelectionStore.getState().selectionMode) {
|
||||
clearSelection(true);
|
||||
|
||||
@@ -135,7 +135,7 @@ export const useEditorEvents = (
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const fullscreen = useSettingStore((state) => state.fullscreen);
|
||||
const corsProxy = useSettingStore((state) => state.settings.corsProxy);
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const [dateFormat, timeFormat] = useSettingStore((state) => [
|
||||
state.dateFormat,
|
||||
state.timeFormat
|
||||
@@ -190,7 +190,7 @@ export const useEditorEvents = (
|
||||
readonly: readonly || editorPropReadonly,
|
||||
tools: tools || getDefaultPresets().default,
|
||||
noHeader: noHeader,
|
||||
noToolbar: noToolbar,
|
||||
noToolbar: readonly || editorPropReadonly || noToolbar,
|
||||
doubleSpacedLines: doubleSpacedLines,
|
||||
corsProxy: corsProxy,
|
||||
fontSize:
|
||||
@@ -395,7 +395,7 @@ export const useEditorEvents = (
|
||||
db.relations
|
||||
.unlink(editorMessage.value as ItemReference, editor.note.current)
|
||||
.then(async () => {
|
||||
useTagStore.getState().refresh();
|
||||
useTagStore.getState().setTags();
|
||||
useRelationStore.getState().update();
|
||||
await editor.commands.setTags(editor.note.current);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
|
||||
@@ -48,7 +48,7 @@ import Notifications from "../../../services/notifications";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { TipManager } from "../../../services/tip-manager";
|
||||
import { useEditorStore } from "../../../stores/use-editor-store";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { useNoteStore } from "../../../stores/use-notes-store";
|
||||
import { useTagStore } from "../../../stores/use-tag-store";
|
||||
import { eClearEditor, eOnLoadNote } from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
@@ -90,7 +90,7 @@ export const useEditor = (
|
||||
const sessionHistoryId = useRef<number>();
|
||||
const state = useRef<Partial<EditorState>>(defaultState);
|
||||
const placeholderTip = useRef(TipManager.placeholderTip());
|
||||
const tags = useTagStore((state) => state.items);
|
||||
const tags = useTagStore((state) => state.tags);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const isDefaultEditor = editorId === "";
|
||||
const saveCount = useRef(0);
|
||||
@@ -653,9 +653,9 @@ export const useEditor = (
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
}
|
||||
if (appState.note) {
|
||||
if (useSettingStore.getState().isAppLoading) {
|
||||
const remove = useSettingStore.subscribe((state) => {
|
||||
if (!state.isAppLoading && appState.note) {
|
||||
if (useNoteStore.getState().loading) {
|
||||
const remove = useNoteStore.subscribe((state) => {
|
||||
if (!state.loading && appState.note) {
|
||||
loadNote({
|
||||
item: appState.note
|
||||
});
|
||||
|
||||
@@ -21,18 +21,21 @@ import React from "react";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import List from "../../components/list";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useFavorites } from "../../stores/use-favorite-store";
|
||||
import { useFavoriteStore } from "../../stores/use-favorite-store";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
|
||||
export const Favorites = ({
|
||||
navigation,
|
||||
route
|
||||
}: NavigationProps<"Favorites">) => {
|
||||
const [favorites, loading, refresh] = useFavorites();
|
||||
const favorites = useFavoriteStore((state) => state.favorites);
|
||||
const setFavorites = useFavoriteStore((state) => state.setFavorites);
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
onFocus: (prev) => {
|
||||
Navigation.routeNeedsUpdate(
|
||||
@@ -40,7 +43,7 @@ export const Favorites = ({
|
||||
Navigation.routeUpdateFunctions[route.name]
|
||||
);
|
||||
useNavigationStore.getState().setFocusedRouteId(route?.name);
|
||||
return false;
|
||||
return !prev?.current;
|
||||
},
|
||||
onBlur: () => false,
|
||||
delay: SettingsService.get().homepage === route.name ? 1 : -1
|
||||
@@ -72,10 +75,10 @@ export const Favorites = ({
|
||||
data={favorites}
|
||||
dataType="note"
|
||||
onRefresh={() => {
|
||||
refresh();
|
||||
setFavorites();
|
||||
}}
|
||||
renderedInRoute="Favorites"
|
||||
loading={loading}
|
||||
loading={loading || !isFocused}
|
||||
placeholder={{
|
||||
title: "Your favorites",
|
||||
paragraph: "You have not added any notes to favorites yet.",
|
||||
|
||||
@@ -22,16 +22,17 @@ import { FloatingButton } from "../../components/container/floating-button";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import List from "../../components/list";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNotes } from "../../stores/use-notes-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { openEditor } from "../notes/common";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
|
||||
export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
const [notes, loading] = useNotes();
|
||||
const notes = useNoteStore((state) => state.notes);
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
onFocus: (prev) => {
|
||||
Navigation.routeNeedsUpdate(
|
||||
@@ -64,7 +65,7 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
id={route.name}
|
||||
onPressDefaultRightButton={openEditor}
|
||||
/>
|
||||
<DelayLayout wait={loading}>
|
||||
<DelayLayout wait={loading} delay={500}>
|
||||
<List
|
||||
data={notes}
|
||||
dataType="note"
|
||||
|
||||
@@ -39,23 +39,11 @@ import { eUpdateNotebookRoute } from "../../utils/events";
|
||||
import { findRootNotebookId } from "../../utils/notebooks";
|
||||
import { openEditor, setOnFirstSave } from "../notes/common";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import { View } from "react-native";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import { PressableButton } from "../../components/ui/pressable";
|
||||
import { resolveItems } from "../../stores/resolve-items";
|
||||
|
||||
const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const params = useRef<NotebookScreenParams>(route?.params);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [breadcrumbs, setBreadcrumbs] = useState<
|
||||
{
|
||||
id: string;
|
||||
title: string;
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
useNavigationFocus(navigation, {
|
||||
onFocus: () => {
|
||||
@@ -101,16 +89,13 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
const notebook = await db.notebooks?.notebook(
|
||||
params?.current?.item?.id
|
||||
);
|
||||
|
||||
if (notebook) {
|
||||
const breadcrumbs = await db.notebooks.breadcrumbs(notebook.id);
|
||||
setBreadcrumbs(breadcrumbs);
|
||||
params.current.item = notebook;
|
||||
const notes = await db.relations
|
||||
.from(notebook, "note")
|
||||
.selector.grouped(db.settings.getGroupOptions("notes"));
|
||||
setNotes(notes);
|
||||
await notes.item(0, resolveItems);
|
||||
setNotes(
|
||||
await db.relations
|
||||
.from(notebook, "note")
|
||||
.selector.grouped(db.settings.getGroupOptions("notes"))
|
||||
);
|
||||
syncWithNavigation();
|
||||
}
|
||||
setLoading(false);
|
||||
@@ -161,66 +146,13 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
id={params.current.item?.id}
|
||||
onPressDefaultRightButton={openEditor}
|
||||
/>
|
||||
|
||||
{breadcrumbs ? (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingHorizontal: 12,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
name="notebook-outline"
|
||||
size={16}
|
||||
customStyle={{ width: 20, height: 25 }}
|
||||
onPress={() => {
|
||||
Navigation.push("Notebooks", {
|
||||
canGoBack: true
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
{breadcrumbs.map((item) => (
|
||||
<PressableButton
|
||||
onPress={async () => {
|
||||
const notebook = await db.notebooks.notebook(item.id);
|
||||
if (!notebook) return;
|
||||
NotebookScreen.navigate(notebook, true);
|
||||
}}
|
||||
key={item.id}
|
||||
customStyle={{
|
||||
width: undefined,
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 0,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
name="chevron-right"
|
||||
size={16}
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
customStyle={{ width: 20, height: 25 }}
|
||||
/>
|
||||
<Paragraph size={SIZE.xs + 1}>{item.title}</Paragraph>
|
||||
</PressableButton>
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<DelayLayout wait={loading}>
|
||||
<DelayLayout>
|
||||
<List
|
||||
data={notes}
|
||||
dataType="note"
|
||||
onRefresh={() => {
|
||||
onRequestUpdate();
|
||||
}}
|
||||
id={params.current.item?.id}
|
||||
renderedInRoute="Notebook"
|
||||
headerTitle={params.current.title}
|
||||
loading={loading}
|
||||
|
||||
@@ -23,14 +23,14 @@ import { FloatingButton } from "../../components/container/floating-button";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import List from "../../components/list";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
|
||||
import { Walkthrough } from "../../components/walkthroughs";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNotebooks } from "../../stores/use-notebook-store";
|
||||
import { useNotebookStore } from "../../stores/use-notebook-store";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
|
||||
const onButtonPress = () => {
|
||||
AddNotebookSheet.present();
|
||||
@@ -40,8 +40,7 @@ export const Notebooks = ({
|
||||
navigation,
|
||||
route
|
||||
}: NavigationProps<"Notebooks">) => {
|
||||
const [notebooks, loading] = useNotebooks();
|
||||
|
||||
const notebooks = useNotebookStore((state) => state.notebooks);
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
onFocus: (prev) => {
|
||||
Navigation.routeNeedsUpdate(
|
||||
@@ -49,7 +48,7 @@ export const Notebooks = ({
|
||||
Navigation.routeUpdateFunctions[route.name]
|
||||
);
|
||||
useNavigationStore.getState().setFocusedRouteId(route.name);
|
||||
return false;
|
||||
return !prev?.current;
|
||||
},
|
||||
onBlur: () => false,
|
||||
delay: SettingsService.get().homepage === route.name ? 1 : -1
|
||||
@@ -84,11 +83,12 @@ export const Notebooks = ({
|
||||
}}
|
||||
onPressDefaultRightButton={onButtonPress}
|
||||
/>
|
||||
<DelayLayout wait={loading}>
|
||||
<DelayLayout delay={1}>
|
||||
<List
|
||||
data={notebooks}
|
||||
dataType="notebook"
|
||||
renderedInRoute="Notebooks"
|
||||
loading={!isFocused}
|
||||
placeholder={{
|
||||
title: "Your notebooks",
|
||||
paragraph: "You have not added any notebooks yet.",
|
||||
|
||||
@@ -107,7 +107,7 @@ export async function onNoteCreated(noteId: string, data: FirstSaveData) {
|
||||
}
|
||||
|
||||
editorState().onNoteCreated = null;
|
||||
useTagStore.getState().refresh();
|
||||
useTagStore.getState().setTags();
|
||||
useRelationStore.getState().update();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import useNavigationStore, {
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { setOnFirstSave } from "./common";
|
||||
import { resolveItems } from "../../stores/resolve-items";
|
||||
export const WARNING_DATA = {
|
||||
title: "Some notes in this topic are not synced"
|
||||
};
|
||||
@@ -75,6 +74,7 @@ const NotesPage = ({
|
||||
>) => {
|
||||
const params = useRef<NotesScreenParams>(route?.params);
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
const [loadingNotes, setLoadingNotes] = useState(true);
|
||||
const isMonograph = route.name === "Monographs";
|
||||
const title =
|
||||
@@ -119,6 +119,7 @@ const NotesPage = ({
|
||||
async (data?: NotesScreenParams) => {
|
||||
const isNew = data && data?.item?.id !== params.current?.item?.id;
|
||||
if (data) params.current = data;
|
||||
const { item } = params.current;
|
||||
try {
|
||||
if (isNew) setLoadingNotes(true);
|
||||
const notes = (await get(
|
||||
@@ -126,10 +127,14 @@ const NotesPage = ({
|
||||
true
|
||||
)) as VirtualizedGrouping<Note>;
|
||||
|
||||
if (
|
||||
(item.type === "tag" || item.type === "color") &&
|
||||
(!notes || notes.ids.length === 0)
|
||||
) {
|
||||
return Navigation.goBack();
|
||||
}
|
||||
if (notes.ids.length === 0) setLoadingNotes(false);
|
||||
setNotes(notes);
|
||||
await notes.item(0, resolveItems);
|
||||
setLoadingNotes(false);
|
||||
syncWithNavigation();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -139,11 +144,10 @@ const NotesPage = ({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingNotes) {
|
||||
if (loadingNotes && !loading) {
|
||||
get(params.current, true)
|
||||
.then(async (items) => {
|
||||
.then((items) => {
|
||||
setNotes(items as VirtualizedGrouping<Note>);
|
||||
await (items as VirtualizedGrouping<Note>).item(0, resolveItems);
|
||||
setLoadingNotes(false);
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -151,7 +155,7 @@ const NotesPage = ({
|
||||
setLoadingNotes(false);
|
||||
});
|
||||
}
|
||||
}, [loadingNotes, get]);
|
||||
}, [loadingNotes, loading, get]);
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent(route.name, onRequestUpdate);
|
||||
@@ -185,14 +189,13 @@ const NotesPage = ({
|
||||
headerRightButtons={rightButtons?.(params?.current)}
|
||||
/>
|
||||
|
||||
<DelayLayout color={accentColor} wait={loadingNotes}>
|
||||
<DelayLayout color={accentColor} wait={loading || loadingNotes}>
|
||||
<List
|
||||
data={notes}
|
||||
dataType="note"
|
||||
onRefresh={onRequestUpdate}
|
||||
loading={!isFocused}
|
||||
loading={loading || !isFocused}
|
||||
renderedInRoute={route.name}
|
||||
id={params.current.item?.id}
|
||||
headerTitle={title}
|
||||
customAccentColor={accentColor}
|
||||
placeholder={placeholder}
|
||||
|
||||
@@ -22,19 +22,19 @@ import { FloatingButton } from "../../components/container/floating-button";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import List from "../../components/list";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import ReminderSheet from "../../components/sheets/reminder";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useReminders } from "../../stores/use-reminder-store";
|
||||
import { useReminderStore } from "../../stores/use-reminder-store";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
|
||||
export const Reminders = ({
|
||||
navigation,
|
||||
route
|
||||
}: NavigationProps<"Reminders">) => {
|
||||
const [reminders, loading] = useReminders();
|
||||
const reminders = useReminderStore((state) => state.reminders);
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
onFocus: (prev) => {
|
||||
Navigation.routeNeedsUpdate(
|
||||
@@ -43,7 +43,7 @@ export const Reminders = ({
|
||||
);
|
||||
|
||||
useNavigationStore.getState().setFocusedRouteId(route.name);
|
||||
return false;
|
||||
return !prev?.current;
|
||||
},
|
||||
onBlur: () => false,
|
||||
delay: SettingsService.get().homepage === route.name ? 1 : -1
|
||||
@@ -72,13 +72,13 @@ export const Reminders = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
<DelayLayout wait={loading}>
|
||||
<DelayLayout>
|
||||
<List
|
||||
data={reminders}
|
||||
dataType="reminder"
|
||||
headerTitle="Reminders"
|
||||
renderedInRoute="Reminders"
|
||||
loading={loading}
|
||||
loading={!isFocused}
|
||||
placeholder={{
|
||||
title: "Your reminders",
|
||||
paragraph: "You have not set any reminders yet.",
|
||||
|
||||
@@ -22,6 +22,7 @@ import create, { State } from "zustand";
|
||||
import { persist, StateStorage } from "zustand/middleware";
|
||||
import { db } from "../../../common/database";
|
||||
import { MMKV } from "../../../common/database/mmkv";
|
||||
import { useNoteStore } from "../../../stores/use-notes-store";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { presets } from "./toolbar-definition";
|
||||
export type ToolDefinition = string | string[];
|
||||
@@ -123,7 +124,7 @@ export const useDragState = create<DragState>(
|
||||
getStorage: () => MMKV as unknown as StateStorage,
|
||||
onRehydrateStorage: () => {
|
||||
return () => {
|
||||
if (!useSettingStore.getState().isAppLoading) {
|
||||
if (!useNoteStore.getState().loading) {
|
||||
useDragState.getState().init();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -57,10 +57,10 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
|
||||
return;
|
||||
}
|
||||
if (!item.property) return;
|
||||
item.onChange?.(!settings[item.property]);
|
||||
SettingsService.set({
|
||||
[item.property]: !settings[item.property]
|
||||
});
|
||||
item.onChange?.(!settings[item.property]);
|
||||
};
|
||||
|
||||
const styles =
|
||||
|
||||
@@ -68,6 +68,7 @@ import { useDragState } from "./editor/state";
|
||||
import { verifyUser } from "./functions";
|
||||
import { SettingSection } from "./types";
|
||||
import { getTimeLeft } from "./user-section";
|
||||
import { BackgroundSync } from "../../services/background-sync";
|
||||
type User = any;
|
||||
|
||||
export const settingsGroups: SettingSection[] = [
|
||||
@@ -297,7 +298,6 @@ export const settingsGroups: SettingSection[] = [
|
||||
await BiometicService.resetCredentials();
|
||||
MMKV.clearStore();
|
||||
clearAllStores();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
SettingsService.resetSettings();
|
||||
useUserStore.getState().setUser(null);
|
||||
useUserStore.getState().setSyncing(false);
|
||||
@@ -305,7 +305,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
Navigation.popToTop();
|
||||
setTimeout(() => {
|
||||
eSendEvent("settings-loading", false);
|
||||
}, 3000);
|
||||
}, 2000);
|
||||
}, 300);
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error, "Error logging out");
|
||||
@@ -398,6 +398,21 @@ export const settingsGroups: SettingSection[] = [
|
||||
type: "switch",
|
||||
property: "disableSync"
|
||||
},
|
||||
{
|
||||
id: "background-sync",
|
||||
name: "Background sync (Experimental)",
|
||||
description:
|
||||
"Periodically wake up the app in background to sync your notes from other devices.",
|
||||
type: "switch",
|
||||
property: "backgroundSync",
|
||||
onChange: (value) => {
|
||||
if (value) {
|
||||
BackgroundSync.start();
|
||||
} else {
|
||||
BackgroundSync.stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "sync-issues-fix",
|
||||
name: "Having problems with sync",
|
||||
|
||||
@@ -21,15 +21,16 @@ import React from "react";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import List from "../../components/list";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useTags } from "../../stores/use-tag-store";
|
||||
import { useTagStore } from "../../stores/use-tag-store";
|
||||
import { db } from "../../common/database";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
|
||||
export const Tags = ({ navigation, route }: NavigationProps<"Tags">) => {
|
||||
const [tags, loading] = useTags();
|
||||
const tags = useTagStore((state) => state.tags);
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
onFocus: (prev) => {
|
||||
Navigation.routeNeedsUpdate(
|
||||
@@ -60,7 +61,7 @@ export const Tags = ({ navigation, route }: NavigationProps<"Tags">) => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<DelayLayout wait={loading}>
|
||||
<DelayLayout>
|
||||
<List
|
||||
data={tags}
|
||||
dataType="tag"
|
||||
|
||||
@@ -29,7 +29,7 @@ import { ToastManager } from "../../services/event-manager";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useTrash, useTrashStore } from "../../stores/use-trash-store";
|
||||
import { useTrashStore } from "../../stores/use-trash-store";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
|
||||
const onPressFloatingButton = () => {
|
||||
@@ -40,7 +40,7 @@ const onPressFloatingButton = () => {
|
||||
negativeText: "Cancel",
|
||||
positivePress: async () => {
|
||||
await db.trash?.clear();
|
||||
useTrashStore.getState().refresh();
|
||||
useTrashStore.getState().setTrash();
|
||||
useSelectionStore.getState().clearSelection();
|
||||
ToastManager.show({
|
||||
heading: "Trash cleared",
|
||||
@@ -63,7 +63,7 @@ const PLACEHOLDER_DATA = (trashCleanupInterval = 7) => ({
|
||||
});
|
||||
|
||||
export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
|
||||
const [trash, loading] = useTrash();
|
||||
const trash = useTrashStore((state) => state.trash);
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
onFocus: () => {
|
||||
Navigation.routeNeedsUpdate(
|
||||
@@ -71,6 +71,12 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
|
||||
Navigation.routeUpdateFunctions[route.name]
|
||||
);
|
||||
useNavigationStore.getState().setFocusedRouteId(route.name);
|
||||
if (
|
||||
!useTrashStore.getState().trash ||
|
||||
useTrashStore.getState().trash?.ids?.length === 0
|
||||
) {
|
||||
useTrashStore.getState().setTrash();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onBlur: () => false
|
||||
@@ -93,7 +99,7 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<DelayLayout wait={loading}>
|
||||
<DelayLayout>
|
||||
<List
|
||||
data={trash}
|
||||
dataType="trash"
|
||||
|
||||
@@ -21,6 +21,11 @@ import {
|
||||
beginBackgroundTask,
|
||||
endBackgroundTask
|
||||
} from "react-native-begin-background-task";
|
||||
import BackgroundFetch from "@ammarahmed/react-native-background-fetch";
|
||||
import { DatabaseLogger, db } from "../common/database";
|
||||
import { AppState, AppRegistry } from "react-native";
|
||||
import Notifications from "./notifications";
|
||||
import SettingsService from "./settings";
|
||||
|
||||
async function doInBackground(callback: () => Promise<void>) {
|
||||
if (Platform.OS === "ios") {
|
||||
@@ -45,107 +50,120 @@ async function doInBackground(callback: () => Promise<void>) {
|
||||
}
|
||||
}
|
||||
|
||||
// import BackgroundFetch from "react-native-background-fetch";
|
||||
// import { DatabaseLogger, db } from "../common/database";
|
||||
// import { AppState, AppRegistry } from "react-native";
|
||||
// import Notifications from "./notifications";
|
||||
// import SettingsService from "./settings";
|
||||
let backgroundFetchStarted = false;
|
||||
async function start() {
|
||||
if (backgroundFetchStarted) return;
|
||||
backgroundFetchStarted = true;
|
||||
if (!SettingsService.getProperty("backgroundSync")) {
|
||||
return;
|
||||
}
|
||||
// BackgroundFetch event handler.
|
||||
const onEvent = async (taskId: string) => {
|
||||
DatabaseLogger.info(
|
||||
`BACKGROUND FETCH ON EVENT ${taskId}, ${AppState.currentState}}`
|
||||
);
|
||||
// Do your background work...
|
||||
await onBackgroundSyncStarted();
|
||||
// IMPORTANT: You must signal to the OS that your task is complete.
|
||||
BackgroundFetch.finish(taskId);
|
||||
};
|
||||
|
||||
// let backgroundFetchStarted = false;
|
||||
// async function start() {
|
||||
// if (backgroundFetchStarted) return;
|
||||
// backgroundFetchStarted = true;
|
||||
// // BackgroundFetch event handler.
|
||||
// const onEvent = async (taskId: string) => {
|
||||
// console.log("[BackgroundFetch] task: ", taskId, AppState.currentState);
|
||||
// // Do your background work...
|
||||
// await onBackgroundSyncStarted();
|
||||
// // IMPORTANT: You must signal to the OS that your task is complete.
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// };
|
||||
// Timeout callback is executed when your Task has exceeded its allowed running-time.
|
||||
// You must stop what you're doing immediately BackgroundFetch.finish(taskId)
|
||||
const onTimeout = async (taskId: string) => {
|
||||
DatabaseLogger.info(`BACKGROUND FETCH TIMEOUT: ${taskId}`);
|
||||
BackgroundFetch.finish(taskId);
|
||||
};
|
||||
|
||||
// // Timeout callback is executed when your Task has exceeded its allowed running-time.
|
||||
// // You must stop what you're doing immediately BackgroundFetch.finish(taskId)
|
||||
// const onTimeout = async (taskId: string) => {
|
||||
// console.warn("[BackgroundFetch] TIMEOUT: ", taskId);
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// };
|
||||
// Initialize BackgroundFetch only once when component mounts.
|
||||
const status = await BackgroundFetch.configure(
|
||||
{
|
||||
minimumFetchInterval: 15,
|
||||
enableHeadless: true,
|
||||
startOnBoot: true,
|
||||
stopOnTerminate: false,
|
||||
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY
|
||||
},
|
||||
onEvent,
|
||||
onTimeout
|
||||
);
|
||||
|
||||
// // Initialize BackgroundFetch only once when component mounts.
|
||||
// const status = await BackgroundFetch.configure(
|
||||
// {
|
||||
// minimumFetchInterval: 15,
|
||||
// enableHeadless: true,
|
||||
// startOnBoot: true,
|
||||
// stopOnTerminate: false,
|
||||
// requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY
|
||||
// },
|
||||
// onEvent,
|
||||
// onTimeout
|
||||
// );
|
||||
// DatabaseLogger.info(`[BackgroundFetch] configure status: ${status}`);
|
||||
// console.log(`[BackgroundFetch] configure status: ${status}`);
|
||||
// }
|
||||
DatabaseLogger.info(`[BackgroundFetch] configure status: ${status}`);
|
||||
}
|
||||
|
||||
// const task = async (event: { taskId: string; timeout: boolean }) => {
|
||||
// // Get task id from event {}:
|
||||
// const taskId = event.taskId;
|
||||
// const isTimeout = event.timeout; // <-- true when your background-time has expired.
|
||||
// if (isTimeout) {
|
||||
// console.log("[BackgroundFetch] Headless TIMEOUT:", taskId);
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// return;
|
||||
// }
|
||||
// DatabaseLogger.info(
|
||||
// "[BackgroundFetch HeadlessTask] start: " + taskId + AppState.currentState
|
||||
// );
|
||||
// await onBackgroundSyncStarted();
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// };
|
||||
const task = async (event: { taskId: string; timeout: boolean }) => {
|
||||
// Get task id from event {}:
|
||||
const taskId = event.taskId;
|
||||
const isTimeout = event.timeout; // <-- true when your background-time has expired.
|
||||
if (isTimeout) {
|
||||
console.log(`BACKGROUND FETCH TIMEOUT: ${taskId}`);
|
||||
BackgroundFetch.finish(taskId);
|
||||
return;
|
||||
}
|
||||
DatabaseLogger.info("BACKGROUND SYNC START" + taskId + AppState.currentState);
|
||||
await onBackgroundSyncStarted();
|
||||
BackgroundFetch.finish(taskId);
|
||||
};
|
||||
|
||||
// BackgroundFetch.registerHeadlessTask(task);
|
||||
BackgroundFetch.registerHeadlessTask(task);
|
||||
|
||||
// async function onBackgroundSyncStarted() {
|
||||
// try {
|
||||
// DatabaseLogger.info("Background Sync" + "start");
|
||||
// await db.init();
|
||||
// const user = await db.user?.getUser();
|
||||
// if (user) {
|
||||
// await db.sync(true, false);
|
||||
// }
|
||||
// await Notifications.setupReminders();
|
||||
// DatabaseLogger.info("Background Sync" + "end");
|
||||
// } catch (e) {
|
||||
// DatabaseLogger.error(e as Error);
|
||||
// console.log("Background Sync Error", (e as Error).message);
|
||||
// }
|
||||
// }
|
||||
async function onBackgroundSyncStarted() {
|
||||
try {
|
||||
if (!db.isInitialized) {
|
||||
await db.init();
|
||||
} else {
|
||||
await db.initCollections();
|
||||
}
|
||||
|
||||
// const onBoot = async () => {
|
||||
// try {
|
||||
// DatabaseLogger.info("BOOT TASK STARTED");
|
||||
// await db.init();
|
||||
// await Notifications.setupReminders();
|
||||
// SettingsService.init();
|
||||
// if (SettingsService.get().notifNotes) {
|
||||
// Notifications.pinQuickNote(false);
|
||||
// }
|
||||
// DatabaseLogger.info("BOOT TASK COMPLETE");
|
||||
// } catch (e) {
|
||||
// console.log(e);
|
||||
// }
|
||||
// };
|
||||
const user = await db.user?.getUser();
|
||||
if (user) {
|
||||
await db.sync(true, false);
|
||||
}
|
||||
await Notifications.setupReminders();
|
||||
DatabaseLogger.info("BACKGROUND SYNC COMPLETE");
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e as Error);
|
||||
console.log("BACKGROUND SYNC ERROR", (e as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
// const registerHeadlessTask = () =>
|
||||
// AppRegistry.registerHeadlessTask(
|
||||
// "com.streetwriters.notesnook.BOOT_TASK",
|
||||
// () => {
|
||||
// return onBoot;
|
||||
// }
|
||||
// );
|
||||
const onBoot = async () => {
|
||||
try {
|
||||
if (!SettingsService.getProperty("backgroundSync")) {
|
||||
return;
|
||||
}
|
||||
|
||||
DatabaseLogger.info("BOOT TASK STARTED");
|
||||
if (!db.isInitialized) {
|
||||
await db.init();
|
||||
} else {
|
||||
await db.initCollections();
|
||||
}
|
||||
|
||||
await Notifications.setupReminders();
|
||||
SettingsService.init();
|
||||
if (SettingsService.get().notifNotes) {
|
||||
Notifications.pinQuickNote(false);
|
||||
}
|
||||
DatabaseLogger.info("BOOT TASK COMPLETE");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const registerHeadlessTask = () =>
|
||||
AppRegistry.registerHeadlessTask(
|
||||
"com.streetwriters.notesnook.BOOT_TASK",
|
||||
() => {
|
||||
return onBoot;
|
||||
}
|
||||
);
|
||||
export const BackgroundSync = {
|
||||
start,
|
||||
registerHeadlessTask,
|
||||
stop: () => {
|
||||
BackgroundFetch.stop();
|
||||
}
|
||||
};
|
||||
|
||||
// export const BackgroundSync = {
|
||||
// start,
|
||||
// registerHeadlessTask
|
||||
// };
|
||||
export default { doInBackground };
|
||||
|
||||
@@ -62,7 +62,6 @@ export const eSubscribeEvent = <T = unknown>(
|
||||
eventName: string,
|
||||
action?: (data: T) => void
|
||||
) => {
|
||||
if (!action) return;
|
||||
return eventManager.subscribe(eventName, action);
|
||||
};
|
||||
|
||||
@@ -70,7 +69,6 @@ export const eUnSubscribeEvent = <T = unknown>(
|
||||
eventName: string,
|
||||
action?: (data: T) => void
|
||||
) => {
|
||||
if (!action) return;
|
||||
eventManager.unsubscribe(eventName, action);
|
||||
};
|
||||
|
||||
@@ -131,7 +129,7 @@ export function hideSheet() {
|
||||
export type ToastOptions = {
|
||||
heading?: string;
|
||||
message?: string;
|
||||
context?: any;
|
||||
context?: "global" | "local";
|
||||
type?: "error" | "success" | "info";
|
||||
duration?: number;
|
||||
func?: () => void;
|
||||
@@ -159,7 +157,7 @@ export const ToastManager = {
|
||||
});
|
||||
},
|
||||
hide: () => eSendEvent(eHideToast),
|
||||
error: (e: Error, title?: string, context?: any) => {
|
||||
error: (e: Error, title?: string, context?: "global" | "local") => {
|
||||
ToastManager.show({
|
||||
heading: title,
|
||||
message: e?.message || "",
|
||||
|
||||
@@ -73,18 +73,18 @@ export type NavigationProps<T extends RouteName> = NativeStackScreenProps<
|
||||
const routeUpdateFunctions: {
|
||||
[name: string]: (...params: GenericRouteParam[]) => void;
|
||||
} = {
|
||||
Notes: () => useNoteStore.getState().refresh(),
|
||||
Notebooks: () => useNotebookStore.getState().refresh(),
|
||||
Tags: () => useTagStore.getState().refresh(),
|
||||
Favorites: () => useFavoriteStore.getState().refresh(),
|
||||
Trash: () => useTrashStore.getState().refresh(),
|
||||
Notes: () => useNoteStore.getState().setNotes(),
|
||||
Notebooks: () => useNotebookStore.getState().setNotebooks(),
|
||||
Tags: () => useTagStore.getState().setTags(),
|
||||
Favorites: () => useFavoriteStore.getState().setFavorites(),
|
||||
Trash: () => useTrashStore.getState().setTrash(),
|
||||
Notebook: (params) => eSendEvent(eUpdateNotebookRoute, params),
|
||||
NotesPage: (params) => eSendEvent("NotesPage", params),
|
||||
TaggedNotes: (params) => eSendEvent("TaggedNotes", params),
|
||||
ColoredNotes: (params) => eSendEvent("ColoredNotes", params),
|
||||
TopicNotes: (params) => eSendEvent("TopicNotes", params),
|
||||
Monographs: (params) => eSendEvent("Monographs", params),
|
||||
Reminders: () => useReminderStore.getState().refresh(),
|
||||
Reminders: () => useReminderStore.getState().setReminders(),
|
||||
Search: () => eSendEvent(eOnRefreshSearch)
|
||||
};
|
||||
|
||||
|
||||
@@ -17,38 +17,38 @@ 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 { Reminder } from "@notesnook/core/dist/types";
|
||||
import notifee, {
|
||||
AndroidStyle,
|
||||
AuthorizationStatus,
|
||||
DisplayedNotification,
|
||||
Event,
|
||||
EventType,
|
||||
RepeatFrequency,
|
||||
TimestampTrigger,
|
||||
Trigger,
|
||||
TriggerType,
|
||||
Event,
|
||||
TriggerNotification,
|
||||
TriggerType
|
||||
TimestampTrigger
|
||||
} from "@notifee/react-native";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import { encodeNonAsciiHTML } from "entities";
|
||||
import { Platform } from "react-native";
|
||||
import { db } from "../common/database";
|
||||
import { MMKV } from "../common/database/mmkv";
|
||||
import { presentDialog } from "../components/dialog/functions";
|
||||
import { editorState } from "../screens/editor/tiptap/utils";
|
||||
import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { useReminderStore } from "../stores/use-reminder-store";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { useNoteStore } from "../stores/use-notes-store";
|
||||
import { eOnLoadNote } from "../utils/events";
|
||||
import { tabBarRef } from "../utils/global-refs";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { sleep } from "../utils/time";
|
||||
import { DDS } from "./device-detection";
|
||||
import { eSendEvent } from "./event-manager";
|
||||
import Navigation from "./navigation";
|
||||
import SettingsService from "./settings";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { sleep } from "../utils/time";
|
||||
import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { useReminderStore } from "../stores/use-reminder-store";
|
||||
import { presentDialog } from "../components/dialog/functions";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { encodeNonAsciiHTML } from "entities";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { Reminder } from "@notesnook/core/dist/types";
|
||||
|
||||
let pinned: DisplayedNotification[] = [];
|
||||
|
||||
@@ -136,7 +136,19 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
}
|
||||
editorState().movedAway = false;
|
||||
const noteId = notification?.id;
|
||||
loadNote(noteId as string, true);
|
||||
if (useNoteStore?.getState()?.loading === false) {
|
||||
loadNote(noteId as string, false);
|
||||
return;
|
||||
}
|
||||
const unsub = useNoteStore.subscribe(
|
||||
(loading) => {
|
||||
if (loading === false) {
|
||||
loadNote(noteId as string, true);
|
||||
}
|
||||
unsub();
|
||||
},
|
||||
(state) => state.loading
|
||||
);
|
||||
}
|
||||
|
||||
if (type === EventType.ACTION_PRESS) {
|
||||
@@ -161,7 +173,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
await db.reminders?.reminder(reminder?.id)
|
||||
);
|
||||
useRelationStore.getState().update();
|
||||
useReminderStore.getState().refresh();
|
||||
useReminderStore.getState().setReminders();
|
||||
break;
|
||||
}
|
||||
case "REMINDER_DISABLE": {
|
||||
@@ -179,7 +191,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
await db.reminders?.reminder(reminder?.id)
|
||||
);
|
||||
useRelationStore.getState().update();
|
||||
useReminderStore.getState().refresh();
|
||||
useReminderStore.getState().setReminders();
|
||||
break;
|
||||
}
|
||||
case "UNPIN": {
|
||||
@@ -195,7 +207,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
disabled: true
|
||||
});
|
||||
useRelationStore.getState().update();
|
||||
useReminderStore.getState().refresh();
|
||||
useReminderStore.getState().setReminders();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -241,7 +253,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
console.log(e, (e as Error).stack);
|
||||
}
|
||||
}
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useNoteStore.getState().setNotes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -352,18 +364,17 @@ async function scheduleNotification(
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await notifee.createTriggerNotification(
|
||||
{
|
||||
id: trigger.id,
|
||||
title: !title ? undefined : title,
|
||||
body: !description ? undefined : description,
|
||||
title: title,
|
||||
body: description,
|
||||
data: {
|
||||
type: "reminder",
|
||||
payload: payload || "",
|
||||
dateModified: reminder.dateModified + ""
|
||||
},
|
||||
subtitle: !description ? undefined : description,
|
||||
subtitle: description,
|
||||
android: {
|
||||
channelId: await getChannelId(priority),
|
||||
smallIcon: "ic_stat_name",
|
||||
@@ -410,17 +421,14 @@ async function loadNote(id: string, jump: boolean) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
}
|
||||
eSendEvent("loadingNote", note);
|
||||
setTimeout(
|
||||
() => {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note
|
||||
});
|
||||
if (!jump && !DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
}
|
||||
},
|
||||
tabBarRef?.current ? 0 : 2000
|
||||
);
|
||||
setTimeout(() => {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note
|
||||
});
|
||||
if (!jump && !DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
async function getChannelId(id: "silent" | "vibrate" | "urgent" | "default") {
|
||||
|
||||
@@ -303,9 +303,7 @@ const subscriptions = {
|
||||
console.log("Subscription.verify", requestData);
|
||||
try {
|
||||
let result = await fetch(
|
||||
__DEV__
|
||||
? "http://192.168.43.5:4264/apple/verify"
|
||||
: "https://payments.streetwriters.co/apple/verify",
|
||||
"http://192.168.43.5:4264/apple/verify",
|
||||
requestData
|
||||
);
|
||||
console.log("Subscribed", result);
|
||||
|
||||
@@ -1,93 +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 { Item, VirtualizedGrouping } from "@notesnook/core";
|
||||
import create, { State, StoreApi, UseBoundStore } from "zustand";
|
||||
import { resolveItems } from "./resolve-items";
|
||||
import { useSettingStore } from "./use-setting-store";
|
||||
|
||||
export interface DBCollectionStore<Type extends Item> extends State {
|
||||
items: VirtualizedGrouping<Type> | undefined;
|
||||
loading: boolean;
|
||||
refresh: () => Promise<void>;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
const ALL_STORES: UseBoundStore<
|
||||
DBCollectionStore<Item>,
|
||||
StoreApi<DBCollectionStore<Item>>
|
||||
>[] = [];
|
||||
|
||||
export function clearAllStores() {
|
||||
ALL_STORES.forEach((store) => {
|
||||
store?.getState().clear();
|
||||
});
|
||||
}
|
||||
|
||||
export function refreshAllStores() {
|
||||
ALL_STORES.forEach((store) => store?.getState().refresh());
|
||||
}
|
||||
|
||||
export default function createDBCollectionStore<Type extends Item>({
|
||||
getCollection,
|
||||
eagerlyFetchFirstBatch
|
||||
}: {
|
||||
getCollection: () => Promise<VirtualizedGrouping<Type>>;
|
||||
eagerlyFetchFirstBatch?: boolean;
|
||||
}) {
|
||||
const useDBCollectionStore = create<DBCollectionStore<Type>>((set, get) => ({
|
||||
items: undefined,
|
||||
loading: true,
|
||||
refresh: async () => {
|
||||
const items = await getCollection();
|
||||
if (get().loading && eagerlyFetchFirstBatch) {
|
||||
await items.item(0, resolveItems);
|
||||
}
|
||||
set({
|
||||
items,
|
||||
loading: false
|
||||
});
|
||||
},
|
||||
clear: () => set({ items: undefined, loading: true })
|
||||
}));
|
||||
|
||||
const useStoreHook = (): [
|
||||
VirtualizedGrouping<Type> | undefined,
|
||||
boolean,
|
||||
() => Promise<void>
|
||||
] => {
|
||||
const isAppLoading = useSettingStore((state) => state.isAppLoading);
|
||||
const [items, loading] = useDBCollectionStore((state) => [
|
||||
state.items,
|
||||
state.loading
|
||||
]);
|
||||
|
||||
if (!items && !isAppLoading) {
|
||||
useDBCollectionStore.getState().refresh();
|
||||
}
|
||||
|
||||
return [items, loading, useDBCollectionStore.getState().refresh];
|
||||
};
|
||||
|
||||
ALL_STORES.push(useDBCollectionStore as any);
|
||||
return {
|
||||
useStore: useDBCollectionStore,
|
||||
useCollection: useStoreHook
|
||||
};
|
||||
}
|
||||
@@ -17,6 +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 { db } from "../common/database";
|
||||
import Navigation from "../services/navigation";
|
||||
import Notifications from "../services/notifications";
|
||||
import { useFavoriteStore } from "./use-favorite-store";
|
||||
@@ -27,26 +28,36 @@ import { useRelationStore } from "./use-relation-store";
|
||||
import { useReminderStore } from "./use-reminder-store";
|
||||
import { useTagStore } from "./use-tag-store";
|
||||
import { useTrashStore } from "./use-trash-store";
|
||||
|
||||
export function initAfterSync() {
|
||||
useMenuStore.getState().setColorNotes();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
// Whenever sync completes, try to reschedule
|
||||
// any new/updated reminders.
|
||||
Notifications.setupReminders(true);
|
||||
useRelationStore.getState().update();
|
||||
}
|
||||
|
||||
export async function initialize() {
|
||||
if (!db) return;
|
||||
useMenuStore.getState().setColorNotes();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
useNotebookStore.getState().setNotebooks();
|
||||
useTrashStore.getState().setTrash();
|
||||
useTagStore.getState().setTags();
|
||||
useFavoriteStore.getState().setFavorites();
|
||||
await useNoteStore.getState().setNotes();
|
||||
useReminderStore.getState().setReminders();
|
||||
Notifications.setupReminders();
|
||||
}
|
||||
|
||||
export async function initialize() {}
|
||||
|
||||
export function clearAllStores() {
|
||||
useNotebookStore.getState().clear();
|
||||
useTagStore.getState().clear();
|
||||
useFavoriteStore.getState().clear();
|
||||
useNotebookStore.getState().clearNotebooks();
|
||||
useTagStore.getState().clearTags();
|
||||
useFavoriteStore.getState().clearFavorites();
|
||||
useMenuStore.getState().clearAll();
|
||||
useNoteStore.getState().clear();
|
||||
useNoteStore.getState().clearNotes();
|
||||
useMenuStore.getState().clearAll();
|
||||
useTrashStore.getState().clear();
|
||||
useReminderStore.getState().clear();
|
||||
useTrashStore.getState().clearTrash();
|
||||
useReminderStore.getState().cleareReminders();
|
||||
}
|
||||
|
||||
@@ -1,158 +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 { Color, Item, Notebook, Reminder, Tag } from "@notesnook/core";
|
||||
import { db } from "../common/database";
|
||||
|
||||
export type WithDateEdited<T> = { items: T[]; dateEdited: number };
|
||||
export type NotebooksWithDateEdited = WithDateEdited<Notebook>;
|
||||
export type TagsWithDateEdited = WithDateEdited<Tag>;
|
||||
|
||||
function withDateEdited<
|
||||
T extends { dateEdited: number } | { dateModified: number }
|
||||
>(items: T[]): WithDateEdited<T> {
|
||||
let latestDateEdited = 0;
|
||||
items.forEach((item) => {
|
||||
const date = "dateEdited" in item ? item.dateEdited : item.dateModified;
|
||||
if (latestDateEdited < date) latestDateEdited = date;
|
||||
});
|
||||
return { dateEdited: latestDateEdited, items };
|
||||
}
|
||||
|
||||
export async function resolveItems(ids: string[], items: Item[]) {
|
||||
if (!ids.length || !items.length) return [];
|
||||
|
||||
const { type } = items[0];
|
||||
if (type === "note") return resolveNotes(ids);
|
||||
else if (type === "notebook") {
|
||||
return Promise.all(ids.map((id) => db.notebooks.totalNotes(id)));
|
||||
} else if (type === "tag") {
|
||||
return Promise.all(
|
||||
ids.map((id) => db.relations.from({ id, type: "tag" }, "note").count())
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
type NoteResolvedData = {
|
||||
notebooks?: NotebooksWithDateEdited;
|
||||
reminder?: Reminder;
|
||||
color?: Color;
|
||||
tags?: TagsWithDateEdited;
|
||||
attachmentsCount?: number;
|
||||
};
|
||||
async function resolveNotes(ids: string[]) {
|
||||
console.time("relations");
|
||||
const relations = [
|
||||
...(await db.relations
|
||||
.to({ type: "note", ids }, ["notebook", "tag", "color"])
|
||||
.get()),
|
||||
...(await db.relations.from({ type: "note", ids }, "reminder").get())
|
||||
];
|
||||
console.timeEnd("relations");
|
||||
const relationIds: {
|
||||
notebooks: Set<string>;
|
||||
colors: Set<string>;
|
||||
tags: Set<string>;
|
||||
reminders: Set<string>;
|
||||
} = {
|
||||
colors: new Set(),
|
||||
notebooks: new Set(),
|
||||
tags: new Set(),
|
||||
reminders: new Set()
|
||||
};
|
||||
|
||||
const grouped: Record<
|
||||
string,
|
||||
{
|
||||
notebooks: string[];
|
||||
color?: string;
|
||||
tags: string[];
|
||||
reminder?: string;
|
||||
}
|
||||
> = {};
|
||||
for (const relation of relations) {
|
||||
const noteId =
|
||||
relation.toType === "reminder" ? relation.fromId : relation.toId;
|
||||
const data = grouped[noteId] || {
|
||||
notebooks: [],
|
||||
tags: []
|
||||
};
|
||||
|
||||
if (relation.toType === "reminder" && !data.reminder) {
|
||||
data.reminder = relation.toId;
|
||||
relationIds.reminders.add(relation.toId);
|
||||
} else if (relation.fromType === "notebook" && data.notebooks.length < 2) {
|
||||
data.notebooks.push(relation.fromId);
|
||||
relationIds.notebooks.add(relation.fromId);
|
||||
} else if (relation.fromType === "tag" && data.tags.length < 3) {
|
||||
data.tags.push(relation.fromId);
|
||||
relationIds.tags.add(relation.fromId);
|
||||
} else if (relation.fromType === "color" && !data.color) {
|
||||
data.color = relation.fromId;
|
||||
relationIds.colors.add(relation.fromId);
|
||||
}
|
||||
grouped[noteId] = data;
|
||||
}
|
||||
|
||||
console.time("resolve");
|
||||
const resolved = {
|
||||
notebooks: await db.notebooks.all.records(
|
||||
Array.from(relationIds.notebooks)
|
||||
),
|
||||
tags: await db.tags.all.records(Array.from(relationIds.tags)),
|
||||
colors: await db.colors.all.records(Array.from(relationIds.colors)),
|
||||
reminders: await db.reminders.all.records(Array.from(relationIds.reminders))
|
||||
};
|
||||
console.timeEnd("resolve");
|
||||
|
||||
const data: NoteResolvedData[] = [];
|
||||
for (const noteId of ids) {
|
||||
const group = grouped[noteId];
|
||||
if (!group) {
|
||||
data.push({});
|
||||
continue;
|
||||
}
|
||||
|
||||
data.push({
|
||||
color: group.color ? resolved.colors[group.color] : undefined,
|
||||
reminder: group.reminder ? resolved.reminders[group.reminder] : undefined,
|
||||
tags: withDateEdited(
|
||||
group.tags.map((id) => resolved.tags[id]).filter(Boolean)
|
||||
),
|
||||
notebooks: withDateEdited(
|
||||
group.notebooks.map((id) => resolved.notebooks[id]).filter(Boolean)
|
||||
),
|
||||
attachmentsCount:
|
||||
(await db.attachments?.ofNote(noteId, "all").ids())?.length || 0
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export function isNoteResolvedData(data: unknown): data is NoteResolvedData {
|
||||
return (
|
||||
typeof data === "object" &&
|
||||
!!data &&
|
||||
"notebooks" in data &&
|
||||
"reminder" in data &&
|
||||
"color" in data &&
|
||||
"tags" in data
|
||||
);
|
||||
}
|
||||
@@ -17,14 +17,26 @@ 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 create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import createDBCollectionStore from "./create-db-collection-store";
|
||||
import { Note, VirtualizedGrouping } from "@notesnook/core";
|
||||
|
||||
const { useStore: useFavoriteStore, useCollection: useFavorites } =
|
||||
createDBCollectionStore({
|
||||
getCollection: () =>
|
||||
db.notes.favorites.grouped(db.settings.getGroupOptions("favorites")),
|
||||
eagerlyFetchFirstBatch: true
|
||||
});
|
||||
export interface FavoriteStore extends State {
|
||||
favorites: VirtualizedGrouping<Note> | undefined;
|
||||
setFavorites: (items?: Note[]) => void;
|
||||
clearFavorites: () => void;
|
||||
}
|
||||
|
||||
export { useFavoriteStore, useFavorites };
|
||||
export const useFavoriteStore = create<FavoriteStore>((set) => ({
|
||||
favorites: undefined,
|
||||
setFavorites: () => {
|
||||
db.notes.favorites
|
||||
.grouped(db.settings.getGroupOptions("favorites"))
|
||||
.then((notes) => {
|
||||
set({
|
||||
favorites: notes
|
||||
});
|
||||
});
|
||||
},
|
||||
clearFavorites: () => set({ favorites: undefined })
|
||||
}));
|
||||
|
||||
@@ -27,25 +27,20 @@ export interface MenuStore extends State {
|
||||
setMenuPins: () => void;
|
||||
setColorNotes: () => void;
|
||||
clearAll: () => void;
|
||||
loadingShortcuts: boolean;
|
||||
loadingColors: boolean;
|
||||
}
|
||||
|
||||
export const useMenuStore = create<MenuStore>((set) => ({
|
||||
menuPins: [],
|
||||
colorNotes: [],
|
||||
loadingShortcuts: true,
|
||||
loadingColors: true,
|
||||
setMenuPins: () => {
|
||||
db.shortcuts.resolved().then((shortcuts) => {
|
||||
set({ menuPins: [...(shortcuts as [])], loadingShortcuts: false });
|
||||
set({ menuPins: [...(shortcuts as [])] });
|
||||
});
|
||||
},
|
||||
setColorNotes: () => {
|
||||
db.colors?.all.items().then((colors) => {
|
||||
set({
|
||||
colorNotes: colors,
|
||||
loadingColors: false
|
||||
colorNotes: colors
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -17,14 +17,26 @@ 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 create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import createDBCollectionStore from "./create-db-collection-store";
|
||||
import { VirtualizedGrouping, Notebook } from "@notesnook/core";
|
||||
|
||||
const { useStore: useNotebookStore, useCollection: useNotebooks } =
|
||||
createDBCollectionStore({
|
||||
getCollection: () =>
|
||||
db.notebooks.roots.grouped(db.settings.getGroupOptions("notebooks")),
|
||||
eagerlyFetchFirstBatch: true
|
||||
});
|
||||
export interface NotebookStore extends State {
|
||||
notebooks: VirtualizedGrouping<Notebook> | undefined;
|
||||
setNotebooks: (items?: Notebook[]) => void;
|
||||
clearNotebooks: () => void;
|
||||
}
|
||||
|
||||
export { useNotebookStore, useNotebooks };
|
||||
export const useNotebookStore = create<NotebookStore>((set) => ({
|
||||
notebooks: undefined,
|
||||
setNotebooks: () => {
|
||||
db.notebooks.roots
|
||||
.grouped(db.settings.getGroupOptions("notebooks"))
|
||||
.then((notebooks) => {
|
||||
set({
|
||||
notebooks: notebooks
|
||||
});
|
||||
});
|
||||
},
|
||||
clearNotebooks: () => set({ notebooks: undefined })
|
||||
}));
|
||||
|
||||
@@ -17,14 +17,28 @@ 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 { Note, VirtualizedGrouping } from "@notesnook/core";
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import createDBCollectionStore from "./create-db-collection-store";
|
||||
|
||||
const { useStore: useNoteStore, useCollection: useNotes } =
|
||||
createDBCollectionStore({
|
||||
getCollection: () =>
|
||||
db.notes.all.grouped(db.settings.getGroupOptions("home")),
|
||||
eagerlyFetchFirstBatch: true
|
||||
});
|
||||
export interface NoteStore extends State {
|
||||
notes: VirtualizedGrouping<Note> | undefined;
|
||||
loading: boolean;
|
||||
setLoading: (loading: boolean) => void;
|
||||
setNotes: () => void;
|
||||
clearNotes: () => void;
|
||||
}
|
||||
|
||||
export { useNoteStore, useNotes };
|
||||
export const useNoteStore = create<NoteStore>((set) => ({
|
||||
notes: undefined,
|
||||
loading: true,
|
||||
setLoading: (loading) => set({ loading: loading }),
|
||||
setNotes: () => {
|
||||
db.notes.all.grouped(db.settings.getGroupOptions("home")).then((notes) => {
|
||||
set({
|
||||
notes: notes
|
||||
});
|
||||
});
|
||||
},
|
||||
clearNotes: () => set({ notes: undefined })
|
||||
}));
|
||||
|
||||
@@ -17,14 +17,26 @@ 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 create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import createDBCollectionStore from "./create-db-collection-store";
|
||||
import { Reminder, VirtualizedGrouping } from "@notesnook/core";
|
||||
|
||||
const { useStore: useReminderStore, useCollection: useReminders } =
|
||||
createDBCollectionStore({
|
||||
getCollection: () =>
|
||||
db.reminders.all.grouped(db.settings.getGroupOptions("reminders")),
|
||||
eagerlyFetchFirstBatch: true
|
||||
});
|
||||
export interface ReminderStore extends State {
|
||||
reminders: VirtualizedGrouping<Reminder> | undefined;
|
||||
setReminders: (items?: Reminder[]) => void;
|
||||
cleareReminders: () => void;
|
||||
}
|
||||
|
||||
export { useReminderStore, useReminders };
|
||||
export const useReminderStore = create<ReminderStore>((set) => ({
|
||||
reminders: undefined,
|
||||
setReminders: () => {
|
||||
db.reminders.all
|
||||
.grouped(db.settings.getGroupOptions("reminders"))
|
||||
.then((reminders) => {
|
||||
set({
|
||||
reminders: reminders
|
||||
});
|
||||
});
|
||||
},
|
||||
cleareReminders: () => set({ reminders: undefined })
|
||||
}));
|
||||
|
||||
@@ -74,6 +74,7 @@ export type Settings = {
|
||||
colorScheme: "dark" | "light";
|
||||
lighTheme: ThemeDefinition;
|
||||
darkTheme: ThemeDefinition;
|
||||
backgroundSync?: boolean;
|
||||
};
|
||||
|
||||
type DimensionsType = {
|
||||
@@ -97,8 +98,8 @@ export interface SettingStore extends State {
|
||||
setFullscreen: (fullscreen: boolean) => void;
|
||||
setDeviceMode: (mode: string) => void;
|
||||
setDimensions: (dimensions: DimensionsType) => void;
|
||||
isAppLoading: boolean;
|
||||
setAppLoading: (isAppLoading: boolean) => void;
|
||||
appLoading: boolean;
|
||||
setAppLoading: (appLoading: boolean) => void;
|
||||
setSheetKeyboardHandler: (sheetKeyboardHandler: boolean) => void;
|
||||
sheetKeyboardHandler: boolean;
|
||||
requestBiometrics: boolean;
|
||||
@@ -163,12 +164,12 @@ export const useSettingStore = create<SettingStore>((set, get) => ({
|
||||
fullscreen: false,
|
||||
deviceMode: null,
|
||||
dimensions: { width, height },
|
||||
isAppLoading: true,
|
||||
appLoading: true,
|
||||
setSettings: (settings) => set({ settings }),
|
||||
setFullscreen: (fullscreen) => set({ fullscreen }),
|
||||
setDeviceMode: (mode) => set({ deviceMode: mode }),
|
||||
setDimensions: (dimensions) => set({ dimensions: dimensions }),
|
||||
setAppLoading: (isAppLoading) => set({ isAppLoading }),
|
||||
setAppLoading: (appLoading) => set({ appLoading }),
|
||||
setSheetKeyboardHandler: (sheetKeyboardHandler) =>
|
||||
set({ sheetKeyboardHandler }),
|
||||
requestBiometrics: false,
|
||||
|
||||
@@ -17,14 +17,24 @@ 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 create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import createDBCollectionStore from "./create-db-collection-store";
|
||||
import { Tag, VirtualizedGrouping } from "@notesnook/core";
|
||||
|
||||
const { useStore: useTagStore, useCollection: useTags } =
|
||||
createDBCollectionStore({
|
||||
getCollection: () =>
|
||||
db.tags.all.grouped(db.settings.getGroupOptions("tags")),
|
||||
eagerlyFetchFirstBatch: true
|
||||
});
|
||||
export interface TagStore extends State {
|
||||
tags: VirtualizedGrouping<Tag> | undefined;
|
||||
setTags: (items?: Tag[]) => void;
|
||||
clearTags: () => void;
|
||||
}
|
||||
|
||||
export { useTagStore, useTags };
|
||||
export const useTagStore = create<TagStore>((set) => ({
|
||||
tags: undefined,
|
||||
setTags: () => {
|
||||
db.tags.all.grouped(db.settings.getGroupOptions("tags")).then((tags) => {
|
||||
set({
|
||||
tags: tags
|
||||
});
|
||||
});
|
||||
},
|
||||
clearTags: () => set({ tags: undefined })
|
||||
}));
|
||||
|
||||
@@ -17,13 +17,25 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import createDBCollectionStore from "./create-db-collection-store";
|
||||
import { GroupedItems, TrashItem } from "@notesnook/core/dist/types";
|
||||
import { VirtualizedGrouping } from "@notesnook/core";
|
||||
|
||||
const { useStore: useTrashStore, useCollection: useTrash } =
|
||||
createDBCollectionStore({
|
||||
getCollection: () => db.trash.grouped(db.settings.getGroupOptions("trash")),
|
||||
eagerlyFetchFirstBatch: true
|
||||
});
|
||||
export interface TrashStore extends State {
|
||||
trash: VirtualizedGrouping<TrashItem> | undefined;
|
||||
setTrash: (items?: GroupedItems<TrashItem>) => void;
|
||||
clearTrash: () => void;
|
||||
}
|
||||
|
||||
export { useTrashStore, useTrash };
|
||||
export const useTrashStore = create<TrashStore>((set, get) => ({
|
||||
trash: undefined,
|
||||
setTrash: () => {
|
||||
db.trash.grouped(db.settings.getGroupOptions("trash")).then((trash) => {
|
||||
set({
|
||||
trash: trash
|
||||
});
|
||||
});
|
||||
},
|
||||
clearTrash: () => set({ trash: undefined })
|
||||
}));
|
||||
|
||||
@@ -111,7 +111,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2073
|
||||
versionCode 2072
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -161,12 +161,12 @@
|
||||
android:resource="@xml/file_viewer_provider_paths" />
|
||||
</provider>
|
||||
|
||||
<!-- <receiver android:exported="true" android:name=".BootRecieverService" >-->
|
||||
<!-- <intent-filter>-->
|
||||
<!-- <action android:name="android.intent.action.BOOT_COMPLETED" />-->
|
||||
<!-- <action android:name="android.intent.action.QUICKBOOT_POWERON" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </receiver>-->
|
||||
<receiver android:exported="true" android:name=".BootRecieverService" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
- Added support to check/uncheck all items in a checklist
|
||||
- Now you can open pdfs/images in readonly mode
|
||||
- Fixed a bug where users are unable to login when app lock is enabled
|
||||
- Added support for markdown with frontmatter in exports
|
||||
- Fixed a bug that would randomly cause the app to freeze on launch after using the app for some days.
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
Binary file not shown.
@@ -9,6 +9,8 @@ import appJson from './app.json';
|
||||
import Notifications from "../app/services/notifications";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { enableFreeze } from "react-native-screens";
|
||||
import {BackgroundSync} from '../app/services/background-sync';
|
||||
|
||||
enableFreeze(true);
|
||||
NetInfo.configure({
|
||||
reachabilityUrl: "https://notesnook.com",
|
||||
@@ -17,7 +19,7 @@ NetInfo.configure({
|
||||
return response?.status >= 200 && response?.status < 300;
|
||||
}
|
||||
});
|
||||
|
||||
BackgroundSync.start();
|
||||
Notifications.init();
|
||||
|
||||
const appName = appJson.name;
|
||||
|
||||
@@ -1003,7 +1003,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2065;
|
||||
CURRENT_PROJECT_VERSION = 2063;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1077,7 +1077,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.14;
|
||||
MARKETING_VERSION = 2.6.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1106,11 +1106,9 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2065;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2063;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
@@ -1182,7 +1180,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.14;
|
||||
MARKETING_VERSION = 2.6.12;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1340,7 +1338,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2065;
|
||||
CURRENT_PROJECT_VERSION = 2063;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1352,7 +1350,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.14;
|
||||
MARKETING_VERSION = 2.6.12;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1382,7 +1380,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2065;
|
||||
CURRENT_PROJECT_VERSION = 2063;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1394,7 +1392,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.14;
|
||||
MARKETING_VERSION = 2.6.12;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1423,7 +1421,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2065;
|
||||
CURRENT_PROJECT_VERSION = 2063;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1497,7 +1495,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.14;
|
||||
MARKETING_VERSION = 2.6.12;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1527,7 +1525,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2065;
|
||||
CURRENT_PROJECT_VERSION = 2063;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1601,7 +1599,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.14;
|
||||
MARKETING_VERSION = 2.6.12;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -63,7 +63,8 @@
|
||||
"react-native-vector-icons": "9.2.0",
|
||||
"react-native-webview": "^11.14.1",
|
||||
"react-native-zip-archive": "6.0.9",
|
||||
"react-native-quick-sqlite": "^8.0.6"
|
||||
"react-native-quick-sqlite": "^8.0.6",
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
|
||||
@@ -100,7 +100,6 @@ module.exports = (env) => {
|
||||
"react-native-blob-util": path.join(__dirname, "../node_modules/react-native-blob-util"),
|
||||
"@mdi/js": path.join(__dirname, "../node_modules/@mdi/js/mdi.js"),
|
||||
"katex": path.join(__dirname, "../node_modules/katex"),
|
||||
"tinycolor2": path.join(__dirname, "../node_modules/tinycolor2"),
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
||||
883
apps/mobile/package-lock.json
generated
883
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.6.14",
|
||||
"version": "2.6.12",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -44,7 +44,6 @@
|
||||
"kysely": "^0.26.3",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0",
|
||||
"react-native-quick-sqlite": "^8.0.6",
|
||||
"tinycolor2": "1.6.0"
|
||||
"react-native-quick-sqlite": "^8.0.6"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
diff --git a/node_modules/recyclerlistview/dist/reactnative/core/layoutmanager/LayoutManager.js b/node_modules/recyclerlistview/dist/reactnative/core/layoutmanager/LayoutManager.js
|
||||
index 9cd3c57..e5bcb69 100644
|
||||
index 9cd3c57..970421a 100644
|
||||
--- a/node_modules/recyclerlistview/dist/reactnative/core/layoutmanager/LayoutManager.js
|
||||
+++ b/node_modules/recyclerlistview/dist/reactnative/core/layoutmanager/LayoutManager.js
|
||||
@@ -111,7 +111,7 @@ var WrapGridLayoutManager = /** @class */ (function (_super) {
|
||||
@@ -7,19 +7,7 @@ index 9cd3c57..e5bcb69 100644
|
||||
var itemRect = null;
|
||||
var oldLayout = null;
|
||||
- for (var i = startIndex; i < itemCount; i++) {
|
||||
+ for (var i = startIndex; i < Math.min(startIndex + 500, itemCount); i++) {
|
||||
+ for (var i = startIndex; i < Math.min(startIndex + 5000, itemCount); i++) {
|
||||
oldLayout = this._layouts[i];
|
||||
var layoutType = this._layoutProvider.getLayoutTypeForIndex(i);
|
||||
if (oldLayout && oldLayout.isOverridden && oldLayout.type === layoutType) {
|
||||
@@ -184,6 +184,11 @@ var WrapGridLayoutManager = /** @class */ (function (_super) {
|
||||
}
|
||||
var i = startIndex - 1;
|
||||
for (; i >= 0; i--) {
|
||||
+
|
||||
+ if (!this._layouts[i]) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
if (this._isHorizontal) {
|
||||
if (this._layouts[i].y === 0) {
|
||||
break;
|
||||
|
||||
@@ -36,7 +36,6 @@ const EXTRA_ICON_NAMES = [
|
||||
"star-outline",
|
||||
"link-variant-remove",
|
||||
"link-variant",
|
||||
"link-variant-off",
|
||||
"bell",
|
||||
"bell-off-outline",
|
||||
"check",
|
||||
|
||||
@@ -17,24 +17,20 @@ 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 {
|
||||
Item,
|
||||
Note,
|
||||
Notebook,
|
||||
Tag,
|
||||
VirtualizedGrouping
|
||||
} from "@notesnook/core";
|
||||
import { Item, Note, Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
FlatList,
|
||||
Platform,
|
||||
StatusBar,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import create from "zustand";
|
||||
import { db } from "../app/common/database";
|
||||
@@ -125,29 +121,23 @@ const NotebookItem = ({
|
||||
items,
|
||||
level = 0
|
||||
}: {
|
||||
id: string | number;
|
||||
id: string;
|
||||
mode: SearchMode;
|
||||
close?: () => void;
|
||||
level?: number;
|
||||
items?: VirtualizedGrouping<Notebook>;
|
||||
}) => {
|
||||
const isExpanded = useNotebookExpandedStore((state) => state.expanded[id]);
|
||||
const { nestedNotebooks, notebook } = useNotebook(id, items);
|
||||
const isExpanded = useNotebookExpandedStore((state) =>
|
||||
!notebook ? false : state.expanded[notebook.id]
|
||||
);
|
||||
const { colors } = useThemeColors();
|
||||
const isSelected = useShareStore((state) =>
|
||||
!notebook
|
||||
? false
|
||||
: state.selectedNotebooks.findIndex(
|
||||
(selectedId) => notebook?.id === selectedId
|
||||
) > -1
|
||||
const isSelected = useShareStore(
|
||||
(state) =>
|
||||
state.selectedNotebooks.findIndex((selectedId) => id === selectedId) > -1
|
||||
);
|
||||
const set = SearchSetters[mode];
|
||||
|
||||
const onSelectItem = async () => {
|
||||
if (!notebook) return;
|
||||
set(notebook.id);
|
||||
set(id);
|
||||
};
|
||||
|
||||
return !notebook ? (
|
||||
@@ -182,8 +172,7 @@ const NotebookItem = ({
|
||||
alignItems: "center"
|
||||
}}
|
||||
onPress={() => {
|
||||
if (!notebook) return;
|
||||
useNotebookExpandedStore.getState().setExpanded(notebook.id);
|
||||
useNotebookExpandedStore.getState().setExpanded(id);
|
||||
}}
|
||||
activeOpacity={1}
|
||||
>
|
||||
@@ -241,10 +230,10 @@ const NotebookItem = ({
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{nestedNotebooks.ids.map((item, index) => (
|
||||
{(nestedNotebooks.ids as string[]).map((id) => (
|
||||
<NotebookItem
|
||||
key={notebook?.id + index}
|
||||
id={index}
|
||||
key={id}
|
||||
id={id}
|
||||
mode={mode}
|
||||
close={close}
|
||||
level={level + 1}
|
||||
@@ -262,7 +251,7 @@ const ListItem = ({
|
||||
close,
|
||||
items
|
||||
}: {
|
||||
id: string | number;
|
||||
id: string;
|
||||
mode: SearchMode;
|
||||
close?: () => void;
|
||||
items?: VirtualizedGrouping<Item>;
|
||||
@@ -270,23 +259,21 @@ const ListItem = ({
|
||||
const [item] = useDBItem(
|
||||
id,
|
||||
mode === "appendNote" ? "note" : "tag",
|
||||
items as VirtualizedGrouping<Note | Tag>
|
||||
items as any
|
||||
);
|
||||
const { colors } = useThemeColors();
|
||||
const isSelected = useShareStore((state) =>
|
||||
mode === "appendNote" || !item
|
||||
? false
|
||||
: state.selectedTags.indexOf(item?.id as never) > -1
|
||||
mode === "appendNote" ? false : state.selectedTags.indexOf(id as never) > -1
|
||||
);
|
||||
|
||||
const set = SearchSetters[mode];
|
||||
|
||||
const onSelectItem = async () => {
|
||||
if ((item as Note)?.locked || !item) {
|
||||
if ((item as Note)?.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(item.id);
|
||||
set(id);
|
||||
};
|
||||
|
||||
return !item ? null : (
|
||||
@@ -369,6 +356,12 @@ export const Search = ({
|
||||
.then((exists) => setQueryExists(!!exists));
|
||||
};
|
||||
|
||||
const insets =
|
||||
Platform.OS === "android"
|
||||
? { top: StatusBar.currentHeight }
|
||||
: // eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useSafeAreaInsets();
|
||||
|
||||
const get = SearchGetters[mode];
|
||||
const lookup = SearchLookup[mode];
|
||||
|
||||
@@ -396,16 +389,16 @@ export const Search = ({
|
||||
}, [get]);
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({ index }: { item: string | number; index: number }) =>
|
||||
({ item }: { item: string }) =>
|
||||
mode === "selectNotebooks" ? (
|
||||
<NotebookItem
|
||||
id={index}
|
||||
id={item}
|
||||
mode={mode}
|
||||
close={close}
|
||||
items={items as VirtualizedGrouping<Notebook>}
|
||||
items={items as any}
|
||||
/>
|
||||
) : (
|
||||
<ListItem id={index} mode={mode} close={close} items={items} />
|
||||
<ListItem id={item} mode={mode} close={close} items={items} />
|
||||
),
|
||||
[close, mode, items]
|
||||
);
|
||||
@@ -509,7 +502,7 @@ export const Search = ({
|
||||
) : null}
|
||||
|
||||
<FlatList
|
||||
data={items?.ids}
|
||||
data={items?.ids as string[]}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
renderItem={renderItem}
|
||||
|
||||
8
apps/theme-builder/package-lock.json
generated
8
apps/theme-builder/package-lock.json
generated
@@ -331,7 +331,6 @@
|
||||
"version": "1.2.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@emotion/react": "11.11.1",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
@@ -340,7 +339,6 @@
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"react": "18.2.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"ts-json-schema-generator": "^1.2.0",
|
||||
"zustand": "4.4.7"
|
||||
},
|
||||
@@ -977,7 +975,7 @@
|
||||
},
|
||||
"../web": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@aws-sdk/util-base64-browser": "^3.208.0",
|
||||
@@ -1004,14 +1002,13 @@
|
||||
"@react-pdf-viewer/core": "^3.12.0",
|
||||
"@react-pdf-viewer/toolbar": "^3.12.0",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@tanstack/react-virtual": "^3.0.1",
|
||||
"@tanstack/react-virtual": "^3.0.0-beta.68",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"allotment": "^1.19.3",
|
||||
"async-mutex": "^0.4.0",
|
||||
"axios": "^1.3.4",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"comlink": "^4.3.1",
|
||||
@@ -1042,7 +1039,6 @@
|
||||
"react-modal": "3.16.1",
|
||||
"react-qrcode-logo": "^2.9.0",
|
||||
"react-scroll-sync": "^0.11.2",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"timeago.js": "4.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"w3c-keyname": "^2.2.6",
|
||||
|
||||
@@ -81,7 +81,7 @@ export class AppModel {
|
||||
|
||||
async goToTags() {
|
||||
await this.navigateTo("Tags");
|
||||
return new ItemsViewModel(this.page);
|
||||
return new ItemsViewModel(this.page, "tags");
|
||||
}
|
||||
|
||||
async goToColor(color: string) {
|
||||
|
||||
@@ -35,7 +35,7 @@ test("chunked stream should create equal sized chunks", async (t) => {
|
||||
path.join(__dirname, "..", "__e2e__", "data", "importer-data.zip")
|
||||
)
|
||||
) as ReadableStream<Uint8Array>
|
||||
).pipeThrough(new ChunkedStream(CHUNK_SIZE, "nocopy"))
|
||||
).pipeThrough(new ChunkedStream(CHUNK_SIZE))
|
||||
);
|
||||
|
||||
t.expect(await Promise.all(chunks.map((a) => xxhash64(a)))).toMatchObject([
|
||||
|
||||
54
apps/web/package-lock.json
generated
54
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@aws-sdk/util-base64-browser": "^3.208.0",
|
||||
@@ -33,14 +33,13 @@
|
||||
"@react-pdf-viewer/core": "^3.12.0",
|
||||
"@react-pdf-viewer/toolbar": "^3.12.0",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@tanstack/react-virtual": "^3.0.1",
|
||||
"@tanstack/react-virtual": "^3.0.0-beta.68",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"allotment": "^1.19.3",
|
||||
"async-mutex": "^0.4.0",
|
||||
"axios": "^1.3.4",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"comlink": "^4.3.1",
|
||||
@@ -71,7 +70,6 @@
|
||||
"react-modal": "3.16.1",
|
||||
"react-qrcode-logo": "^2.9.0",
|
||||
"react-scroll-sync": "^0.11.2",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"timeago.js": "4.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"w3c-keyname": "^2.2.6",
|
||||
@@ -247,7 +245,7 @@
|
||||
"@types/spark-md5": "^3.0.2",
|
||||
"@types/streetwriters__showdown": "npm:@types/showdown@^2.0.0",
|
||||
"@types/ws": "^8.5.5",
|
||||
"@vitest/coverage-v8": "^1.0.1",
|
||||
"@vitest/coverage-v8": "^0.34.1",
|
||||
"abortcontroller-polyfill": "^1.7.3",
|
||||
"better-sqlite3": "^8.6.0",
|
||||
"bson-objectid": "^2.0.4",
|
||||
@@ -262,7 +260,7 @@
|
||||
"nanoid": "^5.0.1",
|
||||
"otplib": "^12.0.1",
|
||||
"refractor": "^4.8.1",
|
||||
"vitest": "^1.0.1",
|
||||
"vitest": "^0.34.1",
|
||||
"vitest-fetch-mock": "^0.2.2",
|
||||
"ws": "^8.13.0"
|
||||
}
|
||||
@@ -385,7 +383,6 @@
|
||||
"version": "1.2.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@emotion/react": "11.11.1",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
@@ -394,7 +391,6 @@
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"react": "18.2.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"ts-json-schema-generator": "^1.2.0",
|
||||
"zustand": "4.4.7"
|
||||
},
|
||||
@@ -465,7 +461,7 @@
|
||||
},
|
||||
"../desktop": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -483,8 +479,8 @@
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "25.9.8",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron": "25.9.3",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
"tree-kill": "^1.2.2",
|
||||
@@ -4233,11 +4229,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.0.1.tgz",
|
||||
"integrity": "sha512-IFOFuRUTaiM/yibty9qQ9BfycQnYXIDHGP2+cU+0LrFFGNhVxCXSQnaY6wkX8uJVteFEBjUondX0Hmpp7TNcag==",
|
||||
"version": "3.0.0-beta.68",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.0.0-beta.68.tgz",
|
||||
"integrity": "sha512-YEFNCf+N3ZlNou2r4qnh+GscMe24foYEjTL05RS0ZHvah2RoUDPGuhnuedTv0z66dO2vIq6+Bl4TXatht5T7GQ==",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.0.0"
|
||||
"@tanstack/virtual-core": "3.0.0-beta.68"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -4249,9 +4245,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.0.0.tgz",
|
||||
"integrity": "sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==",
|
||||
"version": "3.0.0-beta.68",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.0.0-beta.68.tgz",
|
||||
"integrity": "sha512-CnvsEJWK7cugigckt13AeY80FMzH+OMdEP0j0bS3/zjs44NiRe49x8FZC6R9suRXGMVMXtUHet0zbTp/Ec9Wfg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
@@ -4796,14 +4792,6 @@
|
||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz",
|
||||
"integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@@ -8419,18 +8407,6 @@
|
||||
"react-dom": "16.x || 17.x || 18.x"
|
||||
}
|
||||
},
|
||||
"node_modules/react-virtuoso": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.6.2.tgz",
|
||||
"integrity": "sha512-vvlqvzPif+MvBrJ09+hJJrVY0xJK9yran+A+/1iwY78k0YCVKsyoNPqoLxOxzYPggspNBNXqUXEcvckN29OxyQ==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16 || >=17 || >= 18",
|
||||
"react-dom": ">=16 || >=17 || >= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.6.13",
|
||||
"version": "2.6.12",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
@@ -32,14 +32,13 @@
|
||||
"@react-pdf-viewer/core": "^3.12.0",
|
||||
"@react-pdf-viewer/toolbar": "^3.12.0",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@tanstack/react-virtual": "^3.0.1",
|
||||
"@tanstack/react-virtual": "^3.0.0-beta.68",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"allotment": "^1.19.3",
|
||||
"async-mutex": "^0.4.0",
|
||||
"axios": "^1.3.4",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"comlink": "^4.3.1",
|
||||
@@ -61,22 +60,21 @@
|
||||
"phone": "^3.1.14",
|
||||
"platform": "^1.3.6",
|
||||
"qclone": "^1.2.0",
|
||||
"react": "18.2.0",
|
||||
"react-complex-tree": "^2.2.4",
|
||||
"react-dom": "18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-loading-skeleton": "^3.3.1",
|
||||
"react-modal": "3.16.1",
|
||||
"react-qrcode-logo": "^2.9.0",
|
||||
"react-scroll-sync": "^0.11.2",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"timeago.js": "4.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"w3c-keyname": "^2.2.6",
|
||||
"web-streams-polyfill": "^3.1.1",
|
||||
"wouter": "2.12.1",
|
||||
"zustand": "4.4.7"
|
||||
"zustand": "4.4.7",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
@@ -129,8 +127,7 @@
|
||||
"build:beta": "cross-env PLATFORM=web BETA=true vite build",
|
||||
"build:desktop": "cross-env PLATFORM=desktop vite build",
|
||||
"analyze": "cross-env ANALYZING=true PLATFORM=web vite build",
|
||||
"test": "playwright test -u",
|
||||
"postinstall": "patch-package"
|
||||
"test": "playwright test -u"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,14 +18,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { lazify } from "../utils/lazify";
|
||||
import { Attachment } from "@notesnook/core";
|
||||
import { db } from "./db";
|
||||
|
||||
async function download(hash: string) {
|
||||
const attachment = await db.attachments.attachment(hash);
|
||||
const attachment = db.attachments.attachment(hash);
|
||||
if (!attachment) return;
|
||||
const downloadResult = await db
|
||||
.fs()
|
||||
.downloadFile(attachment.hash, attachment.hash, attachment.chunkSize);
|
||||
.downloadFile(
|
||||
attachment.metadata.hash,
|
||||
attachment.metadata.hash,
|
||||
attachment.chunkSize,
|
||||
attachment.metadata
|
||||
);
|
||||
if (!downloadResult) throw new Error("Failed to download file.");
|
||||
|
||||
const key = await db.attachments.decryptKey(attachment.key);
|
||||
@@ -40,7 +46,7 @@ export async function saveAttachment(hash: string) {
|
||||
|
||||
const { attachment, key } = response;
|
||||
await lazify(import("../interfaces/fs"), ({ saveFile }) =>
|
||||
saveFile(attachment.hash, {
|
||||
saveFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.filename,
|
||||
@@ -67,7 +73,7 @@ export async function downloadAttachment<
|
||||
return (await db.attachments.read(hash, type)) as TOutputType;
|
||||
|
||||
const blob = await lazify(import("../interfaces/fs"), ({ decryptFile }) =>
|
||||
decryptFile(attachment.hash, {
|
||||
decryptFile(attachment.metadata.hash, {
|
||||
key,
|
||||
iv: attachment.iv,
|
||||
name: attachment.filename,
|
||||
|
||||
@@ -17,6 +17,16 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export const COLORS = [
|
||||
{ key: "red", title: "Red" },
|
||||
{ key: "orange", title: "Orange" },
|
||||
{ key: "yellow", title: "Yellow" },
|
||||
{ key: "green", title: "Green" },
|
||||
{ key: "blue", title: "Blue" },
|
||||
{ key: "purple", title: "Purple" },
|
||||
{ key: "gray", title: "Gray" }
|
||||
] as const;
|
||||
|
||||
export const SUBSCRIPTION_STATUS = {
|
||||
BASIC: 0,
|
||||
TRIAL: 1,
|
||||
|
||||
@@ -57,15 +57,14 @@ async function initializeDatabase(persistence: DatabasePersistence) {
|
||||
|
||||
database.setup({
|
||||
sqliteOptions: {
|
||||
dialect: (name) => ({
|
||||
dialect: {
|
||||
createDriver: () =>
|
||||
new WaSqliteWorkerDriver({ async: true, dbName: name }),
|
||||
new WaSqliteWorkerDriver({ async: true, dbName: "test.db" }),
|
||||
createAdapter: () => new SqliteAdapter(),
|
||||
createIntrospector: (db) => new SqliteIntrospector(db),
|
||||
createQueryCompiler: () => new SqliteQueryCompiler()
|
||||
}),
|
||||
},
|
||||
journalMode: "MEMORY",
|
||||
tempStore: "memory",
|
||||
synchronous: "normal",
|
||||
pageSize: 8192,
|
||||
cacheSize: -16000,
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Dialogs } from "../dialogs";
|
||||
import { store as notebookStore } from "../stores/notebook-store";
|
||||
import { store as tagStore } from "../stores/tag-store";
|
||||
import { store as appStore } from "../stores/app-store";
|
||||
import { store as editorStore } from "../stores/editor-store";
|
||||
@@ -25,6 +26,7 @@ import { store as noteStore } from "../stores/note-store";
|
||||
import { db } from "./db";
|
||||
import { showToast } from "../utils/toast";
|
||||
import { Text } from "@theme-ui/components";
|
||||
import { Topic as TopicIcon } from "../components/icons";
|
||||
import Config from "../utils/config";
|
||||
import { AppVersion, getChangelog } from "../utils/version";
|
||||
import { Period } from "../dialogs/buy-dialog/types";
|
||||
@@ -34,7 +36,7 @@ import { ConfirmDialogProps } from "../dialogs/confirm";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
import { downloadUpdate } from "../utils/updater";
|
||||
import { ThemeMetadata } from "@notesnook/themes-server";
|
||||
import { Color, Reminder, Tag } from "@notesnook/core";
|
||||
import { Reminder } from "@notesnook/core";
|
||||
import { AuthenticatorType } from "@notesnook/core/dist/api/user-manager";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
@@ -88,15 +90,6 @@ export function showAddTagsDialog(noteIds: string[]) {
|
||||
));
|
||||
}
|
||||
|
||||
export function showCreateColorDialog() {
|
||||
return showDialog<"CreateColorDialog", string | undefined>(
|
||||
"CreateColorDialog",
|
||||
(Dialog, perform) => (
|
||||
<Dialog onClose={() => perform(undefined)} onDone={(id) => perform(id)} />
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function showAddNotebookDialog(parentId?: string) {
|
||||
return showDialog("AddNotebookDialog", (Dialog, perform) => (
|
||||
<Dialog
|
||||
@@ -419,25 +412,21 @@ export function showPasswordDialog(
|
||||
password?: string;
|
||||
oldPassword?: string;
|
||||
newPassword?: string;
|
||||
deleteAllLockedNotes?: boolean;
|
||||
}) => boolean | Promise<boolean>
|
||||
) {
|
||||
const { title, subtitle, positiveButtonText, checks } = getDialogData(type);
|
||||
return showDialog<"PasswordDialog", boolean>(
|
||||
"PasswordDialog",
|
||||
(Dialog, perform) => (
|
||||
<Dialog
|
||||
type={type}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
checks={checks}
|
||||
positiveButtonText={positiveButtonText}
|
||||
validate={validate}
|
||||
onClose={() => perform(false)}
|
||||
onDone={() => perform(true)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
return showDialog("PasswordDialog", (Dialog, perform) => (
|
||||
<Dialog
|
||||
type={type}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
checks={checks}
|
||||
positiveButtonText={positiveButtonText}
|
||||
validate={validate}
|
||||
onClose={() => perform(false)}
|
||||
onDone={() => perform(true)}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
export function showRecoveryKeyDialog() {
|
||||
@@ -469,7 +458,9 @@ export function showCreateTagDialog() {
|
||||
));
|
||||
}
|
||||
|
||||
export function showEditTagDialog(tag: Tag) {
|
||||
export function showEditTagDialog(tagId: string) {
|
||||
const tag = db.tags.tag(tagId);
|
||||
if (!tag) return;
|
||||
return showDialog("ItemDialog", (Dialog, perform) => (
|
||||
<Dialog
|
||||
title={"Edit tag"}
|
||||
@@ -479,7 +470,7 @@ export function showEditTagDialog(tag: Tag) {
|
||||
onClose={() => perform(false)}
|
||||
onAction={async (title: string) => {
|
||||
if (!title) return;
|
||||
await db.tags.add({ id: tag.id, title });
|
||||
await db.tags.add({ id: tagId, title });
|
||||
showToast("success", "Tag edited!");
|
||||
tagStore.refresh();
|
||||
editorStore.refreshTags();
|
||||
@@ -491,7 +482,9 @@ export function showEditTagDialog(tag: Tag) {
|
||||
));
|
||||
}
|
||||
|
||||
export function showRenameColorDialog(color: Color) {
|
||||
export function showRenameColorDialog(colorId: string) {
|
||||
const color = db.colors.color(colorId);
|
||||
if (!color) return;
|
||||
return showDialog("ItemDialog", (Dialog, perform) => (
|
||||
<Dialog
|
||||
title={"Rename color"}
|
||||
@@ -501,7 +494,7 @@ export function showRenameColorDialog(color: Color) {
|
||||
onClose={() => perform(false)}
|
||||
onAction={async (title: string) => {
|
||||
if (!title) return;
|
||||
await db.tags.add({ id: color.id, title });
|
||||
await db.tags.add({ id: colorId, title });
|
||||
showToast("success", "Color renamed!");
|
||||
appStore.refreshNavItems();
|
||||
perform(true);
|
||||
|
||||
@@ -39,8 +39,7 @@ import { createWritableStream } from "./desktop-bridge";
|
||||
import { ZipStream } from "../utils/streams/zip-stream";
|
||||
import { FeatureKeys } from "../dialogs/feature-dialog";
|
||||
import { Entry, Reader } from "../utils/zip-reader";
|
||||
import { User } from "@notesnook/core/dist/api/user-manager";
|
||||
import { LegacyBackupFile } from "@notesnook/core";
|
||||
import { IndexedDBKVStore } from "../interfaces/key-value";
|
||||
|
||||
export const CREATE_BUTTON_MAP = {
|
||||
notes: {
|
||||
@@ -52,6 +51,10 @@ export const CREATE_BUTTON_MAP = {
|
||||
title: "Create a notebook",
|
||||
onClick: () => hashNavigate("/notebooks/create", { replace: true })
|
||||
},
|
||||
topics: {
|
||||
title: "Create a topic",
|
||||
onClick: () => hashNavigate(`/topics/create`, { replace: true })
|
||||
},
|
||||
tags: {
|
||||
title: "Create a tag",
|
||||
onClick: () => hashNavigate(`/tags/create`, { replace: true })
|
||||
@@ -136,9 +139,8 @@ export async function selectBackupFile() {
|
||||
|
||||
export async function importBackup() {
|
||||
const backupFile = await selectBackupFile();
|
||||
if (!backupFile) return false;
|
||||
if (!backupFile) return;
|
||||
await restoreBackupFile(backupFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function restoreBackupFile(backupFile: File) {
|
||||
@@ -194,7 +196,7 @@ export async function restoreBackupFile(backupFile: File) {
|
||||
if (!result) break;
|
||||
} else await db.backup?.import(backup, cachedPassword);
|
||||
} else {
|
||||
await db.backup?.import(backup);
|
||||
await db.backup?.import(backup, null);
|
||||
}
|
||||
|
||||
report({
|
||||
@@ -214,7 +216,7 @@ export async function restoreBackupFile(backupFile: File) {
|
||||
}
|
||||
|
||||
async function restoreWithProgress(
|
||||
backup: LegacyBackupFile,
|
||||
backup: Record<string, unknown>,
|
||||
password?: string
|
||||
) {
|
||||
return await TaskManager.startTask<Error | void>({
|
||||
@@ -249,8 +251,8 @@ async function restoreWithProgress(
|
||||
|
||||
export async function verifyAccount() {
|
||||
if (!(await db.user?.getUser())) return true;
|
||||
return showPasswordDialog("verify_account", async ({ password }) => {
|
||||
return !!password && (await db.user?.verifyPassword(password));
|
||||
return showPasswordDialog("verify_account", ({ password }) => {
|
||||
return db.user?.verifyPassword(password) || false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -282,7 +284,7 @@ export async function showUpgradeReminderDialogs() {
|
||||
}
|
||||
}
|
||||
|
||||
async function restore(backup: LegacyBackupFile, password?: string) {
|
||||
async function restore(backup: Record<string, unknown>, password?: string) {
|
||||
try {
|
||||
await db.backup?.import(backup, password);
|
||||
showToast("success", "Backup restored!");
|
||||
|
||||
@@ -27,6 +27,7 @@ import { showToast } from "../utils/toast";
|
||||
import Vault from "./vault";
|
||||
import { TaskManager } from "./task-manager";
|
||||
import { pluralize } from "@notesnook/common";
|
||||
import { Reminder } from "@notesnook/core";
|
||||
|
||||
async function moveNotesToTrash(ids: string[], confirm = true) {
|
||||
if (confirm && !(await showMultiDeleteConfirmation(ids.length))) return;
|
||||
@@ -96,7 +97,7 @@ async function deleteAttachments(ids: string[]) {
|
||||
action: async (report) => {
|
||||
for (let i = 0; i < ids.length; ++i) {
|
||||
const id = ids[i];
|
||||
const attachment = await db.attachments.attachment(id);
|
||||
const attachment = await attachmentStore.get().attachments?.item(id);
|
||||
if (!attachment) continue;
|
||||
|
||||
report({
|
||||
@@ -111,10 +112,10 @@ async function deleteAttachments(ids: string[]) {
|
||||
showToast("success", `${pluralize(ids.length, "attachment")} deleted`);
|
||||
}
|
||||
|
||||
async function moveRemindersToTrash(ids: string[]) {
|
||||
const isMultiselect = ids.length > 1;
|
||||
async function moveRemindersToTrash(reminders: Reminder[]) {
|
||||
const isMultiselect = reminders.length > 1;
|
||||
if (isMultiselect) {
|
||||
if (!(await showMultiDeleteConfirmation(ids.length))) return;
|
||||
if (!(await showMultiDeleteConfirmation(reminders.length))) return;
|
||||
}
|
||||
|
||||
await TaskManager.startTask({
|
||||
@@ -122,13 +123,13 @@ async function moveRemindersToTrash(ids: string[]) {
|
||||
id: "deleteReminders",
|
||||
action: async (report) => {
|
||||
report({
|
||||
text: `Deleting ${pluralize(ids.length, "reminder")}...`
|
||||
text: `Deleting ${pluralize(reminders.length, "reminder")}...`
|
||||
});
|
||||
await reminderStore.delete(...ids);
|
||||
await reminderStore.delete(...reminders.map((i) => i.id));
|
||||
}
|
||||
});
|
||||
|
||||
showToast("success", `${pluralize(ids.length, "reminder")} deleted.`);
|
||||
showToast("success", `${pluralize(reminders.length, "reminder")} deleted.`);
|
||||
}
|
||||
|
||||
export const Multiselect = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user